UNPKG

@nova-ui/charts

Version:

Nova Charts is a library created to provide potential consumers with solutions for various data visualizations that conform with the Nova Design Language. It's designed to solve common patterns identified by UX designers, but also be very flexible so that

484 lines 77.1 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 isEmpty from "lodash/isEmpty"; import isUndefined from "lodash/isUndefined"; import { DONUT_GAUGE_LABEL_CLEARANCE_DEFAULT, GaugeMode, GAUGE_QUANTITY_SERIES_ID, GAUGE_REMAINDER_SERIES_ID, GAUGE_THRESHOLD_MARKERS_SERIES_ID, LINEAR_GAUGE_LABEL_CLEARANCE_DEFAULTS, StandardGaugeColor, StandardGaugeThresholdId, StandardGaugeThresholdMarkerRadius, } from "./constants"; import { Chart } from "../core/chart"; import { ChartAssist } from "../core/chart-assists/chart-assist"; import { linearGaugeGridConfig } from "../core/grid/config/linear-gauge-grid-config-fn"; import { XYGrid } from "../core/grid/xy-grid"; import { GAUGE_LABEL_FORMATTER_NAME_DEFAULT } from "../core/plugins/gauge/constants"; import { DonutGaugeLabelsPlugin } from "../core/plugins/gauge/donut-gauge-labels-plugin"; import { LinearGaugeLabelsPlugin } from "../core/plugins/gauge/linear-gauge-labels-plugin"; import { HorizontalBarAccessors } from "../renderers/bar/accessors/horizontal-bar-accessors"; import { VerticalBarAccessors } from "../renderers/bar/accessors/vertical-bar-accessors"; import { BarRenderer } from "../renderers/bar/bar-renderer"; import { barScales } from "../renderers/bar/bar-scales"; import { linearGaugeRendererConfig } from "../renderers/bar/linear-gauge-renderer-config"; import { LinearGaugeThresholdsRenderer } from "../renderers/bar/linear-gauge-thresholds-renderer"; import { stack } from "../renderers/bar/stacked-preprocessor"; import { RadialAccessors } from "../renderers/radial/accessors/radial-accessors"; import { donutGaugeRendererConfig } from "../renderers/radial/gauge/donut-gauge-renderer-config"; import { DonutGaugeThresholdsRenderer } from "../renderers/radial/gauge/donut-gauge-thresholds-renderer"; import { radialGrid } from "../renderers/radial/radial-grid-fn"; import { radial } from "../renderers/radial/radial-preprocessor"; import { RadialRenderer } from "../renderers/radial/radial-renderer"; import { radialScales } from "../renderers/radial/radial-scales"; /** * Convenience utility for simplifying gauge usage */ export class GaugeUtil { /** Value used for unifying the linear-style gauge visualization into a single bar stack */ static DATA_CATEGORY = "gauge"; /** * Creates a ChartAssist pre-configured based on the provided IGaugeConfig and GaugeMode * * @param gaugeConfig The gauge configuration * @param mode The gauge mode * @param labelsPlugin Optional labels plugin for direct control of label configuration. If not provided, a plugin will * be generated automatically. * * @returns {ChartAssist} A pre-configured chart assist */ static createChartAssist(gaugeConfig, mode, labelsPlugin) { const grid = mode === GaugeMode.Donut ? radialGrid() : new XYGrid(linearGaugeGridConfig(mode, gaugeConfig.linearThickness)); const gridConfig = grid.config(); const chart = new Chart(grid); const enableLabels = !isEmpty(gaugeConfig.thresholds?.definitions) && !gaugeConfig.thresholds?.disableMarkers; if (mode === GaugeMode.Donut) { if (enableLabels) { chart.addPlugin(labelsPlugin ?? new DonutGaugeLabelsPlugin()); gridConfig.dimension.margin = GaugeUtil.getMarginForLabelClearance(gaugeConfig, mode, gridConfig.dimension.margin); } return new ChartAssist(chart, radial); } if (enableLabels) { const flippedLabels = !!gaugeConfig.labels?.flipped; chart.addPlugin(labelsPlugin ?? new LinearGaugeLabelsPlugin({ flippedLabels })); gridConfig.dimension.margin = GaugeUtil.getMarginForLabelClearance(gaugeConfig, mode, gridConfig.dimension.margin); } return new ChartAssist(chart, stack); } /** * Assembles a gauge series set with all of the standard scales, renderers, accessors, etc. needed for creating a gauge visualization * * @param gaugeConfig The configuration for the gauge * @param mode The mode of the gauge (Donut, Horizontal, or Vertical) * * @returns {IChartAssistSeries<IAccessors>[]} The assembled series set */ static assembleSeriesSet(gaugeConfig, mode) { gaugeConfig.value = gaugeConfig.value ?? 0; gaugeConfig.max = gaugeConfig.max ?? 0; const clampedConfig = GaugeUtil.clampConfigToRange(gaugeConfig); const renderAttrs = GaugeUtil.generateRenderingAttributes(clampedConfig, mode); let thresholdsSeries; let seriesSet = []; if (clampedConfig.thresholds) { thresholdsSeries = GaugeUtil.generateThresholdSeries(clampedConfig, renderAttrs); seriesSet.push(thresholdsSeries); } const quantityAndRemainderSeriesSet = GaugeUtil.generateQuantityAndRemainderSeriesSet(clampedConfig, renderAttrs, thresholdsSeries?.activeThreshold); seriesSet = [ ...quantityAndRemainderSeriesSet, ...seriesSet, ]; return seriesSet; } /** * Updates the series set based on the provided configuration * * @param seriesSet The set of series to update * @param gaugeConfig The configuration for the gauge * * @returns {IChartAssistSeries<IAccessors>[]} The updated series set */ static update(seriesSet, gaugeConfig) { gaugeConfig.value = gaugeConfig.value ?? 0; gaugeConfig.max = gaugeConfig.max ?? 0; const clampedConfig = GaugeUtil.clampConfigToRange(gaugeConfig); const thresholdsData = GaugeUtil.generateThresholdsData(clampedConfig); const updatedSeriesSet = seriesSet.map((series) => { if (series.id === GAUGE_QUANTITY_SERIES_ID) { return { ...series, data: [ { category: GaugeUtil.DATA_CATEGORY, value: clampedConfig.value, }, ], activeThreshold: thresholdsData.activeThreshold, defaultColor: clampedConfig.defaultQuantityColor || StandardGaugeColor.Ok, }; } if (series.id === GAUGE_REMAINDER_SERIES_ID) { return { ...series, data: [ { category: GaugeUtil.DATA_CATEGORY, value: clampedConfig.max - clampedConfig.value, }, ], }; } if (series.id === GAUGE_THRESHOLD_MARKERS_SERIES_ID) { series.renderer.config.enabled = !clampedConfig.thresholds?.disableMarkers; series.renderer.config.markerRadius = clampedConfig.thresholds?.markerRadius ?? StandardGaugeThresholdMarkerRadius.Large; return { ...series, ...thresholdsData }; } return series; }); return updatedSeriesSet; } /** * Convenience function for creating a standard standard set of threshold configs. Includes configurations for warning and error thresholds. * * @param warningVal Value for the warning threshold * @param criticalVal Value for the critical threshold * * @returns {IGaugeThresholdsConfig} The thresholds configuration */ static createStandardThresholdsConfig(warningVal, criticalVal) { // assigning to variable to prevent "Expression form not supported" error const config = { definitions: { [StandardGaugeThresholdId.Warning]: { id: StandardGaugeThresholdId.Warning, value: warningVal, enabled: true, color: StandardGaugeColor.Warning, }, [StandardGaugeThresholdId.Critical]: { id: StandardGaugeThresholdId.Critical, value: criticalVal, enabled: true, color: StandardGaugeColor.Critical, }, }, reversed: false, disableMarkers: false, markerRadius: StandardGaugeThresholdMarkerRadius.Large, }; return config; } /** * Gets a new instance of the provided grid margin with updated values (if needed) to accommodate the label clearance specified * in the provided gauge configuration * * @param gaugeConfig The gauge's configuration * @param mode The mode of the gauge * @param margin The margin to update * * @returns {IAllAround<number>} The updated margin */ static getMarginForLabelClearance(gaugeConfig, mode, margin) { if (mode === GaugeMode.Donut) { const clearance = gaugeConfig.labels?.clearance ?? DONUT_GAUGE_LABEL_CLEARANCE_DEFAULT; // on the radial grid the maximum margin value is used for all margins, so we use the max value here const marginValue = Math.max(...Object.values(margin), clearance); return { top: marginValue, right: marginValue, bottom: marginValue, left: marginValue, }; } let marginToAdjust; const flippedLabels = !!gaugeConfig.labels?.flipped; if (mode === GaugeMode.Horizontal) { marginToAdjust = flippedLabels ? "top" : "bottom"; } else { marginToAdjust = flippedLabels ? "left" : "right"; } const clearance = gaugeConfig.labels?.clearance ?? LINEAR_GAUGE_LABEL_CLEARANCE_DEFAULTS[marginToAdjust]; const marginValue = Math.max(margin[marginToAdjust], clearance); return { ...margin, [marginToAdjust]: marginValue }; } /** * Generates a set of chart series used for visualizing the gauge's quantity and remainder data * * @param gaugeConfig The configuration for the gauge * @param renderingAttributes The attributes needed to visualized the thresholds * @param activeThreshold The currently active threshold * * @returns {IChartAssistSeries<IAccessors<any>>[]} A set of chart series representing the quantity and remainder data */ static generateQuantityAndRemainderSeriesSet(gaugeConfig, renderingAttributes, activeThreshold) { // assigning to variable to prevent "Lambda not supported" error const seriesSet = GaugeUtil.generateQuantityAndRemainderData(gaugeConfig).map((series) => { const { quantityAccessors, remainderAccessors, scales, mainRenderer, } = renderingAttributes; if (quantityAccessors.data) { quantityAccessors.data.color = (data, i, series, dataSeries) => dataSeries.activeThreshold ? dataSeries.activeThreshold.color : dataSeries.defaultColor; } if (remainderAccessors.data) { remainderAccessors.data.color = () => gaugeConfig.remainderColor || StandardGaugeColor.Remainder; } if (series.id === GAUGE_QUANTITY_SERIES_ID) { series.activeThreshold = activeThreshold; series.defaultColor = gaugeConfig.defaultQuantityColor || StandardGaugeColor.Ok; } return { ...series, accessors: series.id === GAUGE_QUANTITY_SERIES_ID ? quantityAccessors : remainderAccessors, scales, renderer: mainRenderer, }; }); return seriesSet; } /** * Generates a series for visualizing the gauge's thresholds * * @param gaugeConfig The configuration for the gauge * @param renderingAttributes The attributes needed to visualized the thresholds * * @returns {IChartAssistSeries<IAccessors>} The threshold series */ static generateThresholdSeries(gaugeConfig, renderingAttributes) { const { quantityAccessors, scales, thresholdsRenderer } = renderingAttributes; return { ...GaugeUtil.generateThresholdsData(gaugeConfig), accessors: quantityAccessors, scales, renderer: thresholdsRenderer, // instruct the radial preprocessor to ignore the threshold series when calculating the donut arcs excludeFromArcCalculation: true, // instruct the stacked and radial preprocessors that the threshold data doesn't need to be mutated preprocess: false, }; } /** * Generates the attributes required to instantiate a standard gauge in the specified mode * * @param gaugeConfig The configuration for the gauge * @param mode The mode of the gauge (Donut, Horizontal, or Vertical) * * @returns {IGaugeRenderingAttributes} The attributes required to instantiate a gauge */ static generateRenderingAttributes(gaugeConfig, mode) { const renderTools = GaugeUtil.generateRenderingTools(gaugeConfig, mode); const renderAttrs = { quantityAccessors: renderTools.quantityAccessorFunction(), remainderAccessors: renderTools.remainderAccessorFunction(), mainRenderer: renderTools.mainRendererFunction(), thresholdsRenderer: renderTools.thresholdsRendererFunction(), scales: renderTools.scaleFunction(), }; return renderAttrs; } /** * Generates rendering tools for standard gauge attributes * * @param gaugeConfig The configuration for the gauge * @param mode The mode of the gauge (Donut, Horizontal, or Vertical) * * @returns {IGaugeRenderingTools} The rendering tools for standard gauge attributes */ static generateRenderingTools(gaugeConfig, mode) { const thresholdsRendererConfig = { enabled: !gaugeConfig.thresholds?.disableMarkers, markerRadius: gaugeConfig.thresholds?.markerRadius, }; const labelFormatter = gaugeConfig.labels?.formatter; const renderingTools = { [GaugeMode.Donut]: { mainRendererFunction: () => new RadialRenderer(donutGaugeRendererConfig()), thresholdsRendererFunction: () => new DonutGaugeThresholdsRenderer(thresholdsRendererConfig), quantityAccessorFunction: () => new RadialAccessors(), remainderAccessorFunction: () => new RadialAccessors(), scaleFunction: () => { const scales = radialScales(); scales.r.formatters[GAUGE_LABEL_FORMATTER_NAME_DEFAULT] = labelFormatter; return scales; }, }, [GaugeMode.Horizontal]: { mainRendererFunction: () => new BarRenderer(linearGaugeRendererConfig()), thresholdsRendererFunction: () => new LinearGaugeThresholdsRenderer(thresholdsRendererConfig), quantityAccessorFunction: () => new HorizontalBarAccessors(), remainderAccessorFunction: () => new HorizontalBarAccessors(), scaleFunction: () => { const scales = barScales({ horizontal: true }); scales.x.formatters[GAUGE_LABEL_FORMATTER_NAME_DEFAULT] = labelFormatter; return scales; }, }, [GaugeMode.Vertical]: { mainRendererFunction: () => new BarRenderer(linearGaugeRendererConfig()), thresholdsRendererFunction: () => new LinearGaugeThresholdsRenderer(thresholdsRendererConfig), quantityAccessorFunction: () => new VerticalBarAccessors(), remainderAccessorFunction: () => new VerticalBarAccessors(), scaleFunction: () => { const scales = barScales(); scales.y.formatters[GAUGE_LABEL_FORMATTER_NAME_DEFAULT] = labelFormatter; return scales; }, }, }; return renderingTools[mode]; } /** * Generates quantity and remainder data in the format needed by the gauge visualization * * @param gaugeConfig The configuration for the gauge * * @returns {Partial<IDataSeries<IAccessors>>[]} Array of partial IDataSeries objects including the 'id' and 'data' properties of the quantity and remainder * series needed by the gauge visualization */ static generateQuantityAndRemainderData(gaugeConfig) { return [ // category property is used for unifying the linear-style gauge visualization into a single bar stack { id: GAUGE_QUANTITY_SERIES_ID, data: [ { category: GaugeUtil.DATA_CATEGORY, value: gaugeConfig.value, }, ], }, { id: GAUGE_REMAINDER_SERIES_ID, data: [ { category: GaugeUtil.DATA_CATEGORY, value: gaugeConfig.max - gaugeConfig.value, }, ], }, ]; } /** * Generates threshold data in the form needed by the gauge's thresholds visualization * * @param gaugeConfig The configuration for the gauge * * @returns {Partial<IDataSeries<IAccessors, IGaugeThresholdDatum>>} A partial IDataSeries object including the 'id' and 'data' properties of the thresholds * series used for the the gauge's thresholds visualization */ static generateThresholdsData(gaugeConfig) { if (!gaugeConfig.thresholds) { throw new Error("Thresholds are not defined in the gauge config. Unable to generate threshold data."); } const { thresholds, activeThreshold } = GaugeUtil.prepareThresholdsData(gaugeConfig); return { id: GAUGE_THRESHOLD_MARKERS_SERIES_ID, data: [ ...thresholds, // tack the max value onto the end (this is used for donut arc calculation) { id: "max", enabled: true, category: GaugeUtil.DATA_CATEGORY, value: gaugeConfig.max, color: "", }, ], activeThreshold, }; } /** * Generates visualization-specific thresholds data based on the gauge's configuration * * @param gaugeConfig The gauge configuration * * @returns {IGaugeThresholdsData} Thresholds data modified for use by the gauge visualization */ static prepareThresholdsData(gaugeConfig) { const thresholds = Object.values(gaugeConfig.thresholds?.definitions || []) .filter((threshold) => threshold.enabled) .map((threshold) => ({ ...threshold, category: GaugeUtil.DATA_CATEGORY, // threshold values are exclusive when in reversed mode, inclusive otherwise hit: gaugeConfig.thresholds?.reversed ? threshold.value < gaugeConfig.value : threshold.value <= gaugeConfig.value, })) .sort((a, b) => a.value - b.value); const activeThreshold = gaugeConfig .thresholds?.reversed ? thresholds?.find((threshold) => !isUndefined(threshold.hit) && !threshold.hit) : thresholds ?.slice() .reverse() .find((threshold) => threshold.hit); return { thresholds, activeThreshold, }; } static clampConfigToRange(gaugeConfig) { let value = gaugeConfig.value; if (gaugeConfig.value > gaugeConfig.max) { console.warn(`Configured gauge value ${gaugeConfig.value} is greater` + `than configured max ${gaugeConfig.max}. Clamping value to ${gaugeConfig.max}.`); value = gaugeConfig.max; } if (gaugeConfig.value < 0) { console.warn(`Configured gauge value ${gaugeConfig.value} is less than 0. Clamping value to 0.`); value = 0; } const thresholdDefs = gaugeConfig.thresholds?.definitions || {}; Object.keys(thresholdDefs || {}).forEach((thresholdId) => { const threshold = thresholdDefs[thresholdId]; if (threshold) { if (threshold.value > gaugeConfig.max) { console.warn(`Configured threshold value ${threshold.value} for threshold ${thresholdId} is greater` + `than configured max ${gaugeConfig.max}. Clamping value to ${gaugeConfig.max}.`); threshold.value = gaugeConfig.max; } if (threshold.value < 0) { console.warn(`Configured threshold value ${threshold.value} is less than 0. Clamping value to 0.`); threshold.value = 0; } } }); return { ...gaugeConfig, thresholds: { ...gaugeConfig.thresholds, definitions: thresholdDefs, }, value, }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2F1Z2UtdXRpbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9nYXVnZS9nYXVnZS11dGlsLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlEQUF5RDtBQUN6RCxFQUFFO0FBQ0YsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDNUQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSx1REFBdUQ7QUFDdkQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLDBFQUEwRTtBQUMxRSxpRkFBaUY7QUFDakYsNkVBQTZFO0FBQzdFLGlCQUFpQjtBQUVqQixPQUFPLE9BQU8sTUFBTSxnQkFBZ0IsQ0FBQztBQUNyQyxPQUFPLFdBQVcsTUFBTSxvQkFBb0IsQ0FBQztBQUU3QyxPQUFPLEVBQ0gsbUNBQW1DLEVBQ25DLFNBQVMsRUFDVCx3QkFBd0IsRUFDeEIseUJBQXlCLEVBQ3pCLGlDQUFpQyxFQUNqQyxxQ0FBcUMsRUFDckMsa0JBQWtCLEVBQ2xCLHdCQUF3QixFQUN4QixrQ0FBa0MsR0FDckMsTUFBTSxhQUFhLENBQUM7QUFRckIsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN0QyxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sb0NBQW9DLENBQUM7QUFTakUsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saURBQWlELENBQUM7QUFFeEYsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQzlDLE9BQU8sRUFBRSxrQ0FBa0MsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3JGLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGlEQUFpRCxDQUFDO0FBQ3pGLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLGtEQUFrRCxDQUFDO0FBQzNGLE9BQU8sRUFBRSxzQkFBc0Is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WUFDN0MsSUFBSSxTQUFTLEVBQUU7Z0JBQ1gsSUFBSSxTQUFTLENBQUMsS0FBSyxHQUFHLFdBQVcsQ0FBQyxHQUFHLEVBQUU7b0JBQ25DLE9BQU8sQ0FBQyxJQUFJLENBQ1IsOEJBQThCLFNBQVMsQ0FBQyxLQUFLLGtCQUFrQixXQUFXLGFBQWE7d0JBQ25GLHVCQUF1QixXQUFXLENBQUMsR0FBRyx1QkFBdUIsV0FBVyxDQUFDLEdBQUcsR0FBRyxDQUN0RixDQUFDO29CQUNGLFNBQVMsQ0FBQyxLQUFLLEdBQUcsV0FBVyxDQUFDLEdBQUcsQ0FBQztpQkFDckM7Z0JBRUQsSUFBSSxTQUFTLENBQUMsS0FBSyxHQUFHLENBQUMsRUFBRTtvQkFDckIsT0FBTyxDQUFDLElBQUksQ0FDUiw4QkFBOEIsU0FBUyxDQUFDLEtBQUssdUNBQXVDLENBQ3ZGLENBQUM7b0JBQ0YsU0FBUyxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7aUJBQ3ZCO2FBQ0o7UUFDTCxDQUFDLENBQUMsQ0FBQztRQUVILE9BQU87WUFDSCxHQUFHLFdBQVc7WUFDZCxVQUFVLEVBQUU7Z0JBQ1IsR0FBRyxXQUFXLENBQUMsVUFBVTtnQkFDekIsV0FBVyxFQUFFLGFBQWE7YUFDN0I7WUFDRCxLQUFLO1NBQ1IsQ0FBQztJQUNOLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyDCqSAyMDIyIFNvbGFyV2luZHMgV29ybGR3aWRlLCBMTEMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXG4vL1xuLy8gUGVybWlzc2lvbiBpcyBoZXJlYnkgZ3JhbnRlZCwgZnJlZSBvZiBjaGFyZ2UsIHRvIGFueSBwZXJzb24gb2J0YWluaW5nIGEgY29weVxuLy8gIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvXG4vLyAgZGVhbCBpbiB0aGUgU29mdHdhcmUgd2l0aG91dCByZXN0cmljdGlvbiwgaW5jbHVkaW5nIHdpdGhvdXQgbGltaXRhdGlvbiB0aGVcbi8vICByaWdodHMgdG8gdXNlLCBjb3B5LCBtb2RpZnksIG1lcmdlLCBwdWJsaXNoLCBkaXN0cmlidXRlLCBzdWJsaWNlbnNlLCBhbmQvb3Jcbi8vICBzZWxsIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpc1xuLy8gIGZ1cm5pc2hlZCB0byBkbyBzbywgc3ViamVjdCB0byB0aGUgZm9sbG93aW5nIGNvbmRpdGlvbnM6XG4vL1xuLy8gVGhlIGFib3ZlIGNvcHlyaWdodCBub3RpY2UgYW5kIHRoaXMgcGVybWlzc2lvbiBub3RpY2Ugc2hhbGwgYmUgaW5jbHVkZWQgaW5cbi8vICBhbGwgY29waWVzIG9yIHN1YnN0YW50aWFsIHBvcnRpb25zIG9mIHRoZSBTb2Z0d2FyZS5cbi8vXG4vLyBUSEUgU09GVFdBUkUgSVMgUFJPVklERUQgXCJBUyBJU1wiLCBXSVRIT1VUIFdBUlJBTlRZIE9GIEFOWSBLSU5ELCBFWFBSRVNTIE9SXG4vLyAgSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG4vLyAgRklUTkVTUyBGT1IgQSBQQVJUSUNVTEFSIFBVUlBPU0UgQU5EIE5PTklORlJJTkdFTUVOVC4gSU4gTk8gRVZFTlQgU0hBTEwgVEhFXG4vLyAgQVVUSE9SUyBPUiBDT1BZUklHSFQgSE9MREVSUyBCRSBMSUFCTEUgRk9SIEFOWSBDTEFJTSwgREFNQUdFUyBPUiBPVEhFUlxuLy8gIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG4vLyAgT1VUIE9GIE9SIElOIENPTk5FQ1RJT04gV0lUSCBUSEUgU09GVFdBUkUgT1IgVEhFIFVTRSBPUiBPVEhFUiBERUFMSU5HUyBJTlxuLy8gIFRIRSBTT0ZUV0FSRS5cblxuaW1wb3J0IGlzRW1wdHkgZnJvbSBcImxvZGFzaC9pc0VtcHR5XCI7XG5pbXBvcnQgaXNVbmRlZmluZWQgZnJvbSBcImxvZGFzaC9pc1VuZGVmaW5lZFwiO1xuXG5pbXBvcnQge1xuICAgIERPTlVUX0dBVUdFX0xBQkVMX0NMRUFSQU5DRV9ERUZBVUxULFxuICAgIEdhdWdlTW9kZSxcbiAgICBHQVVHRV9RVUFOVElUWV9TRVJJRVNfSUQsXG4gICAgR0FVR0VfUkVNQUlOREVSX1NFUklFU19JRCxcbiAgICBHQVVHRV9USFJFU0hPTERfTUFSS0VSU19TRVJJRVNfSUQsXG4gICAgTElORUFSX0dBVUdFX0xBQkVMX0NMRUFSQU5DRV9ERUZBVUxUUyxcbiAgICBTdGFuZGFyZEdhdWdlQ29sb3IsXG4gICAgU3RhbmRhcmRHYXVnZVRocmVzaG9sZElkLFxuICAgIFN0YW5kYXJkR2F1Z2VUaHJlc2hvbGRNYXJrZXJSYWRpdXMsXG59IGZyb20gXCIuL2NvbnN0YW50c1wiO1xuaW1wb3J0IHtcbiAgICBJR2F1Z2VDb25maWcsXG4gICAgSUdhdWdlVGhyZXNob2xkRGF0dW0sXG4gICAgSUdhdWdlVGhyZXNob2xkRGVmLFxuICAgIElHYXVnZVRocmVzaG9sZHNEYXRhLFxuICAgIElHYXVnZVRocmVzaG9sZHNDb25maWcsXG59IGZyb20gXCIuL3R5cGVzXCI7XG5pbXBvcnQgeyBDaGFydCB9IGZyb20gXCIuLi9jb3JlL2NoYXJ0XCI7XG5pbXBvcnQgeyBDaGFydEFzc2lzdCB9IGZyb20gXCIuLi9jb3JlL2NoYXJ0LWFzc2lzdHMvY2hhcnQtYXNzaXN0XCI7XG5pbXBvcnQgeyBSZW5kZXJlciB9IGZyb20gXCIuLi9jb3JlL2NvbW1vbi9yZW5kZXJlclwiO1xuaW1wb3J0IHsgSVJhZGlhbFNjYWxlcywgU2NhbGVzIH0gZnJvbSBcIi4uL2NvcmUvY29tbW9uL3NjYWxlcy90eXBlc1wiO1xuaW1wb3J0IHtcbiAgICBJQWNjZXNzb3JzLFxuICAgIElDaGFydEFzc2lzdFNlcmllcyxcbiAgICBJRGF0YVNlcmllcyxcbiAgICBJR2F1Z2VUaHJlc2hvbGRzUmVuZGVyZXJDb25maWcsXG59IGZyb20gXCIuLi9jb3JlL2NvbW1vbi90eXBlc1wiO1xuaW1wb3J0IHsgbGluZWFyR2F1Z2VHcmlkQ29uZmlnIH0gZnJvbSBcIi4uL2NvcmUvZ3JpZC9jb25maWcvbGluZWFyLWdhdWdlLWdyaWQtY29uZmlnLWZuXCI7XG5pbXBvcnQgeyBJQWxsQXJvdW5kIH0gZnJvbSBcIi4uL2NvcmUvZ3JpZC90eXBlc1wiO1xuaW1wb3J0IHsgWFlHcmlkIH0gZnJvbSBcIi4uL2NvcmUvZ3JpZC94eS1ncmlkXCI7XG5pbXBvcnQgeyBHQVVHRV9MQUJFTF9GT1JNQVRURVJfTkFNRV9ERUZBVUxUIH0gZnJvbSBcIi4uL2NvcmUvcGx1Z2lucy9nYXVnZS9jb25zdGFudHNcIjtcbmltcG9ydCB7IERvbnV0R2F1Z2VMYWJlbHNQbHVnaW4gfSBmcm9tIFwiLi4vY29yZS9wbHVnaW5zL2dhdWdlL2RvbnV0LWdhdWdlLWxhYmVscy1wbHVnaW5cIjtcbmltcG9ydCB7IExpbmVhckdhdWdlTGFiZWxzUGx1Z2luIH0gZnJvbSBcIi4uL2NvcmUvcGx1Z2lucy9nYXVnZS9saW5lYXItZ2F1Z2UtbGFiZWxzLXBsdWdpblwiO1xuaW1wb3J0IHsgSG9yaXpvbnRhbEJhckFjY2Vzc29ycyB9IGZyb20gXCIuLi9yZW5kZXJlcnMvYmFyL2FjY2Vzc29ycy9ob3Jpem9udGFsLWJhci1hY2Nlc3NvcnNcIjtcbmltcG9ydCB7IFZlcnRpY2FsQmFyQWNjZXNzb3JzIH0gZnJvbSBcIi4uL3JlbmRlcmVycy9iYXIvYWNjZXNzb3JzL3ZlcnRpY2FsLWJhci1hY2Nlc3NvcnNcIjtcbmltcG9ydCB7IEJhclJlbmRlcmVyIH0gZnJvbSBcIi4uL3JlbmRlcmVycy9iYXIvYmFyLXJlbmRlcmVyXCI7XG5pbXBvcnQgeyBiYXJTY2FsZXMgfSBmcm9tIFwiLi4vcmVuZGVyZXJzL2Jhci9iYXItc2NhbGVzXCI7XG5pbXBvcnQgeyBsaW5lYXJHYXVnZVJlbmRlcmVyQ29uZmlnIH0gZnJvbSBcIi4uL3JlbmRlcmVycy9iYXIvbGluZWFyLWdhdWdlLXJlbmRlcmVyLWNvbmZpZ1wiO1xuaW1wb3J0IHsgTGluZWFyR2F1Z2VUaHJlc2hvbGRzUmVuZGVyZXIgfSBmcm9tIFwiLi4vcmVuZGVyZXJzL2Jhci9saW5lYXItZ2F1Z2UtdGhyZXNob2xkcy1yZW5kZXJlclwiO1xuaW1wb3J0IHsgc3RhY2sgfSBmcm9tIFwiLi4vcmVuZGVyZXJzL2Jhci9zdGFja2VkLXByZXByb2Nlc3NvclwiO1xuaW1wb3J0IHsgUmFkaWFsQWNjZXNzb3JzIH0gZnJvbSBcIi4uL3JlbmRlcmVycy9yYWRpYWwvYWNjZXNzb3JzL3JhZGlhbC1hY2Nlc3NvcnNcIjtcbmltcG9ydCB7IGRvbnV0R2F1Z2VSZW5kZXJlckNvbmZpZyB9IGZyb20gXCIuLi9yZW5kZXJlcnMvcmFkaWFsL2dhdWdlL2RvbnV0LWdhdWdlLXJlbmRlcmVyLWNvbmZpZ1wiO1xuaW1wb3J0IHsgRG9udXRHYXVnZVRocmVzaG9sZHNSZW5kZXJlciB9IGZyb20gXCIuLi9yZW5kZXJlcnMvcmFkaWFsL2dhdWdlL2RvbnV0LWdhdWdlLXRocmVzaG9sZHMtcmVuZGVyZXJcIjtcbmltcG9ydCB7IHJhZGlhbEdyaWQgfSBmcm9tIFwiLi4vcmVuZGVyZXJzL3JhZGlhbC9yYWRpYWwtZ3JpZC1mblwiO1xuaW1wb3J0IHsgcmFkaWFsIH0gZnJvbSBcIi4uL3JlbmRlcmVycy9yYWRpYWwvcmFkaWFsLXByZXByb2Nlc3NvclwiO1xuaW1wb3J0IHsgUmFkaWFsUmVuZGVyZXIgfSBmcm9tIFwiLi4vcmVuZGVyZXJzL3JhZGlhbC9yYWRpYWwtcmVuZGVyZXJcIjtcbmltcG9ydCB7IHJhZGlhbFNjYWxlcyB9IGZyb20gXCIuLi9yZW5kZXJlcnMvcmFkaWFsL3JhZGlhbC1zY2FsZXNcIjtcblxuLyoqXG4gKiBAaWdub3JlXG4gKiBBdHRyaWJ1dGVzIG5lZWRlZCBieSBhIGdhdWdlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSUdhdWdlUmVuZGVyaW5nQXR0cmlidXRlcyB7XG4gICAgLyoqIEFjY2Vzc29ycyBmb3IgdGhlIGdhdWdlIHF1YW50aXR5IHNlZ21lbnQgKi9cbiAgICBxdWFudGl0eUFjY2Vzc29yczogSUFjY2Vzc29ycztcbiAgICAvKiogQWNjZXNzb3JzIGZvciB0aGUgZ2F1Z2UgcmVtYWluZGVyIHNlZ21lbnQgKi9cbiAgICByZW1haW5kZXJBY2Nlc3NvcnM6IElBY2Nlc3NvcnM7XG4gICAgLyoqIFNjYWxlcyBmb3IgdGhlIGdhdWdlICovXG4gICAgc2NhbGVzOiBTY2FsZXM7XG4gICAgLyoqIFJlbmRlcmVyIGZvciB0aGUgcHJpbWFyeSBnYXVnZSB2aXN1YWxpemF0aW9uICovXG4gICAgbWFpblJlbmRlcmVyOiBSZW5kZXJlcjxJQWNjZXNzb3JzPjtcbiAgICAvKiogUmVuZGVyZXIgZm9yIHRoZSBnYXVnZSB0aHJlc2hvbGQgdmlzdWFsaXphdGlvbiAqL1xuICAgIHRocmVzaG9sZHNSZW5kZXJlcjogUmVuZGVyZXI8SUFjY2Vzc29ycz47XG59XG5cbi8qKlxuICogQGlnbm9yZVxuICogSW50ZXJmYWNlIGZvciBhbiBvYmplY3QgdGhhdCBjYW4gYmUgdXNlZCB0byBjcmVhdGUgdGhlIHJlbmRlcmluZyBhdHRyaWJ1dGVzIG5lZWRlZCBieSBhIGdhdWdlXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgSUdhdWdlUmVuZGVyaW5nVG9vbHMge1xuICAgIC8qKiBGdW5jdGlvbiBmb3IgY3JlYXRpbmcgcXVhbnRpdHkgYWNjZXNzb3JzICovXG4gICAgcXVhbnRpdHlBY2Nlc3NvckZ1bmN0aW9uOiAoKSA9PiBJQWNjZXNzb3JzO1xuICAgIC8qKiBGdW5jdGlvbiBmb3IgY3JlYXRpbmcgcmVtYWluZGVyIGFjY2Vzc29ycyAqL1xuICAgIHJlbWFpbmRlckFjY2Vzc29yRnVuY3Rpb246ICgpID0+IElBY2Nlc3NvcnM7XG4gICAgLyoqIEZ1bmN0aW9uIGZvciBjcmVhdGluZyBzY2FsZXMgKi9cbiAgICBzY2FsZUZ1bmN0aW9uOiAoKSA9PiBTY2FsZXMgfCBJUmFkaWFsU2NhbGVzO1xuICAgIC8qKiBGdW5jdGlvbiBmb3IgY3JlYXRpbmcgYSBtYWluIHJlbmRlcmVyICovXG4gICAgbWFpblJlbmRlcmVyRnVuY3Rpb246ICgpID0+IFJlbmRlcmVyPElBY2Nlc3NvcnM+O1xuICAgIC8qKiBGdW5jdGlvbiBmb3IgY3JlYXRpbmcgYSB0aHJlc2hvbGRzIHJlbmRlcmVyICovXG4gICAgdGhyZXNob2xkc1JlbmRlcmVyRnVuY3Rpb246ICgpID0+IFJlbmRlcmVyPElBY2Nlc3NvcnM+O1xufVxuXG4vKipcbiAqIENvbnZlbmllbmNlIHV0aWxpdHkgZm9yIHNpbXBsaWZ5aW5nIGdhdWdlIHVzYWdlXG4gKi9cbmV4cG9ydCBjbGFzcyBHYXVnZVV0aWwge1xuICAgIC8qKiBWYWx1ZSB1c2VkIGZvciB1bmlmeWluZyB0aGUgbGluZWFyLXN0eWxlIGdhdWdlIHZpc3VhbGl6YXRpb24gaW50byBhIHNpbmdsZSBiYXIgc3RhY2sgKi9cbiAgICBwdWJsaWMgc3RhdGljIHJlYWRvbmx5IERBVEFfQ0FURUdPUlkgPSBcImdhdWdlXCI7XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgQ2hhcnRBc3Npc3QgcHJlLWNvbmZpZ3VyZWQgYmFzZWQgb24gdGhlIHByb3ZpZGVkIElHYXVnZUNvbmZpZyBhbmQgR2F1Z2VNb2RlXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZ2F1Z2VDb25maWcgVGhlIGdhdWdlIGNvbmZpZ3VyYXRpb25cbiAgICAgKiBAcGFyYW0gbW9kZSBUaGUgZ2F1Z2UgbW9kZVxuICAgICAqIEBwYXJhbSBsYWJlbHNQbHVnaW4gT3B0aW9uYWwgbGFiZWxzIHBsdWdpbiBmb3IgZGlyZWN0IGNvbnRyb2wgb2YgbGFiZWwgY29uZmlndXJhdGlvbi4gSWYgbm90IHByb3ZpZGVkLCBhIHBsdWdpbiB3aWxsXG4gICAgICogICAgICAgICAgICAgICAgICAgICBiZSBnZW5lcmF0ZWQgYXV0b21hdGljYWxseS5cbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtDaGFydEFzc2lzdH0gQSBwcmUtY29uZmlndXJlZCBjaGFydCBhc3Npc3RcbiAgICAgKi9cbiAgICBwdWJsaWMgc3RhdGljIGNyZWF0ZUNoYXJ0QXNzaXN0KFxuICAgICAgICBnYXVnZUNvbmZpZzogSUdhdWdlQ29uZmlnLFxuICAgICAgICBtb2RlOiBHYXVnZU1vZGUsXG4gICAgICAgIGxhYmVsc1BsdWdpbj86IERvbnV0R2F1Z2VMYWJlbHNQbHVnaW4gfCBMaW5lYXJHYXVnZUxhYmVsc1BsdWdpblxuICAgICk6IENoYXJ0QXNzaXN0IHtcbiAgICAgICAgY29uc3QgZ3JpZCA9XG4gICAgICAgICAgICBtb2RlID09PSBHYXVnZU1vZGUuRG9udXRcbiAgICAgICAgICAgICAgICA/IHJhZGlhbEdyaWQoKVxuICAgICAgICAgICAgICAgIDogbmV3IFhZR3JpZChcbiAgICAgICAgICAgICAgICAgICAgICBsaW5lYXJHYXVnZUdyaWRDb25maWcobW9kZSwgZ2F1Z2VDb25maWcubGluZWFyVGhpY2tuZXNzKVxuICAgICAgICAgICAgICAgICAgKTtcbiAgICAgICAgY29uc3QgZ3JpZENvbmZpZyA9IGdyaWQuY29uZmlnKCk7XG4gICAgICAgIGNvbnN0IGNoYXJ0ID0gbmV3IENoYXJ0KGdyaWQpO1xuICAgICAgICBjb25zdCBlbmFibGVMYWJlbHMgPVxuICAgICAgICAgICAgIWlzRW1wdHkoZ2F1Z2VDb25maWcudGhyZXNob2xkcz8uZGVmaW5pdGlvbnMpICYmXG4gICAgICAgICAgICAhZ2F1Z2VDb25maWcudGhyZXNob2xkcz8uZGlzYWJsZU1hcmtlcnM7XG5cbiAgICAgICAgaWYgKG1vZGUgPT09IEdhdWdlTW9kZS5Eb251dCkge1xuICAgICAgICAgICAgaWYgKGVuYWJsZUxhYmVscykge1xuICAgICAgICAgICAgICAgIGNoYXJ0LmFkZFBsdWdpbihsYWJlbHNQbHVnaW4gPz8gbmV3IERvbnV0R2F1Z2VMYWJlbHNQbHVnaW4oKSk7XG4gICAgICAgICAgICAgICAgZ3JpZENvbmZpZy5kaW1lbnNpb24ubWFyZ2luID1cbiAgICAgICAgICAgICAgICAgICAgR2F1Z2VVdGlsLmdldE1hcmdpbkZvckxhYmVsQ2xlYXJhbmNlKFxuICAgICAgICAgICAgICAgICAgICAgICAgZ2F1Z2VDb25maWcsXG4gICAgICAgICAgICAgICAgICAgICAgICBtb2RlLFxuICAgICAgICAgICAgICAgICAgICAgICAgZ3JpZENvbmZpZy5kaW1lbnNpb24ubWFyZ2luXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiBuZXcgQ2hhcnRBc3Npc3QoY2hhcnQsIHJhZGlhbCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZW5hYmxlTGFiZWxzKSB7XG4gICAgICAgICAgICBjb25zdCBmbGlwcGVkTGFiZWxzID0gISFnYXVnZUNvbmZpZy5sYWJlbHM/LmZsaXBwZWQ7XG4gICAgICAgICAgICBjaGFydC5hZGRQbHVnaW4oXG4gICAgICAgICAgICAgICAgbGFiZWxzUGx1Z2luID8/IG5ldyBMaW5lYXJHYXVnZUxhYmVsc1BsdWdpbih7IGZsaXBwZWRMYWJlbHMgfSlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBncmlkQ29uZmlnLmRpbWVuc2lvbi5tYXJnaW4gPSBHYXVnZVV0aWwuZ2V0TWFyZ2luRm9yTGFiZWxDbGVhcmFuY2UoXG4gICAgICAgICAgICAgICAgZ2F1Z2VDb25maWcsXG4gICAgICAgICAgICAgICAgbW9kZSxcbiAgICAgICAgICAgICAgICBncmlkQ29uZmlnLmRpbWVuc2lvbi5tYXJnaW5cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gbmV3IENoYXJ0QXNzaXN0KGNoYXJ0LCBzdGFjayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQXNzZW1ibGVzIGEgZ2F1Z2Ugc2VyaWVzIHNldCB3aXRoIGFsbCBvZiB0aGUgc3RhbmRhcmQgc2NhbGVzLCByZW5kZXJlcnMsIGFjY2Vzc29ycywgZXRjLiBuZWVkZWQgZm9yIGNyZWF0aW5nIGEgZ2F1Z2UgdmlzdWFsaXphdGlvblxuICAgICAqXG4gICAgICogQHBhcmFtIGdhdWdlQ29uZmlnIFRoZSBjb25maWd1cmF0aW9uIGZvciB0aGUgZ2F1Z2VcbiAgICAgKiBAcGFyYW0gbW9kZSBUaGUgbW9kZSBvZiB0aGUgZ2F1Z2UgKERvbnV0LCBIb3Jpem9udGFsLCBvciBWZXJ0aWNhbClcbiAgICAgKlxuICAgICAqIEByZXR1cm5zIHtJQ2hhcnRBc3Npc3RTZXJpZXM8SUFjY2Vzc29ycz5bXX0gVGhlIGFzc2VtYmxlZCBzZXJpZXMgc2V0XG4gICAgICovXG4gICAgcHVibGljIHN0YXRpYyBhc3NlbWJsZVNlcmllc1NldChcbiAgICAgICAgZ2F1Z2VDb25maWc6IElHYXVnZUNvbmZpZyxcbiAgICAgICAgbW9kZTogR2F1Z2VNb2RlXG4gICAgKTogSUNoYXJ0QXNzaXN0U2VyaWVzPElBY2Nlc3NvcnM+W10ge1xuICAgICAgICBnYXVnZUNvbmZpZy52YWx1ZSA9IGdhdWdlQ29uZmlnLnZhbHVlID8/IDA7XG4gICAgICAgIGdhdWdlQ29uZmlnLm1heCA9IGdhdWdlQ29uZmlnLm1heCA/PyAwO1xuICAgICAgICBjb25zdCBjbGFtcGVkQ29uZmlnID0gR2F1Z2VVdGlsLmNsYW1wQ29uZmlnVG9SYW5nZShnYXVnZUNvbmZpZyk7XG4gICAgICAgIGNvbnN0IHJlbmRlckF0dHJzID0gR2F1Z2VVdGlsLmdlbmVyYXRlUmVuZGVyaW5nQXR0cmlidXRlcyhcbiAgICAgICAgICAgIGNsYW1wZWRDb25maWcsXG4gICAgICA