@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
198 lines • 41.4 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, Injector, Input, Output, } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import isEqual from "lodash/isEqual";
import { Subject } from "rxjs/internal/Subject";
import { take } from "rxjs/operators";
import { EventBus, LoggerService } from "@nova-ui/bits";
import { ProviderRegistryService } from "../../../../../services/provider-registry.service";
import { PIZZAGNA_EVENT_BUS, } from "../../../../../types";
import { ConfiguratorHeadingService } from "../../../../services/configurator-heading.service";
import { DATA_SOURCE_CHANGE, DATA_SOURCE_CREATED, DATA_SOURCE_OUTPUT, } from "../../../../types";
import { DataSourceErrorComponent } from "../data-source-error/data-source-error.component";
import * as i0 from "@angular/core";
import * as i1 from "../../../../services/configurator-heading.service";
import * as i2 from "@angular/forms";
import * as i3 from "../../../../../services/provider-registry.service";
import * as i4 from "@nova-ui/bits";
import * as i5 from "@angular/common";
import * as i6 from "@angular/cdk/portal";
import * as i7 from "../../../../../pizzagna/directives/component-portal/component-portal.directive";
import * as i8 from "../../../widget-editor-accordion/widget-editor-accordion.component";
import * as i9 from "../../../widget-editor-accordion/widget-editor-accordion-header/widget-editor-accordion-header.component";
import * as i10 from "../../../../pipe/widget-editor-accordion-form-state.pipe";
import * as i11 from "../../../../pipe/widget-editor-accordion-header-icon.pipe";
/**
* This is a basic implementation of a data source configuration component. In the real world scenario, this component will most likely be replaced by a
* custom one, but still can be used as a template for developing a custom specific solution.
*/
export class DataSourceConfigurationV2Component {
static { this.lateLoadKey = "DataSourceConfigurationV2Component"; }
constructor(changeDetector, configuratorHeading, formBuilder, providerRegistryService, eventBus, injector, logger) {
this.changeDetector = changeDetector;
this.configuratorHeading = configuratorHeading;
this.formBuilder = formBuilder;
this.providerRegistryService = providerRegistryService;
this.eventBus = eventBus;
this.injector = injector;
this.logger = logger;
/**
* This component shows a dropdown with options for selecting a data source, this input represents these options.
*/
this.dataSourceProviders = [];
this.errorComponent = DataSourceErrorComponent.lateLoadKey;
this.formReady = new EventEmitter();
this.hasDataSourceError = false;
// used by the Broadcaster
this.dataFieldIds = new Subject();
}
ngOnInit() {
this.form = this.formBuilder.group({
// Widget properties, form inputs and form values need to be in sync to work with generic converter
// which is why we have providerId and properties as a form group even if we don't explicitly use it in the file.
providerId: [this.providerId || "", [Validators.required]],
properties: [this.properties || {}],
dataSource: [null, [Validators.required]],
});
this.form.setValidators([
() => (this.hasDataSourceError ? { dataSourceError: true } : null),
]);
this.form
.get("dataSource")
?.valueChanges.subscribe((selectedDataSource) => {
this.form
.get("providerId")
?.setValue(selectedDataSource?.providerId);
if (selectedDataSource?.properties) {
this.form
.get("properties")
?.setValue(selectedDataSource?.properties);
}
});
this.formReady.emit(this.form);
}
ngOnChanges(changes) {
if ((changes.providerId && !changes.providerId.isFirstChange()) ||
(changes.properties && !changes.properties.isFirstChange())) {
const previousProvider = changes.providerId?.previousValue;
const previousProperties = changes.properties?.previousValue;
if (previousProvider !== this.providerId ||
previousProperties !== this.properties) {
const dataSource = this.dataSourceProviders.find((provider) => provider.providerId === this.providerId &&
isEqual(provider.properties ?? {}, this.properties ?? {}));
if (!dataSource) {
this.form?.get("providerId")?.setValue(this.providerId);
this.form?.get("properties")?.setValue(this.properties);
this.invokeDataSource({
properties: this.properties,
providerId: this.providerId,
});
}
else {
this.form?.get("dataSource")?.setValue(dataSource);
this.invokeDataSource(dataSource);
}
}
}
if (changes.dataSourceProviders &&
!changes.dataSourceProviders.isFirstChange() &&
this.dataSourceProviders.length === 1) {
const dataSource = this.dataSourceProviders[0];
this.form.get("dataSource")?.setValue(dataSource);
this.invokeDataSource(dataSource);
}
}
ngAfterViewInit() {
if (this.dataSourceProviders && this.dataSourceProviders.length === 1) {
const dataSource = this.dataSourceProviders[0];
this.form.get("dataSource")?.setValue(dataSource);
this.invokeDataSource(dataSource);
}
}
onDataSourceSelected(selectedDataSource) {
this.eventBus.next(DATA_SOURCE_CHANGE, {});
this.invokeDataSource(selectedDataSource);
}
/**
* The data source is invoked here to notify the rest of the form about changes in the data source output.
* DATA_SOURCE_OUTPUT event is emitted through the event bus carrying the data source result as the payload.
* This might not be necessary in every situation. If the important information is already stored in the
* data source properties, this step can be omitted.
*
* @param data
*/
invokeDataSource(data) {
if (!data.providerId) {
return;
}
const provider = this.providerRegistryService.getProvider(data.providerId);
if (provider) {
this.dataSource = this.providerRegistryService.getProviderInstance(provider, this.injector);
this.eventBus.next(DATA_SOURCE_CREATED, {
payload: this.dataSource,
});
this.dataSource.outputsSubject
.pipe(take(1))
.subscribe((result) => {
this.eventBus.next(DATA_SOURCE_OUTPUT, { payload: result });
this.dataFieldIds.next(Object.keys(result.result || result));
});
const configurableDataSource = this
.dataSource;
if (configurableDataSource?.updateConfiguration) {
configurableDataSource.updateConfiguration(data.properties ?? {});
}
// This setTimeout is because the output of the data source might come faster than the data-source-error-component is initiated
setTimeout(() => {
this.dataSource.applyFilters();
});
}
else {
this.logger.warn("No provider found for id:", data.providerId);
}
}
onErrorState(isError) {
this.hasDataSourceError = isError;
this.form.markAsTouched({ onlySelf: true });
this.form.updateValueAndValidity({ emitEvent: false });
this.changeDetector.detectChanges();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataSourceConfigurationV2Component, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.ConfiguratorHeadingService }, { token: i2.FormBuilder }, { token: i3.ProviderRegistryService }, { token: PIZZAGNA_EVENT_BUS }, { token: i0.Injector }, { token: i4.LoggerService }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: DataSourceConfigurationV2Component, selector: "nui-data-source-configuration", inputs: { dataSourceProviders: "dataSourceProviders", properties: "properties", providerId: "providerId", errorComponent: "errorComponent" }, 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 : 'database' | async\"\n [subtitle]=\"form.get('dataSource')?.value?.label\"\n headerTitle=\"Data Source\"\n />\n <div class=\"datasource-configuration__accordion-content\">\n <nui-form-field [control]=\"form.get('dataSource')\">\n <nui-select-v2\n placeholder=\"Select data source\"\n i18n-placeholder\n [title]=\"form.get('dataSource')?.value?.label\"\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"dataSource\"\n (valueSelected)=\"onDataSourceSelected($event)\"\n class=\"datasource-configuration__accordion-content__datasource-input\"\n >\n <nui-select-v2-option\n *ngFor=\"let dataSourceProvider of dataSourceProviders\"\n [title]=\"dataSourceProvider?.label\"\n [value]=\"dataSourceProvider\"\n >\n {{ dataSourceProvider.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n <ng-container\n nuiComponentPortal\n [componentId]=\"errorComponent\"\n [componentType]=\"errorComponent\"\n [outputs]=\"['errorState']\"\n [properties]=\"{ dataSource: dataSource }\"\n (output)=\"onErrorState($event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".datasource-configuration__accordion-content{padding:15px 15px 15px 46px}\n"], dependencies: [{ kind: "directive", type: i5.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "directive", type: i7.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "component", type: i4.FormFieldComponent, selector: "nui-form-field", inputs: ["control", "caption", "info", "infoTemplate", "hint", "hintTemplate", "customBoxWidth", "showOptionalText"] }, { kind: "component", type: i4.SelectV2Component, selector: "nui-select-v2", inputs: ["displayValueTemplate"] }, { kind: "component", type: i4.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: "component", type: i9.WidgetEditorAccordionHeaderComponent, selector: "nui-widget-editor-accordion-header", inputs: ["headerIcon", "iconColor", "subtitle", "headerTitle"] }, { kind: "pipe", type: i5.AsyncPipe, name: "async" }, { kind: "pipe", type: i10.WidgetEditorAccordionFormStatePipe, name: "nuiWidgetEditorAccordionFormState" }, { kind: "pipe", type: i11.FormHeaderIconPipePipe, name: "nuiFormHeaderIconPipe" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: DataSourceConfigurationV2Component, decorators: [{
type: Component,
args: [{ selector: "nui-data-source-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 : 'database' | async\"\n [subtitle]=\"form.get('dataSource')?.value?.label\"\n headerTitle=\"Data Source\"\n />\n <div class=\"datasource-configuration__accordion-content\">\n <nui-form-field [control]=\"form.get('dataSource')\">\n <nui-select-v2\n placeholder=\"Select data source\"\n i18n-placeholder\n [title]=\"form.get('dataSource')?.value?.label\"\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"dataSource\"\n (valueSelected)=\"onDataSourceSelected($event)\"\n class=\"datasource-configuration__accordion-content__datasource-input\"\n >\n <nui-select-v2-option\n *ngFor=\"let dataSourceProvider of dataSourceProviders\"\n [title]=\"dataSourceProvider?.label\"\n [value]=\"dataSourceProvider\"\n >\n {{ dataSourceProvider.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n </nui-form-field>\n <ng-container\n nuiComponentPortal\n [componentId]=\"errorComponent\"\n [componentType]=\"errorComponent\"\n [outputs]=\"['errorState']\"\n [properties]=\"{ dataSource: dataSource }\"\n (output)=\"onErrorState($event.payload)\"\n #componentPortal=\"nuiComponentPortal\"\n >\n <ng-template\n [cdkPortalOutlet]=\"componentPortal.portal\"\n (attached)=\"componentPortal.attached($event)\"\n >\n </ng-template>\n </ng-container>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".datasource-configuration__accordion-content{padding:15px 15px 15px 46px}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.ConfiguratorHeadingService }, { type: i2.FormBuilder }, { type: i3.ProviderRegistryService }, { type: i4.EventBus, decorators: [{
type: Inject,
args: [PIZZAGNA_EVENT_BUS]
}] }, { type: i0.Injector }, { type: i4.LoggerService }], propDecorators: { dataSourceProviders: [{
type: Input
}], properties: [{
type: Input
}], providerId: [{
type: Input
}], errorComponent: [{
type: Input
}], formReady: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi12Mi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24tdjIvZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi12Mi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24tdjIvZGF0YS1zb3VyY2UtY29uZmlndXJhdGlvbi12Mi5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUVILHVCQUF1QixFQUN2QixpQkFBaUIsRUFDakIsU0FBUyxFQUNULFlBQVksRUFDWixNQUFNLEVBQ04sUUFBUSxFQUNSLEtBQUssRUFHTCxNQUFNLEdBRVQsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxVQUFVLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNwRSxPQUFPLE9BQU8sTUFBTSxnQkFBZ0IsQ0FBQztBQUNyQyxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0s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Q0FBQyxDQUFDLENBQUM7U0FDTjthQUFNO1lBQ0gsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsMkJBQTJCLEVBQUUsSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1NBQ2xFO0lBQ0wsQ0FBQztJQUVNLFlBQVksQ0FBQyxPQUFnQjtRQUNoQyxJQUFJLENBQUMsa0JBQWtCLEdBQUcsT0FBTyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxJQUFJLENBQUMsYUFBYSxDQUFDLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDNUMsSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLFNBQVMsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZELElBQUksQ0FBQyxjQUFjLENBQUMsYUFBYSxFQUFFLENBQUM7SUFDeEMsQ0FBQztrSUEvS1Esa0NBQWtDLCtKQTRCL0Isa0JBQWtCO3NIQTVCckIsa0NBQWtDLDZRQ3JFL0MsZzhEQThDQTs7NEZEdUJhLGtDQUFrQztrQkFOOUMsU0FBUzsrQkFDSSwrQkFBK0IsbUJBR3hCLHVCQUF1QixDQUFDLE1BQU07OzBCQThCMUMsTUFBTTsyQkFBQyxrQkFBa0I7NEZBcEJyQixtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBRUcsVUFBVTtzQkFBbEIsS0FBSztnQkFDRyxVQUFVO3NCQUFsQixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBRUksU0FBUztzQkFBbEIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3ksXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEV2ZW50RW1pdHRlcixcbiAgICBJbmplY3QsXG4gICAgSW5qZWN0b3IsXG4gICAgSW5wdXQsXG4gICAgT25DaGFuZ2VzLFxuICAgIE9uSW5pdCxcbiAgICBPdXRwdXQsXG4gICAgU2ltcGxlQ2hhbmdlcyxcbn0gZnJvbSBcIkBhbmd1bGFyL2NvcmVcIjtcbmltcG9ydCB7IEZvcm1CdWlsZGVyLCBGb3JtR3JvdXAsIFZhbGlkYXRvcnMgfSBmcm9tIFwiQGFuZ3VsYXIvZm9ybXNcIjtcbmltcG9ydCBpc0VxdWFsIGZyb20gXCJsb2Rhc2gvaXNFcXVhbFwiO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gXCJyeGpzL2ludGVybmFsL1N1YmplY3RcIjtcbmltcG9ydCB7IHRha2UgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHsgRXZlbnRCdXMsIElEYXRhU291cmNlLCBJRXZlbnQsIExvZ2dlclNlcnZpY2UgfSBmcm9tIFwiQG5vdmEtdWkvYml0c1wiO1xuXG5pbXBvcnQgeyBQcm92aWRlclJlZ2lzdHJ5U2VydmljZSB9IGZyb20gXCIuLi8uLi8uLi8uLi8uLi9zZXJ2aWNlcy9wcm92aWRlci1yZWdpc3RyeS5zZXJ2aWNlXCI7XG5pbXBvcnQge1xuICAgIElDb25maWd1cmFibGUsXG4gICAgSUhhc0NoYW5nZURldGVjdG9yLFxuICAgIElIYXNGb3JtLFxuICAgIElQcm9wZXJ0aWVzLFxuICAgIElQcm92aWRlckNvbmZpZ3VyYXRpb24sXG4gICAgSVByb3ZpZGVyQ29uZmlndXJhdGlvbkZvckRpc3BsYXksXG4gICAgUElaWkFHTkFfRVZFTlRfQlVTLFxufSBmcm9tIFwiLi4vLi4vLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IENvbmZpZ3VyYXRvckhlYWRpbmdTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uLy4uLy4uL3NlcnZpY2VzL2NvbmZpZ3VyYXRvci1oZWFkaW5nLnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgREFUQV9TT1VSQ0VfQ0hBTkdFLFxuICAgIERBVEFfU09VUkNFX0NSRUFURUQsXG4gICAgREFUQV9TT1VSQ0VfT1VUUFVULFxufSBmcm9tIFwiLi4vLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IERhdGFTb3VyY2VFcnJvckNvbXBvbmVudCB9IGZyb20gXCIuLi9kYXRhLXNvdXJjZS1lcnJvci9kYXRhLXNvdXJjZS1lcnJvci5jb21wb25lbnRcIjtcblxuLyoqXG4gKiBUaGlzIGlzIGEgYmFzaWMgaW1wbGVtZW50YXRpb24gb2YgYSBkYXRhIHNvdXJjZSBjb25maWd1cmF0aW9uIGNvbXBvbmVudC4gSW4gdGhlIHJlYWwgd29ybGQgc2NlbmFyaW8sIHRoaXMgY29tcG9uZW50IHdpbGwgbW9zdCBsaWtlbHkgYmUgcmVwbGFjZWQgYnkgYVxuICogY3VzdG9tIG9uZSwgYnV0IHN0aWxsIGNhbiBiZSB1c2VkIGFzIGEgdGVtcGxhdGUgZm9yIGRldmVsb3BpbmcgYSBjdXN0b20gc3BlY2lmaWMgc29sdXRpb24uXG4gKi9cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiBcIm51aS1kYXRhLXNvdXJjZS1jb25maWd1cmF0aW9uXCIsXG4gICAgdGVtcGxhdGVVcmw6IFwiLi9kYXRhLXNvdXJjZS1jb25maWd1cmF0aW9uLXYyLmNvbXBvbmVudC5odG1sXCIsXG4gICAgc3R5bGVVcmxzOiBbXCIuL2RhdGEtc291cmNlLWNvbmZpZ3VyYXRpb24tdjIuY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgY2hhbmdlRGV0ZWN0aW9uOiBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneS5PblB1c2gsXG59KVxuZXhwb3J0IGNsYXNzIERhdGFTb3VyY2VDb25maWd1cmF0aW9uVjJDb21wb25lbnRcbiAgICBpbXBsZW1lbnRzIElIYXNDaGFuZ2VEZXRlY3RvciwgSUhhc0Zvcm0sIE9uSW5pdCwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0XG57XG4gICAgcHVibGljIHN0YXRpYyBsYXRlTG9hZEtleSA9IFwiRGF0YVNvdXJjZUNvbmZpZ3VyYXRpb25WMkNvbXBvbmVudFwiO1xuXG4gICAgLyoqXG4gICAgICogVGhpcyBjb21wb25lbnQgc2hvd3MgYSBkcm9wZG93biB3aXRoIG9wdGlvbnMgZm9yIHNlbGVjdGluZyBhIGRhdGEgc291cmNlLCB0aGlzIGlucHV0IHJlcHJlc2VudHMgdGhlc2Ugb3B0aW9ucy5cbiAgICAgKi9cbiAgICBASW5wdXQoKSBkYXRhU291cmNlUHJvdmlkZXJzOiBJUHJvdmlkZXJDb25maWd1cmF0aW9uRm9yRGlzcGxheVtdID0gW107XG5cbiAgICBASW5wdXQoKSBwcm9wZXJ0aWVzOiBJUHJvcGVydGllcztcbiAgICBASW5wdXQoKSBwcm92aWRlcklkOiBzdHJpbmc7XG4gICAgQElucHV0KCkgZXJyb3JDb21wb25lbnQ6IHN0cmluZyA9IERhdGFTb3VyY2VFcnJvckNvbXBvbmVudC5sYXRlTG9hZEtleTtcblxuICAgIEBPdXRwdXQoKSBmb3JtUmVhZHkgPSBuZXcgRXZlbnRFbWl0dGVyPEZvcm1Hcm91cD4oKTtcblxuICAgIHB1YmxpYyBmb3JtOiBGb3JtR3JvdXA7XG4gICAgcHVibGljIGhhc0RhdGFTb3VyY2VFcnJvcjogYm9vbGVhbiA9IGZhbHNlO1xuICAgIHB1YmxpYyBkYXRhU291cmNlOiBJRGF0YVNvdXJjZTtcblxuICAgIC8vIHVzZWQgYnkgdGhlIEJyb2FkY2FzdGVyXG4gICAgcHVibGljIGRhdGFGaWVsZElkcyA9IG5ldyBTdWJqZWN0PGFueT4oKTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgY2hhbmdlRGV0ZWN0b3I6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICBwdWJsaWMgY29uZmlndXJhdG9ySGVhZGluZzogQ29uZmlndXJhdG9ySGVhZGluZ1NlcnZpY2UsXG4gICAgICAgIHByb3RlY3RlZCBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXIsXG4gICAgICAgIHByb3RlY3RlZCBwcm92aWRlclJlZ2lzdHJ5U2VydmljZTogUHJvdmlkZXJSZWdpc3RyeVNlcnZpY2UsXG4gICAgICAgIEBJbmplY3QoUElaWkFHTkFfRVZFTlRfQlVTKSBwcm90ZWN0ZWQgZXZlbnRCdXM6IEV2ZW50QnVzPElFdmVudD4sXG4gICAgICAgIHByb3RlY3RlZCBpbmplY3RvcjogSW5qZWN0b3IsXG4gICAgICAgIHByb3RlY3RlZCBsb2dnZXI6IExvZ2dlclNlcnZpY2VcbiAgICApIHt9XG5cbiAgICBwdWJsaWMgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZm9ybSA9IHRoaXMuZm9ybUJ1aWxkZXIuZ3JvdXAoe1xuICAgICAgICAgICAgLy8gV2lkZ2V0IHByb3BlcnRpZXMsIGZvcm0gaW5wdXRzIGFuZCBmb3JtIHZhbHVlcyBuZWVkIHRvIGJlIGluIHN5bmMgdG8gd29yayB3aXRoIGdlbmVyaWMgY29udmVydGVyXG4gICAgICAgICAgICAvLyB3aGljaCBpcyB3aHkgd2UgaGF2ZSBwcm92aWRlcklkIGFuZCBwcm9wZXJ0aWVzIGFzIGEgZm9ybSBncm91cCBldmVuIGlmIHdlIGRvbid0IGV4cGxpY2l0bHkgdXNlIGl0IGluIHRoZSBmaWxlLlxuICAgICAgICAgICAgcHJvdmlkZXJJZDogW3RoaXMucHJvdmlkZXJJZCB8fCBcIlwiLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF1dLFxuICAgICAgICAgICAgcHJvcGVydGllczogW3RoaXMucHJvcGVydGllcyB8fCB7fV0sXG4gICAgICAgICAgICBkYXRhU291cmNlOiBbbnVsbCwgW1ZhbGlkYXRvcnMucmVxdWlyZWRdXSxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuZm9ybS5zZXRWYWxpZGF0b3JzKFtcbiAgICAgICAgICAgICgpID0+ICh0aGlzLmhhc0RhdGFTb3VyY2VFcnJvciA/IHsgZGF0YVNvdXJjZUVycm9yOiB0cnVlIH0gOiBudWxsKSxcbiAgICAgICAgXSk7XG5cbiAgICAgICAgdGhpcy5mb3JtXG4gICAgICAgICAgICAuZ2V0KFwiZGF0YVNvdXJjZVwiKVxuICAgICAgICAgICAgPy52YWx1ZUNoYW5nZXMuc3Vic2NyaWJlKChzZWxlY3RlZERhdGFTb3VyY2UpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmZvcm1cbiAgICAgICAgICAgICAgICAgICAgLmdldChcInByb3ZpZGVySWRcIilcbiAgICAgICAgICAgICAgICAgICAgPy5zZXRWYWx1ZShzZWxlY3RlZERhdGFTb3VyY2U/LnByb3ZpZGVySWQpO1xuICAgICAgICAgICAgICAgIGlmIChzZWxlY3RlZERhdGFTb3VyY2U/LnByb3BlcnRpZXMpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5mb3JtXG4gICAgICAgICAgICAgICAgICAgICAgICAuZ2V0KFwicHJvcGVydGllc1wiKVxuICAgICAgICAgICAgICAgICAgICAgICAgPy5zZXRWYWx1ZShzZWxlY3RlZERhdGFTb3VyY2U/LnByb3BlcnRpZXMpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMuZm9ybVJlYWR5LmVtaXQodGhpcy5mb3JtKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgICAoY2hhbmdlcy5wcm92aWRlcklkICYmICFjaGFuZ2VzLnByb3ZpZGVySWQuaXNGaXJzdENoYW5nZSgpKSB8fFxuICAgICAgICAgICAgKGNoYW5nZXMucHJvcGVydGllcyAmJiAhY2hhbmdlcy5wcm9wZXJ0aWVzLmlzRmlyc3RDaGFuZ2UoKSlcbiAgICAgICAgKSB7XG4gICAgICAgICAgICBjb25zdCBwcmV2aW91c1Byb3ZpZGVyOiBzdHJpbmcgPSBjaGFuZ2VzLnByb3ZpZGVySWQ/LnByZXZpb3VzVmFsdWU7XG4gICAgICAgICAgICBjb25zdCBwcmV2aW91c1Byb3BlcnRpZXM6IElQcm9wZXJ0aWVzID1cbiAgICAgICAgICAgICAgICBjaGFuZ2VzLnByb3BlcnRpZXM/LnByZXZpb3VzVmFsdWU7XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgcHJldmlvdXNQcm92aWRlciAhPT0gdGhpcy5wcm92aWRlcklkIHx8XG4gICAgICAgICAgICAgICAgcHJldmlvdXNQcm9wZXJ0aWVzICE9PSB0aGlzLnByb3BlcnRpZXNcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGFTb3VyY2UgPSB0aGlzLmRhdGFTb3VyY2VQcm92aWRlcnMuZmluZChcbiAgICAgICAgICAgICAgICAgICAgKHByb3ZpZGVyKSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgcHJvdmlkZXIucHJvdmlkZXJJZCA9PT0gdGhpcy5wcm92aWRlcklkICYmXG4gICAgICAgICAgICAgICAgICAgICAgICBpc0VxdWFsKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb3ZpZGVyLnByb3BlcnRpZXMgPz8ge30sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5wcm9wZXJ0aWVzID8/IHt9XG4gICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBpZiAoIWRhdGFTb3VyY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5mb3JtPy5nZXQoXCJwcm92aWRlcklkXCIpPy5zZXRWYWx1ZSh0aGlzLnByb3ZpZGVySWQpO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmZvcm0/LmdldChcInByb3BlcnRpZXNcIik/LnNldFZhbHVlKHRoaXMucHJvcGVydGllcyk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaW52b2tlRGF0YVNvdXJjZSh7XG4gICAgICAgICAgICAgICAgICAgICAgICBwcm9wZXJ0aWVzOiB0aGlzLnByb3BlcnRpZXMsXG4gICAgICAgICAgICAgICAgICAgICAgICBwcm92aWRlcklkOiB0aGlzLnByb3ZpZGVySWQsXG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZm9ybT8uZ2V0KFwiZGF0YVNvdXJjZVwiKT8uc2V0VmFsdWUoZGF0YVNvdXJjZSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaW52b2tlRGF0YVNvdXJjZShkYXRhU291cmNlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgY2hhbmdlcy5kYXRhU291cmNlUHJvdmlkZXJzICYmXG4gICAgICAgICAgICAhY2hhbmdlcy5kYXRhU291cmNlUHJvdmlkZXJzLmlzRmlyc3RDaGFuZ2UoKSAmJlxuICAgICAgICAgICAgdGhpcy5kYXRhU291cmNlUHJvdmlkZXJzLmxlbmd0aCA9PT0gMVxuICAgICAgICApIHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGFTb3VyY2UgPSB0aGlzLmRhdGFTb3VyY2VQcm92aWRlcnNbMF07XG4gICAgICAgICAgICB0aGlzLmZvcm0uZ2V0KFwiZGF0YVNvdXJjZVwiKT8uc2V0VmFsdWUoZGF0YVNvdXJjZSk7XG4gICAgICAgICAgICB0aGlzLmludm9rZURhdGFTb3VyY2UoZGF0YVNvdXJjZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5kYXRhU291cmNlUHJvdmlkZXJzICYmIHRoaXMuZGF0YVNvdXJjZVByb3ZpZGVycy5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgICAgIGNvbnN0IGRhdGFTb3VyY2UgPSB0aGlzLmRhdGFTb3VyY2VQcm92aWRlcnNbMF07XG4gICAgICAgICAgICB0aGlzLmZvcm0uZ2V0KFwiZGF0YVNvdXJjZVwiKT8uc2V0VmFsdWUoZGF0YVNvdXJjZSk7XG4gICAgICAgICAgICB0aGlzLmludm9rZURhdGFTb3VyY2UoZGF0YVNvdXJjZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgb25EYXRhU291cmNlU2VsZWN0ZWQoXG4gICAgICAgIHNlbGVjdGVkRGF0YVNvdXJjZTogSVByb3ZpZGVyQ29uZmlndXJhdGlvbkZvckRpc3BsYXlcbiAgICApOiB2b2lkIHtcbiAgICAgICAgdGhpcy5ldmVudEJ1cy5uZXh0KERBVEFfU09VUkNFX0NIQU5HRSwge30pO1xuICAgICAgICB0aGlzLmludm9rZURhdGFTb3VyY2Uoc2VsZWN0ZWREYXRhU291cmNlKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUaGUgZGF0YSBzb3VyY2UgaXMgaW52b2tlZCBoZXJlIHRvIG5vdGlmeSB0aGUgcmVzdCBvZiB0aGUgZm9ybSBhYm91dCAgY2hhbmdlcyBpbiB0aGUgZGF0YSBzb3VyY2Ugb3V0cHV0LlxuICAgICAqIERBVEFfU09VUkNFX09VVFBVVCBldmVudCBpcyBlbWl0dGVkIHRocm91Z2ggdGhlIGV2ZW50IGJ1cyBjYXJyeWluZyB0aGUgZGF0YSBzb3VyY2UgcmVzdWx0IGFzIHRoZSBwYXlsb2FkLlxuICAgICAqIFRoaXMgbWlnaHQgbm90IGJlIG5lY2Vzc2FyeSBpbiBldmVyeSBzaXR1YXRpb24uIElmIHRoZSBpbXBvcnRhbnQgaW5mb3JtYXRpb24gaXMgYWxyZWFkeSBzdG9yZWQgaW4gdGhlXG4gICAgICogZGF0YSBzb3VyY2UgcHJvcGVydGllcywgdGhpcyBzdGVwIGNhbiBiZSBvbWl0dGVkLlxuICAgICAqXG4gICAgICogQHBhcmFtIGRhdGFcbiAgICAgKi9cbiAgICBwdWJsaWMgaW52b2tlRGF0YVNvdXJjZShkYXRhOiBJUHJvdmlkZXJDb25maWd1cmF0aW9uKTogdm9pZCB7XG4gICAgICAgIGlmICghZGF0YS5wcm92aWRlcklkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcHJvdmlkZXIgPSB0aGlzLnByb3ZpZGVyUmVnaXN0cnlTZXJ2aWNlLmdldFByb3ZpZGVyKFxuICAgICAgICAgICAgZGF0YS5wcm92aWRlcklkXG4gICAgICAgICk7XG4gICAgICAgIGlmIChwcm92aWRlcikge1xuICAgICAgICAgICAgdGhpcy5kYXRhU291cmNlID0gdGhpcy5wcm92aWRlclJlZ2lzdHJ5U2VydmljZS5nZXRQcm92aWRlckluc3RhbmNlKFxuICAgICAgICAgICAgICAgIHByb3ZpZGVyLFxuICAgICAgICAgICAgICAgIHRoaXMuaW5qZWN0b3JcbiAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgIHRoaXMuZXZlbnRCdXMubmV4dChEQVRBX1NPVVJDRV9DUkVBVEVELCB7XG4gICAgICAgICAgICAgICAgcGF5bG9hZDogdGhpcy5kYXRhU291cmNlLFxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIHRoaXMuZGF0YVNvdXJjZS5vdXRwdXRzU3ViamVjdFxuICAgICAgICAgICAgICAgIC5waXBlKHRha2UoMSkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgocmVzdWx0OiBhbnkpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5uZXh0KERBVEFfU09VUkNFX09VVFBVVCwgeyBwYXlsb2FkOiByZXN1bHQgfSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZGF0YUZpZWxkSWRzLm5leHQoXG4gICAgICAgICAgICAgICAgICAgICAgICBPYmplY3Qua2V5cyhyZXN1bHQucmVzdWx0IHx8IHJlc3VsdClcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgY29uc3QgY29uZmlndXJhYmxlRGF0YVNvdXJjZSA9IHRoaXNcbiAgICAgICAgICAgICAgICAuZGF0YVNvdXJjZSBhcyB1bmtub3duIGFzIElDb25maWd1cmFibGU7XG4gICAgICAgICAgICBpZiAoY29uZmlndXJhYmxlRGF0YVNvdXJjZT8udXBkYXRlQ29uZmlndXJhdGlvbikge1xuICAgICAgICAgICAgICAgIGNvbmZpZ3VyYWJsZURhdGFTb3VyY2UudXBkYXRlQ29uZmlndXJhdGlvbihcbiAgICAgICAgICAgICAgICAgICAgZGF0YS5wcm9wZXJ0aWVzID8/IHt9XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIFRoaXMgc2V0VGltZW91dCBpcyBiZWNhdXNlIHRoZSBvdXRwdXQgb2YgdGhlIGRhdGEgc291cmNlIG1pZ2h0IGNvbWUgZmFzdGVyIHRoYW4gdGhlIGRhdGEtc291cmNlLWVycm9yLWNvbXBvbmVudCBpcyBpbml0aWF0ZWRcbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuZGF0YVNvdXJjZS5hcHBseUZpbHRlcnMoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5sb2dnZXIud2FybihcIk5vIHByb3ZpZGVyIGZvdW5kIGZvciBpZDpcIiwgZGF0YS5wcm92aWRlcklkKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvbkVycm9yU3RhdGUoaXNFcnJvcjogYm9vbGVhbik6IHZvaWQge1xuICAgICAgICB0aGlzLmhhc0RhdGFTb3VyY2VFcnJvciA9IGlzRXJyb3I7XG4gICAgICAgIHRoaXMuZm9ybS5tYXJrQXNUb3VjaGVkKHsgb25seVNlbGY6IHRydWUgfSk7XG4gICAgICAgIHRoaXMuZm9ybS51cGRhdGVWYWx1ZUFuZFZhbGlkaXR5KHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcbiAgICAgICAgdGhpcy5jaGFuZ2VEZXRlY3Rvci5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfVxufVxuIiwiPG51aS13aWRnZXQtZWRpdG9yLWFjY29yZGlvblxuICAgIFtmb3JtR3JvdXBdPVwiZm9ybVwiXG4gICAgW3N0YXRlXT1cImZvcm0gfCBudWlXaWRnZXRFZGl0b3JBY2NvcmRpb25Gb3JtU3RhdGUgfCBhc3luY1wiXG4+XG4gICAgPG51aS13aWRnZXQtZWRpdG9yLWFjY29yZGlvbi1oZWFkZXJcbiAgICAgICAgW2hlYWRlckljb25dPVwiZm9ybSB8IG51aUZvcm1IZWFkZXJJY29uUGlwZSA6ICdkYXRhYmFzZScgfCBhc3luY1wiXG4gICAgICAgIFtzdWJ0aXRsZV09XCJmb3JtLmdldCgnZGF0YVNvdXJjZScpPy52YWx1ZT8ubGFiZWxcIlxuICAgICAgICBoZWFkZXJUaXRsZT1cIkRhdGEgU291cmNlXCJcbiAgICAvPlxuICAgIDxkaXYgY2xhc3M9XCJkYXRhc291cmNlLWNvbmZpZ3VyYXRpb25fX2FjY29yZGlvbi1jb250ZW50XCI+XG4gICAgICAgIDxudWktZm9ybS1maWVsZCBbY29udHJvbF09XCJmb3JtLmdldCgnZGF0YVNvdXJjZScpXCI+XG4gICAgICAgICAgICA8bnVpLXNlbGVjdC12MlxuICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU2VsZWN0IGRhdGEgc291cmNlXCJcbiAgICAgICAgICAgICAgICBpMThuLXBsYWNlaG9sZGVyXG4gICAgICAgICAgICAgICAgW3RpdGxlXT1cImZvcm0uZ2V0KCdkYXRhU291cmNlJyk/LnZhbHVlPy5sYWJlbFwiXG4gICAgICAgICAgICAgICAgW3BvcHVwVmlld3BvcnRNYXJnaW5dPVwiY29uZmlndXJhdG9ySGVhZGluZy5oZWlnaHQkIHwgYXN5bmNcIlxuICAgICAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cImRhdGFTb3VyY2VcIlxuICAgICAgICAgICAgICAgICh2YWx1ZVNlbGVjdGVkKT1cIm9uRGF0YVNvdXJjZVNlbGVjdGVkKCRldmVudClcIlxuICAgICAgICAgICAgICAgIGNsYXNzPVwiZGF0YXNvdXJjZS1jb25maWd1cmF0aW9uX19hY2NvcmRpb24tY29udGVudF9fZGF0YXNvdXJjZS1pbnB1dFwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPG51aS1zZWxlY3QtdjItb3B0aW9uXG4gICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBkYXRhU291cmNlUHJvdmlkZXIgb2YgZGF0YVNvdXJjZVByb3ZpZGVyc1wiXG4gICAgICAgICAgICAgICAgICAgIFt0aXRsZV09XCJkYXRhU291cmNlUHJvdmlkZXI/LmxhYmVsXCJcbiAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cImRhdGFTb3VyY2VQcm92aWRlclwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICB7eyBkYXRhU291cmNlUHJvdmlkZXIubGFiZWwgfX1cbiAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjItb3B0aW9uPlxuICAgICAgICAgICAgPC9udWktc2VsZWN0LXYyPlxuICAgICAgICA8L251aS1mb3JtLWZpZWxkPlxuICAgICAgICA8bmctY29udGFpbmVyXG4gICAgICAgICAgICBudWlDb21wb25lbnRQb3J0YWxcbiAgICAgICAgICAgIFtjb21wb25lbnRJZF09XCJlcnJvckNvbXBvbmVudFwiXG4gICAgICAgICAgICBbY29tcG9uZW50VHlwZV09XCJlcnJvckNvbXBvbmVudFwiXG4gICAgICAgICAgICBbb3V0cHV0c109XCJbJ2Vycm9yU3RhdGUnXVwiXG4gICAgICAgICAgICBbcHJvcGVydGllc109XCJ7IGRhdGFTb3VyY2U6IGRhdGFTb3VyY2UgfVwiXG4gICAgICAgICAgICAob3V0cHV0KT1cIm9uRXJyb3JTdGF0ZSgkZXZlbnQucGF5bG9hZClcIlxuICAgICAgICAgICAgI2NvbXBvbmVudFBvcnRhbD1cIm51aUNvbXBvbmVudFBvcnRhbFwiXG4gICAgICAgID5cbiAgICAgICAgICAgIDxuZy10ZW1wbGF0ZVxuICAgICAgICAgICAgICAgIFtjZGtQb3J0YWxPdXRsZXRdPVwiY29tcG9uZW50UG9ydGFsLnBvcnRhbFwiXG4gICAgICAgICAgICAgICAgKGF0dGFjaGVkKT1cImNvbXBvbmVudFBvcnRhbC5hdHRhY2hlZCgkZXZlbnQpXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgIDwvbmctdGVtcGxhdGU+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuPC9udWktd2lkZ2V0LWVkaXRvci1hY2NvcmRpb24+XG4iXX0=