@nova-ui/dashboards
Version:
Nova Dashboards is a framework designed to provide feature developers with a common solution for presenting data coming from various sources within a single view, as well as a set of predefined widget visualizations that are 100% configuration-driven and
140 lines • 37.8 kB
JavaScript
// © 2022 SolarWinds Worldwide, LLC. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, Output, } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import includes from "lodash/includes";
import _isNil from "lodash/isNil";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { LoggerService } from "@nova-ui/bits";
import { thresholdsValidator } from "./thresholds-validator";
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "@nova-ui/bits";
import * as i3 from "@angular/common";
import * as i4 from "../../../widget-editor-accordion/widget-editor-accordion.component";
import * as i5 from "../../../widget-editor-accordion/widget-editor-accordion-header/widget-editor-accordion-header.component";
import * as i6 from "../../../../pipe/widget-editor-accordion-form-state.pipe";
import * as i7 from "../../../../pipe/widget-editor-accordion-header-icon.pipe";
export class ThresholdsConfigurationComponent {
static { this.lateLoadKey = "ThresholdsConfigurationComponent"; }
constructor(changeDetector, formBuilder, logger, cd) {
this.changeDetector = changeDetector;
this.formBuilder = formBuilder;
this.logger = logger;
this.cd = cd;
this.formReady = new EventEmitter();
this.thresholdOptions = [
{
value: false,
displayValue: $localize `Never show critical or warning states for this KPI`,
},
{
value: true,
displayValue: $localize `Set custom thresholds for this widget`,
},
];
this.destroy$ = new Subject();
}
ngOnInit() {
this.validateRadioButtonsGroupValue();
this.form = this.formBuilder.group({
criticalThresholdValue: [
this.criticalThresholdValue || 0,
[Validators.required],
],
warningThresholdValue: [this.warningThresholdValue || 0],
showThresholds: [
this.showThresholds || false,
[Validators.required],
],
reversedThresholds: [this.reversedThresholds || false],
}, {
validators: thresholdsValidator,
});
this.handleWarningThreshold(this.warningThresholdValue);
this.form.statusChanges
.pipe(takeUntil(this.destroy$))
.subscribe((change) => {
this.form.markAllAsTouched();
this.cd.detectChanges();
});
this.formReady.emit(this.form);
}
ngOnChanges(changes) {
const formKeys = [
"criticalThresholdValue",
"warningThresholdValue",
"showThresholds",
"reversedThresholds",
];
if (changes) {
for (const key of Object.keys(changes)) {
if (includes(formKeys, key)) {
const value = changes[key].currentValue;
if (value) {
this.form?.patchValue({ [key]: value });
}
}
}
if (changes.showThresholds) {
setTimeout(() => this.validateRadioButtonsGroupValue());
}
}
if (changes.warningThresholdValue) {
this.handleWarningThreshold(changes.warningThresholdValue.currentValue);
}
}
getThresholdsSubtitle(showThreshold) {
return showThreshold
? $localize `Custom thresholds`
: $localize `No thresholds`;
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
validateRadioButtonsGroupValue() {
this.radioButtonGroupValue = this.showThresholds;
}
handleWarningThreshold(value) {
const warningThresholdFormControl = this.form?.get("warningThresholdValue");
if (_isNil(value)) {
warningThresholdFormControl?.setValue(null);
}
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThresholdsConfigurationComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.FormBuilder }, { token: i2.LoggerService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: ThresholdsConfigurationComponent, selector: "nui-thresholds-configuration", inputs: { criticalThresholdValue: "criticalThresholdValue", warningThresholdValue: "warningThresholdValue", showThresholds: "showThresholds", reversedThresholds: "reversedThresholds" }, outputs: { formReady: "formReady" }, usesOnChanges: true, ngImport: i0, template: "<nui-widget-editor-accordion\n [formGroup]=\"form\"\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n>\n <nui-widget-editor-accordion-header\n [headerIcon]=\"form | nuiFormHeaderIconPipe : 'thresholds' | async\"\n [subtitle]=\"getThresholdsSubtitle(form.get('showThresholds')?.value)\"\n headerTitle=\"Thresholds\"\n />\n <div class=\"kpi-thresholds-configuration__accordion-content\">\n <nui-form-field\n [control]=\"form.get('showThresholds')\"\n caption=\"How do you want to set warning/critical thresholds?\"\n i18n-caption\n >\n <nui-radio-group\n formControlName=\"showThresholds\"\n [value]=\"radioButtonGroupValue\"\n name=\"showThresholds\"\n required\n >\n <nui-radio\n *ngFor=\"let thresholdOption of thresholdOptions\"\n [value]=\"thresholdOption.value\"\n >\n {{ thresholdOption.displayValue }}\n </nui-radio>\n </nui-radio-group>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n\n <div class=\"mt-4\" *ngIf=\"form.get('showThresholds')?.value\">\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Warning value\"\n i18n-caption\n [control]=\"form.get('warningThresholdValue')\"\n >\n <nui-textbox-number\n formControlName=\"warningThresholdValue\"\n placeholder=\"Never show the warning state\"\n i18n-placeholder\n ></nui-textbox-number>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n <nui-validation-message for=\"min\" i18n>\n Warning threshold should be greater than critical\n threshold\n </nui-validation-message>\n <nui-validation-message for=\"max\" i18n>\n Warning threshold should be lower than critical\n threshold\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Critical value\"\n i18n-caption\n [control]=\"form.get('criticalThresholdValue')\"\n >\n <nui-textbox-number\n formControlName=\"criticalThresholdValue\"\n placeholder=\"Set critical value\"\n i18n-placeholder\n ></nui-textbox-number>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n <nui-validation-message for=\"min\" i18n>\n Critical threshold should be greater than warning\n threshold\n </nui-validation-message>\n <nui-validation-message for=\"max\" i18n>\n Critical threshold should be lower than warning\n threshold\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n [control]=\"form.get('reversedThresholds')\"\n [showOptionalText]=\"false\"\n >\n <nui-checkbox formControlName=\"reversedThresholds\" required>\n <span class=\"nui-text-label\" i18n\n >Warning and critical when below threshold\n values</span\n >\n </nui-checkbox>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n </div>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".kpi-thresholds-configuration__accordion-content{padding:15px 15px 15px 46px}\n"], dependencies: [{ 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: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i2.TextboxNumberComponent, selector: "nui-textbox-number", inputs: ["value", "customBoxWidth", "disabled", "name", "placeholder", "readonly", "step", "stepPrecision", "isInErrorState", "ariaLabel", "minValue", "maxValue"], outputs: ["blurred", "valueChange"] }, { kind: "component", type: i2.FormFieldComponent, selector: "nui-form-field", inputs: ["control", "caption", "info", "infoTemplate", "hint", "hintTemplate", "customBoxWidth", "showOptionalText"] }, { kind: "component", type: i2.RadioComponent, selector: "nui-radio", inputs: ["value", "hovered", "checked", "hint", "disabled", "ariaLabel"], outputs: ["valueChange"] }, { kind: "component", type: i2.RadioGroupComponent, selector: "nui-radio-group", inputs: ["ariaLabel", "name", "value", "disabled"], outputs: ["valueChange"] }, { kind: "component", type: i2.CheckboxComponent, selector: "nui-checkbox", inputs: ["ariaLabel", "ariaLabelledby", "ariaDescribedby", "name", "title", "value", "hovered", "checked", "required", "hint", "disabled", "indeterminate"], outputs: ["valueChange"] }, { kind: "component", type: i2.ValidationMessageComponent, selector: "nui-validation-message", inputs: ["show", "for"] }, { kind: "component", type: i4.WidgetEditorAccordionComponent, selector: "nui-widget-editor-accordion", inputs: ["showOpenStateIcon", "state"], outputs: ["openToggle"] }, { kind: "component", type: i5.WidgetEditorAccordionHeaderComponent, selector: "nui-widget-editor-accordion-header", inputs: ["headerIcon", "iconColor", "subtitle", "headerTitle"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i6.WidgetEditorAccordionFormStatePipe, name: "nuiWidgetEditorAccordionFormState" }, { kind: "pipe", type: i7.FormHeaderIconPipePipe, name: "nuiFormHeaderIconPipe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: ThresholdsConfigurationComponent, decorators: [{
type: Component,
args: [{ selector: "nui-thresholds-configuration", changeDetection: ChangeDetectionStrategy.OnPush, template: "<nui-widget-editor-accordion\n [formGroup]=\"form\"\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n>\n <nui-widget-editor-accordion-header\n [headerIcon]=\"form | nuiFormHeaderIconPipe : 'thresholds' | async\"\n [subtitle]=\"getThresholdsSubtitle(form.get('showThresholds')?.value)\"\n headerTitle=\"Thresholds\"\n />\n <div class=\"kpi-thresholds-configuration__accordion-content\">\n <nui-form-field\n [control]=\"form.get('showThresholds')\"\n caption=\"How do you want to set warning/critical thresholds?\"\n i18n-caption\n >\n <nui-radio-group\n formControlName=\"showThresholds\"\n [value]=\"radioButtonGroupValue\"\n name=\"showThresholds\"\n required\n >\n <nui-radio\n *ngFor=\"let thresholdOption of thresholdOptions\"\n [value]=\"thresholdOption.value\"\n >\n {{ thresholdOption.displayValue }}\n </nui-radio>\n </nui-radio-group>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n\n <div class=\"mt-4\" *ngIf=\"form.get('showThresholds')?.value\">\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Warning value\"\n i18n-caption\n [control]=\"form.get('warningThresholdValue')\"\n >\n <nui-textbox-number\n formControlName=\"warningThresholdValue\"\n placeholder=\"Never show the warning state\"\n i18n-placeholder\n ></nui-textbox-number>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n <nui-validation-message for=\"min\" i18n>\n Warning threshold should be greater than critical\n threshold\n </nui-validation-message>\n <nui-validation-message for=\"max\" i18n>\n Warning threshold should be lower than critical\n threshold\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Critical value\"\n i18n-caption\n [control]=\"form.get('criticalThresholdValue')\"\n >\n <nui-textbox-number\n formControlName=\"criticalThresholdValue\"\n placeholder=\"Set critical value\"\n i18n-placeholder\n ></nui-textbox-number>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n <nui-validation-message for=\"min\" i18n>\n Critical threshold should be greater than warning\n threshold\n </nui-validation-message>\n <nui-validation-message for=\"max\" i18n>\n Critical threshold should be lower than warning\n threshold\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n [control]=\"form.get('reversedThresholds')\"\n [showOptionalText]=\"false\"\n >\n <nui-checkbox formControlName=\"reversedThresholds\" required>\n <span class=\"nui-text-label\" i18n\n >Warning and critical when below threshold\n values</span\n >\n </nui-checkbox>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n </div>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".kpi-thresholds-configuration__accordion-content{padding:15px 15px 15px 46px}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.FormBuilder }, { type: i2.LoggerService }, { type: i0.ChangeDetectorRef }], propDecorators: { criticalThresholdValue: [{
type: Input
}], warningThresholdValue: [{
type: Input
}], showThresholds: [{
type: Input
}], reversedThresholds: [{
type: Input
}], formReady: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGhyZXNob2xkcy1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29uZmlndXJhdG9yL2NvbXBvbmVudHMvd2lkZ2V0cy9jb25maWd1cmF0b3ItaXRlbXMvdGhyZXNob2xkcy1jb25maWd1cmF0aW9uL3RocmVzaG9sZHMtY29uZmlndXJhdGlvbi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL3RocmVzaG9sZHMtY29uZmlndXJhdGlvbi90aHJlc2hvbGRzLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseURBQXlEO0FBQ3pELEVBQUU7QUFDRiwrRUFBK0U7QUFDL0UsNEVBQTRFO0FBQzVFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsOEVBQThFO0FBQzlFLDREQUE0RDtBQUM1RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLHVEQUF1RDtBQUN2RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDRFQUE0RTtBQUM1RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsaUJBQWlCO0FBRWpCLE9BQU8sRUFDSCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxZQUFZLEVBQ1osS0FBSyxFQUlMLE1BQU0sR0FFVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsV0FBVyxFQUFhLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3BFLE9BQU8sUUFBUSxNQUFNLGlCQUFpQixDQUFDO0FBQ3ZDLE9BQU8sTUFBTSxNQUFNLGNBQWMsQ0FBQztBQUNsQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFD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U0FBUztzQkFBbEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICAgIENoYW5nZURldGVjdG9yUmVmLFxuICAgIENvbXBvbmVudCxcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5wdXQsXG4gICAgT25DaGFuZ2VzLFxuICAgIE9uRGVzdHJveSxcbiAgICBPbkluaXQsXG4gICAgT3V0cHV0LFxuICAgIFNpbXBsZUNoYW5nZXMsXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBGb3JtQnVpbGRlciwgRm9ybUdyb3VwLCBWYWxpZGF0b3JzIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgaW5jbHVkZXMgZnJvbSBcImxvZGFzaC9pbmNsdWRlc1wiO1xuaW1wb3J0IF9pc05pbCBmcm9tIFwibG9kYXNoL2lzTmlsXCI7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBMb2dnZXJTZXJ2aWNlIH0gZnJvbSBcIkBub3ZhLXVpL2JpdHNcIjtcblxuaW1wb3J0IHsgdGhyZXNob2xkc1ZhbGlkYXRvciB9IGZyb20gXCIuL3RocmVzaG9sZHMtdmFsaWRhdG9yXCI7XG5pbXBvcnQgeyBJSGFzQ2hhbmdlRGV0ZWN0b3IsIElIYXNGb3JtIH0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL3R5cGVzXCI7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiBcIm51aS10aHJlc2hvbGRzLWNvbmZpZ3VyYXRpb25cIixcbiAgICB0ZW1wbGF0ZVVybDogXCIuL3RocmVzaG9sZHMtY29uZmlndXJhdGlvbi5jb21wb25lbnQuaHRtbFwiLFxuICAgIHN0eWxlVXJsczogW1wiLi90aHJlc2hvbGRzLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIFRocmVzaG9sZHNDb25maWd1cmF0aW9uQ29tcG9uZW50XG4gICAgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSwgT25DaGFuZ2VzLCBJSGFzQ2hhbmdlRGV0ZWN0b3IsIElIYXNGb3JtXG57XG4gICAgcHVibGljIHN0YXRpYyBsYXRlTG9hZEtleSA9IFwiVGhyZXNob2xkc0NvbmZpZ3VyYXRpb25Db21wb25lbnRcIjtcblxuICAgIEBJbnB1dCgpIGNyaXRpY2FsVGhyZXNob2xkVmFsdWU6IG51bWJlcjtcbiAgICBASW5wdXQoKSB3YXJuaW5nVGhyZXNob2xkVmFsdWU6IG51bWJlcjtcbiAgICBASW5wdXQoKSBzaG93VGhyZXNob2xkczogYm9vbGVhbjtcbiAgICBASW5wdXQoKSByZXZlcnNlZFRocmVzaG9sZHM6IGJvb2xlYW47XG4gICAgQE91dHB1dCgpIGZvcm1SZWFkeSA9IG5ldyBFdmVudEVtaXR0ZXI8Rm9ybUdyb3VwPigpO1xuXG4gICAgcHVibGljIHRocmVzaG9sZE9wdGlvbnMgPSBbXG4gICAgICAgIHtcbiAgICAgICAgICAgIHZhbHVlOiBmYWxzZSxcbiAgICAgICAgICAgIGRpc3BsYXlWYWx1ZTogJGxvY2FsaXplYE5ldmVyIHNob3cgY3JpdGljYWwgb3Igd2FybmluZyBzdGF0ZXMgZm9yIHRoaXMgS1BJYCxcbiAgICAgICAgfSxcbiAgICAgICAge1xuICAgICAgICAgICAgdmFsdWU6IHRydWUsXG4gICAgICAgICAgICBkaXNwbGF5VmFsdWU6ICRsb2NhbGl6ZWBTZXQgY3VzdG9tIHRocmVzaG9sZHMgZm9yIHRoaXMgd2lkZ2V0YCxcbiAgICAgICAgfSxcbiAgICBdO1xuXG4gICAgLyoqXG4gICAgICogdGhpcyB2YWx1ZSBpcyBhZGRlZCBiZWNhdXNlIG9mIHRoZSBzZXRUaW1lb3V0IGluIFJhZGlvQnV0dG9uQ29tcG9uZW50IHdoaWNoIHdpbGwgYmUgcmVtb3ZlZCBpbiB2MTAgLSBOVUktNDg0My5cbiAgICAgKiB3aGVuIHNldFRpbWVvdXQgaXMgcmVtb3ZlZCB0aGlzIGhhY2sgY2FuIGJlIHJlbW92ZWQgYXMgd2VsbFxuICAgICAqIEBkZXByZWNhdGVkIC0gcmVtb3ZlIGluIHNjb3BlIG9mIE5VSS00ODQzXG4gICAgICogKi9cbiAgICBwdWJsaWMgcmFkaW9CdXR0b25Hcm91cFZhbHVlOiBib29sZWFuO1xuXG4gICAgcHVibGljIGZvcm06IEZvcm1Hcm91cDtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHB1YmxpYyBjaGFuZ2VEZXRlY3RvcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgICAgIHByaXZhdGUgZm9ybUJ1aWxkZXI6IEZvcm1CdWlsZGVyLFxuICAgICAgICBwcml2YXRlIGxvZ2dlcjogTG9nZ2VyU2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSBjZDogQ2hhbmdlRGV0ZWN0b3JSZWZcbiAgICApIHt9XG5cbiAgICBwdWJsaWMgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMudmFsaWRhdGVSYWRpb0J1dHRvbnNHcm91cFZhbHVlKCk7XG4gICAgICAgIHRoaXMuZm9ybSA9IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgY3JpdGljYWxUaHJlc2hvbGRWYWx1ZTogW1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmNyaXRpY2FsVGhyZXNob2xkVmFsdWUgfHwgMCxcbiAgICAgICAgICAgICAgICAgICAgW1ZhbGlkYXRvcnMucmVxdWlyZWRdLFxuICAgICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgICAgd2FybmluZ1RocmVzaG9sZFZhbHVlOiBbdGhpcy53YXJuaW5nVGhyZXNob2xkVmFsdWUgfHwgMF0sXG4gICAgICAgICAgICAgICAgc2hvd1RocmVzaG9sZHM6IFtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zaG93VGhyZXNob2xkcyB8fCBmYWxzZSxcbiAgICAgICAgICAgICAgICAgICAgW1ZhbGlkYXRvcnMucmVxdWlyZWRdLFxuICAgICAgICAgICAgICAgIF0sXG4gICAgICAgICAgICAgICAgcmV2ZXJzZWRUaHJlc2hvbGRzOiBbdGhpcy5yZXZlcnNlZFRocmVzaG9sZHMgfHwgZmFsc2VdLFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICB2YWxpZGF0b3JzOiB0aHJlc2hvbGRzVmFsaWRhdG9yLFxuICAgICAgICAgICAgfVxuICAgICAgICApO1xuXG4gICAgICAgIHRoaXMuaGFuZGxlV2FybmluZ1RocmVzaG9sZCh0aGlzLndhcm5pbmdUaHJlc2hvbGRWYWx1ZSk7XG5cbiAgICAgICAgdGhpcy5mb3JtLnN0YXR1c0NoYW5nZXNcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKGNoYW5nZSkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuZm9ybS5tYXJrQWxsQXNUb3VjaGVkKCk7XG4gICAgICAgICAgICAgICAgdGhpcy5jZC5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmZvcm1SZWFkeS5lbWl0KHRoaXMuZm9ybSk7XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgZm9ybUtleXMgPSBbXG4gICAgICAgICAgICBcImNyaXRpY2FsVGhyZXNob2xkVmFsdWVcIixcbiAgICAgICAgICAgIFwid2FybmluZ1RocmVzaG9sZFZhbHVlXCIsXG4gICAgICAgICAgICBcInNob3dUaHJlc2hvbGRzXCIsXG4gICAgICAgICAgICBcInJldmVyc2VkVGhyZXNob2xkc1wiLFxuICAgICAgICBdO1xuXG4gICAgICAgIGlmIChjaGFuZ2VzKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGtleSBvZiBPYmplY3Qua2V5cyhjaGFuZ2VzKSkge1xuICAgICAgICAgICAgICAgIGlmIChpbmNsdWRlcyhmb3JtS2V5cywga2V5KSkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCB2YWx1ZSA9IGNoYW5nZXNba2V5XS5jdXJyZW50VmFsdWU7XG4gICAgICAgICAgICAgICAgICAgIGlmICh2YWx1ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5mb3JtPy5wYXRjaFZhbHVlKHsgW2tleV06IHZhbHVlIH0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoY2hhbmdlcy5zaG93VGhyZXNob2xkcykge1xuICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4gdGhpcy52YWxpZGF0ZVJhZGlvQnV0dG9uc0dyb3VwVmFsdWUoKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY2hhbmdlcy53YXJuaW5nVGhyZXNob2xkVmFsdWUpIHtcbiAgICAgICAgICAgIHRoaXMuaGFuZGxlV2FybmluZ1RocmVzaG9sZChcbiAgICAgICAgICAgICAgICBjaGFuZ2VzLndhcm5pbmdUaHJlc2hvbGRWYWx1ZS5jdXJyZW50VmFsdWVcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0VGhyZXNob2xkc1N1YnRpdGxlKHNob3dUaHJlc2hvbGQ6IGJvb2xlYW4pOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gc2hvd1RocmVzaG9sZFxuICAgICAgICAgICAgPyAkbG9jYWxpemVgQ3VzdG9tIHRocmVzaG9sZHNgXG4gICAgICAgICAgICA6ICRsb2NhbGl6ZWBObyB0aHJlc2hvbGRzYDtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGVzdHJveSQubmV4dCgpO1xuICAgICAgICB0aGlzLmRlc3Ryb3kkLmNvbXBsZXRlKCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSB2YWxpZGF0ZVJhZGlvQnV0dG9uc0dyb3VwVmFsdWUoKSB7XG4gICAgICAgIHRoaXMucmFkaW9CdXR0b25Hcm91cFZhbHVlID0gdGhpcy5zaG93VGhyZXNob2xkcztcbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZVdhcm5pbmdUaHJlc2hvbGQodmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZCkge1xuICAgICAgICBjb25zdCB3YXJuaW5nVGhyZXNob2xkRm9ybUNvbnRyb2wgPSB0aGlzLmZvcm0/LmdldChcbiAgICAgICAgICAgIFwid2FybmluZ1RocmVzaG9sZFZhbHVlXCJcbiAgICAgICAgKTtcbiAgICAgICAgaWYgKF9pc05pbCh2YWx1ZSkpIHtcbiAgICAgICAgICAgIHdhcm5pbmdUaHJlc2hvbGRGb3JtQ29udHJvbD8uc2V0VmFsdWUobnVsbCk7XG4gICAgICAgIH1cbiAgICB9XG59XG4iLCI8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uXG4gICAgW2Zvcm1Hcm91cF09XCJmb3JtXCJcbiAgICBbc3RhdGVdPVwiZm9ybSB8IG51aVdpZGdldEVkaXRvckFjY29yZGlvbkZvcm1TdGF0ZSB8IGFzeW5jXCJcbj5cbiAgICA8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uLWhlYWRlclxuICAgICAgICBbaGVhZGVySWNvbl09XCJmb3JtIHwgbnVpRm9ybUhlYWRlckljb25QaXBlIDogJ3RocmVzaG9sZHMnIHwgYXN5bmNcIlxuICAgICAgICBbc3VidGl0bGVdPVwiZ2V0VGhyZXNob2xkc1N1YnRpdGxlKGZvcm0uZ2V0KCdzaG93VGhyZXNob2xkcycpPy52YWx1ZSlcIlxuICAgICAgICBoZWFkZXJUaXRsZT1cIlRocmVzaG9sZHNcIlxuICAgIC8+XG4gICAgPGRpdiBjbGFzcz1cImtwaS10aHJlc2hvbGRzLWNvbmZpZ3VyYXRpb25fX2FjY29yZGlvbi1jb250ZW50XCI+XG4gICAgICAgIDxudWktZm9ybS1maWVsZFxuICAgICAgICAgICAgW2NvbnRyb2xdPVwiZm9ybS5nZXQoJ3Nob3dUaHJlc2hvbGRzJylcIlxuICAgICAgICAgICAgY2FwdGlvbj1cIkhvdyBkbyB5b3Ugd2FudCB0byBzZXQgd2FybmluZy9jcml0aWNhbCB0aHJlc2hvbGRzP1wiXG4gICAgICAgICAgICBpMThuLWNhcHRpb25cbiAgICAgICAgPlxuICAgICAgICAgICAgPG51aS1yYWRpby1ncm91cFxuICAgICAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cInNob3dUaHJlc2hvbGRzXCJcbiAgICAgICAgICAgICAgICBbdmFsdWVdPVwicmFkaW9CdXR0b25Hcm91cFZhbHVlXCJcbiAgICAgICAgICAgICAgICBuYW1lPVwic2hvd1RocmVzaG9sZHNcIlxuICAgICAgICAgICAgICAgIHJlcXVpcmVkXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPG51aS1yYWRpb1xuICAgICAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgdGhyZXNob2xkT3B0aW9uIG9mIHRocmVzaG9sZE9wdGlvbnNcIlxuICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwidGhyZXNob2xkT3B0aW9uLnZhbHVlXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIHt7IHRocmVzaG9sZE9wdGlvbi5kaXNwbGF5VmFsdWUgfX1cbiAgICAgICAgICAgICAgICA8L251aS1yYWRpbz5cbiAgICAgICAgICAgIDwvbnVpLXJhZGlvLWdyb3VwPlxuICAgICAgICAgICAgPG51aS12YWxpZGF0aW9uLW1lc3NhZ2UgZm9yPVwicmVxdWlyZWRcIiBpMThuPlxuICAgICAgICAgICAgICAgIFRoaXMgZmllbGQgaXMgcmVxdWlyZWRcbiAgICAgICAgICAgIDwvbnVpLXZhbGlkYXRpb24tbWVzc2FnZT5cbiAgICAgICAgPC9udWktZm9ybS1maWVsZD5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibXQtNFwiICpuZ0lmPVwiZm9ybS5nZXQoJ3Nob3dUaHJlc2hvbGRzJyk/LnZhbHVlXCI+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiPlxuICAgICAgICAgICAgICAgIDxudWktZm9ybS1maWVsZFxuICAgICAgICAgICAgICAgICAgICBjYXB0aW9uPVwiV2FybmluZyB2YWx1ZVwiXG4gICAgICAgICAgICAgICAgICAgIGkxOG4tY2FwdGlvblxuICAgICAgICAgICAgICAgICAgICBbY29udHJvbF09XCJmb3JtLmdldCgnd2FybmluZ1RocmVzaG9sZFZhbHVlJylcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgPG51aS10ZXh0Ym94LW51bWJlclxuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwid2FybmluZ1RocmVzaG9sZFZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiTmV2ZXIgc2hvdyB0aGUgd2FybmluZyBzdGF0ZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICBpMThuLXBsYWNlaG9sZGVyXG4gICAgICAgICAgICAgICAgICAgID48L251aS10ZXh0Ym94LW51bWJlcj5cbiAgICAgICAgICAgICAgICAgICAgPG51aS12YWxpZGF0aW9uLW1lc3NhZ2UgZm9yPVwicmVxdWlyZWRcIiBpMThuPlxuICAgICAgICAgICAgICAgICAgICAgICAgVGhpcyBmaWVsZCBpcyByZXF1aXJlZFxuICAgICAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICAgICAgICAgIDxudWktdmFsaWRhdGlvbi1tZXNzYWdlIGZvcj1cIm1pblwiIGkxOG4+XG4gICAgICAgICAgICAgICAgICAgICAgICBXYXJuaW5nIHRocmVzaG9sZCBzaG91bGQgYmUgZ3JlYXRlciB0aGFuIGNyaXRpY2FsXG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJlc2hvbGRcbiAgICAgICAgICAgICAgICAgICAgPC9udWktdmFsaWRhdGlvbi1tZXNzYWdlPlxuICAgICAgICAgICAgICAgICAgICA8bnVpLXZhbGlkYXRpb24tbWVzc2FnZSBmb3I9XCJtYXhcIiBpMThuPlxuICAgICAgICAgICAgICAgICAgICAgICAgV2FybmluZyB0aHJlc2hvbGQgc2hvdWxkIGJlIGxvd2VyIHRoYW4gY3JpdGljYWxcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZFxuICAgICAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICAgICAgPC9udWktZm9ybS1maWVsZD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiPlxuICAgICAgICAgICAgICAgIDxudWktZm9ybS1maWVsZFxuICAgICAgICAgICAgICAgICAgICBjYXB0aW9uPVwiQ3JpdGljYWwgdmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICBpMThuLWNhcHRpb25cbiAgICAgICAgICAgICAgICAgICAgW2NvbnRyb2xdPVwiZm9ybS5nZXQoJ2NyaXRpY2FsVGhyZXNob2xkVmFsdWUnKVwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICA8bnVpLXRleHRib3gtbnVtYmVyXG4gICAgICAgICAgICAgICAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJjcml0aWNhbFRocmVzaG9sZFZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU2V0IGNyaXRpY2FsIHZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIGkxOG4tcGxhY2Vob2xkZXJcbiAgICAgICAgICAgICAgICAgICAgPjwvbnVpLXRleHRib3gtbnVtYmVyPlxuICAgICAgICAgICAgICAgICAgICA8bnVpLXZhbGlkYXRpb24tbWVzc2FnZSBmb3I9XCJyZXF1aXJlZFwiIGkxOG4+XG4gICAgICAgICAgICAgICAgICAgICAgICBUaGlzIGZpZWxkIGlzIHJlcXVpcmVkXG4gICAgICAgICAgICAgICAgICAgIDwvbnVpLXZhbGlkYXRpb24tbWVzc2FnZT5cbiAgICAgICAgICAgICAgICAgICAgPG51aS12YWxpZGF0aW9uLW1lc3NhZ2UgZm9yPVwibWluXCIgaTE4bj5cbiAgICAgICAgICAgICAgICAgICAgICAgIENyaXRpY2FsIHRocmVzaG9sZCBzaG91bGQgYmUgZ3JlYXRlciB0aGFuIHdhcm5pbmdcbiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVzaG9sZFxuICAgICAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICAgICAgICAgIDxudWktdmFsaWRhdGlvbi1tZXNzYWdlIGZvcj1cIm1heFwiIGkxOG4+XG4gICAgICAgICAgICAgICAgICAgICAgICBDcml0aWNhbCB0aHJlc2hvbGQgc2hvdWxkIGJlIGxvd2VyIHRoYW4gd2FybmluZ1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhyZXNob2xkXG4gICAgICAgICAgICAgICAgICAgIDwvbnVpLXZhbGlkYXRpb24tbWVzc2FnZT5cbiAgICAgICAgICAgICAgICA8L251aS1mb3JtLWZpZWxkPlxuICAgICAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJtYi00XCI+XG4gICAgICAgICAgICAgICAgPG51aS1mb3JtLWZpZWxkXG4gICAgICAgICAgICAgICAgICAgIFtjb250cm9sXT1cImZvcm0uZ2V0KCdyZXZlcnNlZFRocmVzaG9sZHMnKVwiXG4gICAgICAgICAgICAgICAgICAgIFtzaG93T3B0aW9uYWxUZXh0XT1cImZhbHNlXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIDxudWktY2hlY2tib3ggZm9ybUNvbnRyb2xOYW1lPVwicmV2ZXJzZWRUaHJlc2hvbGRzXCIgcmVxdWlyZWQ+XG4gICAgICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cIm51aS10ZXh0LWxhYmVsXCIgaTE4blxuICAgICAgICAgICAgICAgICAgICAgICAgICAgID5XYXJuaW5nIGFuZCBjcml0aWNhbCB3aGVuIGJlbG93IHRocmVzaG9sZFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlczwvc3BhblxuICAgICAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICA8L251aS1jaGVja2JveD5cbiAgICAgICAgICAgICAgICAgICAgPG51aS12YWxpZGF0aW9uLW1lc3NhZ2UgZm9yPVwicmVxdWlyZWRcIiBpMThuPlxuICAgICAgICAgICAgICAgICAgICAgICAgVGhpcyBmaWVsZCBpcyByZXF1aXJlZFxuICAgICAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICAgICAgPC9udWktZm9ybS1maWVsZD5cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICA8L2Rpdj5cbiAgICA8L2Rpdj5cbjwvbnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uPlxuIl19