UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

129 lines 47.5 kB
import { Component, EventEmitter, Input, Output } from '@angular/core'; import { FormGroup } from '@angular/forms'; import { PlatformConfigurationFormProviderService } from './platform-configuration-form-provider.service'; import * as i0 from "@angular/core"; import * as i1 from "./platform-configuration-form-provider.service"; import * as i2 from "@c8y/ngx-components"; import * as i3 from "@angular/common"; import * as i4 from "@angular/forms"; export class PlatformConfigurationFormComponent { set _optionsGroups(groupIds) { if (groupIds === undefined) { return; } this.optionsGroups = groupIds.map(groupId => this.formProviderService.optionsGroups.find(option => option.id === groupId)); } set readOnly(disabled) { disabled ? this.form.disable() : this.form.enable(); } set formValue(value) { if (value) { const convertedValue = this.afterLoad(value); this.form.patchValue(convertedValue, { emitEvent: false }); this.form.markAsUntouched(); this.form.markAsPristine(); } } constructor(formProviderService) { this.formProviderService = formProviderService; this.optionsGroups = []; this.onSave = new EventEmitter(); this.form = new FormGroup({}); this.lineBreakHint = this.formProviderService.lineBreakHint; Object.keys(formProviderService.platformConfigurationFormDefinitions).forEach(fieldId => { this.form.addControl(fieldId, formProviderService.platformConfigurationFormDefinitions[fieldId].control); }); } /** * Parses provided object's property values * @param options */ parseConfiguration(options) { const definitions = this.formProviderService.platformConfigurationFormDefinitions; Object.entries(options).forEach(([key, rawValue]) => { if (definitions[key] && (definitions[key].type === 'number' || definitions[key].type === 'boolean')) { try { options[key] = JSON.parse(rawValue); } catch { options[key] = rawValue; } } }); } /** * Parses incoming raw object before it is applied to FormGroup. */ afterLoad(options) { this.parseConfiguration(options); delete options['ui.email.protocolAndEncryption']; const emailProtocol = options['email.protocol']; const connectionEncrypted = options['email.connection.encrypted'] || false; if (emailProtocol === 'smtp') { if (connectionEncrypted) { options['ui.email.protocolAndEncryption'] = 'SMTP_ENCRYPTED'; } else { options['ui.email.protocolAndEncryption'] = 'SMTP_PLAIN'; } } else if (emailProtocol === 'smtps') { options['ui.email.protocolAndEncryption'] = 'SMTPS_ENCRYPTED'; } else { options['ui.email.protocolAndEncryption'] = undefined; } return options; } /** * Translates ui.email.protocolAndEncryption field into * separate email.protocol and email.connection.encrypted */ beforeSave(value) { const protocolAndEncryption = value['ui.email.protocolAndEncryption']; if (protocolAndEncryption) { //email.protocol const protocol = protocolAndEncryption.match(/^(.+?)_(.+?)$/)[1]; value['email.protocol'] = protocol ? protocol.toLowerCase() : protocol; // email.connection.encrypted const encryption = protocolAndEncryption.match(/^(.+?)_(.+?)$/)[2]; value['email.connection.encrypted'] = encryption === 'ENCRYPTED'; delete value['ui.email.protocolAndEncryption']; } return value; } getDirtyValues() { const dirtyValues = {}; Object.keys(this.form.controls).forEach(key => { const currentControl = this.form.controls[key]; if (currentControl.dirty) { dirtyValues[key] = currentControl.value; } }); return dirtyValues; } emitForm() { if (this.form.valid) { const changedValues = this.getDirtyValues(); const convertedValue = this.beforeSave(changedValues); this.onSave.emit(convertedValue); } } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PlatformConfigurationFormComponent, deps: [{ token: i1.PlatformConfigurationFormProviderService }], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "18.2.13", type: PlatformConfigurationFormComponent, selector: "c8y-platform-configuration-form", inputs: { _optionsGroups: ["optionsGroups", "_optionsGroups"], readOnly: "readOnly", formValue: "formValue" }, outputs: { onSave: "onSave" }, ngImport: i0, template: "<form [formGroup]=\"form\">\n <div class=\"row\">\n <div class=\"col-lg-12 col-lg-max\">\n <div class=\"card card--fullpage\">\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Configuration' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div>\n <fieldset\n class=\"row schema-form-fieldset separator-bottom p-t-24\"\n *ngFor=\"let category of optionsGroups\"\n >\n <div class=\"col-sm-3 col-md-2 p-l-24 p-l-xs-8 p-b-8\">\n <div\n class=\"h4 text-normal text-right text-left-xs\"\n data-cy=\"fieldset--section-title\"\n >\n {{ category.title | translate }}\n </div>\n </div>\n <div class=\"col-sm-9 col-md-8\">\n <div\n class=\"form-group\"\n *ngFor=\"let field of category.items\"\n >\n <ng-container [ngSwitch]=\"field.type\">\n <ng-container *ngSwitchCase=\"'string'\">\n <ng-container\n *ngTemplateOutlet=\"stringTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'password'\">\n <ng-container\n *ngTemplateOutlet=\"passwordTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'textarea'\">\n <ng-container\n *ngTemplateOutlet=\"textareaTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'boolean'\">\n <ng-container\n *ngTemplateOutlet=\"booleanTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <ng-container\n *ngTemplateOutlet=\"selectTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-primary\"\n [title]=\"'Save configuration' | translate\"\n type=\"submit\"\n (click)=\"emitForm()\"\n [disabled]=\"!form.dirty || form.invalid || form.disabled\"\n >\n <span translate>Save</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <ng-template\n #stringTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"text\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #passwordTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"password\"\n autocomplete=\"new-password\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"number\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #textareaTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <textarea\n class=\"form-control\"\n [title]=\"field.title | translate\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n [rows]=\"10\"\n ></textarea>\n <div\n class=\"help-block\"\n *ngIf=\"field.description || field.lineBreakHint\"\n >\n <span *ngIf=\"field.description\">\n {{ field.description | translate : field.descriptionTranslateParams }}\n </span>\n <span *ngIf=\"field.description && field.lineBreakHint\"><br /></span>\n <span *ngIf=\"field.lineBreakHint\">{{ lineBreakHint | translate }}</span>\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #booleanTemplate\n let-field\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n title=\"{{ field.title | translate }}\"\n >\n <input\n [title]=\"field.title | translate\"\n type=\"checkbox\"\n [formControlName]=\"field.formId\"\n />\n <span></span>\n <span>{{ field.title | translate }}</span>\n </label>\n\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #selectTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n <span>{{ field.title | translate }}</span>\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n >\n <option></option>\n <option\n *ngFor=\"let option of field.options\"\n [value]=\"option.value\"\n >\n {{ option.name | translate }}\n </option>\n </select>\n <span></span>\n </div>\n\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n</form>\n", dependencies: [{ kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i3.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i4.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i4.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i4.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: PlatformConfigurationFormComponent, decorators: [{ type: Component, args: [{ selector: 'c8y-platform-configuration-form', template: "<form [formGroup]=\"form\">\n <div class=\"row\">\n <div class=\"col-lg-12 col-lg-max\">\n <div class=\"card card--fullpage\">\n <div class=\"card-header separator\">\n <div class=\"card-title\">\n {{ 'Configuration' | translate }}\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-block\">\n <div>\n <fieldset\n class=\"row schema-form-fieldset separator-bottom p-t-24\"\n *ngFor=\"let category of optionsGroups\"\n >\n <div class=\"col-sm-3 col-md-2 p-l-24 p-l-xs-8 p-b-8\">\n <div\n class=\"h4 text-normal text-right text-left-xs\"\n data-cy=\"fieldset--section-title\"\n >\n {{ category.title | translate }}\n </div>\n </div>\n <div class=\"col-sm-9 col-md-8\">\n <div\n class=\"form-group\"\n *ngFor=\"let field of category.items\"\n >\n <ng-container [ngSwitch]=\"field.type\">\n <ng-container *ngSwitchCase=\"'string'\">\n <ng-container\n *ngTemplateOutlet=\"stringTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'number'\">\n <ng-container\n *ngTemplateOutlet=\"numberTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n <ng-container *ngSwitchCase=\"'password'\">\n <ng-container\n *ngTemplateOutlet=\"passwordTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'textarea'\">\n <ng-container\n *ngTemplateOutlet=\"textareaTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'boolean'\">\n <ng-container\n *ngTemplateOutlet=\"booleanTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n\n <ng-container *ngSwitchCase=\"'select'\">\n <ng-container\n *ngTemplateOutlet=\"selectTemplate; context: { $implicit: field }\"\n ></ng-container>\n </ng-container>\n </ng-container>\n </div>\n </div>\n </fieldset>\n </div>\n </div>\n </div>\n\n <div class=\"card-footer separator\">\n <button\n class=\"btn btn-primary\"\n [title]=\"'Save configuration' | translate\"\n type=\"submit\"\n (click)=\"emitForm()\"\n [disabled]=\"!form.dirty || form.invalid || form.disabled\"\n >\n <span translate>Save</span>\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <ng-template\n #stringTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"text\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #passwordTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"password\"\n autocomplete=\"new-password\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #numberTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <input\n class=\"text-truncate form-control\"\n [title]=\"field.title | translate\"\n type=\"number\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n />\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #textareaTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n {{ field.title | translate }}\n </label>\n <textarea\n class=\"form-control\"\n [title]=\"field.title | translate\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n [placeholder]=\"field.placeholder || '' | translate : field.placeholderParams\"\n [rows]=\"10\"\n ></textarea>\n <div\n class=\"help-block\"\n *ngIf=\"field.description || field.lineBreakHint\"\n >\n <span *ngIf=\"field.description\">\n {{ field.description | translate : field.descriptionTranslateParams }}\n </span>\n <span *ngIf=\"field.description && field.lineBreakHint\"><br /></span>\n <span *ngIf=\"field.lineBreakHint\">{{ lineBreakHint | translate }}</span>\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #booleanTemplate\n let-field\n >\n <c8y-form-group>\n <label\n class=\"c8y-checkbox\"\n title=\"{{ field.title | translate }}\"\n >\n <input\n [title]=\"field.title | translate\"\n type=\"checkbox\"\n [formControlName]=\"field.formId\"\n />\n <span></span>\n <span>{{ field.title | translate }}</span>\n </label>\n\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n\n <ng-template\n #selectTemplate\n let-field\n >\n <c8y-form-group>\n <label\n title=\"{{ field.title | translate }}\"\n [for]=\"field.formId\"\n >\n <span>{{ field.title | translate }}</span>\n </label>\n <div class=\"c8y-select-wrapper\">\n <select\n class=\"form-control\"\n [id]=\"field.formId\"\n [formControlName]=\"field.formId\"\n >\n <option></option>\n <option\n *ngFor=\"let option of field.options\"\n [value]=\"option.value\"\n >\n {{ option.name | translate }}\n </option>\n </select>\n <span></span>\n </div>\n\n <div\n class=\"help-block\"\n *ngIf=\"field.description\"\n >\n {{ field.description | translate : field.descriptionTranslateParams }}\n </div>\n </c8y-form-group>\n </ng-template>\n</form>\n" }] }], ctorParameters: () => [{ type: i1.PlatformConfigurationFormProviderService }], propDecorators: { _optionsGroups: [{ type: Input, args: ['optionsGroups'] }], readOnly: [{ type: Input }], formValue: [{ type: Input }], onSave: [{ type: Output }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGxhdGZvcm0tY29uZmlndXJhdGlvbi1mb3JtLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BsYXRmb3JtLWNvbmZpZ3VyYXRpb24vcGxhdGZvcm0tY29uZmlndXJhdGlvbi1mb3JtLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL3BsYXRmb3JtLWNvbmZpZ3VyYXRpb24vcGxhdGZvcm0tY29uZmlndXJhdGlvbi1mb3JtLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdkUsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQzNDLE9BQU8sRUFBRSx3Q0FBd0MsRUFBRSxNQUFNLGdEQUFnRCxDQUFDOzs7Ozs7QUFNMUcsTUFBTSxPQUFPLGtDQUFrQztJQUM3QyxJQUNJLGNBQWMsQ0FBQyxRQUFrQjtRQUNuQyxJQUFJLFFBQVEsS0FBSyxTQUFTLEVBQUUsQ0FBQztZQUMzQixPQUFPO1FBQ1QsQ0FBQztRQUNELElBQUksQ0FBQyxhQUFhLEdBQUcsUUFBUSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUMxQyxJQUFJLENBQUMsbUJBQW1CLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEtBQUssT0FBTyxDQUFDLENBQzdFLENBQUM7SUFDSixDQUFDO0lBRUQsSUFDSSxRQUFRLENBQUMsUUFBaUI7UUFDNUIsUUFBUSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDO0lBQ3RELENBQUM7SUFFRCxJQUNJLFNBQVMsQ0FBQyxLQUFLO1FBQ2pCLElBQUksS0FBSyxF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QUFDO1lBQ3BCLE1BQU0sYUFBYSxHQUFHLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztZQUM1QyxNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQ3RELElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ25DLENBQUM7SUFDSCxDQUFDOytHQTlIVSxrQ0FBa0M7bUdBQWxDLGtDQUFrQyxxTkNSL0MsOHVRQTJRQTs7NEZEblFhLGtDQUFrQztrQkFKOUMsU0FBUzsrQkFDRSxpQ0FBaUM7NkdBS3ZDLGNBQWM7c0JBRGpCLEtBQUs7dUJBQUMsZUFBZTtnQkFXbEIsUUFBUTtzQkFEWCxLQUFLO2dCQU1GLFNBQVM7c0JBRFosS0FBSztnQkFZTixNQUFNO3NCQURMLE1BQU0iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21wb25lbnQsIEV2ZW50RW1pdHRlciwgSW5wdXQsIE91dHB1dCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgRm9ybUdyb3VwIH0gZnJvbSAnQGFuZ3VsYXIvZm9ybXMnO1xuaW1wb3J0IHsgUGxhdGZvcm1Db25maWd1cmF0aW9uRm9ybVByb3ZpZGVyU2VydmljZSB9IGZyb20gJy4vcGxhdGZvcm0tY29uZmlndXJhdGlvbi1mb3JtLXByb3ZpZGVyLnNlcnZpY2UnO1xuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICdjOHktcGxhdGZvcm0tY29uZmlndXJhdGlvbi1mb3JtJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3BsYXRmb3JtLWNvbmZpZ3VyYXRpb24tZm9ybS5jb21wb25lbnQuaHRtbCdcbn0pXG5leHBvcnQgY2xhc3MgUGxhdGZvcm1Db25maWd1cmF0aW9uRm9ybUNvbXBvbmVudCB7XG4gIEBJbnB1dCgnb3B0aW9uc0dyb3VwcycpXG4gIHNldCBfb3B0aW9uc0dyb3Vwcyhncm91cElkczogc3RyaW5nW10pIHtcbiAgICBpZiAoZ3JvdXBJZHMgPT09IHVuZGVmaW5lZCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLm9wdGlvbnNHcm91cHMgPSBncm91cElkcy5tYXAoZ3JvdXBJZCA9PlxuICAgICAgdGhpcy5mb3JtUHJvdmlkZXJTZXJ2aWNlLm9wdGlvbnNHcm91cHMuZmluZChvcHRpb24gPT4gb3B0aW9uLmlkID09PSBncm91cElkKVxuICAgICk7XG4gIH1cblxuICBASW5wdXQoKVxuICBzZXQgcmVhZE9ubHkoZGlzYWJsZWQ6IGJvb2xlYW4pIHtcbiAgICBkaXNhYmxlZCA/IHRoaXMuZm9ybS5kaXNhYmxlKCkgOiB0aGlzLmZvcm0uZW5hYmxlKCk7XG4gIH1cblxuICBASW5wdXQoKVxuICBzZXQgZm9ybVZhbHVlKHZhbHVlKSB7XG4gICAgaWYgKHZhbHVlKSB7XG4gICAgICBjb25zdCBjb252ZXJ0ZWRWYWx1ZSA9IHRoaXMuYWZ0ZXJMb2FkKHZhbHVlKTtcbiAgICAgIHRoaXMuZm9ybS5wYXRjaFZhbHVlKGNvbnZlcnRlZFZhbHVlLCB7IGVtaXRFdmVudDogZmFsc2UgfSk7XG4gICAgICB0aGlzLmZvcm0ubWFya0FzVW50b3VjaGVkKCk7XG4gICAgICB0aGlzLmZvcm0ubWFya0FzUHJpc3RpbmUoKTtcbiAgICB9XG4gIH1cbiAgb3B0aW9uc0dyb3VwcyA9IFtdO1xuXG4gIEBPdXRwdXQoKVxuICBvblNhdmUgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgZm9ybSA9IG5ldyBGb3JtR3JvdXAoe30pO1xuXG4gIGxpbmVCcmVha0hpbnQgPSB0aGlzLmZvcm1Qcm92aWRlclNlcnZpY2UubGluZUJyZWFrSGludDtcblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGZvcm1Qcm92aWRlclNlcnZpY2U6IFBsYXRmb3JtQ29uZmlndXJhdGlvbkZvcm1Qcm92aWRlclNlcnZpY2UpIHtcbiAgICBPYmplY3Qua2V5cyhmb3JtUHJvdmlkZXJTZXJ2aWNlLnBsYXRmb3JtQ29uZmlndXJhdGlvbkZvcm1EZWZpbml0aW9ucykuZm9yRWFjaChmaWVsZElkID0+IHtcbiAgICAgIHRoaXMuZm9ybS5hZGRDb250cm9sKFxuICAgICAgICBmaWVsZElkLFxuICAgICAgICBmb3JtUHJvdmlkZXJTZXJ2aWNlLnBsYXRmb3JtQ29uZmlndXJhdGlvbkZvcm1EZWZpbml0aW9uc1tmaWVsZElkXS5jb250cm9sXG4gICAgICApO1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlcyBwcm92aWRlZCBvYmplY3QncyBwcm9wZXJ0eSB2YWx1ZXNcbiAgICogQHBhcmFtIG9wdGlvbnNcbiAgICovXG4gIHBhcnNlQ29uZmlndXJhdGlvbihvcHRpb25zOiBvYmplY3QpOiB2b2lkIHtcbiAgICBjb25zdCBkZWZpbml0aW9uczogb2JqZWN0ID0gdGhpcy5mb3JtUHJvdmlkZXJTZXJ2aWNlLnBsYXRmb3JtQ29uZmlndXJhdGlvbkZvcm1EZWZpbml0aW9ucztcbiAgICBPYmplY3QuZW50cmllcyhvcHRpb25zKS5mb3JFYWNoKChba2V5LCByYXdWYWx1ZV0pID0+IHtcbiAgICAgIGlmIChcbiAgICAgICAgZGVmaW5pdGlvbnNba2V5XSAmJlxuICAgICAgICAoZGVmaW5pdGlvbnNba2V5XS50eXBlID09PSAnbnVtYmVyJyB8fCBkZWZpbml0aW9uc1trZXldLnR5cGUgPT09ICdib29sZWFuJylcbiAgICAgICkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIG9wdGlvbnNba2V5XSA9IEpTT04ucGFyc2UocmF3VmFsdWUpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICBvcHRpb25zW2tleV0gPSByYXdWYWx1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFBhcnNlcyBpbmNvbWluZyByYXcgb2JqZWN0IGJlZm9yZSBpdCBpcyBhcHBsaWVkIHRvIEZvcm1Hcm91cC5cbiAgICovXG4gIGFmdGVyTG9hZChvcHRpb25zOiBvYmplY3QpOiBvYmplY3Qge1xuICAgIHRoaXMucGFyc2VDb25maWd1cmF0aW9uKG9wdGlvbnMpO1xuXG4gICAgZGVsZXRlIG9wdGlvbnNbJ3VpLmVtYWlsLnByb3RvY29sQW5kRW5jcnlwdGlvbiddO1xuICAgIGNvbnN0IGVtYWlsUHJvdG9jb2wgPSBvcHRpb25zWydlbWFpbC5wcm90b2NvbCddO1xuICAgIGNvbnN0IGNvbm5lY3Rpb25FbmNyeXB0ZWQgPSBvcHRpb25zWydlbWFpbC5jb25uZWN0aW9uLmVuY3J5cHRlZCddIHx8IGZhbHNlO1xuICAgIGlmIChlbWFpbFByb3RvY29sID09PSAnc210cCcpIHtcbiAgICAgIGlmIChjb25uZWN0aW9uRW5jcnlwdGVkKSB7XG4gICAgICAgIG9wdGlvbnNbJ3VpLmVtYWlsLnByb3RvY29sQW5kRW5jcnlwdGlvbiddID0gJ1NNVFBfRU5DUllQVEVEJztcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIG9wdGlvbnNbJ3VpLmVtYWlsLnByb3RvY29sQW5kRW5jcnlwdGlvbiddID0gJ1NNVFBfUExBSU4nO1xuICAgICAgfVxuICAgIH0gZWxzZSBpZiAoZW1haWxQcm90b2NvbCA9PT0gJ3NtdHBzJykge1xuICAgICAgb3B0aW9uc1sndWkuZW1haWwucHJvdG9jb2xBbmRFbmNyeXB0aW9uJ10gPSAnU01UUFNfRU5DUllQVEVEJztcbiAgICB9IGVsc2Uge1xuICAgICAgb3B0aW9uc1sndWkuZW1haWwucHJvdG9jb2xBbmRFbmNyeXB0aW9uJ10gPSB1bmRlZmluZWQ7XG4gICAgfVxuICAgIHJldHVybiBvcHRpb25zO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYW5zbGF0ZXMgdWkuZW1haWwucHJvdG9jb2xBbmRFbmNyeXB0aW9uIGZpZWxkIGludG9cbiAgICogc2VwYXJhdGUgZW1haWwucHJvdG9jb2wgYW5kIGVtYWlsLmNvbm5lY3Rpb24uZW5jcnlwdGVkXG4gICAqL1xuICBiZWZvcmVTYXZlKHZhbHVlKTogb2JqZWN0IHtcbiAgICBjb25zdCBwcm90b2NvbEFuZEVuY3J5cHRpb24gPSB2YWx1ZVsndWkuZW1haWwucHJvdG9jb2xBbmRFbmNyeXB0aW9uJ107XG4gICAgaWYgKHByb3RvY29sQW5kRW5jcnlwdGlvbikge1xuICAgICAgLy9lbWFpbC5wcm90b2NvbFxuICAgICAgY29uc3QgcHJvdG9jb2wgPSBwcm90b2NvbEFuZEVuY3J5cHRpb24ubWF0Y2goL14oLis/KV8oLis/KSQvKVsxXTtcbiAgICAgIHZhbHVlWydlbWFpbC5wcm90b2NvbCddID0gcHJvdG9jb2wgPyBwcm90b2NvbC50b0xvd2VyQ2FzZSgpIDogcHJvdG9jb2w7XG5cbiAgICAgIC8vIGVtYWlsLmNvbm5lY3Rpb24uZW5jcnlwdGVkXG4gICAgICBjb25zdCBlbmNyeXB0aW9uID0gcHJvdG9jb2xBbmRFbmNyeXB0aW9uLm1hdGNoKC9eKC4rPylfKC4rPykkLylbMl07XG4gICAgICB2YWx1ZVsnZW1haWwuY29ubmVjdGlvbi5lbmNyeXB0ZWQnXSA9IGVuY3J5cHRpb24gPT09ICdFTkNSWVBURUQnO1xuICAgICAgZGVsZXRlIHZhbHVlWyd1aS5lbWFpbC5wcm90b2NvbEFuZEVuY3J5cHRpb24nXTtcbiAgICB9XG5cbiAgICByZXR1cm4gdmFsdWU7XG4gIH1cblxuICBnZXREaXJ0eVZhbHVlcygpIHtcbiAgICBjb25zdCBkaXJ0eVZhbHVlcyA9IHt9O1xuXG4gICAgT2JqZWN0LmtleXModGhpcy5mb3JtLmNvbnRyb2xzKS5mb3JFYWNoKGtleSA9PiB7XG4gICAgICBjb25zdCBjdXJyZW50Q29udHJvbCA9IHRoaXMuZm9ybS5jb250cm9sc1trZXldO1xuXG4gICAgICBpZiAoY3VycmVudENvbnRyb2wuZGlydHkpIHtcbiAgICAgICAgZGlydHlWYWx1ZXNba2V5XSA9IGN1cnJlbnRDb250cm9sLnZhbHVlO1xuICAgICAgfVxuICAgIH0pO1xuXG4gICAgcmV0dXJuIGRpcnR5VmFsdWVzO1xuICB9XG5cbiAgZW1pdEZvcm0oKSB7XG4gICAgaWYgKHRoaXMuZm9ybS52YWxpZCkge1xuICAgICAgY29uc3QgY2hhbmdlZFZhbHVlcyA9IHRoaXMuZ2V0RGlydHlWYWx1ZXMoKTtcbiAgICAgIGNvbnN0IGNvbnZlcnRlZFZhbHVlID0gdGhpcy5iZWZvcmVTYXZlKGNoYW5nZWRWYWx1ZXMpO1xuICAgICAgdGhpcy5vblNhdmUuZW1pdChjb252ZXJ0ZWRWYWx1ZSk7XG4gICAgfVxuICB9XG59XG4iLCI8Zm9ybSBbZm9ybUdyb3VwXT1cImZvcm1cIj5cbiAgPGRpdiBjbGFzcz1cInJvd1wiPlxuICAgIDxkaXYgY2xhc3M9XCJjb2wtbGctMTIgY29sLWxnLW1heFwiPlxuICAgICAgPGRpdiBjbGFzcz1cImNhcmQgY2FyZC0tZnVsbHBhZ2VcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNhcmQtaGVhZGVyIHNlcGFyYXRvclwiPlxuICAgICAgICAgIDxkaXYgY2xhc3M9XCJjYXJkLXRpdGxlXCI+XG4gICAgICAgICAgICB7eyAnQ29uZmlndXJhdGlvbicgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJpbm5lci1zY3JvbGxcIj5cbiAgICAgICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1ibG9ja1wiPlxuICAgICAgICAgICAgPGRpdj5cbiAgICAgICAgICAgICAgPGZpZWxkc2V0XG4gICAgICAgICAgICAgICAgY2xhc3M9XCJyb3cgc2NoZW1hLWZvcm0tZmllbGRzZXQgc2VwYXJhdG9yLWJvdHRvbSBwLXQtMjRcIlxuICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBjYXRlZ29yeSBvZiBvcHRpb25zR3JvdXBzXCJcbiAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb2wtc20tMyBjb2wtbWQtMiBwLWwtMjQgcC1sLXhzLTggcC1iLThcIj5cbiAgICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJoNCB0ZXh0LW5vcm1hbCB0ZXh0LXJpZ2h0IHRleHQtbGVmdC14c1wiXG4gICAgICAgICAgICAgICAgICAgIGRhdGEtY3k9XCJmaWVsZHNldC0tc2VjdGlvbi10aXRsZVwiXG4gICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIHt7IGNhdGVnb3J5LnRpdGxlIHwgdHJhbnNsYXRlIH19XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwiY29sLXNtLTkgY29sLW1kLThcIj5cbiAgICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJmb3JtLWdyb3VwXCJcbiAgICAgICAgICAgICAgICAgICAgKm5nRm9yPVwibGV0IGZpZWxkIG9mIGNhdGVnb3J5Lml0ZW1zXCJcbiAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciBbbmdTd2l0Y2hdPVwiZmllbGQudHlwZVwiPlxuICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nU3dpdGNoQ2FzZT1cIidzdHJpbmcnXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwic3RyaW5nVGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZCB9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ1N3aXRjaENhc2U9XCInbnVtYmVyJ1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICAgICAgICAgICAqbmdUZW1wbGF0ZU91dGxldD1cIm51bWJlclRlbXBsYXRlOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogZmllbGQgfVwiXG4gICAgICAgICAgICAgICAgICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdTd2l0Y2hDYXNlPVwiJ3Bhc3N3b3JkJ1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICAgICAgICAgICAqbmdUZW1wbGF0ZU91dGxldD1cInBhc3N3b3JkVGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZCB9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nU3dpdGNoQ2FzZT1cIid0ZXh0YXJlYSdcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJ0ZXh0YXJlYVRlbXBsYXRlOyBjb250ZXh0OiB7ICRpbXBsaWNpdDogZmllbGQgfVwiXG4gICAgICAgICAgICAgICAgICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG5cbiAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ1N3aXRjaENhc2U9XCInYm9vbGVhbidcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgKm5nVGVtcGxhdGVPdXRsZXQ9XCJib29sZWFuVGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZCB9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgICAgICAgICAgICAgICAgICAgIDxuZy1jb250YWluZXIgKm5nU3dpdGNoQ2FzZT1cIidzZWxlY3QnXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICpuZ1RlbXBsYXRlT3V0bGV0PVwic2VsZWN0VGVtcGxhdGU7IGNvbnRleHQ6IHsgJGltcGxpY2l0OiBmaWVsZCB9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID48L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgPC9maWVsZHNldD5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiY2FyZC1mb290ZXIgc2VwYXJhdG9yXCI+XG4gICAgICAgICAgPGJ1dHRvblxuICAgICAgICAgICAgY2xhc3M9XCJidG4gYnRuLXByaW1hcnlcIlxuICAgICAgICAgICAgW3RpdGxlXT1cIidTYXZlIGNvbmZpZ3VyYXRpb24nIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgICAgIHR5cGU9XCJzdWJtaXRcIlxuICAgICAgICAgICAgKGNsaWNrKT1cImVtaXRGb3JtKClcIlxuICAgICAgICAgICAgW2Rpc2FibGVkXT1cIiFmb3JtLmRpcnR5IHx8IGZvcm0uaW52YWxpZCB8fCBmb3JtLmRpc2FibGVkXCJcbiAgICAgICAgICA+XG4gICAgICAgICAgICA8c3BhbiB0cmFuc2xhdGU+U2F2ZTwvc3Bhbj5cbiAgICAgICAgICA8L2J1dHRvbj5cbiAgICAgICAgPC9kaXY+XG4gICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG5cbiAgPG5nLXRlbXBsYXRlXG4gICAgI3N0cmluZ1RlbXBsYXRlXG4gICAgbGV0LWZpZWxkXG4gID5cbiAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8bGFiZWxcbiAgICAgICAgdGl0bGU9XCJ7eyBmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgIFtmb3JdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgID5cbiAgICAgICAge3sgZmllbGQudGl0bGUgfCB0cmFuc2xhdGUgfX1cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8aW5wdXRcbiAgICAgICAgY2xhc3M9XCJ0ZXh0LXRydW5jYXRlIGZvcm0tY29udHJvbFwiXG4gICAgICAgIFt0aXRsZV09XCJmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgW2lkXT1cImZpZWxkLmZvcm1JZFwiXG4gICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnIHwgdHJhbnNsYXRlIDogZmllbGQucGxhY2Vob2xkZXJQYXJhbXNcIlxuICAgICAgLz5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJoZWxwLWJsb2NrXCJcbiAgICAgICAgKm5nSWY9XCJmaWVsZC5kZXNjcmlwdGlvblwiXG4gICAgICA+XG4gICAgICAgIHt7IGZpZWxkLmRlc2NyaXB0aW9uIHwgdHJhbnNsYXRlIDogZmllbGQuZGVzY3JpcHRpb25UcmFuc2xhdGVQYXJhbXMgfX1cbiAgICAgIDwvZGl2PlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvbmctdGVtcGxhdGU+XG5cbiAgPG5nLXRlbXBsYXRlXG4gICAgI3Bhc3N3b3JkVGVtcGxhdGVcbiAgICBsZXQtZmllbGRcbiAgPlxuICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgIDxsYWJlbFxuICAgICAgICB0aXRsZT1cInt7IGZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgW2Zvcl09XCJmaWVsZC5mb3JtSWRcIlxuICAgICAgPlxuICAgICAgICB7eyBmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZSB9fVxuICAgICAgPC9sYWJlbD5cbiAgICAgIDxpbnB1dFxuICAgICAgICBjbGFzcz1cInRleHQtdHJ1bmNhdGUgZm9ybS1jb250cm9sXCJcbiAgICAgICAgW3RpdGxlXT1cImZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgdHlwZT1cInBhc3N3b3JkXCJcbiAgICAgICAgYXV0b2NvbXBsZXRlPVwibmV3LXBhc3N3b3JkXCJcbiAgICAgICAgW2lkXT1cImZpZWxkLmZvcm1JZFwiXG4gICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnIHwgdHJhbnNsYXRlIDogZmllbGQucGxhY2Vob2xkZXJQYXJhbXNcIlxuICAgICAgLz5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJoZWxwLWJsb2NrXCJcbiAgICAgICAgKm5nSWY9XCJmaWVsZC5kZXNjcmlwdGlvblwiXG4gICAgICA+XG4gICAgICAgIHt7IGZpZWxkLmRlc2NyaXB0aW9uIHwgdHJhbnNsYXRlIDogZmllbGQuZGVzY3JpcHRpb25UcmFuc2xhdGVQYXJhbXMgfX1cbiAgICAgIDwvZGl2PlxuICAgIDwvYzh5LWZvcm0tZ3JvdXA+XG4gIDwvbmctdGVtcGxhdGU+XG5cbiAgPG5nLXRlbXBsYXRlXG4gICAgI251bWJlclRlbXBsYXRlXG4gICAgbGV0LWZpZWxkXG4gID5cbiAgICA8Yzh5LWZvcm0tZ3JvdXA+XG4gICAgICA8bGFiZWxcbiAgICAgICAgdGl0bGU9XCJ7eyBmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZSB9fVwiXG4gICAgICAgIFtmb3JdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgID5cbiAgICAgICAge3sgZmllbGQudGl0bGUgfCB0cmFuc2xhdGUgfX1cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8aW5wdXRcbiAgICAgICAgY2xhc3M9XCJ0ZXh0LXRydW5jYXRlIGZvcm0tY29udHJvbFwiXG4gICAgICAgIFt0aXRsZV09XCJmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZVwiXG4gICAgICAgIHR5cGU9XCJudW1iZXJcIlxuICAgICAgICBbaWRdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgICAgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5mb3JtSWRcIlxuICAgICAgICBbcGxhY2Vob2xkZXJdPVwiZmllbGQucGxhY2Vob2xkZXIgfHwgJycgfCB0cmFuc2xhdGUgOiBmaWVsZC5wbGFjZWhvbGRlclBhcmFtc1wiXG4gICAgICAvPlxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImhlbHAtYmxvY2tcIlxuICAgICAgICAqbmdJZj1cImZpZWxkLmRlc2NyaXB0aW9uXCJcbiAgICAgID5cbiAgICAgICAge3sgZmllbGQuZGVzY3JpcHRpb24gfCB0cmFuc2xhdGUgOiBmaWVsZC5kZXNjcmlwdGlvblRyYW5zbGF0ZVBhcmFtcyB9fVxuICAgICAgPC9kaXY+XG4gICAgPC9jOHktZm9ybS1ncm91cD5cbiAgPC9uZy10ZW1wbGF0ZT5cblxuICA8bmctdGVtcGxhdGVcbiAgICAjdGV4dGFyZWFUZW1wbGF0ZVxuICAgIGxldC1maWVsZFxuICA+XG4gICAgPGM4eS1mb3JtLWdyb3VwPlxuICAgICAgPGxhYmVsXG4gICAgICAgIHRpdGxlPVwie3sgZmllbGQudGl0bGUgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgICBbZm9yXT1cImZpZWxkLmZvcm1JZFwiXG4gICAgICA+XG4gICAgICAgIHt7IGZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlIH19XG4gICAgICA8L2xhYmVsPlxuICAgICAgPHRleHRhcmVhXG4gICAgICAgIGNsYXNzPVwiZm9ybS1jb250cm9sXCJcbiAgICAgICAgW3RpdGxlXT1cImZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlXCJcbiAgICAgICAgW2lkXT1cImZpZWxkLmZvcm1JZFwiXG4gICAgICAgIFtmb3JtQ29udHJvbE5hbWVdPVwiZmllbGQuZm9ybUlkXCJcbiAgICAgICAgW3BsYWNlaG9sZGVyXT1cImZpZWxkLnBsYWNlaG9sZGVyIHx8ICcnIHwgdHJhbnNsYXRlIDogZmllbGQucGxhY2Vob2xkZXJQYXJhbXNcIlxuICAgICAgICBbcm93c109XCIxMFwiXG4gICAgICA+PC90ZXh0YXJlYT5cbiAgICAgIDxkaXZcbiAgICAgICAgY2xhc3M9XCJoZWxwLWJsb2NrXCJcbiAgICAgICAgKm5nSWY9XCJmaWVsZC5kZXNjcmlwdGlvbiB8fCBmaWVsZC5saW5lQnJlYWtIaW50XCJcbiAgICAgID5cbiAgICAgICAgPHNwYW4gKm5nSWY9XCJmaWVsZC5kZXNjcmlwdGlvblwiPlxuICAgICAgICAgIHt7IGZpZWxkLmRlc2NyaXB0aW9uIHwgdHJhbnNsYXRlIDogZmllbGQuZGVzY3JpcHRpb25UcmFuc2xhdGVQYXJhbXMgfX1cbiAgICAgICAgPC9zcGFuPlxuICAgICAgICA8c3BhbiAqbmdJZj1cImZpZWxkLmRlc2NyaXB0aW9uICYmIGZpZWxkLmxpbmVCcmVha0hpbnRcIj48YnIgLz48L3NwYW4+XG4gICAgICAgIDxzcGFuICpuZ0lmPVwiZmllbGQubGluZUJyZWFrSGludFwiPnt7IGxpbmVCcmVha0hpbnQgfCB0cmFuc2xhdGUgfX08L3NwYW4+XG4gICAgICA8L2Rpdj5cbiAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICA8L25nLXRlbXBsYXRlPlxuXG4gIDxuZy10ZW1wbGF0ZVxuICAgICNib29sZWFuVGVtcGxhdGVcbiAgICBsZXQtZmllbGRcbiAgPlxuICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgIDxsYWJlbFxuICAgICAgICBjbGFzcz1cImM4eS1jaGVja2JveFwiXG4gICAgICAgIHRpdGxlPVwie3sgZmllbGQudGl0bGUgfCB0cmFuc2xhdGUgfX1cIlxuICAgICAgPlxuICAgICAgICA8aW5wdXRcbiAgICAgICAgICBbdGl0bGVdPVwiZmllbGQudGl0bGUgfCB0cmFuc2xhdGVcIlxuICAgICAgICAgIHR5cGU9XCJjaGVja2JveFwiXG4gICAgICAgICAgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5mb3JtSWRcIlxuICAgICAgICAvPlxuICAgICAgICA8c3Bhbj48L3NwYW4+XG4gICAgICAgIDxzcGFuPnt7IGZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlIH19PC9zcGFuPlxuICAgICAgPC9sYWJlbD5cblxuICAgICAgPGRpdlxuICAgICAgICBjbGFzcz1cImhlbHAtYmxvY2tcIlxuICAgICAgICAqbmdJZj1cImZpZWxkLmRlc2NyaXB0aW9uXCJcbiAgICAgID5cbiAgICAgICAge3sgZmllbGQuZGVzY3JpcHRpb24gfCB0cmFuc2xhdGUgOiBmaWVsZC5kZXNjcmlwdGlvblRyYW5zbGF0ZVBhcmFtcyB9fVxuICAgICAgPC9kaXY+XG4gICAgPC9jOHktZm9ybS1ncm91cD5cbiAgPC9uZy10ZW1wbGF0ZT5cblxuICA8bmctdGVtcGxhdGVcbiAgICAjc2VsZWN0VGVtcGxhdGVcbiAgICBsZXQtZmllbGRcbiAgPlxuICAgIDxjOHktZm9ybS1ncm91cD5cbiAgICAgIDxsYWJlbFxuICAgICAgICB0aXRsZT1cInt7IGZpZWxkLnRpdGxlIHwgdHJhbnNsYXRlIH19XCJcbiAgICAgICAgW2Zvcl09XCJmaWVsZC5mb3JtSWRcIlxuICAgICAgPlxuICAgICAgICA8c3Bhbj57eyBmaWVsZC50aXRsZSB8IHRyYW5zbGF0ZSB9fTwvc3Bhbj5cbiAgICAgIDwvbGFiZWw+XG4gICAgICA8ZGl2IGNsYXNzPVwiYzh5LXNlbGVjdC13cmFwcGVyXCI+XG4gICAgICAgIDxzZWxlY3RcbiAgICAgICAgICBjbGFzcz1cImZvcm0tY29udHJvbFwiXG4gICAgICAgICAgW2lkXT1cImZpZWxkLmZvcm1JZFwiXG4gICAgICAgICAgW2Zvcm1Db250cm9sTmFtZV09XCJmaWVsZC5mb3JtSWRcIlxuICAgICAgICA+XG4gICAgICAgICAgPG9wdGlvbj48L29wdGlvbj5cbiAgICAgICAgICA8b3B0aW9uXG4gICAgICAgICAgICAqbmdGb3I9XCJsZXQgb3B0aW9uIG9mIGZpZWxkLm9wdGlvbnNcIlxuICAgICAgICAgICAgW3ZhbHVlXT1cIm9wdGlvbi52YWx1ZVwiXG4gICAgICAgICAgPlxuICAgICAgICAgICAge3sgb3B0aW9uLm5hbWUgfCB0cmFuc2xhdGUgfX1cbiAgICAgICAgICA8L29wdGlvbj5cbiAgICAgICAgPC9zZWxlY3Q+XG4gICAgICAgIDxzcGFuPjwvc3Bhbj5cbiAgICAgIDwvZGl2PlxuXG4gICAgICA8ZGl2XG4gICAgICAgIGNsYXNzPVwiaGVscC1ibG9ja1wiXG4gICAgICAgICpuZ0lmPVwiZmllbGQuZGVzY3JpcHRpb25cIlxuICAgICAgPlxuICAgICAgICB7eyBmaWVsZC5kZXNjcmlwdGlvbiB8IHRyYW5zbGF0ZSA6IGZpZWxkLmRlc2NyaXB0aW9uVHJhbnNsYXRlUGFyYW1zIH19XG4gICAgICA8L2Rpdj5cbiAgICA8L2M4eS1mb3JtLWdyb3VwPlxuICA8L25nLXRlbXBsYXRlPlxuPC9mb3JtPlxuIl19