@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
151 lines • 38.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 { ChangeDetectorRef, Component, EventEmitter, Inject, Input, Output, } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";
import { Subject } from "rxjs";
import { takeUntil } from "rxjs/operators";
import { EventBus } from "@nova-ui/bits";
import { TimeseriesChartPreset } from "../../../../../components/timeseries-widget/types";
import { PREVIEW_EVENT, REFRESH } from "../../../../../services/types";
import { PIZZAGNA_EVENT_BUS, } from "../../../../../types";
import { LegendPlacement } from "../../../../../widget-types/common/widget/legend";
import { ConfiguratorHeadingService } from "../../../../services/configurator-heading.service";
import * as i0 from "@angular/core";
import * as i1 from "../../../../services/configurator-heading.service";
import * as i2 from "@angular/forms";
import * as i3 from "@angular/common";
import * as i4 from "@nova-ui/bits";
import * as i5 from "../../../widget-editor-accordion/widget-editor-accordion.component";
import * as i6 from "../../../widget-editor-accordion/widget-editor-accordion-header/widget-editor-accordion-header.component";
import * as i7 from "../../../../pipe/widget-editor-accordion-form-state.pipe";
import * as i8 from "../../../../pipe/widget-editor-accordion-header-icon.pipe";
export class TimeseriesMetadataConfigurationComponent {
static { this.lateLoadKey = "TimeseriesMetadataConfigurationComponent"; }
constructor(changeDetector, configuratorHeading, formBuilder, eventBus) {
this.changeDetector = changeDetector;
this.configuratorHeading = configuratorHeading;
this.formBuilder = formBuilder;
this.eventBus = eventBus;
this.legendPlacements = [];
this.timeSpans = [];
this.availableChartTypes = [
{ label: $localize `Line`, value: TimeseriesChartPreset.Line },
{
label: $localize `Stacked area`,
value: TimeseriesChartPreset.StackedArea,
},
{
label: $localize `Stacked % area`,
value: TimeseriesChartPreset.StackedPercentageArea,
},
{ label: $localize `Bar`, value: TimeseriesChartPreset.StackedBar },
{ label: $localize `Status`, value: TimeseriesChartPreset.StatusBar },
];
this.formReady = new EventEmitter();
this.destroy$ = new Subject();
this.form = this.formBuilder.group({
leftAxisLabel: [""],
startingTimespan: [null, Validators.required],
legendPlacement: [LegendPlacement.None, [Validators.required]],
preset: [TimeseriesChartPreset.Line, [Validators.required]],
});
}
ngOnInit() {
this.form
.get("startingTimespan")
?.valueChanges.pipe(takeUntil(this.destroy$))
.subscribe((value) => {
// this is to wait until the value of 'startingTimespan' is converted to the preview
setTimeout(() => {
this.eventBus.next(PREVIEW_EVENT, {
payload: { id: REFRESH },
});
});
});
this.formReady.emit(this.form);
}
ngOnChanges(changes) {
const startingTimespanControl = this.form.get("startingTimespan");
if (changes.leftAxisLabel) {
this.form
.get("leftAxisLabel")
?.setValue(this.leftAxisLabel, { emitEvent: false });
}
if (changes.startingTimespan) {
startingTimespanControl?.setValue(this.startingTimespan, {
emitEvent: false,
});
}
if (changes.legendPlacement) {
this.form
.get("legendPlacement")
?.setValue(this.legendPlacement, { emitEvent: false });
}
if (changes.preset) {
this.form
.get("preset")
?.setValue(this.preset, { emitEvent: false });
}
if (changes.timeSpans) {
if (!startingTimespanControl?.value && this.timeSpans?.length > 0) {
startingTimespanControl?.setValue(this.timeSpans[0]);
}
}
}
getSecondaryText() {
const startingTimespan = this.form.controls["startingTimespan"].value;
const forTimeframe = (startingTimespan && startingTimespan.name) ||
$localize `No timespan`;
const forLegend = this.form.controls["legendPlacement"].value || $localize `No legend`;
const forAxisLabel = this.form.controls["leftAxisLabel"].value ||
$localize `No y-axis label`;
return `${forTimeframe}, ${forLegend}, ${forAxisLabel}`;
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeseriesMetadataConfigurationComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: i1.ConfiguratorHeadingService }, { token: i2.FormBuilder }, { token: PIZZAGNA_EVENT_BUS }], target: i0.ɵɵFactoryTarget.Component }); }
/** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: TimeseriesMetadataConfigurationComponent, selector: "nui-timeseries-metadata-configuration", inputs: { legendPlacements: "legendPlacements", timeSpans: "timeSpans", startingTimespan: "startingTimespan", legendPlacement: "legendPlacement", leftAxisLabel: "leftAxisLabel", preset: "preset", availableChartTypes: "availableChartTypes" }, 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 : 'edit' | async\"\n [iconColor]=\"form | nuiFormHeaderIconPipe : 'gray' : '' | async\"\n [subtitle]=\"getSecondaryText()\"\n headerTitle=\"Timeseries metadata\"\n />\n <div class=\"timeseries-metadata-configuration__accordion-content\">\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Starting timespan\"\n i18n-caption\n [control]=\"form.controls['startingTimespan']\"\n >\n <nui-select-v2\n placeholder=\"Starting Timespan\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"startingTimespan\"\n >\n <nui-select-v2-option\n *ngFor=\"let timeSpan of timeSpans\"\n [value]=\"timeSpan\"\n >\n {{ timeSpan.name }}\n </nui-select-v2-option>\n </nui-select-v2>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Chart legend\"\n i18n-caption\n [control]=\"form.controls['legendPlacement']\"\n >\n <nui-select-v2\n placeholder=\"Select Legend Orientation\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"legendPlacement\"\n >\n <nui-select-v2-option\n *ngFor=\"let legendPlacement of legendPlacements\"\n [value]=\"legendPlacement.id\"\n >\n {{ legendPlacement.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Chart y-axis label\"\n i18n-caption\n [control]=\"form.controls['leftAxisLabel']\"\n >\n <nui-textbox\n formControlName=\"leftAxisLabel\"\n placeholder=\"Set chart y-axis label\"\n i18n-placeholder\n ></nui-textbox>\n\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <nui-form-field\n caption=\"Chart type\"\n i18n-caption\n [control]=\"form.controls['preset']\"\n >\n <nui-select-v2\n placeholder=\"Chart type\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"preset\"\n >\n <nui-select-v2-option\n *ngFor=\"let option of availableChartTypes\"\n [value]=\"option.value\"\n >{{ option.label }}</nui-select-v2-option\n >\n </nui-select-v2>\n\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".timeseries-metadata-configuration__accordion-content{padding:15px 15px 15px 46px}\n"], dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: 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: "component", type: i4.TextboxComponent, selector: "nui-textbox", inputs: ["caption", "autocomplete", "info", "customBoxWidth", "disabled", "hint", "name", "placeholder", "readonly", "rows", "type", "value", "isInErrorState", "ariaLabel", "isBusy"], outputs: ["textChange", "blurred"] }, { 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: i4.ValidationMessageComponent, selector: "nui-validation-message", inputs: ["show", "for"] }, { kind: "component", type: i5.WidgetEditorAccordionComponent, selector: "nui-widget-editor-accordion", inputs: ["showOpenStateIcon", "state"], outputs: ["openToggle"] }, { kind: "component", type: i6.WidgetEditorAccordionHeaderComponent, selector: "nui-widget-editor-accordion-header", inputs: ["headerIcon", "iconColor", "subtitle", "headerTitle"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i7.WidgetEditorAccordionFormStatePipe, name: "nuiWidgetEditorAccordionFormState" }, { kind: "pipe", type: i8.FormHeaderIconPipePipe, name: "nuiFormHeaderIconPipe" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeseriesMetadataConfigurationComponent, decorators: [{
type: Component,
args: [{ selector: "nui-timeseries-metadata-configuration", template: "<nui-widget-editor-accordion\n [formGroup]=\"form\"\n [state]=\"form | nuiWidgetEditorAccordionFormState | async\"\n>\n <nui-widget-editor-accordion-header\n [headerIcon]=\"form | nuiFormHeaderIconPipe : 'edit' | async\"\n [iconColor]=\"form | nuiFormHeaderIconPipe : 'gray' : '' | async\"\n [subtitle]=\"getSecondaryText()\"\n headerTitle=\"Timeseries metadata\"\n />\n <div class=\"timeseries-metadata-configuration__accordion-content\">\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Starting timespan\"\n i18n-caption\n [control]=\"form.controls['startingTimespan']\"\n >\n <nui-select-v2\n placeholder=\"Starting Timespan\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"startingTimespan\"\n >\n <nui-select-v2-option\n *ngFor=\"let timeSpan of timeSpans\"\n [value]=\"timeSpan\"\n >\n {{ timeSpan.name }}\n </nui-select-v2-option>\n </nui-select-v2>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Chart legend\"\n i18n-caption\n [control]=\"form.controls['legendPlacement']\"\n >\n <nui-select-v2\n placeholder=\"Select Legend Orientation\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"legendPlacement\"\n >\n <nui-select-v2-option\n *ngFor=\"let legendPlacement of legendPlacements\"\n [value]=\"legendPlacement.id\"\n >\n {{ legendPlacement.label }}\n </nui-select-v2-option>\n </nui-select-v2>\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <div class=\"mb-4\">\n <nui-form-field\n caption=\"Chart y-axis label\"\n i18n-caption\n [control]=\"form.controls['leftAxisLabel']\"\n >\n <nui-textbox\n formControlName=\"leftAxisLabel\"\n placeholder=\"Set chart y-axis label\"\n i18n-placeholder\n ></nui-textbox>\n\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n\n <nui-form-field\n caption=\"Chart type\"\n i18n-caption\n [control]=\"form.controls['preset']\"\n >\n <nui-select-v2\n placeholder=\"Chart type\"\n i18n-placeholder\n [popupViewportMargin]=\"configuratorHeading.height$ | async\"\n formControlName=\"preset\"\n >\n <nui-select-v2-option\n *ngFor=\"let option of availableChartTypes\"\n [value]=\"option.value\"\n >{{ option.label }}</nui-select-v2-option\n >\n </nui-select-v2>\n\n <nui-validation-message for=\"required\" i18n>\n This field is required\n </nui-validation-message>\n </nui-form-field>\n </div>\n</nui-widget-editor-accordion>\n", styles: [".timeseries-metadata-configuration__accordion-content{padding:15px 15px 15px 46px}\n"] }]
}], ctorParameters: () => [{ type: i0.ChangeDetectorRef }, { type: i1.ConfiguratorHeadingService }, { type: i2.FormBuilder }, { type: i4.EventBus, decorators: [{
type: Inject,
args: [PIZZAGNA_EVENT_BUS]
}] }], propDecorators: { legendPlacements: [{
type: Input
}], timeSpans: [{
type: Input
}], startingTimespan: [{
type: Input
}], legendPlacement: [{
type: Input
}], leftAxisLabel: [{
type: Input
}], preset: [{
type: Input
}], availableChartTypes: [{
type: Input
}], formReady: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXNlcmllcy1tZXRhZGF0YS1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29uZmlndXJhdG9yL2NvbXBvbmVudHMvd2lkZ2V0cy9jb25maWd1cmF0b3ItaXRlbXMvdGltZXNlcmllcy1tZXRhZGF0YS1jb25maWd1cmF0aW9uL3RpbWVzZXJpZXMtbWV0YWRhdGEtY29uZmlndXJhdGlvbi5jb21wb25lbnQudHMiLCIuLi8uLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbmZpZ3VyYXRvci9jb21wb25lbnRzL3dpZGdldHMvY29uZmlndXJhdG9yLWl0ZW1zL3RpbWVzZXJpZXMtbWV0YWRhdGEtY29uZmlndXJhdGlvbi90aW1lc2VyaWVzLW1ldGFkYXRhLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEseURBQXlEO0FBQ3pELEVBQUU7QUFDRiwrRUFBK0U7QUFDL0UsNEVBQTRFO0FBQzVFLDhFQUE4RTtBQUM5RSwrRUFBK0U7QUFDL0UsOEVBQThFO0FBQzlFLDREQUE0RDtBQUM1RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLHVEQUF1RDtBQUN2RCxFQUFFO0FBQ0YsNkVBQTZFO0FBQzdFLDRFQUE0RTtBQUM1RSwrRUFBK0U7QUFDL0UsMEVBQTBFO0FBQzFFLGlGQUFpRjtBQUNqRiw2RUFBNkU7QUFDN0UsaUJBQWlCO0FBRWpCLE9BQU8sRUFDSCxpQkFBaUIsRUFDakIsU0FBUyxFQUNULFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUlMLE1BQU0sR0FFVCxNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsV0FBVyxFQUFhLFVBQVUsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3BFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTNDLE9BQU8sRUFBRSxRQUFRLEVBQVUsTUFBTSxlQUFlLENBQUM7QUFFakQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sbURBQW1ELENBQUM7QUFDMUYsT0FBTyxFQUFFLGFBQWEsRUFBRSxPQUFPLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUN2RSxPQUFPLEVBR0gsa0JBQWtCLEdBQ3JCLE1BQU0sc0JBQXNCLENBQUM7QUFDOUIsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQ25GLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLG1EQUFtRCxDQUFDOzs7Ozs7Ozs7O0FBaUIvRixNQUFNLE9BQU8sd0NBQXdDO2FBRzFDLGdCQUFXLEdBQUcsMENBQTBDLEFBQTdDLENBQThDO0lBNEJoRSxZQUNXLGNBQWlDLEVBQ2pDLG1CQUErQyxFQUM5QyxXQUF3QixFQUNJLFFBQTBCO1FBSHZELG1CQUFjLEdBQWQsY0FBYyxDQUFtQjtRQUNqQyx3QkFBbUIsR0FBbkIsbUJBQW1CLENBQTRCO1FBQzlDLGdCQUFXLEdBQVgsV0FBVyxDQUFhO1FBQ0ksYUFBUSxHQUFSLFFBQVEsQ0FBa0I7UUE5QnpELHFCQUFnQixHQUFzQixFQUFFLENBQUM7UUFDekMsY0FBUyxHQUFzQixFQUFFLENBQUM7UUFLbEMsd0JBQW1CLEdBQWlDO1lBQ3pELEVBQUUsS0FBSyxFQUFFLFNBQVMsQ0FBQSxNQUFNLEVBQUUsS0FBSyxFQUFFLHFCQUFxQixDQUFDLElBQUksRUFBRTtZQUM3RDtnQkFDSSxLQUFLLEVBQUUsU0FBUyxDQUFBLGNBQWM7Z0JBQzlCLEtBQUssRUFBRSxxQkFBcUIsQ0FBQyxXQUFXO2FBQzNDO1lBQ0Q7Z0JBQ0ksS0FBSyxFQUFFLFNBQVMsQ0FBQSxnQkFBZ0I7Z0JBQ2hDLEtBQUssRUFBRSxxQkFBcUIsQ0FBQyxxQkFBcUI7YUFDckQ7WUFDRCxFQUFFLEtBQUssRUFBRSxTQUFTLENBQUEsS0FBSyxFQUFFLEtBQUssRUFBRSxxQkFBcUIsQ0FBQyxVQUFVLEVBQUU7WUFDbEUsRUFBRSxLQUFLLEVBQUUsU0FBUyxDQUFBLFFBQVEsRUFBRSxLQUFLLEVBQUUscUJBQXFCLENBQUMsU0FBUyxFQUFFO1NBQ3ZFLENBQUM7UUFFUSxjQUFTLEdBQUcsSUFBSSxZQUFZLEVBQWEsQ0FBQztRQUluQyxhQUFRLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQVE1QyxJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDO1lBQy9CLGFBQWEsRUFBRSxDQUFDLEVBQUUsQ0FBQztZQUNuQixnQkFBZ0IsRUFBRSxDQUFDLElBQUksRUFBRSxVQUFVLENBQUMsUUFBUSxDQUFDO1lBQzdDLGVBQWUsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLEVBQUUsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLENBQUM7WUFDOUQsTUFBTSxFQUFFLENBQUMscUJBQXFCLENBQUMsSUFBSSxFQUFFLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1NBQzlELENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTSxRQUFRO1FBQ1gsSUFBSSxDQUFDLElBQUk7YUFDSixHQUFHLENBQUMsa0JBQWtCLENBQUM7WUFDeEIsRUFBRSxZQUFZLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7YUFDNUMsU0FBUyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDakIsb0ZBQW9GO1lBQ3BGLFVBQVUsQ0FBQyxHQUFHLEVBQUU7Z0JBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFO29CQUM5QixPQUFPLEVBQUUsRUFBRSxFQUFFLEVBQUUsT0FBTyxFQUFFO2lCQUMzQixDQUFDLENBQUM7WUFDUCxDQUFDLENBQUMsQ0FBQztRQUNQLENBQUMsQ0FBQyxDQUFDO1FBRVAsSUFBSSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ25DLENBQUM7SUFFTSxXQUFXLENBQUMsT0FBc0I7UUFDckMsTUFBTSx1QkFBdUIsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO1FBRWxFLElBQUksT0FBTyxDQUFDLGFBQWEsRUFBRTtZQUN2QixJQUFJLENBQUMsSUFBSTtpQkFDSixHQUFHLENBQUMsZUFBZSxDQUFDO2dCQUNyQixFQUFFLFFBQVEsQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUM7U0FDNUQ7UUFFRCxJQUFJLE9BQU8sQ0FBQyxnQkFBZ0IsRUFBRTtZQUMxQix1QkFBdUIsRUFBRSxRQUFRLENBQUMsSUFBSSxDQUFDLGdCQUFnQixFQUFFO2dCQUNyRCxTQUFTLEVBQUUsS0FBSzthQUNuQixDQUFDLENBQUM7U0FDTjtRQUVELElBQUksT0FBTyxDQUFDLGVBQWUsRUFBRTtZQUN6QixJQUFJLENBQUMsSUFBSTtpQkFDSixHQUFHLENBQUMsaUJBQWlCLENBQUM7Z0JBQ3ZCLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztTQUM5RDtRQUVELElBQUksT0FBTyxDQUFDLE1BQU0sRUFBRTtZQUNoQixJQUFJLENBQUMsSUFBSTtpQkFDSixHQUFHLENBQUMsUUFBUSxDQUFDO2dCQUNkLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxTQUFTLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztTQUNyRDtRQUVELElBQUksT0FBTyxDQUFDLFNBQVMsRUFBRTtZQUNuQixJQUFJLENBQUMsdUJBQXVCLEVBQUUsS0FBSyxJQUFJLElBQUksQ0FBQyxTQUFTLEVBQUUsTUFBTSxHQUFHLENBQUMsRUFBRTtnQkFDL0QsdUJBQXVCLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQzthQUN4RDtTQUNKO0lBQ0wsQ0FBQztJQUVNLGdCQUFnQjtRQUNuQixNQUFNLGdCQUFnQixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFDLENBQUMsS0FBSyxDQUFDO1FBQ3RFLE1BQU0sWUFBWSxHQUNkLENBQUMsZ0JBQWdCLElBQUksZ0JBQWdCLENBQUMsSUFBSSxDQUFDO1lBQzNDLFNBQVMsQ0FBQSxhQUFhLENBQUM7UUFDM0IsTUFBTSxTQUFTLEdBQ1gsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxLQUFLLElBQUksU0FBUyxDQUFBLFdBQVcsQ0FBQztRQUN4RSxNQUFNLFlBQVksR0FDZCxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxlQUFlLENBQUMsQ0FBQyxLQUFLO1lBQ3pDLFNBQVMsQ0FBQSxpQkFBaUIsQ0FBQztRQUMvQixPQUFPLEdBQUcsWUFBWSxLQUFLLFNBQVMsS0FBSyxZQUFZLEVBQUUsQ0FBQztJQUM1RCxDQUFDO0lBRU0sV0FBVztRQUNkLElBQUksQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUM3QixDQUFDO2tJQS9HUSx3Q0FBd0Msd0hBbUNyQyxrQkFBa0I7c0hBbkNyQix3Q0FBd0MseVhDL0RyRCxpN0hBdUdBOzs0RkR4Q2Esd0NBQXdDO2tCQUxwRCxTQUFTOytCQUNJLHVDQUF1Qzs7MEJBdUM1QyxNQUFNOzJCQUFDLGtCQUFrQjt5Q0E5QnJCLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFDRyxTQUFTO3NCQUFqQixLQUFLO2dCQUNHLGdCQUFnQjtzQkFBeEIsS0FBSztnQkFDRyxlQUFlO3NCQUF2QixLQUFLO2dCQUNHLGFBQWE7c0JBQXJCLEtBQUs7Z0JBQ0csTUFBTTtzQkFBZCxLQUFLO2dCQUNHLG1CQUFtQjtzQkFBM0IsS0FBSztnQkFjSSxTQUFTO3NCQUFsQixNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiLy8gwqkgMjAyMiBTb2xhcldpbmRzIFdvcmxkd2lkZSwgTExDLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbi8vICBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0b1xuLy8gIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlXG4vLyAgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yXG4vLyAgc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbi8vICBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG4vLyAgYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuLy8gIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLFxuLy8gIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuLy8gIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbi8vICBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLFxuLy8gIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbi8vICBUSEUgU09GVFdBUkUuXG5cbmltcG9ydCB7XG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEV2ZW50RW1pdHRlcixcbiAgICBJbmplY3QsXG4gICAgSW5wdXQsXG4gICAgT25DaGFuZ2VzLFxuICAgIE9uRGVzdHJveSxcbiAgICBPbkluaXQsXG4gICAgT3V0cHV0LFxuICAgIFNpbXBsZUNoYW5nZXMsXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5pbXBvcnQgeyBGb3JtQnVpbGRlciwgRm9ybUdyb3VwLCBWYWxpZGF0b3JzIH0gZnJvbSBcIkBhbmd1bGFyL2Zvcm1zXCI7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7IHRha2VVbnRpbCB9IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBFdmVudEJ1cywgSUV2ZW50IH0gZnJvbSBcIkBub3ZhLXVpL2JpdHNcIjtcblxuaW1wb3J0IHsgVGltZXNlcmllc0NoYXJ0UHJlc2V0IH0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL2NvbXBvbmVudHMvdGltZXNlcmllcy13aWRnZXQvdHlwZXNcIjtcbmltcG9ydCB7IFBSRVZJRVdfRVZFTlQsIFJFRlJFU0ggfSBmcm9tIFwiLi4vLi4vLi4vLi4vLi4vc2VydmljZXMvdHlwZXNcIjtcbmltcG9ydCB7XG4gICAgSUhhc0NoYW5nZURldGVjdG9yLFxuICAgIElIYXNGb3JtLFxuICAgIFBJWlpBR05BX0VWRU5UX0JVUyxcbn0gZnJvbSBcIi4uLy4uLy4uLy4uLy4uL3R5cGVzXCI7XG5pbXBvcnQgeyBMZWdlbmRQbGFjZW1lbnQgfSBmcm9tIFwiLi4vLi4vLi4vLi4vLi4vd2lkZ2V0LXR5cGVzL2NvbW1vbi93aWRnZXQvbGVnZW5kXCI7XG5pbXBvcnQgeyBDb25maWd1cmF0b3JIZWFkaW5nU2VydmljZSB9IGZyb20gXCIuLi8uLi8uLi8uLi9zZXJ2aWNlcy9jb25maWd1cmF0b3ItaGVhZGluZy5zZXJ2aWNlXCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSVRpbWVzZXJpZXNDaGFydFR5cGVPcHRpb24ge1xuICAgIGxhYmVsOiBzdHJpbmc7XG4gICAgdmFsdWU6IFRpbWVzZXJpZXNDaGFydFByZXNldDtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJVGltZVNwYW5PcHRpb24ge1xuICAgIGlkOiBzdHJpbmc7XG4gICAgbmFtZTogc3RyaW5nO1xufVxuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogXCJudWktdGltZXNlcmllcy1tZXRhZGF0YS1jb25maWd1cmF0aW9uXCIsXG4gICAgdGVtcGxhdGVVcmw6IFwiLi90aW1lc2VyaWVzLW1ldGFkYXRhLWNvbmZpZ3VyYXRpb24uY29tcG9uZW50Lmh0bWxcIixcbiAgICBzdHlsZVVybHM6IFtcIi4vdGltZXNlcmllcy1tZXRhZGF0YS1jb25maWd1cmF0aW9uLmNvbXBvbmVudC5sZXNzXCJdLFxufSlcbmV4cG9ydCBjbGFzcyBUaW1lc2VyaWVzTWV0YWRhdGFDb25maWd1cmF0aW9uQ29tcG9uZW50XG4gICAgaW1wbGVtZW50cyBJSGFzQ2hhbmdlRGV0ZWN0b3IsIElIYXNGb3JtLCBPbkluaXQsIE9uQ2hhbmdlcywgT25EZXN0cm95XG57XG4gICAgc3RhdGljIGxhdGVMb2FkS2V5ID0gXCJUaW1lc2VyaWVzTWV0YWRhdGFDb25maWd1cmF0aW9uQ29tcG9uZW50XCI7XG5cbiAgICBASW5wdXQoKSBsZWdlbmRQbGFjZW1lbnRzOiBMZWdlbmRQbGFjZW1lbnRbXSA9IFtdO1xuICAgIEBJbnB1dCgpIHRpbWVTcGFuczogSVRpbWVTcGFuT3B0aW9uW10gPSBbXTtcbiAgICBASW5wdXQoKSBzdGFydGluZ1RpbWVzcGFuOiBhbnk7XG4gICAgQElucHV0KCkgbGVnZW5kUGxhY2VtZW50OiBMZWdlbmRQbGFjZW1lbnQ7XG4gICAgQElucHV0KCkgbGVmdEF4aXNMYWJlbDogc3RyaW5nO1xuICAgIEBJbnB1dCgpIHByZXNldDogVGltZXNlcmllc0NoYXJ0UHJlc2V0O1xuICAgIEBJbnB1dCgpIGF2YWlsYWJsZUNoYXJ0VHlwZXM6IElUaW1lc2VyaWVzQ2hhcnRUeXBlT3B0aW9uW10gPSBbXG4gICAgICAgIHsgbGFiZWw6ICRsb2NhbGl6ZWBMaW5lYCwgdmFsdWU6IFRpbWVzZXJpZXNDaGFydFByZXNldC5MaW5lIH0sXG4gICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAkbG9jYWxpemVgU3RhY2tlZCBhcmVhYCxcbiAgICAgICAgICAgIHZhbHVlOiBUaW1lc2VyaWVzQ2hhcnRQcmVzZXQuU3RhY2tlZEFyZWEsXG4gICAgICAgIH0sXG4gICAgICAgIHtcbiAgICAgICAgICAgIGxhYmVsOiAkbG9jYWxpemVgU3RhY2tlZCAlIGFyZWFgLFxuICAgICAgICAgICAgdmFsdWU6IFRpbWVzZXJpZXNDaGFydFByZXNldC5TdGFja2VkUGVyY2VudGFnZUFyZWEsXG4gICAgICAgIH0sXG4gICAgICAgIHsgbGFiZWw6ICRsb2NhbGl6ZWBCYXJgLCB2YWx1ZTogVGltZXNlcmllc0NoYXJ0UHJlc2V0LlN0YWNrZWRCYXIgfSxcbiAgICAgICAgeyBsYWJlbDogJGxvY2FsaXplYFN0YXR1c2AsIHZhbHVlOiBUaW1lc2VyaWVzQ2hhcnRQcmVzZXQuU3RhdHVzQmFyIH0sXG4gICAgXTtcblxuICAgIEBPdXRwdXQoKSBmb3JtUmVhZHkgPSBuZXcgRXZlbnRFbWl0dGVyPEZvcm1Hcm91cD4oKTtcblxuICAgIHB1YmxpYyBmb3JtOiBGb3JtR3JvdXA7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IGRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgY2hhbmdlRGV0ZWN0b3I6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICBwdWJsaWMgY29uZmlndXJhdG9ySGVhZGluZzogQ29uZmlndXJhdG9ySGVhZGluZ1NlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgZm9ybUJ1aWxkZXI6IEZvcm1CdWlsZGVyLFxuICAgICAgICBASW5qZWN0KFBJWlpBR05BX0VWRU5UX0JVUykgcHJpdmF0ZSBldmVudEJ1czogRXZlbnRCdXM8SUV2ZW50PlxuICAgICkge1xuICAgICAgICB0aGlzLmZvcm0gPSB0aGlzLmZvcm1CdWlsZGVyLmdyb3VwKHtcbiAgICAgICAgICAgIGxlZnRBeGlzTGFiZWw6IFtcIlwiXSxcbiAgICAgICAgICAgIHN0YXJ0aW5nVGltZXNwYW46IFtudWxsLCBWYWxpZGF0b3JzLnJlcXVpcmVkXSxcbiAgICAgICAgICAgIGxlZ2VuZFBsYWNlbWVudDogW0xlZ2VuZFBsYWNlbWVudC5Ob25lLCBbVmFsaWRhdG9ycy5yZXF1aXJlZF1dLFxuICAgICAgICAgICAgcHJlc2V0OiBbVGltZXNlcmllc0NoYXJ0UHJlc2V0LkxpbmUsIFtWYWxpZGF0b3JzLnJlcXVpcmVkXV0sXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5mb3JtXG4gICAgICAgICAgICAuZ2V0KFwic3RhcnRpbmdUaW1lc3BhblwiKVxuICAgICAgICAgICAgPy52YWx1ZUNoYW5nZXMucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCh2YWx1ZSkgPT4ge1xuICAgICAgICAgICAgICAgIC8vIHRoaXMgaXMgdG8gd2FpdCB1bnRpbCB0aGUgdmFsdWUgb2YgJ3N0YXJ0aW5nVGltZXNwYW4nIGlzIGNvbnZlcnRlZCB0byB0aGUgcHJldmlld1xuICAgICAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmV2ZW50QnVzLm5leHQoUFJFVklFV19FVkVOVCwge1xuICAgICAgICAgICAgICAgICAgICAgICAgcGF5bG9hZDogeyBpZDogUkVGUkVTSCB9LFxuICAgICAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgIHRoaXMuZm9ybVJlYWR5LmVtaXQodGhpcy5mb3JtKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkNoYW5nZXMoY2hhbmdlczogU2ltcGxlQ2hhbmdlcyk6IHZvaWQge1xuICAgICAgICBjb25zdCBzdGFydGluZ1RpbWVzcGFuQ29udHJvbCA9IHRoaXMuZm9ybS5nZXQoXCJzdGFydGluZ1RpbWVzcGFuXCIpO1xuXG4gICAgICAgIGlmIChjaGFuZ2VzLmxlZnRBeGlzTGFiZWwpIHtcbiAgICAgICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgICAgIC5nZXQoXCJsZWZ0QXhpc0xhYmVsXCIpXG4gICAgICAgICAgICAgICAgPy5zZXRWYWx1ZSh0aGlzLmxlZnRBeGlzTGFiZWwsIHsgZW1pdEV2ZW50OiBmYWxzZSB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjaGFuZ2VzLnN0YXJ0aW5nVGltZXNwYW4pIHtcbiAgICAgICAgICAgIHN0YXJ0aW5nVGltZXNwYW5Db250cm9sPy5zZXRWYWx1ZSh0aGlzLnN0YXJ0aW5nVGltZXNwYW4sIHtcbiAgICAgICAgICAgICAgICBlbWl0RXZlbnQ6IGZhbHNlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY2hhbmdlcy5sZWdlbmRQbGFjZW1lbnQpIHtcbiAgICAgICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgICAgIC5nZXQoXCJsZWdlbmRQbGFjZW1lbnRcIilcbiAgICAgICAgICAgICAgICA/LnNldFZhbHVlKHRoaXMubGVnZW5kUGxhY2VtZW50LCB7IGVtaXRFdmVudDogZmFsc2UgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY2hhbmdlcy5wcmVzZXQpIHtcbiAgICAgICAgICAgIHRoaXMuZm9ybVxuICAgICAgICAgICAgICAgIC5nZXQoXCJwcmVzZXRcIilcbiAgICAgICAgICAgICAgICA/LnNldFZhbHVlKHRoaXMucHJlc2V0LCB7IGVtaXRFdmVudDogZmFsc2UgfSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY2hhbmdlcy50aW1lU3BhbnMpIHtcbiAgICAgICAgICAgIGlmICghc3RhcnRpbmdUaW1lc3BhbkNvbnRyb2w/LnZhbHVlICYmIHRoaXMudGltZVNwYW5zPy5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgc3RhcnRpbmdUaW1lc3BhbkNvbnRyb2w/LnNldFZhbHVlKHRoaXMudGltZVNwYW5zWzBdKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBnZXRTZWNvbmRhcnlUZXh0KCk6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IHN0YXJ0aW5nVGltZXNwYW4gPSB0aGlzLmZvcm0uY29udHJvbHNbXCJzdGFydGluZ1RpbWVzcGFuXCJdLnZhbHVlO1xuICAgICAgICBjb25zdCBmb3JUaW1lZnJhbWUgPVxuICAgICAgICAgICAgKHN0YXJ0aW5nVGltZXNwYW4gJiYgc3RhcnRpbmdUaW1lc3Bhbi5uYW1lKSB8fFxuICAgICAgICAgICAgJGxvY2FsaXplYE5vIHRpbWVzcGFuYDtcbiAgICAgICAgY29uc3QgZm9yTGVnZW5kID1cbiAgICAgICAgICAgIHRoaXMuZm9ybS5jb250cm9sc1tcImxlZ2VuZFBsYWNlbWVudFwiXS52YWx1ZSB8fCAkbG9jYWxpemVgTm8gbGVnZW5kYDtcbiAgICAgICAgY29uc3QgZm9yQXhpc0xhYmVsID1cbiAgICAgICAgICAgIHRoaXMuZm9ybS5jb250cm9sc1tcImxlZnRBeGlzTGFiZWxcIl0udmFsdWUgfHxcbiAgICAgICAgICAgICRsb2NhbGl6ZWBObyB5LWF4aXMgbGFiZWxgO1xuICAgICAgICByZXR1cm4gYCR7Zm9yVGltZWZyYW1lfSwgJHtmb3JMZWdlbmR9LCAke2ZvckF4aXNMYWJlbH1gO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5uZXh0KCk7XG4gICAgICAgIHRoaXMuZGVzdHJveSQuY29tcGxldGUoKTtcbiAgICB9XG59XG4iLCI8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uXG4gICAgW2Zvcm1Hcm91cF09XCJmb3JtXCJcbiAgICBbc3RhdGVdPVwiZm9ybSB8IG51aVdpZGdldEVkaXRvckFjY29yZGlvbkZvcm1TdGF0ZSB8IGFzeW5jXCJcbj5cbiAgICA8bnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uLWhlYWRlclxuICAgICAgICBbaGVhZGVySWNvbl09XCJmb3JtIHwgbnVpRm9ybUhlYWRlckljb25QaXBlIDogJ2VkaXQnIHwgYXN5bmNcIlxuICAgICAgICBbaWNvbkNvbG9yXT1cImZvcm0gfCBudWlGb3JtSGVhZGVySWNvblBpcGUgOiAnZ3JheScgOiAnJyB8IGFzeW5jXCJcbiAgICAgICAgW3N1YnRpdGxlXT1cImdldFNlY29uZGFyeVRleHQoKVwiXG4gICAgICAgIGhlYWRlclRpdGxlPVwiVGltZXNlcmllcyBtZXRhZGF0YVwiXG4gICAgLz5cbiAgICA8ZGl2IGNsYXNzPVwidGltZXNlcmllcy1tZXRhZGF0YS1jb25maWd1cmF0aW9uX19hY2NvcmRpb24tY29udGVudFwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiPlxuICAgICAgICAgICAgPG51aS1mb3JtLWZpZWxkXG4gICAgICAgICAgICAgICAgY2FwdGlvbj1cIlN0YXJ0aW5nIHRpbWVzcGFuXCJcbiAgICAgICAgICAgICAgICBpMThuLWNhcHRpb25cbiAgICAgICAgICAgICAgICBbY29udHJvbF09XCJmb3JtLmNvbnRyb2xzWydzdGFydGluZ1RpbWVzcGFuJ11cIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxudWktc2VsZWN0LXYyXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU3RhcnRpbmcgVGltZXNwYW5cIlxuICAgICAgICAgICAgICAgICAgICBpMThuLXBsYWNlaG9sZGVyXG4gICAgICAgICAgICAgICAgICAgIFtwb3B1cFZpZXdwb3J0TWFyZ2luXT1cImNvbmZpZ3VyYXRvckhlYWRpbmcuaGVpZ2h0JCB8IGFzeW5jXCJcbiAgICAgICAgICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwic3RhcnRpbmdUaW1lc3BhblwiXG4gICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICA8bnVpLXNlbGVjdC12Mi1vcHRpb25cbiAgICAgICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCB0aW1lU3BhbiBvZiB0aW1lU3BhbnNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cInRpbWVTcGFuXCJcbiAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAge3sgdGltZVNwYW4ubmFtZSB9fVxuICAgICAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjItb3B0aW9uPlxuICAgICAgICAgICAgICAgIDwvbnVpLXNlbGVjdC12Mj5cbiAgICAgICAgICAgICAgICA8bnVpLXZhbGlkYXRpb24tbWVzc2FnZSBmb3I9XCJyZXF1aXJlZFwiIGkxOG4+XG4gICAgICAgICAgICAgICAgICAgIFRoaXMgZmllbGQgaXMgcmVxdWlyZWRcbiAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICA8L251aS1mb3JtLWZpZWxkPlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiPlxuICAgICAgICAgICAgPG51aS1mb3JtLWZpZWxkXG4gICAgICAgICAgICAgICAgY2FwdGlvbj1cIkNoYXJ0IGxlZ2VuZFwiXG4gICAgICAgICAgICAgICAgaTE4bi1jYXB0aW9uXG4gICAgICAgICAgICAgICAgW2NvbnRyb2xdPVwiZm9ybS5jb250cm9sc1snbGVnZW5kUGxhY2VtZW50J11cIlxuICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgIDxudWktc2VsZWN0LXYyXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU2VsZWN0IExlZ2VuZCBPcmllbnRhdGlvblwiXG4gICAgICAgICAgICAgICAgICAgIGkxOG4tcGxhY2Vob2xkZXJcbiAgICAgICAgICAgICAgICAgICAgW3BvcHVwVmlld3BvcnRNYXJnaW5dPVwiY29uZmlndXJhdG9ySGVhZGluZy5oZWlnaHQkIHwgYXN5bmNcIlxuICAgICAgICAgICAgICAgICAgICBmb3JtQ29udHJvbE5hbWU9XCJsZWdlbmRQbGFjZW1lbnRcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgPG51aS1zZWxlY3QtdjItb3B0aW9uXG4gICAgICAgICAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgbGVnZW5kUGxhY2VtZW50IG9mIGxlZ2VuZFBsYWNlbWVudHNcIlxuICAgICAgICAgICAgICAgICAgICAgICAgW3ZhbHVlXT1cImxlZ2VuZFBsYWNlbWVudC5pZFwiXG4gICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgIHt7IGxlZ2VuZFBsYWNlbWVudC5sYWJlbCB9fVxuICAgICAgICAgICAgICAgICAgICA8L251aS1zZWxlY3QtdjItb3B0aW9uPlxuICAgICAgICAgICAgICAgIDwvbnVpLXNlbGVjdC12Mj5cbiAgICAgICAgICAgICAgICA8bnVpLXZhbGlkYXRpb24tbWVzc2FnZSBmb3I9XCJyZXF1aXJlZFwiIGkxOG4+XG4gICAgICAgICAgICAgICAgICAgIFRoaXMgZmllbGQgaXMgcmVxdWlyZWRcbiAgICAgICAgICAgICAgICA8L251aS12YWxpZGF0aW9uLW1lc3NhZ2U+XG4gICAgICAgICAgICA8L251aS1mb3JtLWZpZWxkPlxuICAgICAgICA8L2Rpdj5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwibWItNFwiPlxuICAgICAgICAgICAgPG51aS1mb3JtLWZpZWxkXG4gICAgICAgICAgICAgICAgY2FwdGlvbj1cIkNoYXJ0IHktYXhpcyBsYWJlbFwiXG4gICAgICAgICAgICAgICAgaTE4bi1jYXB0aW9uXG4gICAgICAgICAgICAgICAgW2NvbnRyb2xdPVwiZm9ybS5jb250cm9sc1snbGVmdEF4aXNMYWJlbCddXCJcbiAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICA8bnVpLXRleHRib3hcbiAgICAgICAgICAgICAgICAgICAgZm9ybUNvbnRyb2xOYW1lPVwibGVmdEF4aXNMYWJlbFwiXG4gICAgICAgICAgICAgICAgICAgIHBsYWNlaG9sZGVyPVwiU2V0IGNoYXJ0IHktYXhpcyBsYWJlbFwiXG4gICAgICAgICAgICAgICAgICAgIGkxOG4tcGxhY2Vob2xkZXJcbiAgICAgICAgICAgICAgICA+PC9udWktdGV4dGJveD5cblxuICAgICAgICAgICAgICAgIDxudWktdmFsaWRhdGlvbi1tZXNzYWdlIGZvcj1cInJlcXVpcmVkXCIgaTE4bj5cbiAgICAgICAgICAgICAgICAgICAgVGhpcyBmaWVsZCBpcyByZXF1aXJlZFxuICAgICAgICAgICAgICAgIDwvbnVpLXZhbGlkYXRpb24tbWVzc2FnZT5cbiAgICAgICAgICAgIDwvbnVpLWZvcm0tZmllbGQ+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxudWktZm9ybS1maWVsZFxuICAgICAgICAgICAgY2FwdGlvbj1cIkNoYXJ0IHR5cGVcIlxuICAgICAgICAgICAgaTE4bi1jYXB0aW9uXG4gICAgICAgICAgICBbY29udHJvbF09XCJmb3JtLmNvbnRyb2xzWydwcmVzZXQnXVwiXG4gICAgICAgID5cbiAgICAgICAgICAgIDxudWktc2VsZWN0LXYyXG4gICAgICAgICAgICAgICAgcGxhY2Vob2xkZXI9XCJDaGFydCB0eXBlXCJcbiAgICAgICAgICAgICAgICBpMThuLXBsYWNlaG9sZGVyXG4gICAgICAgICAgICAgICAgW3BvcHVwVmlld3BvcnRNYXJnaW5dPVwiY29uZmlndXJhdG9ySGVhZGluZy5oZWlnaHQkIHwgYXN5bmNcIlxuICAgICAgICAgICAgICAgIGZvcm1Db250cm9sTmFtZT1cInByZXNldFwiXG4gICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgPG51aS1zZWxlY3QtdjItb3B0aW9uXG4gICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBvcHRpb24gb2YgYXZhaWxhYmxlQ2hhcnRUeXBlc1wiXG4gICAgICAgICAgICAgICAgICAgIFt2YWx1ZV09XCJvcHRpb24udmFsdWVcIlxuICAgICAgICAgICAgICAgICAgICA+e3sgb3B0aW9uLmxhYmVsIH19PC9udWktc2VsZWN0LXYyLW9wdGlvblxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgIDwvbnVpLXNlbGVjdC12Mj5cblxuICAgICAgICAgICAgPG51aS12YWxpZGF0aW9uLW1lc3NhZ2UgZm9yPVwicmVxdWlyZWRcIiBpMThuPlxuICAgICAgICAgICAgICAgIFRoaXMgZmllbGQgaXMgcmVxdWlyZWRcbiAgICAgICAgICAgIDwvbnVpLXZhbGlkYXRpb24tbWVzc2FnZT5cbiAgICAgICAgPC9udWktZm9ybS1maWVsZD5cbiAgICA8L2Rpdj5cbjwvbnVpLXdpZGdldC1lZGl0b3ItYWNjb3JkaW9uPlxuIl19