@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
180 lines • 26 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 { Injectable } from "@angular/core";
import { duration } from "moment/moment";
import { UnitBase, UnitConversionService } from "@nova-ui/bits";
import { LinearScale, TimeIntervalScale, TimeScale, } from "@nova-ui/charts";
import { TimeseriesScaleType, TimeseriesChartPreset, } from "./types";
import { DashboardUnitConversionPipe } from "../../common/pipes/public-api";
import { roundToOptimalDecimals } from "../../functions/round-array-values";
import { timeSeriesDatetimeFormatter } from "../../functions/timeseries-datetime-formatter";
import * as i0 from "@angular/core";
import * as i1 from "@nova-ui/bits";
/**
* This service handles scale creation and configuration for the timeseries widget
*/
export class TimeseriesScalesService {
constructor(unitConversionService) {
this.unitConversionService = unitConversionService;
this.unitConversionPipe = new DashboardUnitConversionPipe(this.unitConversionService);
}
/**
* Creates a scale based on given configuration
*
* @param scaleConfig
*/
getScale(scaleConfig, units, widgetConfig) {
let scale;
switch (scaleConfig.type) {
case TimeseriesScaleType.Time: {
scale = new TimeScale();
scale.isTimeseriesScale = true;
scale.formatters.title = timeSeriesDatetimeFormatter;
break;
}
case TimeseriesScaleType.Linear: {
scale = new LinearScale();
scale.isTimeseriesScale = true;
scale.formatters.tick = (value, isLabelFormatter) => this.unitConversionPipe.transform(value, scaleConfig.properties?.axisUnits ?? units, UnitBase.Standard);
break;
}
case TimeseriesScaleType.TimeInterval: {
scale = new TimeIntervalScale(duration(1, "hour"));
break;
}
}
this.updateConfiguration(scale, scaleConfig, widgetConfig);
return scale;
}
/**
* Currently only TimeIntervalScale has configuration
*
* @param scale
* @param scaleConfig
*/
updateConfiguration(scale, scaleConfig, widgetConfig) {
switch (scaleConfig.type) {
case TimeseriesScaleType.Time: {
const interval = scaleConfig.properties?.timeInterval;
if (interval?.startDatetime && interval?.endDatetime) {
scale.fixDomain([
interval.startDatetime,
interval.endDatetime,
]);
}
break;
}
case TimeseriesScaleType.TimeInterval: {
const interval = scaleConfig.properties?.interval;
if (typeof interval === "number") {
if (interval <= 0) {
throw new Error("Interval value must be greater than zero.");
}
if (scale instanceof TimeIntervalScale) {
scale.interval(duration(interval, "seconds"));
}
}
break;
}
case TimeseriesScaleType.Linear: {
if (scaleConfig.properties?.axisUnits) {
scale.scaleUnits = scaleConfig.properties.axisUnits;
}
if (widgetConfig?.preset === TimeseriesChartPreset.StatusBar) {
return;
}
if (scaleConfig.properties?.axisUnits === "percent" &&
scale.setFixDomainValues) {
scale.setFixDomainValues([0, 25, 50, 75, 100]);
}
if (scaleConfig.properties?.axisUnits !== "percent" &&
scaleConfig.properties?.domain &&
scale.setFixDomainValues) {
let domainAdjusted;
switch (widgetConfig?.preset) {
case TimeseriesChartPreset.StackedBar:
domainAdjusted = this.getStackedBarScaleDomain(scaleConfig.properties.domain);
break;
case TimeseriesChartPreset.StackedArea:
domainAdjusted = this.getStackedAreaScaleDomain(scaleConfig.properties.domain.max);
break;
default:
domainAdjusted = this.getLineScaleDomain(scaleConfig.properties.domain);
}
scale.setFixDomainValues(domainAdjusted);
}
break;
}
}
}
getStackedBarScaleDomain({ min, max, }) {
if (max === 0 || max % 4 > 0) {
max = max + 4 - (max % 4);
}
const increment = Math.floor((max - min) / 4);
return [
min,
min + increment,
min + 2 * increment,
max - increment,
max,
];
}
getLineScaleDomain({ min, max, }) {
if (min > max) {
const tmp = min;
min = max;
max = tmp;
}
const extentRange = Math.abs((max - min) / ((max + min) / 2));
// for small domain ranges increase domain so that spikes are not exaggerated
if (extentRange < 0.5) {
min = min - Math.abs(min) * 0.5;
max = max + Math.abs(max) * 0.5;
}
// handles zero case
if (min === 0 && max === 0) {
min = -1;
max = 1;
}
// special case for real small number since not using si prefix anymore
if (Math.abs(max - min) < 0.04) {
max += 0.04;
}
const point = (max - min) / 4;
return roundToOptimalDecimals([
min,
min + point,
min + 2 * point,
max - point,
max,
]);
}
getStackedAreaScaleDomain(max) {
const point = max / 4;
return [0, point, point * 2, max - point, max];
}
/** @nocollapse */ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeseriesScalesService, deps: [{ token: i1.UnitConversionService }], target: i0.ɵɵFactoryTarget.Injectable }); }
/** @nocollapse */ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeseriesScalesService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TimeseriesScalesService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.UnitConversionService }] });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXNlcmllcy1zY2FsZXMuc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy90aW1lc2VyaWVzLXdpZGdldC90aW1lc2VyaWVzLXNjYWxlcy5zZXJ2aWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlEQUF5RDtBQUN6RCxFQUFFO0FBQ0YsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDNUQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSx1REFBdUQ7QUFDdkQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLDBFQUEwRTtBQUMxRSxpRkFBaUY7QUFDakYsNkVBQTZFO0FBQzdFLGlCQUFpQjtBQUVqQixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQzNDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFFekMsT0FBTyxFQUFFLFFBQVEsRUFBRSxxQkFBcUIsRUFBYyxNQUFNLGVBQWUsQ0FBQztBQUM1RSxPQUFPLEVBRUgsV0FBVyxFQUNYLGlCQUFpQixFQUNqQixTQUFTLEdBQ1osTUFBTSxpQkFBaUIsQ0FBQztBQUV6QixPQUFPLEVBR0gsbUJBQW1CLEVBQ25CLHFCQUFxQixHQUN4QixNQUFNLFNBQVMsQ0FBQztBQUNqQixPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSwrQkFBK0IsQ0FBQztBQUM1RSxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxvQ0FBb0MsQ0FBQztBQUM1RSxPQUFPLEVBQUUsMkJBQTJCLEVBQUUsTUFBTSwrQ0FBK0MsQ0FBQzs7O0FBRTVGOztHQUVHO0FBRUgsTUFBTSxPQUFPLHVCQUF1QjtJQUdoQyxZQUFvQixxQkFBNEM7UUFBNUMsMEJBQXFCLEdBQXJCLHFCQUFxQixDQUF1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RyxDQUFDLEdBQUcsSUFBSSxFQUFFO1lBQzVCLEdBQUcsSUFBSSxJQUFJLENBQUM7U0FDZjtRQUVELE1BQU0sS0FBSyxHQUFHLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUM5QixPQUFPLHNCQUFzQixDQUFDO1lBQzFCLEdBQUc7WUFDSCxHQUFHLEdBQUcsS0FBSztZQUNYLEdBQUcsR0FBRyxDQUFDLEdBQUcsS0FBSztZQUNmLEdBQUcsR0FBRyxLQUFLO1lBQ1gsR0FBRztTQUNOLENBQUMsQ0FBQztJQUNQLENBQUM7SUFFTyx5QkFBeUIsQ0FBQyxHQUFXO1FBQ3pDLE1BQU0sS0FBSyxHQUFHLEdBQUcsR0FBRyxDQUFDLENBQUM7UUFDdEIsT0FBTyxDQUFDLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBSyxHQUFHLENBQUMsRUFBRSxHQUFHLEdBQUcsS0FBSyxFQUFFLEdBQUcsQ0FBQyxDQUFDO0lBQ25ELENBQUM7a0lBdk1RLHVCQUF1QjtzSUFBdkIsdUJBQXVCOzs0RkFBdkIsdUJBQXVCO2tCQURuQyxVQUFVIiwic291cmNlc0NvbnRlbnQiOlsiLy8gwqkgMjAyMiBTb2xhcldpbmRzIFdvcmxkd2lkZSwgTExDLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbi8vICBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0b1xuLy8gIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlXG4vLyAgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yXG4vLyAgc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbi8vICBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG4vLyAgYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuLy8gIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLFxuLy8gIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuLy8gIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbi8vICBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLFxuLy8gIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbi8vICBUSEUgU09GVFdBUkUuXG5cbmltcG9ydCB7IEluamVjdGFibGUgfSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IHsgZHVyYXRpb24gfSBmcm9tIFwibW9tZW50L21vbWVudFwiO1xuXG5pbXBvcnQgeyBVbml0QmFzZSwgVW5pdENvbnZlcnNpb25TZXJ2aWNlLCBVbml0T3B0aW9uIH0gZnJvbSBcIkBub3ZhLXVpL2JpdHNcIjtcbmltcG9ydCB7XG4gICAgSVNjYWxlLFxuICAgIExpbmVhclNjYWxlLFxuICAgIFRpbWVJbnRlcnZhbFNjYWxlLFxuICAgIFRpbWVTY2FsZSxcbn0gZnJvbSBcIkBub3ZhLXVpL2NoYXJ0c1wiO1xuXG5pbXBvcnQge1xuICAgIElUaW1lc2VyaWVzU2NhbGVDb25maWcsXG4gICAgSVRpbWVzZXJpZXNXaWRnZXRDb25maWcsXG4gICAgVGltZXNlcmllc1NjYWxlVHlwZSxcbiAgICBUaW1lc2VyaWVzQ2hhcnRQcmVzZXQsXG59IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBEYXNoYm9hcmRVbml0Q29udmVyc2lvblBpcGUgfSBmcm9tIFwiLi4vLi4vY29tbW9uL3BpcGVzL3B1YmxpYy1hcGlcIjtcbmltcG9ydCB7IHJvdW5kVG9PcHRpbWFsRGVjaW1hbHMgfSBmcm9tIFwiLi4vLi4vZnVuY3Rpb25zL3JvdW5kLWFycmF5LXZhbHVlc1wiO1xuaW1wb3J0IHsgdGltZVNlcmllc0RhdGV0aW1lRm9ybWF0dGVyIH0gZnJvbSBcIi4uLy4uL2Z1bmN0aW9ucy90aW1lc2VyaWVzLWRhdGV0aW1lLWZvcm1hdHRlclwiO1xuXG4vKipcbiAqIFRoaXMgc2VydmljZSBoYW5kbGVzIHNjYWxlIGNyZWF0aW9uIGFuZCBjb25maWd1cmF0aW9uIGZvciB0aGUgdGltZXNlcmllcyB3aWRnZXRcbiAqL1xuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIFRpbWVzZXJpZXNTY2FsZXNTZXJ2aWNlIHtcbiAgICBwcml2YXRlIHVuaXRDb252ZXJzaW9uUGlwZTogRGFzaGJvYXJkVW5pdENvbnZlcnNpb25QaXBlO1xuXG4gICAgY29uc3RydWN0b3IocHJpdmF0ZSB1bml0Q29udmVyc2lvblNlcnZpY2U6IFVuaXRDb252ZXJzaW9uU2VydmljZSkge1xuICAgICAgICB0aGlzLnVuaXRDb252ZXJzaW9uUGlwZSA9IG5ldyBEYXNoYm9hcmRVbml0Q29udmVyc2lvblBpcGUoXG4gICAgICAgICAgICB0aGlzLnVuaXRDb252ZXJzaW9uU2VydmljZVxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENyZWF0ZXMgYSBzY2FsZSBiYXNlZCBvbiBnaXZlbiBjb25maWd1cmF0aW9uXG4gICAgICpcbiAgICAgKiBAcGFyYW0gc2NhbGVDb25maWdcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0U2NhbGUoXG4gICAgICAgIHNjYWxlQ29uZmlnOiBJVGltZXNlcmllc1NjYWxlQ29uZmlnLFxuICAgICAgICB1bml0czogVW5pdE9wdGlvbixcbiAgICAgICAgd2lkZ2V0Q29uZmlnPzogSVRpbWVzZXJpZXNXaWRnZXRDb25maWdcbiAgICApOiBJU2NhbGU8YW55PiB7XG4gICAgICAgIGxldCBzY2FsZTogSVNjYWxlPGFueT47XG5cbiAgICAgICAgc3dpdGNoIChzY2FsZUNvbmZpZy50eXBlKSB7XG4gICAgICAgICAgICBjYXNlIFRpbWVzZXJpZXNTY2FsZVR5cGUuVGltZToge1xuICAgICAgICAgICAgICAgIHNjYWxlID0gbmV3IFRpbWVTY2FsZSgpO1xuICAgICAgICAgICAgICAgIHNjYWxlLmlzVGltZXNlcmllc1NjYWxlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICBzY2FsZS5mb3JtYXR0ZXJzLnRpdGxlID0gdGltZVNlcmllc0RhdGV0aW1lRm9ybWF0dGVyO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2FzZSBUaW1lc2VyaWVzU2NhbGVUeXBlLkxpbmVhcjoge1xuICAgICAgICAgICAgICAgIHNjYWxlID0gbmV3IExpbmVhclNjYWxlKCk7XG4gICAgICAgICAgICAgICAgc2NhbGUuaXNUaW1lc2VyaWVzU2NhbGUgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHNjYWxlLmZvcm1hdHRlcnMudGljayA9IChcbiAgICAgICAgICAgICAgICAgICAgdmFsdWU6IHN0cmluZyB8IG51bWJlciB8IHVuZGVmaW5lZCxcbiAgICAgICAgICAgICAgICAgICAgaXNMYWJlbEZvcm1hdHRlcj86IGJvb2xlYW5cbiAgICAgICAgICAgICAgICApID0+XG4gICAgICAgICAgICAgICAgICAgIHRoaXMudW5pdENvbnZlcnNpb25QaXBlLnRyYW5zZm9ybShcbiAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlLFxuICAgICAgICAgICAgICAgICAgICAgICAgc2NhbGVDb25maWcucHJvcGVydGllcz8uYXhpc1VuaXRzID8/IHVuaXRzLFxuICAgICAgICAgICAgICAgICAgICAgICAgVW5pdEJhc2UuU3RhbmRhcmRcbiAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgVGltZXNlcmllc1NjYWxlVHlwZS5UaW1lSW50ZXJ2YWw6IHtcbiAgICAgICAgICAgICAgICBzY2FsZSA9IG5ldyBUaW1lSW50ZXJ2YWxTY2FsZShkdXJhdGlvbigxLCBcImhvdXJcIikpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy51cGRhdGVDb25maWd1cmF0aW9uKHNjYWxlLCBzY2FsZUNvbmZpZywgd2lkZ2V0Q29uZmlnKTtcblxuICAgICAgICByZXR1cm4gc2NhbGU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ3VycmVudGx5IG9ubHkgVGltZUludGVydmFsU2NhbGUgaGFzIGNvbmZpZ3VyYXRpb25cbiAgICAgKlxuICAgICAqIEBwYXJhbSBzY2FsZVxuICAgICAqIEBwYXJhbSBzY2FsZUNvbmZpZ1xuICAgICAqL1xuICAgIHB1YmxpYyB1cGRhdGVDb25maWd1cmF0aW9uKFxuICAgICAgICBzY2FsZTogSVNjYWxlPGFueT4sXG4gICAgICAgIHNjYWxlQ29uZmlnOiBJVGltZXNlcmllc1NjYWxlQ29uZmlnLFxuICAgICAgICB3aWRnZXRDb25maWc/OiBJVGltZXNlcmllc1dpZGdldENvbmZpZ1xuICAgICk6IHZvaWQge1xuICAgICAgICBzd2l0Y2ggKHNjYWxlQ29uZmlnLnR5cGUpIHtcbiAgICAgICAgICAgIGNhc2UgVGltZXNlcmllc1NjYWxlVHlwZS5UaW1lOiB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW50ZXJ2YWwgPSBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzPy50aW1lSW50ZXJ2YWw7XG4gICAgICAgICAgICAgICAgaWYgKGludGVydmFsPy5zdGFydERhdGV0aW1lICYmIGludGVydmFsPy5lbmREYXRldGltZSkge1xuICAgICAgICAgICAgICAgICAgICBzY2FsZS5maXhEb21haW4oW1xuICAgICAgICAgICAgICAgICAgICAgICAgaW50ZXJ2YWwuc3RhcnREYXRldGltZSxcbiAgICAgICAgICAgICAgICAgICAgICAgIGludGVydmFsLmVuZERhdGV0aW1lLFxuICAgICAgICAgICAgICAgICAgICBdKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlIFRpbWVzZXJpZXNTY2FsZVR5cGUuVGltZUludGVydmFsOiB7XG4gICAgICAgICAgICAgICAgY29uc3QgaW50ZXJ2YWwgPSBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzPy5pbnRlcnZhbDtcbiAgICAgICAgICAgICAgICBpZiAodHlwZW9mIGludGVydmFsID09PSBcIm51bWJlclwiKSB7XG4gICAgICAgICAgICAgICAgICAgIGlmIChpbnRlcnZhbCA8PSAwKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJJbnRlcnZhbCB2YWx1ZSBtdXN0IGJlIGdyZWF0ZXIgdGhhbiB6ZXJvLlwiXG4gICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChzY2FsZSBpbnN0YW5jZW9mIFRpbWVJbnRlcnZhbFNjYWxlKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBzY2FsZS5pbnRlcnZhbChkdXJhdGlvbihpbnRlcnZhbCwgXCJzZWNvbmRzXCIpKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgVGltZXNlcmllc1NjYWxlVHlwZS5MaW5lYXI6IHtcbiAgICAgICAgICAgICAgICBpZiAoc2NhbGVDb25maWcucHJvcGVydGllcz8uYXhpc1VuaXRzKSB7XG4gICAgICAgICAgICAgICAgICAgIHNjYWxlLnNjYWxlVW5pdHMgPSBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzLmF4aXNVbml0cztcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAod2lkZ2V0Q29uZmlnPy5wcmVzZXQgPT09IFRpbWVzZXJpZXNDaGFydFByZXNldC5TdGF0dXNCYXIpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICAgICAgc2NhbGVDb25maWcucHJvcGVydGllcz8uYXhpc1VuaXRzID09PSBcInBlcmNlbnRcIiAmJlxuICAgICAgICAgICAgICAgICAgICBzY2FsZS5zZXRGaXhEb21haW5WYWx1ZXNcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgc2NhbGUuc2V0Rml4RG9tYWluVmFsdWVzKFswLCAyNSwgNTAsIDc1LCAxMDBdKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzPy5heGlzVW5pdHMgIT09IFwicGVyY2VudFwiICYmXG4gICAgICAgICAgICAgICAgICAgIHNjYWxlQ29uZmlnLnByb3BlcnRpZXM/LmRvbWFpbiAmJlxuICAgICAgICAgICAgICAgICAgICBzY2FsZS5zZXRGaXhEb21haW5WYWx1ZXNcbiAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgbGV0IGRvbWFpbkFkanVzdGVkO1xuICAgICAgICAgICAgICAgICAgICBzd2l0Y2ggKHdpZGdldENvbmZpZz8ucHJlc2V0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjYXNlIFRpbWVzZXJpZXNDaGFydFByZXNldC5TdGFja2VkQmFyOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbkFkanVzdGVkID0gdGhpcy5nZXRTdGFja2VkQmFyU2NhbGVEb21haW4oXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlQ29uZmlnLnByb3BlcnRpZXMuZG9tYWluXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhc2UgVGltZXNlcmllc0NoYXJ0UHJlc2V0LlN0YWNrZWRBcmVhOlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvbWFpbkFkanVzdGVkID0gdGhpcy5nZXRTdGFja2VkQXJlYVNjYWxlRG9tYWluKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzLmRvbWFpbi5tYXhcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkb21haW5BZGp1c3RlZCA9IHRoaXMuZ2V0TGluZVNjYWxlRG9tYWluKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2FsZUNvbmZpZy5wcm9wZXJ0aWVzLmRvbWFpblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgICAgICBzY2FsZS5zZXRGaXhEb21haW5WYWx1ZXMoZG9tYWluQWRqdXN0ZWQpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0U3RhY2tlZEJhclNjYWxlRG9tYWluKHtcbiAgICAgICAgbWluLFxuICAgICAgICBtYXgsXG4gICAgfToge1xuICAgICAgICBtaW46IG51bWJlcjtcbiAgICAgICAgbWF4OiBudW1iZXI7XG4gICAgfSk6IG51bWJlcltdIHtcbiAgICAgICAgaWYgKG1heCA9PT0gMCB8fCBtYXggJSA0ID4gMCkge1xuICAgICAgICAgICAgbWF4ID0gbWF4ICsgNCAtIChtYXggJSA0KTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpbmNyZW1lbnQgPSBNYXRoLmZsb29yKChtYXggLSBtaW4pIC8gNCk7XG4gICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICBtaW4sXG4gICAgICAgICAgICBtaW4gKyBpbmNyZW1lbnQsXG4gICAgICAgICAgICBtaW4gKyAyICogaW5jcmVtZW50LFxuICAgICAgICAgICAgbWF4IC0gaW5jcmVtZW50LFxuICAgICAgICAgICAgbWF4LFxuICAgICAgICBdO1xuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0TGluZVNjYWxlRG9tYWluKHtcbiAgICAgICAgbWluLFxuICAgICAgICBtYXgsXG4gICAgfToge1xuICAgICAgICBtaW46IG51bWJlcjtcbiAgICAgICAgbWF4OiBudW1iZXI7XG4gICAgfSk6IG51bWJlcltdIHtcbiAgICAgICAgaWYgKG1pbiA+IG1heCkge1xuICAgICAgICAgICAgY29uc3QgdG1wID0gbWluO1xuICAgICAgICAgICAgbWluID0gbWF4O1xuICAgICAgICAgICAgbWF4ID0gdG1wO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgZXh0ZW50UmFuZ2UgPSBNYXRoLmFicygobWF4IC0gbWluKSAvICgobWF4ICsgbWluKSAvIDIpKTtcblxuICAgICAgICAvLyBmb3Igc21hbGwgZG9tYWluIHJhbmdlcyBpbmNyZWFzZSBkb21haW4gc28gdGhhdCBzcGlrZXMgYXJlIG5vdCBleGFnZ2VyYXRlZFxuICAgICAgICBpZiAoZXh0ZW50UmFuZ2UgPCAwLjUpIHtcbiAgICAgICAgICAgIG1pbiA9IG1pbiAtIE1hdGguYWJzKG1pbikgKiAwLjU7XG4gICAgICAgICAgICBtYXggPSBtYXggKyBNYXRoLmFicyhtYXgpICogMC41O1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gaGFuZGxlcyB6ZXJvIGNhc2VcbiAgICAgICAgaWYgKG1pbiA9PT0gMCAmJiBtYXggPT09IDApIHtcbiAgICAgICAgICAgIG1pbiA9IC0xO1xuICAgICAgICAgICAgbWF4ID0gMTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNwZWNpYWwgY2FzZSBmb3IgcmVhbCBzbWFsbCBudW1iZXIgc2luY2Ugbm90IHVzaW5nIHNpIHByZWZpeCBhbnltb3JlXG4gICAgICAgIGlmIChNYXRoLmFicyhtYXggLSBtaW4pIDwgMC4wNCkge1xuICAgICAgICAgICAgbWF4ICs9IDAuMDQ7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBwb2ludCA9IChtYXggLSBtaW4pIC8gNDtcbiAgICAgICAgcmV0dXJuIHJvdW5kVG9PcHRpbWFsRGVjaW1hbHMoW1xuICAgICAgICAgICAgbWluLFxuICAgICAgICAgICAgbWluICsgcG9pbnQsXG4gICAgICAgICAgICBtaW4gKyAyICogcG9pbnQsXG4gICAgICAgICAgICBtYXggLSBwb2ludCxcbiAgICAgICAgICAgIG1heCxcbiAgICAgICAgXSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXRTdGFja2VkQXJlYVNjYWxlRG9tYWluKG1heDogbnVtYmVyKTogbnVtYmVyW10ge1xuICAgICAgICBjb25zdCBwb2ludCA9IG1heCAvIDQ7XG4gICAgICAgIHJldHVybiBbMCwgcG9pbnQsIHBvaW50ICogMiwgbWF4IC0gcG9pbnQsIG1heF07XG4gICAgfVxufVxuIl19