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

331 lines 44.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 { select } from "d3-selection"; import each from "lodash/each"; import isEmpty from "lodash/isEmpty"; import { Lasagna } from "../common/lasagna"; import { UtilityService } from "../common/utility.service"; import { GridConfig } from "./config/grid-config"; export const borderMidpoint = 0.5; /** * @implements {IGrid} * Implementation for the dimensions, scaling, interactive area, and borders of a basic grid */ export class Grid { /** Class name for the grid */ static GRID_CLASS_NAME = "nui-chart-grid"; /** Class name applied to each of the grid's borders by default */ static DEFAULT_BORDER_CLASS_NAME = "nui-chart-border"; /** Prefix applied to the rendering area clip path id */ static RENDERING_AREA_CLIP_PATH_PREFIX = "clip-path_"; /** Name for the lasagna layer containing the grid's rendered elements */ static GRID_ELEMENTS_LAYER_NAME = "grid-elements"; /** Name for the rendering area lasagna layer */ static RENDERING_AREA_LAYER_NAME = "rendering-area"; /** @ignore Height correction needed to prevent interaction gap between vertically stacked charts */ static RENDER_AREA_HEIGHT_CORRECTION = 1; /** @ignore Width correction needed to prevent interaction gap between right side of grid and the edge of the rendering area */ static RENDER_AREA_WIDTH_CORRECTION = 1; /** @ignore Width correction needed to sync bottom border length and grid width to tick placement */ static TICK_DIMENSION_CORRECTION = 1; /** Subject for indicating that the chart's dimensions should be updated */ updateChartDimensionsSubject; /** Event bus provided by the chart */ eventBus; /** d3 container for the grid */ container; /** d3 selection for the grid's rendering area clip path */ renderingAreaClipPath; /** d3 selection for the grid's rendering area */ renderingArea; /** d3 selection for the grid's interactive area */ interactiveArea; /** The grid's layer manager */ lasagna; /** Lasagna layer for the grid's rendered elements */ gridElementsLayer; /** Definition of the grid's borders as rendered */ borders = {}; /** Property value of the grid's scales */ _scales; /** Property value of the grid's configuration */ _config; /** Property value of the grid's target d3 selection */ _target; /** See {@link IGrid#getInteractiveArea} */ getInteractiveArea() { return this.interactiveArea; } /** See {@link IGrid#getLasagna} */ getLasagna() { return this.lasagna; } /** @ignore */ set scales(scales) { this._scales = scales; } /** @ignore */ get scales() { return this._scales; } /** See {@link IGrid#config} */ config(config) { if (config === undefined) { return this._config; } this._config = config; return this; } /** See {@link IGrid#target} */ target(target) { if (target === undefined) { return this._target; } this._target = target; return this; } /** See {@link IGrid#build} */ build() { if (!this.config()) { const config = new GridConfig(); this.config(config); } this.container = this._target.append("g").attrs({ class: Grid.GRID_CLASS_NAME, }); const clipPathId = Grid.RENDERING_AREA_CLIP_PATH_PREFIX + UtilityService.uuid(); // Asserting similar type to avoid refactoring all the grids // TODO: Refactor lasagna service to accept multiple D3Selection types // or refactor grid implementations/interfaces to maintain the same selection type this.lasagna = new Lasagna(this.container, clipPathId); this.renderingArea = this.buildRenderingArea(clipPathId); this.adjustRenderingArea(); this.gridElementsLayer = this.lasagna.addLayer({ name: Grid.GRID_ELEMENTS_LAYER_NAME, order: 100, clipped: false, }); const borders = this.buildBorders(this.gridElementsLayer); if (borders) { this.borders = borders; } return this; } /** * Derived classes override this method to build the grid's plugins * * @param {IChart} chart The chart instance to pass to each plugin * * @returns {IChartPlugin[]} Default implementation returns an empty array */ buildPlugins(chart) { return []; } /** See {@link IGrid#update} */ update() { if (isEmpty(this.scales)) { return this; } this.updateBorders(); this.adjustRenderingArea(); return this; } /** See {@link IGrid#updateDimensions} */ updateDimensions(dimensions) { const dimensionConfig = this.config().dimension; if (dimensions.width) { dimensionConfig.outerWidth(dimensions.width - this.getOuterWidthDimensionCorrection()); } if (dimensions.height) { dimensionConfig.outerHeight(dimensions.height); } this.adjustRenderingArea(); this.updateRanges(); return this; } /** See {@link IGrid#updateRanges} */ updateRanges() { this.update(); return this; } /** * Calculate the width correction needed for accommodating grid elements that may extend beyond the chart's configured width */ getOuterWidthDimensionCorrection() { return Grid.TICK_DIMENSION_CORRECTION; } /** * Builds the grid borders as SVGElements based on the specified configuration * * @param {D3Selection} container d3 container for the borders * * @returns {Partial<IBorders>} The grid's borders */ buildBorders(container) { if (!this.config() || !this.config().borders) { return; } const borderConfigs = this.config().borders; const borders = {}; const borderKeys = Object.keys(borderConfigs); each(borderKeys, (side) => { // We're creating even invisible borders and updating visibility afterwards if (borderConfigs[side]) { borders[side] = this.createBorder(container, borderConfigs[side]) ?? undefined; } }); return borders; } /** * Adjusts the grid's rendering area and clip path based on the grid's configured width and height */ adjustRenderingArea = () => { const d = this.config().dimension; const disableHeightCorrection = this.config().disableRenderAreaHeightCorrection; const disableWidthCorrection = this.config().disableRenderAreaWidthCorrection; const renderingAreaClipPathAttrs = { width: Math.max(0, d.width()), height: Math.max(0, d.height() + (disableHeightCorrection ? 0 : Grid.RENDER_AREA_HEIGHT_CORRECTION)), }; if (!disableHeightCorrection) { renderingAreaClipPathAttrs["y"] = -Grid.RENDER_AREA_HEIGHT_CORRECTION; } const renderingAreaAttrs = { ...renderingAreaClipPathAttrs, // Width correction needed to prevent interaction gap between right side of grid and the edge of the rendering area width: Math.max(0, d.width() - (disableWidthCorrection ? 0 : Grid.RENDER_AREA_WIDTH_CORRECTION)), }; this.renderingAreaClipPath.attrs(renderingAreaClipPathAttrs); this.renderingArea.attrs(renderingAreaAttrs); }; /** * Builds the grid's rendering area as a layer on the lasagna * * @param {string} clipPathId The clip path identifier * * @returns {D3Selection} The grid's rendering area */ buildRenderingArea(clipPathId) { this.renderingAreaClipPath = this._target .append("clipPath") .attr("id", clipPathId) .append("rect"); const renderingAreaContainer = this.lasagna.addLayer({ name: Grid.RENDERING_AREA_LAYER_NAME, order: -1, clipped: true, }); return renderingAreaContainer.append("rect").attrs({ "pointer-events": "all", fill: "transparent", }); } /** * Creates a border with the specified configuration in the provided container * * @param {D3Selection} container The container to append the border to * @param {IBorderConfig} config The configuration to apply to the border * * @returns {SVGElement} The created border */ createBorder(container, config) { const border = container .append("line") .attr("class", config.className || Grid.DEFAULT_BORDER_CLASS_NAME); if (config.width) { // use style instead of attr to override css style border.style("stroke-width", config.width); } if (config.color) { // use style instead of attr to override css style border.style("stroke", config.color); } return border.node(); } // TODO: borders are evil. reconsider! // We're using borders instead of axis line and because of that we need to do these weird size adjustments updateBottomBorder() { if (!this.borders.bottom) { throw new Error("BottomBorder is not defined"); } select(this.borders.bottom) .attrs({ x1: 0, y1: this.config().dimension.height() - borderMidpoint, x2: this.config().dimension.width() + Grid.TICK_DIMENSION_CORRECTION, y2: this.config().dimension.height() - borderMidpoint, class: this._config.borders.bottom.className || Grid.DEFAULT_BORDER_CLASS_NAME, }) .classed("hidden", !this._config.borders.bottom?.visible); } /** * Updates the d3 line positioning and visibility attributes of each of the configured borders */ updateBorders() { if (this.borders.bottom) { this.updateBottomBorder(); } if (this.borders.top) { select(this.borders.top) .attrs({ x1: 0, y1: borderMidpoint, x2: this.config().dimension.width() + Grid.TICK_DIMENSION_CORRECTION, y2: borderMidpoint, }) .classed("hidden", !this._config.borders.top?.visible); } if (this.borders.right) { select(this.borders.right) .attrs({ x1: this.config().dimension.width() - borderMidpoint, y1: 0, x2: this.config().dimension.width() - borderMidpoint, y2: this.config().dimension.height() + Grid.TICK_DIMENSION_CORRECTION, // to get nice alignment with ticks }) .classed("hidden", !this._config.borders.right?.visible); } if (this.borders.left) { select(this.borders.left) .attrs({ x1: borderMidpoint, y1: 0, x2: borderMidpoint, y2: this.config().dimension.height() + Grid.TICK_DIMENSION_CORRECTION, // to get nice alignment with ticks }) .classed("hidden", !this._config.borders.left?.visible); } } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ3JpZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb3JlL2dyaWQvZ3JpZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUFFLE1BQU0sRUFBYSxNQUFNLGNBQWMsQ0FBQztBQUNqRCxPQUFPLElBQUksTUFBTSxhQUFhLENBQUM7QUFDL0IsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFJckMsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLG1CQUFtQixDQUFDO0FBSTVDLE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFVbEQsTUFBTSxDQUFDLE1BQU0sY0FBYyxHQUFHLEdBQUcsQ0FBQztBQUlsQzs7O0dBR0c7QUFDSCxNQUFNLE9BQWdCLElBQUk7SUFDdEIsOEJBQThCO0lBQ3ZCLE1BQU0sQ0FBQyxlQUFlLEdBQUcsZ0JBQWdCLENBQUM7SUFFakQsa0VBQWtFO0lBQzNELE1BQU0sQ0FBQyx5QkFBeUIsR0FBRyxrQkFBa0IsQ0FBQztJQUU3RCx3REFBd0Q7SUFDakQsTUFBTSxDQUFDLCtCQUErQixHQUFHLFlBQVksQ0FBQztJQUU3RCx5RUFBeUU7SUFDbEUsTUFBTSxDQUFDLHdCQUF3QixHQUFHLGVBQWUsQ0FBQztJQUV6RCxnREFBZ0Q7SUFDekMsTUFBTSxDQUFDLHlC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QUksQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO2lCQUNyQixLQUFLLENBQUM7Z0JBQ0gsRUFBRSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLEdBQUcsY0FBYztnQkFDcEQsRUFBRSxFQUFFLENBQUM7Z0JBQ0wsRUFBRSxFQUFFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsS0FBSyxFQUFFLEdBQUcsY0FBYztnQkFDcEQsRUFBRSxFQUNFLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFO29CQUNoQyxJQUFJLENBQUMseUJBQXlCLEVBQUUsbUNBQW1DO2FBQzFFLENBQUM7aUJBQ0QsT0FBTyxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxPQUFPLENBQUMsQ0FBQztTQUNoRTtRQUNELElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUU7WUFDbkIsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO2lCQUNwQixLQUFLLENBQUM7Z0JBQ0gsRUFBRSxFQUFFLGNBQWM7Z0JBQ2xCLEVBQUUsRUFBRSxDQUFDO2dCQUNMLEVBQUUsRUFBRSxjQUFjO2dCQUNsQixFQUFFLEVBQ0UsSUFBSSxDQUFDLE1BQU0sRUFBRSxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUU7b0JBQ2hDLElBQUksQ0FBQyx5QkFBeUIsRUFBRSxtQ0FBbUM7YUFDMUUsQ0FBQztpQkFDRCxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1NBQy9EO0lBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQgeyBzZWxlY3QsIFNlbGVjdGlvbiB9IGZyb20gXCJkMy1zZWxlY3Rpb25cIjtcbmltcG9ydCBlYWNoIGZyb20gXCJsb2Rhc2gvZWFjaFwiO1xuaW1wb3J0IGlzRW1wdHkgZnJvbSBcImxvZGFzaC9pc0VtcHR5XCI7XG5pbXBvcnQgeyBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcblxuaW1wb3J0IHsgRXZlbnRCdXMgfSBmcm9tIFwiLi4vY29tbW9uL2V2ZW50LWJ1c1wiO1xuaW1wb3J0IHsgTGFzYWduYSB9IGZyb20gXCIuLi9jb21tb24vbGFzYWduYVwiO1xuaW1wb3J0IHsgU2NhbGVzSW5kZXggfSBmcm9tIFwiLi4vY29tbW9uL3NjYWxlcy90eXBlc1wiO1xuaW1wb3J0IHsgSUNoYXJ0LCBJQ2hhcnRFdmVudCwgSUNoYXJ0UGx1Z2luIH0gZnJvbSBcIi4uL2NvbW1vbi90eXBlc1wiO1xuaW1wb3J0IHsgRDNTZWxlY3Rpb24gfSBmcm9tIFwiLi4vY29tbW9uL3R5cGVzXCI7XG5pbXBvcnQgeyBVdGlsaXR5U2VydmljZSB9IGZyb20gXCIuLi9jb21tb24vdXRpbGl0eS5zZXJ2aWNlXCI7XG5pbXBvcnQgeyBHcmlkQ29uZmlnIH0gZnJvbSBcIi4vY29uZmlnL2dyaWQtY29uZmlnXCI7XG5pbXBvcnQge1xuICAgIElBbGxBcm91bmQsXG4gICAgSUJvcmRlckNvbmZpZyxcbiAgICBJQm9yZGVycyxcbiAgICBJRGltZW5zaW9ucyxcbiAgICBJR3JpZCxcbiAgICBJR3JpZENvbmZpZyxcbn0gZnJvbSBcIi4vdHlwZXNcIjtcblxuZXhwb3J0IGNvbnN0IGJvcmRlck1pZHBvaW50ID0gMC41O1xuXG50eXBlIEJvcmRlcktleSA9IGtleW9mIElBbGxBcm91bmQ8SUJvcmRlckNvbmZpZz47XG5cbi8qKlxuICogQGltcGxlbWVudHMge0lHcmlkfVxuICogSW1wbGVtZW50YXRpb24gZm9yIHRoZSBkaW1lbnNpb25zLCBzY2FsaW5nLCBpbnRlcmFjdGl2ZSBhcmVhLCBhbmQgYm9yZGVycyBvZiBhIGJhc2ljIGdyaWRcbiAqL1xuZXhwb3J0IGFic3RyYWN0IGNsYXNzIEdyaWQgaW1wbGVtZW50cyBJR3JpZCB7XG4gICAgLyoqIENsYXNzIG5hbWUgZm9yIHRoZSBncmlkICovXG4gICAgcHVibGljIHN0YXRpYyBHUklEX0NMQVNTX05BTUUgPSBcIm51aS1jaGFydC1ncmlkXCI7XG5cbiAgICAvKiogQ2xhc3MgbmFtZSBhcHBsaWVkIHRvIGVhY2ggb2YgdGhlIGdyaWQncyBib3JkZXJzIGJ5IGRlZmF1bHQgKi9cbiAgICBwdWJsaWMgc3RhdGljIERFRkFVTFRfQk9SREVSX0NMQVNTX05BTUUgPSBcIm51aS1jaGFydC1ib3JkZXJcIjtcblxuICAgIC8qKiBQcmVmaXggYXBwbGllZCB0byB0aGUgcmVuZGVyaW5nIGFyZWEgY2xpcCBwYXRoIGlkICovXG4gICAgcHVibGljIHN0YXRpYyBSRU5ERVJJTkdfQVJFQV9DTElQX1BBVEhfUFJFRklYID0gXCJjbGlwLXBhdGhfXCI7XG5cbiAgICAvKiogTmFtZSBmb3IgdGhlIGxhc2FnbmEgbGF5ZXIgY29udGFpbmluZyB0aGUgZ3JpZCdzIHJlbmRlcmVkIGVsZW1lbnRzICovXG4gICAgcHVibGljIHN0YXRpYyBHUklEX0VMRU1FTlRTX0xBWUVSX05BTUUgPSBcImdyaWQtZWxlbWVudHNcIjtcblxuICAgIC8qKiBOYW1lIGZvciB0aGUgcmVuZGVyaW5nIGFyZWEgbGFzYWduYSBsYXllciAqL1xuICAgIHB1YmxpYyBzdGF0aWMgUkVOREVSSU5HX0FSRUFfTEFZRVJfTkFNRSA9IFwicmVuZGVyaW5nLWFyZWFcIjtcblxuICAgIC8qKiBAaWdub3JlIEhlaWdodCBjb3JyZWN0aW9uIG5lZWRlZCB0byBwcmV2ZW50IGludGVyYWN0aW9uIGdhcCBiZXR3ZWVuIHZlcnRpY2FsbHkgc3RhY2tlZCBjaGFydHMgKi9cbiAgICBwdWJsaWMgc3RhdGljIFJFTkRFUl9BUkVBX0hFSUdIVF9DT1JSRUNUSU9OID0gMTtcblxuICAgIC8qKiBAaWdub3JlIFdpZHRoIGNvcnJlY3Rpb24gbmVlZGVkIHRvIHByZXZlbnQgaW50ZXJhY3Rpb24gZ2FwIGJldHdlZW4gcmlnaHQgc2lkZSBvZiBncmlkIGFuZCB0aGUgZWRnZSBvZiB0aGUgcmVuZGVyaW5nIGFyZWEgKi9cbiAgICBwdWJsaWMgc3RhdGljIFJFTkRFUl9BUkVBX1dJRFRIX0NPUlJFQ1RJT04gPSAxO1xuXG4gICAgLyoqIEBpZ25vcmUgV2lkdGggY29ycmVjdGlvbiBuZWVkZWQgdG8gc3luYyBib3R0b20gYm9yZGVyIGxlbmd0aCBhbmQgZ3JpZCB3aWR0aCB0byB0aWNrIHBsYWNlbWVudCAqL1xuICAgIHB1YmxpYyBzdGF0aWMgVElDS19ESU1FTlNJT05fQ09SUkVDVElPTiA9IDE7XG5cbiAgICAvKiogU3ViamVjdCBmb3IgaW5kaWNhdGluZyB0aGF0IHRoZSBjaGFydCdzIGRpbWVuc2lvbnMgc2hvdWxkIGJlIHVwZGF0ZWQgKi9cbiAgICBwdWJsaWMgdXBkYXRlQ2hhcnREaW1lbnNpb25zU3ViamVjdDogU3ViamVjdDx2b2lkPjtcblxuICAgIC8qKiBFdmVudCBidXMgcHJvdmlkZWQgYnkgdGhlIGNoYXJ0ICovXG4gICAgcHVibGljIGV2ZW50QnVzOiBFdmVudEJ1czxJQ2hhcnRFdmVudD47XG5cbiAgICAvKiogZDMgY29udGFpbmVyIGZvciB0aGUgZ3JpZCAqL1xuICAgIHByb3RlY3RlZCBjb250YWluZXI6IEQzU2VsZWN0aW9uPFNWR0dFbGVtZW50PjtcblxuICAgIC8qKiBkMyBzZWxlY3Rpb24gZm9yIHRoZSBncmlkJ3MgcmVuZGVyaW5nIGFyZWEgY2xpcCBwYXRoICovXG4gICAgcHJvdGVjdGVkIHJlbmRlcmluZ0FyZWFDbGlwUGF0aDogRDNTZWxlY3Rpb248U1ZHUmVjdEVsZW1lbnQ+O1xuXG4gICAgLyoqIGQzIHNlbGVjdGlvbiBmb3IgdGhlIGdyaWQncyByZW5kZXJpbmcgYXJlYSAqL1xuICAgIHByb3RlY3RlZCByZW5kZXJpbmdBcmVhOiBEM1NlbGVjdGlvbjxTVkdSZWN0RWxlbWVudD47XG5cbiAgICAvKiogZDMgc2VsZWN0aW9uIGZvciB0aGUgZ3JpZCdzIGludGVyYWN0aXZlIGFyZWEgKi9cbiAgICBwcm90ZWN0ZWQgaW50ZXJhY3RpdmVBcmVhOiBEM1NlbGVjdGlvbjxTVkdSZWN0RWxlbWVudD47XG5cbiAgICAvKiogVGhlIGdyaWQncyBsYXllciBtYW5hZ2VyICovXG4gICAgcHJvdGVjdGVkIGxhc2FnbmE6IExhc2FnbmE7XG5cbiAgICAvKiogTGFzYWduYSBsYXllciBmb3IgdGhlIGdyaWQncyByZW5kZXJlZCBlbGVtZW50cyAqL1xuICAgIHByb3RlY3RlZCBncmlkRWxlbWVudHNMYXllcjogU2VsZWN0aW9uPFNWR0VsZW1lbnQsIGFueSwgU1ZHRWxlbWVudCwgYW55PjtcblxuICAgIC8qKiBEZWZpbml0aW9uIG9mIHRoZSBncmlkJ3MgYm9yZGVycyBhcyByZW5kZXJlZCAqL1xuICAgIHByb3RlY3RlZCBib3JkZXJzOiBQYXJ0aWFsPElCb3JkZXJzPiA9IHt9O1xuXG4gICAgLyoqIFByb3BlcnR5IHZhbHVlIG9mIHRoZSBncmlkJ3Mgc2NhbGVzICovXG4gICAgcHJvdGVjdGVkIF9zY2FsZXM6IFNjYWxlc0luZGV4O1xuXG4gICAgLyoqIFByb3BlcnR5IHZhbHVlIG9mIHRoZSBncmlkJ3MgY29uZmlndXJhdGlvbiAqL1xuICAgIHByb3RlY3RlZCBfY29uZmlnOiBJR3JpZENvbmZpZztcblxuICAgIC8qKiBQcm9wZXJ0eSB2YWx1ZSBvZiB0aGUgZ3JpZCdzIHRhcmdldCBkMyBzZWxlY3Rpb24gKi9cbiAgICBwcm90ZWN0ZWQgX3RhcmdldDogRDNTZWxlY3Rpb248U1ZHU1ZHRWxlbWVudD47XG5cbiAgICAvKiogU2VlIHtAbGluayBJR3JpZCNnZXRJbnRlcmFjdGl2ZUFyZWF9ICovXG4gICAgcHVibGljIGdldEludGVyYWN0aXZlQXJlYSgpOiBEM1NlbGVjdGlvbjxTVkdSZWN0RWxlbWVudD4ge1xuICAgICAgICByZXR1cm4gdGhpcy5pbnRlcmFjdGl2ZUFyZWE7XG4gICAgfVxuXG4gICAgLyoqIFNlZSB7QGxpbmsgSUdyaWQjZ2V0TGFzYWduYX0gKi9cbiAgICBwdWJsaWMgZ2V0TGFzYWduYSgpOiBMYXNhZ25hIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubGFzYWduYTtcbiAgICB9XG5cbiAgICAvKiogQGlnbm9yZSAqL1xuICAgIHB1YmxpYyBzZXQgc2NhbGVzKHNjYWxlczogU2NhbGVzSW5kZXgpIHtcbiAgICAgICAgdGhpcy5fc2NhbGVzID0gc2NhbGVzO1xuICAgIH1cblxuICAgIC8qKiBAaWdub3JlICovXG4gICAgcHVibGljIGdldCBzY2FsZXMoKTogU2NhbGVzSW5kZXgge1xuICAgICAgICByZXR1cm4gdGhpcy5fc2NhbGVzO1xuICAgIH1cblxuICAgIC8qKiBTZWUge0BsaW5rIElHcmlkI2NvbmZpZ30gKi9cbiAgICBwdWJsaWMgY29uZmlnKCk6IElHcmlkQ29uZmlnO1xuICAgIC8qKiBTZWUge0BsaW5rIElHcmlkI2NvbmZpZ30gKi9cbiAgICBwdWJsaWMgY29uZmlnKGNvbmZpZzogSUdyaWRDb25maWcpOiB0aGlzO1xuICAgIC8qKiBTZWUge0BsaW5rIElHcmlkI2NvbmZpZ30gKi9cbiAgICBwdWJsaWMgY29uZmlnKGNvbmZpZz86IElHcmlkQ29uZmlnKTogSUdyaWRDb25maWcgfCB0aGlzIHtcbiAgICAgICAgaWYgKGNvbmZpZyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fY29uZmlnO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuX2NvbmZpZyA9IGNvbmZpZztcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqIFNlZSB7QGxpbmsgSUdyaWQjdGFyZ2V0fSAqL1xuICAgIHB1YmxpYyB0YXJnZXQoKTogRDNTZWxlY3Rpb248U1ZHU1ZHRWxlbWVudD47XG4gICAgLyoqIFNlZSB7QGxpbmsgSUdyaWQjdGFyZ2V0fSAqL1xuICAgIHB1YmxpYyB0YXJnZXQodGFyZ2V0OiBEM1NlbGVjdGlvbjxTVkdTVkdFbGVtZW50Pik6IElHcmlkO1xuICAgIC8qKiBTZWUge0BsaW5rIElHcmlkI3RhcmdldH0gKi9cbiAgICBwdWJsaWMgdGFyZ2V0KFxuICAgICAgICB0YXJnZXQ/OiBEM1NlbGVjdGlvbjxTVkdTVkdFbGVtZW50PlxuICAgICk6IEQzU2VsZWN0aW9uPFNWR1NWR0VsZW1lbnQ+IHwgSUdyaWQge1xuICAgICAgICBpZiAodGFyZ2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl90YXJnZXQ7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fdGFyZ2V0ID0gdGFyZ2V0O1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKiogU2VlIHtAbGluayBJR3JpZCNidWlsZH0gKi9cbiAgICBwdWJsaWMgYnVpbGQoKTogSUdyaWQge1xuICAgICAgICBpZiAoIXRoaXMuY29uZmlnKCkpIHtcbiAgICAgICAgICAgIGNvbnN0IGNvbmZpZyA9IG5ldyBHcmlkQ29uZmlnKCk7XG4gICAgICAgICAgICB0aGlzLmNvbmZpZyhjb25maWcpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5jb250YWluZXIgPSB0aGlzLl90YXJnZXQuYXBwZW5kKFwiZ1wiKS5hdHRycyh7XG4gICAgICAgICAgICBjbGFzczogR3JpZC5HUklEX0NMQVNTX05BTUUsXG4gICAgICAgIH0pO1xuXG4gICAgICAgIGNvbnN0IGNsaXBQYXRoSWQgPVxuICAgICAgICAgICAgR3JpZC5SRU5ERVJJTkdfQVJFQV9DTElQX1BBVEhfUFJFRklYICsgVXRpbGl0eVNlcnZpY2UudXVpZCgpO1xuICAgICAgICAvLyBBc3NlcnRpbmcgc2ltaWxhciB0eXBlIHRvIGF2b2lkIHJlZmFjdG9yaW5nIGFsbCB0aGUgZ3JpZHNcbiAgICAgICAgLy8gVE9ETzogUmVmYWN0b3IgbGFzYWduYSBzZXJ2aWNlIHRvIGFjY2VwdCBtdWx0aXBsZSBEM1NlbGVjdGlvbiB0eXBlc1xuICAgICAgICAvLyAgb3IgcmVmYWN0b3IgZ3JpZCBpbXBsZW1lbnRhdGlvbnMvaW50ZXJmYWNlcyB0byBtYWludGFpbiB0aGUgc2FtZSBzZWxlY3Rpb24gdHlwZVxuICAgICAgICB0aGlzLmxhc2FnbmEgPSBuZXcgTGFzYWduYShcbiAgICAgICAgICAgIDxEM1NlbGVjdGlvbjxTVkdTVkdFbGVtZW50Pj4oPHVua25vd24+dGhpcy5jb250YWluZXIpLFxuICAgICAgICAgICAgY2xpcFBhdGhJZFxuICAgICAgICApO1xuICAgICAgICB0aGlzLnJlbmRlcmluZ0FyZWEgPSB0aGlzLmJ1aWxkUmVuZGVyaW5nQXJlYShjbGlwUGF0aElkKTtcbiAgICAgICAgdGhpcy5hZGp1c3RSZW5kZXJpbmdBcmVhKCk7XG5cbiAgICAgICAgdGhpcy5ncmlkRWxlbWVudHNMYXllciA9IHRoaXMubGFzYWduYS5hZGRMYXllcih7XG4gICAgICAgICAgICBuYW1lOiBHcmlkLkdSSURfRUxFTUVOVFNfTEFZRVJfTkFNRSxcbiAgICAgICAgICAgIG9yZGVyOiAxMDAsXG4gICAgICAgICAgICBjbGlwcGVkOiBmYWxzZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgY29uc3QgYm9yZGVycyA9IHRoaXMuYnVpbGRCb3JkZXJzKHRoaXMuZ3JpZEVsZW1lbnRzTGF5ZXIpO1xuICAgICAgICBpZiAoYm9yZGVycykge1xuICAgICAgICAgICAgdGhpcy5ib3JkZXJzID0gYm9yZGVycztcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIERlcml2ZWQgY2xhc3NlcyBvdmVycmlkZSB0aGlzIG1ldGhvZCB0byBidWlsZCB0aGUgZ3JpZCdzIHBsdWdpbnNcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7SUNoYXJ0fSBjaGFydCBUaGUgY2hhcnQgaW5zdGFuY2UgdG8gcGFzcyB0byBlYWNoIHBsdWdpblxuICAgICAqXG4gICAgICogQHJldHVybnMge0lDaGFydFBsdWdpbltdfSBEZWZhdWx0IGltcGxlbWVudGF0aW9uIHJldHVybnMgYW4gZW1wdHkgYXJyYXlcbiAgICAgKi9cbiAgICBwdWJsaWMgYnVpbGRQbHVnaW5zKGNoYXJ0OiBJQ2hhcnQpOiBJQ2hhcnRQbHVnaW5bXSB7XG4gICAgICAgIHJldHVybiBbXTtcbiAgICB9XG5cbiAgICAvKiogU2VlIHtAbGluayBJR3JpZCN1cGRhdGV9ICovXG4gICAgcHVibGljIHVwZGF0ZSgpOiBJR3JpZCB7XG4gICAgICAgIGlmIChpc0VtcHR5KHRoaXMuc2NhbGVzKSkge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnVwZGF0ZUJvcmRlcnMoKTtcbiAgICAgICAgdGhpcy5hZGp1c3RSZW5kZXJpbmdBcmVhKCk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKiBTZWUge0BsaW5rIElHcmlkI3VwZGF0ZURpbWVuc2lvbnN9ICovXG4gICAgcHVibGljIHVwZGF0ZURpbWVuc2lvbnMoZGltZW5zaW9uczogUGFydGlhbDxJRGltZW5zaW9ucz4pOiBJR3JpZCB7XG4gICAgICAgIGNvbnN0IGRpbWVuc2lvbkNvbmZpZyA9IHRoaXMuY29uZmlnKCkuZGltZW5zaW9uO1xuXG4gICAgICAgIGlmIChkaW1lbnNpb25zLndpZHRoKSB7XG4gICAgICAgICAgICBkaW1lbnNpb25Db25maWcub3V0ZXJXaWR0aChcbiAgICAgICAgICAgICAgICBkaW1lbnNpb25zLndpZHRoIC0gdGhpcy5nZXRPdXRlcldpZHRoRGltZW5zaW9uQ29ycmVjdGlvbigpXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIGlmIChkaW1lbnNpb25zLmhlaWdodCkge1xuICAgICAgICAgICAgZGltZW5zaW9uQ29uZmlnLm91dGVySGVpZ2h0KGRpbWVuc2lvbnMuaGVpZ2h0KTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuYWRqdXN0UmVuZGVyaW5nQXJlYSgpO1xuICAgICAgICB0aGlzLnVwZGF0ZVJhbmdlcygpO1xuXG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cbiAgICAvKiogU2VlIHtAbGluayBJR3JpZCN1cGRhdGVSYW5nZXN9ICovXG4gICAgcHVibGljIHVwZGF0ZVJhbmdlcygpOiBJR3JpZCB7XG4gICAgICAgIHRoaXMudXBkYXRlKCk7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENhbGN1bGF0ZSB0aGUgd2lkdGggY29ycmVjdGlvbiBuZWVkZWQgZm9yIGFjY29tbW9kYXRpbmcgZ3JpZCBlbGVtZW50cyB0aGF0IG1heSBleHRlbmQgYmV5b25kIHRoZSBjaGFydCdzIGNvbmZpZ3VyZWQgd2lkdGhcbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgZ2V0T3V0ZXJXaWR0aERpbWVuc2lvbkNvcnJlY3Rpb24oKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuIEdyaWQuVElDS19ESU1FTlNJT05fQ09SUkVDVElPTjtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBCdWlsZHMgdGhlIGdyaWQgYm9yZGVycyBhcyBTVkdFbGVtZW50cyBiYXNlZCBvbiB0aGUgc3BlY2lmaWVkIGNvbmZpZ3VyYXRpb25cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RDNTZWxlY3Rpb259IGNvbnRhaW5lciBkMyBjb250YWluZXIgZm9yIHRoZSBib3JkZXJzXG4gICAgICpcbiAgICAgKiBAcmV0dXJucyB7UGFydGlhbDxJQm9yZGVycz59IFRoZSBncmlkJ3MgYm9yZGVyc1xuICAgICAqL1xuICAgIHByb3RlY3RlZCBidWlsZEJvcmRlcnMoXG4gICAgICAgIGNvbnRhaW5lcjogRDNTZWxlY3Rpb25cbiAgICApOiBQYXJ0aWFsPElCb3JkZXJzPiB8IHVuZGVmaW5lZCB7XG4gICAgICAgIGlmICghdGhpcy5jb25maWcoKSB8fCAhdGhpcy5jb25maWcoKS5ib3JkZXJzKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBib3JkZXJDb25maWdzID0gdGhpcy5jb25maWcoKS5ib3JkZXJzO1xuICAgICAgICBjb25zdCBib3JkZXJzOiBQYXJ0aWFsPElCb3JkZXJzPiA9IHt9O1xuICAgICAgICBjb25zdCBib3JkZXJLZXlzID0gPEJvcmRlcktleVtdPk9iamVjdC5rZXlzKGJvcmRlckNvbmZpZ3MpO1xuICAgICAgICBlYWNoKGJvcmRlcktleXMsIChzaWRlOiBCb3JkZXJLZXkpID0+IHtcbiAgICAgICAgICAgIC8vIFdlJ3JlIGNyZWF0aW5nIGV2ZW4gaW52aXNpYmxlIGJvcmRlcnMgYW5kIHVwZGF0aW5nIHZpc2liaWxpdHkgYWZ0ZXJ3YXJkc1xuICAgICAgICAgICAgaWYgKGJvcmRlckNvbmZpZ3Nbc2lkZV0pIHtcbiAgICAgICAgICAgICAgICBib3JkZXJzW3NpZGVdID1cbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jcmVhdGVCb3JkZXIoY29udGFpbmVyLCBib3JkZXJDb25maWdzW3NpZGVdKSA/P1xuICAgICAgICAgICAgICAgICAgICB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuXG4gICAgICAgIHJldHVybiBib3JkZXJzO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEFkanVzdHMgdGhlIGdyaWQncyByZW5kZXJpbmcgYXJlYSBhbmQgY2xpcCBwYXRoIGJhc2VkIG9uIHRoZSBncmlkJ3MgY29uZmlndXJlZCB3aWR0aCBhbmQgaGVpZ2h0XG4gICAgICovXG4gICAgcHJvdGVjdGVkIGFkanVzdFJlbmRlcmluZ0FyZWEgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGNvbnN0IGQgPSB0aGlzLmNvbmZpZygpLmRpbWVuc2lvbjtcbiAgICAgICAgY29uc3QgZGlzYWJsZUhlaWdodENvcnJlY3Rpb24gPVxuICAgICAgICAgICAgdGhpcy5jb25maWcoKS5kaXNhYmxlUmVuZGVyQXJlYUhlaWdodENvcnJlY3Rpb247XG4gICAgICAgIGNvbnN0IGRpc2FibGVXaWR0aENvcnJlY3Rpb24gPVxuICAgICAgICAgICAgdGhpcy5jb25maWcoKS5kaXNhYmxlUmVuZGVyQXJlYVdpZHRoQ29ycmVjdGlvbjtcblxuICAgICAgICBjb25zdCByZW5kZXJpbmdBcmVhQ2xpcFBhdGhBdHRycyA9IHtcbiAgICAgICAgICAgIHdpZHRoOiBNYXRoLm1heCgwLCBkLndpZHRoKCkpLFxuICAgICAgICAgICAgaGVpZ2h0OiBNYXRoLm1heChcbiAgICAgICAgICAgICAgICAwLFxuICAgICAgICAgICAgICAgIGQuaGVpZ2h0KCkgK1xuICAgICAgICAgICAgICAgICAgICAoZGlzYWJsZUhlaWdodENvcnJlY3Rpb25cbiAgICAgICAgICAgICAgICAgICAgICAgID8gMFxuICAgICAgICAgICAgICAgICAgICAgICAgOiBHcmlkLlJFTkRFUl9BUkVBX0hFSUdIVF9DT1JSRUNUSU9OKVxuICAgICAgICAgICAgKSxcbiAgICAgICAgfSBhcyBhbnk7XG5cbiAgICAgICAgaWYgKCFkaXNhYmxlSGVpZ2h0Q29ycmVjdGlvbikge1xuICAgICAgICAgICAgcmVuZGVyaW5nQXJlYUNsaXBQYXRoQXR0cnNbXCJ5XCJdID1cbiAgICAgICAgICAgICAgICAtR3JpZC5SRU5ERVJfQVJFQV9IRUlHSFRfQ09SUkVDVElPTjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJlbmRlcmluZ0FyZWFBdHRycyA9IHtcbiAgICAgICAgICAgIC4uLnJlbmRlcmluZ0FyZWFDbGlwUGF0aEF0dHJzLFxuICAgICAgICAgICAgLy8gV2lkdGggY29ycmVjdGlvbiBuZWVkZWQgdG8gcHJldmVudCBpbnRlcmFjdGlvbiBnYXAgYmV0d2VlbiByaWdodCBzaWRlIG9mIGdyaWQgYW5kIHRoZSBlZGdlIG9mIHRoZSByZW5kZXJpbmcgYXJlYVxuICAgICAgICAgICAgd2lkdGg6IE1hdGgubWF4KFxuICAgICAgICAgICAgICAgIDAsXG4gICAgICAgICAgICAgICAgZC53aWR0aCgpIC1cbiAgICAgICAgICAgICAgICAgICAgKGRpc2FibGVXaWR0aENvcnJlY3Rpb25cbiAgICAgICAgICAgICAgICAgICAgICAgID8gMFxuICAgICAgICAgICAgICAgICAgICAgICAgOiBHcmlkLlJFTkRFUl9BUkVBX1dJRFRIX0NPUlJFQ1RJT04pXG4gICAgICAgICAgICApLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMucmVuZGVyaW5nQXJlYUNsaXBQYXRoLmF0dHJzKHJlbmRlcmluZ0FyZWFDbGlwUGF0aEF0dHJzKTtcbiAgICAgICAgdGhpcy5yZW5kZXJpbmdBcmVhLmF0dHJzKHJlbmRlcmluZ0FyZWFBdHRycyk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEJ1aWxkcyB0aGUgZ3JpZCdzIHJlbmRlcmluZyBhcmVhIGFzIGEgbGF5ZXIgb24gdGhlIGxhc2FnbmFcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7c3RyaW5nfSBjbGlwUGF0aElkIFRoZSBjbGlwIHBhdGggaWRlbnRpZmllclxuICAgICAqXG4gICAgICogQHJldHVybnMge0QzU2VsZWN0aW9ufSBUaGUgZ3JpZCdzIHJlbmRlcmluZyBhcmVhXG4gICAgICovXG4gICAgcHJpdmF0ZSBidWlsZFJlbmRlcmluZ0FyZWEoXG4gICAgICAgIGNsaXBQYXRoSWQ6IHN0cmluZ1xuICAgICk6IEQzU2VsZWN0aW9uPFNWR1JlY3RFbGVtZW50PiB7XG4gICAgICAgIHRoaXMucmVuZGVyaW5nQXJlYUNsaXBQYXRoID0gdGhpcy5fdGFyZ2V0XG4gICAgICAgICAgICAuYXBwZW5kKFwiY2xpcFBhdGhcIilcbiAgICAgICAgICAgIC5hdHRyKFwiaWRcIiwgY2xpcFBhdGhJZClcbiAgICAgICAgICAgIC5hcHBlbmQoXCJyZWN0XCIpO1xuXG4gICAgICAgIGNvbnN0IHJlbmRlcmluZ0FyZWFDb250YWluZXIgPSB0aGlzLmxhc2FnbmEuYWRkTGF5ZXIoe1xuICAgICAgICAgICAgbmFtZTogR3JpZC5SRU5ERVJJTkdfQVJFQV9MQVlFUl9OQU1FLFxuICAgICAgICAgICAgb3JkZXI6IC0xLFxuICAgICAgICAgICAgY2xpcHBlZDogdHJ1ZSxcbiAgICAgICAgfSk7XG4gICAgICAgIHJldHVybiByZW5kZXJpbmdBcmVhQ29udGFpbmVyLmFwcGVuZChcInJlY3RcIikuYXR0cnMoe1xuICAgICAgICAgICAgXCJwb2ludGVyLWV2ZW50c1wiOiBcImFsbFwiLFxuICAgICAgICAgICAgZmlsbDogXCJ0cmFuc3BhcmVudFwiLFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDcmVhdGVzIGEgYm9yZGVyIHdpdGggdGhlIHNwZWNpZmllZCBjb25maWd1cmF0aW9uIGluIHRoZSBwcm92aWRlZCBjb250YWluZXJcbiAgICAgKlxuICAgICAqIEBwYXJhbSB7RDNTZWxlY3Rpb259IGNvbnRhaW5lciBUaGUgY29udGFpbmVyIHRvIGFwcGVuZCB0aGUgYm9yZGVyIHRvXG4gICAgICogQHBhcmFtIHtJQm9yZGVyQ29uZmlnfSBjb25maWcgVGhlIGNvbmZpZ3VyYXRpb24gdG8gYXBwbHkgdG8gdGhlIGJvcmRlclxuICAgICAqXG4gICAgICogQHJldHVybnMge1NWR0VsZW1lbnR9IFRoZSBjcmVhdGVkIGJvcmRlclxuICAgICAqL1xuICAgIHByaXZhdGUgY3JlYXRlQm9yZGVyKFxuICAgICAgICBjb250YWluZXI6IEQzU2VsZWN0aW9uLFxuICAgICAgICBjb25maWc6IElCb3JkZXJDb25maWdcbiAgICApOiBTVkdFbGVtZW50IHwgbnVsbCB7XG4gICAgICAgIGNvbnN0IGJvcmRlciA9IGNvbnRhaW5lclxuICAgICAgICAgICAgLmFwcGVuZChcImxpbmVcIilcbiAgICAgICAgICAgIC5hdHRyKFwiY2xhc3NcIiwgY29uZmlnLmNsYXNzTmFtZSB8fCBHcmlkLkRFRkFVTFRfQk9SREVSX0NMQVNTX05BTUUpO1xuXG4gICAgICAgIGlmIChjb25maWcud2lkdGgpIHtcbiAgICAgICAgICAgIC8vIHVzZSBzdHlsZSBpbnN0ZWFkIG9mIGF0dHIgdG8gb3ZlcnJpZGUgY3NzIHN0eWxlXG4gICAgICAgICAgICBib3JkZXIuc3R5bGUoXCJzdHJva2Utd2lkdGhcIiwgY29uZmlnLndpZHRoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoY29uZmlnLmNvbG9yKSB7XG4gICAgICAgICAgICAvLyB1c2Ugc3R5bGUgaW5zdGVhZCBvZiBhdHRyIHRvIG92ZXJyaWRlIGNzcyBzdHlsZVxuICAgICAgICAgICAgYm9yZGVyLnN0eWxlKFwic3Ryb2tlXCIsIGNvbmZpZy5jb2xvcik7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gYm9yZGVyLm5vZGUoKTtcbiAgICB9XG5cbiAgICAvLyBUT0RPOiBib3JkZXJzIGFyZSBldmlsLiByZWNvbnNpZGVyIVxuICAgIC8vIFdlJ3JlIHVzaW5nIGJvcmRlcnMgaW5zdGVhZCBvZiBheGlzIGxpbmUgYW5kIGJlY2F1c2Ugb2YgdGhhdCB3ZSBuZWVkIHRvIGRvIHRoZXNlIHdlaXJkIHNpemUgYWRqdXN0bWVudHNcbiAgICBwcm90ZWN0ZWQgdXBkYXRlQm90dG9tQm9yZGVyKCk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuYm9yZGVycy5ib3R0b20pIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIkJvdHRvbUJvcmRlciBpcyBub3QgZGVmaW5lZFwiKTtcbiAgICAgICAgfVxuICAgICAgICBzZWxlY3QodGhpcy5ib3JkZXJzLmJvdHRvbSlcbiAgICAgICAgICAgIC5hdHRycyh7XG4gICAgICAgICAgICAgICAgeDE6IDAsXG4gICAgICAgICAgICAgICAgeTE6IHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLmhlaWdodCgpIC0gYm9yZGVyTWlkcG9pbnQsXG4gICAgICAgICAgICAgICAgeDI6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLndpZHRoKCkgK1xuICAgICAgICAgICAgICAgICAgICBHcmlkLlRJQ0tfRElNRU5TSU9OX0NPUlJFQ1RJT04sIC8vIHRvIGdldCBuaWNlIGFsaWdubWVudCB3aXRoIHRpY2tzXG4gICAgICAgICAgICAgICAgeTI6IHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLmhlaWdodCgpIC0gYm9yZGVyTWlkcG9pbnQsXG4gICAgICAgICAgICAgICAgY2xhc3M6XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX2NvbmZpZy5ib3JkZXJzLmJvdHRvbS5jbGFzc05hbWUgfHxcbiAgICAgICAgICAgICAgICAgICAgR3JpZC5ERUZBVUxUX0JPUkRFUl9DTEFTU19OQU1FLFxuICAgICAgICAgICAgfSlcbiAgICAgICAgICAgIC5jbGFzc2VkKFwiaGlkZGVuXCIsICF0aGlzLl9jb25maWcuYm9yZGVycy5ib3R0b20/LnZpc2libGUpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFVwZGF0ZXMgdGhlIGQzIGxpbmUgcG9zaXRpb25pbmcgYW5kIHZpc2liaWxpdHkgYXR0cmlidXRlcyBvZiBlYWNoIG9mIHRoZSBjb25maWd1cmVkIGJvcmRlcnNcbiAgICAgKi9cbiAgICBwcm90ZWN0ZWQgdXBkYXRlQm9yZGVycygpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuYm9yZGVycy5ib3R0b20pIHtcbiAgICAgICAgICAgIHRoaXMudXBkYXRlQm90dG9tQm9yZGVyKCk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuYm9yZGVycy50b3ApIHtcbiAgICAgICAgICAgIHNlbGVjdCh0aGlzLmJvcmRlcnMudG9wKVxuICAgICAgICAgICAgICAgIC5hdHRycyh7XG4gICAgICAgICAgICAgICAgICAgIHgxOiAwLFxuICAgICAgICAgICAgICAgICAgICB5MTogYm9yZGVyTWlkcG9pbnQsIC8vIHRoZSBsaW5lIHdhcyBvdXRzaWRlIG9mIHRoZSB2aWV3cG9ydCBpbiBzb21lIGJyb3dzZXIgd2hlbiBzZXQgdG8gMFxuICAgICAgICAgICAgICAgICAgICB4MjpcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLndpZHRoKCkgK1xuICAgICAgICAgICAgICAgICAgICAgICAgR3JpZC5USUNLX0RJTUVOU0lPTl9DT1JSRUNUSU9OLCAvLyB0byBnZXQgbmljZSBhbGlnbm1lbnQgd2l0aCB0aWNrc1xuICAgICAgICAgICAgICAgICAgICB5MjogYm9yZGVyTWlkcG9pbnQsXG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAuY2xhc3NlZChcImhpZGRlblwiLCAhdGhpcy5fY29uZmlnLmJvcmRlcnMudG9wPy52aXNpYmxlKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5ib3JkZXJzLnJpZ2h0KSB7XG4gICAgICAgICAgICBzZWxlY3QodGhpcy5ib3JkZXJzLnJpZ2h0KVxuICAgICAgICAgICAgICAgIC5hdHRycyh7XG4gICAgICAgICAgICAgICAgICAgIHgxOiB0aGlzLmNvbmZpZygpLmRpbWVuc2lvbi53aWR0aCgpIC0gYm9yZGVyTWlkcG9pbnQsXG4gICAgICAgICAgICAgICAgICAgIHkxOiAwLFxuICAgICAgICAgICAgICAgICAgICB4MjogdGhpcy5jb25maWcoKS5kaW1lbnNpb24ud2lkdGgoKSAtIGJvcmRlck1pZHBvaW50LFxuICAgICAgICAgICAgICAgICAgICB5MjpcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLmhlaWdodCgpICtcbiAgICAgICAgICAgICAgICAgICAgICAgIEdyaWQuVElDS19ESU1FTlNJT05fQ09SUkVDVElPTiwgLy8gdG8gZ2V0IG5pY2UgYWxpZ25tZW50IHdpdGggdGlja3NcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5jbGFzc2VkKFwiaGlkZGVuXCIsICF0aGlzLl9jb25maWcuYm9yZGVycy5yaWdodD8udmlzaWJsZSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuYm9yZGVycy5sZWZ0KSB7XG4gICAgICAgICAgICBzZWxlY3QodGhpcy5ib3JkZXJzLmxlZnQpXG4gICAgICAgICAgICAgICAgLmF0dHJzKHtcbiAgICAgICAgICAgICAgICAgICAgeDE6IGJvcmRlck1pZHBvaW50LFxuICAgICAgICAgICAgICAgICAgICB5MTogMCxcbiAgICAgICAgICAgICAgICAgICAgeDI6IGJvcmRlck1pZHBvaW50LFxuICAgICAgICAgICAgICAgICAgICB5MjpcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY29uZmlnKCkuZGltZW5zaW9uLmhlaWdodCgpICtcbiAgICAgICAgICAgICAgICAgICAgICAgIEdyaWQuVElDS19ESU1FTlNJT05fQ09SUkVDVElPTiwgLy8gdG8gZ2V0IG5pY2UgYWxpZ25tZW50IHdpdGggdGlja3NcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgIC5jbGFzc2VkKFwiaGlkZGVuXCIsICF0aGlzLl9jb25maWcuYm9yZGVycy5sZWZ0Py52aXNpYmxlKTtcbiAgICAgICAgfVxuICAgIH1cbn1cbiJdfQ==