@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
167 lines • 44.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, Inject, Input, Output, } from "@angular/core";
import { FormBuilder } from "@angular/forms";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { EventBus } from "@nova-ui/bits";
import { ProportionalWidgetChartTypes } from "../../../../../components/proportional-widget/types";
import { ProportionalContentAggregatorsRegistryService } from "../../../../../services/proportional-content-aggregators-registry.service";
import { ProportionalDonutContentFormattersRegistryService } from "../../../../../services/table-formatter-registry.service";
import { PIZZAGNA_EVENT_BUS } from "../../../../../types";
import { DATA_SOURCE_CREATED } from "../../../../types";
import * as i0 from "@angular/core";
import * as i1 from "@angular/forms";
import * as i2 from "../../../../../services/table-formatter-registry.service";
import * as i3 from "../../../../../services/proportional-content-aggregators-registry.service";
import * as i4 from "@angular/common";
import * as i5 from "@angular/cdk/portal";
import * as i6 from "../../../../../pizzagna/directives/component-portal/component-portal.directive";
import * as i7 from "@nova-ui/bits";
import * as i8 from "../../../widget-editor-accordion/widget-editor-accordion.component";
import * as i9 from "../../../../pipe/widget-editor-accordion-form-state.pipe";
import * as i10 from "../../../../pipe/widget-editor-accordion-header-icon.pipe";
export class DonutContentConfigurationComponent {
static { this.lateLoadKey = "DonutContentConfigurationComponent"; }
constructor(formBuilder, changeDetector, eventBus, contentFormattersRegistry, aggregatorRegistry) {
this.formBuilder = formBuilder;
this.changeDetector = changeDetector;
this.eventBus = eventBus;
this.contentFormattersRegistry = contentFormattersRegistry;
this.aggregatorRegistry = aggregatorRegistry;
this.formReady = new EventEmitter();
this.shown = true;
this.form = this.formBuilder.group({
formatter: this.formBuilder.group({
componentType: [""],
properties: [],
}),
aggregator: this.formBuilder.group({
aggregatorType: [""],
properties: [],
}),
});
this.dataFields = [];
this.destroy$ = new Subject();
}
ngOnInit() {
this.watchFormChanges();
this.setFromRegistry();
this.eventBus
.getStream(DATA_SOURCE_CREATED)
.pipe(takeUntil(this.destroy$))
.subscribe((event) => {
const dataSource = event.payload;
if (dataSource) {
dataSource.dataFieldsConfig?.dataFields$
.pipe(takeUntil(this.destroy$))
.subscribe((dataFields) => {
this.dataFields = dataFields;
this.updateAggregatorConfiguratorProps();
});
}
});
this.formReady.emit(this.form);
}
ngOnChanges(changes) {
if (changes.formatter) {
this.form.get("formatter")?.patchValue({
...changes.formatter.currentValue,
});
}
if (changes.aggregator) {
this.form.get("aggregator")?.patchValue({
...changes.aggregator.currentValue,
});
}
if (changes.chartType) {
const chartType = changes.chartType.currentValue;
this.shown = chartType === ProportionalWidgetChartTypes.DonutChart;
}
}
ngOnDestroy() {
this.destroy$.complete();
}
onFormReady(formControlName, form) {
this.form.get(formControlName).setControl("properties", form);
}
get subtitleText() {
return `${this.currentAggregatorDefinition?.label}, ${this.currentFormatterDefinition?.label}`;
}
setFromRegistry() {
this.formatters = this.contentFormattersRegistry.getItems();
this.aggregators = this.aggregatorRegistry.getItems();
let formatterToSet = this.formatters.find((formatter) => formatter.componentType === this.formatter?.componentType);
if (!formatterToSet) {
formatterToSet = this.formatters[0];
}
this.form
.get("formatter.componentType")
?.setValue(formatterToSet.componentType);
let aggregatorToSet = this.aggregators.find((aggregator) => aggregator.aggregatorType === this.aggregator?.aggregatorType);
if (!aggregatorToSet) {
aggregatorToSet = this.aggregators[0];
}
this.form
.get("aggregator.aggregatorType")
?.setValue(aggregatorToSet.aggregatorType);
}
watchFormChanges() {
this.form
.get("aggregator.aggregatorType")
?.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((type) => {
this.currentAggregatorDefinition = this.aggregators.find(({ aggregatorType }) => aggregatorType === type);
this.updateAggregatorConfiguratorProps();
});
this.form
.get("formatter.componentType")
?.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((type) => {
this.currentFormatterDefinition = this.formatters.find(({ componentType }) => componentType === type);
this.updateAggregatorConfiguratorProps();
});
}
updateAggregatorConfiguratorProps() {
this.aggregatorConfiguratorProps = {
...this.currentAggregatorDefinition?.properties,
...this.aggregator?.properties,
metrics: this.dataFields,
};
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DonutContentConfigurationComponent, deps: [{ token: i1.FormBuilder }, { token: i0.ChangeDetectorRef }, { token: PIZZAGNA_EVENT_BUS }, { token: i2.ProportionalDonutContentFormattersRegistryService }, { token: i3.ProportionalContentAggregatorsRegistryService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DonutContentConfigurationComponent, selector: "nui-donut-content-configuration", inputs: { chartType: "chartType", formatter: "formatter", aggregator: "aggregator" }, outputs: { formReady: "formReady" }, usesOnChanges: true, ngImport: i0, template: "<ng-container *ngIf=\"shown\" [formGroup]=\"form\">\n <nui-widget-editor-accordion\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n >\n <div accordionHeader class=\"d-flex align-items-center px-4 py-2\">\n <nui-icon\n class=\"align-self-start pt-2\"\n [icon]=\"form | nuiFormHeaderIconPipe : 'customize' | async\"\n ></nui-icon>\n <div class=\"d-flex flex-column ml-4 pt-1\">\n <span class=\"nui-text-label\" i18n\n >Donut Content Configuration</span\n >\n <div class=\"nui-text-secondary\" [title]=\"subtitleText\">\n {{ subtitleText }}\n </div>\n </div>\n </div>\n\n <div class=\"donut-content-configuration__accordion-content\">\n <!-- aggregator -->\n <div class=\"mb-4\" formGroupName=\"aggregator\">\n <nui-form-field\n caption=\"Aggregate\"\n i18n-caption\n [control]=\"form.get('aggregator.aggregatorType')\"\n >\n <nui-select-v2\n placeholder=\"Select Format\"\n i18n-placeholder\n formControlName=\"aggregatorType\"\n >\n <nui-select-v2-option\n *ngFor=\"let aggregator of aggregators\"\n [displayValueContext]=\"aggregator.label\"\n [value]=\"aggregator.aggregatorType\"\n >\n {{ aggregator.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n </div>\n\n <!-- formatter -->\n <div class=\"mb-4\" formGroupName=\"formatter\">\n <nui-form-field\n caption=\"Format\"\n i18n-caption\n [control]=\"form.get('formatter.componentType')\"\n >\n <nui-select-v2\n placeholder=\"Select Format\"\n i18n-placeholder\n formControlName=\"componentType\"\n >\n <nui-select-v2-option\n *ngFor=\"let formatter of formatters\"\n [displayValueContext]=\"formatter.label\"\n [value]=\"formatter.componentType\"\n >\n {{ formatter.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n </div>\n\n <!-- aggregator configuration component -->\n <ng-container\n *ngIf=\"currentAggregatorDefinition?.configurationComponent\"\n nuiComponentPortal\n componentId=\"formatterConfigurator\"\n [componentType]=\"\n currentAggregatorDefinition?.configurationComponent\n \"\n [properties]=\"aggregatorConfiguratorProps\"\n [outputs]=\"['formReady']\"\n (output)=\"onFormReady('aggregator', $event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n\n <!-- formatter configuration component -->\n <ng-container\n *ngIf=\"currentFormatterDefinition?.configurationComponent\"\n nuiComponentPortal\n componentId=\"formatterConfigurator\"\n [componentType]=\"\n currentFormatterDefinition?.configurationComponent\n \"\n [properties]=\"formatterConfiguratorProps\"\n [outputs]=\"['formReady']\"\n (output)=\"onFormReady('formatter', $event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n </div>\n </nui-widget-editor-accordion>\n</ng-container>\n", styles: [".donut-content-configuration__accordion-content{padding:15px 15px 15px 46px}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { 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: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i5.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i6.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "component", type: i7.IconComponent, selector: "nui-icon", inputs: ["iconColor", "brushType", "iconHoverColor", "iconSize", "cssClass", "fillContainer", "status", "childStatus", "icon", "counter"] }, { kind: "component", type: i7.FormFieldComponent, selector: "nui-form-field", inputs: ["control", "caption", "info", "infoTemplate", "hint", "hintTemplate", "customBoxWidth", "showOptionalText"] }, { kind: "component", type: i7.SelectV2Component, selector: "nui-select-v2", inputs: ["displayValueTemplate"] }, { kind: "component", type: i7.SelectV2OptionComponent, selector: "nui-select-v2-option", inputs: ["value", "index", "displayValueContext", "outfiltered"] }, { kind: "component", type: i8.WidgetEditorAccordionComponent, selector: "nui-widget-editor-accordion", inputs: ["showOpenStateIcon", "state"], outputs: ["openToggle"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i9.WidgetEditorAccordionFormStatePipe, name: "nuiWidgetEditorAccordionFormState" }, { kind: "pipe", type: i10.FormHeaderIconPipePipe, name: "nuiFormHeaderIconPipe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DonutContentConfigurationComponent, decorators: [{
type: Component,
args: [{ selector: "nui-donut-content-configuration", changeDetection: ChangeDetectionStrategy.OnPush, template: "<ng-container *ngIf=\"shown\" [formGroup]=\"form\">\n <nui-widget-editor-accordion\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n >\n <div accordionHeader class=\"d-flex align-items-center px-4 py-2\">\n <nui-icon\n class=\"align-self-start pt-2\"\n [icon]=\"form | nuiFormHeaderIconPipe : 'customize' | async\"\n ></nui-icon>\n <div class=\"d-flex flex-column ml-4 pt-1\">\n <span class=\"nui-text-label\" i18n\n >Donut Content Configuration</span\n >\n <div class=\"nui-text-secondary\" [title]=\"subtitleText\">\n {{ subtitleText }}\n </div>\n </div>\n </div>\n\n <div class=\"donut-content-configuration__accordion-content\">\n <!-- aggregator -->\n <div class=\"mb-4\" formGroupName=\"aggregator\">\n <nui-form-field\n caption=\"Aggregate\"\n i18n-caption\n [control]=\"form.get('aggregator.aggregatorType')\"\n >\n <nui-select-v2\n placeholder=\"Select Format\"\n i18n-placeholder\n formControlName=\"aggregatorType\"\n >\n <nui-select-v2-option\n *ngFor=\"let aggregator of aggregators\"\n [displayValueContext]=\"aggregator.label\"\n [value]=\"aggregator.aggregatorType\"\n >\n {{ aggregator.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n </div>\n\n <!-- formatter -->\n <div class=\"mb-4\" formGroupName=\"formatter\">\n <nui-form-field\n caption=\"Format\"\n i18n-caption\n [control]=\"form.get('formatter.componentType')\"\n >\n <nui-select-v2\n placeholder=\"Select Format\"\n i18n-placeholder\n formControlName=\"componentType\"\n >\n <nui-select-v2-option\n *ngFor=\"let formatter of formatters\"\n [displayValueContext]=\"formatter.label\"\n [value]=\"formatter.componentType\"\n >\n {{ formatter.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n </div>\n\n <!-- aggregator configuration component -->\n <ng-container\n *ngIf=\"currentAggregatorDefinition?.configurationComponent\"\n nuiComponentPortal\n componentId=\"formatterConfigurator\"\n [componentType]=\"\n currentAggregatorDefinition?.configurationComponent\n \"\n [properties]=\"aggregatorConfiguratorProps\"\n [outputs]=\"['formReady']\"\n (output)=\"onFormReady('aggregator', $event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n\n <!-- formatter configuration component -->\n <ng-container\n *ngIf=\"currentFormatterDefinition?.configurationComponent\"\n nuiComponentPortal\n componentId=\"formatterConfigurator\"\n [componentType]=\"\n currentFormatterDefinition?.configurationComponent\n \"\n [properties]=\"formatterConfiguratorProps\"\n [outputs]=\"['formReady']\"\n (output)=\"onFormReady('formatter', $event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n ></ng-template>\n </ng-container>\n </div>\n </nui-widget-editor-accordion>\n</ng-container>\n", styles: [".donut-content-configuration__accordion-content{padding:15px 15px 15px 46px}\n"] }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i0.ChangeDetectorRef }, { type: i7.EventBus, decorators: [{
type: Inject,
args: [PIZZAGNA_EVENT_BUS]
}] }, { type: i2.ProportionalDonutContentFormattersRegistryService }, { type: i3.ProportionalContentAggregatorsRegistryService }], propDecorators: { chartType: [{
type: Input
}], formatter: [{
type: Input
}], aggregator: [{
type: Input
}], formReady: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZG9udXQtY29udGVudC1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29uZmlndXJhdG9yL2NvbXBvbmVudHMvd2lkZ2V0cy9wcm9wb3J0aW9uYWwvZG9udXQtY29udGVudC1jb25maWd1cmF0aW9uL2RvbnV0LWNvbnRlbnQtY29uZmlndXJhdGlvbi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvcHJvcG9ydGlvbmFsL2RvbnV0LWNvbnRlbnQtY29uZmlndXJhdGlvbi9kb251dC1jb250ZW50LWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseURBQXlEO0FBQ3pELEVBQUU7QUFDRiwrRUFBK0U7QUFDL0UsNEVBQTRFO0FBQzVFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsOEVBQThFO0FBQzlFLDREQUE0RDtBQUM1RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLHVEQUF1RDtBQUN2RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDRFQUE0RTtBQUM1RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsaUJBQWlCO0FBRWpCLE9BQU8sRUFDSCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxZQUFZLEVBQ1osTUFBTSxFQUNOLEtBQUssRUFJTCxNQUFNLEdBRVQsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxNQUFNLGdCQUFnQixDQUFDO0FBQ3hELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTNDLE9BQU8sRUFBRSxR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Q0FBQyxJQUFJLENBQ2xELENBQUMsRUFBRSxhQUFhLEVBQUUsRUFBRSxFQUFFLENBQUMsYUFBYSxLQUFLLElBQUksQ0FDeEIsQ0FBQztZQUMxQixJQUFJLENBQUMsaUNBQWlDLEVBQUUsQ0FBQztRQUM3QyxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7SUFFTyxpQ0FBaUM7UUFDckMsSUFBSSxDQUFDLDJCQUEyQixHQUFHO1lBQy9CLEdBQUcsSUFBSSxDQUFDLDJCQUEyQixFQUFFLFVBQVU7WUFDL0MsR0FBRyxJQUFJLENBQUMsVUFBVSxFQUFFLFVBQVU7WUFDOUIsT0FBTyxFQUFFLElBQUksQ0FBQyxVQUFVO1NBQzNCLENBQUM7SUFDTixDQUFDO2tJQTNKUSxrQ0FBa0MsOEVBdUMvQixrQkFBa0I7c0hBdkNyQixrQ0FBa0MsdU5DN0QvQyx5Z0pBMEdBOzs0RkQ3Q2Esa0NBQWtDO2tCQU45QyxTQUFTOytCQUNJLGlDQUFpQyxtQkFHMUIsdUJBQXVCLENBQUMsTUFBTTs7MEJBeUMxQyxNQUFNOzJCQUFDLGtCQUFrQjtxS0FsQ2QsU0FBUztzQkFBeEIsS0FBSztnQkFDVSxTQUFTO3NCQUF4QixLQUFLO2dCQUNVLFVBQVU7c0JBQXpCLEtBQUs7Z0JBRVcsU0FBUztzQkFBekIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICAgIENoYW5nZURldGVjdG9yUmVmLFxuICAgIENvbXBvbmVudCxcbiAgICBFdmVudEVtaXR0ZXIsXG4gICAgSW5qZWN0LFxuICAgIElucHV0LFxuICAgIE9uQ2hhbmdlcyxcbiAgICBPbkRlc3Ryb3ksXG4gICAgT25Jbml0LFxuICAgIE91dHB1dCxcbiAgICBTaW1wbGVDaGFuZ2VzLFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgRm9ybUJ1aWxkZXIsIEZvcm1Hcm91cCB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gXCJyeGpzXCI7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHsgRXZlbnRCdXMsIElEYXRhRmllbGQsIElEYXRhU291cmNlLCBJRXZlbnQgfSBmcm9tIFwiQG5vdmEtdWkvYml0c1wiO1xuXG5pbXBvcnQgeyBQcm9wb3J0aW9uYWxXaWRnZXRDaGFydFR5cGVzIH0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL2NvbXBvbmVudHMvcHJvcG9ydGlvbmFsLXdpZGdldC90eXBlc1wiO1xuaW1wb3J0IHtcbiAgICBJRm9ybWF0dGVyLFxuICAgIElGb3JtYXR0ZXJDb25maWd1cmF0b3IsXG4gICAgSUZvcm1hdHRlckRlZmluaXRpb24sXG59IGZyb20gXCIuLi8uLi8uLi8uLi8uLi9jb21wb25lbnRzL3R5cGVzXCI7XG5pbXBvcnQge1xuICAgIElQcm9wb3J0aW9uYWxEb251dENvbnRlbnRBZ2dyZWdhdG9yLFxuICAgIElQcm9wb3J0aW9uYWxEb251dENvbnRlbnRBZ2dyZWdhdG9yRGVmaW5pdGlvbixcbn0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL2Z1bmN0aW9ucy9wcm9wb3J0aW9uYWwtYWdncmVnYXRvcnMvdHlwZXNcIjtcbmltcG9ydCB7IFByb3BvcnRpb25hbENvbnRlbnRBZ2dyZWdhdG9yc1JlZ2lzdHJ5U2VydmljZSB9IGZyb20gXCIuLi8uLi8uLi8uLi8uLi9zZXJ2aWNlcy9wcm9wb3J0aW9uYWwtY29udGVudC1hZ2dyZWdhdG9ycy1yZWdpc3RyeS5zZXJ2aWNlXCI7XG5pbXBvcnQgeyBQcm9wb3J0aW9uYWxEb251dENvbnRlbnRGb3JtYXR0ZXJzUmVnaXN0cnlTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL3NlcnZpY2VzL3RhYmxlLWZvcm1hdHRlci1yZWdpc3RyeS5zZXJ2aWNlXCI7XG5pbXBvcnQgeyBJSGFzQ2hhbmdlRGV0ZWN0b3IsIFBJWlpBR05BX0VWRU5UX0JVUyB9IGZyb20gXCIuLi8uLi8uLi8uLi8uLi90eXBlc1wiO1xuaW1wb3J0IHsgREFUQV9TT1VSQ0VfQ1JFQVRFRCB9IGZyb20gXCIuLi8uLi8uLi8uLi90eXBlc1wiO1xuaW1wb3J0IHsgSUFnZ3JlZ2F0b3JDb25maWd1cmF0b3JQcm9wZXJ0aWVzIH0gZnJvbSBcIi4uL2FnZ3JlZ2F0b3JzLWNvbmZpZ3VyYXRvcnMvYWdncmVnYXRvci1jb25maWd1cmF0b3IvYWdncmVnYXRvci1jb25maWd1cmF0b3IuY29tcG9uZW50XCI7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiBcIm51aS1kb251dC1jb250ZW50LWNvbmZpZ3VyYXRpb25cIixcbiAgICB0ZW1wbGF0ZVVybDogXCIuL2RvbnV0LWNvbnRlbnQtY29uZmlndXJhdGlvbi5jb21wb25lbnQuaHRtbFwiLFxuICAgIHN0eWxlVXJsczogW1wiLi9kb251dC1jb250ZW50LWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIERvbnV0Q29udGVudENvbmZpZ3VyYXRpb25Db21wb25lbnRcbiAgICBpbXBsZW1lbnRzIE9uSW5pdCwgT25DaGFuZ2VzLCBJSGFzQ2hhbmdlRGV0ZWN0b3IsIE9uRGVzdHJveVxue1xuICAgIHN0YXRpYyBsYXRlTG9hZEtleSA9IFwiRG9udXRDb250ZW50Q29uZmlndXJhdGlvbkNvbXBvbmVudFwiO1xuXG4gICAgQElucHV0KCkgcHVibGljIGNoYXJ0VHlwZTogUHJvcG9ydGlvbmFsV2lkZ2V0Q2hhcnRUeXBlcztcbiAgICBASW5wdXQoKSBwdWJsaWMgZm9ybWF0dGVyOiBJRm9ybWF0dGVyO1xuICAgIEBJbnB1dCgpIHB1YmxpYyBhZ2dyZWdhdG9yOiBJUHJvcG9ydGlvbmFsRG9udXRDb250ZW50QWdncmVnYXRvcjtcblxuICAgIEBPdXRwdXQoKSBwdWJsaWMgZm9ybVJlYWR5ID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG4gICAgcHVibGljIHNob3duOiBib29sZWFuID0gdHJ1ZTtcbiAgICBwdWJsaWMgZm9ybSA9IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoe1xuICAgICAgICBmb3JtYXR0ZXI6IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoe1xuICAgICAgICAgICAgY29tcG9uZW50VHlwZTogW1wiXCJdLFxuICAgICAgICAgICAgcHJvcGVydGllczogW10sXG4gICAgICAgIH0pLFxuICAgICAgICBhZ2dyZWdhdG9yOiB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKHtcbiAgICAgICAgICAgIGFnZ3JlZ2F0b3JUeXBlOiBbXCJcIl0sXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOiBbXSxcbiAgICAgICAgfSksXG4gICAgfSk7XG4gICAgcHVibGljIGZvcm1hdHRlcnM6IElGb3JtYXR0ZXJEZWZpbml0aW9uW107XG4gICAgcHVibGljIGFnZ3JlZ2F0b3JzOiBJUHJvcG9ydGlvbmFsRG9udXRDb250ZW50QWdncmVnYXRvckRlZmluaXRpb25bXTtcbiAgICBwdWJsaWMgZGF0YUZpZWxkczogSURhdGFGaWVsZFtdID0gW107XG5cbiAgICBwdWJsaWMgY3VycmVudEZvcm1hdHRlckRlZmluaXRpb246IElGb3JtYXR0ZXJEZWZpbml0aW9uIHwgdW5kZWZpbmVkO1xuICAgIHB1YmxpYyBmb3JtYXR0ZXJDb25maWd1cmF0b3JQcm9wczogSUZvcm1hdHRlckNvbmZpZ3VyYXRvcjtcblxuICAgIHB1YmxpYyBjdXJyZW50QWdncmVnYXRvckRlZmluaXRpb246XG4gICAgICAgIHwgSVByb3BvcnRpb25hbERvbnV0Q29udGVudEFnZ3JlZ2F0b3JEZWZpbml0aW9uXG4gICAgICAgIHwgdW5kZWZpbmVkO1xuICAgIHB1YmxpYyBhZ2dyZWdhdG9yQ29uZmlndXJhdG9yUHJvcHM6IElBZ2dyZWdhdG9yQ29uZmlndXJhdG9yUHJvcGVydGllcztcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgZm9ybUJ1aWxkZXI6IEZvcm1CdWlsZGVyLFxuICAgICAgICBwdWJsaWMgY2hhbmdlRGV0ZWN0b3I6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICBASW5qZWN0KFBJWlpBR05BX0VWRU5UX0JVUykgcHJpdmF0ZSBldmVudEJ1czogRXZlbnRCdXM8SUV2ZW50PixcbiAgICAgICAgcHJpdmF0ZSBjb250ZW50Rm9ybWF0dGVyc1JlZ2lzdHJ5OiBQcm9wb3J0aW9uYWxEb251dENvbnRlbnRGb3JtYXR0ZXJzUmVnaXN0cnlTZXJ2aWNlLFxuICAgICAgICBwcml2YXRlIGFnZ3JlZ2F0b3JSZWdpc3RyeTogUHJvcG9ydGlvbmFsQ29udGVudEFnZ3JlZ2F0b3JzUmVnaXN0cnlTZXJ2aWNlXG4gICAgKSB7fVxuXG4gICAgcHVibGljIG5nT25Jbml0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLndhdGNoRm9ybUNoYW5nZXMoKTtcbiAgICAgICAgdGhpcy5zZXRGcm9tUmVnaXN0cnkoKTtcblxuICAgICAgICB0aGlzLmV2ZW50QnVzXG4gICAgICAgICAgICAuZ2V0U3RyZWFtKERBVEFfU09VUkNFX0NSRUFURUQpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKChldmVudDogSUV2ZW50PElEYXRhU291cmNlPikgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGFTb3VyY2UgPSBldmVudC5wYXlsb2FkO1xuICAgICAgICAgICAgICAgIGlmIChkYXRhU291cmNlKSB7XG4gICAgICAgICAgICAgICAgICAgIGRhdGFTb3VyY2UuZGF0YUZpZWxkc0NvbmZpZz8uZGF0YUZpZWxkcyRcbiAgICAgICAgICAgICAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5zdWJzY3JpYmUoKGRhdGFGaWVsZHMpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmRhdGFGaWVsZHMgPSBkYXRhRmllbGRzO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQWdncmVnYXRvckNvbmZpZ3VyYXRvclByb3BzKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmZvcm1SZWFkeS5lbWl0KHRoaXMuZm9ybSk7XG4gICAgfVxuXG4gICAgcHVibGljIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgaWYgKGNoYW5nZXMuZm9ybWF0dGVyKSB7XG4gICAgICAgICAgICB0aGlzLmZvcm0uZ2V0KFwiZm9ybWF0dGVyXCIpPy5wYXRjaFZhbHVlKHtcbiAgICAgICAgICAgICAgICAuLi5jaGFuZ2VzLmZvcm1hdHRlci5jdXJyZW50VmFsdWUsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGFuZ2VzLmFnZ3JlZ2F0b3IpIHtcbiAgICAgICAgICAgIHRoaXMuZm9ybS5nZXQoXCJhZ2dyZWdhdG9yXCIpPy5wYXRjaFZhbHVlKHtcbiAgICAgICAgICAgICAgICAuLi5jaGFuZ2VzLmFnZ3JlZ2F0b3IuY3VycmVudFZhbHVlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY2hhbmdlcy5jaGFydFR5cGUpIHtcbiAgICAgICAgICAgIGNvbnN0IGNoYXJ0VHlwZSA9IGNoYW5nZXMuY2hhcnRUeXBlLmN1cnJlbnRWYWx1ZTtcbiAgICAgICAgICAgIHRoaXMuc2hvd24gPSBjaGFydFR5cGUgPT09IFByb3BvcnRpb25hbFdpZGdldENoYXJ0VHlwZXMuRG9udXRDaGFydDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBvbkZvcm1SZWFkeShmb3JtQ29udHJvbE5hbWU6IHN0cmluZywgZm9ybTogRm9ybUdyb3VwKTogdm9pZCB7XG4gICAgICAgICg8Rm9ybUdyb3VwPnRoaXMuZm9ybS5nZXQoZm9ybUNvbnRyb2xOYW1lKSkuc2V0Q29udHJvbChcbiAgICAgICAgICAgIFwicHJvcGVydGllc1wiLFxuICAgICAgICAgICAgZm9ybVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgc3VidGl0bGVUZXh0KCk6IHN0cmluZyB7XG4gICAgICAgIHJldHVybiBgJHt0aGlzLmN1cnJlbnRBZ2dyZWdhdG9yRGVmaW5pdGlvbj8ubGFiZWx9LCAke3RoaXMuY3VycmVudEZvcm1hdHRlckRlZmluaXRpb24/LmxhYmVsfWA7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzZXRGcm9tUmVnaXN0cnkoKSB7XG4gICAgICAgIHRoaXMuZm9ybWF0dGVycyA9IHRoaXMuY29udGVudEZvcm1hdHRlcnNSZWdpc3RyeS5nZXRJdGVtcygpO1xuICAgICAgICB0aGlzLmFnZ3JlZ2F0b3JzID0gdGhpcy5hZ2dyZWdhdG9yUmVnaXN0cnkuZ2V0SXRlbXMoKTtcblxuICAgICAgICBsZXQgZm9ybWF0dGVyVG9TZXQgPSB0aGlzLmZvcm1hdHRlcnMuZmluZChcbiAgICAgICAgICAgIChmb3JtYXR0ZXIpID0+XG4gICAgICAgICAgICAgICAgZm9ybWF0dGVyLmNvbXBvbmVudFR5cGUgPT09IHRoaXMuZm9ybWF0dGVyPy5jb21wb25lbnRUeXBlXG4gICAgICAgICk7XG4gICAgICAgIGlmICghZm9ybWF0dGVyVG9TZXQpIHtcbiAgICAgICAgICAgIGZvcm1hdHRlclRvU2V0ID0gdGhpcy5mb3JtYXR0ZXJzWzBdO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgLmdldChcImZvcm1hdHRlci5jb21wb25lbnRUeXBlXCIpXG4gICAgICAgICAgICA/LnNldFZhbHVlKGZvcm1hdHRlclRvU2V0LmNvbXBvbmVudFR5cGUpO1xuXG4gICAgICAgIGxldCBhZ2dyZWdhdG9yVG9TZXQgPSB0aGlzLmFnZ3JlZ2F0b3JzLmZpbmQoXG4gICAgICAgICAgICAoYWdncmVnYXRvcikgPT5cbiAgICAgICAgICAgICAgICBhZ2dyZWdhdG9yLmFnZ3JlZ2F0b3JUeXBlID09PSB0aGlzLmFnZ3JlZ2F0b3I/LmFnZ3JlZ2F0b3JUeXBlXG4gICAgICAgICk7XG4gICAgICAgIGlmICghYWdncmVnYXRvclRvU2V0KSB7XG4gICAgICAgICAgICBhZ2dyZWdhdG9yVG9TZXQgPSB0aGlzLmFnZ3JlZ2F0b3JzWzBdO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgLmdldChcImFnZ3JlZ2F0b3IuYWdncmVnYXRvclR5cGVcIilcbiAgICAgICAgICAgID8uc2V0VmFsdWUoYWdncmVnYXRvclRvU2V0LmFnZ3JlZ2F0b3JUeXBlKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHdhdGNoRm9ybUNoYW5nZXMoKSB7XG4gICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgLmdldChcImFnZ3JlZ2F0b3IuYWdncmVnYXRvclR5cGVcIilcbiAgICAgICAgICAgID8udmFsdWVDaGFuZ2VzLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgodHlwZTogc3RyaW5nIHwgbnVsbCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuY3VycmVudEFnZ3JlZ2F0b3JEZWZpbml0aW9uID0gdGhpcy5hZ2dyZWdhdG9ycy5maW5kKFxuICAgICAgICAgICAgICAgICAgICAoeyBhZ2dyZWdhdG9yVHlwZSB9KSA9PiBhZ2dyZWdhdG9yVHlwZSA9PT0gdHlwZVxuICAgICAgICAgICAgICAgICkgYXMgSVByb3BvcnRpb25hbERvbnV0Q29udGVudEFnZ3JlZ2F0b3JEZWZpbml0aW9uO1xuICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQWdncmVnYXRvckNvbmZpZ3VyYXRvclByb3BzKCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmZvcm1cbiAgICAgICAgICAgIC5nZXQoXCJmb3JtYXR0ZXIuY29tcG9uZW50VHlwZVwiKVxuICAgICAgICAgICAgPy52YWx1ZUNoYW5nZXMucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCh0eXBlOiBzdHJpbmcgfCBudWxsKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5jdXJyZW50Rm9ybWF0dGVyRGVmaW5pdGlvbiA9IHRoaXMuZm9ybWF0dGVycy5maW5kKFxuICAgICAgICAgICAgICAgICAgICAoeyBjb21wb25lbnRUeXBlIH0pID0+IGNvbXBvbmVudFR5cGUgPT09IHR5cGVcbiAgICAgICAgICAgICAgICApIGFzIElGb3JtYXR0ZXJEZWZpbml0aW9uO1xuICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQWdncmVnYXRvckNvbmZpZ3VyYXRvclByb3BzKCk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHVwZGF0ZUFnZ3JlZ2F0b3JDb25maWd1cmF0b3JQcm9wcygpIHtcbiAgICAgICAgdGhpcy5hZ2dyZWdhdG9yQ29uZmlndXJhdG9yUHJvcHMgPSB7XG4gICAgICAgICAgICAuLi50aGlzLmN1cnJlbnRBZ2dyZWdhdG9yRGVmaW5pdGlvbj8ucHJvcGVydGllcyxcbiAgICAgICAgICAgIC4uLnRoaXMuYWdncmVnYXRvcj8ucHJvcGVydGllcyxcbiAgICAgICAgICAgIG1ldHJpY3M6IHRoaXMuZGF0YUZpZWxkcyxcbiAgICAgICAgfTtcbiAgICB9XG59XG4iLCI8bmctY29udGFpbmVyICpuZ0lmPVwic2hvd25cIiBbZm9ybUdyb3VwXT1cImZvcm1cIj5cbiAgICA8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uXG4gICAgICAgIFtzdGF0ZV09XCJmb3JtIHwgbnVpV2lkZ2V0RWRpdG9yQWNjb3JkaW9uRm9ybVN0YXRlIHwgYXN5bmNcIlxuICAgID5cbiAgICAgICAgPGRpdiBhY2NvcmRpb25IZWFkZXIgY2xhc3M9XCJkLWZsZXggYWxpZ24taXRlbXMtY2VudGVyIHB4LTQgcHktMlwiPlxuICAgICAgICAgICAgPG51aS1pY29uXG4gICAgICAgICAgICAgICAgY2xhc3M9XCJhbGlnbi1zZWxmLXN0YXJ0IHB0LTJcIlxuICAgICAgICAgICAgICAgIFtpY29uXT1cImZvcm0gfCBudWlGb3JtSGVhZGVySWNvblBpcGUgOiAnY3VzdG9taXplJyB8IGFzeW5jXCJcbiAgICAgICAgICAgID48L251aS1pY29uPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImQtZmxleCBmbGV4LWNvbHVtbiBtbC00IHB0LTFcIj5cbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzcz1cIm51aS10ZXh0LWxhYmVsXCIgaTE4blxuICAgICAgICAgICAgICAgICAgICA+RG9udXQgQ29udGVudCBDb25maWd1cmF0aW9uPC9zcGFuXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJudWktdGV4dC1zZWNvbmRhcnlcIiBbdGl0bGVdPVwic3VidGl0bGVUZXh0XCI+XG4gICAgICAgICAgICAgICAgICAgIHt7IHN1YnRpdGxlVGV4dCB9fVxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxkaXYgY2xhc3M9XCJkb251dC1jb250ZW50LWNvbmZpZ3VyYXRpb25fX2FjY29yZGlvbi1jb250ZW50XCI+XG4gICAgICAgICAgICA8IS0tIGFnZ3JlZ2F0b3IgLS0+XG4gICAgICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiIGZvcm1Hcm91cE5hbWU9XCJhZ2dyZWdhdG9yXCI+XG4gICAgICAgICAgICAgICAgPG51aS1mb3JtLWZpZWxkXG4gICAgICAgICAgICAgICAgICAgIGNhcHRpb249XCJBZ2dyZWdhdGVcIlxuICAgICAgICAgICAgICAgICAgICBpMThuLWNhcHRpb25cbiAgICAgICAgICAgICAgICAgICAgW2NvbnRyb2xdPVwiZm9ybS5nZXQoJ2FnZ3JlZ2F0b3IuYWdncmVnYXRvclR5cGUnKVwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICA8bnVpLXNlbGVjdC12MlxuICAgICAgICAgICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJTZWxlY3QgRm9ybWF0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgIGkxOG4tcGxhY2Vob2xkZXJcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cImFnZ3JlZ2F0b3JUeXBlXCJcbiAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG51aS1zZWxlY3QtdjItb3B0aW9uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKm5nRm9yPVwibGV0IGFnZ3JlZ2F0b3Igb2YgYWdncmVnYXRvcnNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFtkaXNwbGF5VmFsdWVDb250ZXh0XT1cImFnZ3JlZ2F0b3IubGFiZWxcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFt2YWx1ZV09XCJhZ2dyZWdhdG9yLmFnZ3JlZ2F0b3JUeXBlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7eyBhZ2dyZWdhdG9yLmxhYmVsIH19XG4gICAgICAgICAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjItb3B0aW9uPlxuICAgICAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjI+XG4gICAgICAgICAgICAgICAgPC9udWktZm9ybS1maWVsZD5cbiAgICAgICAgICAgIDwvZGl2PlxuXG4gICAgICAgICAgICA8IS0tIGZvcm1hdHRlciAtLT5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJtYi00XCIgZm9ybUdyb3VwTmFtZT1cImZvcm1hdHRlclwiPlxuICAgICAgICAgICAgICAgIDxudWktZm9ybS1maWVsZFxuICAgICAgICAgICAgICAgICAgICBjYXB0aW9uPVwiRm9ybWF0XCJcbiAgICAgICAgICAgICAgICAgICAgaTE4bi1jYXB0aW9uXG4gICAgICAgICAgICAgICAgICAgIFtjb250cm9sXT1cImZvcm0uZ2V0KCdmb3JtYXR0ZXIuY29tcG9uZW50VHlwZScpXCJcbiAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgIDxudWktc2VsZWN0LXYyXG4gICAgICAgICAgICAgICAgICAgICAgICBwbGFjZWhvbGRlcj1cIlNlbGVjdCBGb3JtYXRcIlxuICAgICAgICAgICAgICAgICAgICAgICAgaTE4bi1wbGFjZWhvbGRlclxuICAgICAgICAgICAgICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwiY29tcG9uZW50VHlwZVwiXG4gICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxudWktc2VsZWN0LXYyLW9wdGlvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBmb3JtYXR0ZXIgb2YgZm9ybWF0dGVyc1wiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW2Rpc3BsYXlWYWx1ZUNvbnRleHRdPVwiZm9ybWF0dGVyLmxhYmVsXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBbdmFsdWVdPVwiZm9ybWF0dGVyLmNvbXBvbmVudFR5cGVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHt7IGZvcm1hdHRlci5sYWJlbCB9fVxuICAgICAgICAgICAgICAgICAgICAgICAgPC9udWktc2VsZWN0LXYyLW9wdGlvbj5cbiAgICAgICAgICAgICAgICAgICAgPC9udWktc2VsZWN0LXYyPlxuICAgICAgICAgICAgICAgIDwvbnVpLWZvcm0tZmllbGQ+XG4gICAgICAgICAgICA8L2Rpdj5cblxuICAgICAgICAgICAgPCEtLSBhZ2dyZWdhdG9yIGNvbmZpZ3VyYXRpb24gY29tcG9uZW50IC0tPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgICAgICpuZ0lmPVwiY3VycmVudEFnZ3JlZ2F0b3JEZWZpbml0aW9uPy5jb25maWd1cmF0aW9uQ29tcG9uZW50XCJcbiAgICAgICAgICAgICAgICBudWlDb21wb25lbnRQb3J0YWxcbiAgICAgICAgICAgICAgICBjb21wb25lbnRJZD1cImZvcm1hdHRlckNvbmZpZ3VyYXRvclwiXG4gICAgICAgICAgICAgICAgW2NvbXBvbmVudFR5cGVdPVwiXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRBZ2dyZWdhdG9yRGVmaW5pdGlvbj8uY29uZmlndXJhdGlvbkNvbXBvbmVudFxuICAgICAgICAgICAgICAgIFwiXG4gICAgICAgICAgICAgICAgW3Byb3BlcnRpZXNdPVwiYWdncmVnYXRvckNvbmZpZ3VyYXRvclByb3BzXCJcbiAgICAgICAgICAgICAgICBbb3V0cHV0c109XCJbJ2Zvcm1SZWFkeSddXCJcbiAgICAgICAgICAgICAgICAob3V0cHV0KT1cIm9uRm9ybVJlYWR5KCdhZ2dyZWdhdG9yJywgJGV2ZW50LnBheWxvYWQpXCJcbiAgICAgICAgICAgICAgICAjY29tcG9uZW50UG9ydGFsPVwibnVpQ29tcG9uZW50UG9ydGFsXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgICAgICAgW2Nka1BvcnRhbE91dGxldF09XCJjb21wb25lbnRQb3J0YWwucG9ydGFsXCJcbiAgICAgICAgICAgICAgICAgICAgKGF0dGFjaGVkKT1cImNvbXBvbmVudFBvcnRhbC5hdHRhY2hlZCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICA+PC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgICAgICA8IS0tIGZvcm1hdHRlciBjb25maWd1cmF0aW9uIGNvbXBvbmVudCAtLT5cbiAgICAgICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgICAgICAqbmdJZj1cImN1cnJlbnRGb3JtYXR0ZXJEZWZpbml0aW9uPy5jb25maWd1cmF0aW9uQ29tcG9uZW50XCJcbiAgICAgICAgICAgICAgICBudWlDb21wb25lbnRQb3J0YWxcbiAgICAgICAgICAgICAgICBjb21wb25lbnRJZD1cImZvcm1hdHRlckNvbmZpZ3VyYXRvclwiXG4gICAgICAgICAgICAgICAgW2NvbXBvbmVudFR5cGVdPVwiXG4gICAgICAgICAgICAgICAgICAgIGN1cnJlbnRGb3JtYXR0ZXJEZWZpbml0aW9uPy5jb25maWd1cmF0aW9uQ29tcG9uZW50XG4gICAgICAgICAgICAgICAgXCJcbiAgICAgICAgICAgICAgICBbcHJvcGVydGllc109XCJmb3JtYXR0ZXJDb25maWd1cmF0b3JQcm9wc1wiXG4gICAgICAgICAgICAgICAgW291dHB1dHNdPVwiWydmb3JtUmVhZHknXVwiXG4gICAgICAgICAgICAgICAgKG91dHB1dCk9XCJvbkZvcm1SZWFkeSgnZm9ybWF0dGVyJywgJGV2ZW50LnBheWxvYWQpXCJcbiAgICAgICAgICAgICAgICAjY29tcG9uZW50UG9ydGFsPVwibnVpQ29tcG9uZW50UG9ydGFsXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8bmctdGVtcGxhdGVcbiAgICAgICAgICAgICAgICAgICAgW2Nka1BvcnRhbE91dGxldF09XCJjb21wb25lbnRQb3J0YWwucG9ydGFsXCJcbiAgICAgICAgICAgICAgICAgICAgKGF0dGFjaGVkKT1cImNvbXBvbmVudFBvcnRhbC5hdHRhY2hlZCgkZXZlbnQpXCJcbiAgICAgICAgICAgICAgICA+PC9uZy10ZW1wbGF0ZT5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8L2Rpdj5cbiAgICA8L251aS13aWRnZXQtZWRpdG9yLWFjY29yZGlvbj5cbjwvbmctY29udGFpbmVyPlxuIl19