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

237 lines 55.6 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 { ChangeDetectorRef, Component, Inject, Optional, } from "@angular/core"; // eslint-disable-next-line import/no-deprecated import { merge, Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { EventBus, IconService, uuid, } from "@nova-ui/bits"; import { BandScale, BarHighlightStrategy, BarHighlightStrategyOutline, BarRenderer, DESTROY_EVENT, SET_DOMAIN_EVENT, SparkChartAssist, StatusAccessors, statusAccessors, TimeIntervalScale, TimeseriesZoomPlugin, ZoomPlugin, } from "@nova-ui/charts"; import { SET_TIMEFRAME } from "../../../../services/types"; import { DATA_SOURCE, PIZZAGNA_EVENT_BUS } from "../../../../types"; import { SUMMARY_LEGEND_BCG_COLOR, SUMMARY_LEGEND_COLOR, } from "../../timeseries-helpers"; import { TimeseriesScalesService } from "../../timeseries-scales.service"; import { TimeseriesChartTypes, TimeseriesWidgetProjectType, } from "../../types"; import { TimeseriesChartComponent } from "../timeseries-chart.component"; import * as i0 from "@angular/core"; import * as i1 from "@nova-ui/bits"; import * as i2 from "../../timeseries-scales.service"; import * as i3 from "@angular/common"; import * as i4 from "@nova-ui/charts"; export class StatusBarChartComponent extends TimeseriesChartComponent { static { this.lateLoadKey = "StatusChartComponent"; } constructor(iconService, dataSource, timeseriesScalesService, changeDetector, eventBus) { super(eventBus, timeseriesScalesService, dataSource); this.iconService = iconService; this.timeseriesScalesService = timeseriesScalesService; this.changeDetector = changeDetector; this.eventBus = eventBus; this.collectionId = uuid("timeseries-status-charts"); this.chartUpdate$ = new Subject(); this.timeseriesChartTypes = TimeseriesChartTypes; this.summaryLegendBcgColor = SUMMARY_LEGEND_BCG_COLOR; this.summaryLegendColor = SUMMARY_LEGEND_COLOR; } ngOnInit() { } getDataPointData(series, key) { const data = this.chartAssist.highlightedDataPoints[series.id]?.data; if (data) { return data[key]; } return series.data.length > 0 ? series.data[series.data.length - 1][key] : undefined; } buildChart() { this.buildChart$.next(); this.chartAssist = new SparkChartAssist(); this.accessors = statusAccessors(this.chartAssist.palette.standardColors); this.accessors.data.color = (d, i, series, dataSeries) => typeof d.color === "undefined" ? "var(--nui-color-semantic-unknown)" : d.color; this.accessors.data.thickness = (data) => typeof data.thick === "undefined" || data.thick ? BarRenderer.THICK : BarRenderer.THIN; const iconSize = 8; this.accessors.data.marker = (data) => data.icon ? this.iconService.getIconResized(this.iconService.getIconData(data.icon).code, iconSize) : undefined; this.accessors.data.y = (d) => d.value; // disable pointer events on bars to ensure the zoom drag target is the mouse interactive area rather than the bars this.renderer = new BarRenderer({ highlightStrategy: this.configuration?.projectType === TimeseriesWidgetProjectType.PerfstackApp ? new BarHighlightStrategyOutline("x") : new BarHighlightStrategy("x"), pointerEvents: false, }); this.setGridConfigFromConfiguration(this.chartAssist.gridConfig); this.setGridConfigFromConfiguration(this.chartAssist.lastGridConfig); this.scales.y = new BandScale(); this.scales.y.fixDomain(StatusAccessors.STATUS_DOMAIN); this.scales.y.isTimeseriesScale = true; if (this.scales.yRight) { this.scales.yRight = this.scales.y; } } updateChartData() { const { gridConfig, lastGridConfig } = this.chartAssist; // hides botom axis if it's not last chart in the group gridConfig.axis.bottom.visible = !this.configuration?.hasAdjacentChart; lastGridConfig.axis.bottom.visible = !this.configuration?.hasAdjacentChart; gridConfig.borders.bottom.className = this.configuration ?.hasAdjacentChart ? "nui-chart-border" : "nui-chart-border nui-chart-border--thick"; lastGridConfig.borders.bottom.className = this.configuration ?.hasAdjacentChart ? "nui-chart-border" : "nui-chart-border nui-chart-border--thick"; this.chartUpdate$.next(); // Assemble the series set let series = this.widgetData.series; const summarySerie = this.widgetData.summarySerie ? [this.widgetData.summarySerie] : []; if (series.length === 1 && series[0].id === this.widgetData.summarySerie?.id) { // if the series contains only one item and its the same as the summary serie, // there is no submetric and only the summary serie should be displayed series = []; } const seriesSet = [ ...summarySerie, ...series, ].map((d) => ({ ...d, data: this.transformData(d.data, this.scales.x instanceof TimeIntervalScale), accessors: this.accessors, renderer: this.renderer, scales: this.scales, })); // Update the chart this.chartAssist.update(seriesSet); if (this.configuration.enableZoom) { this.chartAssist.sparks.forEach((spark, i) => { if (this.configuration?.projectType === TimeseriesWidgetProjectType.PerfstackApp) { if (!spark?.chart?.hasPlugin(TimeseriesZoomPlugin)) { spark?.chart?.addPlugin(this.zoomPlugins[i]); } } else { if (!spark?.chart?.hasPlugin(ZoomPlugin)) { spark?.chart?.addPlugin(new ZoomPlugin({ enableExternalEvents: true })); } } }); // only need to subscribe to one chart's SET_DOMAIN_EVENT this.chartAssist.sparks[0].chart ?.getEventBus() .getStream(SET_DOMAIN_EVENT) .pipe(takeUntil( // eslint-disable-next-line import/no-deprecated merge(this.chartUpdate$, this.chartAssist.sparks[0].chart?.eventBus.getStream(DESTROY_EVENT), this.buildChart$))) .subscribe((event) => { const payload = event.data; const newDomain = payload[Object.keys(payload)[0]]; this.eventBus.getStream(SET_TIMEFRAME).next({ payload: { startDatetime: newDomain[0], endDatetime: newDomain[1], selectedPresetId: undefined, }, }); }); } } /** * Transforms standard timeseries x/y data so that it can be understood by a status chart * * @param data The data to transform * @param isIntervalProgression Whether the data should be treated as continuous or occurring at a regular interval * * @returns The transformed data */ transformData(data, isIntervalProgression) { const statusDataArray = []; data.forEach((d, i) => { if (isIntervalProgression || data.length - 1 !== i) { statusDataArray.push({ start: d.x, end: isIntervalProgression ? d.x : data[i + 1].x, status: d.y, color: d.color, thick: d.thick, icon: d.icon, value: d.y, }); } }); return statusDataArray; } setGridConfigFromConfiguration(gridConfig) { const configuration = this.configuration; if (configuration.gridConfig?.xAxisTicksCount) { gridConfig.axis.bottom.approximateTicks = configuration.gridConfig.xAxisTicksCount; } if (gridConfig.dimension.marginLocked && configuration.gridConfig?.sideMarginLocked) { gridConfig.dimension.marginLocked.left = true; gridConfig.dimension.marginLocked.right = true; } if (configuration.gridConfig?.sideMargin) { gridConfig.dimension.margin.left = configuration.gridConfig.sideMargin; gridConfig.dimension.margin.right = configuration.gridConfig.sideMargin; } } displayDeleteButton() { return (!!this.configuration.allowLegendMenu && this.configuration.projectType === TimeseriesWidgetProjectType.PerfstackApp); } isStatusChart() { return this.configuration?.type !== TimeseriesChartTypes.alert; } getDescriptionSecondary(series, index) { if (this.isStatusChart()) { return series.legendDescriptionSecondary; } return index === 0 ? series.legendDescriptionSecondary : ""; } /** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusBarChartComponent, deps: [{ token: i1.IconService }, { token: DATA_SOURCE, optional: true }, { token: i2.TimeseriesScalesService }, { token: i0.ChangeDetectorRef }, { token: PIZZAGNA_EVENT_BUS }], target: i0.ɵɵFactoryTarget.Component }); } /** @nocollapse */ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: StatusBarChartComponent, selector: "nui-status-bar-chart", usesInheritance: true, ngImport: i0, template: "<div\n *ngIf=\"chartAssist\"\n class=\"status-bar-chart\"\n [class.layout-fixed]=\"configuration.gridConfig?.fixedLayout\"\n>\n <div\n class=\"d-flex\"\n *ngFor=\"\n let spark of chartAssist.sparks;\n let i = index;\n trackBy: chartAssist.trackByFn\n \"\n >\n <nui-chart\n class=\"d-flex flex-grow-1\"\n [chart]=\"spark.chart\"\n [nuiChartCollectionId]=\"collectionId\"\n ></nui-chart>\n <div\n class=\"d-flex flex-column pl-2\"\n [class.justify-content-center]=\"\n configuration.type === timeseriesChartTypes.alert &&\n chartAssist.sparks.length > 1\n \"\n [class.last-legend-margin]=\"\n configuration.type === timeseriesChartTypes.alert &&\n i > 0 &&\n i === chartAssist.sparks.length - 1\n \"\n [class.layout-fixed-legend]=\"configuration.gridConfig?.fixedLayout\"\n *ngIf=\"\n spark.chartSeriesSet?.length > 0 && spark.chartSeriesSet[0];\n let series\n \"\n >\n <nui-legend\n class=\"status-bar-chart__legend\"\n [active]=\"chartAssist.isLegendActive\"\n >\n <nui-legend-series\n [descriptionPrimary]=\"series.legendDescriptionPrimary\"\n [descriptionSecondary]=\"getDescriptionSecondary(series, i)\"\n >\n <nui-rich-legend-tile\n *ngIf=\"\n configuration.type === timeseriesChartTypes.alert\n \"\n [value]=\"\n chartAssist.getHighlightedValue(\n series,\n 'y',\n 'value'\n )\n \"\n [backgroundColor]=\"\n i === 0\n ? summaryLegendBcgColor\n : chartAssist.palette.standardColors.get(\n series.id\n )\n \"\n [color]=\"\n i === 0\n ? summaryLegendColor\n : chartAssist.palette.textColors.get(series.id)\n \"\n >\n </nui-rich-legend-tile>\n <div\n *ngIf=\"isStatusChart()\"\n class=\"status-bar-chart__legend__tile d-flex justify-content-center align-items-center\"\n >\n <div\n class=\"w-100 h-100 status-bar-chart__legend__tile__background\"\n [style.background-color]=\"\n getDataPointData(series, 'color')\n \"\n ></div>\n <nui-icon\n *ngIf=\"getDataPointData(series, 'icon'); let icon\"\n class=\"status-bar-chart__legend__tile__icon\"\n [icon]=\"icon\"\n ></nui-icon>\n </div>\n <button\n nui-button\n legendMenu\n *ngIf=\"displayDeleteButton()\"\n (click)=\"removeMetric(series.id)\"\n icon=\"remove\"\n type=\"button\"\n displayStyle=\"action\"\n ></button>\n </nui-legend-series>\n </nui-legend>\n </div>\n </div>\n</div>\n", styles: [".status-bar-chart__legend__tile{width:60px;position:relative}.status-bar-chart__legend__tile__background{position:absolute;opacity:.4}.status-bar-chart__legend__tile__icon{position:absolute}:host ::ng-deep .description-container{flex-grow:1}nui-legend-series:hover{background-color:var(--nui-color-bg-content-hover,#f2f2f2);max-width:100%}.layout-fixed-legend{max-width:250px;min-width:250px;padding-left:8px!important}.layout-fixed-legend nui-legend{margin:0!important;width:100%}.last-legend-margin{justify-content:start!important;margin-top:7px}\n"], dependencies: [{ kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i1.ButtonComponent, selector: "[nui-button]", inputs: ["displayStyle", "icon", "iconColor", "iconRight", "isBusy", "isEmpty", "ariaLabel", "isRepeat", "size"] }, { kind: "component", type: i4.LegendComponent, selector: "nui-legend", inputs: ["active", "interactive", "orientation", "seriesColor", "seriesIcon", "seriesUnitLabel"] }, { kind: "component", type: i4.LegendSeriesComponent, selector: "nui-legend-series", inputs: ["active", "interactive", "isSelected", "descriptionPrimary", "descriptionSecondary", "icon", "color", "seriesRenderState"], outputs: ["isSelectedChange"] }, { kind: "component", type: i4.RichLegendTileComponent, selector: "nui-rich-legend-tile", inputs: ["unitLabel", "value", "backgroundColor", "color"] }, { kind: "component", type: i4.ChartComponent, selector: "nui-chart", inputs: ["chart"] }, { kind: "directive", type: i4.ChartCollectionIdDirective, selector: "[nuiChartCollectionId]", inputs: ["nuiChartCollectionId"] }, { kind: "component", type: i1.IconComponent, selector: "nui-icon", inputs: ["iconColor", "brushType", "iconHoverColor", "iconSize", "cssClass", "fillContainer", "status", "childStatus", "icon", "counter"] }] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: StatusBarChartComponent, decorators: [{ type: Component, args: [{ selector: "nui-status-bar-chart", template: "<div\n *ngIf=\"chartAssist\"\n class=\"status-bar-chart\"\n [class.layout-fixed]=\"configuration.gridConfig?.fixedLayout\"\n>\n <div\n class=\"d-flex\"\n *ngFor=\"\n let spark of chartAssist.sparks;\n let i = index;\n trackBy: chartAssist.trackByFn\n \"\n >\n <nui-chart\n class=\"d-flex flex-grow-1\"\n [chart]=\"spark.chart\"\n [nuiChartCollectionId]=\"collectionId\"\n ></nui-chart>\n <div\n class=\"d-flex flex-column pl-2\"\n [class.justify-content-center]=\"\n configuration.type === timeseriesChartTypes.alert &&\n chartAssist.sparks.length > 1\n \"\n [class.last-legend-margin]=\"\n configuration.type === timeseriesChartTypes.alert &&\n i > 0 &&\n i === chartAssist.sparks.length - 1\n \"\n [class.layout-fixed-legend]=\"configuration.gridConfig?.fixedLayout\"\n *ngIf=\"\n spark.chartSeriesSet?.length > 0 && spark.chartSeriesSet[0];\n let series\n \"\n >\n <nui-legend\n class=\"status-bar-chart__legend\"\n [active]=\"chartAssist.isLegendActive\"\n >\n <nui-legend-series\n [descriptionPrimary]=\"series.legendDescriptionPrimary\"\n [descriptionSecondary]=\"getDescriptionSecondary(series, i)\"\n >\n <nui-rich-legend-tile\n *ngIf=\"\n configuration.type === timeseriesChartTypes.alert\n \"\n [value]=\"\n chartAssist.getHighlightedValue(\n series,\n 'y',\n 'value'\n )\n \"\n [backgroundColor]=\"\n i === 0\n ? summaryLegendBcgColor\n : chartAssist.palette.standardColors.get(\n series.id\n )\n \"\n [color]=\"\n i === 0\n ? summaryLegendColor\n : chartAssist.palette.textColors.get(series.id)\n \"\n >\n </nui-rich-legend-tile>\n <div\n *ngIf=\"isStatusChart()\"\n class=\"status-bar-chart__legend__tile d-flex justify-content-center align-items-center\"\n >\n <div\n class=\"w-100 h-100 status-bar-chart__legend__tile__background\"\n [style.background-color]=\"\n getDataPointData(series, 'color')\n \"\n ></div>\n <nui-icon\n *ngIf=\"getDataPointData(series, 'icon'); let icon\"\n class=\"status-bar-chart__legend__tile__icon\"\n [icon]=\"icon\"\n ></nui-icon>\n </div>\n <button\n nui-button\n legendMenu\n *ngIf=\"displayDeleteButton()\"\n (click)=\"removeMetric(series.id)\"\n icon=\"remove\"\n type=\"button\"\n displayStyle=\"action\"\n ></button>\n </nui-legend-series>\n </nui-legend>\n </div>\n </div>\n</div>\n", styles: [".status-bar-chart__legend__tile{width:60px;position:relative}.status-bar-chart__legend__tile__background{position:absolute;opacity:.4}.status-bar-chart__legend__tile__icon{position:absolute}:host ::ng-deep .description-container{flex-grow:1}nui-legend-series:hover{background-color:var(--nui-color-bg-content-hover,#f2f2f2);max-width:100%}.layout-fixed-legend{max-width:250px;min-width:250px;padding-left:8px!important}.layout-fixed-legend nui-legend{margin:0!important;width:100%}.last-legend-margin{justify-content:start!important;margin-top:7px}\n"] }] }], ctorParameters: () => [{ type: i1.IconService }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DATA_SOURCE] }] }, { type: i2.TimeseriesScalesService }, { type: i0.ChangeDetectorRef }, { type: i1.EventBus, decorators: [{ type: Inject, args: [PIZZAGNA_EVENT_BUS] }] }] }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhdHVzLWJhci1jaGFydC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi8uLi9zcmMvbGliL2NvbXBvbmVudHMvdGltZXNlcmllcy13aWRnZXQvY2hhcnQtcHJlc2V0cy9zdGF0dXMtYmFyLWNoYXJ0L3N0YXR1cy1iYXItY2hhcnQuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL3RpbWVzZXJpZXMtd2lkZ2V0L2NoYXJ0LXByZXNldHMvc3RhdHVzLWJhci1jaGFydC9zdGF0dXMtYmFyLWNoYXJ0LmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlEQUF5RDtBQUN6RCxFQUFFO0FBQ0YsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDNUQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSx1REFBdUQ7QUFDdkQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLDBFQUEwRTtBQUMxRSxpRkFBaUY7QUFDakYsNkVBQTZFO0FBQzdFLGlCQUFpQjtBQUVqQixPQUFPLEVBQ0gsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxNQUFNLEVBRU4sUUFBUSxHQUNYLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLGdEQUFnRDtBQUNoRCxPQUFPLEVBQUUsS0FBSyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN0QyxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFFM0MsT0FBTyxFQUNILFFBQVEsRUFDUixXQUFXLEVBR1gsSUFBSSxHQUNQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFDSCxTQUFTLEVBQ1Qsb0JBQW9CLEVBQ3BCLDJCQUEyQixFQUMzQixXQUFXLEVBRVgsYUFBYSxFQU9iLGdC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QiIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIENoYW5nZURldGVjdG9yUmVmLFxuICAgIENvbXBvbmVudCxcbiAgICBJbmplY3QsXG4gICAgT25Jbml0LFxuICAgIE9wdGlvbmFsLFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1kZXByZWNhdGVkXG5pbXBvcnQgeyBtZXJnZSwgU3ViamVjdCB9IGZyb20gXCJyeGpzXCI7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHtcbiAgICBFdmVudEJ1cyxcbiAgICBJY29uU2VydmljZSxcbiAgICBJRGF0YVNvdXJjZSxcbiAgICBJRXZlbnQsXG4gICAgdXVpZCxcbn0gZnJvbSBcIkBub3ZhLXVpL2JpdHNcIjtcbmltcG9ydCB7XG4gICAgQmFuZFNjYWxlLFxuICAgIEJhckhpZ2hsaWdodFN0cmF0ZWd5LFxuICAgIEJhckhpZ2hsaWdodFN0cmF0ZWd5T3V0bGluZSxcbiAgICBCYXJSZW5kZXJlcixcbiAgICBDaGFydCxcbiAgICBERVNUUk9ZX0VWRU5ULFxuICAgIElBY2Nlc3NvcnMsXG4gICAgSUNoYXJ0QXNzaXN0U2VyaWVzLFxuICAgIElDaGFydEV2ZW50LFxuICAgIElTZXREb21haW5FdmVudFBheWxvYWQsXG4gICAgSVN0YXR1c0FjY2Vzc29ycyxcbiAgICBSZW5kZXJlcixcbiAgICBTRVRfRE9NQUlOX0VWRU5ULFxuICAgIFNwYXJrQ2hhcnRBc3Npc3QsXG4gICAgU3RhdHVzQWNjZXNzb3JzLFxuICAgIHN0YXR1c0FjY2Vzc29ycyxcbiAgICBUaW1lSW50ZXJ2YWxTY2FsZSxcbiAgICBUaW1lc2VyaWVzWm9vbVBsdWdpbixcbiAgICBYWUdyaWRDb25maWcsXG4gICAgWm9vbVBsdWdpbixcbn0gZnJvbSBcIkBub3ZhLXVpL2NoYXJ0c1wiO1xuXG5pbXBvcnQgeyBTRVRfVElNRUZSQU1FIH0gZnJvbSBcIi4uLy4uLy4uLy4uL3NlcnZpY2VzL3R5cGVzXCI7XG5pbXBvcnQgeyBEQVRBX1NPVVJDRSwgUElaWkFHTkFfRVZFTlRfQlVTIH0gZnJvbSBcIi4uLy4uLy4uLy4uL3R5cGVzXCI7XG5pbXBvcnQge1xuICAgIFNVTU1BUllfTEVHRU5EX0JDR19DT0xPUixcbiAgICBTVU1NQVJZX0xFR0VORF9DT0xPUixcbn0gZnJvbSBcIi4uLy4uL3RpbWVzZXJpZXMtaGVscGVyc1wiO1xuaW1wb3J0IHsgVGltZXNlcmllc1NjYWxlc1NlcnZpY2UgfSBmcm9tIFwiLi4vLi4vdGltZXNlcmllcy1zY2FsZXMuc2VydmljZVwiO1xuaW1wb3J0IHtcbiAgICBJVGltZXNlcmllc1dpZGdldERhdGEsXG4gICAgSVRpbWVzZXJpZXNXaWRnZXRTdGF0dXNEYXRhLFxuICAgIFRpbWVzZXJpZXNDaGFydFR5cGVzLFxuICAgIFRpbWVzZXJpZXNXaWRnZXRQcm9qZWN0VHlwZSxcbiAgICBUaW1lc2VyaWVzV2lkZ2V0Wm9vbVBsdWdpbixcbn0gZnJvbSBcIi4uLy4uL3R5cGVzXCI7XG5pbXBvcnQgeyBUaW1lc2VyaWVzQ2hhcnRDb21wb25lbnQgfSBmcm9tIFwiLi4vdGltZXNlcmllcy1jaGFydC5jb21wb25lbnRcIjtcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6IFwibnVpLXN0YXR1cy1iYXItY2hhcnRcIixcbiAgICB0ZW1wbGF0ZVVybDogXCIuL3N0YXR1cy1iYXItY2hhcnQuY29tcG9uZW50Lmh0bWxcIixcbiAgICBzdHlsZVVybHM6IFtcIi4vc3RhdHVzLWJhci1jaGFydC5jb21wb25lbnQubGVzc1wiXSxcbn0pXG5leHBvcnQgY2xhc3MgU3RhdHVzQmFyQ2hhcnRDb21wb25lbnRcbiAgICBleHRlbmRzIFRpbWVzZXJpZXNDaGFydENvbXBvbmVudDxJVGltZXNlcmllc1dpZGdldFN0YXR1c0RhdGE+XG4gICAgaW1wbGVtZW50cyBPbkluaXRcbntcbiAgICBwdWJsaWMgc3RhdGljIGxhdGVMb2FkS2V5ID0gXCJTdGF0dXNDaGFydENvbXBvbmVudFwiO1xuXG4gICAgcHVibGljIGNoYXJ0QXNzaXN0OiBTcGFya0NoYXJ0QXNzaXN0O1xuICAgIHB1YmxpYyBjb2xsZWN0aW9uSWQ6IHN0cmluZyA9IHV1aWQoXCJ0aW1lc2VyaWVzLXN0YXR1cy1jaGFydHNcIik7XG4gICAgcHJvdGVjdGVkIGFjY2Vzc29yczogU3RhdHVzQWNjZXNzb3JzO1xuICAgIHByb3RlY3RlZCByZW5kZXJlcjogUmVuZGVyZXI8SUFjY2Vzc29ycz47XG4gICAgcHJpdmF0ZSBjaGFydFVwZGF0ZSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG4gICAgcHVibGljIHpvb21QbHVnaW5zOiBUaW1lc2VyaWVzV2lkZ2V0Wm9vbVBsdWdpbltdO1xuICAgIHB1YmxpYyB0aW1lc2VyaWVzQ2hhcnRUeXBlcyA9IFRpbWVzZXJpZXNDaGFydFR5cGVzO1xuICAgIHB1YmxpYyBzdW1tYXJ5TGVnZW5kQmNnQ29sb3IgPSBTVU1NQVJZX0xFR0VORF9CQ0dfQ09MT1I7XG4gICAgcHVibGljIHN1bW1hcnlMZWdlbmRDb2xvciA9IFNVTU1BUllfTEVHRU5EX0NPTE9SO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgaWNvblNlcnZpY2U6IEljb25TZXJ2aWNlLFxuICAgICAgICBAT3B0aW9uYWwoKSBASW5qZWN0KERBVEFfU09VUkNFKSBkYXRhU291cmNlOiBJRGF0YVNvdXJjZSxcbiAgICAgICAgcHVibGljIHRpbWVzZXJpZXNTY2FsZXNTZXJ2aWNlOiBUaW1lc2VyaWVzU2NhbGVzU2VydmljZSxcbiAgICAgICAgcHVibGljIGNoYW5nZURldGVjdG9yOiBDaGFuZ2VEZXRlY3RvclJlZixcbiAgICAgICAgQEluamVjdChQSVpaQUdOQV9FVkVOVF9CVVMpIHByb3RlY3RlZCBldmVudEJ1czogRXZlbnRCdXM8SUV2ZW50PlxuICAgICkge1xuICAgICAgICBzdXBlcihldmVudEJ1cywgdGltZXNlcmllc1NjYWxlc1NlcnZpY2UsIGRhdGFTb3VyY2UpO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uSW5pdCgpOiB2b2lkIHt9XG5cbiAgICBwdWJsaWMgZ2V0RGF0YVBvaW50RGF0YShcbiAgICAgICAgc2VyaWVzOiBJQ2hhcnRBc3Npc3RTZXJpZXM8SUFjY2Vzc29ycz4sXG4gICAgICAgIGtleTogc3RyaW5nXG4gICAgKTogYW55IHtcbiAgICAgICAgY29uc3QgZGF0YSA9IHRoaXMuY2hhcnRBc3Npc3QuaGlnaGxpZ2h0ZWREYXRhUG9pbnRzW3Nlcmllcy5pZF0/LmRhdGE7XG4gICAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgICAgICByZXR1cm4gZGF0YVtrZXldO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBzZXJpZXMuZGF0YS5sZW5ndGggPiAwXG4gICAgICAgICAgICA/IHNlcmllcy5kYXRhW3Nlcmllcy5kYXRhLmxlbmd0aCAtIDFdW2tleV1cbiAgICAgICAgICAgIDogdW5kZWZpbmVkO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBidWlsZENoYXJ0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmJ1aWxkQ2hhcnQkLm5leHQoKTtcbiAgICAgICAgdGhpcy5jaGFydEFzc2lzdCA9IG5ldyBTcGFya0NoYXJ0QXNzaXN0KCk7XG5cbiAgICAgICAgdGhpcy5hY2Nlc3NvcnMgPSBzdGF0dXNBY2Nlc3NvcnMoXG4gICAgICAgICAgICB0aGlzLmNoYXJ0QXNzaXN0LnBhbGV0dGUuc3RhbmRhcmRDb2xvcnNcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5hY2Nlc3NvcnMuZGF0YS5jb2xvciA9IChkLCBpLCBzZXJpZXMsIGRhdGFTZXJpZXMpID0+XG4gICAgICAgICAgICB0eXBlb2YgZC5jb2xvciA9PT0gXCJ1bmRlZmluZWRcIlxuICAgICAgICAgICAgICAgID8gXCJ2YXIoLS1udWktY29sb3Itc2VtYW50aWMtdW5rbm93bilcIlxuICAgICAgICAgICAgICAgIDogZC5jb2xvcjtcblxuICAgICAgICB0aGlzLmFjY2Vzc29ycy5kYXRhLnRoaWNrbmVzcyA9IChkYXRhOiBhbnkpID0+XG4gICAgICAgICAgICB0eXBlb2YgZGF0YS50aGljayA9PT0gXCJ1bmRlZmluZWRcIiB8fCBkYXRhLnRoaWNrXG4gICAgICAgICAgICAgICAgPyBCYXJSZW5kZXJlci5USElDS1xuICAgICAgICAgICAgICAgIDogQmFyUmVuZGVyZXIuVEhJTjtcblxuICAgICAgICBjb25zdCBpY29uU2l6ZTogbnVtYmVyID0gODtcbiAgICAgICAgdGhpcy5hY2Nlc3NvcnMuZGF0YS5tYXJrZXIgPSAoZGF0YTogYW55KSA9PlxuICAgICAgICAgICAgZGF0YS5pY29uXG4gICAgICAgICAgICAgICAgPyB0aGlzLmljb25TZXJ2aWNlLmdldEljb25SZXNpemVkKFxuICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaWNvblNlcnZpY2UuZ2V0SWNvbkRhdGEoZGF0YS5pY29uKS5jb2RlLFxuICAgICAgICAgICAgICAgICAgICAgIGljb25TaXplXG4gICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgOiB1bmRlZmluZWQ7XG5cbiAgICAgICAgdGhpcy5hY2Nlc3NvcnMuZGF0YS55ID0gKGQpID0+IGQudmFsdWU7XG5cbiAgICAgICAgLy8gZGlzYWJsZSBwb2ludGVyIGV2ZW50cyBvbiBiYXJzIHRvIGVuc3VyZSB0aGUgem9vbSBkcmFnIHRhcmdldCBpcyB0aGUgbW91c2UgaW50ZXJhY3RpdmUgYXJlYSByYXRoZXIgdGhhbiB0aGUgYmFyc1xuICAgICAgICB0aGlzLnJlbmRlcmVyID0gbmV3IEJhclJlbmRlcmVyKHtcbiAgICAgICAgICAgIGhpZ2hsaWdodFN0cmF0ZWd5OlxuICAgICAgICAgICAgICAgIHRoaXMuY29uZmlndXJhdGlvbj8ucHJvamVjdFR5cGUgPT09XG4gICAgICAgICAgICAgICAgVGltZXNlcmllc1dpZGdldFByb2plY3RUeXBlLlBlcmZzdGFja0FwcFxuICAgICAgICAgICAgICAgICAgICA/IG5ldyBCYXJIaWdobGlnaHRTdHJhdGVneU91dGxpbmUoXCJ4XCIpXG4gICAgICAgICAgICAgICAgICAgIDogbmV3IEJhckhpZ2hsaWdodFN0cmF0ZWd5KFwieFwiKSxcbiAgICAgICAgICAgIHBvaW50ZXJFdmVudHM6IGZhbHNlLFxuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnNldEdyaWRDb25maWdGcm9tQ29uZmlndXJhdGlvbih0aGlzLmNoYXJ0QXNzaXN0LmdyaWRDb25maWcpO1xuICAgICAgICB0aGlzLnNldEdyaWRDb25maWdGcm9tQ29uZmlndXJhdGlvbih0aGlzLmNoYXJ0QXNzaXN0Lmxhc3RHcmlkQ29uZmlnKTtcblxuICAgICAgICB0aGlzLnNjYWxlcy55ID0gbmV3IEJhbmRTY2FsZSgpO1xuICAgICAgICB0aGlzLnNjYWxlcy55LmZpeERvbWFpbihTdGF0dXNBY2Nlc3NvcnMuU1RBVFVTX0RPTUFJTik7XG4gICAgICAgIHRoaXMuc2NhbGVzLnkuaXNUaW1lc2VyaWVzU2NhbGUgPSB0cnVlO1xuXG4gICAgICAgIGlmICh0aGlzLnNjYWxlcy55UmlnaHQpIHtcbiAgICAgICAgICAgIHRoaXMuc2NhbGVzLnlSaWdodCA9IHRoaXMuc2NhbGVzLnk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgdXBkYXRlQ2hhcnREYXRhKCk6IHZvaWQge1xuICAgICAgICBjb25zdCB7IGdyaWRDb25maWcsIGxhc3RHcmlkQ29uZmlnIH0gPSB0aGlzLmNoYXJ0QXNzaXN0O1xuICAgICAgICAvLyBoaWRlcyBib3RvbSBheGlzIGlmIGl0J3Mgbm90IGxhc3QgY2hhcnQgaW4gdGhlIGdyb3VwXG4gICAgICAgIGdyaWRDb25maWcuYXhpcy5ib3R0b20udmlzaWJsZSA9ICF0aGlzLmNvbmZpZ3VyYXRpb24/Lmhhc0FkamFjZW50Q2hhcnQ7XG4gICAgICAgIGxhc3RHcmlkQ29uZmlnLmF4aXMuYm90dG9tLnZpc2libGUgPVxuICAgICAgICAgICAgIXRoaXMuY29uZmlndXJhdGlvbj8uaGFzQWRqYWNlbnRDaGFydDtcbiAgICAgICAgZ3JpZENvbmZpZy5ib3JkZXJzLmJvdHRvbS5jbGFzc05hbWUgPSB0aGlzLmNvbmZpZ3VyYXRpb25cbiAgICAgICAgICAgID8uaGFzQWRqYWNlbnRDaGFydFxuICAgICAgICAgICAgPyBcIm51aS1jaGFydC1ib3JkZXJcIlxuICAgICAgICAgICAgOiBcIm51aS1jaGFydC1ib3JkZXIgbnVpLWNoYXJ0LWJvcmRlci0tdGhpY2tcIjtcbiAgICAgICAgbGFzdEdyaWRDb25maWcuYm9yZGVycy5ib3R0b20uY2xhc3NOYW1lID0gdGhpcy5jb25maWd1cmF0aW9uXG4gICAgICAgICAgICA/Lmhhc0FkamFjZW50Q2hhcnRcbiAgICAgICAgICAgID8gXCJudWktY2hhcnQtYm9yZGVyXCJcbiAgICAgICAgICAgIDogXCJudWktY2hhcnQtYm9yZGVyIG51aS1jaGFydC1ib3JkZXItLXRoaWNrXCI7XG5cbiAgICAgICAgdGhpcy5jaGFydFVwZGF0ZSQubmV4dCgpO1xuXG4gICAgICAgIC8vIEFzc2VtYmxlIHRoZSBzZXJpZXMgc2V0XG4gICAgICAgIGxldCBzZXJpZXMgPSB0aGlzLndpZGdldERhdGEuc2VyaWVzO1xuICAgICAgICBjb25zdCBzdW1tYXJ5U2VyaWUgPSB0aGlzLndpZGdldERhdGEuc3VtbWFyeVNlcmllXG4gICAgICAgICAgICA/IFt0aGlzLndpZGdldERhdGEuc3VtbWFyeVNlcmllXVxuICAgICAgICAgICAgOiBbXTtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgc2VyaWVzLmxlbmd0aCA9PT0gMSAmJlxuICAgICAgICAgICAgc2VyaWVzWzBdLmlkID09PSB0aGlzLndpZGdldERhdGEuc3VtbWFyeVNlcmllPy5pZFxuICAgICAgICApIHtcbiAgICAgICAgICAgIC8vIGlmIHRoZSBzZXJpZXMgY29udGFpbnMgb25seSBvbmUgaXRlbSBhbmQgaXRzIHRoZSBzYW1lIGFzIHRoZSBzdW1tYXJ5IHNlcmllLFxuICAgICAgICAgICAgLy8gdGhlcmUgaXMgbm8gc3VibWV0cmljIGFuZCBvbmx5IHRoZSBzdW1tYXJ5IHNlcmllIHNob3VsZCBiZSBkaXNwbGF5ZWRcbiAgICAgICAgICAgIHNlcmllcyA9IFtdO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IHNlcmllc1NldDogSUNoYXJ0QXNzaXN0U2VyaWVzPElTdGF0dXNBY2Nlc3NvcnM+W10gPSBbXG4gICAgICAgICAgICAuLi5zdW1tYXJ5U2VyaWUsXG4gICAgICAgICAgICAuLi5zZXJpZXMsXG4gICAgICAgIF0ubWFwKChkOiBJVGltZXNlcmllc1dpZGdldERhdGE8SVRpbWVzZXJpZXNXaWRnZXRTdGF0dXNEYXRhPikgPT4gKHtcbiAgICAgICAgICAgIC4uLmQsXG4gICAgICAgICAgICBkYXRhOiB0aGlzLnRyYW5zZm9ybURhdGEoXG4gICAgICAgICAgICAgICAgZC5kYXRhLFxuICAgICAgICAgICAgICAgIHRoaXMuc2NhbGVzLnggaW5zdGFuY2VvZiBUaW1lSW50ZXJ2YWxTY2FsZVxuICAgICAgICAgICAgKSxcbiAgICAgICAgICAgIGFjY2Vzc29yczogdGhpcy5hY2Nlc3NvcnMsXG4gICAgICAgICAgICByZW5kZXJlcjogdGhpcy5yZW5kZXJlcixcbiAgICAgICAgICAgIHNjYWxlczogdGhpcy5zY2FsZXMsXG4gICAgICAgIH0pKTtcblxuICAgICAgICAvLyBVcGRhdGUgdGhlIGNoYXJ0XG4gICAgICAgIHRoaXMuY2hhcnRBc3Npc3QudXBkYXRlKHNlcmllc1NldCk7XG5cbiAgICAgICAgaWYgKHRoaXMuY29uZmlndXJhdGlvbi5lbmFibGVab29tKSB7XG4gICAgICAgICAgICB0aGlzLmNoYXJ0QXNzaXN0LnNwYXJrcy5mb3JFYWNoKChzcGFyaywgaSkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jb25maWd1cmF0aW9uPy5wcm9qZWN0VHlwZSA9PT1cbiAgICAgICAgICAgICAgICAgICAgVGltZXNlcmllc1dpZGdldFByb2plY3RUeXBlLlBlcmZzdGFja0FwcFxuICAgICAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgICAgICAgICAhKHNwYXJrPy5jaGFydCBhcyBDaGFydCk/Lmhhc1BsdWdpbihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lc2VyaWVzWm9vbVBsdWdpblxuICAgICAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHNwYXJrPy5jaGFydD8uYWRkUGx1Z2luKHRoaXMuem9vbVBsdWdpbnNbaV0pO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKCEoc3Bhcms/LmNoYXJ0IGFzIENoYXJ0KT8uaGFzUGx1Z2luKFpvb21QbHVnaW4pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzcGFyaz8uY2hhcnQ/LmFkZFBsdWdpbihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgWm9vbVBsdWdpbih7IGVuYWJsZUV4dGVybmFsRXZlbnRzOiB0cnVlIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgICAgIC8vIG9ubHkgbmVlZCB0byBzdWJzY3JpYmUgdG8gb25lIGNoYXJ0J3MgU0VUX0RPTUFJTl9FVkVOVFxuICAgICAgICAgICAgdGhpcy5jaGFydEFzc2lzdC5zcGFya3NbMF0uY2hhcnRcbiAgICAgICAgICAgICAgICA/LmdldEV2ZW50QnVzKClcbiAgICAgICAgICAgICAgICAuZ2V0U3RyZWFtKFNFVF9ET01BSU5fRVZFTlQpXG4gICAgICAgICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICAgICAgICAgIHRha2VVbnRpbChcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZGVwcmVjYXRlZFxuICAgICAgICAgICAgICAgICAgICAgICAgbWVyZ2UoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jaGFydFVwZGF0ZSQsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNoYXJ0QXNzaXN0LnNwYXJrc1swXS5jaGFydCBhcyBDaGFydFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICk/LmV2ZW50QnVzLmdldFN0cmVhbShERVNUUk9ZX0VWRU5UKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmJ1aWxkQ2hhcnQkXG4gICAgICAgICAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgoZXZlbnQ6IElDaGFydEV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBheWxvYWQgPSA8SVNldERvbWFpbkV2ZW50UGF5bG9hZD5ldmVudC5kYXRhO1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBuZXdEb21haW4gPSBwYXlsb2FkW09iamVjdC5rZXlzKHBheWxvYWQpWzBdXTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5ldmVudEJ1cy5nZXRTdHJlYW0oU0VUX1RJTUVGUkFNRSkubmV4dCh7XG4gICAgICAgICAgICAgICAgICAgICAgICBwYXlsb2FkOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RhcnREYXRldGltZTogbmV3RG9tYWluWzBdLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZERhdGV0aW1lOiBuZXdEb21haW5bMV0sXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0ZWRQcmVzZXRJZDogdW5kZWZpbmVkLFxuICAgICAgICAgICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBUcmFuc2Zvcm1zIHN0YW5kYXJkIHRpbWVzZXJpZXMgeC95IGRhdGEgc28gdGhhdCBpdCBjYW4gYmUgdW5kZXJzdG9vZCBieSBhIHN0YXR1cyBjaGFydFxuICAgICAqXG4gICAgICogQHBhcmFtIGRhdGEgVGhlIGRhdGEgdG8gdHJhbnNmb3JtXG4gICAgICogQHBhcmFtIGlzSW50ZXJ2YWxQcm9ncmVzc2lvbiBXaGV0aGVyIHRoZSBkYXRhIHNob3VsZCBiZSB0cmVhdGVkIGFzIGNvbnRpbnVvdXMgb3Igb2NjdXJyaW5nIGF0IGEgcmVndWxhciBpbnRlcnZhbFxuICAgICAqXG4gICAgICogQHJldHVybnMgVGhlIHRyYW5zZm9ybWVkIGRhdGFcbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgdHJhbnNmb3JtRGF0YShcbiAgICAgICAgZGF0YTogSVRpbWVzZXJpZXNXaWRnZXRTdGF0dXNEYXRhW10sXG4gICAgICAgIGlzSW50ZXJ2YWxQcm9ncmVzc2lvbjogYm9vbGVhblxuICAgICk6IElTdGF0dXNEYXRhW10ge1xuICAgICAgICBjb25zdCBzdGF0dXNEYXRhQXJyYXk6IGFueVtdID0gW107XG4gICAgICAgIGRhdGEuZm9yRWFjaCgoZCwgaSkgPT4ge1xuICAgICAgICAgICAgaWYgKGlzSW50ZXJ2YWxQcm9ncmVzc2lvbiB8fCBkYXRhLmxlbmd0aCAtIDEgIT09IGkpIHtcbiAgICAgICAgICAgICAgICBzdGF0dXNEYXRhQXJyYXkucHVzaCh7XG4gICAgICAgICAgICAgICAgICAgIHN0YXJ0OiBkLngsXG4gICAgICAgICAgICAgICAgICAgIGVuZDogaXNJbnRlcnZhbFByb2dyZXNzaW9uID8gZC54IDogZGF0YVtpICsgMV0ueCxcbiAgICAgICAgICAgICAgICAgICAgc3RhdHVzOiBkLnksXG4gICAgICAgICAgICAgICAgICAgIGNvbG9yOiBkLmNvbG9yLFxuICAgICAgICAgICAgICAgICAgICB0aGljazogZC50aGljayxcbiAgICAgICAgICAgICAgICAgICAgaWNvbjogZC5pY29uLFxuICAgICAgICAgICAgICAgICAgICB2YWx1ZTogZC55LFxuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgICAgcmV0dXJuIHN0YXR1c0RhdGFBcnJheTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHNldEdyaWRDb25maWdGcm9tQ29uZmlndXJhdGlvbihncmlkQ29uZmlnOiBYWUdyaWRDb25maWcpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgY29uZmlndXJhdGlvbiA9IHRoaXMuY29uZmlndXJhdGlvbjtcblxuICAgICAgICBpZiAoY29uZmlndXJhdGlvbi5ncmlkQ29uZmlnPy54QXhpc1RpY2tzQ291bnQpIHtcbiAgICAgICAgICAgIGdyaWRDb25maWcuYXhpcy5ib3R0b20uYXBwcm94aW1hdGVUaWNrcyA9XG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbi5ncmlkQ29uZmlnLnhBeGlzVGlja3NDb3VudDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIGdyaWRDb25maWcuZGltZW5zaW9uLm1hcmdpbkxvY2tlZCAmJlxuICAgICAgICAgICAgY29uZmlndXJhdGlvbi5ncmlkQ29uZmlnPy5zaWRlTWFyZ2luTG9ja2VkXG4gICAgICAgICkge1xuICAgICAgICAgICAgZ3JpZENvbmZpZy5kaW1lbnNpb24ubWFyZ2luTG9ja2VkLmxlZnQgPSB0cnVlO1xuICAgICAgICAgICAgZ3JpZENvbmZpZy5kaW1lbnNpb24ubWFyZ2luTG9ja2VkLnJpZ2h0ID0gdHJ1ZTtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChjb25maWd1cmF0aW9uLmdyaWRDb25maWc/LnNpZGVNYXJnaW4pIHtcbiAgICAgICAgICAgIGdyaWRDb25maWcuZGltZW5zaW9uLm1hcmdpbi5sZWZ0ID1cbiAgICAgICAgICAgICAgICBjb25maWd1cmF0aW9uLmdyaWRDb25maWcuc2lkZU1hcmdpbjtcbiAgICAgICAgICAgIGdyaWRDb25maWcuZGltZW5zaW9uLm1hcmdpbi5yaWdodCA9XG4gICAgICAgICAgICAgICAgY29uZmlndXJhdGlvbi5ncmlkQ29uZmlnLnNpZGVNYXJnaW47XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgZGlzcGxheURlbGV0ZUJ1dHRvbigpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICEhdGhpcy5jb25maWd1cmF0aW9uLmFsbG93TGVnZW5kTWVudSAmJlxuICAgICAgICAgICAgdGhpcy5jb25maWd1cmF0aW9uLnByb2plY3RUeXBlID09PVxuICAgICAgICAgICAgICAgIFRpbWVzZXJpZXNXaWRnZXRQcm9qZWN0VHlwZS5QZXJmc3RhY2tBcHBcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgaXNTdGF0dXNDaGFydCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29uZmlndXJhdGlvbj8udHlwZSAhPT0gVGltZXNlcmllc0NoYXJ0VHlwZXMuYWxlcnQ7XG4gICAgfVxuXG4gICAgcHVibGljIGdldERlc2NyaXB0aW9uU2Vjb25kYXJ5KFxuICAgICAgICBzZXJpZXM6IElDaGFydEFzc2lzdFNlcmllczxJQWNjZXNzb3JzPixcbiAgICAgICAgaW5kZXg6IG51bWJlclxuICAgICk6IHN0cmluZyB7XG4gICAgICAgIGlmICh0aGlzLmlzU3RhdHVzQ2hhcnQoKSkge1xuICAgICAgICAgICAgcmV0dXJuIHNlcmllcy5sZWdlbmREZXNjcmlwdGlvblNlY29uZGFyeTtcbiAgICAgICAgfVxuXG4gICAgICAgI