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

364 lines 52.8 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 { brushX } from "d3-brush"; import { event } from "d3-selection"; import debounce from "lodash/debounce"; import defaultsDeep from "lodash/defaultsDeep"; import find from "lodash/find"; import isEmpty from "lodash/isEmpty"; import isUndefined from "lodash/isUndefined"; import moment from "moment/moment"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { INTERACTION_COORDINATES_EVENT, INTERACTION_VALUES_EVENT, STANDARD_RENDER_LAYERS, } from "../../constants"; import { RenderLayerName } from "../../renderers/types"; import { ChartPlugin } from "../common/chart-plugin"; import { InteractionType } from "../common/types"; import { UtilityService } from "../public-api"; export class TimeseriesZoomPlugin extends ChartPlugin { config; syncService; static LAYER_NAME = "timeseries-zoom-brush"; static DEFAULT_CONFIG = { enableExternalEvents: true, collectionId: "", }; grid; brush; zoomBrushLayer; brushElement; destroy$ = new Subject(); interactionHandlerMap; xScale; brushStartXCoord; brushEndXCoord; brushStartXDate; brushEndXDate; isChartHoverd = false; isPopoverDisplayed = false; zoomLineLayer; openPopoverSubject = new Subject(); openPopover$ = this.openPopoverSubject .asObservable() .pipe(takeUntil(this.destroy$)); closePopoverSubject = new Subject(); closePopover$ = this.closePopoverSubject .asObservable() .pipe(takeUntil(this.destroy$)); zoomCreatedSubject = new Subject(); zoomCreated$ = this.zoomCreatedSubject .asObservable() .pipe(takeUntil(this.destroy$)); resizeHandler = debounce(() => { if (isUndefined(this.brushStartXDate) || isUndefined(this.brushEndXDate)) { return; } // makes sure that popover is closed while resizing this.moveBrushByDate(this.brushStartXDate, this.brushEndXDate); this.closePopover(); }, 10); constructor(config = {}, syncService) { super(); this.config = config; this.syncService = syncService; this.config = defaultsDeep(this.config, TimeseriesZoomPlugin.DEFAULT_CONFIG); // registers handlers this.interactionHandlerMap = { [InteractionType.MouseDown]: this.onBrushStart, [InteractionType.MouseMove]: this.onBrushMove, [InteractionType.MouseUp]: this.onBrushEnd, }; if (this.config.collectionId) { this.syncService?.registerPlugin(this.config.collectionId, this); } } initialize() { this.grid = this.chart.getGrid(); this.zoomBrushLayer = this.grid.getLasagna().addLayer({ name: TimeseriesZoomPlugin.LAYER_NAME, // add 1 to the foreground layer's order to ensure the brush is rendered in front of it order: STANDARD_RENDER_LAYERS[RenderLayerName.foreground].order + 1, clipped: true, }); this.zoomLineLayer = this.chart.getGrid().getLasagna().addLayer({ name: "zoom-interaction-line", order: 900, clipped: true, }); this.chart .getEventBus() .getStream(INTERACTION_COORDINATES_EVENT) .pipe(takeUntil(this.destroy$)) .subscribe((chartEvent) => { if (chartEvent.broadcast && !this.config.enableExternalEvents) { return; } const data = chartEvent.data; if (isEmpty(this.grid.scales) || isEmpty(data.coordinates)) { return; } if (this.interactionHandlerMap[data.interactionType]) { const xCoord = data.coordinates && data.coordinates.x; const yCoord = data.coordinates && data.coordinates.y; this.interactionHandlerMap[data.interactionType](xCoord, yCoord); } }); this.chart .getEventBus() .getStream(INTERACTION_VALUES_EVENT) .pipe(takeUntil(this.destroy$)) .subscribe((event) => { // detects hovering on different charts and closes popover if (event.data.values?.x) { const chartGrid = this.grid; const eventXAxisIds = Object.keys(event.data.values.x); const chartLeftXAxis = chartGrid.scales.x.list[0]; // shows popover only for a chart that is currently being hovered if (chartLeftXAxis.isTimeseriesScale && !eventXAxisIds.includes(chartLeftXAxis.id)) { this.isChartHoverd = false; this.closePopover(); } else { this.isChartHoverd = true; } } }); this.brush = brushX(); this.brushElement = this.zoomBrushLayer .append("g") .attr("class", "brush"); // engage pointer capture to confine mouse events to the interactive area // (in other words, if the 'mouseup' is physically triggered outside the interactive area, // the pointer capture allows us to still zoom based on that event) this.chart .getGrid() .getInteractiveArea() .on("pointerdown", () => event.target.setPointerCapture(event.pointerId)) .on("pointerup", () => event.target.releasePointerCapture(event.pointerId)); } updateDimensions() { const dimension = this.grid?.config()?.dimension; if (this.grid) { this.xScale = find(this.grid.scales["x"].index, { id: this.grid.bottomScaleId, }); } if (!dimension) { return; } // set the brush area's dimensions this.brush.extent([ [0, 0], [dimension.width(), dimension.height()], ]); // render the brush area after we have dimensions this.brush(this.zoomBrushLayer.select(".brush")); // prevent the brush from handling its own pointer events this.brushElement.select(".overlay").style("pointer-events", "none"); // remove stroke per mockups this.brushElement.select(".selection").attr("stroke", null); // makes sure that zoom brush gets correctly resized when chart's dimension changes this.resizeHandler(); } destroy() { if (this.config.collectionId) { this.syncService?.removePlugin(this.config.collectionId, this); } this.clearBrush(); this.grid.getLasagna().removeLayer(TimeseriesZoomPlugin.LAYER_NAME); this.destroy$.next(); this.destroy$.complete(); } showPopover() { if (!this.isChartHoverd || this.isPopoverDisplayed || isUndefined(this.brushStartXCoord) || isUndefined(this.brushEndXCoord)) { return; } this.isPopoverDisplayed = true; this.openPopoverSubject.next(this.brushEndXCoord); } closePopover() { if (!this.isPopoverDisplayed) { return; } this.isPopoverDisplayed = false; this.closePopoverSubject.next(); } moveBrushByDate(startDate, endDate) { const startXCoord = this.xScale.convert(startDate); const endXCoord = this.xScale.convert(endDate); // brush is already in the correct position if (this.brushStartXCoord === startXCoord && this.brushEndXCoord === endXCoord) { return; } // in case brush doesn't exist yet if (isUndefined(this.brushStartXCoord) && isUndefined(this.brushStartXCoord)) { this.createBrushWithoutDrag(startXCoord, endXCoord); return; } this.moveBrush(startDate, endDate, startXCoord, endXCoord); } moveBrushByCoord(startX, endX) { if (isUndefined(startX) || isUndefined(endX)) { return; } // brush is already in the correct position if (startX === this.brushStartXCoord && endX === this.brushEndXCoord) { return; } // in case brush doesn't exist yet if (isUndefined(this.brushStartXCoord) && isUndefined(this.brushEndXCoord)) { this.createBrushWithoutDrag(startX, endX); return; } const startDate = this.getDateFromCoord(startX); const endDate = this.getDateFromCoord(endX); this.moveBrush(startDate, endDate, startX, endX); } clearBrush() { this.brush.move(this.brushElement, null); this.zoomLineLayer .selectAll("." + TimeseriesZoomPlugin.LAYER_NAME) .remove(); this.brushEndXCoord = undefined; this.brushStartXCoord = undefined; this.brushStartXDate = undefined; this.brushEndXDate = undefined; this.closePopover(); } getInspectionFrame() { return { startDate: this.brushStartXDate, endDate: this.brushEndXDate, }; } getDateFromCoord(xCoord) { const xScaleValue = UtilityService.getScaleValues([this.xScale], xCoord); return moment(UtilityService.getInteractionValues(xScaleValue, this.xScale.id)); } addZoomBoundaryLine(xDate, xCoord) { const line = this.zoomLineLayer .selectAll(TimeseriesZoomPlugin.LAYER_NAME) .data([xDate]); const attrs = { class: TimeseriesZoomPlugin.LAYER_NAME, x1: xCoord, y1: 0, x2: xCoord, y2: this.chart.getGrid().config().dimension.height(), }; line.enter() .append("line") .merge(line) .attrs(attrs); line.exit().remove(); } onBrushStart = (xCoord) => { if (!isUndefined(this.brushStartXCoord)) { // brush already exist, clear first this.clearBrush(); } this.brushStartXCoord = xCoord; this.brushStartXDate = this.getDateFromCoord(xCoord); this.addZoomBoundaryLine(this.brushStartXDate, this.brushStartXCoord); }; // event that is triggered when hovering over chart onBrushMove = (xCoord) => { if (isUndefined(this.brushStartXCoord)) { return; } if (isUndefined(this.brushEndXCoord)) { const selection = [this.brushStartXCoord, xCoord].sort((a, b) => a - b); this.brush.move(this.brushElement, selection); } // if the zoom brush is displayed, shows and hides the popover when hovering over zoom area if (!isUndefined(this.brushStartXCoord) && !isUndefined(this.brushEndXCoord)) { if (xCoord >= this.brushStartXCoord && xCoord <= this.brushEndXCoord) { this.showPopover(); } else { this.closePopover(); } } }; onBrushEnd = (xCoord) => { if (isUndefined(this.brushStartXCoord)) { return; } const date = this.getDateFromCoord(xCoord); this.addZoomBoundaryLine(date, xCoord); if (this.brushStartXCoord > xCoord) { // updates position incase dragging from right to left this.brushEndXCoord = this.brushStartXCoord; this.brushStartXCoord = xCoord; this.brushEndXDate = this.brushStartXDate; this.brushStartXDate = date; } else { this.brushEndXCoord = xCoord; this.brushEndXDate = date; } if (this.isChartHoverd) { this.zoomCreatedSubject.next(); } }; // simulates creating brash with mouse dragging createBrushWithoutDrag(startX, endX) { this.brushStartXCoord = startX; this.brushEndXCoord = endX; this.brushStartXDate = this.getDateFromCoord(startX); this.brushEndXDate = this.getDateFromCoord(endX); this.brush.move(this.brushElement, [startX, endX]); this.addZoomBoundaryLine(this.brushStartXDate, this.brushStartXCoord); this.addZoomBoundaryLine(this.brushEndXDate, this.brushEndXCoord); } moveBrush(startDate, endDate, startXCoord, endXCoord) { const nodes = this.zoomLineLayer .selectAll("." + TimeseriesZoomPlugin.LAYER_NAME) .nodes(); if (nodes.length !== 2) { return; } // moves boundary lines nodes[0].setAttribute("x1", startXCoord.toString()); nodes[0].setAttribute("x2", startXCoord.toString()); nodes[1].setAttribute("x1", endXCoord.toString()); nodes[1].setAttribute("x2", endXCoord.toString()); // moves brush this.brush.move(this.brushElement, [ startXCoord, endXCoord, ]); this.brushStartXCoord = startXCoord; this.brushStartXDate = startDate; this.brushEndXCoord = endXCoord; this.brushEndXDate = endDate; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGltZXNlcmllcy16b29tLXBsdWdpbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb3JlL3BsdWdpbnMvdGltZXNlcmllcy16b29tLXBsdWdpbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSx5REFBeUQ7QUFDekQsRUFBRTtBQUNGLCtFQUErRTtBQUMvRSw0RUFBNEU7QUFDNUUsOEVBQThFO0FBQzlFLCtFQUErRTtBQUMvRSw4RUFBOEU7QUFDOUUsNERBQTREO0FBQzVELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsdURBQXVEO0FBQ3ZELEVBQUU7QUFDRiw2RUFBNkU7QUFDN0UsNEVBQTRFO0FBQzVFLCtFQUErRTtBQUMvRSwwRUFBMEU7QUFDMUUsaUZBQWlGO0FBQ2pGLDZFQUE2RTtBQUM3RSxpQkFBaUI7QUFFakIsT0FBTyxFQUFpQyxNQUFNLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDakUsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNyQyxPQUFPLFFBQVEsTUFBTSxpQkFBaUIsQ0FBQztBQUN2QyxPQUFPLFlBQVksTUFBTSxxQkFBcUIsQ0FBQztBQUMvQyxPQUFPLElBQUksTUFBTSxhQUFhLENBQUM7QUFDL0IsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFDckMsT0FBTyxXQUFXLE1BQU0sb0JBQW9CLENBQUM7QUFDN0MsT0FBTyxNQUFNLE1BQU0sZUFBZSxDQUFDO0FBQ25DLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDL0IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTNDLE9BQU8sRUFDSCw2QkFBNkIsRUFDN0Isd0JBQXdCLEVBQ3hCLHNCQUFzQixHQUN6QixNQUFNLGlCQUFpQixDQUFDO0FBQ3pCLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0s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c291cmNlc0NvbnRlbnQiOlsiLy8gwqkgMjAyMiBTb2xhcldpbmRzIFdvcmxkd2lkZSwgTExDLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbi8vICBvZiB0aGlzIHNvZnR3YXJlIGFuZCBhc3NvY2lhdGVkIGRvY3VtZW50YXRpb24gZmlsZXMgKHRoZSBcIlNvZnR3YXJlXCIpLCB0b1xuLy8gIGRlYWwgaW4gdGhlIFNvZnR3YXJlIHdpdGhvdXQgcmVzdHJpY3Rpb24sIGluY2x1ZGluZyB3aXRob3V0IGxpbWl0YXRpb24gdGhlXG4vLyAgcmlnaHRzIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yXG4vLyAgc2VsbCBjb3BpZXMgb2YgdGhlIFNvZnR3YXJlLCBhbmQgdG8gcGVybWl0IHBlcnNvbnMgdG8gd2hvbSB0aGUgU29mdHdhcmUgaXNcbi8vICBmdXJuaXNoZWQgdG8gZG8gc28sIHN1YmplY3QgdG8gdGhlIGZvbGxvd2luZyBjb25kaXRpb25zOlxuLy9cbi8vIFRoZSBhYm92ZSBjb3B5cmlnaHQgbm90aWNlIGFuZCB0aGlzIHBlcm1pc3Npb24gbm90aWNlIHNoYWxsIGJlIGluY2x1ZGVkIGluXG4vLyAgYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuLy8gIElNUExJRUQsIElOQ0xVRElORyBCVVQgTk9UIExJTUlURUQgVE8gVEhFIFdBUlJBTlRJRVMgT0YgTUVSQ0hBTlRBQklMSVRZLFxuLy8gIEZJVE5FU1MgRk9SIEEgUEFSVElDVUxBUiBQVVJQT1NFIEFORCBOT05JTkZSSU5HRU1FTlQuIElOIE5PIEVWRU5UIFNIQUxMIFRIRVxuLy8gIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbi8vICBMSUFCSUxJVFksIFdIRVRIRVIgSU4gQU4gQUNUSU9OIE9GIENPTlRSQUNULCBUT1JUIE9SIE9USEVSV0lTRSwgQVJJU0lORyBGUk9NLFxuLy8gIE9VVCBPRiBPUiBJTiBDT05ORUNUSU9OIFdJVEggVEhFIFNPRlRXQVJFIE9SIFRIRSBVU0UgT1IgT1RIRVIgREVBTElOR1MgSU5cbi8vICBUSEUgU09GVFdBUkUuXG5cbmltcG9ydCB7IEJydXNoQmVoYXZpb3IsIEJydXNoU2VsZWN0aW9uLCBicnVzaFggfSBmcm9tIFwiZDMtYnJ1c2hcIjtcbmltcG9ydCB7IGV2ZW50IH0gZnJvbSBcImQzLXNlbGVjdGlvblwiO1xuaW1wb3J0IGRlYm91bmNlIGZyb20gXCJsb2Rhc2gvZGVib3VuY2VcIjtcbmltcG9ydCBkZWZhdWx0c0RlZXAgZnJvbSBcImxvZGFzaC9kZWZhdWx0c0RlZXBcIjtcbmltcG9ydCBmaW5kIGZyb20gXCJsb2Rhc2gvZmluZFwiO1xuaW1wb3J0IGlzRW1wdHkgZnJvbSBcImxvZGFzaC9pc0VtcHR5XCI7XG5pbXBvcnQgaXNVbmRlZmluZWQgZnJvbSBcImxvZGFzaC9pc1VuZGVmaW5lZFwiO1xuaW1wb3J0IG1vbWVudCBmcm9tIFwibW9tZW50L21vbWVudFwiO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gXCJyeGpzXCI7XG5pbXBvcnQgeyB0YWtlVW50aWwgfSBmcm9tIFwicnhqcy9vcGVyYXRvcnNcIjtcblxuaW1wb3J0IHtcbiAgICBJTlRFUkFDVElPTl9DT09SRElOQVRFU19FVkVOVCxcbiAgICBJTlRFUkFDVElPTl9WQUxVRVNfRVZFTlQsXG4gICAgU1RBTkRBUkRfUkVOREVSX0xBWUVSUyxcbn0gZnJvbSBcIi4uLy4uL2NvbnN0YW50c1wiO1xuaW1wb3J0IHsgUmVuZGVyTGF5ZXJOYW1lIH0gZnJvbSBcIi4uLy4uL3JlbmRlcmVycy90eXBlc1wiO1xuaW1wb3J0IHsgQ2hhcnRQbHVnaW4gfSBmcm9tIFwiLi4vY29tbW9uL2NoYXJ0LXBsdWdpblwiO1xuaW1wb3J0IHsgSVNjYWxlIH0gZnJvbSBcIi4uL2NvbW1vbi9zY2FsZXMvdHlwZXNcIjtcbmltcG9ydCB7IEQzU2VsZWN0aW9uLCBJQ2hhcnRFdmVudCwgSW50ZXJhY3Rpb25UeXBlIH0gZnJvbSBcIi4uL2NvbW1vbi90eXBlc1wiO1xuaW1wb3J0IHsgWFlHcmlkIH0gZnJvbSBcIi4uL2dyaWQveHktZ3JpZFwiO1xuaW1wb3J0IHsgVXRpbGl0eVNlcnZpY2UgfSBmcm9tIFwiLi4vcHVibGljLWFwaVwiO1xuaW1wb3J0IHsgVGltZXNlcmllc1pvb21QbHVnaW5zU3luY1NlcnZpY2UgfSBmcm9tIFwiLi90aW1lc2VyaWVzLXpvb20tcGx1Z2luLXN5bmMuc2VydmljZVwiO1xuaW1wb3J0IHsgSUludGVyYWN0aW9uQ29vcmRpbmF0ZXNQYXlsb2FkIH0gZnJvbSBcIi4vdHlwZXNcIjtcblxuZXhwb3J0IGludGVyZmFjZSBJVGltZXNlcmllc1pvb21QbHVnaW5Db25maWcge1xuICAgIGNvbGxlY3Rpb25JZD86IHN0cmluZztcbiAgICBlbmFibGVFeHRlcm5hbEV2ZW50cz86IGJvb2xlYW47XG59XG5leHBvcnQgaW50ZXJmYWNlIElUaW1lc2VyaWVzWm9vbVBsdWdpbkluc3BlY3Rpb25GcmFtZSB7XG4gICAgc3RhcnREYXRlOiBtb21lbnQuTW9tZW50IHwgdW5kZWZpbmVkO1xuICAgIGVuZERhdGU6IG1vbWVudC5Nb21lbnQgfCB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBUaW1lc2VyaWVzWm9vbVBsdWdpbiBleHRlbmRzIENoYXJ0UGx1Z2luIHtcbiAgICBwdWJsaWMgc3RhdGljIExBWUVSX05BTUUgPSBcInRpbWVzZXJpZXMtem9vbS1icnVzaFwiO1xuICAgIHB1YmxpYyBzdGF0aWMgcmVhZG9ubHkgREVGQVVMVF9DT05GSUc6IElUaW1lc2VyaWVzWm9vbVBsdWdpbkNvbmZpZyA9IHtcbiAgICAgICAgZW5hYmxlRXh0ZXJuYWxFdmVudHM6IHRydWUsXG4gICAgICAgIGNvbGxlY3Rpb25JZDogXCJcIixcbiAgICB9O1xuICAgIHByaXZhdGUgZ3JpZDogWFlHcmlkO1xuICAgIHByaXZhdGUgYnJ1c2g6IEJydXNoQmVoYXZpb3I8YW55PjtcbiAgICBwcml2YXRlIHpvb21CcnVzaExheWVyOiBEM1NlbGVjdGlvbjtcbiAgICBwcml2YXRlIGJydXNoRWxlbWVudDogRDNTZWxlY3Rpb248U1ZHR0VsZW1lbnQ+O1xuICAgIHByaXZhdGUgZGVzdHJveSQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuICAgIHByaXZhdGUgaW50ZXJhY3Rpb25IYW5kbGVyTWFwOiBSZWNvcmQ8c3RyaW5nLCBGdW5jdGlvbj47XG4gICAgcHJpdmF0ZSB4U2NhbGU6IElTY2FsZTxhbnk+O1xuXG4gICAgcHJpdmF0ZSBicnVzaFN0YXJ0WENvb3JkPzogbnVtYmVyO1xuICAgIHByaXZhdGUgYnJ1c2hFbmRYQ29vcmQ/OiBudW1iZXI7XG4gICAgcHJpdmF0ZSBicnVzaFN0YXJ0WERhdGU/OiBtb21lbnQuTW9tZW50O1xuICAgIHByaXZhdGUgYnJ1c2hFbmRYRGF0ZT86IG1vbWVudC5Nb21lbnQ7XG5cbiAgICBwcml2YXRlIGlzQ2hhcnRIb3ZlcmQgPSBmYWxzZTtcbiAgICBwcml2YXRlIGlzUG9wb3ZlckRpc3BsYXllZCA9IGZhbHNlO1xuXG4gICAgcHJpdmF0ZSB6b29tTGluZUxheWVyOiBEM1NlbGVjdGlvbjxTVkdFbGVtZW50PjtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgb3BlblBvcG92ZXJTdWJqZWN0ID0gbmV3IFN1YmplY3Q8bnVtYmVyPigpO1xuICAgIHB1YmxpYyByZWFkb25seSBvcGVuUG9wb3ZlciQgPSB0aGlzLm9wZW5Qb3BvdmVyU3ViamVjdFxuICAgICAgICAuYXNPYnNlcnZhYmxlKClcbiAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgY2xvc2VQb3BvdmVyU3ViamVjdCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gICAgcHVibGljIHJlYWRvbmx5IGNsb3NlUG9wb3ZlciQgPSB0aGlzLmNsb3NlUG9wb3ZlclN1YmplY3RcbiAgICAgICAgLmFzT2JzZXJ2YWJsZSgpXG4gICAgICAgIC5waXBlKHRha2VVbnRpbCh0aGlzLmRlc3Ryb3kkKSk7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IHpvb21DcmVhdGVkU3ViamVjdCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gICAgcHVibGljIHJlYWRvbmx5IHpvb21DcmVhdGVkJCA9IHRoaXMuem9vbUNyZWF0ZWRTdWJqZWN0XG4gICAgICAgIC5hc09ic2VydmFibGUoKVxuICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpO1xuXG4gICAgcHJpdmF0ZSByZXNpemVIYW5kbGVyID0gZGVib3VuY2UoKCkgPT4ge1xuICAgICAgICBpZiAoXG4gICAgICAgICAgICBpc1VuZGVmaW5lZCh0aGlzLmJydXNoU3RhcnRYRGF0ZSkgfHxcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hFbmRYRGF0ZSlcbiAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgLy8gbWFrZXMgc3VyZSB0aGF0IHBvcG92ZXIgaXMgY2xvc2VkIHdoaWxlIHJlc2l6aW5nXG4gICAgICAgIHRoaXMubW92ZUJydXNoQnlEYXRlKHRoaXMuYnJ1c2hTdGFydFhEYXRlLCB0aGlzLmJydXNoRW5kWERhdGUpO1xuICAgICAgICB0aGlzLmNsb3NlUG9wb3ZlcigpO1xuICAgIH0sIDEwKTtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgY29uZmlnOiBJVGltZXNlcmllc1pvb21QbHVnaW5Db25maWcgPSB7fSxcbiAgICAgICAgcHJpdmF0ZSBzeW5jU2VydmljZT86IFRpbWVzZXJpZXNab29tUGx1Z2luc1N5bmNTZXJ2aWNlXG4gICAgKSB7XG4gICAgICAgIHN1cGVyKCk7XG4gICAgICAgIHRoaXMuY29uZmlnID0gZGVmYXVsdHNEZWVwKFxuICAgICAgICAgICAgdGhpcy5jb25maWcsXG4gICAgICAgICAgICBUaW1lc2VyaWVzWm9vbVBsdWdpbi5ERUZBVUxUX0NPTkZJR1xuICAgICAgICApO1xuICAgICAgICAvLyByZWdpc3RlcnMgaGFuZGxlcnNcbiAgICAgICAgdGhpcy5pbnRlcmFjdGlvbkhhbmRsZXJNYXAgPSB7XG4gICAgICAgICAgICBbSW50ZXJhY3Rpb25UeXBlLk1vdXNlRG93bl06IHRoaXMub25CcnVzaFN0YXJ0LFxuICAgICAgICAgICAgW0ludGVyYWN0aW9uVHlwZS5Nb3VzZU1vdmVdOiB0aGlzLm9uQnJ1c2hNb3ZlLFxuICAgICAgICAgICAgW0ludGVyYWN0aW9uVHlwZS5Nb3VzZVVwXTogdGhpcy5vbkJydXNoRW5kLFxuICAgICAgICB9O1xuXG4gICAgICAgIGlmICh0aGlzLmNvbmZpZy5jb2xsZWN0aW9uSWQpIHtcbiAgICAgICAgICAgIHRoaXMuc3luY1NlcnZpY2U/LnJlZ2lzdGVyUGx1Z2luKHRoaXMuY29uZmlnLmNvbGxlY3Rpb25JZCwgdGhpcyk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgaW5pdGlhbGl6ZSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5ncmlkID0gdGhpcy5jaGFydC5nZXRHcmlkKCkgYXMgWFlHcmlkO1xuICAgICAgICB0aGlzLnpvb21CcnVzaExheWVyID0gdGhpcy5ncmlkLmdldExhc2FnbmEoKS5hZGRMYXllcih7XG4gICAgICAgICAgICBuYW1lOiBUaW1lc2VyaWVzWm9vbVBsdWdpbi5MQVlFUl9OQU1FLFxuICAgICAgICAgICAgLy8gYWRkIDEgdG8gdGhlIGZvcmVncm91bmQgbGF5ZXIncyBvcmRlciB0byBlbnN1cmUgdGhlIGJydXNoIGlzIHJlbmRlcmVkIGluIGZyb250IG9mIGl0XG4gICAgICAgICAgICBvcmRlcjogU1RBTkRBUkRfUkVOREVSX0xBWUVSU1tSZW5kZXJMYXllck5hbWUuZm9yZWdyb3VuZF0ub3JkZXIgKyAxLFxuICAgICAgICAgICAgY2xpcHBlZDogdHJ1ZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy56b29tTGluZUxheWVyID0gdGhpcy5jaGFydC5nZXRHcmlkKCkuZ2V0TGFzYWduYSgpLmFkZExheWVyKHtcbiAgICAgICAgICAgIG5hbWU6IFwiem9vbS1pbnRlcmFjdGlvbi1saW5lXCIsXG4gICAgICAgICAgICBvcmRlcjogOTAwLFxuICAgICAgICAgICAgY2xpcHBlZDogdHJ1ZSxcbiAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5jaGFydFxuICAgICAgICAgICAgLmdldEV2ZW50QnVzKClcbiAgICAgICAgICAgIC5nZXRTdHJlYW0oSU5URVJBQ1RJT05fQ09PUkRJTkFURVNfRVZFTlQpXG4gICAgICAgICAgICAucGlwZSh0YWtlVW50aWwodGhpcy5kZXN0cm95JCkpXG4gICAgICAgICAgICAuc3Vic2NyaWJlKChjaGFydEV2ZW50OiBJQ2hhcnRFdmVudCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmIChjaGFydEV2ZW50LmJyb2FkY2FzdCAmJiAhdGhpcy5jb25maWcuZW5hYmxlRXh0ZXJuYWxFdmVudHMpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGNvbnN0IGRhdGE6IElJbnRlcmFjdGlvbkNvb3JkaW5hdGVzUGF5bG9hZCA9IGNoYXJ0RXZlbnQuZGF0YTtcbiAgICAgICAgICAgICAgICBpZiAoaXNFbXB0eSh0aGlzLmdyaWQuc2NhbGVzKSB8fCBpc0VtcHR5KGRhdGEuY29vcmRpbmF0ZXMpKSB7XG4gICAgICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICBpZiAodGhpcy5pbnRlcmFjdGlvbkhhbmRsZXJNYXBbZGF0YS5pbnRlcmFjdGlvblR5cGVdKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHhDb29yZCA9IGRhdGEuY29vcmRpbmF0ZXMgJiYgZGF0YS5jb29yZGluYXRlcy54O1xuICAgICAgICAgICAgICAgICAgICBjb25zdCB5Q29vcmQgPSBkYXRhLmNvb3JkaW5hdGVzICYmIGRhdGEuY29vcmRpbmF0ZXMueTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5pbnRlcmFjdGlvbkhhbmRsZXJNYXBbZGF0YS5pbnRlcmFjdGlvblR5cGVdKFxuICAgICAgICAgICAgICAgICAgICAgICAgeENvb3JkLFxuICAgICAgICAgICAgICAgICAgICAgICAgeUNvb3JkXG4gICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSk7XG5cbiAgICAgICAgdGhpcy5jaGFydFxuICAgICAgICAgICAgLmdldEV2ZW50QnVzKClcbiAgICAgICAgICAgIC5nZXRTdHJlYW0oSU5URVJBQ1RJT05fVkFMVUVTX0VWRU5UKVxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuZGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoZXZlbnQ6IElDaGFydEV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgLy8gZGV0ZWN0cyBob3ZlcmluZyBvbiBkaWZmZXJlbnQgY2hhcnRzIGFuZCBjbG9zZXMgcG9wb3ZlclxuICAgICAgICAgICAgICAgIGlmIChldmVudC5kYXRhLnZhbHVlcz8ueCkge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBjaGFydEdyaWQgPSB0aGlzLmdyaWQ7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGV2ZW50WEF4aXNJZHMgPSBPYmplY3Qua2V5cyhldmVudC5kYXRhLnZhbHVlcy54KTtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgY2hhcnRMZWZ0WEF4aXMgPSBjaGFydEdyaWQuc2NhbGVzLngubGlzdFswXTtcbiAgICAgICAgICAgICAgICAgICAgLy8gc2hvd3MgcG9wb3ZlciBvbmx5IGZvciBhIGNoYXJ0IHRoYXQgaXMgY3VycmVudGx5IGJlaW5nIGhvdmVyZWRcbiAgICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAgICAgY2hhcnRMZWZ0WEF4aXMuaXNUaW1lc2VyaWVzU2NhbGUgJiZcbiAgICAgICAgICAgICAgICAgICAgICAgICFldmVudFhBeGlzSWRzLmluY2x1ZGVzKGNoYXJ0TGVmdFhBeGlzLmlkKVxuICAgICAgICAgICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXNDaGFydEhvdmVyZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5jbG9zZVBvcG92ZXIoKTtcbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuaXNDaGFydEhvdmVyZCA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLmJydXNoID0gYnJ1c2hYKCk7XG4gICAgICAgIHRoaXMuYnJ1c2hFbGVtZW50ID0gdGhpcy56b29tQnJ1c2hMYXllclxuICAgICAgICAgICAgLmFwcGVuZChcImdcIilcbiAgICAgICAgICAgIC5hdHRyKFwiY2xhc3NcIiwgXCJicnVzaFwiKTtcblxuICAgICAgICAvLyBlbmdhZ2UgcG9pbnRlciBjYXB0dXJlIHRvIGNvbmZpbmUgbW91c2UgZXZlbnRzIHRvIHRoZSBpbnRlcmFjdGl2ZSBhcmVhXG4gICAgICAgIC8vIChpbiBvdGhlciB3b3JkcywgaWYgdGhlICdtb3VzZXVwJyBpcyBwaHlzaWNhbGx5IHRyaWdnZXJlZCBvdXRzaWRlIHRoZSBpbnRlcmFjdGl2ZSBhcmVhLFxuICAgICAgICAvLyB0aGUgcG9pbnRlciBjYXB0dXJlIGFsbG93cyB1cyB0byBzdGlsbCB6b29tIGJhc2VkIG9uIHRoYXQgZXZlbnQpXG4gICAgICAgIHRoaXMuY2hhcnRcbiAgICAgICAgICAgIC5nZXRHcmlkKClcbiAgICAgICAgICAgIC5nZXRJbnRlcmFjdGl2ZUFyZWEoKVxuICAgICAgICAgICAgLm9uKFwicG9pbnRlcmRvd25cIiwgKCkgPT5cbiAgICAgICAgICAgICAgICBldmVudC50YXJnZXQuc2V0UG9pbnRlckNhcHR1cmUoZXZlbnQucG9pbnRlcklkKVxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgLm9uKFwicG9pbnRlcnVwXCIsICgpID0+XG4gICAgICAgICAgICAgICAgZXZlbnQudGFyZ2V0LnJlbGVhc2VQb2ludGVyQ2FwdHVyZShldmVudC5wb2ludGVySWQpXG4gICAgICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyB1cGRhdGVEaW1lbnNpb25zKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBkaW1lbnNpb24gPSB0aGlzLmdyaWQ/LmNvbmZpZygpPy5kaW1lbnNpb247XG4gICAgICAgIGlmICh0aGlzLmdyaWQpIHtcbiAgICAgICAgICAgIHRoaXMueFNjYWxlID0gZmluZCh0aGlzLmdyaWQuc2NhbGVzW1wieFwiXS5pbmRleCwge1xuICAgICAgICAgICAgICAgIGlkOiB0aGlzLmdyaWQuYm90dG9tU2NhbGVJZCxcbiAgICAgICAgICAgIH0pIGFzIElTY2FsZTxhbnk+O1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCFkaW1lbnNpb24pIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHNldCB0aGUgYnJ1c2ggYXJlYSdzIGRpbWVuc2lvbnNcbiAgICAgICAgdGhpcy5icnVzaC5leHRlbnQoW1xuICAgICAgICAgICAgWzAsIDBdLFxuICAgICAgICAgICAgW2RpbWVuc2lvbi53aWR0aCgpLCBkaW1lbnNpb24uaGVpZ2h0KCldLFxuICAgICAgICBdKTtcblxuICAgICAgICAvLyByZW5kZXIgdGhlIGJydXNoIGFyZWEgYWZ0ZXIgd2UgaGF2ZSBkaW1lbnNpb25zXG4gICAgICAgIHRoaXMuYnJ1c2godGhpcy56b29tQnJ1c2hMYXllci5zZWxlY3QoXCIuYnJ1c2hcIikpO1xuXG4gICAgICAgIC8vIHByZXZlbnQgdGhlIGJydXNoIGZyb20gaGFuZGxpbmcgaXRzIG93biBwb2ludGVyIGV2ZW50c1xuICAgICAgICB0aGlzLmJydXNoRWxlbWVudC5zZWxlY3QoXCIub3ZlcmxheVwiKS5zdHlsZShcInBvaW50ZXItZXZlbnRzXCIsIFwibm9uZVwiKTtcblxuICAgICAgICAvLyByZW1vdmUgc3Ryb2tlIHBlciBtb2NrdXBzXG4gICAgICAgIHRoaXMuYnJ1c2hFbGVtZW50LnNlbGVjdChcIi5zZWxlY3Rpb25cIikuYXR0cihcInN0cm9rZVwiLCBudWxsKTtcblxuICAgICAgICAvLyBtYWtlcyBzdXJlIHRoYXQgem9vbSBicnVzaCBnZXRzIGNvcnJlY3RseSByZXNpemVkIHdoZW4gY2hhcnQncyBkaW1lbnNpb24gY2hhbmdlc1xuICAgICAgICB0aGlzLnJlc2l6ZUhhbmRsZXIoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgZGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmNvbGxlY3Rpb25JZCkge1xuICAgICAgICAgICAgdGhpcy5zeW5jU2VydmljZT8ucmVtb3ZlUGx1Z2luKHRoaXMuY29uZmlnLmNvbGxlY3Rpb25JZCwgdGhpcyk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNsZWFyQnJ1c2goKTtcblxuICAgICAgICB0aGlzLmdyaWQuZ2V0TGFzYWduYSgpLnJlbW92ZUxheWVyKFRpbWVzZXJpZXNab29tUGx1Z2luLkxBWUVSX05BTUUpO1xuICAgICAgICB0aGlzLmRlc3Ryb3kkLm5leHQoKTtcbiAgICAgICAgdGhpcy5kZXN0cm95JC5jb21wbGV0ZSgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBzaG93UG9wb3ZlcigpOiB2b2lkIHtcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgIXRoaXMuaXNDaGFydEhvdmVyZCB8fFxuICAgICAgICAgICAgdGhpcy5pc1BvcG92ZXJEaXNwbGF5ZWQgfHxcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkgfHxcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hFbmRYQ29vcmQpXG4gICAgICAgICkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaXNQb3BvdmVyRGlzcGxheWVkID0gdHJ1ZTtcbiAgICAgICAgdGhpcy5vcGVuUG9wb3ZlclN1YmplY3QubmV4dCh0aGlzLmJydXNoRW5kWENvb3JkKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY2xvc2VQb3BvdmVyKCk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuaXNQb3BvdmVyRGlzcGxheWVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5pc1BvcG92ZXJEaXNwbGF5ZWQgPSBmYWxzZTtcbiAgICAgICAgdGhpcy5jbG9zZVBvcG92ZXJTdWJqZWN0Lm5leHQoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbW92ZUJydXNoQnlEYXRlKFxuICAgICAgICBzdGFydERhdGU6IG1vbWVudC5Nb21lbnQsXG4gICAgICAgIGVuZERhdGU6IG1vbWVudC5Nb21lbnRcbiAgICApOiB2b2lkIHtcbiAgICAgICAgY29uc3Qgc3RhcnRYQ29vcmQgPSB0aGlzLnhTY2FsZS5jb252ZXJ0KHN0YXJ0RGF0ZSk7XG4gICAgICAgIGNvbnN0IGVuZFhDb29yZCA9IHRoaXMueFNjYWxlLmNvbnZlcnQoZW5kRGF0ZSk7XG5cbiAgICAgICAgLy8gYnJ1c2ggaXMgYWxyZWFkeSBpbiB0aGUgY29ycmVjdCBwb3NpdGlvblxuICAgICAgICBpZiAoXG4gICAgICAgICAgICB0aGlzLmJydXNoU3RhcnRYQ29vcmQgPT09IHN0YXJ0WENvb3JkICYmXG4gICAgICAgICAgICB0aGlzLmJydXNoRW5kWENvb3JkID09PSBlbmRYQ29vcmRcbiAgICAgICAgKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpbiBjYXNlIGJydXNoIGRvZXNuJ3QgZXhpc3QgeWV0XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkgJiZcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZClcbiAgICAgICAgKSB7XG4gICAgICAgICAgICB0aGlzLmNyZWF0ZUJydXNoV2l0aG91dERyYWcoc3RhcnRYQ29vcmQsIGVuZFhDb29yZCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLm1vdmVCcnVzaChzdGFydERhdGUsIGVuZERhdGUsIHN0YXJ0WENvb3JkLCBlbmRYQ29vcmQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBtb3ZlQnJ1c2hCeUNvb3JkKFxuICAgICAgICBzdGFydFg6IG51bWJlciB8IHVuZGVmaW5lZCxcbiAgICAgICAgZW5kWDogbnVtYmVyIHwgdW5kZWZpbmVkXG4gICAgKTogdm9pZCB7XG4gICAgICAgIGlmIChpc1VuZGVmaW5lZChzdGFydFgpIHx8IGlzVW5kZWZpbmVkKGVuZFgpKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBicnVzaCBpcyBhbHJlYWR5IGluIHRoZSBjb3JyZWN0IHBvc2l0aW9uXG4gICAgICAgIGlmIChzdGFydFggPT09IHRoaXMuYnJ1c2hTdGFydFhDb29yZCAmJiBlbmRYID09PSB0aGlzLmJydXNoRW5kWENvb3JkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpbiBjYXNlIGJydXNoIGRvZXNuJ3QgZXhpc3QgeWV0XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkgJiZcbiAgICAgICAgICAgIGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hFbmRYQ29vcmQpXG4gICAgICAgICkge1xuICAgICAgICAgICAgdGhpcy5jcmVhdGVCcnVzaFdpdGhvdXREcmFnKHN0YXJ0WCwgZW5kWCk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBzdGFydERhdGUgPSB0aGlzLmdldERhdGVGcm9tQ29vcmQoc3RhcnRYKTtcbiAgICAgICAgY29uc3QgZW5kRGF0ZSA9IHRoaXMuZ2V0RGF0ZUZyb21Db29yZChlbmRYKTtcblxuICAgICAgICB0aGlzLm1vdmVCcnVzaChzdGFydERhdGUsIGVuZERhdGUsIHN0YXJ0WCwgZW5kWCk7XG4gICAgfVxuXG4gICAgcHVibGljIGNsZWFyQnJ1c2goKTogdm9pZCB7XG4gICAgICAgIHRoaXMuYnJ1c2gubW92ZSh0aGlzLmJydXNoRWxlbWVudCwgbnVsbCk7XG4gICAgICAgIHRoaXMuem9vbUxpbmVMYXllclxuICAgICAgICAgICAgLnNlbGVjdEFsbChcIi5cIiArIFRpbWVzZXJpZXNab29tUGx1Z2luLkxBWUVSX05BTUUpXG4gICAgICAgICAgICAucmVtb3ZlKCk7XG5cbiAgICAgICAgdGhpcy5icnVzaEVuZFhDb29yZCA9IHVuZGVmaW5lZDtcbiAgICAgICAgdGhpcy5icnVzaFN0YXJ0WENvb3JkID0gdW5kZWZpbmVkO1xuICAgICAgICB0aGlzLmJydXNoU3RhcnRYRGF0ZSA9IHVuZGVmaW5lZDtcbiAgICAgICAgdGhpcy5icnVzaEVuZFhEYXRlID0gdW5kZWZpbmVkO1xuXG4gICAgICAgIHRoaXMuY2xvc2VQb3BvdmVyKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEluc3BlY3Rpb25GcmFtZSgpOiBJVGltZXNlcmllc1pvb21QbHVnaW5JbnNwZWN0aW9uRnJhbWUge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgc3RhcnREYXRlOiB0aGlzLmJydXNoU3RhcnRYRGF0ZSxcbiAgICAgICAgICAgIGVuZERhdGU6IHRoaXMuYnJ1c2hFbmRYRGF0ZSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGdldERhdGVGcm9tQ29vcmQoeENvb3JkOiBudW1iZXIpOiBtb21lbnQuTW9tZW50IHtcbiAgICAgICAgY29uc3QgeFNjYWxlVmFsdWUgPSBVdGlsaXR5U2VydmljZS5nZXRTY2FsZVZhbHVlcyhcbiAgICAgICAgICAgIFt0aGlzLnhTY2FsZV0sXG4gICAgICAgICAgICB4Q29vcmRcbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIG1vbWVudChcbiAgICAgICAgICAgIFV0aWxpdHlTZXJ2aWNlLmdldEludGVyYWN0aW9uVmFsdWVzKHhTY2FsZVZhbHVlLCB0aGlzLnhTY2FsZS5pZClcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFkZFpvb21Cb3VuZGFyeUxpbmUoeERhdGU6IG1vbWVudC5Nb21lbnQsIHhDb29yZDogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IGxpbmUgPSB0aGlzLnpvb21MaW5lTGF5ZXJcbiAgICAgICAgICAgIC5zZWxlY3RBbGwoVGltZXNlcmllc1pvb21QbHVnaW4uTEFZRVJfTkFNRSlcbiAgICAgICAgICAgIC5kYXRhKFt4RGF0ZV0pO1xuXG4gICAgICAgIGNvbnN0IGF0dHJzID0ge1xuICAgICAgICAgICAgY2xhc3M6IFRpbWVzZXJpZXNab29tUGx1Z2luLkxBWUVSX05BTUUsXG4gICAgICAgICAgICB4MTogeENvb3JkLFxuICAgICAgICAgICAgeTE6IDAsXG4gICAgICAgICAgICB4MjogeENvb3JkLFxuICAgICAgICAgICAgeTI6IHRoaXMuY2hhcnQuZ2V0R3JpZCgpLmNvbmZpZygpLmRpbWVuc2lvbi5oZWlnaHQoKSxcbiAgICAgICAgfTtcblxuICAgICAgICBsaW5lLmVudGVyKClcbiAgICAgICAgICAgIC5hcHBlbmQoXCJsaW5lXCIpXG4gICAgICAgICAgICAubWVyZ2UobGluZSBhcyBhbnkpXG4gICAgICAgICAgICAuYXR0cnMoYXR0cnMpO1xuXG4gICAgICAgIGxpbmUuZXhpdCgpLnJlbW92ZSgpO1xuICAgIH1cblxuICAgIHByaXZhdGUgb25CcnVzaFN0YXJ0ID0gKHhDb29yZDogbnVtYmVyKSA9PiB7XG4gICAgICAgIGlmICghaXNVbmRlZmluZWQodGhpcy5icnVzaFN0YXJ0WENvb3JkKSkge1xuICAgICAgICAgICAgLy8gYnJ1c2ggYWxyZWFkeSBleGlzdCwgY2xlYXIgZmlyc3RcbiAgICAgICAgICAgIHRoaXMuY2xlYXJCcnVzaCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5icnVzaFN0YXJ0WENvb3JkID0geENvb3JkO1xuICAgICAgICB0aGlzLmJydXNoU3RhcnRYRGF0ZSA9IHRoaXMuZ2V0RGF0ZUZyb21Db29yZCh4Q29vcmQpO1xuICAgICAgICB0aGlzLmFkZFpvb21Cb3VuZGFyeUxpbmUodGhpcy5icnVzaFN0YXJ0WERhdGUsIHRoaXMuYnJ1c2hTdGFydFhDb29yZCk7XG4gICAgfTtcblxuICAgIC8vIGV2ZW50IHRoYXQgaXMgdHJpZ2dlcmVkIHdoZW4gaG92ZXJpbmcgb3ZlciBjaGFydFxuICAgIHByaXZhdGUgb25CcnVzaE1vdmUgPSAoeENvb3JkOiBudW1iZXIpID0+IHtcbiAgICAgICAgaWYgKGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChpc1VuZGVmaW5lZCh0aGlzLmJydXNoRW5kWENvb3JkKSkge1xuICAgICAgICAgICAgY29uc3Qgc2VsZWN0aW9uID0gW3RoaXMuYnJ1c2hTdGFydFhDb29yZCwgeENvb3JkXS5zb3J0KFxuICAgICAgICAgICAgICAgIChhLCBiKSA9PiBhIC0gYlxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIHRoaXMuYnJ1c2gubW92ZSh0aGlzLmJydXNoRWxlbWVudCwgc2VsZWN0aW9uIGFzIEJydXNoU2VsZWN0aW9uKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGlmIHRoZSB6b29tIGJydXNoIGlzIGRpc3BsYXllZCwgc2hvd3MgYW5kIGhpZGVzIHRoZSBwb3BvdmVyIHdoZW4gaG92ZXJpbmcgb3ZlciB6b29tIGFyZWFcbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgIWlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkgJiZcbiAgICAgICAgICAgICFpc1VuZGVmaW5lZCh0aGlzLmJydXNoRW5kWENvb3JkKVxuICAgICAgICApIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICB4Q29vcmQgPj0gdGhpcy5icnVzaFN0YXJ0WENvb3JkICYmXG4gICAgICAgICAgICAgICAgeENvb3JkIDw9IHRoaXMuYnJ1c2hFbmRYQ29vcmRcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2hvd1BvcG92ZXIoKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5jbG9zZVBvcG92ZXIoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH07XG5cbiAgICBwcml2YXRlIG9uQnJ1c2hFbmQgPSAoeENvb3JkOiBudW1iZXIpID0+IHtcbiAgICAgICAgaWYgKGlzVW5kZWZpbmVkKHRoaXMuYnJ1c2hTdGFydFhDb29yZCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGRhdGUgPSB0aGlzLmdldERhdGVGcm9tQ29vcmQoeENvb3JkKTtcbiAgICAgICAgdGhpcy5hZGRab29tQm91bmRhcnlMaW5lKGRhdGUsIHhDb29yZCk7XG5cb