UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

323 lines 51.4 kB
import { sum } from 'd3'; export class SankeyChart { constructor() { /** Define the nodes in the chart */ this._nodes = []; /** Define the links in the chart */ this._links = []; /** Store the node-links */ this._nodeLinks = []; /** Define the minimum width of the nodes */ this._minWidth = 0; /** Define the maximum width of the nodes */ this._maxWidth = Infinity; /** Define the minimum distance from the edge of the chart */ this._padding = 24; } /** Define the spacing of the chart */ spacing(spacing) { this._spacing = spacing; return this; } /** Define the width of the chart */ width(width) { this._width = width; return this; } /** Define the height of the chart */ height(height) { this._height = height; return this; } /** Define the nodes */ nodes(nodes) { this._nodes = nodes; return this; } /** Define the links */ links(links) { this._links = links; return this; } /** Define the minimum and maximum size of the nodes */ size(minWidth, maxWidth, minHeight) { this._minWidth = minWidth; this._maxWidth = maxWidth; this._minHeight = minHeight; return this; } /** Get the sizes of each column */ columns() { // get the number of columns - we use this a lot so avoid multiple function calls const columnCount = this.getColumnCount(); // get the amount of padding there should be on each side of a node const padding = this.getColumnPadding(); const columnWidths = []; for (let idx = 0; idx < columnCount; idx++) { columnWidths[idx] = this.getNodeWidth() + (padding * 2); // do no have the default padding on the left of the start node // or right of the last node, instead have a default padding if (idx === 0 || idx === columnCount - 1) { columnWidths[idx] -= (padding - this._padding); } } return columnWidths; } /** * Perform the various stages of the layout * in the correct order as some steps are dependant * on the previous layout stages. */ layout() { this.getNodeLinks(); this.getNodeValues(); this.getNodeColumns(); this.getNodeWidths(); this.getNodeHeights(); this.getNodePositions(); this.getLinkPlots(); return this._nodeLinks; } /** The curve equation for links */ link(link) { // const dist = chart.blockSpacing / 2; const { topLeft, topRight, bottomLeft, bottomRight } = link; const dist = (topRight[0] - topLeft[0]) / 2; const topLeftCurve = [topLeft[0] + dist, topLeft[1]]; const topRightCurve = [topRight[0] - dist, topRight[1]]; const bottomLeftCurve = [bottomLeft[0] + dist, bottomLeft[1]]; const bottomRightCurve = [bottomRight[0] - dist, bottomRight[1]]; return 'M' + topLeft[0] + ',' + topLeft[1] + 'C' + topLeftCurve[0] + ',' + topLeftCurve[1] + ' ' + topRightCurve[0] + ',' + topRightCurve[1] + ' ' + topRight[0] + ',' + topRight[1] + 'L' + bottomRight[0] + ',' + bottomRight[1] + 'C' + bottomRightCurve[0] + ',' + bottomRightCurve[1] + ' ' + bottomLeftCurve[0] + ',' + bottomLeftCurve[1] + ' ' + bottomLeft[0] + ',' + bottomLeft[1] + 'L' + topLeft[0] + ',' + topLeft[1]; } getFalloffPath(nodeLink) { const x = (nodeLink.x + nodeLink.width); const y = nodeLink.outputs.reduce((bottom, output) => Math.max(bottom, output.bottomLeft[1]), 0); const width = 20; const radius = 6; const height = (nodeLink.y + nodeLink.height - y) + (this._spacing / 2); return 'M' + x + ',' + y + 'h ' + (width - radius) + 'a' + radius + ',' + radius + ' 0 0,1' + (radius) + ',' + (radius) + ' ' + 'v' + Math.max(radius, height) + 'h-' + width + 'Z'; } /** * Get a `SankeyNodeLink` object from the id of a node */ getNodeLink(id) { return this._nodeLinks.find(nodeLink => nodeLink.node.id === id); } /** Replace the node ids with actual references */ getNodeLinks() { this._nodeLinks = this._nodes.map(node => { // get all the links that input into and output from this node const inputs = this._links.filter(link => link.target === node.id); const outputs = this._links.filter(link => link.source === node.id); return { node, inputs, outputs, value: 0, column: 0, x: 0, y: 0, width: 0, height: 0, naturalHeight: 0, falloff: 0, active: false, focus: false }; }); } /** Get the value for the node based on all its inputs and outputs */ getNodeValues() { for (const node of this._nodeLinks) { // the node value can be determined by the total values from all inputs // however the first column of nodes have no inputs so must be based of their outputs. // We should take the maximum value based on the inputs and outputs as nodes that are // not in the first column may not output all of the amount the receive from inputs, // for example in the case of falloff etc.. node.value = Math.max(sum(node.inputs, input => input.value), sum(node.outputs, output => output.value)); } } /** * We need to determine which column the node should * be placed in. This is determined by taking the input * and adding one. */ getNodeColumns(nodeLinks = this._nodeLinks.filter(node => node.inputs.length === 0), column = 0) { for (const nodeLink of nodeLinks) { nodeLink.column = column; // call this function to all output links this.getNodeColumns(nodeLink.outputs.map(output => this.getNodeLink(output.target)), column + 1); } } /** Get the width of each node */ getNodeWidths() { this._nodeLinks.forEach(node => node.width = this.getNodeWidth()); } /** * Scale the nodes height based on the value the represent */ getNodeHeights() { // get columns by group const groups = this.getColumnGroups(); const groupList = Object.keys(groups).map(group => groups[group]); // get the column with the largest total value const total = groupList.reduce((count, nodes) => Math.max(count, nodes.reduce((accumulation, node) => accumulation + node.value, 0)), 0); // Calculate node heights for (const nodeLinks of groupList) { // get the proportional size of each node based on the available space for (const nodeLink of nodeLinks) { nodeLink.naturalHeight = ((nodeLink.value / total) * this._height) - this._spacing; nodeLink.height = Math.max(nodeLink.naturalHeight, this._minHeight); } } // If minHeight is defined, it might cause some columns to exceed the height of the chart following the // initial height calculation. if (this._minHeight > 0) { try { // Recalculate node heights until they fit (if possible) this.adjustNodeHeightsToFit(groupList); } catch (error) { // If the above recalculation fails, give up and use the naturalHeight (ignore minHeight) this.setNodesToNaturalHeight(groupList); } } } /** * Recalculate node heights within height limits until they fit (if possible). * @throws If it is not possible to fit all nodes in the chart due to `minHeight`. */ adjustNodeHeightsToFit(groupList) { let largestColumn = this.getLargestColumn(groupList); while (largestColumn.height > this._height) { // Get the list of nodes whose height cannot be reduced const fixedNodes = largestColumn.nodes.filter(nodeLink => nodeLink.height <= this._minHeight); // Get the total height in the column which cannot shrink (including spacing) const fixedHeight = fixedNodes.length * this._minHeight + largestColumn.nodes.length * this._spacing; // If the unshrinkable height is greater than the available height, we can't continue if (fixedHeight > this._height) { throw new Error(`Cannot fit data into chart with minHeight = ${this._minHeight}px (need ${fixedHeight}px; ${this._height}px available)`); } // Find the amount of height which can potentially be reduced const flexibleHeight = largestColumn.height - fixedHeight; // Find the amount of height that the above needs to fit into const availableHeight = this._height - fixedHeight; // Get the multiplier to reduce the nodes in order to fit the available height const ratio = availableHeight / flexibleHeight; // Adjust the nodes and reapply the minHeight for (const group of groupList) { for (const nodeLink of group) { if (nodeLink.height > this._minHeight) { nodeLink.height *= ratio; } if (nodeLink.height < this._minHeight) { nodeLink.height = this._minHeight; } } } largestColumn = this.getLargestColumn(groupList); } } /** Set all nodes height to be the same as the naturalHeight. */ setNodesToNaturalHeight(groupList) { for (const group of groupList) { for (const nodeLink of group) { nodeLink.height = nodeLink.naturalHeight; } } } /** * Get all nodes grouped in their corresponding columns */ getColumnGroups() { // group nodes by columns return this._nodeLinks.reduce((collection, nodeLink) => { collection[nodeLink.column] = collection[nodeLink.column] || []; collection[nodeLink.column].push(nodeLink); return collection; }, {}); } /** * Get the number of columns */ getColumnCount() { return this._nodeLinks.reduce((column, nodeLink) => Math.max(nodeLink.column + 1, column), 0); } /** * Position the nodes in their corresponding x and y positions */ getNodePositions() { // get all nodes by group const groups = this.getColumnGroups(); // get the amount of padding required between each item const padding = this.getColumnPadding(); for (const nodeLink of this._nodeLinks) { // get the x position based on the column nodeLink.x = this.getColumnPosition(nodeLink.column) + padding; if (nodeLink.column === 0) { nodeLink.x = this._padding; } // get the y position based on the accumulative height of the nodes above it nodeLink.y = groups[nodeLink.column] .slice(0, groups[nodeLink.column].indexOf(nodeLink)) .reduce((top, _node) => top + _node.height, 0) + (this._spacing * groups[nodeLink.column].indexOf(nodeLink)); } } getColumnPadding() { // get the number of columns - we use this a lot so avoid multiple function calls const columnCount = this.getColumnCount(); // get the chart width minus the width of the nodes const width = (this._width - (columnCount * this.getNodeWidth())) - (this._padding * 2); // get the total amount of places requiring padding (the first and last columns only have padding on one side) const paddingCount = Math.max(((columnCount * 2) - 2), 0); // get the actual size of the padding return width / paddingCount; } getLinkPlots() { for (const nodeLink of this._nodeLinks) { let inputY = nodeLink.y; // process each input link for (const link of nodeLink.inputs) { link.topRight = [nodeLink.x, inputY]; inputY += ((link.value / nodeLink.value) * nodeLink.height); link.bottomRight = [nodeLink.x, inputY]; } let outputValue = 0; let outputY = nodeLink.y; // process each output link for (const link of nodeLink.outputs) { link.topLeft = [nodeLink.x + nodeLink.width, outputY]; outputY += ((link.value / nodeLink.value) * nodeLink.height); link.bottomLeft = [nodeLink.x + nodeLink.width, outputY]; outputValue += link.value; } // determine how much falloff there is nodeLink.falloff = nodeLink.value - outputValue; } } /** Determine the position at which a column starts */ getColumnPosition(column) { // the position is the acculation of the widths of all previous columns return this.columns().splice(0, column).reduce((total, width) => total + width, 0); } /** Get the pixel width of a node */ getNodeWidth() { const width = (this._width - (this._padding * 2)) / ((this.getColumnCount() * 2) - 1); return Math.min(this._maxWidth, Math.max(this._minWidth, width)); } /** Get the column with the greatest height (along with its height) */ getLargestColumn(groupList) { let largestColumn = null; let largestColumnHeight = 0; for (const group of groupList) { const totalHeight = group.reduce((acc, node) => acc += node.height, 0) + group.length * this._spacing; if (totalHeight > largestColumnHeight) { largestColumnHeight = totalHeight; largestColumn = group; } } return { nodes: largestColumn, height: largestColumnHeight }; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2Fua2V5LWNoYXJ0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvc2Fua2V5LWNoYXJ0L3NhbmtleS1jaGFydC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sSUFBSSxDQUFDO0FBS3pCLE1BQU0sT0FBTyxXQUFXO0lBQXhCO1FBRUksb0NBQW9DO1FBQzVCLFdBQU0sR0FBaUMsRUFBRSxDQUFDO1FBRWxELG9DQUFvQztRQUM1QixXQUFNLEdBQThCLEVBQUUsQ0FBQztRQUUvQywyQkFBMkI7UUFDbkIsZUFBVSxHQUFxQyxFQUFFLENBQUM7UUFFMUQsNENBQTRDO1FBQ3BDLGNBQVMsR0FBVyxDQUFDLENBQUM7UUFFOUIsNENBQTRDO1FBQ3BDLGNBQVMsR0FBVyxRQUFRLENBQUM7UUFjckMsNkRBQTZEO1FBQzVDLGFBQVEsR0FBVyxFQUFFLENBQUM7SUErWDNDLENBQUM7SUE3WEcsc0NBQXNDO0lBQ3RDLE9BQU8sQ0FBQyxPQUFlO1FBQ25CLElBQUksQ0FBQyxRQUFRLEdBQUcsT0FBTyxDQUFDO1FBQ3hCLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFFRCxvQ0FBb0M7SUFDcEMsS0FBSyxDQUFDLEtBQWE7UUFDZixJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztRQUNwQixPQUFPLElBQUksQ0FBQztJQUNoQixDQUFDO0lBRUQscUNBQXFDO0lBQ3JDLE1BQU0sQ0FBQyxNQUFjO1FBQ2pCLElBQUksQ0FBQyxPQUFPLEdBQUcsTUFBTSxDQUFDO1FBQ3RCLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFFRCx1QkFBdUI7SUFDdkIsS0FBSyxDQUFDLEtBQW1DO1FBQ3JDLElBQUksQ0FBQyxNQUFNLEdBQUcsS0FBSyxDQUFDO1FBQ3BCLE9BQU8sSUFBSSxDQUFDO0lBQ2hCLENBQUM7SUFFRCx1QkFBdUI7SUFDdkIsS0FBSyxDQUFDLEtB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cG9ydCB7IHN1bSB9IGZyb20gJ2QzJztcbmltcG9ydCB7IFNhbmtleUxpbmssIFNhbmtleUxpbmtQbG90IH0gZnJvbSAnLi9pbnRlcmZhY2VzL2xpbmsuaW50ZXJmYWNlJztcbmltcG9ydCB7IFNhbmtleU5vZGVMaW5rIH0gZnJvbSAnLi9pbnRlcmZhY2VzL25vZGUtbGluay5pbnRlcmZhY2UnO1xuaW1wb3J0IHsgU2Fua2V5Tm9kZSB9IGZyb20gJy4vaW50ZXJmYWNlcy9ub2RlLmludGVyZmFjZSc7XG5cbmV4cG9ydCBjbGFzcyBTYW5rZXlDaGFydDxUPiB7XG5cbiAgICAvKiogRGVmaW5lIHRoZSBub2RlcyBpbiB0aGUgY2hhcnQgKi9cbiAgICBwcml2YXRlIF9ub2RlczogUmVhZG9ubHlBcnJheTxTYW5rZXlOb2RlPFQ+PiA9IFtdO1xuXG4gICAgLyoqIERlZmluZSB0aGUgbGlua3MgaW4gdGhlIGNoYXJ0ICovXG4gICAgcHJpdmF0ZSBfbGlua3M6IFJlYWRvbmx5QXJyYXk8U2Fua2V5TGluaz4gPSBbXTtcblxuICAgIC8qKiBTdG9yZSB0aGUgbm9kZS1saW5rcyAqL1xuICAgIHByaXZhdGUgX25vZGVMaW5rczogUmVhZG9ubHlBcnJheTxTYW5rZXlOb2RlTGluazxUPj4gPSBbXTtcblxuICAgIC8qKiBEZWZpbmUgdGhlIG1pbmltdW0gd2lkdGggb2YgdGhlIG5vZGVzICovXG4gICAgcHJpdmF0ZSBfbWluV2lkdGg6IG51bWJlciA9IDA7XG5cbiAgICAvKiogRGVmaW5lIHRoZSBtYXhpbXVtIHdpZHRoIG9mIHRoZSBub2RlcyAqL1xuICAgIHByaXZhdGUgX21heFdpZHRoOiBudW1iZXIgPSBJbmZpbml0eTtcblxuICAgIC8qKiBUaGUgbWluaW11bSBoZWlnaHQgb2YgYSBub2RlLiAqL1xuICAgIHByaXZhdGUgX21pbkhlaWdodDogbnVtYmVyO1xuXG4gICAgLyoqIERlZmluZSB0aGUgd2lkdGggb2YgdGhlIGNoYXJ0ICovXG4gICAgcHJpdmF0ZSBfd2lkdGg6IG51bWJlcjtcblxuICAgIC8qKiBEZWZpbmUgdGhlIGhlaWdodCBvZiB0aGUgY2hhcnQgKi9cbiAgICBwcml2YXRlIF9oZWlnaHQ6IG51bWJlcjtcblxuICAgIC8qKiBEZWZpbmUgdGhlIHZlcnRpY2FsIHBhZGRpbmcgYmV0d2VlbiBub2RlcyAqL1xuICAgIHByaXZhdGUgX3NwYWNpbmc6IG51bWJlcjtcblxuICAgIC8qKiBEZWZpbmUgdGhlIG1pbmltdW0gZGlzdGFuY2UgZnJvbSB0aGUgZWRnZSBvZiB0aGUgY2hhcnQgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9wYWRkaW5nOiBudW1iZXIgPSAyNDtcblxuICAgIC8qKiBEZWZpbmUgdGhlIHNwYWNpbmcgb2YgdGhlIGNoYXJ0ICovXG4gICAgc3BhY2luZyhzcGFjaW5nOiBudW1iZXIpOiB0aGlzIHtcbiAgICAgICAgdGhpcy5fc3BhY2luZyA9IHNwYWNpbmc7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKiBEZWZpbmUgdGhlIHdpZHRoIG9mIHRoZSBjaGFydCAqL1xuICAgIHdpZHRoKHdpZHRoOiBudW1iZXIpOiB0aGlzIHtcbiAgICAgICAgdGhpcy5fd2lkdGggPSB3aWR0aDtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqIERlZmluZSB0aGUgaGVpZ2h0IG9mIHRoZSBjaGFydCAqL1xuICAgIGhlaWdodChoZWlnaHQ6IG51bWJlcik6IHRoaXMge1xuICAgICAgICB0aGlzLl9oZWlnaHQgPSBoZWlnaHQ7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKiBEZWZpbmUgdGhlIG5vZGVzICovXG4gICAgbm9kZXMobm9kZXM6IFJlYWRvbmx5QXJyYXk8U2Fua2V5Tm9kZTxUPj4pOiB0aGlzIHtcbiAgICAgICAgdGhpcy5fbm9kZXMgPSBub2RlcztcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLyoqIERlZmluZSB0aGUgbGlua3MgKi9cbiAgICBsaW5rcyhsaW5rczogUmVhZG9ubHlBcnJheTxTYW5rZXlMaW5rPik6IHRoaXMge1xuICAgICAgICB0aGlzLl9saW5rcyA9IGxpbmtzO1xuICAgICAgICByZXR1cm4gdGhpcztcbiAgICB9XG5cbiAgICAvKiogRGVmaW5lIHRoZSBtaW5pbXVtIGFuZCBtYXhpbXVtIHNpemUgb2YgdGhlIG5vZGVzICovXG4gICAgc2l6ZShtaW5XaWR0aDogbnVtYmVyLCBtYXhXaWR0aDogbnVtYmVyLCBtaW5IZWlnaHQ6IG51bWJlcik6IHRoaXMge1xuICAgICAgICB0aGlzLl9taW5XaWR0aCA9IG1pbldpZHRoO1xuICAgICAgICB0aGlzLl9tYXhXaWR0aCA9IG1heFdpZHRoO1xuICAgICAgICB0aGlzLl9taW5IZWlnaHQgPSBtaW5IZWlnaHQ7XG4gICAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8qKiBHZXQgdGhlIHNpemVzIG9mIGVhY2ggY29sdW1uICovXG4gICAgY29sdW1ucygpOiBudW1iZXJbXSB7XG5cbiAgICAgICAgLy8gZ2V0IHRoZSBudW1iZXIgb2YgY29sdW1ucyAtIHdlIHVzZSB0aGlzIGEgbG90IHNvIGF2b2lkIG11bHRpcGxlIGZ1bmN0aW9uIGNhbGxzXG4gICAgICAgIGNvbnN0IGNvbHVtbkNvdW50ID0gdGhpcy5nZXRDb2x1bW5Db3VudCgpO1xuXG4gICAgICAgIC8vIGdldCB0aGUgYW1vdW50IG9mIHBhZGRpbmcgdGhlcmUgc2hvdWxkIGJlIG9uIGVhY2ggc2lkZSBvZiBhIG5vZGVcbiAgICAgICAgY29uc3QgcGFkZGluZyA9IHRoaXMuZ2V0Q29sdW1uUGFkZGluZygpO1xuXG4gICAgICAgIGNvbnN0IGNvbHVtbldpZHRoczogbnVtYmVyW10gPSBbXTtcblxuICAgICAgICBmb3IgKGxldCBpZHggPSAwOyBpZHggPCBjb2x1bW5Db3VudDsgaWR4KyspIHtcbiAgICAgICAgICAgIGNvbHVtbldpZHRoc1tpZHhdID0gdGhpcy5nZXROb2RlV2lkdGgoKSArIChwYWRkaW5nICogMik7XG5cbiAgICAgICAgICAgIC8vIGRvIG5vIGhhdmUgdGhlIGRlZmF1bHQgcGFkZGluZyBvbiB0aGUgbGVmdCBvZiB0aGUgc3RhcnQgbm9kZVxuICAgICAgICAgICAgLy8gb3IgcmlnaHQgb2YgdGhlIGxhc3Qgbm9kZSwgaW5zdGVhZCBoYXZlIGEgZGVmYXVsdCBwYWRkaW5nXG4gICAgICAgICAgICBpZiAoaWR4ID09PSAwIHx8IGlkeCA9PT0gY29sdW1uQ291bnQgLSAxKSB7XG4gICAgICAgICAgICAgICAgY29sdW1uV2lkdGhzW2lkeF0gLT0gKHBhZGRpbmcgLSB0aGlzLl9wYWRkaW5nKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBjb2x1bW5XaWR0aHM7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUGVyZm9ybSB0aGUgdmFyaW91cyBzdGFnZXMgb2YgdGhlIGxheW91dFxuICAgICAqIGluIHRoZSBjb3JyZWN0IG9yZGVyIGFzIHNvbWUgc3RlcHMgYXJlIGRlcGVuZGFudFxuICAgICAqIG9uIHRoZSBwcmV2aW91cyBsYXlvdXQgc3RhZ2VzLlxuICAgICAqL1xuICAgIGxheW91dCgpOiBSZWFkb25seUFycmF5PFNhbmtleU5vZGVMaW5rPFQ+PiB7XG4gICAgICAgIHRoaXMuZ2V0Tm9kZUxpbmtzKCk7XG4gICAgICAgIHRoaXMuZ2V0Tm9kZVZhbHVlcygpO1xuICAgICAgICB0aGlzLmdldE5vZGVDb2x1bW5zKCk7XG4gICAgICAgIHRoaXMuZ2V0Tm9kZVdpZHRocygpO1xuICAgICAgICB0aGlzLmdldE5vZGVIZWlnaHRzKCk7XG4gICAgICAgIHRoaXMuZ2V0Tm9kZVBvc2l0aW9ucygpO1xuICAgICAgICB0aGlzLmdldExpbmtQbG90cygpO1xuICAgICAgICByZXR1cm4gdGhpcy5fbm9kZUxpbmtzO1xuICAgIH1cblxuICAgIC8qKiBUaGUgY3VydmUgZXF1YXRpb24gZm9yIGxpbmtzICovXG4gICAgbGluayhsaW5rOiBTYW5rZXlMaW5rICYgU2Fua2V5TGlua1Bsb3QpOiBzdHJpbmcge1xuXG4gICAgICAgIC8vIGNvbnN0IGRpc3QgPSBjaGFydC5ibG9ja1NwYWNpbmcgLyAyO1xuICAgICAgICBjb25zdCB7IHRvcExlZnQsIHRvcFJpZ2h0LCBib3R0b21MZWZ0LCBib3R0b21SaWdodCB9ID0gbGluaztcbiAgICAgICAgY29uc3QgZGlzdCA9ICh0b3BSaWdodFswXSAtIHRvcExlZnRbMF0pIC8gMjtcblxuICAgICAgICBjb25zdCB0b3BMZWZ0Q3VydmUgPSBbdG9wTGVmdFswXSArIGRpc3QsIHRvcExlZnRbMV1dO1xuICAgICAgICBjb25zdCB0b3BSaWdodEN1cnZlID0gW3RvcFJpZ2h0WzBdIC0gZGlzdCwgdG9wUmlnaHRbMV1dO1xuICAgICAgICBjb25zdCBib3R0b21MZWZ0Q3VydmUgPSBbYm90dG9tTGVmdFswXSArIGRpc3QsIGJvdHRvbUxlZnRbMV1dO1xuICAgICAgICBjb25zdCBib3R0b21SaWdodEN1cnZlID0gW2JvdHRvbVJpZ2h0WzBdIC0gZGlzdCwgYm90dG9tUmlnaHRbMV1dO1xuXG4gICAgICAgIHJldHVybiAnTScgKyB0b3BMZWZ0WzBdICsgJywnICsgdG9wTGVmdFsxXSArXG4gICAgICAgICAgICAnQycgKyB0b3BMZWZ0Q3VydmVbMF0gKyAnLCcgKyB0b3BMZWZ0Q3VydmVbMV0gK1xuICAgICAgICAgICAgJyAnICsgdG9wUmlnaHRDdXJ2ZVswXSArICcsJyArIHRvcFJpZ2h0Q3VydmVbMV0gK1xuICAgICAgICAgICAgJyAnICsgdG9wUmlnaHRbMF0gKyAnLCcgKyB0b3BSaWdodFsxXSArXG4gICAgICAgICAgICAnTCcgKyBib3R0b21SaWdodFswXSArICcsJyArIGJvdHRvbVJpZ2h0WzFdICtcbiAgICAgICAgICAgICdDJyArIGJvdHRvbVJpZ2h0Q3VydmVbMF0gKyAnLCcgKyBib3R0b21SaWdodEN1cnZlWzFdICtcbiAgICAgICAgICAgICcgJyArIGJvdHRvbUxlZnRDdXJ2ZVswXSArICcsJyArIGJvdHRvbUxlZnRDdXJ2ZVsxXSArXG4gICAgICAgICAgICAnICcgKyBib3R0b21MZWZ0WzBdICsgJywnICsgYm90dG9tTGVmdFsxXSArXG4gICAgICAgICAgICAnTCcgKyB0b3BMZWZ0WzBdICsgJywnICsgdG9wTGVmdFsxXTtcbiAgICB9XG5cbiAgICBnZXRGYWxsb2ZmUGF0aChub2RlTGluazogU2Fua2V5Tm9kZUxpbms8VD4pOiBzdHJpbmcge1xuICAgICAgICBjb25zdCB4ID0gKG5vZGVMaW5rLnggKyBub2RlTGluay53aWR0aCk7XG4gICAgICAgIGNvbnN0IHkgPSBub2RlTGluay5vdXRwdXRzLnJlZHVjZSgoYm90dG9tLCBvdXRwdXQpID0+IE1hdGgubWF4KGJvdHRvbSwgb3V0cHV0LmJvdHRvbUxlZnRbMV0pLCAwKTtcbiAgICAgICAgY29uc3Qgd2lkdGggPSAyMDtcbiAgICAgICAgY29uc3QgcmFkaXVzID0gNjtcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gKG5vZGVMaW5rLnkgKyBub2RlTGluay5oZWlnaHQgLSB5KSArICh0aGlzLl9zcGFjaW5nIC8gMik7XG5cbiAgICAgICAgcmV0dXJuICdNJyArIHggKyAnLCcgKyB5ICtcbiAgICAgICAgICAgICdoICcgKyAod2lkdGggLSByYWRpdXMpICtcbiAgICAgICAgICAgICdhJyArIHJhZGl1cyArICcsJyArIHJhZGl1cyArICcgMCAwLDEnICtcbiAgICAgICAgICAgIChyYWRpdXMpICsgJywnICsgKHJhZGl1cykgKyAnICcgK1xuICAgICAgICAgICAgJ3YnICsgTWF0aC5tYXgocmFkaXVzLCBoZWlnaHQpICsgJ2gtJyArIHdpZHRoICsgJ1onO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCBhIGBTYW5rZXlOb2RlTGlua2Agb2JqZWN0IGZyb20gdGhlIGlkIG9mIGEgbm9kZVxuICAgICAqL1xuICAgIGdldE5vZGVMaW5rKGlkOiBzdHJpbmcgfCBudW1iZXIpOiBTYW5rZXlOb2RlTGluazxUPiB7XG4gICAgICAgIHJldHVybiB0aGlzLl9ub2RlTGlua3MuZmluZChub2RlTGluayA9PiBub2RlTGluay5ub2RlLmlkID09PSBpZCk7XG4gICAgfVxuXG4gICAgLyoqIFJlcGxhY2UgdGhlIG5vZGUgaWRzIHdpdGggYWN0dWFsIHJlZmVyZW5jZXMgKi9cbiAgICBwcml2YXRlIGdldE5vZGVMaW5rcygpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5fbm9kZUxpbmtzID0gdGhpcy5fbm9kZXMubWFwKG5vZGUgPT4ge1xuICAgICAgICAgICAgLy8gZ2V0IGFsbCB0aGUgbGlua3MgdGhhdCBpbnB1dCBpbnRvIGFuZCBvdXRwdXQgZnJvbSB0aGlzIG5vZGVcbiAgICAgICAgICAgIGNvbnN0IGlucHV0cyA9IHRoaXMuX2xpbmtzLmZpbHRlcihsaW5rID0+IGxpbmsudGFyZ2V0ID09PSBub2RlLmlkKTtcbiAgICAgICAgICAgIGNvbnN0IG91dHB1dHMgPSB0aGlzLl9saW5rcy5maWx0ZXIobGluayA9PiBsaW5rLnNvdXJjZSA9PT0gbm9kZS5pZCk7XG5cbiAgICAgICAgICAgIHJldHVybiB7IG5vZGUsIGlucHV0cywgb3V0cHV0cywgdmFsdWU6IDAsIGNvbHVtbjogMCwgeDogMCwgeTogMCwgd2lkdGg6IDAsIGhlaWdodDogMCwgbmF0dXJhbEhlaWdodDogMCwgZmFsbG9mZjogMCwgYWN0aXZlOiBmYWxzZSwgZm9jdXM6IGZhbHNlIH0gYXMgU2Fua2V5Tm9kZUxpbms8VD47XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKiBHZXQgdGhlIHZhbHVlIGZvciB0aGUgbm9kZSBiYXNlZCBvbiBhbGwgaXRzIGlucHV0cyBhbmQgb3V0cHV0cyAqL1xuICAgIHByaXZhdGUgZ2V0Tm9kZVZhbHVlcygpOiB2b2lkIHtcbiAgICAgICAgZm9yIChjb25zdCBub2RlIG9mIHRoaXMuX25vZGVMaW5rcykge1xuICAgICAgICAgICAgLy8gdGhlIG5vZGUgdmFsdWUgY2FuIGJlIGRldGVybWluZWQgYnkgdGhlIHRvdGFsIHZhbHVlcyBmcm9tIGFsbCBpbnB1dHNcbiAgICAgICAgICAgIC8vIGhvd2V2ZXIgdGhlIGZpcnN0IGNvbHVtbiBvZiBub2RlcyBoYXZlIG5vIGlucHV0cyBzbyBtdXN0IGJlIGJhc2VkIG9mIHRoZWlyIG91dHB1dHMuXG4gICAgICAgICAgICAvLyBXZSBzaG91bGQgdGFrZSB0aGUgbWF4aW11bSB2YWx1ZSBiYXNlZCBvbiB0aGUgaW5wdXRzIGFuZCBvdXRwdXRzIGFzIG5vZGVzIHRoYXQgYXJlXG4gICAgICAgICAgICAvLyBub3QgaW4gdGhlIGZpcnN0IGNvbHVtbiBtYXkgbm90IG91dHB1dCBhbGwgb2YgdGhlIGFtb3VudCB0aGUgcmVjZWl2ZSBmcm9tIGlucHV0cyxcbiAgICAgICAgICAgIC8vIGZvciBleGFtcGxlIGluIHRoZSBjYXNlIG9mIGZhbGxvZmYgZXRjLi5cbiAgICAgICAgICAgIG5vZGUudmFsdWUgPSBNYXRoLm1heChcbiAgICAgICAgICAgICAgICBzdW0obm9kZS5pbnB1dHMsIGlucHV0ID0+IGlucHV0LnZhbHVlKSxcbiAgICAgICAgICAgICAgICBzdW0obm9kZS5vdXRwdXRzLCBvdXRwdXQgPT4gb3V0cHV0LnZhbHVlKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFdlIG5lZWQgdG8gZGV0ZXJtaW5lIHdoaWNoIGNvbHVtbiB0aGUgbm9kZSBzaG91bGRcbiAgICAgKiBiZSBwbGFjZWQgaW4uIFRoaXMgaXMgZGV0ZXJtaW5lZCBieSB0YWtpbmcgdGhlIGlucHV0XG4gICAgICogYW5kIGFkZGluZyBvbmUuXG4gICAgICovXG4gICAgcHJpdmF0ZSBnZXROb2RlQ29sdW1ucyhub2RlTGlua3M6IFNhbmtleU5vZGVMaW5rPFQ+W10gPSB0aGlzLl9ub2RlTGlua3MuZmlsdGVyKG5vZGUgPT4gbm9kZS5pbnB1dHMubGVuZ3RoID09PSAwKSwgY29sdW1uOiBudW1iZXIgPSAwKTogdm9pZCB7XG4gICAgICAgIGZvciAoY29uc3Qgbm9kZUxpbmsgb2Ygbm9kZUxpbmtzKSB7XG4gICAgICAgICAgICBub2RlTGluay5jb2x1bW4gPSBjb2x1bW47XG5cbiAgICAgICAgICAgIC8vIGNhbGwgdGhpcyBmdW5jdGlvbiB0byBhbGwgb3V0cHV0IGxpbmtzXG4gICAgICAgICAgICB0aGlzLmdldE5vZGVDb2x1bW5zKG5vZGVMaW5rLm91dHB1dHMubWFwKG91dHB1dCA9PiB0aGlzLmdldE5vZGVMaW5rKG91dHB1dC50YXJnZXQpKSwgY29sdW1uICsgMSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogR2V0IHRoZSB3aWR0aCBvZiBlYWNoIG5vZGUgKi9cbiAgICBwcml2YXRlIGdldE5vZGVXaWR0aHMoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX25vZGVMaW5rcy5mb3JFYWNoKG5vZGUgPT4gbm9kZS53aWR0aCA9IHRoaXMuZ2V0Tm9kZVdpZHRoKCkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNjYWxlIHRoZSBub2RlcyBoZWlnaHQgYmFzZWQgb24gdGhlIHZhbHVlIHRoZSByZXByZXNlbnRcbiAgICAgKi9cbiAgICBwcml2YXRlIGdldE5vZGVIZWlnaHRzKCk6IHZvaWQge1xuXG4gICAgICAgIC8vIGdldCBjb2x1bW5zIGJ5IGdyb3VwXG4gICAgICAgIGNvbnN0IGdyb3VwcyA9IHRoaXMuZ2V0Q29sdW1uR3JvdXBzKCk7XG4gICAgICAgIGNvbnN0IGdyb3VwTGlzdCA9IE9iamVjdC5rZXlzKGdyb3VwcykubWFwPFNhbmtleU5vZGVMaW5rPFQ+W10+KGdyb3VwID0+IGdyb3Vwc1tncm91cF0pO1xuXG4gICAgICAgIC8vIGdldCB0aGUgY29sdW1uIHdpdGggdGhlIGxhcmdlc3QgdG90YWwgdmFsdWVcbiAgICAgICAgY29uc3QgdG90YWwgPSBncm91cExpc3QucmVkdWNlKFxuICAgICAgICAgICAgKGNvdW50LCBub2RlcykgPT4gTWF0aC5tYXgoY291bnQsIG5vZGVzLnJlZHVjZSgoYWNjdW11bGF0aW9uLCBub2RlKSA9PiBhY2N1bXVsYXRpb24gKyBub2RlLnZhbHVlLCAwKSksXG4gICAgICAgICAgICAwXG4gICAgICAgICk7XG5cbiAgICAgICAgLy8gQ2FsY3VsYXRlIG5vZGUgaGVpZ2h0c1xuICAgICAgICBmb3IgKGNvbnN0IG5vZGVMaW5rcyBvZiBncm91cExpc3QpIHtcblxuICAgICAgICAgICAgLy8gZ2V0IHRoZSBwcm9wb3J0aW9uYWwgc2l6ZSBvZiBlYWNoIG5vZGUgYmFzZWQgb24gdGhlIGF2YWlsYWJsZSBzcGFjZVxuICAgICAgICAgICAgZm9yIChjb25zdCBub2RlTGluayBvZiBub2RlTGlua3MpIHtcbiAgICAgICAgICAgICAgICBub2RlTGluay5uYXR1cmFsSGVpZ2h0ID0gKChub2RlTGluay52YWx1ZSAvIHRvdGFsKSAqIHRoaXMuX2hlaWdodCkgLSB0aGlzLl9zcGFjaW5nO1xuICAgICAgICAgICAgICAgIG5vZGVMaW5rLmhlaWdodCA9IE1hdGgubWF4KG5vZGVMaW5rLm5hdHVyYWxIZWlnaHQsIHRoaXMuX21pbkhlaWdodCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBJZiBtaW5IZWlnaHQgaXMgZGVmaW5lZCwgaXQgbWlnaHQgY2F1c2Ugc29tZSBjb2x1bW5zIHRvIGV4Y2VlZCB0aGUgaGVpZ2h0IG9mIHRoZSBjaGFydCBmb2xsb3dpbmcgdGhlXG4gICAgICAgIC8vIGluaXRpYWwgaGVpZ2h0IGNhbGN1bGF0aW9uLlxuICAgICAgICBpZiAodGhpcy5fbWluSGVpZ2h0ID4gMCkge1xuXG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIFJlY2FsY3VsYXRlIG5vZGUgaGVpZ2h0cyB1bnRpbCB0aGV5IGZpdCAoaWYgcG9zc2libGUpXG4gICAgICAgICAgICAgICAgdGhpcy5hZGp1c3ROb2RlSGVpZ2h0c1RvRml0KGdyb3VwTGlzdCk7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgICAgICAgIC8vIElmIHRoZSBhYm92ZSByZWNhbGN1bGF0aW9uIGZhaWxzLCBnaXZlIHVwIGFuZCB1c2UgdGhlIG5hdHVyYWxIZWlnaHQgKGlnbm9yZSBtaW5IZWlnaHQpXG4gICAgICAgICAgICAgICAgdGhpcy5zZXROb2Rlc1RvTmF0dXJhbEhlaWdodChncm91cExpc3QpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVjYWxjdWxhdGUgbm9kZSBoZWlnaHRzIHdpdGhpbiBoZWlnaHQgbGltaXRzIHVudGlsIHRoZXkgZml0IChpZiBwb3NzaWJsZSkuXG4gICAgICogQHRocm93cyBJZiBpdCBpcyBub3QgcG9zc2libGUgdG8gZml0IGFsbCBub2RlcyBpbiB0aGUgY2hhcnQgZHVlIHRvIGBtaW5IZWlnaHRgLlxuICAgICAqL1xuICAgIHByaXZhdGUgYWRqdXN0Tm9kZUhlaWdodHNUb0ZpdChncm91cExpc3Q6IFNhbmtleU5vZGVMaW5rPFQ+W11bXSkge1xuXG4gICAgICAgIGxldCBsYXJnZXN0Q29sdW1uID0gdGhpcy5nZXRMYXJnZXN0Q29sdW1uKGdyb3VwTGlzdCk7XG5cbiAgICAgICAgd2hpbGUgKGxhcmdlc3RDb2x1bW4uaGVpZ2h0ID4gdGhpcy5faGVpZ2h0KSB7XG5cbiAgICAgICAgICAgIC8vIEdldCB0aGUgbGlzdCBvZiBub2RlcyB3aG9zZSBoZWlnaHQgY2Fubm90IGJlIHJlZHVjZWRcbiAgICAgICAgICAgIGNvbnN0IGZpeGVkTm9kZXMgPSBsYXJnZXN0Q29sdW1uLm5vZGVzLmZpbHRlcihub2RlTGluayA9PiBub2RlTGluay5oZWlnaHQgPD0gdGhpcy5fbWluSGVpZ2h0KTtcblxuICAgICAgICAgICAgLy8gR2V0IHRoZSB0b3RhbCBoZWlnaHQgaW4gdGhlIGNvbHVtbiB3aGljaCBjYW5ub3Qgc2hyaW5rIChpbmNsdWRpbmcgc3BhY2luZylcbiAgICAgICAgICAgIGNvbnN0IGZpeGVkSGVpZ2h0ID0gZml4ZWROb2Rlcy5sZW5ndGggKiB0aGlzLl9taW5IZWlnaHQgKyBsYXJnZXN0Q29sdW1uLm5vZGVzLmxlbmd0aCAqIHRoaXMuX3NwYWNpbmc7XG5cbiAgICAgICAgICAgIC8vIElmIHRoZSB1bnNocmlua2FibGUgaGVpZ2h0IGlzIGdyZWF0ZXIgdGhhbiB0aGUgYXZhaWxhYmxlIGhlaWdodCwgd2UgY2FuJ3QgY29udGludWVcbiAgICAgICAgICAgIGlmIChmaXhlZEhlaWdodCA+IHRoaXMuX2hlaWdodCkge1xuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgQ2Fubm90IGZpdCBkYXRhIGludG8gY2hhcnQgd2l0aCBtaW5IZWlnaHQgPSAke3RoaXMuX21pbkhlaWdodH1weCAobmVlZCAke2ZpeGVkSGVpZ2h0fXB4OyAke3RoaXMuX2hlaWdodH1weCBhdmFpbGFibGUpYCk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIEZpbmQgdGhlIGFtb3VudCBvZiBoZWlnaHQgd2hpY2ggY2FuIHBvdGVudGlhbGx5IGJlIHJlZHVjZWRcbiAgICAgICAgICAgIGNvbnN0IGZsZXhpYmxlSGVpZ2h0ID0gbGFyZ2VzdENvbHVtbi5oZWlnaHQgLSBmaXhlZEhlaWdodDtcblxuICAgICAgICAgICAgLy8gRmluZCB0aGUgYW1vdW50IG9mIGhlaWdodCB0aGF0IHRoZSBhYm92ZSBuZWVkcyB0byBmaXQgaW50b1xuICAgICAgICAgICAgY29uc3QgYXZhaWxhYmxlSGVpZ2h0ID0gdGhpcy5faGVpZ2h0IC0gZml4ZWRIZWlnaHQ7XG5cbiAgICAgICAgICAgIC8vIEdldCB0aGUgbXVsdGlwbGllciB0byByZWR1Y2UgdGhlIG5vZGVzIGluIG9yZGVyIHRvIGZpdCB0aGUgYXZhaWxhYmxlIGhlaWdodFxuICAgICAgICAgICAgY29uc3QgcmF0aW8gPSBhdmFpbGFibGVIZWlnaHQgLyBmbGV4aWJsZUhlaWdodDtcblxuICAgICAgICAgICAgLy8gQWRqdXN0IHRoZSBub2RlcyBhbmQgcmVhcHBseSB0aGUgbWluSGVpZ2h0XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGdyb3VwIG9mIGdyb3VwTGlzdCkge1xuICAgICAgICAgICAgICAgIGZvciAoY29uc3Qgbm9kZUxpbmsgb2YgZ3JvdXApIHtcbiAgICAgICAgICAgICAgICAgICAgaWYgKG5vZGVMaW5rLmhlaWdodCA+IHRoaXMuX21pbkhlaWdodCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgbm9kZUxpbmsuaGVpZ2h0ICo9IHJhdGlvO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIGlmIChub2RlTGluay5oZWlnaHQgPCB0aGlzLl9taW5IZWlnaHQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIG5vZGVMaW5rLmhlaWdodCA9IHRoaXMuX21pbkhlaWdodDtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgbGFyZ2VzdENvbHVtbiA9IHRoaXMuZ2V0TGFyZ2VzdENvbHVtbihncm91cExpc3QpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIFNldCBhbGwgbm9kZXMgaGVpZ2h0IHRvIGJlIHRoZSBzYW1lIGFzIHRoZSBuYXR1cmFsSGVpZ2h0LiAqL1xuICAgIHByaXZhdGUgc2V0Tm9kZXNUb05hdHVyYWxIZWlnaHQoZ3JvdXBMaXN0OiBTYW5rZXlOb2RlTGluazxUPltdW10pIHtcbiAgICAgICAgZm9yIChjb25zdCBncm91cCBvZiBncm91cExpc3QpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3Qgbm9kZUxpbmsgb2YgZ3JvdXApIHtcbiAgICAgICAgICAgICAgICBub2RlTGluay5oZWlnaHQgPSBub2RlTGluay5uYXR1cmFsSGVpZ2h0O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogR2V0IGFsbCBub2RlcyBncm91cGVkIGluIHRoZWlyIGNvcnJlc3BvbmRpbmcgY29sdW1uc1xuICAgICAqL1xuICAgIHByaXZhdGUgZ2V0Q29sdW1uR3JvdXBzKCk6IHsgW2tleTogbnVtYmVyXTogU2Fua2V5Tm9kZUxpbms8VD5bXSB9IHtcbiAgICAgICAgLy8gZ3JvdXAgbm9kZXMgYnkgY29sdW1uc1xuICAgICAgICByZXR1cm4gdGhpcy5fbm9kZUxpbmtzLnJlZHVjZTx7IFtrZXk6IG51bWJlcl06IFNhbmtleU5vZGVMaW5rPFQ+W10gfT4oKGNvbGxlY3Rpb24sIG5vZGVMaW5rKSA9PiB7XG4gICAgICAgICAgICBjb2xsZWN0aW9uW25vZGVMaW5rLmNvbHVtbl0gPSBjb2xsZWN0aW9uW25vZGVMaW5rLmNvbHVtbl0gfHwgW107XG4gICAgICAgICAgICBjb2xsZWN0aW9uW25vZGVMaW5rLmNvbHVtbl0ucHVzaChub2RlTGluayk7XG4gICAgICAgICAgICByZXR1cm4gY29sbGVjdGlvbjtcbiAgICAgICAgfSwge30pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEdldCB0aGUgbnVtYmVyIG9mIGNvbHVtbnNcbiAgICAgKi9cbiAgICBwcml2YXRlIGdldENvbHVtbkNvdW50KCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiB0aGlzLl9ub2RlTGlua3MucmVkdWNlKChjb2x1bW4sIG5vZGVMaW5rKSA9PiBNYXRoLm1heChub2RlTGluay5jb2x1bW4gKyAxLCBjb2x1bW4pLCAwKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBQb3NpdGlvbiB0aGUgbm9kZXMgaW4gdGhlaXIgY29ycmVzcG9uZGluZyB4IGFuZCB5IHBvc2l0aW9uc1xuICAgICAqL1xuICAgIHByaXZhdGUgZ2V0Tm9kZVBvc2l0aW9ucygpOiB2b2lkIHtcblxuICAgICAgICAvLyBnZXQgYWxsIG5vZGVzIGJ5IGdyb3VwXG4gICAgICAgIGNvbnN0IGdyb3VwcyA9IHRoaXMuZ2V0Q29sdW1uR3JvdXBzKCk7XG5cbiAgICAgICAgLy8gZ2V0IHRoZSBhbW91bnQgb2YgcGFkZGluZyByZXF1aXJlZCBiZXR3ZWVuIGVhY2ggaXRlbVxuICAgICAgICBjb25zdCBwYWRkaW5nID0gdGhpcy5nZXRDb2x1bW5QYWRkaW5nKCk7XG5cbiAgICAgICAgZm9yIChjb25zdCBub2RlTGluayBvZiB0aGlzLl9ub2RlTGlua3MpIHtcbiAgICAgICAgICAgIC8vIGdldCB0aGUgeCBwb3NpdGlvbiBiYXNlZCBvbiB0aGUgY29sdW1uXG4gICAgICAgICAgICBub2RlTGluay54ID0gdGhpcy5nZXRDb2x1bW5Qb3NpdGlvbihub2RlTGluay5jb2x1bW4pICsgcGFkZGluZztcblxuICAgICAgICAgICAgaWYgKG5vZGVMaW5rLmNvbHVtbiA9PT0gMCkge1xuICAgICAgICAgICAgICAgIG5vZGVMaW5rLnggPSB0aGlzLl9wYWRkaW5nO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBnZXQgdGhlIHkgcG9zaXRpb24gYmFzZWQgb24gdGhlIGFjY3VtdWxhdGl2ZSBoZWlnaHQgb2YgdGhlIG5vZGVzIGFib3ZlIGl0XG4gICAgICAgICAgICBub2RlTGluay55ID0gZ3JvdXBzW25vZGVMaW5rLmNvbHVtbl1cbiAgICAgICAgICAgICAgICAuc2xpY2UoMCwgZ3JvdXBzW25vZGVMaW5rLmNvbHVtbl0uaW5kZXhPZihub2RlTGluaykpXG4gICAgICAgICAgICAgICAgLnJlZHVjZSgodG9wLCBfbm9kZSkgPT4gdG9wICsgX25vZGUuaGVpZ2h0LCAwKSArICh0aGlzLl9zcGFjaW5nICogZ3JvdXBzW25vZGVMaW5rLmNvbHVtbl0uaW5kZXhPZihub2RlTGluaykpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXRDb2x1bW5QYWRkaW5nKCk6IG51bWJlciB7XG4gICAgICAgIC8vIGdldCB0aGUgbnVtYmVyIG9mIGNvbHVtbnMgLSB3ZSB1c2UgdGhpcyBhIGxvdCBzbyBhdm9pZCBtdWx0aXBsZSBmdW5jdGlvbiBjYWxsc1xuICAgICAgICBjb25zdCBjb2x1bW5Db3VudCA9IHRoaXMuZ2V0Q29sdW1uQ291bnQoKTtcblxuICAgICAgICAvLyBnZXQgdGhlIGNoYXJ0IHdpZHRoIG1pbnVzIHRoZSB3aWR0aCBvZiB0aGUgbm9kZXNcbiAgICAgICAgY29uc3Qgd2lkdGggPSAodGhpcy5fd2lkdGggLSAoY29sdW1uQ291bnQgKiB0aGlzLmdldE5vZGVXaWR0aCgpKSkgLSAodGhpcy5fcGFkZGluZyAqIDIpO1xuXG4gICAgICAgIC8vIGdldCB0aGUgdG90YWwgYW1vdW50IG9mIHBsYWNlcyByZXF1aXJpbmcgcGFkZGluZyAodGhlIGZpcnN0IGFuZCBsYXN0IGNvbHVtbnMgb25seSBoYXZlIHBhZGRpbmcgb24gb25lIHNpZGUpXG4gICAgICAgIGNvbnN0IHBhZGRpbmdDb3VudCA9IE1hdGgubWF4KCgoY29sdW1uQ291bnQgKiAyKSAtIDIpLCAwKTtcblxuICAgICAgICAvLyBnZXQgdGhlIGFjdHVhbCBzaXplIG9mIHRoZSBwYWRkaW5nXG4gICAgICAgIHJldHVybiB3aWR0aCAvIHBhZGRpbmdDb3VudDtcbiAgICB9XG5cbiAgICBwcml2YXRlIGdldExpbmtQbG90cygpOiB2b2lkIHtcblxuICAgICAgICBmb3IgKGNvbnN0IG5vZGVMaW5rIG9mIHRoaXMuX25vZGVMaW5rcykge1xuXG4gICAgICAgICAgICBsZXQgaW5wdXRZOiBudW1iZXIgPSBub2RlTGluay55O1xuXG4gICAgICAgICAgICAvLyBwcm9jZXNzIGVhY2ggaW5wdXQgbGlua1xuICAgICAgICAgICAgZm9yIChjb25zdCBsaW5rIG9mIG5vZGVMaW5rLmlucHV0cykge1xuICAgICAgICAgICAgICAgIGxpbmsudG9wUmlnaHQgPSBbbm9kZUxpbmsueCwgaW5wdXRZXTtcbiAgICAgICAgICAgICAgICBpbnB1dFkgKz0gKChsaW5rLnZhbHVlIC8gbm9kZUxpbmsudmFsdWUpICogbm9kZUxpbmsuaGVpZ2h0KTtcbiAgICAgICAgICAgICAgICBsaW5rLmJvdHRvbVJpZ2h0ID0gW25vZGVMaW5rLngsIGlucHV0WV07XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGxldCBvdXRwdXRWYWx1ZTogbnVtYmVyID0gMDtcbiAgICAgICAgICAgIGxldCBvdXRwdXRZOiBudW1iZXIgPSBub2RlTGluay55O1xuXG4gICAgICAgICAgICAvLyBwcm9jZXNzIGVhY2ggb3V0cHV0IGxpbmtcbiAgICAgICAgICAgIGZvciAoY29uc3QgbGluayBvZiBub2RlTGluay5vdXRwdXRzKSB7XG4gICAgICAgICAgICAgICAgbGluay50b3BMZWZ0ID0gW25vZGVMaW5rLnggKyBub2RlTGluay53aWR0aCwgb3V0cHV0WV07XG4gICAgICAgICAgICAgICAgb3V0cHV0WSArPSAoKGxpbmsudmFsdWUgLyBub2RlTGluay52YWx1ZSkgKiBub2RlTGluay5oZWlnaHQpO1xuICAgICAgICAgICAgICAgIGxpbmsuYm90dG9tTGVmdCA9IFtub2RlTGluay54ICsgbm9kZUxpbmsud2lkdGgsIG91dHB1dFldO1xuXG4gICAgICAgICAgICAgICAgb3V0cHV0VmFsdWUgKz0gbGluay52YWx1ZTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gZGV0ZXJtaW5lIGhvdyBtdWNoIGZhbGxvZmYgdGhlcmUgaXNcbiAgICAgICAgICAgIG5vZGVMaW5rLmZhbGxvZmYgPSBub2RlTGluay52YWx1ZSAtIG91dHB1dFZhbHVlO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIERldGVybWluZSB0aGUgcG9zaXRpb24gYXQgd2hpY2ggYSBjb2x1bW4gc3RhcnRzICovXG4gICAgcHJpdmF0ZSBnZXRDb2x1bW5Qb3NpdGlvbihjb2x1bW46IG51bWJlcik6IG51bWJlciB7XG4gICAgICAgIC8vIHRoZSBwb3NpdGlvbiBpcyB0aGUgYWNjdWxhdGlvbiBvZiB0aGUgd2lkdGhzIG9mIGFsbCBwcmV2aW91cyBjb2x1bW5zXG4gICAgICAgIHJldHVybiB0aGlzLmNvbHVtbnMoKS5zcGxpY2UoMCwgY29sdW1uKS5yZWR1Y2UoKHRvdGFsLCB3aWR0aCkgPT4gdG90YWwgKyB3aWR0aCwgMCk7XG4gICA