UNPKG

js-angusj-clipper

Version:

Polygon and line clipping and offsetting library for Javascript / Typescript - a port of Angus Johnson's clipper to WebAssembly / Asm.JS

349 lines 51.5 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Clipper = void 0; var nativeEnumConversion_1 = require("./native/nativeEnumConversion"); var PathsToNativePaths_1 = require("./native/PathsToNativePaths"); var PathToNativePath_1 = require("./native/PathToNativePath"); var PolyTree_1 = require("./PolyTree"); var nativeFinalizationRegistry_1 = require("./nativeFinalizationRegistry"); var Clipper = /** @class */ (function () { /** * The Clipper constructor creates an instance of the Clipper class. One or more InitOptions may be passed as a parameter to set the corresponding properties. * (These properties can still be set or reset after construction.) * * @param _nativeLib * @param initOptions */ function Clipper(_nativeLib, initOptions) { if (initOptions === void 0) { initOptions = {}; } this._nativeLib = _nativeLib; var realInitOptions = __assign({ reverseSolutions: false, strictlySimple: false, preserveCollinear: false }, initOptions); var nativeInitOptions = 0; if (realInitOptions.reverseSolutions) { nativeInitOptions += _nativeLib.InitOptions.ReverseSolution.value; } if (realInitOptions.strictlySimple) { nativeInitOptions += _nativeLib.InitOptions.StrictlySimple.value; } if (realInitOptions.preserveCollinear) { nativeInitOptions += _nativeLib.InitOptions.PreserveCollinear.value; } this._clipper = new _nativeLib.Clipper(nativeInitOptions); nativeFinalizationRegistry_1.nativeFinalizationRegistry === null || nativeFinalizationRegistry_1.nativeFinalizationRegistry === void 0 ? void 0 : nativeFinalizationRegistry_1.nativeFinalizationRegistry.register(this, this._clipper, this); } Object.defineProperty(Clipper.prototype, "preserveCollinear", { /** * By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before * clipping. When enabled the preserveCollinear property prevents this default behavior to allow these inner vertices to appear in the solution. * * @return {boolean} - true if set, false otherwise */ get: function () { return this._clipper.preserveCollinear; }, /** * By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before * clipping. When enabled the preserveCollinear property prevents this default behavior to allow these inner vertices to appear in the solution. * * @param value - value to set */ set: function (value) { this._clipper.preserveCollinear = value; }, enumerable: false, configurable: true }); Object.defineProperty(Clipper.prototype, "reverseSolution", { /** * When this property is set to true, polygons returned in the solution parameter of the execute() method will have orientations opposite to their normal * orientations. * * @return {boolean} - true if set, false otherwise */ get: function () { return this._clipper.reverseSolution; }, /** * When this property is set to true, polygons returned in the solution parameter of the execute() method will have orientations opposite to their normal * orientations. * * @param value - value to set */ set: function (value) { this._clipper.reverseSolution = value; }, enumerable: false, configurable: true }); Object.defineProperty(Clipper.prototype, "strictlySimple", { /** * Terminology: * - A simple polygon is one that does not self-intersect. * - A weakly simple polygon is a simple polygon that contains 'touching' vertices, or 'touching' edges. * - A strictly simple polygon is a simple polygon that does not contain 'touching' vertices, or 'touching' edges. * * Vertices 'touch' if they share the same coordinates (and are not adjacent). An edge touches another if one of its end vertices touches another edge * excluding its adjacent edges, or if they are co-linear and overlapping (including adjacent edges). * * Polygons returned by clipping operations (see Clipper.execute()) should always be simple polygons. When the StrictlySimply property is enabled, * polygons returned will be strictly simple, otherwise they may be weakly simple. It's computationally expensive ensuring polygons are strictly simple * and so this property is disabled by default. * * Note: There's currently no guarantee that polygons will be strictly simple since 'simplifying' is still a work in progress. * * @return {boolean} - true if set, false otherwise */ get: function () { return this._clipper.strictlySimple; }, /** * Terminology: * - A simple polygon is one that does not self-intersect. * - A weakly simple polygon is a simple polygon that contains 'touching' vertices, or 'touching' edges. * - A strictly simple polygon is a simple polygon that does not contain 'touching' vertices, or 'touching' edges. * * Vertices 'touch' if they share the same coordinates (and are not adjacent). An edge touches another if one of its end vertices touches another edge * excluding its adjacent edges, or if they are co-linear and overlapping (including adjacent edges). * * Polygons returned by clipping operations (see Clipper.execute()) should always be simple polygons. When the StrictlySimply property is enabled, * polygons returned will be strictly simple, otherwise they may be weakly simple. It's computationally expensive ensuring polygons are strictly simple * and so this property is disabled by default. * * Note: There's currently no guarantee that polygons will be strictly simple since 'simplifying' is still a work in progress. * * @param value - value to set */ set: function (value) { this._clipper.strictlySimple = value; }, enumerable: false, configurable: true }); /** * Any number of subject and clip paths can be added to a clipping task, either individually via the addPath() method, or as groups via the addPaths() * method, or even using both methods. * * 'Subject' paths may be either open (lines) or closed (polygons) or even a mixture of both, but 'clipping' paths must always be closed. Clipper allows * polygons to clip both lines and other polygons, but doesn't allow lines to clip either lines or polygons. * * With closed paths, orientation should conform with the filling rule that will be passed via Clipper's execute method. * * Path Coordinate range: * Path coordinates must be between ± 9007199254740991, otherwise a range error will be thrown when attempting to add the path to the Clipper object. * If coordinates can be kept between ± 0x3FFFFFFF (± 1.0e+9), a modest increase in performance (approx. 15-20%) over the larger range can be achieved by * avoiding large integer math. * * Return Value: * The function will return false if the path is invalid for clipping. A path is invalid for clipping when: * - it has less than 2 vertices * - it has 2 vertices but is not an open path * - the vertices are all co-linear and it is not an open path * * @param path - Path to add * @param polyType - Polygon type * @param closed - If the path is closed */ Clipper.prototype.addPath = function (path, polyType, closed) { var nativePath = (0, PathToNativePath_1.pathToNativePath)(this._nativeLib, path); try { return this._clipper.addPath(nativePath, (0, nativeEnumConversion_1.polyTypeToNative)(this._nativeLib, polyType), closed); } finally { nativePath.delete(); } }; /** * Any number of subject and clip paths can be added to a clipping task, either individually via the addPath() method, or as groups via the addPaths() * method, or even using both methods. * * 'Subject' paths may be either open (lines) or closed (polygons) or even a mixture of both, but 'clipping' paths must always be closed. Clipper allows * polygons to clip both lines and other polygons, but doesn't allow lines to clip either lines or polygons. * * With closed paths, orientation should conform with the filling rule that will be passed via Clipper's execute method. * * Path Coordinate range: * Path coordinates must be between ± 9007199254740991, otherwise a range error will be thrown when attempting to add the path to the Clipper object. * If coordinates can be kept between ± 0x3FFFFFFF (± 1.0e+9), a modest increase in performance (approx. 15-20%) over the larger range can be achieved * by avoiding large integer math. * * Return Value: * The function will return false if the path is invalid for clipping. A path is invalid for clipping when: * - it has less than 2 vertices * - it has 2 vertices but is not an open path * - the vertices are all co-linear and it is not an open path * * @param paths - Paths to add * @param polyType - Paths polygon type * @param closed - If all the inner paths are closed */ Clipper.prototype.addPaths = function (paths, polyType, closed) { var nativePaths = (0, PathsToNativePaths_1.pathsToNativePaths)(this._nativeLib, paths); try { return this._clipper.addPaths(nativePaths, (0, nativeEnumConversion_1.polyTypeToNative)(this._nativeLib, polyType), closed); } finally { nativePaths.delete(); } }; /** * The Clear method removes any existing subject and clip polygons allowing the Clipper object to be reused for clipping operations on different polygon sets. */ Clipper.prototype.clear = function () { this._clipper.clear(); }; /** * This method returns the axis-aligned bounding rectangle of all polygons that have been added to the Clipper object. * * @return {{left: number, right: number, top: number, bottom: number}} - Bounds */ Clipper.prototype.getBounds = function () { var nativeBounds = this._clipper.getBounds(); var rect = { left: nativeBounds.left, right: nativeBounds.right, top: nativeBounds.top, bottom: nativeBounds.bottom, }; nativeBounds.delete(); return rect; }; /** * Once subject and clip paths have been assigned (via addPath and/or addPaths), execute can then perform the clipping operation (intersection, union, * difference or XOR) specified by the clipType parameter. * * The solution parameter in this case is a Paths or PolyTree structure. The Paths structure is simpler than the PolyTree structure. Because of this it is * quicker to populate and hence clipping performance is a little better (it's roughly 10% faster). However, the PolyTree data structure provides more * information about the returned paths which may be important to users. Firstly, the PolyTree structure preserves nested parent-child polygon relationships * (ie outer polygons owning/containing holes and holes owning/containing other outer polygons etc). Also, only the PolyTree structure can differentiate * between open and closed paths since each PolyNode has an IsOpen property. (The Path structure has no member indicating whether it's open or closed.) * For this reason, when open paths are passed to a Clipper object, the user must use a PolyTree object as the solution parameter, otherwise an exception * will be raised. * * When a PolyTree object is used in a clipping operation on open paths, two ancilliary functions have been provided to quickly separate out open and * closed paths from the solution - OpenPathsFromPolyTree and ClosedPathsFromPolyTree. PolyTreeToPaths is also available to convert path data to a Paths * structure (irrespective of whether they're open or closed). * * There are several things to note about the solution paths returned: * - they aren't in any specific order * - they should never overlap or be self-intersecting (but see notes on rounding) * - holes will be oriented opposite outer polygons * - the solution fill type can be considered either EvenOdd or NonZero since it will comply with either filling rule * - polygons may rarely share a common edge (though this is now very rare as of version 6) * * The subjFillType and clipFillType parameters define the polygon fill rule to be applied to the polygons (ie closed paths) in the subject and clip * paths respectively. (It's usual though obviously not essential that both sets of polygons use the same fill rule.) * * execute can be called multiple times without reassigning subject and clip polygons (ie when different clipping operations are required on the * same polygon sets). * * @param clipType - Clip operation type * @param subjFillType - Fill type of the subject polygons * @param clipFillType - Fill type of the clip polygons * @param cleanDistance - Clean distance over the output, or undefined for no cleaning. * @return {Paths | undefined} - The solution or undefined if there was an error */ Clipper.prototype.executeToPaths = function (clipType, subjFillType, clipFillType, cleanDistance) { var outNativePaths = new this._nativeLib.Paths(); try { var success = this._clipper.executePathsWithFillTypes((0, nativeEnumConversion_1.clipTypeToNative)(this._nativeLib, clipType), outNativePaths, (0, nativeEnumConversion_1.polyFillTypeToNative)(this._nativeLib, subjFillType), (0, nativeEnumConversion_1.polyFillTypeToNative)(this._nativeLib, clipFillType)); if (!success) { return undefined; } else { if (cleanDistance !== undefined) { this._nativeLib.cleanPolygons(outNativePaths, cleanDistance); } return (0, PathsToNativePaths_1.nativePathsToPaths)(this._nativeLib, outNativePaths, true); // frees outNativePaths } } finally { if (!outNativePaths.isDeleted()) { outNativePaths.delete(); } } }; /** * Once subject and clip paths have been assigned (via addPath and/or addPaths), execute can then perform the clipping operation (intersection, union, * difference or XOR) specified by the clipType parameter. * * The solution parameter can be either a Paths or PolyTree structure. The Paths structure is simpler than the PolyTree structure. Because of this it is * quicker to populate and hence clipping performance is a little better (it's roughly 10% faster). However, the PolyTree data structure provides more * information about the returned paths which may be important to users. Firstly, the PolyTree structure preserves nested parent-child polygon relationships * (ie outer polygons owning/containing holes and holes owning/containing other outer polygons etc). Also, only the PolyTree structure can differentiate * between open and closed paths since each PolyNode has an IsOpen property. (The Path structure has no member indicating whether it's open or closed.) * For this reason, when open paths are passed to a Clipper object, the user must use a PolyTree object as the solution parameter, otherwise an exception * will be raised. * * When a PolyTree object is used in a clipping operation on open paths, two ancilliary functions have been provided to quickly separate out open and * closed paths from the solution - OpenPathsFromPolyTree and ClosedPathsFromPolyTree. PolyTreeToPaths is also available to convert path data to a Paths * structure (irrespective of whether they're open or closed). * * There are several things to note about the solution paths returned: * - they aren't in any specific order * - they should never overlap or be self-intersecting (but see notes on rounding) * - holes will be oriented opposite outer polygons * - the solution fill type can be considered either EvenOdd or NonZero since it will comply with either filling rule * - polygons may rarely share a common edge (though this is now very rare as of version 6) * * The subjFillType and clipFillType parameters define the polygon fill rule to be applied to the polygons (ie closed paths) in the subject and clip * paths respectively. (It's usual though obviously not essential that both sets of polygons use the same fill rule.) * * execute can be called multiple times without reassigning subject and clip polygons (ie when different clipping operations are required on the * same polygon sets). * * @param clipType - Clip operation type * @param subjFillType - Fill type of the subject polygons * @param clipFillType - Fill type of the clip polygons * @return {PolyTree | undefined} - The solution or undefined if there was an error */ Clipper.prototype.executeToPolyTee = function (clipType, subjFillType, clipFillType) { var outNativePolyTree = new this._nativeLib.PolyTree(); try { var success = this._clipper.executePolyTreeWithFillTypes((0, nativeEnumConversion_1.clipTypeToNative)(this._nativeLib, clipType), outNativePolyTree, (0, nativeEnumConversion_1.polyFillTypeToNative)(this._nativeLib, subjFillType), (0, nativeEnumConversion_1.polyFillTypeToNative)(this._nativeLib, clipFillType)); if (!success) { return undefined; } else { return PolyTree_1.PolyTree.fromNativePolyTree(this._nativeLib, outNativePolyTree, true); // frees outNativePolyTree } } finally { if (!outNativePolyTree.isDeleted()) { outNativePolyTree.delete(); } } }; /** * Checks if the object has been disposed. * * @return {boolean} - true if disposed, false if not */ Clipper.prototype.isDisposed = function () { return this._clipper === undefined || this._clipper.isDeleted(); }; /** * Since this library uses WASM/ASM.JS internally for speed this means that you must dispose objects after you are done using them or mem leaks will occur. * (If the runtime supports FinalizationRegistry then this becomes non-mandatory, but still recommended). */ Clipper.prototype.dispose = function () { if (this._clipper) { this._clipper.delete(); nativeFinalizationRegistry_1.nativeFinalizationRegistry === null || nativeFinalizationRegistry_1.nativeFinalizationRegistry === void 0 ? void 0 : nativeFinalizationRegistry_1.nativeFinalizationRegistry.unregister(this); this._clipper = undefined; } }; return Clipper; }()); exports.Clipper = Clipper; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2xpcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9DbGlwcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7O0FBS0Esc0VBSXVDO0FBQ3ZDLGtFQUFxRjtBQUNyRiw4REFBNkQ7QUFHN0QsdUNBQXNDO0FBQ3RDLDJFQUEwRTtBQXNCMUU7SUFxRkU7Ozs7OztPQU1HO0lBQ0gsaUJBQ21CLFVBQW9DLEVBQ3JELFdBQW9DO1FBQXBDLDRCQUFBLEVBQUEsZ0JBQW9DO1FBRG5CLGVBQVUsR0FBVixVQUFVLENBQTBCO1FBR3JELElBQU0sZUFBZSxjQUNuQixnQkFBZ0IsRUFBRSxLQUFLLEVBQ3ZCLGNBQWMsRUFBRSxLQUFLLEVBQ3JCLGlCQUFpQixFQUFFLEtBQUssSUFDckIsV0FBVyxDQUNmLENBQUM7UUFFRixJQUFJLGlCQUFpQixHQUFHLENBQUMsQ0FBQztRQUMxQixJQUFJLGVBQWUsQ0FBQyxnQkFBZ0IsRUFBRTtZQUNwQyxpQkFBaUIsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUM7U0FDbkU7UUFDRCxJQUFJLGVBQWUsQ0FBQyxjQUFjLEVBQUU7WUFDbEMsaUJBQWlCLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDO1NBQ2xFO1FBQ0QsSUFBSSxlQUFlLENBQUMsaUJBQWlCLEVBQUU7WUFDckMsaUJBQWlCLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUM7U0FDckU7UUFFRCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzFELHVEQUEwQixhQUExQix1REFBMEIsdUJBQTFCLHVEQUEwQixDQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUMsQ0FBQztJQUNsRSxDQUFDO0lBM0dELHNCQUFJ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Q0FBQyxVQUFVLEVBQUUsWUFBWSxDQUFDLEVBQ25ELElBQUEsMkNBQW9CLEVBQUMsSUFBSSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FDcEQsQ0FBQztZQUNGLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ1osT0FBTyxTQUFTLENBQUM7YUFDbEI7aUJBQU07Z0JBQ0wsT0FBTyxtQkFBUSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQywwQkFBMEI7YUFDekc7U0FDRjtnQkFBUztZQUNSLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsRUFBRTtnQkFDbEMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLENBQUM7YUFDNUI7U0FDRjtJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsNEJBQVUsR0FBVjtRQUNFLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUNsRSxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gseUJBQU8sR0FBUDtRQUNFLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNqQixJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3ZCLHVEQUEwQixhQUExQix1REFBMEIsdUJBQTFCLHVEQUEwQixDQUFFLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUM3QyxJQUFJLENBQUMsUUFBUSxHQUFHLFNBQVMsQ0FBQztTQUMzQjtJQUNILENBQUM7SUFDSCxjQUFDO0FBQUQsQ0FBQyxBQXZXRCxJQXVXQztBQXZXWSwwQkFBTyIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24gKi9cbmltcG9ydCB7IENsaXBUeXBlLCBQb2x5RmlsbFR5cGUsIFBvbHlUeXBlIH0gZnJvbSBcIi4vZW51bXNcIjtcbmltcG9ydCB7IEludFJlY3QgfSBmcm9tIFwiLi9JbnRSZWN0XCI7XG5pbXBvcnQgeyBOYXRpdmVDbGlwcGVyIH0gZnJvbSBcIi4vbmF0aXZlL05hdGl2ZUNsaXBwZXJcIjtcbmltcG9ydCB7IE5hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZSB9IGZyb20gXCIuL25hdGl2ZS9OYXRpdmVDbGlwcGVyTGliSW5zdGFuY2VcIjtcbmltcG9ydCB7XG4gIGNsaXBUeXBlVG9OYXRpdmUsXG4gIHBvbHlGaWxsVHlwZVRvTmF0aXZlLFxuICBwb2x5VHlwZVRvTmF0aXZlLFxufSBmcm9tIFwiLi9uYXRpdmUvbmF0aXZlRW51bUNvbnZlcnNpb25cIjtcbmltcG9ydCB7IG5hdGl2ZVBhdGhzVG9QYXRocywgcGF0aHNUb05hdGl2ZVBhdGhzIH0gZnJvbSBcIi4vbmF0aXZlL1BhdGhzVG9OYXRpdmVQYXRoc1wiO1xuaW1wb3J0IHsgcGF0aFRvTmF0aXZlUGF0aCB9IGZyb20gXCIuL25hdGl2ZS9QYXRoVG9OYXRpdmVQYXRoXCI7XG5pbXBvcnQgeyBSZWFkb25seVBhdGggfSBmcm9tIFwiLi9QYXRoXCI7XG5pbXBvcnQgeyBQYXRocywgUmVhZG9ubHlQYXRocyB9IGZyb20gXCIuL1BhdGhzXCI7XG5pbXBvcnQgeyBQb2x5VHJlZSB9IGZyb20gXCIuL1BvbHlUcmVlXCI7XG5pbXBvcnQgeyBuYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeSB9IGZyb20gXCIuL25hdGl2ZUZpbmFsaXphdGlvblJlZ2lzdHJ5XCI7XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2xpcHBlckluaXRPcHRpb25zIHtcbiAgLyoqXG4gICAqIFdoZW4gdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gdHJ1ZSwgcG9seWdvbnMgcmV0dXJuZWQgaW4gdGhlIHNvbHV0aW9uIHBhcmFtZXRlciBvZiB0aGUgZXhlY3V0ZSgpIG1ldGhvZCB3aWxsIGhhdmUgb3JpZW50YXRpb25zIG9wcG9zaXRlIHRvIHRoZWlyIG5vcm1hbFxuICAgKiBvcmllbnRhdGlvbnMuXG4gICAqL1xuICByZXZlcnNlU29sdXRpb24/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBXaGVuIHRoaXMgcHJvcGVydHkgaXMgc2V0IHRvIHRydWUsIHBvbHlnb25zIHJldHVybmVkIGluIHRoZSBzb2x1dGlvbiBwYXJhbWV0ZXIgb2YgdGhlIGV4ZWN1dGUoKSBtZXRob2Qgd2lsbCBoYXZlIG9yaWVudGF0aW9ucyBvcHBvc2l0ZSB0byB0aGVpciBub3JtYWxcbiAgICogb3JpZW50YXRpb25zLlxuICAgKi9cbiAgc3RyaWN0bHlTaW1wbGU/OiBib29sZWFuO1xuXG4gIC8qKlxuICAgKiBCeSBkZWZhdWx0LCB3aGVuIHRocmVlIG9yIG1vcmUgdmVydGljZXMgYXJlIGNvbGxpbmVhciBpbiBpbnB1dCBwb2x5Z29ucyAoc3ViamVjdCBvciBjbGlwKSwgdGhlIENsaXBwZXIgb2JqZWN0IHJlbW92ZXMgdGhlICdpbm5lcicgdmVydGljZXMgYmVmb3JlXG4gICAqIGNsaXBwaW5nLiBXaGVuIGVuYWJsZWQgdGhlIHByZXNlcnZlQ29sbGluZWFyIHByb3BlcnR5IHByZXZlbnRzIHRoaXMgZGVmYXVsdCBiZWhhdmlvciB0byBhbGxvdyB0aGVzZSBpbm5lciB2ZXJ0aWNlcyB0byBhcHBlYXIgaW4gdGhlIHNvbHV0aW9uLlxuICAgKi9cbiAgcHJlc2VydmVDb2xsaW5lYXI/OiBib29sZWFuO1xufVxuXG5leHBvcnQgY2xhc3MgQ2xpcHBlciB7XG4gIHByaXZhdGUgX2NsaXBwZXI/OiBOYXRpdmVDbGlwcGVyO1xuXG4gIC8qKlxuICAgKiBCeSBkZWZhdWx0LCB3aGVuIHRocmVlIG9yIG1vcmUgdmVydGljZXMgYXJlIGNvbGxpbmVhciBpbiBpbnB1dCBwb2x5Z29ucyAoc3ViamVjdCBvciBjbGlwKSwgdGhlIENsaXBwZXIgb2JqZWN0IHJlbW92ZXMgdGhlICdpbm5lcicgdmVydGljZXMgYmVmb3JlXG4gICAqIGNsaXBwaW5nLiBXaGVuIGVuYWJsZWQgdGhlIHByZXNlcnZlQ29sbGluZWFyIHByb3BlcnR5IHByZXZlbnRzIHRoaXMgZGVmYXVsdCBiZWhhdmlvciB0byBhbGxvdyB0aGVzZSBpbm5lciB2ZXJ0aWNlcyB0byBhcHBlYXIgaW4gdGhlIHNvbHV0aW9uLlxuICAgKlxuICAgKiBAcmV0dXJuIHtib29sZWFufSAtIHRydWUgaWYgc2V0LCBmYWxzZSBvdGhlcndpc2VcbiAgICovXG4gIGdldCBwcmVzZXJ2ZUNvbGxpbmVhcigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlciEucHJlc2VydmVDb2xsaW5lYXI7XG4gIH1cblxuICAvKipcbiAgICogQnkgZGVmYXVsdCwgd2hlbiB0aHJlZSBvciBtb3JlIHZlcnRpY2VzIGFyZSBjb2xsaW5lYXIgaW4gaW5wdXQgcG9seWdvbnMgKHN1YmplY3Qgb3IgY2xpcCksIHRoZSBDbGlwcGVyIG9iamVjdCByZW1vdmVzIHRoZSAnaW5uZXInIHZlcnRpY2VzIGJlZm9yZVxuICAgKiBjbGlwcGluZy4gV2hlbiBlbmFibGVkIHRoZSBwcmVzZXJ2ZUNvbGxpbmVhciBwcm9wZXJ0eSBwcmV2ZW50cyB0aGlzIGRlZmF1bHQgYmVoYXZpb3IgdG8gYWxsb3cgdGhlc2UgaW5uZXIgdmVydGljZXMgdG8gYXBwZWFyIGluIHRoZSBzb2x1dGlvbi5cbiAgICpcbiAgICogQHBhcmFtIHZhbHVlIC0gdmFsdWUgdG8gc2V0XG4gICAqL1xuICBzZXQgcHJlc2VydmVDb2xsaW5lYXIodmFsdWU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9jbGlwcGVyIS5wcmVzZXJ2ZUNvbGxpbmVhciA9IHZhbHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZW4gdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gdHJ1ZSwgcG9seWdvbnMgcmV0dXJuZWQgaW4gdGhlIHNvbHV0aW9uIHBhcmFtZXRlciBvZiB0aGUgZXhlY3V0ZSgpIG1ldGhvZCB3aWxsIGhhdmUgb3JpZW50YXRpb25zIG9wcG9zaXRlIHRvIHRoZWlyIG5vcm1hbFxuICAgKiBvcmllbnRhdGlvbnMuXG4gICAqXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IC0gdHJ1ZSBpZiBzZXQsIGZhbHNlIG90aGVyd2lzZVxuICAgKi9cbiAgZ2V0IHJldmVyc2VTb2x1dGlvbigpOiBib29sZWFuIHtcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlciEucmV2ZXJzZVNvbHV0aW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFdoZW4gdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gdHJ1ZSwgcG9seWdvbnMgcmV0dXJuZWQgaW4gdGhlIHNvbHV0aW9uIHBhcmFtZXRlciBvZiB0aGUgZXhlY3V0ZSgpIG1ldGhvZCB3aWxsIGhhdmUgb3JpZW50YXRpb25zIG9wcG9zaXRlIHRvIHRoZWlyIG5vcm1hbFxuICAgKiBvcmllbnRhdGlvbnMuXG4gICAqXG4gICAqIEBwYXJhbSB2YWx1ZSAtIHZhbHVlIHRvIHNldFxuICAgKi9cbiAgc2V0IHJldmVyc2VTb2x1dGlvbih2YWx1ZTogYm9vbGVhbikge1xuICAgIHRoaXMuX2NsaXBwZXIhLnJldmVyc2VTb2x1dGlvbiA9IHZhbHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRlcm1pbm9sb2d5OlxuICAgKiAtIEEgc2ltcGxlIHBvbHlnb24gaXMgb25lIHRoYXQgZG9lcyBub3Qgc2VsZi1pbnRlcnNlY3QuXG4gICAqIC0gQSB3ZWFrbHkgc2ltcGxlIHBvbHlnb24gaXMgYSBzaW1wbGUgcG9seWdvbiB0aGF0IGNvbnRhaW5zICd0b3VjaGluZycgdmVydGljZXMsIG9yICd0b3VjaGluZycgZWRnZXMuXG4gICAqIC0gQSBzdHJpY3RseSBzaW1wbGUgcG9seWdvbiBpcyBhIHNpbXBsZSBwb2x5Z29uIHRoYXQgZG9lcyBub3QgY29udGFpbiAndG91Y2hpbmcnIHZlcnRpY2VzLCBvciAndG91Y2hpbmcnIGVkZ2VzLlxuICAgKlxuICAgKiBWZXJ0aWNlcyAndG91Y2gnIGlmIHRoZXkgc2hhcmUgdGhlIHNhbWUgY29vcmRpbmF0ZXMgKGFuZCBhcmUgbm90IGFkamFjZW50KS4gQW4gZWRnZSB0b3VjaGVzIGFub3RoZXIgaWYgb25lIG9mIGl0cyBlbmQgdmVydGljZXMgdG91Y2hlcyBhbm90aGVyIGVkZ2VcbiAgICogZXhjbHVkaW5nIGl0cyBhZGphY2VudCBlZGdlcywgb3IgaWYgdGhleSBhcmUgY28tbGluZWFyIGFuZCBvdmVybGFwcGluZyAoaW5jbHVkaW5nIGFkamFjZW50IGVkZ2VzKS5cbiAgICpcbiAgICogUG9seWdvbnMgcmV0dXJuZWQgYnkgY2xpcHBpbmcgb3BlcmF0aW9ucyAoc2VlIENsaXBwZXIuZXhlY3V0ZSgpKSBzaG91bGQgYWx3YXlzIGJlIHNpbXBsZSBwb2x5Z29ucy4gV2hlbiB0aGUgU3RyaWN0bHlTaW1wbHkgcHJvcGVydHkgaXMgZW5hYmxlZCxcbiAgICogcG9seWdvbnMgcmV0dXJuZWQgd2lsbCBiZSBzdHJpY3RseSBzaW1wbGUsIG90aGVyd2lzZSB0aGV5IG1heSBiZSB3ZWFrbHkgc2ltcGxlLiBJdCdzIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUgZW5zdXJpbmcgcG9seWdvbnMgYXJlIHN0cmljdGx5IHNpbXBsZVxuICAgKiBhbmQgc28gdGhpcyBwcm9wZXJ0eSBpcyBkaXNhYmxlZCBieSBkZWZhdWx0LlxuICAgKlxuICAgKiBOb3RlOiBUaGVyZSdzIGN1cnJlbnRseSBubyBndWFyYW50ZWUgdGhhdCBwb2x5Z29ucyB3aWxsIGJlIHN0cmljdGx5IHNpbXBsZSBzaW5jZSAnc2ltcGxpZnlpbmcnIGlzIHN0aWxsIGEgd29yayBpbiBwcm9ncmVzcy5cbiAgICpcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gLSB0cnVlIGlmIHNldCwgZmFsc2Ugb3RoZXJ3aXNlXG4gICAqL1xuICBnZXQgc3RyaWN0bHlTaW1wbGUoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2NsaXBwZXIhLnN0cmljdGx5U2ltcGxlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRlcm1pbm9sb2d5OlxuICAgKiAtIEEgc2ltcGxlIHBvbHlnb24gaXMgb25lIHRoYXQgZG9lcyBub3Qgc2VsZi1pbnRlcnNlY3QuXG4gICAqIC0gQSB3ZWFrbHkgc2ltcGxlIHBvbHlnb24gaXMgYSBzaW1wbGUgcG9seWdvbiB0aGF0IGNvbnRhaW5zICd0b3VjaGluZycgdmVydGljZXMsIG9yICd0b3VjaGluZycgZWRnZXMuXG4gICAqIC0gQSBzdHJpY3RseSBzaW1wbGUgcG9seWdvbiBpcyBhIHNpbXBsZSBwb2x5Z29uIHRoYXQgZG9lcyBub3QgY29udGFpbiAndG91Y2hpbmcnIHZlcnRpY2VzLCBvciAndG91Y2hpbmcnIGVkZ2VzLlxuICAgKlxuICAgKiBWZXJ0aWNlcyAndG91Y2gnIGlmIHRoZXkgc2hhcmUgdGhlIHNhbWUgY29vcmRpbmF0ZXMgKGFuZCBhcmUgbm90IGFkamFjZW50KS4gQW4gZWRnZSB0b3VjaGVzIGFub3RoZXIgaWYgb25lIG9mIGl0cyBlbmQgdmVydGljZXMgdG91Y2hlcyBhbm90aGVyIGVkZ2VcbiAgICogZXhjbHVkaW5nIGl0cyBhZGphY2VudCBlZGdlcywgb3IgaWYgdGhleSBhcmUgY28tbGluZWFyIGFuZCBvdmVybGFwcGluZyAoaW5jbHVkaW5nIGFkamFjZW50IGVkZ2VzKS5cbiAgICpcbiAgICogUG9seWdvbnMgcmV0dXJuZWQgYnkgY2xpcHBpbmcgb3BlcmF0aW9ucyAoc2VlIENsaXBwZXIuZXhlY3V0ZSgpKSBzaG91bGQgYWx3YXlzIGJlIHNpbXBsZSBwb2x5Z29ucy4gV2hlbiB0aGUgU3RyaWN0bHlTaW1wbHkgcHJvcGVydHkgaXMgZW5hYmxlZCxcbiAgICogcG9seWdvbnMgcmV0dXJuZWQgd2lsbCBiZSBzdHJpY3RseSBzaW1wbGUsIG90aGVyd2lzZSB0aGV5IG1heSBiZSB3ZWFrbHkgc2ltcGxlLiBJdCdzIGNvbXB1dGF0aW9uYWxseSBleHBlbnNpdmUgZW5zdXJpbmcgcG9seWdvbnMgYXJlIHN0cmljdGx5IHNpbXBsZVxuICAgKiBhbmQgc28gdGhpcyBwcm9wZXJ0eSBpcyBkaXNhYmxlZCBieSBkZWZhdWx0LlxuICAgKlxuICAgKiBOb3RlOiBUaGVyZSdzIGN1cnJlbnRseSBubyBndWFyYW50ZWUgdGhhdCBwb2x5Z29ucyB3aWxsIGJlIHN0cmljdGx5IHNpbXBsZSBzaW5jZSAnc2ltcGxpZnlpbmcnIGlzIHN0aWxsIGEgd29yayBpbiBwcm9ncmVzcy5cbiAgICpcbiAgICogQHBhcmFtIHZhbHVlIC0gdmFsdWUgdG8gc2V0XG4gICAqL1xuICBzZXQgc3RyaWN0bHlTaW1wbGUodmFsdWU6IGJvb2xlYW4pIHtcbiAgICB0aGlzLl9jbGlwcGVyIS5zdHJpY3RseVNpbXBsZSA9IHZhbHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoZSBDbGlwcGVyIGNvbnN0cnVjdG9yIGNyZWF0ZXMgYW4gaW5zdGFuY2Ugb2YgdGhlIENsaXBwZXIgY2xhc3MuIE9uZSBvciBtb3JlIEluaXRPcHRpb25zIG1heSBiZSBwYXNzZWQgYXMgYSBwYXJhbWV0ZXIgdG8gc2V0IHRoZSBjb3JyZXNwb25kaW5nIHByb3BlcnRpZXMuXG4gICAqIChUaGVzZSBwcm9wZXJ0aWVzIGNhbiBzdGlsbCBiZSBzZXQgb3IgcmVzZXQgYWZ0ZXIgY29uc3RydWN0aW9uLilcbiAgICpcbiAgICogQHBhcmFtIF9uYXRpdmVMaWJcbiAgICogQHBhcmFtIGluaXRPcHRpb25zXG4gICAqL1xuICBjb25zdHJ1Y3RvcihcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9uYXRpdmVMaWI6IE5hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZSxcbiAgICBpbml0T3B0aW9uczogQ2xpcHBlckluaXRPcHRpb25zID0ge31cbiAgKSB7XG4gICAgY29uc3QgcmVhbEluaXRPcHRpb25zID0ge1xuICAgICAgcmV2ZXJzZVNvbHV0aW9uczogZmFsc2UsXG4gICAgICBzdHJpY3RseVNpbXBsZTogZmFsc2UsXG4gICAgICBwcmVzZXJ2ZUNvbGxpbmVhcjogZmFsc2UsXG4gICAgICAuLi5pbml0T3B0aW9ucyxcbiAgICB9O1xuXG4gICAgbGV0IG5hdGl2ZUluaXRPcHRpb25zID0gMDtcbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnJldmVyc2VTb2x1dGlvbnMpIHtcbiAgICAgIG5hdGl2ZUluaXRPcHRpb25zICs9IF9uYXRpdmVMaWIuSW5pdE9wdGlvbnMuUmV2ZXJzZVNvbHV0aW9uLnZhbHVlO1xuICAgIH1cbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnN0cmljdGx5U2ltcGxlKSB7XG4gICAgICBuYXRpdmVJbml0T3B0aW9ucyArPSBfbmF0aXZlTGliLkluaXRPcHRpb25zLlN0cmljdGx5U2ltcGxlLnZhbHVlO1xuICAgIH1cbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnByZXNlcnZlQ29sbGluZWFyKSB7XG4gICAgICBuYXRpdmVJbml0T3B0aW9ucyArPSBfbmF0aXZlTGliLkluaXRPcHRpb25zLlByZXNlcnZlQ29sbGluZWFyLnZhbHVlO1xuICAgIH1cblxuICAgIHRoaXMuX2NsaXBwZXIgPSBuZXcgX25hdGl2ZUxpYi5DbGlwcGVyKG5hdGl2ZUluaXRPcHRpb25zKTtcbiAgICBuYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeT8ucmVnaXN0ZXIodGhpcywgdGhpcy5fY2xpcHBlciwgdGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogQW55IG51bWJlciBvZiBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGNhbiBiZSBhZGRlZCB0byBhIGNsaXBwaW5nIHRhc2ssIGVpdGhlciBpbmRpdmlkdWFsbHkgdmlhIHRoZSBhZGRQYXRoKCkgbWV0aG9kLCBvciBhcyBncm91cHMgdmlhIHRoZSBhZGRQYXRocygpXG4gICAqIG1ldGhvZCwgb3IgZXZlbiB1c2luZyBib3RoIG1ldGhvZHMuXG4gICAqXG4gICAqICdTdWJqZWN0JyBwYXRocyBtYXkgYmUgZWl0aGVyIG9wZW4gKGxpbmVzKSBvciBjbG9zZWQgKHBvbHlnb25zKSBvciBldmVuIGEgbWl4dHVyZSBvZiBib3RoLCBidXQgJ2NsaXBwaW5nJyBwYXRocyBtdXN0IGFsd2F5cyBiZSBjbG9zZWQuIENsaXBwZXIgYWxsb3dzXG4gICAqIHBvbHlnb25zIHRvIGNsaXAgYm90aCBsaW5lcyBhbmQgb3RoZXIgcG9seWdvbnMsIGJ1dCBkb2Vzbid0IGFsbG93IGxpbmVzIHRvIGNsaXAgZWl0aGVyIGxpbmVzIG9yIHBvbHlnb25zLlxuICAgKlxuICAgKiBXaXRoIGNsb3NlZCBwYXRocywgb3JpZW50YXRpb24gc2hvdWxkIGNvbmZvcm0gd2l0aCB0aGUgZmlsbGluZyBydWxlIHRoYXQgd2lsbCBiZSBwYXNzZWQgdmlhIENsaXBwZXIncyBleGVjdXRlIG1ldGhvZC5cbiAgICpcbiAgICogUGF0aCBDb29yZGluYXRlIHJhbmdlOlxuICAgKiBQYXRoIGNvb3JkaW5hdGVzIG11c3QgYmUgYmV0d2VlbiDCsSA5MDA3MTk5MjU0NzQwOTkxLCBvdGhlcndpc2UgYSByYW5nZSBlcnJvciB3aWxsIGJlIHRocm93biB3aGVuIGF0dGVtcHRpbmcgdG8gYWRkIHRoZSBwYXRoIHRvIHRoZSBDbGlwcGVyIG9iamVjdC5cbiAgICogSWYgY29vcmRpbmF0ZXMgY2FuIGJlIGtlcHQgYmV0d2VlbiDCsSAweDNGRkZGRkZGICjCsSAxLjBlKzkpLCBhIG1vZGVzdCBpbmNyZWFzZSBpbiBwZXJmb3JtYW5jZSAoYXBwcm94LiAxNS0yMCUpIG92ZXIgdGhlIGxhcmdlciByYW5nZSBjYW4gYmUgYWNoaWV2ZWQgYnlcbiAgICogYXZvaWRpbmcgbGFyZ2UgaW50ZWdlciBtYXRoLlxuICAgKlxuICAgKiBSZXR1cm4gVmFsdWU6XG4gICAqIFRoZSBmdW5jdGlvbiB3aWxsIHJldHVybiBmYWxzZSBpZiB0aGUgcGF0aCBpcyBpbnZhbGlkIGZvciBjbGlwcGluZy4gQSBwYXRoIGlzIGludmFsaWQgZm9yIGNsaXBwaW5nIHdoZW46XG4gICAqIC0gaXQgaGFzIGxlc3MgdGhhbiAyIHZlcnRpY2VzXG4gICAqIC0gaXQgaGFzIDIgdmVydGljZXMgYnV0IGlzIG5vdCBhbiBvcGVuIHBhdGhcbiAgICogLSB0aGUgdmVydGljZXMgYXJlIGFsbCBjby1saW5lYXIgYW5kIGl0IGlzIG5vdCBhbiBvcGVuIHBhdGhcbiAgICpcbiAgICogQHBhcmFtIHBhdGggLSBQYXRoIHRvIGFkZFxuICAgKiBAcGFyYW0gcG9seVR5cGUgLSBQb2x5Z29uIHR5cGVcbiAgICogQHBhcmFtIGNsb3NlZCAtIElmIHRoZSBwYXRoIGlzIGNsb3NlZFxuICAgKi9cbiAgYWRkUGF0aChwYXRoOiBSZWFkb25seVBhdGgsIHBvbHlUeXBlOiBQb2x5VHlwZSwgY2xvc2VkOiBib29sZWFuKTogYm9vbGVhbiB7XG4gICAgY29uc3QgbmF0aXZlUGF0aCA9IHBhdGhUb05hdGl2ZVBhdGgodGhpcy5fbmF0aXZlTGliLCBwYXRoKTtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NsaXBwZXIhLmFkZFBhdGgoXG4gICAgICAgIG5hdGl2ZVBhdGgsXG4gICAgICAgIHBvbHlUeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBwb2x5VHlwZSksXG4gICAgICAgIGNsb3NlZFxuICAgICAgKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgbmF0aXZlUGF0aC5kZWxldGUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQW55IG51bWJlciBvZiBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGNhbiBiZSBhZGRlZCB0byBhIGNsaXBwaW5nIHRhc2ssIGVpdGhlciBpbmRpdmlkdWFsbHkgdmlhIHRoZSBhZGRQYXRoKCkgbWV0aG9kLCBvciBhcyBncm91cHMgdmlhIHRoZSBhZGRQYXRocygpXG4gICAqIG1ldGhvZCwgb3IgZXZlbiB1c2luZyBib3RoIG1ldGhvZHMuXG4gICAqXG4gICAqICdTdWJqZWN0JyBwYXRocyBtYXkgYmUgZWl0aGVyIG9wZW4gKGxpbmVzKSBvciBjbG9zZWQgKHBvbHlnb25zKSBvciBldmVuIGEgbWl4dHVyZSBvZiBib3RoLCBidXQgJ2NsaXBwaW5nJyBwYXRocyBtdXN0IGFsd2F5cyBiZSBjbG9zZWQuIENsaXBwZXIgYWxsb3dzXG4gICAqIHBvbHlnb25zIHRvIGNsaXAgYm90aCBsaW5lcyBhbmQgb3RoZXIgcG9seWdvbnMsIGJ1dCBkb2Vzbid0IGFsbG93IGxpbmVzIHRvIGNsaXAgZWl0aGVyIGxpbmVzIG9yIHBvbHlnb25zLlxuICAgKlxuICAgKiBXaXRoIGNsb3NlZCBwYXRocywgb3JpZW50YXRpb24gc2hvdWxkIGNvbmZvcm0gd2l0aCB0aGUgZmlsbGluZyBydWxlIHRoYXQgd2lsbCBiZSBwYXNzZWQgdmlhIENsaXBwZXIncyBleGVjdXRlIG1ldGhvZC5cbiAgICpcbiAgICogUGF0aCBDb29yZGluYXRlIHJhbmdlOlxuICAgKiBQYXRoIGNvb3JkaW5hdGVzIG11c3QgYmUgYmV0d2VlbiDCsSA5MDA3MTk5MjU0NzQwOTkxLCBvdGhlcndpc2UgYSByYW5nZSBlcnJvciB3aWxsIGJlIHRocm93biB3aGVuIGF0dGVtcHRpbmcgdG8gYWRkIHRoZSBwYXRoIHRvIHRoZSBDbGlwcGVyIG9iamVjdC5cbiAgICogSWYgY29vcmRpbmF0ZXMgY2FuIGJlIGtlcHQgYmV0d2VlbiDCsSAweDNGRkZGRkZGICjCsSAxLjBlKzkpLCBhIG1vZGVzdCBpbmNyZWFzZSBpbiBwZXJmb3JtYW5jZSAoYXBwcm94LiAxNS0yMCUpIG92ZXIgdGhlIGxhcmdlciByYW5nZSBjYW4gYmUgYWNoaWV2ZWRcbiAgICogYnkgYXZvaWRpbmcgbGFyZ2UgaW50ZWdlciBtYXRoLlxuICAgKlxuICAgKiBSZXR1cm4gVmFsdWU6XG4gICAqIFRoZSBmdW5jdGlvbiB3aWxsIHJldHVybiBmYWxzZSBpZiB0aGUgcGF0aCBpcyBpbnZhbGlkIGZvciBjbGlwcGluZy4gQSBwYXRoIGlzIGludmFsaWQgZm9yIGNsaXBwaW5nIHdoZW46XG4gICAqIC0gaXQgaGFzIGxlc3MgdGhhbiAyIHZlcnRpY2VzXG4gICAqIC0gaXQgaGFzIDIgdmVydGljZXMgYnV0IGlzIG5vdCBhbiBvcGVuIHBhdGhcbiAgICogLSB0aGUgdmVydGljZXMgYXJlIGFsbCBjby1saW5lYXIgYW5kIGl0IGlzIG5vdCBhbiBvcGVuIHBhdGhcbiAgICpcbiAgICogQHBhcmFtIHBhdGhzIC0gUGF0aHMgdG8gYWRkXG4gICAqIEBwYXJhbSBwb2x5VHlwZSAtIFBhdGhzIHBvbHlnb24gdHlwZVxuICAgKiBAcGFyYW0gY2xvc2VkIC0gSWYgYWxsIHRoZSBpbm5lciBwYXRocyBhcmUgY2xvc2VkXG4gICAqL1xuICBhZGRQYXRocyhwYXRoczogUmVhZG9ubHlQYXRocywgcG9seVR5cGU6IFBvbHlUeXBlLCBjbG9zZWQ6IGJvb2xlYW4pOiBib29sZWFuIHtcbiAgICBjb25zdCBuYXRpdmVQYXRocyA9IHBhdGhzVG9OYXRpdmVQYXRocyh0aGlzLl9uYXRpdmVMaWIsIHBhdGhzKTtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHRoaXMuX2NsaXBwZXIhLmFkZFBhdGhzKFxuICAgICAgICBuYXRpdmVQYXRocyxcbiAgICAgICAgcG9seVR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIHBvbHlUeXBlKSxcbiAgICAgICAgY2xvc2VkXG4gICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBuYXRpdmVQYXRocy5kZWxldGUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhlIENsZWFyIG1ldGhvZCByZW1vdmVzIGFueSBleGlzdGluZyBzdWJqZWN0IGFuZCBjbGlwIHBvbHlnb25zIGFsbG93aW5nIHRoZSBDbGlwcGVyIG9iamVjdCB0byBiZSByZXVzZWQgZm9yIGNsaXBwaW5nIG9wZXJhdGlvbnMgb24gZGlmZmVyZW50IHBvbHlnb24gc2V0cy5cbiAgICovXG4gIGNsZWFyKCk6IHZvaWQge1xuICAgIHRoaXMuX2NsaXBwZXIhLmNsZWFyKCk7XG4gIH1cblxuICAvKipcbiAgICogVGhpcyBtZXRob2QgcmV0dXJucyB0aGUgYXhpcy1hbGlnbmVkIGJvdW5kaW5nIHJlY3RhbmdsZSBvZiBhbGwgcG9seWdvbnMgdGhhdCBoYXZlIGJlZW4gYWRkZWQgdG8gdGhlIENsaXBwZXIgb2JqZWN0LlxuICAgKlxuICAgKiBAcmV0dXJuIHt7bGVmdDogbnVtYmVyLCByaWdodDogbnVtYmVyLCB0b3A6IG51bWJlciwgYm90dG9tOiBudW1iZXJ9fSAtIEJvdW5kc1xuICAgKi9cbiAgZ2V0Qm91bmRzKCk6IEludFJlY3Qge1xuICAgIGNvbnN0IG5hdGl2ZUJvdW5kcyA9IHRoaXMuX2NsaXBwZXIhLmdldEJvdW5kcygpO1xuICAgIGNvbnN0IHJlY3QgPSB7XG4gICAgICBsZWZ0OiBuYXRpdmVCb3VuZHMubGVmdCxcbiAgICAgIHJpZ2h0OiBuYXRpdmVCb3VuZHMucmlnaHQsXG4gICAgICB0b3A6IG5hdGl2ZUJvdW5kcy50b3AsXG4gICAgICBib3R0b206IG5hdGl2ZUJvdW5kcy5ib3R0b20sXG4gICAgfTtcbiAgICBuYXRpdmVCb3VuZHMuZGVsZXRlKCk7XG4gICAgcmV0dXJuIHJlY3Q7XG4gIH1cblxuICAvKipcbiAgICogT25jZSBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGhhdmUgYmVlbiBhc3NpZ25lZCAodmlhIGFkZFBhdGggYW5kL29yIGFkZFBhdGhzKSwgZXhlY3V0ZSBjYW4gdGhlbiBwZXJmb3JtIHRoZSBjbGlwcGluZyBvcGVyYXRpb24gKGludGVyc2VjdGlvbiwgdW5pb24sXG4gICAqIGRpZmZlcmVuY2Ugb3IgWE9SKSBzcGVjaWZpZWQgYnkgdGhlIGNsaXBUeXBlIHBhcmFtZXRlci5cbiAgICpcbiAgICogVGhlIHNvbHV0aW9uIHBhcmFtZXRlciBpbiB0aGlzIGNhc2UgaXMgYSBQYXRocyBvciBQb2x5VHJlZSBzdHJ1Y3R1cmUuIFRoZSBQYXRocyBzdHJ1Y3R1cmUgaXMgc2ltcGxlciB0aGFuIHRoZSBQb2x5VHJlZSBzdHJ1Y3R1cmUuIEJlY2F1c2Ugb2YgdGhpcyBpdCBpc1xuICAgKiBxdWlja2VyIHRvIHBvcHVsYXRlIGFuZCBoZW5jZSBjbGlwcGluZyBwZXJmb3JtYW5jZSBpcyBhIGxpdHRsZSBiZXR0ZXIgKGl0J3Mgcm91Z2hseSAxMCUgZmFzdGVyKS4gSG93ZXZlciwgdGhlIFBvbHlUcmVlIGRhdGEgc3RydWN0dXJlIHByb3ZpZGVzIG1vcmVcbiAgICogaW5mb3JtYXRpb24gYWJvdXQgdGhlIHJldHVybmVkIHBhdGhzIHdoaWNoIG1heSBiZSBpbXBvcnRhbnQgdG8gdXNlcnMuIEZpcnN0bHksIHRoZSBQb2x5VHJlZSBzdHJ1Y3R1cmUgcHJlc2VydmVzIG5lc3RlZCBwYXJlbnQtY2hpbGQgcG9seWdvbiByZWxhdGlvbnNoaXBzXG4gICAqIChpZSBvdXRlciBwb2x5Z29ucyBvd25pbmcvY29udGFpbmluZyBob2xlcyBhbmQgaG9sZXMgb3duaW5nL2NvbnRhaW5pbmcgb3RoZXIgb3V0ZXIgcG9seWdvbnMgZXRjKS4gQWxzbywgb25seSB0aGUgUG9seVRyZWUgc3RydWN0dXJlIGNhbiBkaWZmZXJlbnRpYXRlXG4gICAqIGJldHdlZW4gb3BlbiBhbmQgY2xvc2VkIHBhdGhzIHNpbmNlIGVhY2ggUG9seU5vZGUgaGFzIGFuIElzT3BlbiBwcm9wZXJ0eS4gKFRoZSBQYXRoIHN0cnVjdHVyZSBoYXMgbm8gbWVtYmVyIGluZGljYXRpbmcgd2hldGhlciBpdCdzIG9wZW4gb3IgY2xvc2VkLilcbiAgICogRm9yIHRoaXMgcmVhc29uLCB3aGVuIG9wZW4gcGF0aHMgYXJlIHBhc3NlZCB0byBhIENsaXBwZXIgb2JqZWN0LCB0aGUgdXNlciBtdXN0IHVzZSBhIFBvbHlUcmVlIG9iamVjdCBhcyB0aGUgc29sdXRpb24gcGFyYW1ldGVyLCBvdGhlcndpc2UgYW4gZXhjZXB0aW9uXG4gICAqIHdpbGwgYmUgcmFpc2VkLlxuICAgKlxuICAgKiBXaGVuIGEgUG9seVRyZWUgb2JqZWN0IGlzIHVzZWQgaW4gYSBjbGlwcGluZyBvcGVyYXRpb24gb24gb3BlbiBwYXRocywgdHdvIGFuY2lsbGlhcnkgZnVuY3Rpb25zIGhhdmUgYmVlbiBwcm92aWRlZCB0byBxdWlja2x5IHNlcGFyYXRlIG91dCBvcGVuIGFuZFxuICAgKiBjbG9zZWQgcGF0aHMgZnJvbSB0aGUgc29sdXRpb24gLSBPcGVuUGF0aHNGcm9tUG9seVRyZWUgYW5kIENsb3NlZFBhdGhzRnJvbVBvbHlUcmVlLiBQb2x5VHJlZVRvUGF0aHMgaXMgYWxzbyBhdmFpbGFibGUgdG8gY29udmVydCBwYXRoIGRhdGEgdG8gYSBQYXRoc1xuICAgKiBzdHJ1Y3R1cmUgKGlycmVzcGVjdGl2ZSBvZiB3aGV0aGVyIHRoZXkncmUgb3BlbiBvciBjbG9zZWQpLlxuICAgKlxuICAgKiBUaGVyZSBhcmUgc2V2ZXJhbCB0aGluZ3MgdG8gbm90ZSBhYm91dCB0aGUgc29sdXRpb24gcGF0aHMgcmV0dXJuZWQ6XG4gICAqIC0gdGhleSBhcmVuJ3QgaW4gYW55IHNwZWNpZmljIG9yZGVyXG4gICAqIC0gdGhleSBzaG91bGQgbmV2ZXIgb3ZlcmxhcCBvciBiZSBzZWxmLWludGVyc2VjdGluZyAoYnV0IHNlZSBub3RlcyBvbiByb3VuZGluZylcbiAgICogLSBob2xlcyB3aWxsIGJlIG9yaWVudGVkIG9wcG9zaXRlIG91dGVyIHBvbHlnb25zXG4gICAqIC0gdGhlIHNvbHV0aW9uIGZpbGwgdHlwZSBjYW4gYmUgY29uc2lkZXJlZCBlaXRoZXIgRXZlbk9kZCBvciBOb25aZXJvIHNpbmNlIGl0IHdpbGwgY29tcGx5IHdpdGggZWl0aGVyIGZpbGxpbmcgcnVsZVxuICAgKiAtIHBvbHlnb25zIG1heSByYXJlbHkgc2hhcmUgYSBjb21tb24gZWRnZSAodGhvdWdoIHRoaXMgaXMgbm93IHZlcnkgcmFyZSBhcyBvZiB2ZXJzaW9uIDYpXG4gICAqXG4gICAqIFRoZSBzdWJqRmlsbFR5cGUgYW5kIGNsaXBGaWxsVHlwZSBwYXJhbWV0ZXJzIGRlZmluZSB0aGUgcG9seWdvbiBmaWxsIHJ1bGUgdG8gYmUgYXBwbGllZCB0byB0aGUgcG9seWdvbnMgKGllIGNsb3NlZCBwYXRocykgaW4gdGhlIHN1YmplY3QgYW5kIGNsaXBcbiAgICogcGF0aHMgcmVzcGVjdGl2ZWx5LiAoSXQncyB1c3VhbCB0aG91Z2ggb2J2aW91c2x5IG5vdCBlc3NlbnRpYWwgdGhhdCBib3RoIHNldHMgb2YgcG9seWdvbnMgdXNlIHRoZSBzYW1lIGZpbGwgcnVsZS4pXG4gICAqXG4gICAqIGV4ZWN1dGUgY2FuIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcyB3aXRob3V0IHJlYXNzaWduaW5nIHN1YmplY3QgYW5kIGNsaXAgcG9seWdvbnMgKGllIHdoZW4gZGlmZmVyZW50IGNsaXBwaW5nIG9wZXJhdGlvbnMgYXJlIHJlcXVpcmVkIG9uIHRoZVxuICAgKiBzYW1lIHBvbHlnb24gc2V0cykuXG4gICAqXG4gICAqIEBwYXJhbSBjbGlwVHlwZSAtIENsaXAgb3BlcmF0aW9uIHR5cGVcbiAgICogQHBhcmFtIHN1YmpGaWxsVHlwZSAtIEZpbGwgdHlwZSBvZiB0aGUgc3ViamVjdCBwb2x5Z29uc1xuICAgKiBAcGFyYW0gY2xpcEZpbGxUeXBlIC0gRmlsbCB0eXBlIG9mIHRoZSBjbGlwIHBvbHlnb25zXG4gICAqIEBwYXJhbSBjbGVhbkRpc3RhbmNlIC0gQ2xlYW4gZGlzdGFuY2Ugb3ZlciB0aGUgb3V0cHV0LCBvciB1bmRlZmluZWQgZm9yIG5vIGNsZWFuaW5nLlxuICAgKiBAcmV0dXJuIHtQYXRocyB8IHVuZGVmaW5lZH0gLSBUaGUgc29sdXRpb24gb3IgdW5kZWZpbmVkIGlmIHRoZXJlIHdhcyBhbiBlcnJvclxuICAgKi9cbiAgZXhlY3V0ZVRvUGF0aHMoXG4gICAgY2xpcFR5cGU6IENsaXBUeXBlLFxuICAgIHN1YmpGaWxsVHlwZTogUG9seUZpbGxUeXBlLFxuICAgIGNsaXBGaWxsVHlwZTogUG9seUZpbGxUeXBlLFxuICAgIGNsZWFuRGlzdGFuY2U6IG51bWJlciB8IHVuZGVmaW5lZFxuICApOiBQYXRocyB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3Qgb3V0TmF0aXZlUGF0aHMgPSBuZXcgdGhpcy5fbmF0aXZlTGliLlBhdGhzKCk7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IHN1Y2Nlc3MgPSB0aGlzLl9jbGlwcGVyIS5leGVjdXRlUGF0aHNXaXRoRmlsbFR5cGVzKFxuICAgICAgICBjbGlwVHlwZVRvTmF0aXZlKHRoaXMuX25hdGl2ZUxpYiwgY2xpcFR5cGUpLFxuICAgICAgICBvdXROYXRpdmVQYXRocyxcbiAgICAgICAgcG9seUZpbGxUeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBzdWJqRmlsbFR5cGUpLFxuICAgICAgICBwb2x5RmlsbFR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIGNsaXBGaWxsVHlwZSlcbiAgICAgICk7XG4gICAgICBpZiAoIXN1Y2Nlc3MpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmIChjbGVhbkRpc3RhbmNlICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICB0aGlzLl9uYXRpdmVMaWIuY2xlYW5Qb2x5Z29ucyhvdXROYXRpdmVQYXRocywgY2xlYW5EaXN0YW5jZSk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5hdGl2ZVBhdGhzVG9QYXRocyh0aGlzLl9uYXRpdmVMaWIsIG91dE5hdGl2ZVBhdGhzLCB0cnVlKTsgLy8gZnJlZXMgb3V0TmF0aXZlUGF0aHNcbiAgICAgIH1cbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKCFvdXROYXRpdmVQYXRocy5pc0RlbGV0ZWQoKSkge1xuICAgICAgICBvdXROYXRpdmVQYXRocy5kZWxldGUoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogT25jZSBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGhhdmUgYmVlbiBhc3NpZ25lZCAodmlhIGFkZFBhdGggYW5kL29yIGFkZFBhdGhzKSwgZXhlY3V0ZSBjYW4gdGhlbiBwZXJmb3JtIHRoZSBjbGlwcGluZyBvcGVyYXRpb24gKGludGVyc2VjdGlvbiwgdW5pb24sXG4gICAqIGRpZmZlcmVuY2Ugb3IgWE9SKSBzcGVjaWZpZWQgYnkgdGhlIGNsaXBUeXBlIHBhcmFtZXRlci5cbiAgICpcbiAgICogVGhlIHNvbHV0aW9uIHBhcmFtZXRlciBjYW4gYmUgZWl0aGVyIGEgUGF0aHMgb3IgUG9seVRyZWUgc3RydWN0dXJlLiBUaGUgUGF0aHMgc3RydWN0dXJlIGlzIHNpbXBsZXIgdGhhbiB0aGUgUG9seVRyZWUgc3RydWN0dXJlLiBCZWNhdXNlIG9mIHRoaXMgaXQgaXNcbiAgICogcXVpY2tlciB0byBwb3B1bGF0ZSBhbmQgaGVuY2UgY2xpcHBpbmcgcGVyZm9ybWFuY2UgaXMgYSBsaXR0bGUgYmV0dGVyIChpdCdzIHJvdWdobHkgMTAlIGZhc3RlcikuIEhvd2V2ZXIsIHRoZSBQb2x5VHJlZSBkYXRhIHN0cnVjdHVyZSBwcm92aWRlcyBtb3JlXG4gICAqIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZXR1cm5lZCBwYXRocyB3aGljaCBtYXkgYmUgaW1wb3J0YW50IHRvIHVzZXJzLiBGaXJzdGx5LCB0aGUgUG9seVRyZWUgc3RydWN0dXJlIHByZXNlcnZlcyBuZXN0ZWQgcGFyZW50LWNoaWxkIHBvbHlnb24gcmVsYXRpb25zaGlwc1xuICAgKiAoaWUgb3V0ZXIgcG9seWdvbnMgb3duaW5nL2NvbnRhaW5pbmcgaG9sZXMgYW5kIGhvbGVzIG93bmluZy9jb250YWluaW5nIG90aGVyIG91dGVyIHBvbHlnb25zIGV0YykuIEFsc28sIG9ubHkgdGhlIFBvbHlUcmVlIHN0cnVjdHVyZSBjYW4gZGlmZmVyZW50aWF0ZVxuICAgKiBiZXR3ZWVuIG9wZW4gYW5kIGNsb3NlZCBwYXRocyBzaW5jZSBlYWNoIFBvbHlOb2RlIGhhcyBhbiBJc09wZW4gcHJvcGVydHkuIChUaGUgUGF0aCBzdHJ1Y3R1cmUgaGFzIG5vIG1lbWJlciBpbmRpY2F0aW5nIHdoZXRoZXIgaXQncyBvcGVuIG9yIGNsb3NlZC4pXG4gICAqIEZvciB0aGlzIHJlYXNvbiwgd2hlbiBvcGVuIHBhdGhzIGFyZSBwYXNzZWQgdG8gYSBDbGlwcGVyIG9iamVjdCwgdGhlIHVzZXIgbXVzdCB1c2UgYSBQb2x5VHJlZSBvYmplY3QgYXMgdGhlIHNvbHV0aW9uIHBhcmFtZXRlciwgb3RoZXJ3aXNlIGFuIGV4Y2VwdGlvblxuICAgKiB3aWxsIGJlIHJhaXNlZC5cbiAgICpcbiAgICogV2hlbiBhIFBvbHlUcmVlIG9iamVjdCBpcyB1c2VkIGluIGEgY2xpcHBpbmcgb3BlcmF0aW9uIG9uIG9wZW4gcGF0aHMsIHR3byBhbmNpbGxpYXJ5IGZ1bmN0aW9ucyBoYXZlIGJlZW4gcHJvdmlkZWQgdG8gcXVpY2tseSBzZXBhcmF0ZSBvdXQgb3BlbiBhbmRcbiAgICogY2xvc2VkIHBhdGhzIGZyb20gdGhlIHNvbHV0aW9uIC0gT3BlblBhdGhzRnJvbVBvbHlUcmVlIGFuZCBDbG9zZWRQYXRoc0Zyb21Qb2x5VHJlZS4gUG9seVRyZWVUb1BhdGhzIGlzIGFsc28gYXZhaWxhYmxlIHRvIGNvbnZlcnQgcGF0aCBkYXRhIHRvIGEgUGF0aHNcbiAgICogc3RydWN0dXJlIChpcnJlc3BlY3RpdmUgb2Ygd2hldGhlciB0aGV5J3JlIG9wZW4gb3IgY2xvc2VkKS5cbiAgICpcbiAgICogVGhlcmUgYXJlIHNldmVyYWwgdGhpbmdzIHRvIG5vdGUgYWJvdXQgdGhlIHNvbHV0aW9uIHBhdGhzIHJldHVybmVkOlxuICAgKiAtIHRoZXkgYXJlbid0IGluIGFueSBzcGVjaWZpYyBvcmRlclxuICAgKiAtIHRoZXkgc2hvdWxkIG5ldmVyIG92ZXJsYXAgb3IgYmUgc2VsZi1pbnRlcnNlY3RpbmcgKGJ1dCBzZWUgbm90ZXMgb24gcm91bmRpbmcpXG4gICAqIC0gaG9sZXMgd2lsbCBiZSBvcmllbnRlZCBvcHBvc2l0ZSBvdXRlciBwb2x5Z29uc1xuICAgKiAtIHRoZSBzb2x1dGlvbiBmaWxsIHR5cGUgY2FuIGJlIGNvbnNpZGVyZWQgZWl0aGVyIEV2ZW5PZGQgb3IgTm9uWmVybyBzaW5jZSBpdCB3aWxsIGNvbXBseSB3aXRoIGVpdGhlciBmaWxsaW5nIHJ1bGVcbiAgICogLSBwb2x5Z29ucyBtYXkgcmFyZWx5IHNoYXJlIGEgY29tbW9uIGVkZ2UgKHRob3VnaCB0aGlzIGlzIG5vdyB2ZXJ5IHJhcmUgYXMgb2YgdmVyc2lvbiA2KVxuICAgKlxuICAgKiBUaGUgc3ViakZpbGxUeXBlIGFuZCBjbGlwRmlsbFR5cGUgcGFyYW1ldGVycyBkZWZpbmUgdGhlIHBvbHlnb24gZmlsbCBydWxlIHRvIGJlIGFwcGxpZWQgdG8gdGhlIHBvbHlnb25zIChpZSBjbG9zZWQgcGF0aHMpIGluIHRoZSBzdWJqZWN0IGFuZCBjbGlwXG4gICAqIHBhdGhzIHJlc3BlY3RpdmVseS4gKEl0J3MgdXN1YWwgdGhvdWdoIG9idmlvdXNseSBub3QgZXNzZW50aWFsIHRoYXQgYm90aCBzZXRzIG9mIHBvbHlnb25zIHVzZSB0aGUgc2FtZSBmaWxsIHJ1bGUuKVxuICAgKlxuICAgKiBleGVjdXRlIGNhbiBiZSBjYWxsZWQgbXVsdGlwbGUgdGltZXMgd2l0aG91dCByZWFzc2lnbmluZyBzdWJqZWN0IGFuZCBjbGlwIHBvbHlnb25zIChpZSB3aGVuIGRpZmZlcmVudCBjbGlwcGluZyBvcGVyYXRpb25zIGFyZSByZXF1aXJlZCBvbiB0aGVcbiAgICogc2FtZSBwb2x5Z29uIHNldHMpLlxuICAgKlxuICAgKiBAcGFyYW0gY2xpcFR5cGUgLSBDbGlwIG9wZXJhdGlvbiB0eXBlXG4gICAqIEBwYXJhbSBzdWJqRmlsbFR5cGUgLSBGaWxsIHR5cGUgb2YgdGhlIHN1YmplY3QgcG9seWdvbnNcbiAgICogQHBhcmFtIGNsaXBGaWxsVHlwZSAtIEZpbGwgdHlwZSBvZiB0aGUgY2xpcCBwb2x5Z29uc1xuICAgKiBAcmV0dXJuIHtQb2x5VHJlZSB8IHVuZGVmaW5lZH0gLSBUaGUgc29sdXRpb24gb3IgdW5kZWZpbmVkIGlmIHRoZXJlIHdhcyBhbiBlcnJvclxuICAgKi9cbiAgZXhlY3V0ZVRvUG9seVRlZShcbiAgICBjbGlwVHlwZTogQ2xpcFR5cGUsXG4gICAgc3ViakZpbGxUeXBlOiBQb2x5RmlsbFR5cGUsXG4gICAgY2xpcEZpbGxUeXBlOiBQb2x5RmlsbFR5cGVcbiAgKTogUG9seVRyZWUgfCB1bmRlZmluZWQge1xuICAgIGNvbnN0IG91dE5hdGl2ZVBvbHlUcmVlID0gbmV3IHRoaXMuX25hdGl2ZUxpYi5Qb2x5VHJlZSgpO1xuICAgIHRyeSB7XG4gICAgICBjb25zdCBzdWNjZXNzID0gdGhpcy5fY2xpcHBlciEuZXhlY3V0ZVBvbHlUcmVlV2l0aEZpbGxUeXBlcyhcbiAgICAgICAgY2xpcFR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIGNsaXBUeXBlKSxcbiAgICAgICAgb3V0TmF0aXZlUG9seVRyZWUsXG4gICAgICAgIHBv