UNPKG

@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

184 lines 34.2 kB
// © 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 { ScrollDispatcher } from "@angular/cdk/overlay"; import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, } from "@angular/core"; import { FormBuilder } from "@angular/forms"; import cloneDeep from "lodash/cloneDeep"; import { BehaviorSubject, Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { WidgetTypesService } from "../../../services/widget-types.service"; import { PizzagnaLayer, } from "../../../types"; import { PreviewService } from "../../services/preview.service"; import { ConfiguratorComponent } from "../configurator/configurator.component"; import * as i0 from "@angular/core"; import * as i1 from "../configurator/configurator.component"; import * as i2 from "../../services/preview.service"; import * as i3 from "@angular/forms"; import * as i4 from "../../../services/widget-types.service"; import * as i5 from "@angular/cdk/overlay"; import * as i6 from "@angular/cdk/portal"; import * as i7 from "../../../pizzagna/components/pizzagna/pizzagna.component"; import * as i8 from "../../../pizzagna/directives/component-portal/component-portal.directive"; import * as i9 from "../wizard/dashwiz/dashwiz.component"; import * as i10 from "../wizard/dashwiz-step/dashwiz-step.component"; import * as i11 from "../heading/configurator-heading.component"; export class WidgetClonerComponent { static { this.lateLoadKey = "WidgetClonerComponent"; } constructor(changeDetector, configurator, previewService, formBuilder, widgetTypesService, scrollDispatcher) { this.changeDetector = changeDetector; this.configurator = configurator; this.previewService = previewService; this.formBuilder = formBuilder; this.widgetTypesService = widgetTypesService; this.scrollDispatcher = scrollDispatcher; this.navigationControl = new BehaviorSubject({ busyState: { busy: false }, allowStepChange: true, }); this.busy = false; this.scrolled = false; this.isFormDisplayed = false; this.destroy$ = new Subject(); this.resetForm$ = new Subject(); this.resetForm(); } ngOnInit() { this.previewService.previewChanged .pipe(takeUntil(this.destroy$)) .subscribe((pizzagna) => { this.onPreviewPizzagnaUpdate(pizzagna); }); this.configurator.submitError .pipe(takeUntil(this.destroy$)) .subscribe(() => { this.toggleBusy(); }); } ngAfterViewInit() { // trigger dashwiz to update its button configuration this.changeDetector.markForCheck(); // Scroll shadows this.scrollDispatcher .scrolled() .subscribe((event) => { const element = event?.getElementRef()?.nativeElement; if (element?.classList?.contains("configurator-scrollable")) { this.scrolled = !!element?.scrollTop; this.changeDetector.detectChanges(); } }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); this.resetForm$.complete(); } // -------------------------------------------------------------------------------- onPreviewPizzagnaUpdate(configLayer) { if (!this.isFormDisplayed) { return; } const previewWidget = { ...this.configurator.previewWidget, pizzagna: { ...this.configurator.previewWidget?.pizzagna, [PizzagnaLayer.Configuration]: configLayer, }, }; // TODO: Make previewWidget to be assignable to IWidget // @ts-ignore: Type '{ pizzagna: { configuration: IPizzagnaLayer; }; }' is missing the following properties from type 'IWidget': id, type this.configurator.updateWidget(previewWidget); } canFinish() { return (!!this.widgetTemplate?.pizzagna?.configuration && !this.widgetTemplate?.metadata?.needsConfiguration); } onFinish() { if (this.form.invalid) { this.form.markAllAsTouched(); // is used to trigger valueChanges to detect changes in forms // as form is invalid and marking as touched doesn't trigger neither statusChanges nor valueChanges // and validation messages and styles are not applied in that cases this.form.patchValue(this.form.value); this.form.updateValueAndValidity({ onlySelf: false }); return; } this.toggleBusy(); this.configurator.formSubmit(); } onCancel() { this.configurator.formCancel(); } onStepNavigated(event) { this.isFormDisplayed = event.currentStepIndex === 1; if (event.currentStepIndex === 0 && event.previousStepIndex === 1) { this.formPizzagna = undefined; this.resetForm(); this.onSelect(this.widgetTemplate); } else if (event.currentStepIndex === 1) { this.formPizzagna = this.widgetTypesService.getWidgetType(this.widgetTemplate.type, this.widgetTemplate.version).configurator; this.previewService.preview = this.widgetTemplate.pizzagna[PizzagnaLayer.Configuration]; } } onSelect(widget) { this.widgetTemplate = widget; const previewWidget = { ...widget, id: this.configurator.previewWidget?.id || null, }; // clear out the old widget first because we want to recreate the widget from scratch this.configurator.updateWidget(null); // the new widget is assigned in a setTimeout to apply the change in the next change detection cycle setTimeout(() => { // @ts-ignore: Type 'null' is not assignable to type 'string' on id property this.configurator.updateWidget(cloneDeep(previewWidget)); this.changeDetector.detectChanges(); }); } toggleBusy() { this.busy = !this.busy; this.navigationControl.next({ busyState: { busy: this.busy }, allowStepChange: !this.busy, }); } resetForm() { this.resetForm$.next(); this.form = this.formBuilder.group({}); this.form.statusChanges .pipe(takeUntil(this.resetForm$), takeUntil(this.destroy$)) .subscribe(() => { this.changeDetector.markForCheck(); }); } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetClonerComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.ConfiguratorComponent }, { token: i2.PreviewService }, { token: i3.FormBuilder }, { token: i4.WidgetTypesService }, { token: i5.ScrollDispatcher }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: WidgetClonerComponent, selector: "nui-widget-cloner", inputs: { formPizzagna: "formPizzagna", cloneSelectionComponentType: "cloneSelectionComponentType" }, host: { classAttribute: "d-flex flex-column h-100" }, ngImport: i0, template: "<nui-configurator-heading\n configuratorTitle=\"Creating Widget\"\n i18n-configuratorTitle\n class=\"header\"\n (close)=\"onCancel()\"\n [disableCloseButton]=\"busy\"\n [class.shadow]=\"scrolled\"\n></nui-configurator-heading>\n<nui-dashwiz\n [hideHeader]=\"true\"\n [canProceed]=\"!!widgetTemplate\"\n [canFinish]=\"canFinish()\"\n finishText=\"Create Widget\"\n i18n-finishText\n [navigationControl]=\"navigationControl\"\n (finish)=\"onFinish()\"\n (cancel)=\"onCancel()\"\n (stepNavigated)=\"onStepNavigated($event)\"\n>\n <nui-dashwiz-step [stepTemplate]=\"step1\" nextText=\"Customize\" i18n-nextText>\n </nui-dashwiz-step>\n <nui-dashwiz-step [stepTemplate]=\"step2\"></nui-dashwiz-step>\n</nui-dashwiz>\n\n<ng-template #step1>\n <div class=\"p-4 h-100\">\n <ng-container\n nuiComponentPortal\n [componentType]=\"cloneSelectionComponentType\"\n [outputs]=\"['widgetSelected']\"\n (output)=\"onSelect($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</ng-template>\n\n<ng-template #step2>\n <div [formGroup]=\"form\">\n <nui-pizzagna\n #formPizzagnaComponent\n [(pizzagna)]=\"formPizzagna\"\n ></nui-pizzagna>\n </div>\n</ng-template>\n", styles: [".header{transition:box-shadow .3s ease-in-out}.header.shadow{box-shadow:0 0 5px 0 var(--nui-shadow-color-stronger, rgba(17, 17, 17, .6))}\n"], dependencies: [{ kind: "directive", type: i3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i6.CdkPortalOutlet, selector: "[cdkPortalOutlet]", inputs: ["cdkPortalOutlet"], outputs: ["attached"], exportAs: ["cdkPortalOutlet"] }, { kind: "component", type: i7.PizzagnaComponent, selector: "nui-pizzagna", inputs: ["rootNode", "pizzagna", "outputs"], outputs: ["pizzagnaChange", "output"] }, { kind: "directive", type: i8.ComponentPortalDirective, selector: "[nuiComponentPortal]", inputs: ["componentId", "componentType", "properties", "providers", "outputs"], outputs: ["output"], exportAs: ["nuiComponentPortal"] }, { kind: "component", type: i9.DashwizComponent, selector: "nui-dashwiz", inputs: ["hideHeader", "canProceed", "canFinish", "finishText", "stretchStepLines", "buttonComponentTypes", "navigationControl"], outputs: ["stepNavigated", "cancel", "finish", "next", "back"] }, { kind: "component", type: i10.DashwizStepComponent, selector: "nui-dashwiz-step", inputs: ["stepTemplate", "stepControl", "title", "nextText", "hidden", "disabled", "spinnerSize"], outputs: ["enter", "valid", "exit", "next"] }, { kind: "component", type: i11.ConfiguratorHeadingComponent, selector: "nui-configurator-heading", inputs: ["configuratorTitle", "disableCloseButton"], outputs: ["close"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: WidgetClonerComponent, decorators: [{ type: Component, args: [{ selector: "nui-widget-cloner", host: { class: "d-flex flex-column h-100" }, changeDetection: ChangeDetectionStrategy.OnPush, template: "<nui-configurator-heading\n configuratorTitle=\"Creating Widget\"\n i18n-configuratorTitle\n class=\"header\"\n (close)=\"onCancel()\"\n [disableCloseButton]=\"busy\"\n [class.shadow]=\"scrolled\"\n></nui-configurator-heading>\n<nui-dashwiz\n [hideHeader]=\"true\"\n [canProceed]=\"!!widgetTemplate\"\n [canFinish]=\"canFinish()\"\n finishText=\"Create Widget\"\n i18n-finishText\n [navigationControl]=\"navigationControl\"\n (finish)=\"onFinish()\"\n (cancel)=\"onCancel()\"\n (stepNavigated)=\"onStepNavigated($event)\"\n>\n <nui-dashwiz-step [stepTemplate]=\"step1\" nextText=\"Customize\" i18n-nextText>\n </nui-dashwiz-step>\n <nui-dashwiz-step [stepTemplate]=\"step2\"></nui-dashwiz-step>\n</nui-dashwiz>\n\n<ng-template #step1>\n <div class=\"p-4 h-100\">\n <ng-container\n nuiComponentPortal\n [componentType]=\"cloneSelectionComponentType\"\n [outputs]=\"['widgetSelected']\"\n (output)=\"onSelect($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</ng-template>\n\n<ng-template #step2>\n <div [formGroup]=\"form\">\n <nui-pizzagna\n #formPizzagnaComponent\n [(pizzagna)]=\"formPizzagna\"\n ></nui-pizzagna>\n </div>\n</ng-template>\n", styles: [".header{transition:box-shadow .3s ease-in-out}.header.shadow{box-shadow:0 0 5px 0 var(--nui-shadow-color-stronger, rgba(17, 17, 17, .6))}\n"] }] }], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.ConfiguratorComponent }, { type: i2.PreviewService }, { type: i3.FormBuilder }, { type: i4.WidgetTypesService }, { type: i5.ScrollDispatcher }], propDecorators: { formPizzagna: [{ type: Input }], cloneSelectionComponentType: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2lkZ2V0LWNsb25lci5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldC1jbG9uZXIvd2lkZ2V0LWNsb25lci5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldC1jbG9uZXIvd2lkZ2V0LWNsb25lci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUFpQixnQkFBZ0IsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3ZFLE9BQU8sRUFFSCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxLQUFLLEdBR1IsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFFLFdBQVcsRUFBYSxNQUFNLGdCQUFnQixDQUFDO0FBQ3hELE9BQU8sU0FBUyxNQUFNLGtCQUFrQixDQUFDO0FBQ3pDLE9BQU8sRUFBRSxlQUFlLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQ2hELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUczQyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUM1RSxPQUFPLEVBSUgsYUFBYSxHQUNoQixNQUFNLGdCQUFnQixDQUFDO0FBQ3hCLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNoRSxPQUFPLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQzs7Ozs7Ozs7Ozs7OztBQVUvRSxNQUFNLE9BQU8scUJBQXFCO2FBR2hCLGdCQUFXLEdBQUcsdUJBQXVCLEFBQTFCLENBQTJCO0lBa0JwRCxZQUNXLGNBQWlDLEVBQ2pDLFlBQW1DLEVBQ2xDLGNBQThCLEVBQzlCLFdBQXdCLEVBQ3hCLGtCQUFzQyxFQUN0QyxnQkFBa0M7UUFMbkMsbUJBQWMsR0FBZCxjQUFjLENBQW1CO1FBQ2pDLGlCQUFZLEdBQVosWUFBWSxDQUF1QjtRQUNsQyxtQkFBYyxHQUFkLGNBQWMsQ0FBZ0I7UUFDOUIsZ0JBQVcsR0FBWCxXQUFXLENBQWE7UUFDeEIsdUJBQWtCLEdBQWxCLGtCQUFrQixDQUFvQjtRQUN0QyxxQkFBZ0IsR0FBaEIsZ0JBQWdCLENBQWtCO1FBakJ2QyxzQkFBaUIsR0FDcEIsSUFBSSxlQUFlLENBQW9CO1lBQ25DLFNBQVMsRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUU7WUFDMUIsZUFBZSxFQUFFLElBQUk7U0FDeEIsQ0FBQyxDQUFDO1FBQ0EsU0FBSSxHQUFHLEtBQUssQ0FBQztRQUNiLGFBQVEsR0FBRyxLQUFLLENBQUM7UUFDakIsb0JBQWUsR0FBRyxLQUFLLENBQUM7UUFDZCxhQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUN4QyxlQUFVLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQVVyQyxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7SUFDckIsQ0FBQztJQUVNLFFBQVE7UUFDWCxJQUFJLENBQUMsY0FBYyxDQUFDLGNBQWM7YUFDN0IsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7YUFDOUIsU0FBUyxDQUFDLENBQUMsUUFBd0IsRUFBRSxFQUFFO1lBQ3BDLElBQUksQ0FBQyx1QkFBdUIsQ0FBQyxRQUFRLENBQUMsQ0FBQztRQUMzQyxDQUFDLENBQUMsQ0FBQztRQUVQLElBQUksQ0FBQyxZQUFZLENBQUMsV0FBVzthQUN4QixJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUM5QixTQUFTLENBQUMsR0FBRyxFQUFFO1lBQ1osSUFBSSxDQUFDLFVBQVUsRUFBRSxDQUFDO1FBQ3RCLENBQUMsQ0FBQyxDQUFDO0lBQ1gsQ0FBQztJQUVNLGVBQWU7UUFDbEIscURBQXFEO1FBQ3JELElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxFQUFFLENBQUM7UUFFbkMsaUJBQWlCO1FBQ2pCLElBQUksQ0FBQyxnQkFBZ0I7YUFDaEIsUUFBUSxFQUFFO2FBQ1YsU0FBUyxDQUFDLENBQUMsS0FBMkIsRUFBRSxFQUFFO1lBQ3ZDLE1BQU0sT0FBTyxHQUFHLEtBQUssRUFBRSxhQUFhLEVBQUUsRUFBRSxhQUFhLENBQUM7WUFFdEQsSUFBSSxPQUFPLEVBQUUsU0FBUyxFQUFFLFFBQVEsQ0FBQyx5QkFBeUIsQ0FBQyxFQUFFO2dCQUN6RCxJQUFJLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQyxPQUFPLEVBQUUsU0FBUyxDQUFDO2dCQUNyQyxJQUFJLENBQUMsY0FBYyxDQUFDLGFBQWEsRUFBRSxDQUFDO2FBQ3ZDO1FBQ0wsQ0FBQyxDQUFDLENBQUM7SUFDWCxDQUFDO0lBRU0sV0FBVztRQUNkLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUN6QixJQUFJLENBQUMsVUFBVSxDQUFDLFFBQVEsRUFBRSxDQUFDO0lBQy9CLENBQUM7SUFFRCxtRkFBbUY7SUFFNUUsdUJBQXVCLENBQUMsV0FBMkI7UUFDdEQsSUFBSSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUU7WUFDdkIsT0FBTztTQUNWO1FBRUQsTUFBTSxhQUFhLEdBQUc7WUFDbEIsR0FBRyxJQUFJLENBQUMsWUFBWSxDQUFDLGFBQWE7WUFDbEMsUUFBUSxFQUFFO2dCQUNOLEdBQUcsSUFBSSxDQUFDLFlBQVksQ0FBQyxhQUFhLEVBQUUsUUFBUTtnQkFDNUMsQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDLEVBQUUsV0FBVzthQUM3QztTQUNKLENBQUM7UUFDRix1REFBdUQ7UUFDdkQseUlBQXlJO1FBQ3pJLElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ2xELENBQUM7SUFFTSxTQUFTO1FBQ1osT0FBTyxDQUNILENBQUMsQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLFFBQVEsRUFBRSxhQUFhO1lBQzlDLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxRQUFRLEVBQUUsa0JBQWtCLENBQ3JELENBQUM7SUFDTixDQUFDO0lBRU0sUUFBUTtRQUNYLElBQUksSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUU7WUFDbkIsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1lBQzdCLDZEQUE2RDtZQUM3RCxtR0FBbUc7WUFDbkcsbUVBQW1FO1lBQ25FLElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDdEMsSUFBSSxDQUFDLElBQUksQ0FBQyxzQkFBc0IsQ0FBQyxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsQ0FBQyxDQUFDO1lBQ3RELE9BQU87U0FDVjtRQUVELElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNsQixJQUFJLENBQUMsWUFBWSxDQUFDLFVBQVUsRUFBRSxDQUFDO0lBQ25DLENBQUM7SUFFTSxRQUFRO1FBQ1gsSUFBSSxDQUFDLFlBQVksQ0FBQyxVQUFVLEVBQUUsQ0FBQztJQUNuQyxDQUFDO0lBRU0sZUFBZSxDQUFDLEtBQWlDO1FBQ3BELElBQUksQ0FBQyxlQUFlLEdBQUcsS0FBSyxDQUFDLGdCQUFnQixLQUFLLENBQUMsQ0FBQztRQUVwRCxJQUFJLEtBQUssQ0FBQyxnQkFBZ0IsS0FBSyxDQUFDLElBQUksS0FBSyxDQUFDLGlCQUFpQixLQUFLLENBQUMsRUFBRTtZQUMvRCxJQUFJLENBQUMsWUFBWSxHQUFHLFNBQVMsQ0FBQztZQUM5QixJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7WUFDakIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsY0FBYyxDQUFDLENBQUM7U0FDdEM7YUFBTSxJQUFJLEtBQUssQ0FBQyxnQkFBZ0IsS0FBSyxDQUFDLEVBQUU7WUFDckMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsa0JBQWtCLENBQUMsYUFBYSxDQUNyRCxJQUFJLENBQUMsY0FBYyxDQUFDLElBQUksRUFDeEIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQzlCLENBQUMsWUFBWSxDQUFDO1lBQ2YsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPO2dCQUN2QixJQUFJLENBQUMsY0FBYyxDQUFDLFFBQVEsQ0FBQyxhQUFhLENBQUMsYUFBYSxDQUFDLENBQUM7U0FDakU7SUFDTCxDQUFDO0lBRU0sUUFBUSxDQUFDLE1BQWU7UUFDM0IsSUFBSSxDQUFDLGNBQWMsR0FBRyxNQUFNLENBQUM7UUFDN0IsTUFBTSxhQUFhLEdBQUc7WUFDbEIsR0FBRyxNQUFNO1lBQ1QsRUFBRSxFQUFFLElBQUksQ0FBQyxZQUFZLENBQUMsYUFBYSxFQUFFLEVBQUUsSUFBSSxJQUFJO1NBQ2xELENBQUM7UUFFRixxRkFBcUY7UUFDckYsSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFckMsb0dBQW9HO1FBQ3BHLFVBQVUsQ0FBQyxHQUFHLEVBQUU7WUFDWiw0RUFBNEU7WUFDNUUsSUFBSSxDQUFDLFlBQVksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7WUFDekQsSUFBSSxDQUFDLGNBQWMsQ0FBQyxhQUFhLEVBQUUsQ0FBQztRQUN4QyxDQUFDLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyxVQUFVO1FBQ2QsSUFBSSxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUM7UUFDdkIsSUFBSSxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQztZQUN4QixTQUFTLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRTtZQUM5QixlQUFlLEVBQUUsQ0FBQyxJQUFJLENBQUMsSUFBSTtTQUM5QixDQUFDLENBQUM7SUFDUCxDQUFDO0lBRU8sU0FBUztRQUNiLElBQUksQ0FBQyxVQUFVLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN2QyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWE7YUFDbEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQzthQUMxRCxTQUFTLENBQUMsR0FBRyxFQUFFO1lBQ1osSUFBSSxDQUFDLGNBQWMsQ0FBQyxZQUFZLEVBQUUsQ0FBQztRQUN2QyxDQUFDLENBQUMsQ0FBQztJQUNYLENBQUM7a0lBcktRLHFCQUFxQjtzSEFBckIscUJBQXFCLHFOQ3REbEMsMmdEQWlEQTs7NEZES2EscUJBQXFCO2tCQVBqQyxTQUFTOytCQUNJLG1CQUFtQixRQUd2QixFQUFFLEtBQUssRUFBRSwwQkFBMEIsRUFBRSxtQkFDMUIsdUJBQXVCLENBQUMsTUFBTTtpUEFPdEMsWUFBWTtzQkFBcEIsS0FBSztnQkFDRywyQkFBMkI7c0JBQW5DLEtBQUsiLCJzb3VyY2VzQ29udGVudCI6WyIvLyDCqSAyMDIyIFNvbGFyV2luZHMgV29ybGR3aWRlLCBMTEMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4vL1xuLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weVxuLy8gIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvXG4vLyAgZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGVcbi8vICByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLCBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Jcbi8vICBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpc1xuLy8gIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4vL1xuLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW5cbi8vICBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbi8vXG4vLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXG4vLyAgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG4vLyAgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFXG4vLyAgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUlxuLy8gIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG4vLyAgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTlxuLy8gIFRIRSBTT0ZUV0FSRS5cblxuaW1wb3J0IHsgQ2RrU2Nyb2xsYWJsZSwgU2Nyb2xsRGlzcGF0Y2hlciB9IGZyb20gXCJAYW5ndWxhci9jZGsvb3ZlcmxheVwiO1xuaW1wb3J0IHtcbiAgICBBZnRlclZpZXdJbml0LFxuICAgIENoYW5nZURldGVjdGlvblN0cmF0ZWd5LFxuICAgIENoYW5nZURldGVjdG9yUmVmLFxuICAgIENvbXBvbmVudCxcbiAgICBJbnB1dCxcbiAgICBPbkRlc3Ryb3ksXG4gICAgT25Jbml0LFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgRm9ybUJ1aWxkZXIsIEZvcm1Hcm91cCB9IGZyb20gXCJAYW5ndWxhci9mb3Jtc1wiO1xuaW1wb3J0IGNsb25lRGVlcCBmcm9tIFwibG9kYXNoL2Nsb25lRGVlcFwiO1xuaW1wb3J0IHsgQmVoYXZpb3JTdWJqZWN0LCBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBJV2lkZ2V0IH0gZnJvbSBcIi4uLy4uLy4uL2NvbXBvbmVudHMvd2lkZ2V0L3R5cGVzXCI7XG5pbXBvcnQgeyBXaWRnZXRUeXBlc1NlcnZpY2UgfSBmcm9tIFwiLi4vLi4vLi4vc2VydmljZXMvd2lkZ2V0LXR5cGVzLnNlcnZpY2VcIjtcbmltcG9ydCB7XG4gICAgSUhhc0NoYW5nZURldGVjdG9yLFxuICAgIElQaXp6YWduYSxcbiAgICBJUGl6emFnbmFMYXllcixcbiAgICBQaXp6YWduYUxheWVyLFxufSBmcm9tIFwiLi4vLi4vLi4vdHlwZXNcIjtcbmltcG9ydCB7IFByZXZpZXdTZXJ2aWNlIH0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3ByZXZpZXcuc2VydmljZVwiO1xuaW1wb3J0IHsgQ29uZmlndXJhdG9yQ29tcG9uZW50IH0gZnJvbSBcIi4uL2NvbmZpZ3VyYXRvci9jb25maWd1cmF0b3IuY29tcG9uZW50XCI7XG5pbXBvcnQgeyBJRGFzaHdpelN0ZXBOYXZpZ2F0ZWRFdmVudCwgSURhc2h3aXpXYWl0RXZlbnQgfSBmcm9tIFwiLi4vd2l6YXJkL3R5cGVzXCI7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiBcIm51aS13aWRnZXQtY2xvbmVyXCIsXG4gICAgdGVtcGxhdGVVcmw6IFwiLi93aWRnZXQtY2xvbmVyLmNvbXBvbmVudC5odG1sXCIsXG4gICAgc3R5bGVVcmxzOiBbXCIuL3dpZGdldC1jbG9uZXIuY29tcG9uZW50Lmxlc3NcIl0sXG4gICAgaG9zdDogeyBjbGFzczogXCJkLWZsZXggZmxleC1jb2x1bW4gaC0xMDBcIiB9LFxuICAgIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBXaWRnZXRDbG9uZXJDb21wb25lbnRcbiAgICBpbXBsZW1lbnRzIE9uSW5pdCwgT25EZXN0cm95LCBBZnRlclZpZXdJbml0LCBJSGFzQ2hhbmdlRGV0ZWN0b3JcbntcbiAgICBwdWJsaWMgc3RhdGljIGxhdGVMb2FkS2V5ID0gXCJXaWRnZXRDbG9uZXJDb21wb25lbnRcIjtcblxuICAgIEBJbnB1dCgpIGZvcm1QaXp6YWduYT86IElQaXp6YWduYTtcbiAgICBASW5wdXQoKSBjbG9uZVNlbGVjdGlvbkNvbXBvbmVudFR5cGU6IEZ1bmN0aW9uO1xuXG4gICAgcHVibGljIGZvcm06IEZvcm1Hcm91cDtcbiAgICBwdWJsaWMgd2lkZ2V0VGVtcGxhdGU6IElXaWRnZXQ7XG4gICAgcHVibGljIG5hdmlnYXRpb25Db250cm9sOiBCZWhhdmlvclN1YmplY3Q8SURhc2h3aXpXYWl0RXZlbnQ+ID1cbiAgICAgICAgbmV3IEJlaGF2aW9yU3ViamVjdDxJRGFzaHdpeldhaXRFdmVudD4oe1xuICAgICAgICAgICAgYnVzeVN0YXRlOiB7IGJ1c3k6IGZhbHNlIH0sXG4gICAgICAgICAgICBhbGxvd1N0ZXBDaGFuZ2U6IHRydWUsXG4gICAgICAgIH0pO1xuICAgIHB1YmxpYyBidXN5ID0gZmFsc2U7XG4gICAgcHVibGljIHNjcm9sbGVkID0gZmFsc2U7XG4gICAgcHVibGljIGlzRm9ybURpc3BsYXllZCA9IGZhbHNlO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgIHByaXZhdGUgcmVzZXRGb3JtJCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHVibGljIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgcHVibGljIGNvbmZpZ3VyYXRvcjogQ29uZmlndXJhdG9yQ29tcG9uZW50LFxuICAgICAgICBwcml2YXRlIHByZXZpZXdTZXJ2aWNlOiBQcmV2aWV3U2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSBmb3JtQnVpbGRlcjogRm9ybUJ1aWxkZXIsXG4gICAgICAgIHByaXZhdGUgd2lkZ2V0VHlwZXNTZXJ2aWNlOiBXaWRnZXRUeXBlc1NlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgc2Nyb2xsRGlzcGF0Y2hlcjogU2Nyb2xsRGlzcGF0Y2hlclxuICAgICkge1xuICAgICAgICB0aGlzLnJlc2V0Rm9ybSgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5wcmV2aWV3U2VydmljZS5wcmV2aWV3Q2hhbmdlZFxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgocGl6emFnbmE6IElQaXp6YWduYUxheWVyKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy5vblByZXZpZXdQaXp6YWduYVVwZGF0ZShwaXp6YWduYSk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmNvbmZpZ3VyYXRvci5zdWJtaXRFcnJvclxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgdGhpcy50b2dnbGVCdXN5KCk7XG4gICAgICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICAvLyB0cmlnZ2VyIGRhc2h3aXogdG8gdXBkYXRlIGl0cyBidXR0b24gY29uZmlndXJhdGlvblxuICAgICAgICB0aGlzLmNoYW5nZURldGVjdG9yLm1hcmtGb3JDaGVjaygpO1xuXG4gICAgICAgIC8vIFNjcm9sbCBzaGFkb3dzXG4gICAgICAgIHRoaXMuc2Nyb2xsRGlzcGF0Y2hlclxuICAgICAgICAgICAgLnNjcm9sbGVkKClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKGV2ZW50OiBDZGtTY3JvbGxhYmxlIHwgdm9pZCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGVsZW1lbnQgPSBldmVudD8uZ2V0RWxlbWVudFJlZigpPy5uYXRpdmVFbGVtZW50O1xuXG4gICAgICAgICAgICAgICAgaWYgKGVsZW1lbnQ/LmNsYXNzTGlzdD8uY29udGFpbnMoXCJjb25maWd1cmF0b3Itc2Nyb2xsYWJsZVwiKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNjcm9sbGVkID0gISFlbGVtZW50Py5zY3JvbGxUb3A7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgICAgICAgdGhpcy5yZXNldEZvcm0kLmNvbXBsZXRlKCk7XG4gICAgfVxuXG4gICAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICAgIHB1YmxpYyBvblByZXZpZXdQaXp6YWduYVVwZGF0ZShjb25maWdMYXllcjogSVBpenphZ25hTGF5ZXIpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmlzRm9ybURpc3BsYXllZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgcHJldmlld1dpZGdldCA9IHtcbiAgICAgICAgICAgIC4uLnRoaXMuY29uZmlndXJhdG9yLnByZXZpZXdXaWRnZXQsXG4gICAgICAgICAgICBwaXp6YWduYToge1xuICAgICAgICAgICAgICAgIC4uLnRoaXMuY29uZmlndXJhdG9yLnByZXZpZXdXaWRnZXQ/LnBpenphZ25hLFxuICAgICAgICAgICAgICAgIFtQaXp6YWduYUxheWVyLkNvbmZpZ3VyYXRpb25dOiBjb25maWdMYXllcixcbiAgICAgICAgICAgIH0sXG4gICAgICAgIH07XG4gICAgICAgIC8vIFRPRE86IE1ha2UgcHJldmlld1dpZGdldCB0byBiZSBhc3NpZ25hYmxlIHRvIElXaWRnZXRcbiAgICAgICAgLy8gQHRzLWlnbm9yZTogVHlwZSAneyBwaXp6YWduYTogeyBjb25maWd1cmF0aW9uOiBJUGl6emFnbmFMYXllcjsgfTsgfScgaXMgbWlzc2luZyB0aGUgZm9sbG93aW5nIHByb3BlcnRpZXMgZnJvbSB0eXBlICdJV2lkZ2V0JzogaWQsIHR5cGVcbiAgICAgICAgdGhpcy5jb25maWd1cmF0b3IudXBkYXRlV2lkZ2V0KHByZXZpZXdXaWRnZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBjYW5GaW5pc2goKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAhIXRoaXMud2lkZ2V0VGVtcGxhdGU/LnBpenphZ25hPy5jb25maWd1cmF0aW9uICYmXG4gICAgICAgICAgICAhdGhpcy53aWRnZXRUZW1wbGF0ZT8ubWV0YWRhdGE/Lm5lZWRzQ29uZmlndXJhdGlvblxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBvbkZpbmlzaCgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuZm9ybS5pbnZhbGlkKSB7XG4gICAgICAgICAgICB0aGlzLmZvcm0ubWFya0FsbEFzVG91Y2hlZCgpO1xuICAgICAgICAgICAgLy8gaXMgdXNlZCB0byB0cmlnZ2VyIHZhbHVlQ2hhbmdlcyB0byBkZXRlY3QgY2hhbmdlcyBpbiBmb3Jtc1xuICAgICAgICAgICAgLy8gYXMgZm9ybSBpcyBpbnZhbGlkIGFuZCBtYXJraW5nIGFzIHRvdWNoZWQgZG9lc24ndCB0cmlnZ2VyIG5laXRoZXIgc3RhdHVzQ2hhbmdlcyBub3IgdmFsdWVDaGFuZ2VzXG4gICAgICAgICAgICAvLyBhbmQgdmFsaWRhdGlvbiBtZXNzYWdlcyBhbmQgc3R5bGVzIGFyZSBub3QgYXBwbGllZCBpbiB0aGF0IGNhc2VzXG4gICAgICAgICAgICB0aGlzLmZvcm0ucGF0Y2hWYWx1ZSh0aGlzLmZvcm0udmFsdWUpO1xuICAgICAgICAgICAgdGhpcy5mb3JtLnVwZGF0ZVZhbHVlQW5kVmFsaWRpdHkoeyBvbmx5U2VsZjogZmFsc2UgfSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnRvZ2dsZUJ1c3koKTtcbiAgICAgICAgdGhpcy5jb25maWd1cmF0b3IuZm9ybVN1Ym1pdCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBvbkNhbmNlbCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5jb25maWd1cmF0b3IuZm9ybUNhbmNlbCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBvblN0ZXBOYXZpZ2F0ZWQoZXZlbnQ6IElEYXNod2l6U3RlcE5hdmlnYXRlZEV2ZW50KTogdm9pZCB7XG4gICAgICAgIHRoaXMuaXNGb3JtRGlzcGxheWVkID0gZXZlbnQuY3VycmVudFN0ZXBJbmRleCA9PT0gMTtcblxuICAgICAgICBpZiAoZXZlbnQuY3VycmVudFN0ZXBJbmRleCA9PT0gMCAmJiBldmVudC5wcmV2aW91c1N0ZXBJbmRleCA9PT0gMSkge1xuICAgICAgICAgICAgdGhpcy5mb3JtUGl6emFnbmEgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgICB0aGlzLnJlc2V0Rm9ybSgpO1xuICAgICAgICAgICAgdGhpcy5vblNlbGVjdCh0aGlzLndpZGdldFRlbXBsYXRlKTtcbiAgICAgICAgfSBlbHNlIGlmIChldmVudC5jdXJyZW50U3RlcEluZGV4ID09PSAxKSB7XG4gICAgICAgICAgICB0aGlzLmZvcm1QaXp6YWduYSA9IHRoaXMud2lkZ2V0VHlwZXNTZXJ2aWNlLmdldFdpZGdldFR5cGUoXG4gICAgICAgICAgICAgICAgdGhpcy53aWRnZXRUZW1wbGF0ZS50eXBlLFxuICAgICAgICAgICAgICAgIHRoaXMud2lkZ2V0VGVtcGxhdGUudmVyc2lvblxuICAgICAgICAgICAgKS5jb25maWd1cmF0b3I7XG4gICAgICAgICAgICB0aGlzLnByZXZpZXdTZXJ2aWNlLnByZXZpZXcgPVxuICAgICAgICAgICAgICAgIHRoaXMud2lkZ2V0VGVtcGxhdGUucGl6emFnbmFbUGl6emFnbmFMYXllci5Db25maWd1cmF0aW9uXTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBvblNlbGVjdCh3aWRnZXQ6IElXaWRnZXQpOiB2b2lkIHtcbiAgICAgICAgdGhpcy53aWRnZXRUZW1wbGF0ZSA9IHdpZGdldDtcbiAgICAgICAgY29uc3QgcHJldmlld1dpZGdldCA9IHtcbiAgICAgICAgICAgIC4uLndpZGdldCxcbiAgICAgICAgICAgIGlkOiB0aGlzLmNvbmZpZ3VyYXRvci5wcmV2aWV3V2lkZ2V0Py5pZCB8fCBudWxsLFxuICAgICAgICB9O1xuXG4gICAgICAgIC8vIGNsZWFyIG91dCB0aGUgb2xkIHdpZGdldCBmaXJzdCBiZWNhdXNlIHdlIHdhbnQgdG8gcmVjcmVhdGUgdGhlIHdpZGdldCBmcm9tIHNjcmF0Y2hcbiAgICAgICAgdGhpcy5jb25maWd1cmF0b3IudXBkYXRlV2lkZ2V0KG51bGwpO1xuXG4gICAgICAgIC8vIHRoZSBuZXcgd2lkZ2V0IGlzIGFzc2lnbmVkIGluIGEgc2V0VGltZW91dCB0byBhcHBseSB0aGUgY2hhbmdlIGluIHRoZSBuZXh0IGNoYW5nZSBkZXRlY3Rpb24gY3ljbGVcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAvLyBAdHMtaWdub3JlOiBUeXBlICdudWxsJyBpcyBub3QgYXNzaWduYWJsZSB0byB0eXBlICdzdHJpbmcnIG9uIGlkIHByb3BlcnR5XG4gICAgICAgICAgICB0aGlzLmNvbmZpZ3VyYXRvci51cGRhdGVXaWRnZXQoY2xvbmVEZWVwKHByZXZpZXdXaWRnZXQpKTtcbiAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3IuZGV0ZWN0Q2hhbmdlcygpO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHRvZ2dsZUJ1c3koKSB7XG4gICAgICAgIHRoaXMuYnVzeSA9ICF0aGlzLmJ1c3k7XG4gICAgICAgIHRoaXMubmF2aWdhdGlvbkNvbnRyb2wubmV4dCh7XG4gICAgICAgICAgICBidXN5U3RhdGU6IHsgYnVzeTogdGhpcy5idXN5IH0sXG4gICAgICAgICAgICBhbGxvd1N0ZXBDaGFuZ2U6ICF0aGlzLmJ1c3ksXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgcmVzZXRGb3JtKCkge1xuICAgICAgICB0aGlzLnJlc2V0Rm9ybSQubmV4dCgpO1xuICAgICAgICB0aGlzLmZvcm0gPSB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKHt9KTtcbiAgICAgICAgdGhpcy5mb3JtLnN0YXR1c0NoYW5nZXNcbiAgICAgICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLnJlc2V0Rm9ybSQpLCB0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLmNoYW5nZURldGVjdG9yLm1hcmtGb3JDaGVjaygpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxufVxuIiwiPG51aS1jb25maWd1cmF0b3ItaGVhZGluZ1xuICAgIGNvbmZpZ3VyYXRvclRpdGxlPVwiQ3JlYXRpbmcgV2lkZ2V0XCJcbiAgICBpMThuLWNvbmZpZ3VyYXRvclRpdGxlXG4gICAgY2xhc3M9XCJoZWFkZXJcIlxuICAgIChjbG9zZSk9XCJvbkNhbmNlbCgpXCJcbiAgICBbZGlzYWJsZUNsb3NlQnV0dG9uXT1cImJ1c3lcIlxuICAgIFtjbGFzcy5zaGFkb3ddPVwic2Nyb2xsZWRcIlxuPjwvbnVpLWNvbmZpZ3VyYXRvci1oZWFkaW5nPlxuPG51aS1kYXNod2l6XG4gICAgW2hpZGVIZWFkZXJdPVwidHJ1ZVwiXG4gICAgW2NhblByb2NlZWRdPVwiISF3aWRnZXRUZW1wbGF0ZVwiXG4gICAgW2NhbkZpbmlzaF09XCJjYW5GaW5pc2goKVwiXG4gICAgZmluaXNoVGV4dD1cIkNyZWF0ZSBXaWRnZXRcIlxuICAgIGkxOG4tZmluaXNoVGV4dFxuICAgIFtuYXZpZ2F0aW9uQ29udHJvbF09XCJuYXZpZ2F0aW9uQ29udHJvbFwiXG4gICAgKGZpbmlzaCk9XCJvbkZpbmlzaCgpXCJcbiAgICAoY2FuY2VsKT1cIm9uQ2FuY2VsKClcIlxuICAgIChzdGVwTmF2aWdhdGVkKT1cIm9uU3RlcE5hdmlnYXRlZCgkZXZlbnQpXCJcbj5cbiAgICA8bnVpLWRhc2h3aXotc3RlcCBbc3RlcFRlbXBsYXRlXT1cInN0ZXAxXCIgbmV4dFRleHQ9XCJDdXN0b21pemVcIiBpMThuLW5leHRUZXh0PlxuICAgIDwvbnVpLWRhc2h3aXotc3RlcD5cbiAgICA8bnVpLWRhc2h3aXotc3RlcCBbc3RlcFRlbXBsYXRlXT1cInN0ZXAyXCI+PC9udWktZGFzaHdpei1zdGVwPlxuPC9udWktZGFzaHdpej5cblxuPG5nLXRlbXBsYXRlICNzdGVwMT5cbiAgICA8ZGl2IGNsYXNzPVwicC00IGgtMTAwXCI+XG4gICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgIG51aUNvbXBvbmVudFBvcnRhbFxuICAgICAgICAgICAgW2NvbXBvbmVudFR5cGVdPVwiY2xvbmVTZWxlY3Rpb25Db21wb25lbnRUeXBlXCJcbiAgICAgICAgICAgIFtvdXRwdXRzXT1cIlsnd2lkZ2V0U2VsZWN0ZWQnXVwiXG4gICAgICAgICAgICAob3V0cHV0KT1cIm9uU2VsZWN0KCRldmVudC5wYXlsb2FkKVwiXG4gICAgICAgICAgICAjY29tcG9uZW50UG9ydGFsPVwibnVpQ29tcG9uZW50UG9ydGFsXCJcbiAgICAgICAgPlxuICAgICAgICAgICAgPG5nLXRlbXBsYXRlXG4gICAgICAgICAgICAgICAgW2Nka1BvcnRhbE91dGxldF09XCJjb21wb25lbnRQb3J0YWwucG9ydGFsXCJcbiAgICAgICAgICAgICAgICAoYXR0YWNoZWQpPVwiY29tcG9uZW50UG9ydGFsLmF0dGFjaGVkKCRldmVudClcIlxuICAgICAgICAgICAgPjwvbmctdGVtcGxhdGU+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgIDwvZGl2PlxuPC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNzdGVwMj5cbiAgICA8ZGl2IFtmb3JtR3JvdXBdPVwiZm9ybVwiPlxuICAgICAgICA8bnVpLXBpenphZ25hXG4gICAgICAgICAgICAjZm9ybVBpenphZ25hQ29tcG9uZW50XG4gICAgICAgICAgICBbKHBpenphZ25hKV09XCJmb3JtUGl6emFnbmFcIlxuICAgICAgICA+PC9udWktcGl6emFnbmE+XG4gICAgPC9kaXY+XG48L25nLXRlbXBsYXRlPlxuIl19