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 52.7 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 = PathToNativePath_1.pathToNativePath(this._nativeLib, path); try { return this._clipper.addPath(nativePath, 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 = PathsToNativePaths_1.pathsToNativePaths(this._nativeLib, paths); try { return this._clipper.addPaths(nativePaths, 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(nativeEnumConversion_1.clipTypeToNative(this._nativeLib, clipType), outNativePaths, nativeEnumConversion_1.polyFillTypeToNative(this._nativeLib, subjFillType), nativeEnumConversion_1.polyFillTypeToNative(this._nativeLib, clipFillType)); if (!success) { return undefined; } else { if (cleanDistance !== undefined) { this._nativeLib.cleanPolygons(outNativePaths, cleanDistance); } return 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(nativeEnumConversion_1.clipTypeToNative(this._nativeLib, clipType), outNativePolyTree, nativeEnumConversion_1.polyFillTypeToNative(this._nativeLib, subjFillType), 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2xpcHBlci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9DbGlwcGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7O0FBSUEsc0VBSXVDO0FBQ3ZDLGtFQUFxRjtBQUNyRiw4REFBNkQ7QUFHN0QsdUNBQXNDO0FBQ3RDLDJFQUEwRTtBQXNCMUU7SUFxRkU7Ozs7OztPQU1HO0lBQ0gsaUJBQ21CLFVBQW9DLEVBQ3JELFdBQW9DO1FBQXBDLDRCQUFBLEVBQUEsZ0JBQW9DO1FBRG5CLGVBQVUsR0FBVixVQUFVLENBQTBCO1FBR3JELElBQU0sZUFBZSxjQUNuQixnQkFBZ0IsRUFBRSxLQUFLLEVBQ3ZCLGNBQWMsRUFBRSxLQUFLLEVBQ3JCLGlCQUFpQixFQUFFLEtBQUssSUFDckIsV0FBVyxDQUNmLENBQUM7UUFFRixJQUFJLGlCQUFpQixHQUFHLENBQUMsQ0FBQztRQUMxQixJQUFJLGVBQWUsQ0FBQyxnQkFBZ0IsRUFBRTtZQUNwQyxpQkFBaUIsSUFBSSxVQUFVLENBQUMsV0FBVyxDQUFDLGVBQWUsQ0FBQyxLQUFLLENBQUM7U0FDbkU7UUFDRCxJQUFJLGVBQWUsQ0FBQyxjQUFjLEVBQUU7WUFDbEMsaUJBQWlCLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDO1NBQ2xFO1FBQ0QsSUFBSSxlQUFlLENBQUMsaUJBQWlCLEVBQUU7WUFDckMsaUJBQWlCLElBQUksVUFBVSxDQUFDLFdBQVcsQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLENBQUM7U0FDckU7UUFFRCxJQUFJLENBQUMsUUFBUSxHQUFHLElBQUksVUFBVSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO1FBQzFELHVEQUEwQixhQUExQix1REFBMEIsdUJBQTFCLHVEQUEwQixDQUFFLFFBQVEsQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUU7SUFDbEUsQ0FBQztJQTNHRCxzQkFBSSxzQ0FB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QVUsRUFBRSxZQUFZLENBQUMsQ0FDcEQsQ0FBQztZQUNGLElBQUksQ0FBQyxPQUFPLEVBQUU7Z0JBQ1osT0FBTyxTQUFTLENBQUM7YUFDbEI7aUJBQU07Z0JBQ0wsT0FBTyxtQkFBUSxDQUFDLGtCQUFrQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQywwQkFBMEI7YUFDekc7U0FDRjtnQkFBUztZQUNSLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxTQUFTLEVBQUUsRUFBRTtnQkFDbEMsaUJBQWlCLENBQUMsTUFBTSxFQUFFLENBQUM7YUFDNUI7U0FDRjtJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsNEJBQVUsR0FBVjtRQUNFLE9BQU8sSUFBSSxDQUFDLFFBQVEsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLEVBQUUsQ0FBQztJQUNsRSxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gseUJBQU8sR0FBUDtRQUNFLElBQUksSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNqQixJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sRUFBRSxDQUFDO1lBQ3ZCLHVEQUEwQixhQUExQix1REFBMEIsdUJBQTFCLHVEQUEwQixDQUFFLFVBQVUsQ0FBQyxJQUFJLEVBQUU7WUFDN0MsSUFBSSxDQUFDLFFBQVEsR0FBRyxTQUFTLENBQUM7U0FDM0I7SUFDSCxDQUFDO0lBQ0gsY0FBQztBQUFELENBQUMsQUF2V0QsSUF1V0M7QUF2V1ksMEJBQU8iLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDbGlwVHlwZSwgUG9seUZpbGxUeXBlLCBQb2x5VHlwZSB9IGZyb20gXCIuL2VudW1zXCI7XHJcbmltcG9ydCB7IEludFJlY3QgfSBmcm9tIFwiLi9JbnRSZWN0XCI7XHJcbmltcG9ydCB7IE5hdGl2ZUNsaXBwZXIgfSBmcm9tIFwiLi9uYXRpdmUvTmF0aXZlQ2xpcHBlclwiO1xyXG5pbXBvcnQgeyBOYXRpdmVDbGlwcGVyTGliSW5zdGFuY2UgfSBmcm9tIFwiLi9uYXRpdmUvTmF0aXZlQ2xpcHBlckxpYkluc3RhbmNlXCI7XHJcbmltcG9ydCB7XHJcbiAgY2xpcFR5cGVUb05hdGl2ZSxcclxuICBwb2x5RmlsbFR5cGVUb05hdGl2ZSxcclxuICBwb2x5VHlwZVRvTmF0aXZlXHJcbn0gZnJvbSBcIi4vbmF0aXZlL25hdGl2ZUVudW1Db252ZXJzaW9uXCI7XHJcbmltcG9ydCB7IG5hdGl2ZVBhdGhzVG9QYXRocywgcGF0aHNUb05hdGl2ZVBhdGhzIH0gZnJvbSBcIi4vbmF0aXZlL1BhdGhzVG9OYXRpdmVQYXRoc1wiO1xyXG5pbXBvcnQgeyBwYXRoVG9OYXRpdmVQYXRoIH0gZnJvbSBcIi4vbmF0aXZlL1BhdGhUb05hdGl2ZVBhdGhcIjtcclxuaW1wb3J0IHsgUGF0aCwgUmVhZG9ubHlQYXRoIH0gZnJvbSBcIi4vUGF0aFwiO1xyXG5pbXBvcnQgeyBQYXRocywgUmVhZG9ubHlQYXRocyB9IGZyb20gXCIuL1BhdGhzXCI7XHJcbmltcG9ydCB7IFBvbHlUcmVlIH0gZnJvbSBcIi4vUG9seVRyZWVcIjtcclxuaW1wb3J0IHsgbmF0aXZlRmluYWxpemF0aW9uUmVnaXN0cnkgfSBmcm9tIFwiLi9uYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeVwiO1xyXG5cclxuZXhwb3J0IGludGVyZmFjZSBDbGlwcGVySW5pdE9wdGlvbnMge1xyXG4gIC8qKlxyXG4gICAqIFdoZW4gdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gdHJ1ZSwgcG9seWdvbnMgcmV0dXJuZWQgaW4gdGhlIHNvbHV0aW9uIHBhcmFtZXRlciBvZiB0aGUgZXhlY3V0ZSgpIG1ldGhvZCB3aWxsIGhhdmUgb3JpZW50YXRpb25zIG9wcG9zaXRlIHRvIHRoZWlyIG5vcm1hbFxyXG4gICAqIG9yaWVudGF0aW9ucy5cclxuICAgKi9cclxuICByZXZlcnNlU29sdXRpb24/OiBib29sZWFuO1xyXG5cclxuICAvKipcclxuICAgKiBXaGVuIHRoaXMgcHJvcGVydHkgaXMgc2V0IHRvIHRydWUsIHBvbHlnb25zIHJldHVybmVkIGluIHRoZSBzb2x1dGlvbiBwYXJhbWV0ZXIgb2YgdGhlIGV4ZWN1dGUoKSBtZXRob2Qgd2lsbCBoYXZlIG9yaWVudGF0aW9ucyBvcHBvc2l0ZSB0byB0aGVpciBub3JtYWxcclxuICAgKiBvcmllbnRhdGlvbnMuXHJcbiAgICovXHJcbiAgc3RyaWN0bHlTaW1wbGU/OiBib29sZWFuO1xyXG5cclxuICAvKipcclxuICAgKiBCeSBkZWZhdWx0LCB3aGVuIHRocmVlIG9yIG1vcmUgdmVydGljZXMgYXJlIGNvbGxpbmVhciBpbiBpbnB1dCBwb2x5Z29ucyAoc3ViamVjdCBvciBjbGlwKSwgdGhlIENsaXBwZXIgb2JqZWN0IHJlbW92ZXMgdGhlICdpbm5lcicgdmVydGljZXMgYmVmb3JlXHJcbiAgICogY2xpcHBpbmcuIFdoZW4gZW5hYmxlZCB0aGUgcHJlc2VydmVDb2xsaW5lYXIgcHJvcGVydHkgcHJldmVudHMgdGhpcyBkZWZhdWx0IGJlaGF2aW9yIHRvIGFsbG93IHRoZXNlIGlubmVyIHZlcnRpY2VzIHRvIGFwcGVhciBpbiB0aGUgc29sdXRpb24uXHJcbiAgICovXHJcbiAgcHJlc2VydmVDb2xsaW5lYXI/OiBib29sZWFuO1xyXG59XHJcblxyXG5leHBvcnQgY2xhc3MgQ2xpcHBlciB7XHJcbiAgcHJpdmF0ZSBfY2xpcHBlcj86IE5hdGl2ZUNsaXBwZXI7XHJcblxyXG4gIC8qKlxyXG4gICAqIEJ5IGRlZmF1bHQsIHdoZW4gdGhyZWUgb3IgbW9yZSB2ZXJ0aWNlcyBhcmUgY29sbGluZWFyIGluIGlucHV0IHBvbHlnb25zIChzdWJqZWN0IG9yIGNsaXApLCB0aGUgQ2xpcHBlciBvYmplY3QgcmVtb3ZlcyB0aGUgJ2lubmVyJyB2ZXJ0aWNlcyBiZWZvcmVcclxuICAgKiBjbGlwcGluZy4gV2hlbiBlbmFibGVkIHRoZSBwcmVzZXJ2ZUNvbGxpbmVhciBwcm9wZXJ0eSBwcmV2ZW50cyB0aGlzIGRlZmF1bHQgYmVoYXZpb3IgdG8gYWxsb3cgdGhlc2UgaW5uZXIgdmVydGljZXMgdG8gYXBwZWFyIGluIHRoZSBzb2x1dGlvbi5cclxuICAgKlxyXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IC0gdHJ1ZSBpZiBzZXQsIGZhbHNlIG90aGVyd2lzZVxyXG4gICAqL1xyXG4gIGdldCBwcmVzZXJ2ZUNvbGxpbmVhcigpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLl9jbGlwcGVyIS5wcmVzZXJ2ZUNvbGxpbmVhcjtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEJ5IGRlZmF1bHQsIHdoZW4gdGhyZWUgb3IgbW9yZSB2ZXJ0aWNlcyBhcmUgY29sbGluZWFyIGluIGlucHV0IHBvbHlnb25zIChzdWJqZWN0IG9yIGNsaXApLCB0aGUgQ2xpcHBlciBvYmplY3QgcmVtb3ZlcyB0aGUgJ2lubmVyJyB2ZXJ0aWNlcyBiZWZvcmVcclxuICAgKiBjbGlwcGluZy4gV2hlbiBlbmFibGVkIHRoZSBwcmVzZXJ2ZUNvbGxpbmVhciBwcm9wZXJ0eSBwcmV2ZW50cyB0aGlzIGRlZmF1bHQgYmVoYXZpb3IgdG8gYWxsb3cgdGhlc2UgaW5uZXIgdmVydGljZXMgdG8gYXBwZWFyIGluIHRoZSBzb2x1dGlvbi5cclxuICAgKlxyXG4gICAqIEBwYXJhbSB2YWx1ZSAtIHZhbHVlIHRvIHNldFxyXG4gICAqL1xyXG4gIHNldCBwcmVzZXJ2ZUNvbGxpbmVhcih2YWx1ZTogYm9vbGVhbikge1xyXG4gICAgdGhpcy5fY2xpcHBlciEucHJlc2VydmVDb2xsaW5lYXIgPSB2YWx1ZTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFdoZW4gdGhpcyBwcm9wZXJ0eSBpcyBzZXQgdG8gdHJ1ZSwgcG9seWdvbnMgcmV0dXJuZWQgaW4gdGhlIHNvbHV0aW9uIHBhcmFtZXRlciBvZiB0aGUgZXhlY3V0ZSgpIG1ldGhvZCB3aWxsIGhhdmUgb3JpZW50YXRpb25zIG9wcG9zaXRlIHRvIHRoZWlyIG5vcm1hbFxyXG4gICAqIG9yaWVudGF0aW9ucy5cclxuICAgKlxyXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IC0gdHJ1ZSBpZiBzZXQsIGZhbHNlIG90aGVyd2lzZVxyXG4gICAqL1xyXG4gIGdldCByZXZlcnNlU29sdXRpb24oKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlciEucmV2ZXJzZVNvbHV0aW9uO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogV2hlbiB0aGlzIHByb3BlcnR5IGlzIHNldCB0byB0cnVlLCBwb2x5Z29ucyByZXR1cm5lZCBpbiB0aGUgc29sdXRpb24gcGFyYW1ldGVyIG9mIHRoZSBleGVjdXRlKCkgbWV0aG9kIHdpbGwgaGF2ZSBvcmllbnRhdGlvbnMgb3Bwb3NpdGUgdG8gdGhlaXIgbm9ybWFsXHJcbiAgICogb3JpZW50YXRpb25zLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHZhbHVlIC0gdmFsdWUgdG8gc2V0XHJcbiAgICovXHJcbiAgc2V0IHJldmVyc2VTb2x1dGlvbih2YWx1ZTogYm9vbGVhbikge1xyXG4gICAgdGhpcy5fY2xpcHBlciEucmV2ZXJzZVNvbHV0aW9uID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUZXJtaW5vbG9neTpcclxuICAgKiAtIEEgc2ltcGxlIHBvbHlnb24gaXMgb25lIHRoYXQgZG9lcyBub3Qgc2VsZi1pbnRlcnNlY3QuXHJcbiAgICogLSBBIHdlYWtseSBzaW1wbGUgcG9seWdvbiBpcyBhIHNpbXBsZSBwb2x5Z29uIHRoYXQgY29udGFpbnMgJ3RvdWNoaW5nJyB2ZXJ0aWNlcywgb3IgJ3RvdWNoaW5nJyBlZGdlcy5cclxuICAgKiAtIEEgc3RyaWN0bHkgc2ltcGxlIHBvbHlnb24gaXMgYSBzaW1wbGUgcG9seWdvbiB0aGF0IGRvZXMgbm90IGNvbnRhaW4gJ3RvdWNoaW5nJyB2ZXJ0aWNlcywgb3IgJ3RvdWNoaW5nJyBlZGdlcy5cclxuICAgKlxyXG4gICAqIFZlcnRpY2VzICd0b3VjaCcgaWYgdGhleSBzaGFyZSB0aGUgc2FtZSBjb29yZGluYXRlcyAoYW5kIGFyZSBub3QgYWRqYWNlbnQpLiBBbiBlZGdlIHRvdWNoZXMgYW5vdGhlciBpZiBvbmUgb2YgaXRzIGVuZCB2ZXJ0aWNlcyB0b3VjaGVzIGFub3RoZXIgZWRnZVxyXG4gICAqIGV4Y2x1ZGluZyBpdHMgYWRqYWNlbnQgZWRnZXMsIG9yIGlmIHRoZXkgYXJlIGNvLWxpbmVhciBhbmQgb3ZlcmxhcHBpbmcgKGluY2x1ZGluZyBhZGphY2VudCBlZGdlcykuXHJcbiAgICpcclxuICAgKiBQb2x5Z29ucyByZXR1cm5lZCBieSBjbGlwcGluZyBvcGVyYXRpb25zIChzZWUgQ2xpcHBlci5leGVjdXRlKCkpIHNob3VsZCBhbHdheXMgYmUgc2ltcGxlIHBvbHlnb25zLiBXaGVuIHRoZSBTdHJpY3RseVNpbXBseSBwcm9wZXJ0eSBpcyBlbmFibGVkLFxyXG4gICAqIHBvbHlnb25zIHJldHVybmVkIHdpbGwgYmUgc3RyaWN0bHkgc2ltcGxlLCBvdGhlcndpc2UgdGhleSBtYXkgYmUgd2Vha2x5IHNpbXBsZS4gSXQncyBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGVuc3VyaW5nIHBvbHlnb25zIGFyZSBzdHJpY3RseSBzaW1wbGVcclxuICAgKiBhbmQgc28gdGhpcyBwcm9wZXJ0eSBpcyBkaXNhYmxlZCBieSBkZWZhdWx0LlxyXG4gICAqXHJcbiAgICogTm90ZTogVGhlcmUncyBjdXJyZW50bHkgbm8gZ3VhcmFudGVlIHRoYXQgcG9seWdvbnMgd2lsbCBiZSBzdHJpY3RseSBzaW1wbGUgc2luY2UgJ3NpbXBsaWZ5aW5nJyBpcyBzdGlsbCBhIHdvcmsgaW4gcHJvZ3Jlc3MuXHJcbiAgICpcclxuICAgKiBAcmV0dXJuIHtib29sZWFufSAtIHRydWUgaWYgc2V0LCBmYWxzZSBvdGhlcndpc2VcclxuICAgKi9cclxuICBnZXQgc3RyaWN0bHlTaW1wbGUoKTogYm9vbGVhbiB7XHJcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlciEuc3RyaWN0bHlTaW1wbGU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUZXJtaW5vbG9neTpcclxuICAgKiAtIEEgc2ltcGxlIHBvbHlnb24gaXMgb25lIHRoYXQgZG9lcyBub3Qgc2VsZi1pbnRlcnNlY3QuXHJcbiAgICogLSBBIHdlYWtseSBzaW1wbGUgcG9seWdvbiBpcyBhIHNpbXBsZSBwb2x5Z29uIHRoYXQgY29udGFpbnMgJ3RvdWNoaW5nJyB2ZXJ0aWNlcywgb3IgJ3RvdWNoaW5nJyBlZGdlcy5cclxuICAgKiAtIEEgc3RyaWN0bHkgc2ltcGxlIHBvbHlnb24gaXMgYSBzaW1wbGUgcG9seWdvbiB0aGF0IGRvZXMgbm90IGNvbnRhaW4gJ3RvdWNoaW5nJyB2ZXJ0aWNlcywgb3IgJ3RvdWNoaW5nJyBlZGdlcy5cclxuICAgKlxyXG4gICAqIFZlcnRpY2VzICd0b3VjaCcgaWYgdGhleSBzaGFyZSB0aGUgc2FtZSBjb29yZGluYXRlcyAoYW5kIGFyZSBub3QgYWRqYWNlbnQpLiBBbiBlZGdlIHRvdWNoZXMgYW5vdGhlciBpZiBvbmUgb2YgaXRzIGVuZCB2ZXJ0aWNlcyB0b3VjaGVzIGFub3RoZXIgZWRnZVxyXG4gICAqIGV4Y2x1ZGluZyBpdHMgYWRqYWNlbnQgZWRnZXMsIG9yIGlmIHRoZXkgYXJlIGNvLWxpbmVhciBhbmQgb3ZlcmxhcHBpbmcgKGluY2x1ZGluZyBhZGphY2VudCBlZGdlcykuXHJcbiAgICpcclxuICAgKiBQb2x5Z29ucyByZXR1cm5lZCBieSBjbGlwcGluZyBvcGVyYXRpb25zIChzZWUgQ2xpcHBlci5leGVjdXRlKCkpIHNob3VsZCBhbHdheXMgYmUgc2ltcGxlIHBvbHlnb25zLiBXaGVuIHRoZSBTdHJpY3RseVNpbXBseSBwcm9wZXJ0eSBpcyBlbmFibGVkLFxyXG4gICAqIHBvbHlnb25zIHJldHVybmVkIHdpbGwgYmUgc3RyaWN0bHkgc2ltcGxlLCBvdGhlcndpc2UgdGhleSBtYXkgYmUgd2Vha2x5IHNpbXBsZS4gSXQncyBjb21wdXRhdGlvbmFsbHkgZXhwZW5zaXZlIGVuc3VyaW5nIHBvbHlnb25zIGFyZSBzdHJpY3RseSBzaW1wbGVcclxuICAgKiBhbmQgc28gdGhpcyBwcm9wZXJ0eSBpcyBkaXNhYmxlZCBieSBkZWZhdWx0LlxyXG4gICAqXHJcbiAgICogTm90ZTogVGhlcmUncyBjdXJyZW50bHkgbm8gZ3VhcmFudGVlIHRoYXQgcG9seWdvbnMgd2lsbCBiZSBzdHJpY3RseSBzaW1wbGUgc2luY2UgJ3NpbXBsaWZ5aW5nJyBpcyBzdGlsbCBhIHdvcmsgaW4gcHJvZ3Jlc3MuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gdmFsdWUgLSB2YWx1ZSB0byBzZXRcclxuICAgKi9cclxuICBzZXQgc3RyaWN0bHlTaW1wbGUodmFsdWU6IGJvb2xlYW4pIHtcclxuICAgIHRoaXMuX2NsaXBwZXIhLnN0cmljdGx5U2ltcGxlID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGUgQ2xpcHBlciBjb25zdHJ1Y3RvciBjcmVhdGVzIGFuIGluc3RhbmNlIG9mIHRoZSBDbGlwcGVyIGNsYXNzLiBPbmUgb3IgbW9yZSBJbml0T3B0aW9ucyBtYXkgYmUgcGFzc2VkIGFzIGEgcGFyYW1ldGVyIHRvIHNldCB0aGUgY29ycmVzcG9uZGluZyBwcm9wZXJ0aWVzLlxyXG4gICAqIChUaGVzZSBwcm9wZXJ0aWVzIGNhbiBzdGlsbCBiZSBzZXQgb3IgcmVzZXQgYWZ0ZXIgY29uc3RydWN0aW9uLilcclxuICAgKlxyXG4gICAqIEBwYXJhbSBfbmF0aXZlTGliXHJcbiAgICogQHBhcmFtIGluaXRPcHRpb25zXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9uYXRpdmVMaWI6IE5hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZSxcclxuICAgIGluaXRPcHRpb25zOiBDbGlwcGVySW5pdE9wdGlvbnMgPSB7fVxyXG4gICkge1xyXG4gICAgY29uc3QgcmVhbEluaXRPcHRpb25zID0ge1xyXG4gICAgICByZXZlcnNlU29sdXRpb25zOiBmYWxzZSxcclxuICAgICAgc3RyaWN0bHlTaW1wbGU6IGZhbHNlLFxyXG4gICAgICBwcmVzZXJ2ZUNvbGxpbmVhcjogZmFsc2UsXHJcbiAgICAgIC4uLmluaXRPcHRpb25zXHJcbiAgICB9O1xyXG5cclxuICAgIGxldCBuYXRpdmVJbml0T3B0aW9ucyA9IDA7XHJcbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnJldmVyc2VTb2x1dGlvbnMpIHtcclxuICAgICAgbmF0aXZlSW5pdE9wdGlvbnMgKz0gX25hdGl2ZUxpYi5Jbml0T3B0aW9ucy5SZXZlcnNlU29sdXRpb24udmFsdWU7XHJcbiAgICB9XHJcbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnN0cmljdGx5U2ltcGxlKSB7XHJcbiAgICAgIG5hdGl2ZUluaXRPcHRpb25zICs9IF9uYXRpdmVMaWIuSW5pdE9wdGlvbnMuU3RyaWN0bHlTaW1wbGUudmFsdWU7XHJcbiAgICB9XHJcbiAgICBpZiAocmVhbEluaXRPcHRpb25zLnByZXNlcnZlQ29sbGluZWFyKSB7XHJcbiAgICAgIG5hdGl2ZUluaXRPcHRpb25zICs9IF9uYXRpdmVMaWIuSW5pdE9wdGlvbnMuUHJlc2VydmVDb2xsaW5lYXIudmFsdWU7XHJcbiAgICB9XHJcblxyXG4gICAgdGhpcy5fY2xpcHBlciA9IG5ldyBfbmF0aXZlTGliLkNsaXBwZXIobmF0aXZlSW5pdE9wdGlvbnMpO1xyXG4gICAgbmF0aXZlRmluYWxpemF0aW9uUmVnaXN0cnk/LnJlZ2lzdGVyKHRoaXMsIHRoaXMuX2NsaXBwZXIsIHRoaXMpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogQW55IG51bWJlciBvZiBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGNhbiBiZSBhZGRlZCB0byBhIGNsaXBwaW5nIHRhc2ssIGVpdGhlciBpbmRpdmlkdWFsbHkgdmlhIHRoZSBhZGRQYXRoKCkgbWV0aG9kLCBvciBhcyBncm91cHMgdmlhIHRoZSBhZGRQYXRocygpXHJcbiAgICogbWV0aG9kLCBvciBldmVuIHVzaW5nIGJvdGggbWV0aG9kcy5cclxuICAgKlxyXG4gICAqICdTdWJqZWN0JyBwYXRocyBtYXkgYmUgZWl0aGVyIG9wZW4gKGxpbmVzKSBvciBjbG9zZWQgKHBvbHlnb25zKSBvciBldmVuIGEgbWl4dHVyZSBvZiBib3RoLCBidXQgJ2NsaXBwaW5nJyBwYXRocyBtdXN0IGFsd2F5cyBiZSBjbG9zZWQuIENsaXBwZXIgYWxsb3dzXHJcbiAgICogcG9seWdvbnMgdG8gY2xpcCBib3RoIGxpbmVzIGFuZCBvdGhlciBwb2x5Z29ucywgYnV0IGRvZXNuJ3QgYWxsb3cgbGluZXMgdG8gY2xpcCBlaXRoZXIgbGluZXMgb3IgcG9seWdvbnMuXHJcbiAgICpcclxuICAgKiBXaXRoIGNsb3NlZCBwYXRocywgb3JpZW50YXRpb24gc2hvdWxkIGNvbmZvcm0gd2l0aCB0aGUgZmlsbGluZyBydWxlIHRoYXQgd2lsbCBiZSBwYXNzZWQgdmlhIENsaXBwZXIncyBleGVjdXRlIG1ldGhvZC5cclxuICAgKlxyXG4gICAqIFBhdGggQ29vcmRpbmF0ZSByYW5nZTpcclxuICAgKiBQYXRoIGNvb3JkaW5hdGVzIG11c3QgYmUgYmV0d2VlbiDCsSA5MDA3MTk5MjU0NzQwOTkxLCBvdGhlcndpc2UgYSByYW5nZSBlcnJvciB3aWxsIGJlIHRocm93biB3aGVuIGF0dGVtcHRpbmcgdG8gYWRkIHRoZSBwYXRoIHRvIHRoZSBDbGlwcGVyIG9iamVjdC5cclxuICAgKiBJZiBjb29yZGluYXRlcyBjYW4gYmUga2VwdCBiZXR3ZWVuIMKxIDB4M0ZGRkZGRkYgKMKxIDEuMGUrOSksIGEgbW9kZXN0IGluY3JlYXNlIGluIHBlcmZvcm1hbmNlIChhcHByb3guIDE1LTIwJSkgb3ZlciB0aGUgbGFyZ2VyIHJhbmdlIGNhbiBiZSBhY2hpZXZlZCBieVxyXG4gICAqIGF2b2lkaW5nIGxhcmdlIGludGVnZXIgbWF0aC5cclxuICAgKlxyXG4gICAqIFJldHVybiBWYWx1ZTpcclxuICAgKiBUaGUgZnVuY3Rpb24gd2lsbCByZXR1cm4gZmFsc2UgaWYgdGhlIHBhdGggaXMgaW52YWxpZCBmb3IgY2xpcHBpbmcuIEEgcGF0aCBpcyBpbnZhbGlkIGZvciBjbGlwcGluZyB3aGVuOlxyXG4gICAqIC0gaXQgaGFzIGxlc3MgdGhhbiAyIHZlcnRpY2VzXHJcbiAgICogLSBpdCBoYXMgMiB2ZXJ0aWNlcyBidXQgaXMgbm90IGFuIG9wZW4gcGF0aFxyXG4gICAqIC0gdGhlIHZlcnRpY2VzIGFyZSBhbGwgY28tbGluZWFyIGFuZCBpdCBpcyBub3QgYW4gb3BlbiBwYXRoXHJcbiAgICpcclxuICAgKiBAcGFyYW0gcGF0aCAtIFBhdGggdG8gYWRkXHJcbiAgICogQHBhcmFtIHBvbHlUeXBlIC0gUG9seWdvbiB0eXBlXHJcbiAgICogQHBhcmFtIGNsb3NlZCAtIElmIHRoZSBwYXRoIGlzIGNsb3NlZFxyXG4gICAqL1xyXG4gIGFkZFBhdGgocGF0aDogUmVhZG9ubHlQYXRoLCBwb2x5VHlwZTogUG9seVR5cGUsIGNsb3NlZDogYm9vbGVhbik6IGJvb2xlYW4ge1xyXG4gICAgY29uc3QgbmF0aXZlUGF0aCA9IHBhdGhUb05hdGl2ZVBhdGgodGhpcy5fbmF0aXZlTGliLCBwYXRoKTtcclxuICAgIHRyeSB7XHJcbiAgICAgIHJldHVybiB0aGlzLl9jbGlwcGVyIS5hZGRQYXRoKFxyXG4gICAgICAgIG5hdGl2ZVBhdGgsXHJcbiAgICAgICAgcG9seVR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIHBvbHlUeXBlKSxcclxuICAgICAgICBjbG9zZWRcclxuICAgICAgKTtcclxuICAgIH0gZmluYWxseSB7XHJcbiAgICAgIG5hdGl2ZVBhdGguZGVsZXRlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBBbnkgbnVtYmVyIG9mIHN1YmplY3QgYW5kIGNsaXAgcGF0aHMgY2FuIGJlIGFkZGVkIHRvIGEgY2xpcHBpbmcgdGFzaywgZWl0aGVyIGluZGl2aWR1YWxseSB2aWEgdGhlIGFkZFBhdGgoKSBtZXRob2QsIG9yIGFzIGdyb3VwcyB2aWEgdGhlIGFkZFBhdGhzKClcclxuICAgKiBtZXRob2QsIG9yIGV2ZW4gdXNpbmcgYm90aCBtZXRob2RzLlxyXG4gICAqXHJcbiAgICogJ1N1YmplY3QnIHBhdGhzIG1heSBiZSBlaXRoZXIgb3BlbiAobGluZXMpIG9yIGNsb3NlZCAocG9seWdvbnMpIG9yIGV2ZW4gYSBtaXh0dXJlIG9mIGJvdGgsIGJ1dCAnY2xpcHBpbmcnIHBhdGhzIG11c3QgYWx3YXlzIGJlIGNsb3NlZC4gQ2xpcHBlciBhbGxvd3NcclxuICAgKiBwb2x5Z29ucyB0byBjbGlwIGJvdGggbGluZXMgYW5kIG90aGVyIHBvbHlnb25zLCBidXQgZG9lc24ndCBhbGxvdyBsaW5lcyB0byBjbGlwIGVpdGhlciBsaW5lcyBvciBwb2x5Z29ucy5cclxuICAgKlxyXG4gICAqIFdpdGggY2xvc2VkIHBhdGhzLCBvcmllbnRhdGlvbiBzaG91bGQgY29uZm9ybSB3aXRoIHRoZSBmaWxsaW5nIHJ1bGUgdGhhdCB3aWxsIGJlIHBhc3NlZCB2aWEgQ2xpcHBlcidzIGV4ZWN1dGUgbWV0aG9kLlxyXG4gICAqXHJcbiAgICogUGF0aCBDb29yZGluYXRlIHJhbmdlOlxyXG4gICAqIFBhdGggY29vcmRpbmF0ZXMgbXVzdCBiZSBiZXR3ZWVuIMKxIDkwMDcxOTkyNTQ3NDA5OTEsIG90aGVyd2lzZSBhIHJhbmdlIGVycm9yIHdpbGwgYmUgdGhyb3duIHdoZW4gYXR0ZW1wdGluZyB0byBhZGQgdGhlIHBhdGggdG8gdGhlIENsaXBwZXIgb2JqZWN0LlxyXG4gICAqIElmIGNvb3JkaW5hdGVzIGNhbiBiZSBrZXB0IGJldHdlZW4gwrEgMHgzRkZGRkZGRiAowrEgMS4wZSs5KSwgYSBtb2Rlc3QgaW5jcmVhc2UgaW4gcGVyZm9ybWFuY2UgKGFwcHJveC4gMTUtMjAlKSBvdmVyIHRoZSBsYXJnZXIgcmFuZ2UgY2FuIGJlIGFjaGlldmVkXHJcbiAgICogYnkgYXZvaWRpbmcgbGFyZ2UgaW50ZWdlciBtYXRoLlxyXG4gICAqXHJcbiAgICogUmV0dXJuIFZhbHVlOlxyXG4gICAqIFRoZSBmdW5jdGlvbiB3aWxsIHJldHVybiBmYWxzZSBpZiB0aGUgcGF0aCBpcyBpbnZhbGlkIGZvciBjbGlwcGluZy4gQSBwYXRoIGlzIGludmFsaWQgZm9yIGNsaXBwaW5nIHdoZW46XHJcbiAgICogLSBpdCBoYXMgbGVzcyB0aGFuIDIgdmVydGljZXNcclxuICAgKiAtIGl0IGhhcyAyIHZlcnRpY2VzIGJ1dCBpcyBub3QgYW4gb3BlbiBwYXRoXHJcbiAgICogLSB0aGUgdmVydGljZXMgYXJlIGFsbCBjby1saW5lYXIgYW5kIGl0IGlzIG5vdCBhbiBvcGVuIHBhdGhcclxuICAgKlxyXG4gICAqIEBwYXJhbSBwYXRocyAtIFBhdGhzIHRvIGFkZFxyXG4gICAqIEBwYXJhbSBwb2x5VHlwZSAtIFBhdGhzIHBvbHlnb24gdHlwZVxyXG4gICAqIEBwYXJhbSBjbG9zZWQgLSBJZiBhbGwgdGhlIGlubmVyIHBhdGhzIGFyZSBjbG9zZWRcclxuICAgKi9cclxuICBhZGRQYXRocyhwYXRoczogUmVhZG9ubHlQYXRocywgcG9seVR5cGU6IFBvbHlUeXBlLCBjbG9zZWQ6IGJvb2xlYW4pOiBib29sZWFuIHtcclxuICAgIGNvbnN0IG5hdGl2ZVBhdGhzID0gcGF0aHNUb05hdGl2ZVBhdGhzKHRoaXMuX25hdGl2ZUxpYiwgcGF0aHMpO1xyXG4gICAgdHJ5IHtcclxuICAgICAgcmV0dXJuIHRoaXMuX2NsaXBwZXIhLmFkZFBhdGhzKFxyXG4gICAgICAgIG5hdGl2ZVBhdGhzLFxyXG4gICAgICAgIHBvbHlUeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBwb2x5VHlwZSksXHJcbiAgICAgICAgY2xvc2VkXHJcbiAgICAgICk7XHJcbiAgICB9IGZpbmFsbHkge1xyXG4gICAgICBuYXRpdmVQYXRocy5kZWxldGUoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoZSBDbGVhciBtZXRob2QgcmVtb3ZlcyBhbnkgZXhpc3Rpbmcgc3ViamVjdCBhbmQgY2xpcCBwb2x5Z29ucyBhbGxvd2luZyB0aGUgQ2xpcHBlciBvYmplY3QgdG8gYmUgcmV1c2VkIGZvciBjbGlwcGluZyBvcGVyYXRpb25zIG9uIGRpZmZlcmVudCBwb2x5Z29uIHNldHMuXHJcbiAgICovXHJcbiAgY2xlYXIoKTogdm9pZCB7XHJcbiAgICB0aGlzLl9jbGlwcGVyIS5jbGVhcigpO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogVGhpcyBtZXRob2QgcmV0dXJucyB0aGUgYXhpcy1hbGlnbmVkIGJvdW5kaW5nIHJlY3RhbmdsZSBvZiBhbGwgcG9seWdvbnMgdGhhdCBoYXZlIGJlZW4gYWRkZWQgdG8gdGhlIENsaXBwZXIgb2JqZWN0LlxyXG4gICAqXHJcbiAgICogQHJldHVybiB7e2xlZnQ6IG51bWJlciwgcmlnaHQ6IG51bWJlciwgdG9wOiBudW1iZXIsIGJvdHRvbTogbnVtYmVyfX0gLSBCb3VuZHNcclxuICAgKi9cclxuICBnZXRCb3VuZHMoKTogSW50UmVjdCB7XHJcbiAgICBjb25zdCBuYXRpdmVCb3VuZHMgPSB0aGlzLl9jbGlwcGVyIS5nZXRCb3VuZHMoKTtcclxuICAgIGNvbnN0IHJlY3QgPSB7XHJcbiAgICAgIGxlZnQ6IG5hdGl2ZUJvdW5kcy5sZWZ0LFxyXG4gICAgICByaWdodDogbmF0aXZlQm91bmRzLnJpZ2h0LFxyXG4gICAgICB0b3A6IG5hdGl2ZUJvdW5kcy50b3AsXHJcbiAgICAgIGJvdHRvbTogbmF0aXZlQm91bmRzLmJvdHRvbVxyXG4gICAgfTtcclxuICAgIG5hdGl2ZUJvdW5kcy5kZWxldGUoKTtcclxuICAgIHJldHVybiByZWN0O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogT25jZSBzdWJqZWN0IGFuZCBjbGlwIHBhdGhzIGhhdmUgYmVlbiBhc3NpZ25lZCAodmlhIGFkZFBhdGggYW5kL29yIGFkZFBhdGhzKSwgZXhlY3V0ZSBjYW4gdGhlbiBwZXJmb3JtIHRoZSBjbGlwcGluZyBvcGVyYXRpb24gKGludGVyc2VjdGlvbiwgdW5pb24sXHJcbiAgICogZGlmZmVyZW5jZSBvciBYT1IpIHNwZWNpZmllZCBieSB0aGUgY2xpcFR5cGUgcGFyYW1ldGVyLlxyXG4gICAqXHJcbiAgICogVGhlIHNvbHV0aW9uIHBhcmFtZXRlciBpbiB0aGlzIGNhc2UgaXMgYSBQYXRocyBvciBQb2x5VHJlZSBzdHJ1Y3R1cmUuIFRoZSBQYXRocyBzdHJ1Y3R1cmUgaXMgc2ltcGxlciB0aGFuIHRoZSBQb2x5VHJlZSBzdHJ1Y3R1cmUuIEJlY2F1c2Ugb2YgdGhpcyBpdCBpc1xyXG4gICAqIHF1aWNrZXIgdG8gcG9wdWxhdGUgYW5kIGhlbmNlIGNsaXBwaW5nIHBlcmZvcm1hbmNlIGlzIGEgbGl0dGxlIGJldHRlciAoaXQncyByb3VnaGx5IDEwJSBmYXN0ZXIpLiBIb3dldmVyLCB0aGUgUG9seVRyZWUgZGF0YSBzdHJ1Y3R1cmUgcHJvdmlkZXMgbW9yZVxyXG4gICAqIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZXR1cm5lZCBwYXRocyB3aGljaCBtYXkgYmUgaW1wb3J0YW50IHRvIHVzZXJzLiBGaXJzdGx5LCB0aGUgUG9seVRyZWUgc3RydWN0dXJlIHByZXNlcnZlcyBuZXN0ZWQgcGFyZW50LWNoaWxkIHBvbHlnb24gcmVsYXRpb25zaGlwc1xyXG4gICAqIChpZSBvdXRlciBwb2x5Z29ucyBvd25pbmcvY29udGFpbmluZyBob2xlcyBhbmQgaG9sZXMgb3duaW5nL2NvbnRhaW5pbmcgb3RoZXIgb3V0ZXIgcG9seWdvbnMgZXRjKS4gQWxzbywgb25seSB0aGUgUG9seVRyZWUgc3RydWN0dXJlIGNhbiBkaWZmZXJlbnRpYXRlXHJcbiAgICogYmV0d2VlbiBvcGVuIGFuZCBjbG9zZWQgcGF0aHMgc2luY2UgZWFjaCBQb2x5Tm9kZSBoYXMgYW4gSXNPcGVuIHByb3BlcnR5LiAoVGhlIFBhdGggc3RydWN0dXJlIGhhcyBubyBtZW1iZXIgaW5kaWNhdGluZyB3aGV0aGVyIGl0J3Mgb3BlbiBvciBjbG9zZWQuKVxyXG4gICAqIEZvciB0aGlzIHJlYXNvbiwgd2hlbiBvcGVuIHBhdGhzIGFyZSBwYXNzZWQgdG8gYSBDbGlwcGVyIG9iamVjdCwgdGhlIHVzZXIgbXVzdCB1c2UgYSBQb2x5VHJlZSBvYmplY3QgYXMgdGhlIHNvbHV0aW9uIHBhcmFtZXRlciwgb3RoZXJ3aXNlIGFuIGV4Y2VwdGlvblxyXG4gICAqIHdpbGwgYmUgcmFpc2VkLlxyXG4gICAqXHJcbiAgICogV2hlbiBhIFBvbHlUcmVlIG9iamVjdCBpcyB1c2VkIGluIGEgY2xpcHBpbmcgb3BlcmF0aW9uIG9uIG9wZW4gcGF0aHMsIHR3byBhbmNpbGxpYXJ5IGZ1bmN0aW9ucyBoYXZlIGJlZW4gcHJvdmlkZWQgdG8gcXVpY2tseSBzZXBhcmF0ZSBvdXQgb3BlbiBhbmRcclxuICAgKiBjbG9zZWQgcGF0aHMgZnJvbSB0aGUgc29sdXRpb24gLSBPcGVuUGF0aHNGcm9tUG9seVRyZWUgYW5kIENsb3NlZFBhdGhzRnJvbVBvbHlUcmVlLiBQb2x5VHJlZVRvUGF0aHMgaXMgYWxzbyBhdmFpbGFibGUgdG8gY29udmVydCBwYXRoIGRhdGEgdG8gYSBQYXRoc1xyXG4gICAqIHN0cnVjdHVyZSAoaXJyZXNwZWN0aXZlIG9mIHdoZXRoZXIgdGhleSdyZSBvcGVuIG9yIGNsb3NlZCkuXHJcbiAgICpcclxuICAgKiBUaGVyZSBhcmUgc2V2ZXJhbCB0aGluZ3MgdG8gbm90ZSBhYm91dCB0aGUgc29sdXRpb24gcGF0aHMgcmV0dXJuZWQ6XHJcbiAgICogLSB0aGV5IGFyZW4ndCBpbiBhbnkgc3BlY2lmaWMgb3JkZXJcclxuICAgKiAtIHRoZXkgc2hvdWxkIG5ldmVyIG92ZXJsYXAgb3IgYmUgc2VsZi1pbnRlcnNlY3RpbmcgKGJ1dCBzZWUgbm90ZXMgb24gcm91bmRpbmcpXHJcbiAgICogLSBob2xlcyB3aWxsIGJlIG9yaWVudGVkIG9wcG9zaXRlIG91dGVyIHBvbHlnb25zXHJcbiAgICogLSB0aGUgc29sdXRpb24gZmlsbCB0eXBlIGNhbiBiZSBjb25zaWRlcmVkIGVpdGhlciBFdmVuT2RkIG9yIE5vblplcm8gc2luY2UgaXQgd2lsbCBjb21wbHkgd2l0aCBlaXRoZXIgZmlsbGluZyBydWxlXHJcbiAgICogLSBwb2x5Z29ucyBtYXkgcmFyZWx5IHNoYXJlIGEgY29tbW9uIGVkZ2UgKHRob3VnaCB0aGlzIGlzIG5vdyB2ZXJ5IHJhcmUgYXMgb2YgdmVyc2lvbiA2KVxyXG4gICAqXHJcbiAgICogVGhlIHN1YmpGaWxsVHlwZSBhbmQgY2xpcEZpbGxUeXBlIHBhcmFtZXRlcnMgZGVmaW5lIHRoZSBwb2x5Z29uIGZpbGwgcnVsZSB0byBiZSBhcHBsaWVkIHRvIHRoZSBwb2x5Z29ucyAoaWUgY2xvc2VkIHBhdGhzKSBpbiB0aGUgc3ViamVjdCBhbmQgY2xpcFxyXG4gICAqIHBhdGhzIHJlc3BlY3RpdmVseS4gKEl0J3MgdXN1YWwgdGhvdWdoIG9idmlvdXNseSBub3QgZXNzZW50aWFsIHRoYXQgYm90aCBzZXRzIG9mIHBvbHlnb25zIHVzZSB0aGUgc2FtZSBmaWxsIHJ1bGUuKVxyXG4gICAqXHJcbiAgICogZXhlY3V0ZSBjYW4gYmUgY2FsbGVkIG11bHRpcGxlIHRpbWVzIHdpdGhvdXQgcmVhc3NpZ25pbmcgc3ViamVjdCBhbmQgY2xpcCBwb2x5Z29ucyAoaWUgd2hlbiBkaWZmZXJlbnQgY2xpcHBpbmcgb3BlcmF0aW9ucyBhcmUgcmVxdWlyZWQgb24gdGhlXHJcbiAgICogc2FtZSBwb2x5Z29uIHNldHMpLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIGNsaXBUeXBlIC0gQ2xpcCBvcGVyYXRpb24gdHlwZVxyXG4gICAqIEBwYXJhbSBzdWJqRmlsbFR5cGUgLSBGaWxsIHR5cGUgb2YgdGhlIHN1YmplY3QgcG9seWdvbnNcclxuICAgKiBAcGFyYW0gY2xpcEZpbGxUeXBlIC0gRmlsbCB0eXBlIG9mIHRoZSBjbGlwIHBvbHlnb25zXHJcbiAgICogQHBhcmFtIGNsZWFuRGlzdGFuY2UgLSBDbGVhbiBkaXN0YW5jZSBvdmVyIHRoZSBvdXRwdXQsIG9yIHVuZGVmaW5lZCBmb3Igbm8gY2xlYW5pbmcuXHJcbiAgICogQHJldHVybiB7UGF0aHMgfCB1bmRlZmluZWR9IC0gVGhlIHNvbHV0aW9uIG9yIHVuZGVmaW5lZCBpZiB0aGVyZSB3YXMgYW4gZXJyb3JcclxuICAgKi9cclxuICBleGVjdXRlVG9QYXRocyhcclxuICAgIGNsaXBUeXBlOiBDbGlwVHlwZSxcclxuICAgIHN1YmpGaWxsVHlwZTogUG9seUZpbGxUeXBlLFxyXG4gICAgY2xpcEZpbGxUeXBlOiBQb2x5RmlsbFR5cGUsXHJcbiAgICBjbGVhbkRpc3RhbmNlOiBudW1iZXIgfCB1bmRlZmluZWRcclxuICApOiBQYXRocyB8IHVuZGVmaW5lZCB7XHJcbiAgICBjb25zdCBvdXROYXRpdmVQYXRocyA9IG5ldyB0aGlzLl9uYXRpdmVMaWIuUGF0aHMoKTtcclxuICAgIHRyeSB7XHJcbiAgICAgIGNvbnN0IHN1Y2Nlc3MgPSB0aGlzLl9jbGlwcGVyIS5leGVjdXRlUGF0aHNXaXRoRmlsbFR5cGVzKFxyXG4gICAgICAgIGNsaXBUeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBjbGlwVHlwZSksXHJcbiAgICAgICAgb3V0TmF0aXZlUGF0aHMsXHJcbiAgICAgICAgcG9seUZpbGxUeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBzdWJqRmlsbFR5cGUpLFxyXG4gICAgICAgIHBvbHlGaWxsVHlwZVRvTmF0aXZlKHRoaXMuX25hdGl2ZUxpYiwgY2xpcEZpbGxUeXBlKVxyXG4gICAgICApO1xyXG4gICAgICBpZiAoIXN1Y2Nlc3MpIHtcclxuICAgICAgICByZXR1cm4gdW5kZWZpbmVkO1xyXG4gICAgICB9IGVsc2Uge1xyXG4gICAgICAgIGlmIChjbGVhbkRpc3RhbmNlICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgIHRoaXMuX25hdGl2ZUxpYi5jbGVhblBvbHlnb25zKG91dE5hdGl2ZVBhdGhzLCBjbGVhbkRpc3RhbmNlKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIG5hdGl2ZVBhdGhzVG9QYXRocyh0aGlzLl9uYXRpdmVMaWIsIG91dE5hdGl2ZVBhdGhzLCB0cnVlKTsgLy8gZnJlZXMgb3V0TmF0aXZlUGF0aHNcclxuICAgICAgfVxyXG4gICAgfSBmaW5hbGx5IHtcclxuICAgICAgaWYgKCFvdXROYXRpdmVQYXRocy5pc0RlbGV0ZWQoKSkge1xyXG4gICAgICAgIG91dE5hdGl2ZVBhdGhzLmRlbGV0ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBPbmNlIHN1YmplY3QgYW5kIGNsaXAgcGF0aHMgaGF2ZSBiZWVuIGFzc2lnbmVkICh2aWEgYWRkUGF0aCBhbmQvb3IgYWRkUGF0aHMpLCBleGVjdXRlIGNhbiB0aGVuIHBlcmZvcm0gdGhlIGNsaXBwaW5nIG9wZXJhdGlvbiAoaW50ZXJzZWN0aW9uLCB1bmlvbixcclxuICAgKiBkaWZmZXJlbmNlIG9yIFhPUikgc3BlY2lmaWVkIGJ5IHRoZSBjbGlwVHlwZSBwYXJhbWV0ZXIuXHJcbiAgICpcclxuICAgKiBUaGUgc29sdXRpb24gcGFyYW1ldGVyIGNhbiBiZSBlaXRoZXIgYSBQYXRocyBvciBQb2x5VHJlZSBzdHJ1Y3R1cmUuIFRoZSBQYXRocyBzdHJ1Y3R1cmUgaXMgc2ltcGxlciB0aGFuIHRoZSBQb2x5VHJlZSBzdHJ1Y3R1cmUuIEJlY2F1c2Ugb2YgdGhpcyBpdCBpc1xyXG4gICAqIHF1aWNrZXIgdG8gcG9wdWxhdGUgYW5kIGhlbmNlIGNsaXBwaW5nIHBlcmZvcm1hbmNlIGlzIGEgbGl0dGxlIGJldHRlciAoaXQncyByb3VnaGx5IDEwJSBmYXN0ZXIpLiBIb3dldmVyLCB0aGUgUG9seVRyZWUgZGF0YSBzdHJ1Y3R1cmUgcHJvdmlkZXMgbW9yZVxyXG4gICAqIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZXR1cm5lZCBwYXRocyB3aGljaCBtYXkgYmUgaW1wb3J0YW50IHRvIHVzZXJzLiBGaXJzdGx5LCB0aGUgUG9seVRyZWUgc3RydWN0dXJlIHByZXNlcnZlcyBuZXN0ZWQgcGFyZW50LWNoaWxkIHBvbHlnb24gcmVsYXRpb25zaGlwc1xyXG4gICAqIChpZSBvdXRlciBwb2x5Z29ucyBvd25pbmcvY29udGFpbmluZyBob2xlcyBhbmQgaG9sZXMgb3duaW5nL2NvbnRhaW5pbmcgb3RoZXIgb3V0ZXIgcG9seWdvbnMgZXRjKS4gQWxzbywgb25seSB0aGUgUG9seVRyZWUgc3RydWN0dXJlIGNhbiBkaWZmZXJlbnRpYXRlXHJcbiAgICogYmV0d2VlbiBvcGVuIGFuZCBjbG9zZWQgcGF0aHMgc2luY2UgZWFjaCBQb2x5Tm9kZSBoYXMgYW4gSXNPcGVuIHByb3BlcnR5LiAoVGhlIFBhdGggc3RydWN0dXJlIGhhcyBubyBtZW1iZXIgaW5kaWNhdGluZyB3aGV0aGVyIGl0J3Mgb3BlbiBvciBjbG9zZWQuKVxyXG4gICAqIEZvciB0aGlzIHJlYXNvbiwgd2hlbiBvcGVuIHBhdGhzIGFyZSBwYXNzZWQgdG8gYSBDbGlwcGVyIG9iamVjdCwgdGhlIHVzZXIgbXVzdCB1c2UgYSBQb2x5VHJlZSBvYmplY3QgYXMgdGhlIHNvbHV0aW9uIHBhcmFtZXRlciwgb3RoZXJ3aXNlIGFuIGV4Y2VwdGlvblxyXG4gICAqIHdpbGwgYmUgcmFpc2VkLlxyXG4gICAqXHJcbiAgICogV2hlbiBhIFBvbHlUcmVlIG9iamVjdCBpcyB1c2VkIGluIGEgY2xpcHBpbmcgb3BlcmF0aW9uIG9uIG9wZW4gcGF0aHMsIHR3byBhbmNpbGxpYXJ5IGZ1bmN0aW9ucyBoYXZlIGJlZW4gcHJvdmlkZWQgdG8gcXVpY2tseSBzZXBhcmF0ZSBvdXQgb3BlbiBhbmRcclxuICAgKiBjbG9zZWQgcGF0aHMgZnJvbSB0aGUgc29sdXRpb24gLSBPcGVuUGF0aHNGcm9tUG9seVRyZWUgYW5kIENsb3NlZFBhdGhzRnJvbVBvbHlUcmVlLiBQb2x5VHJlZVRvUGF0aHMgaXMgYWxzbyBhdmFpbGFibGUgdG8gY29udmVydCBwYXRoIGRhdGEgdG8gYSBQYXRoc1xyXG4gICAqIHN0cnVjdHVyZSAoaXJyZXNwZWN0aXZlIG9mIHdoZXRoZXIgdGhleSdyZSBvcGVuIG9yIGNsb3NlZCkuXHJcbiAgICpcclxuICAgKiBUaGVyZSBhcmUgc2V2ZXJhbCB0aGluZ3MgdG8gbm90ZSBhYm91dCB0aGUgc29sdXRpb24gcGF0aHMgcmV0dXJuZWQ6XHJcbiAgICogLSB0aGV5IGFyZW4ndCBpbiBhbnkgc3BlY2lmaWMgb3JkZXJcclxuICAgKiAtIHRoZXkgc2hvdWxkIG5ldmVyIG92ZXJsYXAgb3IgYmUgc2VsZi1pbnRlcnNlY3RpbmcgKGJ1dCBzZWUgbm90ZXMgb24gcm91bmRpbmcpXHJcbiAgICogLSBob2xlcyB3aWxsIGJlIG9yaWVudGVkIG9wcG9zaXRlIG91dGVyIHBvbHlnb25zXHJcbiAgICogLSB0aGUgc29sdXRpb24gZmlsbCB0eXBlIGNhbiBiZSBjb25zaWRlcmVkIGVpdGhlciBFdmVuT2RkIG9yIE5vblplcm8gc2luY2UgaXQgd2lsbCBjb21wbHkgd2l0aCBlaXRoZXIgZmlsbGluZyBydWxlXHJcbiAgICogLSBwb2x5Z29ucyBtYXkgcmFyZWx5IHNoYXJlIGEgY29tbW9uIGVkZ2UgKHRob3VnaCB0aGlzIGlzIG5vdyB2ZXJ5IHJhcmUgYXMgb2YgdmVyc2lvbiA2KVxyXG4gICAqXHJcbiAgICogVGhlIHN1YmpGaWxsVHlwZSBhbmQgY2xpcEZpbGxUeXBlIHBhcmFtZXRlcnMgZGVmaW5lIHRoZSBwb2x5Z29uIGZpbGwgcnVsZSB0byBiZSBhcHBsaWVkIHRvIHRoZSBwb2x5Z29ucyAoaWUgY2xvc2VkIHBhdGhzKSBpbiB0aGUgc3ViamVjdCBhbmQgY2xpcFxyXG4gICAqIHBhdGhzIHJlc3BlY3RpdmVseS4gKEl0J3MgdXN1YWwgdGhvdWdoIG9idmlvdXNseSBub3QgZXNzZW50aWFsIHRoYXQgYm90aCBzZXRzIG9mIHBvbHlnb25zIHVzZSB0aGUgc2FtZSBmaWxsIHJ1bGUuKVxyXG4gICAqXHJcbiAgICogZXhlY3V0ZSBjYW4gYmUgY2FsbGVkIG11bHRpcGxl