angular-dynamic-forms-ai
Version:
AI-enhanced dynamic form generator for Angular using Template-Driven Forms
313 lines (297 loc) • 36 kB
JavaScript
import { Component, Input, Output, EventEmitter } from '@angular/core';
import * as i0 from "@angular/core";
import * as i1 from "../services/ai-validation.service";
import * as i2 from "@angular/common";
import * as i3 from "@angular/forms";
class DynamicFormComponent {
aiValidationService;
formConfig;
formSubmit = new EventEmitter();
constructor(aiValidationService) {
this.aiValidationService = aiValidationService;
}
ngOnInit() {
// Apply AI-based validation rules to each field
this.formConfig.fields = this.formConfig.fields.map(field => this.aiValidationService.suggestValidations(field));
}
isFieldVisible(field) {
if (!field.conditionalDisplay) {
return true;
}
const dependentField = this.formConfig.fields.find(f => f.name === field.conditionalDisplay?.dependsOn);
return dependentField?.value === field.conditionalDisplay.showWhen;
}
onSubmit(form) {
if (form.valid) {
const formData = this.formConfig.fields.reduce((acc, field) => {
if (this.isFieldVisible(field)) {
acc[field.name] = field.value;
}
return acc;
}, {});
this.formSubmit.emit(formData);
}
}
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: DynamicFormComponent, deps: [{ token: i1.AiValidationService }], target: i0.ɵɵFactoryTarget.Component });
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.0.6", type: DynamicFormComponent, selector: "lib-dynamic-form", inputs: { formConfig: "formConfig" }, outputs: { formSubmit: "formSubmit" }, ngImport: i0, template: `
<form
<div *ngFor="let field of formConfig.fields" class="form-field" [ngClass]="{'hidden': !isFieldVisible(field)}">
<label [for]="field.name" class="form-label">
{{ field.label }}
<span *ngIf="field.validations?.required" class="required-marker">*</span>
</label>
<ng-container [ngSwitch]="field.type">
<!-- Text, Email, Number, Password inputs -->
<input *ngSwitchCase="'text'"
[]="field.type"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.maxLength"
[]="field.validations?.pattern"
[]="field.validations?.email"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'email'"
type="email"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="true"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'number'"
type="number"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'password'"
type="password"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.pattern"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<!-- Select dropdown -->
<select *ngSwitchCase="'select'"
[]="field.name"
[]="field.name"
[(ngModel)]="field.value"
[]="field.validations?.required"
class="form-select"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<option value="">Select {{ field.label }}</option>
<option *ngFor="let option of field.options" [value]="option.value">
{{ option.label }}
</option>
</select>
<!-- Textarea -->
<textarea *ngSwitchCase="'textarea'"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.maxLength"
class="form-textarea"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
</textarea>
</ng-container>
<!-- Error messages -->
<div *ngIf="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
class="error-messages"
[]="field.name + '-error'"
role="alert">
<div *ngIf="fieldModel.errors?.required">
{{ field.errorMessages?.required }}
</div>
<div *ngIf="fieldModel.errors?.minlength">
{{ field.errorMessages?.minlength }}
</div>
<div *ngIf="fieldModel.errors?.maxlength">
{{ field.errorMessages?.maxlength }}
</div>
<div *ngIf="fieldModel.errors?.email">
{{ field.errorMessages?.email }}
</div>
<div *ngIf="fieldModel.errors?.pattern">
{{ field.errorMessages?.pattern }}
</div>
</div>
</div>
<button type="submit"
[]="!form.valid"
[]="formConfig.submitButton?.class || 'submit-button'"
[]="!form.valid">
{{ formConfig.submitButton?.text || 'Submit' }}
</button>
</form>
`, isInline: true, styles: [".dynamic-form{max-width:600px;margin:0 auto;padding:20px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.form-field{margin-bottom:20px}.form-label{display:block;margin-bottom:8px;font-weight:500;color:#333}.required-marker{color:#dc3545;margin-left:4px}.form-input,.form-select,.form-textarea{width:100%;padding:8px 12px;border:1px solid #ced4da;border-radius:4px;font-size:16px;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-textarea{min-height:100px;resize:vertical}.form-input:focus,.form-select:focus,.form-textarea:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem #007bff40}.error{border-color:#dc3545}.error:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem #dc354540}.error-messages{margin-top:5px;color:#dc3545;font-size:14px}.hidden{display:none}.submit-button{background-color:#007bff;color:#fff;padding:10px 20px;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:background-color .15s ease-in-out}.submit-button:hover:not(:disabled){background-color:#0056b3}.submit-button:disabled{background-color:#6c757d;cursor:not-allowed;opacity:.65}@media (max-width: 768px){.dynamic-form{padding:15px}}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i3.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: i3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i3.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i3.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i3.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i3.EmailValidator, selector: "[email][formControlName],[email][formControl],[email][ngModel]", inputs: ["email"] }, { kind: "directive", type: i3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i3.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }] });
}
export { DynamicFormComponent };
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.0.6", ngImport: i0, type: DynamicFormComponent, decorators: [{
type: Component,
args: [{ selector: 'lib-dynamic-form', template: `
<form
<div *ngFor="let field of formConfig.fields" class="form-field" [ngClass]="{'hidden': !isFieldVisible(field)}">
<label [for]="field.name" class="form-label">
{{ field.label }}
<span *ngIf="field.validations?.required" class="required-marker">*</span>
</label>
<ng-container [ngSwitch]="field.type">
<!-- Text, Email, Number, Password inputs -->
<input *ngSwitchCase="'text'"
[]="field.type"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.maxLength"
[]="field.validations?.pattern"
[]="field.validations?.email"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'email'"
type="email"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="true"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'number'"
type="number"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<input *ngSwitchCase="'password'"
type="password"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.pattern"
class="form-input"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<!-- Select dropdown -->
<select *ngSwitchCase="'select'"
[]="field.name"
[]="field.name"
[(ngModel)]="field.value"
[]="field.validations?.required"
class="form-select"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
<option value="">Select {{ field.label }}</option>
<option *ngFor="let option of field.options" [value]="option.value">
{{ option.label }}
</option>
</select>
<!-- Textarea -->
<textarea *ngSwitchCase="'textarea'"
[]="field.name"
[]="field.name"
[]="field.placeholder || ''"
[(ngModel)]="field.value"
[]="field.validations?.required"
[]="field.validations?.minLength"
[]="field.validations?.maxLength"
class="form-textarea"
[]="{'error': fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)}"
[]="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
[]="field.name + '-error'">
</textarea>
</ng-container>
<!-- Error messages -->
<div *ngIf="fieldModel.invalid && (fieldModel.dirty || fieldModel.touched)"
class="error-messages"
[]="field.name + '-error'"
role="alert">
<div *ngIf="fieldModel.errors?.required">
{{ field.errorMessages?.required }}
</div>
<div *ngIf="fieldModel.errors?.minlength">
{{ field.errorMessages?.minlength }}
</div>
<div *ngIf="fieldModel.errors?.maxlength">
{{ field.errorMessages?.maxlength }}
</div>
<div *ngIf="fieldModel.errors?.email">
{{ field.errorMessages?.email }}
</div>
<div *ngIf="fieldModel.errors?.pattern">
{{ field.errorMessages?.pattern }}
</div>
</div>
</div>
<button type="submit"
[]="!form.valid"
[]="formConfig.submitButton?.class || 'submit-button'"
[]="!form.valid">
{{ formConfig.submitButton?.text || 'Submit' }}
</button>
</form>
`, styles: [".dynamic-form{max-width:600px;margin:0 auto;padding:20px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.form-field{margin-bottom:20px}.form-label{display:block;margin-bottom:8px;font-weight:500;color:#333}.required-marker{color:#dc3545;margin-left:4px}.form-input,.form-select,.form-textarea{width:100%;padding:8px 12px;border:1px solid #ced4da;border-radius:4px;font-size:16px;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}.form-textarea{min-height:100px;resize:vertical}.form-input:focus,.form-select:focus,.form-textarea:focus{border-color:#80bdff;outline:0;box-shadow:0 0 0 .2rem #007bff40}.error{border-color:#dc3545}.error:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem #dc354540}.error-messages{margin-top:5px;color:#dc3545;font-size:14px}.hidden{display:none}.submit-button{background-color:#007bff;color:#fff;padding:10px 20px;border:none;border-radius:4px;font-size:16px;cursor:pointer;transition:background-color .15s ease-in-out}.submit-button:hover:not(:disabled){background-color:#0056b3}.submit-button:disabled{background-color:#6c757d;cursor:not-allowed;opacity:.65}@media (max-width: 768px){.dynamic-form{padding:15px}}\n"] }]
}], ctorParameters: function () { return [{ type: i1.AiValidationService }]; }, propDecorators: { formConfig: [{
type: Input
}], formSubmit: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHluYW1pYy1mb3JtLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL2R5bmFtaWMtZm9ybXMvc3JjL2xpYi9jb21wb25lbnRzL2R5bmFtaWMtZm9ybS5jb21wb25lbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBVSxNQUFNLGVBQWUsQ0FBQzs7Ozs7QUFLL0UsTUF1T2Esb0JBQW9CO0lBSVg7SUFIWCxVQUFVLENBQWM7SUFDdkIsVUFBVSxHQUFHLElBQUksWUFBWSxFQUFPLENBQUM7SUFFL0MsWUFBb0IsbUJBQXdDO1FBQXhDLHdCQUFtQixHQUFuQixtQkFBbUIsQ0FBcUI7SUFBRyxDQUFDO0lBRWhFLFFBQVE7UUFDTixnREFBZ0Q7UUFDaEQsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQzFELElBQUksQ0FBQyxtQkFBbUIsQ0FBQyxrQkFBa0IsQ0FBQyxLQUFLLENBQUMsQ0FDbkQsQ0FBQztJQUNKLENBQUM7SUFFRCxjQUFjLENBQUMsS0FBc0I7UUFDbkMsSUFBSSxDQUFDLEtBQUssQ0FBQyxrQkFBa0IsRUFBRTtZQUM3QixPQUFPLElBQUksQ0FBQztTQUNiO1FBRUQsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUNoRCxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLEtBQUssS0FBSyxDQUFDLGtCQUFrQixFQUFFLFNBQVMsQ0FDcEQsQ0FBQztRQUVGLE9BQU8sY0FBYyxFQUFFLEtBQUssS0FBSyxLQUFLLENBQUMsa0JBQWtCLENBQUMsUUFBUSxDQUFDO0lBQ3JFLENBQUM7SUFFRCxRQUFRLENBQUMsSUFBWTtRQUNuQixJQUFJLElBQUksQ0FBQyxLQUFLLEVBQUU7WUFDZCxNQUFNLFFBQVEsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxHQUFHLEVBQUUsS0FBSyxFQUFFLEVBQUU7Z0JBQzVELElBQUksSUFBSSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsRUFBRTtvQkFDOUIsR0FBRyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxLQUFLLENBQUMsS0FBSyxDQUFDO2lCQUMvQjtnQkFDRCxPQUFPLEdBQUcsQ0FBQztZQUNiLENBQUMsRUFBRSxFQUFTLENBQUMsQ0FBQztZQUVkLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1NBQ2hDO0lBQ0gsQ0FBQzt1R0FwQ1Usb0JBQW9COzJGQUFwQixvQkFBb0IscUlBck9yQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXFJVDs7U0FnR1Usb0JBQW9COzJGQUFwQixvQkFBb0I7a0JBdk9oQyxTQUFTOytCQUNFLGtCQUFrQixZQUNsQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXFJVDswR0FpR1EsVUFBVTtzQkFBbEIsS0FBSztnQkFDSSxVQUFVO3NCQUFuQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBJbnB1dCwgT3V0cHV0LCBFdmVudEVtaXR0ZXIsIE9uSW5pdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTmdGb3JtIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgRm9ybUNvbmZpZywgRm9ybUZpZWxkQ29uZmlnIH0gZnJvbSAnLi4vaW50ZXJmYWNlcy9mb3JtLWZpZWxkLmludGVyZmFjZSc7XG5pbXBvcnQgeyBBaVZhbGlkYXRpb25TZXJ2aWNlIH0gZnJvbSAnLi4vc2VydmljZXMvYWktdmFsaWRhdGlvbi5zZXJ2aWNlJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnbGliLWR5bmFtaWMtZm9ybScsXG4gIHRlbXBsYXRlOiBgXG4gICAgPGZvcm0gI2Zvcm09XCJuZ0Zvcm1cIiAobmdTdWJtaXQpPVwib25TdWJtaXQoZm9ybSlcIiBjbGFzcz1cImR5bmFtaWMtZm9ybVwiIFthdHRyLmFyaWEtbGFiZWxdPVwiZm9ybUNvbmZpZy5maWVsZHNbMF0/LmxhYmVsICsgJyBmb3JtJ1wiPlxuICAgICAgPGRpdiAqbmdGb3I9XCJsZXQgZmllbGQgb2YgZm9ybUNvbmZpZy5maWVsZHNcIiBjbGFzcz1cImZvcm0tZmllbGRcIiBbbmdDbGFzc109XCJ7J2hpZGRlbic6ICFpc0ZpZWxkVmlzaWJsZShmaWVsZCl9XCI+XG4gICAgICAgIDxsYWJlbCBbZm9yXT1cImZpZWxkLm5hbWVcIiBjbGFzcz1cImZvcm0tbGFiZWxcIj5cbiAgICAgICAgICB7eyBmaWVsZC5sYWJlbCB9fVxuICAgICAgICAgIDxzcGFuICpuZ0lmPVwiZmllbGQudmFsaWRhdGlvbnM/LnJlcXVpcmVkXCIgY2xhc3M9XCJyZXF1aXJlZC1tYXJrZXJcIj4qPC9zcGFuPlxuICAgICAgICA8L2xhYmVsPlxuXG4gICAgICAgIDxuZy1jb250YWluZXIgW25nU3dpdGNoXT1cImZpZWxkLnR5cGVcIj5cbiAgICAgICAgICA8IS0tIFRleHQsIEVtYWlsLCBOdW1iZXIsIFBhc3N3b3JkIGlucHV0cyAtLT5cbiAgICAgICAgICA8aW5wdXQgKm5nU3dpdGNoQ2FzZT1cIid0ZXh0J1wiIFxuICAgICAgICAgICAgICAgICBbdHlwZV09XCJmaWVsZC50eXBlXCJcbiAgICAgICAgICAgICAgICAgW2lkXT1cImZpZWxkLm5hbWVcIlxuICAgICAgICAgICAgICAgICBbbmFtZV09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcbiAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJmaWVsZC52YWx1ZVwiXG4gICAgICAgICAgICAgICAgICNmaWVsZE1vZGVsPVwibmdNb2RlbFwiXG4gICAgICAgICAgICAgICAgIFtyZXF1aXJlZF09XCJmaWVsZC52YWxpZGF0aW9ucz8ucmVxdWlyZWRcIlxuICAgICAgICAgICAgICAgICBbbWlubGVuZ3RoXT1cImZpZWxkLnZhbGlkYXRpb25zPy5taW5MZW5ndGhcIlxuICAgICAgICAgICAgICAgICBbbWF4bGVuZ3RoXT1cImZpZWxkLnZhbGlkYXRpb25zPy5tYXhMZW5ndGhcIlxuICAgICAgICAgICAgICAgICBbcGF0dGVybl09XCJmaWVsZC52YWxpZGF0aW9ucz8ucGF0dGVyblwiXG4gICAgICAgICAgICAgICAgIFtlbWFpbF09XCJmaWVsZC52YWxpZGF0aW9ucz8uZW1haWxcIlxuICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0taW5wdXRcIlxuICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7J2Vycm9yJzogZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZCl9XCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1kZXNjcmliZWRieV09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIj5cblxuICAgICAgICAgIDxpbnB1dCAqbmdTd2l0Y2hDYXNlPVwiJ2VtYWlsJ1wiXG4gICAgICAgICAgICAgICAgIHR5cGU9XCJlbWFpbFwiXG4gICAgICAgICAgICAgICAgIFtpZF09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgW25hbWVdPVwiZmllbGQubmFtZVwiXG4gICAgICAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiXG4gICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwiZmllbGQudmFsdWVcIlxuICAgICAgICAgICAgICAgICAjZmllbGRNb2RlbD1cIm5nTW9kZWxcIlxuICAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwiZmllbGQudmFsaWRhdGlvbnM/LnJlcXVpcmVkXCJcbiAgICAgICAgICAgICAgICAgW2VtYWlsXT1cInRydWVcIlxuICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0taW5wdXRcIlxuICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7J2Vycm9yJzogZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZCl9XCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1kZXNjcmliZWRieV09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIj5cblxuICAgICAgICAgIDxpbnB1dCAqbmdTd2l0Y2hDYXNlPVwiJ251bWJlcidcIlxuICAgICAgICAgICAgICAgICB0eXBlPVwibnVtYmVyXCJcbiAgICAgICAgICAgICAgICAgW2lkXT1cImZpZWxkLm5hbWVcIlxuICAgICAgICAgICAgICAgICBbbmFtZV09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcbiAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJmaWVsZC52YWx1ZVwiXG4gICAgICAgICAgICAgICAgICNmaWVsZE1vZGVsPVwibmdNb2RlbFwiXG4gICAgICAgICAgICAgICAgIFtyZXF1aXJlZF09XCJmaWVsZC52YWxpZGF0aW9ucz8ucmVxdWlyZWRcIlxuICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0taW5wdXRcIlxuICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7J2Vycm9yJzogZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZCl9XCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1kZXNjcmliZWRieV09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIj5cblxuICAgICAgICAgIDxpbnB1dCAqbmdTd2l0Y2hDYXNlPVwiJ3Bhc3N3b3JkJ1wiXG4gICAgICAgICAgICAgICAgIHR5cGU9XCJwYXNzd29yZFwiXG4gICAgICAgICAgICAgICAgIFtpZF09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgW25hbWVdPVwiZmllbGQubmFtZVwiXG4gICAgICAgICAgICAgICAgIFtwbGFjZWhvbGRlcl09XCJmaWVsZC5wbGFjZWhvbGRlciB8fCAnJ1wiXG4gICAgICAgICAgICAgICAgIFsobmdNb2RlbCldPVwiZmllbGQudmFsdWVcIlxuICAgICAgICAgICAgICAgICAjZmllbGRNb2RlbD1cIm5nTW9kZWxcIlxuICAgICAgICAgICAgICAgICBbcmVxdWlyZWRdPVwiZmllbGQudmFsaWRhdGlvbnM/LnJlcXVpcmVkXCJcbiAgICAgICAgICAgICAgICAgW21pbmxlbmd0aF09XCJmaWVsZC52YWxpZGF0aW9ucz8ubWluTGVuZ3RoXCJcbiAgICAgICAgICAgICAgICAgW3BhdHRlcm5dPVwiZmllbGQudmFsaWRhdGlvbnM/LnBhdHRlcm5cIlxuICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0taW5wdXRcIlxuICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7J2Vycm9yJzogZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZCl9XCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1kZXNjcmliZWRieV09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIj5cblxuICAgICAgICAgIDwhLS0gU2VsZWN0IGRyb3Bkb3duIC0tPlxuICAgICAgICAgIDxzZWxlY3QgKm5nU3dpdGNoQ2FzZT1cIidzZWxlY3QnXCJcbiAgICAgICAgICAgICAgICAgIFtpZF09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgIFtuYW1lXT1cImZpZWxkLm5hbWVcIlxuICAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJmaWVsZC52YWx1ZVwiXG4gICAgICAgICAgICAgICAgICAjZmllbGRNb2RlbD1cIm5nTW9kZWxcIlxuICAgICAgICAgICAgICAgICAgW3JlcXVpcmVkXT1cImZpZWxkLnZhbGlkYXRpb25zPy5yZXF1aXJlZFwiXG4gICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0tc2VsZWN0XCJcbiAgICAgICAgICAgICAgICAgIFtuZ0NsYXNzXT1cInsnZXJyb3InOiBmaWVsZE1vZGVsLmludmFsaWQgJiYgKGZpZWxkTW9kZWwuZGlydHkgfHwgZmllbGRNb2RlbC50b3VjaGVkKX1cIlxuICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgIFthdHRyLmFyaWEtZGVzY3JpYmVkYnldPVwiZmllbGQubmFtZSArICctZXJyb3InXCI+XG4gICAgICAgICAgICA8b3B0aW9uIHZhbHVlPVwiXCI+U2VsZWN0IHt7IGZpZWxkLmxhYmVsIH19PC9vcHRpb24+XG4gICAgICAgICAgICA8b3B0aW9uICpuZ0Zvcj1cImxldCBvcHRpb24gb2YgZmllbGQub3B0aW9uc1wiIFt2YWx1ZV09XCJvcHRpb24udmFsdWVcIj5cbiAgICAgICAgICAgICAge3sgb3B0aW9uLmxhYmVsIH19XG4gICAgICAgICAgICA8L29wdGlvbj5cbiAgICAgICAgICA8L3NlbGVjdD5cblxuICAgICAgICAgIDwhLS0gVGV4dGFyZWEgLS0+XG4gICAgICAgICAgPHRleHRhcmVhICpuZ1N3aXRjaENhc2U9XCIndGV4dGFyZWEnXCJcbiAgICAgICAgICAgICAgICAgICAgW2lkXT1cImZpZWxkLm5hbWVcIlxuICAgICAgICAgICAgICAgICAgICBbbmFtZV09XCJmaWVsZC5uYW1lXCJcbiAgICAgICAgICAgICAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnXCJcbiAgICAgICAgICAgICAgICAgICAgWyhuZ01vZGVsKV09XCJmaWVsZC52YWx1ZVwiXG4gICAgICAgICAgICAgICAgICAgICNmaWVsZE1vZGVsPVwibmdNb2RlbFwiXG4gICAgICAgICAgICAgICAgICAgIFtyZXF1aXJlZF09XCJmaWVsZC52YWxpZGF0aW9ucz8ucmVxdWlyZWRcIlxuICAgICAgICAgICAgICAgICAgICBbbWlubGVuZ3RoXT1cImZpZWxkLnZhbGlkYXRpb25zPy5taW5MZW5ndGhcIlxuICAgICAgICAgICAgICAgICAgICBbbWF4bGVuZ3RoXT1cImZpZWxkLnZhbGlkYXRpb25zPy5tYXhMZW5ndGhcIlxuICAgICAgICAgICAgICAgICAgICBjbGFzcz1cImZvcm0tdGV4dGFyZWFcIlxuICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7J2Vycm9yJzogZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZCl9XCJcbiAgICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1pbnZhbGlkXT1cImZpZWxkTW9kZWwuaW52YWxpZCAmJiAoZmllbGRNb2RlbC5kaXJ0eSB8fCBmaWVsZE1vZGVsLnRvdWNoZWQpXCJcbiAgICAgICAgICAgICAgICAgICAgW2F0dHIuYXJpYS1kZXNjcmliZWRieV09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIj5cbiAgICAgICAgICA8L3RleHRhcmVhPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICA8IS0tIEVycm9yIG1lc3NhZ2VzIC0tPlxuICAgICAgICA8ZGl2ICpuZ0lmPVwiZmllbGRNb2RlbC5pbnZhbGlkICYmIChmaWVsZE1vZGVsLmRpcnR5IHx8IGZpZWxkTW9kZWwudG91Y2hlZClcIlxuICAgICAgICAgICAgIGNsYXNzPVwiZXJyb3ItbWVzc2FnZXNcIlxuICAgICAgICAgICAgIFtpZF09XCJmaWVsZC5uYW1lICsgJy1lcnJvcidcIlxuICAgICAgICAgICAgIHJvbGU9XCJhbGVydFwiPlxuICAgICAgICAgIDxkaXYgKm5nSWY9XCJmaWVsZE1vZGVsLmVycm9ycz8ucmVxdWlyZWRcIj5cbiAgICAgICAgICAgIHt7IGZpZWxkLmVycm9yTWVzc2FnZXM/LnJlcXVpcmVkIH19XG4gICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgPGRpdiAqbmdJZj1cImZpZWxkTW9kZWwuZXJyb3JzPy5taW5sZW5ndGhcIj5cbiAgICAgICAgICAgIHt7IGZpZWxkLmVycm9yTWVzc2FnZXM/Lm1pbmxlbmd0aCB9fVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgKm5nSWY9XCJmaWVsZE1vZGVsLmVycm9ycz8ubWF4bGVuZ3RoXCI+XG4gICAgICAgICAgICB7eyBmaWVsZC5lcnJvck1lc3NhZ2VzPy5tYXhsZW5ndGggfX1cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICA8ZGl2ICpuZ0lmPVwiZmllbGRNb2RlbC5lcnJvcnM/LmVtYWlsXCI+XG4gICAgICAgICAgICB7eyBmaWVsZC5lcnJvck1lc3NhZ2VzPy5lbWFpbCB9fVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDxkaXYgKm5nSWY9XCJmaWVsZE1vZGVsLmVycm9ycz8ucGF0dGVyblwiPlxuICAgICAgICAgICAge3sgZmllbGQuZXJyb3JNZXNzYWdlcz8ucGF0dGVybiB9fVxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8YnV0dG9uIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICAgICAgICBbZGlzYWJsZWRdPVwiIWZvcm0udmFsaWRcIlxuICAgICAgICAgICAgICBbY2xhc3NdPVwiZm9ybUNvbmZpZy5zdWJtaXRCdXR0b24/LmNsYXNzIHx8ICdzdWJtaXQtYnV0dG9uJ1wiXG4gICAgICAgICAgICAgIFthdHRyLmFyaWEtZGlzYWJsZWRdPVwiIWZvcm0udmFsaWRcIj5cbiAgICAgICAge3sgZm9ybUNvbmZpZy5zdWJtaXRCdXR0b24/LnRleHQgfHwgJ1N1Ym1pdCcgfX1cbiAgICAgIDwvYnV0dG9uPlxuICAgIDwvZm9ybT5cbiAgYCxcbiAgc3R5bGVzOiBbYFxuICAgIC5keW5hbWljLWZvcm0ge1xuICAgICAgbWF4LXdpZHRoOiA2MDBweDtcbiAgICAgIG1hcmdpbjogMCBhdXRvO1xuICAgICAgcGFkZGluZzogMjBweDtcbiAgICAgIGZvbnQtZmFtaWx5OiAtYXBwbGUtc3lzdGVtLCBCbGlua01hY1N5c3RlbUZvbnQsICdTZWdvZSBVSScsIFJvYm90bywgJ0hlbHZldGljYSBOZXVlJywgQXJpYWwsIHNhbnMtc2VyaWY7XG4gICAgfVxuXG4gICAgLmZvcm0tZmllbGQge1xuICAgICAgbWFyZ2luLWJvdHRvbTogMjBweDtcbiAgICB9XG5cbiAgICAuZm9ybS1sYWJlbCB7XG4gICAgICBkaXNwbGF5OiBibG9jaztcbiAgICAgIG1hcmdpbi1ib3R0b206IDhweDtcbiAgICAgIGZvbnQtd2VpZ2h0OiA1MDA7XG4gICAgICBjb2xvcjogIzMzMztcbiAgICB9XG5cbiAgICAucmVxdWlyZWQtbWFya2VyIHtcbiAgICAgIGNvbG9yOiAjZGMzNTQ1O1xuICAgICAgbWFyZ2luLWxlZnQ6IDRweDtcbiAgICB9XG5cbiAgICAuZm9ybS1pbnB1dCxcbiAgICAuZm9ybS1zZWxlY3QsXG4gICAgLmZvcm0tdGV4dGFyZWEge1xuICAgICAgd2lkdGg6IDEwMCU7XG4gICAgICBwYWRkaW5nOiA4cHggMTJweDtcbiAgICAgIGJvcmRlcjogMXB4IHNvbGlkICNjZWQ0ZGE7XG4gICAgICBib3JkZXItcmFkaXVzOiA0cHg7XG4gICAgICBmb250LXNpemU6IDE2cHg7XG4gICAgICB0cmFuc2l0aW9uOiBib3JkZXItY29sb3IgMC4xNXMgZWFzZS1pbi1vdXQsIGJveC1zaGFkb3cgMC4xNXMgZWFzZS1pbi1vdXQ7XG4gICAgfVxuXG4gICAgLmZvcm0tdGV4dGFyZWEge1xuICAgICAgbWluLWhlaWdodDogMTAwcHg7XG4gICAgICByZXNpemU6IHZlcnRpY2FsO1xuICAgIH1cblxuICAgIC5mb3JtLWlucHV0OmZvY3VzLFxuICAgIC5mb3JtLXNlbGVjdDpmb2N1cyxcbiAgICAuZm9ybS10ZXh0YXJlYTpmb2N1cyB7XG4gICAgICBib3JkZXItY29sb3I6ICM4MGJkZmY7XG4gICAgICBvdXRsaW5lOiAwO1xuICAgICAgYm94LXNoYWRvdzogMCAwIDAgMC4ycmVtIHJnYmEoMCwgMTIzLCAyNTUsIDAuMjUpO1xuICAgIH1cblxuICAgIC5lcnJvciB7XG4gICAgICBib3JkZXItY29sb3I6ICNkYzM1NDU7XG4gICAgfVxuXG4gICAgLmVycm9yOmZvY3VzIHtcbiAgICAgIGJvcmRlci1jb2xvcjogI2RjMzU0NTtcbiAgICAgIGJveC1zaGFkb3c6IDAgMCAwIDAuMnJlbSByZ2JhKDIyMCwgNTMsIDY5LCAwLjI1KTtcbiAgICB9XG5cbiAgICAuZXJyb3ItbWVzc2FnZXMge1xuICAgICAgbWFyZ2luLXRvcDogNXB4O1xuICAgICAgY29sb3I6ICNkYzM1NDU7XG4gICAgICBmb250LXNpemU6IDE0cHg7XG4gICAgfVxuXG4gICAgLmhpZGRlbiB7XG4gICAgICBkaXNwbGF5OiBub25lO1xuICAgIH1cblxuICAgIC5zdWJtaXQtYnV0dG9uIHtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICMwMDdiZmY7XG4gICAgICBjb2xvcjogd2hpdGU7XG4gICAgICBwYWRkaW5nOiAxMHB4IDIwcHg7XG4gICAgICBib3JkZXI6IG5vbmU7XG4gICAgICBib3JkZXItcmFkaXVzOiA0cHg7XG4gICAgICBmb250LXNpemU6IDE2cHg7XG4gICAgICBjdXJzb3I6IHBvaW50ZXI7XG4gICAgICB0cmFuc2l0aW9uOiBiYWNrZ3JvdW5kLWNvbG9yIDAuMTVzIGVhc2UtaW4tb3V0O1xuICAgIH1cblxuICAgIC5zdWJtaXQtYnV0dG9uOmhvdmVyOm5vdCg6ZGlzYWJsZWQpIHtcbiAgICAgIGJhY2tncm91bmQtY29sb3I6ICMwMDU2YjM7XG4gICAgfVxuXG4gICAgLnN1Ym1pdC1idXR0b246ZGlzYWJsZWQge1xuICAgICAgYmFja2dyb3VuZC1jb2xvcjogIzZjNzU3ZDtcbiAgICAgIGN1cnNvcjogbm90LWFsbG93ZWQ7XG4gICAgICBvcGFjaXR5OiAwLjY1O1xuICAgIH1cblxuICAgIEBtZWRpYSAobWF4LXdpZHRoOiA3NjhweCkge1xuICAgICAgLmR5bmFtaWMtZm9ybSB7XG4gICAgICAgIHBhZGRpbmc6IDE1cHg7XG4gICAgICB9XG4gICAgfVxuICBgXVxufSlcbmV4cG9ydCBjbGFzcyBEeW5hbWljRm9ybUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCB7XG4gIEBJbnB1dCgpIGZvcm1Db25maWchOiBGb3JtQ29uZmlnO1xuICBAT3V0cHV0KCkgZm9ybVN1Ym1pdCA9IG5ldyBFdmVudEVtaXR0ZXI8YW55PigpO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgYWlWYWxpZGF0aW9uU2VydmljZTogQWlWYWxpZGF0aW9uU2VydmljZSkge31cblxuICBuZ09uSW5pdCgpIHtcbiAgICAvLyBBcHBseSBBSS1iYXNlZCB2YWxpZGF0aW9uIHJ1bGVzIHRvIGVhY2ggZmllbGRcbiAgICB0aGlzLmZvcm1Db25maWcuZmllbGRzID0gdGhpcy5mb3JtQ29uZmlnLmZpZWxkcy5tYXAoZmllbGQgPT4gXG4gICAgICB0aGlzLmFpVmFsaWRhdGlvblNlcnZpY2Uuc3VnZ2VzdFZhbGlkYXRpb25zKGZpZWxkKVxuICAgICk7XG4gIH1cblxuICBpc0ZpZWxkVmlzaWJsZShmaWVsZDogRm9ybUZpZWxkQ29uZmlnKTogYm9vbGVhbiB7XG4gICAgaWYgKCFmaWVsZC5jb25kaXRpb25hbERpc3BsYXkpIHtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIGNvbnN0IGRlcGVuZGVudEZpZWxkID0gdGhpcy5mb3JtQ29uZmlnLmZpZWxkcy5maW5kKFxuICAgICAgZiA9PiBmLm5hbWUgPT09IGZpZWxkLmNvbmRpdGlvbmFsRGlzcGxheT8uZGVwZW5kc09uXG4gICAgKTtcblxuICAgIHJldHVybiBkZXBlbmRlbnRGaWVsZD8udmFsdWUgPT09IGZpZWxkLmNvbmRpdGlvbmFsRGlzcGxheS5zaG93V2hlbjtcbiAgfVxuXG4gIG9uU3VibWl0KGZvcm06IE5nRm9ybSkge1xuICAgIGlmIChmb3JtLnZhbGlkKSB7XG4gICAgICBjb25zdCBmb3JtRGF0YSA9IHRoaXMuZm9ybUNvbmZpZy5maWVsZHMucmVkdWNlKChhY2MsIGZpZWxkKSA9PiB7XG4gICAgICAgIGlmICh0aGlzLmlzRmllbGRWaXNpYmxlKGZpZWxkKSkge1xuICAgICAgICAgIGFjY1tmaWVsZC5uYW1lXSA9IGZpZWxkLnZhbHVlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBhY2M7XG4gICAgICB9LCB7fSBhcyBhbnkpO1xuXG4gICAgICB0aGlzLmZvcm1TdWJtaXQuZW1pdChmb3JtRGF0YSk7XG4gICAgfVxuICB9XG59XG4iXX0=