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

244 lines 36.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClipperOffset = 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"); /** * The ClipperOffset class encapsulates the process of offsetting (inflating/deflating) both open and closed paths using a number of different join types * and end types. * * Preconditions for offsetting: * 1. The orientations of closed paths must be consistent such that outer polygons share the same orientation, and any holes have the opposite orientation * (ie non-zero filling). Open paths must be oriented with closed outer polygons. * 2. Polygons must not self-intersect. * * Limitations: * When offsetting, small artefacts may appear where polygons overlap. To avoid these artefacts, offset overlapping polygons separately. */ var ClipperOffset = /** @class */ (function () { /** * The ClipperOffset constructor takes 2 optional parameters: MiterLimit and ArcTolerance. The two parameters corresponds to properties of the same name. * MiterLimit is only relevant when JoinType is Miter, and ArcTolerance is only relevant when JoinType is Round or when EndType is OpenRound. * * @param _nativeLib - Native clipper lib instance to use * @param miterLimit - Miter limit * @param arcTolerance - ArcTolerance (round precision) */ function ClipperOffset(_nativeLib, miterLimit, arcTolerance) { if (miterLimit === void 0) { miterLimit = 2; } if (arcTolerance === void 0) { arcTolerance = 0.25; } this._nativeLib = _nativeLib; this._clipperOffset = new _nativeLib.ClipperOffset(miterLimit, arcTolerance); nativeFinalizationRegistry_1.nativeFinalizationRegistry === null || nativeFinalizationRegistry_1.nativeFinalizationRegistry === void 0 ? void 0 : nativeFinalizationRegistry_1.nativeFinalizationRegistry.register(this, this._clipperOffset, this); } Object.defineProperty(ClipperOffset.prototype, "arcTolerance", { /** * Firstly, this field/property is only relevant when JoinType = Round and/or EndType = Round. * * Since flattened paths can never perfectly represent arcs, this field/property specifies a maximum acceptable imprecision ('tolerance') when arcs are * approximated in an offsetting operation. Smaller values will increase 'smoothness' up to a point though at a cost of performance and in creating more * vertices to construct the arc. * * The default ArcTolerance is 0.25 units. This means that the maximum distance the flattened path will deviate from the 'true' arc will be no more * than 0.25 units (before rounding). * * Reducing tolerances below 0.25 will not improve smoothness since vertex coordinates will still be rounded to integer values. The only way to achieve * sub-integer precision is through coordinate scaling before and after offsetting (see example below). * * It's important to make ArcTolerance a sensible fraction of the offset delta (arc radius). Large tolerances relative to the offset delta will produce * poor arc approximations but, just as importantly, very small tolerances will substantially slow offsetting performance while providing unnecessary * degrees of precision. This is most likely to be an issue when offsetting polygons whose coordinates have been scaled to preserve floating point precision. * * Example: Imagine a set of polygons (defined in floating point coordinates) that is to be offset by 10 units using round joins, and the solution is to * retain floating point precision up to at least 6 decimal places. * To preserve this degree of floating point precision, and given that Clipper and ClipperOffset both operate on integer coordinates, the polygon * coordinates will be scaled up by 108 (and rounded to integers) prior to offsetting. Both offset delta and ArcTolerance will also need to be scaled * by this same factor. If ArcTolerance was left unscaled at the default 0.25 units, every arc in the solution would contain a fraction of 44 THOUSAND * vertices while the final arc imprecision would be 0.25 × 10-8 units (ie once scaling was reversed). However, if 0.1 units was an acceptable imprecision * in the final unscaled solution, then ArcTolerance should be set to 0.1 × scaling_factor (0.1 × 108 ). Now if scaling is applied equally to both * ArcTolerance and to Delta Offset, then in this example the number of vertices (steps) defining each arc would be a fraction of 23. * * The formula for the number of steps in a full circular arc is ... Pi / acos(1 - arc_tolerance / abs(delta)) * * @return {number} - Current arc tolerance */ get: function () { return this._clipperOffset.arcTolerance; }, /** * Firstly, this field/property is only relevant when JoinType = Round and/or EndType = Round. * * Since flattened paths can never perfectly represent arcs, this field/property specifies a maximum acceptable imprecision ('tolerance') when arcs are * approximated in an offsetting operation. Smaller values will increase 'smoothness' up to a point though at a cost of performance and in creating more * vertices to construct the arc. * * The default ArcTolerance is 0.25 units. This means that the maximum distance the flattened path will deviate from the 'true' arc will be no more * than 0.25 units (before rounding). * * Reducing tolerances below 0.25 will not improve smoothness since vertex coordinates will still be rounded to integer values. The only way to achieve * sub-integer precision is through coordinate scaling before and after offsetting (see example below). * * It's important to make ArcTolerance a sensible fraction of the offset delta (arc radius). Large tolerances relative to the offset delta will produce * poor arc approximations but, just as importantly, very small tolerances will substantially slow offsetting performance while providing unnecessary * degrees of precision. This is most likely to be an issue when offsetting polygons whose coordinates have been scaled to preserve floating point precision. * * Example: Imagine a set of polygons (defined in floating point coordinates) that is to be offset by 10 units using round joins, and the solution is to * retain floating point precision up to at least 6 decimal places. * To preserve this degree of floating point precision, and given that Clipper and ClipperOffset both operate on integer coordinates, the polygon * coordinates will be scaled up by 108 (and rounded to integers) prior to offsetting. Both offset delta and ArcTolerance will also need to be scaled * by this same factor. If ArcTolerance was left unscaled at the default 0.25 units, every arc in the solution would contain a fraction of 44 THOUSAND * vertices while the final arc imprecision would be 0.25 × 10-8 units (ie once scaling was reversed). However, if 0.1 units was an acceptable imprecision * in the final unscaled solution, then ArcTolerance should be set to 0.1 × scaling_factor (0.1 × 108 ). Now if scaling is applied equally to both * ArcTolerance and to Delta Offset, then in this example the number of vertices (steps) defining each arc would be a fraction of 23. * * The formula for the number of steps in a full circular arc is ... Pi / acos(1 - arc_tolerance / abs(delta)) * * @param value - Arc tolerance to set. */ set: function (value) { this._clipperOffset.arcTolerance = value; }, enumerable: false, configurable: true }); Object.defineProperty(ClipperOffset.prototype, "miterLimit", { /** * This property sets the maximum distance in multiples of delta that vertices can be offset from their original positions before squaring is applied. * (Squaring truncates a miter by 'cutting it off' at 1 × delta distance from the original vertex.) * * The default value for MiterLimit is 2 (ie twice delta). This is also the smallest MiterLimit that's allowed. If mitering was unrestricted (ie without * any squaring), then offsets at very acute angles would generate unacceptably long 'spikes'. * * @return {number} - Current miter limit */ get: function () { return this._clipperOffset.miterLimit; }, /** * Sets the current miter limit (see getter docs for more info). * * @param value - Mit limit to set. */ set: function (value) { this._clipperOffset.miterLimit = value; }, enumerable: false, configurable: true }); /** * Adds a Path to a ClipperOffset object in preparation for offsetting. * * Any number of paths can be added, and each has its own JoinType and EndType. All 'outer' Paths must have the same orientation, and any 'hole' paths must * have reverse orientation. Closed paths must have at least 3 vertices. Open paths may have as few as one vertex. Open paths can only be offset * with positive deltas. * * @param path - Path to add * @param joinType - Join type * @param endType - End type */ ClipperOffset.prototype.addPath = function (path, joinType, endType) { var nativePath = (0, PathToNativePath_1.pathToNativePath)(this._nativeLib, path); try { this._clipperOffset.addPath(nativePath, (0, nativeEnumConversion_1.joinTypeToNative)(this._nativeLib, joinType), (0, nativeEnumConversion_1.endTypeToNative)(this._nativeLib, endType)); } finally { nativePath.delete(); } }; /** * Adds Paths to a ClipperOffset object in preparation for offsetting. * * Any number of paths can be added, and each path has its own JoinType and EndType. All 'outer' Paths must have the same orientation, and any 'hole' * paths must have reverse orientation. Closed paths must have at least 3 vertices. Open paths may have as few as one vertex. Open paths can only be * offset with positive deltas. * * @param paths - Paths to add * @param joinType - Join type * @param endType - End type */ ClipperOffset.prototype.addPaths = function (paths, joinType, endType) { var nativePaths = (0, PathsToNativePaths_1.pathsToNativePaths)(this._nativeLib, paths); try { this._clipperOffset.addPaths(nativePaths, (0, nativeEnumConversion_1.joinTypeToNative)(this._nativeLib, joinType), (0, nativeEnumConversion_1.endTypeToNative)(this._nativeLib, endType)); } finally { nativePaths.delete(); } }; /** * Negative delta values shrink polygons and positive delta expand them. * * This method can be called multiple times, offsetting the same paths by different amounts (ie using different deltas). * * @param delta - Delta * @param cleanDistance - Clean distance over the output, or undefined for no cleaning. * @return {Paths} - Solution paths */ ClipperOffset.prototype.executeToPaths = function (delta, cleanDistance) { var outNativePaths = new this._nativeLib.Paths(); try { this._clipperOffset.executePaths(outNativePaths, delta); 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(); } } }; /** * This method takes two parameters. The first is the structure that receives the result of the offset operation (a PolyTree structure). The second parameter * is the amount to which the supplied paths will be offset. Negative delta values shrink polygons and positive delta expand them. * * This method can be called multiple times, offsetting the same paths by different amounts (ie using different deltas). * * @param delta - Delta * @return {Paths} - Solution paths */ ClipperOffset.prototype.executeToPolyTree = function (delta) { var outNativePolyTree = new this._nativeLib.PolyTree(); try { this._clipperOffset.executePolyTree(outNativePolyTree, delta); return PolyTree_1.PolyTree.fromNativePolyTree(this._nativeLib, outNativePolyTree, true); // frees outNativePolyTree } finally { if (!outNativePolyTree.isDeleted()) { outNativePolyTree.delete(); } } }; /** * This method clears all paths from the ClipperOffset object, allowing new paths to be assigned. */ ClipperOffset.prototype.clear = function () { this._clipperOffset.clear(); }; /** * Checks if the object has been disposed. * * @return {boolean} - true if disposed, false if not */ ClipperOffset.prototype.isDisposed = function () { return this._clipperOffset === undefined || this._clipperOffset.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). */ ClipperOffset.prototype.dispose = function () { if (this._clipperOffset) { this._clipperOffset.delete(); nativeFinalizationRegistry_1.nativeFinalizationRegistry === null || nativeFinalizationRegistry_1.nativeFinalizationRegistry === void 0 ? void 0 : nativeFinalizationRegistry_1.nativeFinalizationRegistry.unregister(this); this._clipperOffset = undefined; } }; return ClipperOffset; }()); exports.ClipperOffset = ClipperOffset; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2xpcHBlck9mZnNldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9DbGlwcGVyT2Zmc2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUlBLHNFQUFrRjtBQUNsRixrRUFBcUY7QUFDckYsOERBQTZEO0FBRzdELHVDQUFzQztBQUN0QywyRUFBMEU7QUFFMUU7Ozs7Ozs7Ozs7O0dBV0c7QUFDSDtJQTZGRTs7Ozs7OztPQU9HO0lBQ0gsdUJBQ21CLFVBQW9DLEVBQ3JELFVBQWMsRUFDZCxZQUFtQjtRQURuQiwyQkFBQSxFQUFBLGNBQWM7UUFDZCw2QkFBQSxFQUFBLG1CQUFtQjtRQUZGLGVBQVUsR0FBVixVQUFVLENBQTBCO1FBSXJELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxVQUFVLENBQUMsYUFBYSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUM3RSx1REFBMEIsYUFBMUIsdURBQTBCLHVCQUExQix1REFBMEIsQ0FBRSxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDeEUsQ0FBQztJQTNFRCxzQkFBSSx1Q0FBWTtRQTlCaEI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBNkJHO2FBQ0g7WUFDRSxPQUFPLElBQUksQ0FBQyxjQUFlLENBQUMsWUFBWSxDQUFDO1FBQzNDLENBQUM7UUFFRDs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7V0E2Qkc7YUFDSCxVQUFpQixLQUFhO1lBQzVCLElBQUksQ0FBQyxjQUFlLENBQUMsWUFBWSxHQUFHLEtBQUssQ0FBQztRQUM1QyxDQUFDOzs7T0FsQ0E7SUE2Q0Qsc0JBQUkscUNBQVU7UUFUZDs7Ozs7Ozs7V0FRRzthQUNIO1lBQ0UsT0FBTyxJQUFJLENBQUMsY0FBZSxDQUFDLFVBQVUsQ0FBQztRQUN6QyxDQUFDO1FBRUQ7Ozs7V0FJRzthQUNILFVBQWUsS0FBYTtZQUMxQixJQUFJLENBQUMsY0FBZSxD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QVMsRUFBRSxDQUFDO0lBQzlFLENBQUM7SUFFRDs7O09BR0c7SUFDSCwrQkFBTyxHQUFQO1FBQ0UsSUFBSSxJQUFJLENBQUMsY0FBYyxFQUFFO1lBQ3ZCLElBQUksQ0FBQyxjQUFjLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDN0IsdURBQTBCLGFBQTFCLHVEQUEwQix1QkFBMUIsdURBQTBCLENBQUUsVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQzdDLElBQUksQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFDO1NBQ2pDO0lBQ0gsQ0FBQztJQUNILG9CQUFDO0FBQUQsQ0FBQyxBQXRPRCxJQXNPQztBQXRPWSxzQ0FBYSIsInNvdXJjZXNDb250ZW50IjpbIi8qIGVzbGludC1kaXNhYmxlIEB0eXBlc2NyaXB0LWVzbGludC9uby1ub24tbnVsbC1hc3NlcnRpb24gKi9cbmltcG9ydCB7IEVuZFR5cGUsIEpvaW5UeXBlIH0gZnJvbSBcIi4vZW51bXNcIjtcbmltcG9ydCB7IE5hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZSB9IGZyb20gXCIuL25hdGl2ZS9OYXRpdmVDbGlwcGVyTGliSW5zdGFuY2VcIjtcbmltcG9ydCB7IE5hdGl2ZUNsaXBwZXJPZmZzZXQgfSBmcm9tIFwiLi9uYXRpdmUvTmF0aXZlQ2xpcHBlck9mZnNldFwiO1xuaW1wb3J0IHsgZW5kVHlwZVRvTmF0aXZlLCBqb2luVHlwZVRvTmF0aXZlIH0gZnJvbSBcIi4vbmF0aXZlL25hdGl2ZUVudW1Db252ZXJzaW9uXCI7XG5pbXBvcnQgeyBuYXRpdmVQYXRoc1RvUGF0aHMsIHBhdGhzVG9OYXRpdmVQYXRocyB9IGZyb20gXCIuL25hdGl2ZS9QYXRoc1RvTmF0aXZlUGF0aHNcIjtcbmltcG9ydCB7IHBhdGhUb05hdGl2ZVBhdGggfSBmcm9tIFwiLi9uYXRpdmUvUGF0aFRvTmF0aXZlUGF0aFwiO1xuaW1wb3J0IHsgUmVhZG9ubHlQYXRoIH0gZnJvbSBcIi4vUGF0aFwiO1xuaW1wb3J0IHsgUGF0aHMsIFJlYWRvbmx5UGF0aHMgfSBmcm9tIFwiLi9QYXRoc1wiO1xuaW1wb3J0IHsgUG9seVRyZWUgfSBmcm9tIFwiLi9Qb2x5VHJlZVwiO1xuaW1wb3J0IHsgbmF0aXZlRmluYWxpemF0aW9uUmVnaXN0cnkgfSBmcm9tIFwiLi9uYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeVwiO1xuXG4vKipcbiAqIFRoZSBDbGlwcGVyT2Zmc2V0IGNsYXNzIGVuY2Fwc3VsYXRlcyB0aGUgcHJvY2VzcyBvZiBvZmZzZXR0aW5nIChpbmZsYXRpbmcvZGVmbGF0aW5nKSBib3RoIG9wZW4gYW5kIGNsb3NlZCBwYXRocyB1c2luZyBhIG51bWJlciBvZiBkaWZmZXJlbnQgam9pbiB0eXBlc1xuICogYW5kIGVuZCB0eXBlcy5cbiAqXG4gKiBQcmVjb25kaXRpb25zIGZvciBvZmZzZXR0aW5nOlxuICogMS4gVGhlIG9yaWVudGF0aW9ucyBvZiBjbG9zZWQgcGF0aHMgbXVzdCBiZSBjb25zaXN0ZW50IHN1Y2ggdGhhdCBvdXRlciBwb2x5Z29ucyBzaGFyZSB0aGUgc2FtZSBvcmllbnRhdGlvbiwgYW5kIGFueSBob2xlcyBoYXZlIHRoZSBvcHBvc2l0ZSBvcmllbnRhdGlvblxuICogKGllIG5vbi16ZXJvIGZpbGxpbmcpLiBPcGVuIHBhdGhzIG11c3QgYmUgb3JpZW50ZWQgd2l0aCBjbG9zZWQgb3V0ZXIgcG9seWdvbnMuXG4gKiAyLiBQb2x5Z29ucyBtdXN0IG5vdCBzZWxmLWludGVyc2VjdC5cbiAqXG4gKiBMaW1pdGF0aW9uczpcbiAqIFdoZW4gb2Zmc2V0dGluZywgc21hbGwgYXJ0ZWZhY3RzIG1heSBhcHBlYXIgd2hlcmUgcG9seWdvbnMgb3ZlcmxhcC4gVG8gYXZvaWQgdGhlc2UgYXJ0ZWZhY3RzLCBvZmZzZXQgb3ZlcmxhcHBpbmcgcG9seWdvbnMgc2VwYXJhdGVseS5cbiAqL1xuZXhwb3J0IGNsYXNzIENsaXBwZXJPZmZzZXQge1xuICBwcml2YXRlIF9jbGlwcGVyT2Zmc2V0PzogTmF0aXZlQ2xpcHBlck9mZnNldDtcblxuICAvKipcbiAgICogRmlyc3RseSwgdGhpcyBmaWVsZC9wcm9wZXJ0eSBpcyBvbmx5IHJlbGV2YW50IHdoZW4gSm9pblR5cGUgPSBSb3VuZCBhbmQvb3IgRW5kVHlwZSA9IFJvdW5kLlxuICAgKlxuICAgKiBTaW5jZSBmbGF0dGVuZWQgcGF0aHMgY2FuIG5ldmVyIHBlcmZlY3RseSByZXByZXNlbnQgYXJjcywgdGhpcyBmaWVsZC9wcm9wZXJ0eSBzcGVjaWZpZXMgYSBtYXhpbXVtIGFjY2VwdGFibGUgaW1wcmVjaXNpb24gKCd0b2xlcmFuY2UnKSB3aGVuIGFyY3MgYXJlXG4gICAqIGFwcHJveGltYXRlZCBpbiBhbiBvZmZzZXR0aW5nIG9wZXJhdGlvbi4gU21hbGxlciB2YWx1ZXMgd2lsbCBpbmNyZWFzZSAnc21vb3RobmVzcycgdXAgdG8gYSBwb2ludCB0aG91Z2ggYXQgYSBjb3N0IG9mIHBlcmZvcm1hbmNlIGFuZCBpbiBjcmVhdGluZyBtb3JlXG4gICAqIHZlcnRpY2VzIHRvIGNvbnN0cnVjdCB0aGUgYXJjLlxuICAgKlxuICAgKiBUaGUgZGVmYXVsdCBBcmNUb2xlcmFuY2UgaXMgMC4yNSB1bml0cy4gVGhpcyBtZWFucyB0aGF0IHRoZSBtYXhpbXVtIGRpc3RhbmNlIHRoZSBmbGF0dGVuZWQgcGF0aCB3aWxsIGRldmlhdGUgZnJvbSB0aGUgJ3RydWUnIGFyYyB3aWxsIGJlIG5vIG1vcmVcbiAgICogdGhhbiAwLjI1IHVuaXRzIChiZWZvcmUgcm91bmRpbmcpLlxuICAgKlxuICAgKiBSZWR1Y2luZyB0b2xlcmFuY2VzIGJlbG93IDAuMjUgd2lsbCBub3QgaW1wcm92ZSBzbW9vdGhuZXNzIHNpbmNlIHZlcnRleCBjb29yZGluYXRlcyB3aWxsIHN0aWxsIGJlIHJvdW5kZWQgdG8gaW50ZWdlciB2YWx1ZXMuIFRoZSBvbmx5IHdheSB0byBhY2hpZXZlXG4gICAqIHN1Yi1pbnRlZ2VyIHByZWNpc2lvbiBpcyB0aHJvdWdoIGNvb3JkaW5hdGUgc2NhbGluZyBiZWZvcmUgYW5kIGFmdGVyIG9mZnNldHRpbmcgKHNlZSBleGFtcGxlIGJlbG93KS5cbiAgICpcbiAgICogSXQncyBpbXBvcnRhbnQgdG8gbWFrZSBBcmNUb2xlcmFuY2UgYSBzZW5zaWJsZSBmcmFjdGlvbiBvZiB0aGUgb2Zmc2V0IGRlbHRhIChhcmMgcmFkaXVzKS4gTGFyZ2UgdG9sZXJhbmNlcyByZWxhdGl2ZSB0byB0aGUgb2Zmc2V0IGRlbHRhIHdpbGwgcHJvZHVjZVxuICAgKiBwb29yIGFyYyBhcHByb3hpbWF0aW9ucyBidXQsIGp1c3QgYXMgaW1wb3J0YW50bHksIHZlcnkgc21hbGwgdG9sZXJhbmNlcyB3aWxsIHN1YnN0YW50aWFsbHkgc2xvdyBvZmZzZXR0aW5nIHBlcmZvcm1hbmNlIHdoaWxlIHByb3ZpZGluZyB1bm5lY2Vzc2FyeVxuICAgKiBkZWdyZWVzIG9mIHByZWNpc2lvbi4gVGhpcyBpcyBtb3N0IGxpa2VseSB0byBiZSBhbiBpc3N1ZSB3aGVuIG9mZnNldHRpbmcgcG9seWdvbnMgd2hvc2UgY29vcmRpbmF0ZXMgaGF2ZSBiZWVuIHNjYWxlZCB0byBwcmVzZXJ2ZSBmbG9hdGluZyBwb2ludCBwcmVjaXNpb24uXG4gICAqXG4gICAqIEV4YW1wbGU6IEltYWdpbmUgYSBzZXQgb2YgcG9seWdvbnMgKGRlZmluZWQgaW4gZmxvYXRpbmcgcG9pbnQgY29vcmRpbmF0ZXMpIHRoYXQgaXMgdG8gYmUgb2Zmc2V0IGJ5IDEwIHVuaXRzIHVzaW5nIHJvdW5kIGpvaW5zLCBhbmQgdGhlIHNvbHV0aW9uIGlzIHRvXG4gICAqIHJldGFpbiBmbG9hdGluZyBwb2ludCBwcmVjaXNpb24gdXAgdG8gYXQgbGVhc3QgNiBkZWNpbWFsIHBsYWNlcy5cbiAgICogVG8gcHJlc2VydmUgdGhpcyBkZWdyZWUgb2YgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLCBhbmQgZ2l2ZW4gdGhhdCBDbGlwcGVyIGFuZCBDbGlwcGVyT2Zmc2V0IGJvdGggb3BlcmF0ZSBvbiBpbnRlZ2VyIGNvb3JkaW5hdGVzLCB0aGUgcG9seWdvblxuICAgKiBjb29yZGluYXRlcyB3aWxsIGJlIHNjYWxlZCB1cCBieSAxMDggKGFuZCByb3VuZGVkIHRvIGludGVnZXJzKSBwcmlvciB0byBvZmZzZXR0aW5nLiBCb3RoIG9mZnNldCBkZWx0YSBhbmQgQXJjVG9sZXJhbmNlIHdpbGwgYWxzbyBuZWVkIHRvIGJlIHNjYWxlZFxuICAgKiBieSB0aGlzIHNhbWUgZmFjdG9yLiBJZiBBcmNUb2xlcmFuY2Ugd2FzIGxlZnQgdW5zY2FsZWQgYXQgdGhlIGRlZmF1bHQgMC4yNSB1bml0cywgZXZlcnkgYXJjIGluIHRoZSBzb2x1dGlvbiB3b3VsZCBjb250YWluIGEgZnJhY3Rpb24gb2YgNDQgVEhPVVNBTkRcbiAgICogdmVydGljZXMgd2hpbGUgdGhlIGZpbmFsIGFyYyBpbXByZWNpc2lvbiB3b3VsZCBiZSAwLjI1IMOXIDEwLTggdW5pdHMgKGllIG9uY2Ugc2NhbGluZyB3YXMgcmV2ZXJzZWQpLiBIb3dldmVyLCBpZiAwLjEgdW5pdHMgd2FzIGFuIGFjY2VwdGFibGUgaW1wcmVjaXNpb25cbiAgICogaW4gdGhlIGZpbmFsIHVuc2NhbGVkIHNvbHV0aW9uLCB0aGVuIEFyY1RvbGVyYW5jZSBzaG91bGQgYmUgc2V0IHRvIDAuMSDDlyBzY2FsaW5nX2ZhY3RvciAoMC4xIMOXIDEwOCApLiBOb3cgaWYgc2NhbGluZyBpcyBhcHBsaWVkIGVxdWFsbHkgdG8gYm90aFxuICAgKiBBcmNUb2xlcmFuY2UgYW5kIHRvIERlbHRhIE9mZnNldCwgdGhlbiBpbiB0aGlzIGV4YW1wbGUgdGhlIG51bWJlciBvZiB2ZXJ0aWNlcyAoc3RlcHMpIGRlZmluaW5nIGVhY2ggYXJjIHdvdWxkIGJlIGEgZnJhY3Rpb24gb2YgMjMuXG4gICAqXG4gICAqIFRoZSBmb3JtdWxhIGZvciB0aGUgbnVtYmVyIG9mIHN0ZXBzIGluIGEgZnVsbCBjaXJjdWxhciBhcmMgaXMgLi4uIFBpIC8gYWNvcygxIC0gYXJjX3RvbGVyYW5jZSAvIGFicyhkZWx0YSkpXG4gICAqXG4gICAqIEByZXR1cm4ge251bWJlcn0gLSBDdXJyZW50IGFyYyB0b2xlcmFuY2VcbiAgICovXG4gIGdldCBhcmNUb2xlcmFuY2UoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlck9mZnNldCEuYXJjVG9sZXJhbmNlO1xuICB9XG5cbiAgLyoqXG4gICAqIEZpcnN0bHksIHRoaXMgZmllbGQvcHJvcGVydHkgaXMgb25seSByZWxldmFudCB3aGVuIEpvaW5UeXBlID0gUm91bmQgYW5kL29yIEVuZFR5cGUgPSBSb3VuZC5cbiAgICpcbiAgICogU2luY2UgZmxhdHRlbmVkIHBhdGhzIGNhbiBuZXZlciBwZXJmZWN0bHkgcmVwcmVzZW50IGFyY3MsIHRoaXMgZmllbGQvcHJvcGVydHkgc3BlY2lmaWVzIGEgbWF4aW11bSBhY2NlcHRhYmxlIGltcHJlY2lzaW9uICgndG9sZXJhbmNlJykgd2hlbiBhcmNzIGFyZVxuICAgKiBhcHByb3hpbWF0ZWQgaW4gYW4gb2Zmc2V0dGluZyBvcGVyYXRpb24uIFNtYWxsZXIgdmFsdWVzIHdpbGwgaW5jcmVhc2UgJ3Ntb290aG5lc3MnIHVwIHRvIGEgcG9pbnQgdGhvdWdoIGF0IGEgY29zdCBvZiBwZXJmb3JtYW5jZSBhbmQgaW4gY3JlYXRpbmcgbW9yZVxuICAgKiB2ZXJ0aWNlcyB0byBjb25zdHJ1Y3QgdGhlIGFyYy5cbiAgICpcbiAgICogVGhlIGRlZmF1bHQgQXJjVG9sZXJhbmNlIGlzIDAuMjUgdW5pdHMuIFRoaXMgbWVhbnMgdGhhdCB0aGUgbWF4aW11bSBkaXN0YW5jZSB0aGUgZmxhdHRlbmVkIHBhdGggd2lsbCBkZXZpYXRlIGZyb20gdGhlICd0cnVlJyBhcmMgd2lsbCBiZSBubyBtb3JlXG4gICAqIHRoYW4gMC4yNSB1bml0cyAoYmVmb3JlIHJvdW5kaW5nKS5cbiAgICpcbiAgICogUmVkdWNpbmcgdG9sZXJhbmNlcyBiZWxvdyAwLjI1IHdpbGwgbm90IGltcHJvdmUgc21vb3RobmVzcyBzaW5jZSB2ZXJ0ZXggY29vcmRpbmF0ZXMgd2lsbCBzdGlsbCBiZSByb3VuZGVkIHRvIGludGVnZXIgdmFsdWVzLiBUaGUgb25seSB3YXkgdG8gYWNoaWV2ZVxuICAgKiBzdWItaW50ZWdlciBwcmVjaXNpb24gaXMgdGhyb3VnaCBjb29yZGluYXRlIHNjYWxpbmcgYmVmb3JlIGFuZCBhZnRlciBvZmZzZXR0aW5nIChzZWUgZXhhbXBsZSBiZWxvdykuXG4gICAqXG4gICAqIEl0J3MgaW1wb3J0YW50IHRvIG1ha2UgQXJjVG9sZXJhbmNlIGEgc2Vuc2libGUgZnJhY3Rpb24gb2YgdGhlIG9mZnNldCBkZWx0YSAoYXJjIHJhZGl1cykuIExhcmdlIHRvbGVyYW5jZXMgcmVsYXRpdmUgdG8gdGhlIG9mZnNldCBkZWx0YSB3aWxsIHByb2R1Y2VcbiAgICogcG9vciBhcmMgYXBwcm94aW1hdGlvbnMgYnV0LCBqdXN0IGFzIGltcG9ydGFudGx5LCB2ZXJ5IHNtYWxsIHRvbGVyYW5jZXMgd2lsbCBzdWJzdGFudGlhbGx5IHNsb3cgb2Zmc2V0dGluZyBwZXJmb3JtYW5jZSB3aGlsZSBwcm92aWRpbmcgdW5uZWNlc3NhcnlcbiAgICogZGVncmVlcyBvZiBwcmVjaXNpb24uIFRoaXMgaXMgbW9zdCBsaWtlbHkgdG8gYmUgYW4gaXNzdWUgd2hlbiBvZmZzZXR0aW5nIHBvbHlnb25zIHdob3NlIGNvb3JkaW5hdGVzIGhhdmUgYmVlbiBzY2FsZWQgdG8gcHJlc2VydmUgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLlxuICAgKlxuICAgKiBFeGFtcGxlOiBJbWFnaW5lIGEgc2V0IG9mIHBvbHlnb25zIChkZWZpbmVkIGluIGZsb2F0aW5nIHBvaW50IGNvb3JkaW5hdGVzKSB0aGF0IGlzIHRvIGJlIG9mZnNldCBieSAxMCB1bml0cyB1c2luZyByb3VuZCBqb2lucywgYW5kIHRoZSBzb2x1dGlvbiBpcyB0b1xuICAgKiByZXRhaW4gZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uIHVwIHRvIGF0IGxlYXN0IDYgZGVjaW1hbCBwbGFjZXMuXG4gICAqIFRvIHByZXNlcnZlIHRoaXMgZGVncmVlIG9mIGZsb2F0aW5nIHBvaW50IHByZWNpc2lvbiwgYW5kIGdpdmVuIHRoYXQgQ2xpcHBlciBhbmQgQ2xpcHBlck9mZnNldCBib3RoIG9wZXJhdGUgb24gaW50ZWdlciBjb29yZGluYXRlcywgdGhlIHBvbHlnb25cbiAgICogY29vcmRpbmF0ZXMgd2lsbCBiZSBzY2FsZWQgdXAgYnkgMTA4IChhbmQgcm91bmRlZCB0byBpbnRlZ2VycykgcHJpb3IgdG8gb2Zmc2V0dGluZy4gQm90aCBvZmZzZXQgZGVsdGEgYW5kIEFyY1RvbGVyYW5jZSB3aWxsIGFsc28gbmVlZCB0byBiZSBzY2FsZWRcbiAgICogYnkgdGhpcyBzYW1lIGZhY3Rvci4gSWYgQXJjVG9sZXJhbmNlIHdhcyBsZWZ0IHVuc2NhbGVkIGF0IHRoZSBkZWZhdWx0IDAuMjUgdW5pdHMsIGV2ZXJ5IGFyYyBpbiB0aGUgc29sdXRpb24gd291bGQgY29udGFpbiBhIGZyYWN0aW9uIG9mIDQ0IFRIT1VTQU5EXG4gICAqIHZlcnRpY2VzIHdoaWxlIHRoZSBmaW5hbCBhcmMgaW1wcmVjaXNpb24gd291bGQgYmUgMC4yNSDDlyAxMC04IHVuaXRzIChpZSBvbmNlIHNjYWxpbmcgd2FzIHJldmVyc2VkKS4gSG93ZXZlciwgaWYgMC4xIHVuaXRzIHdhcyBhbiBhY2NlcHRhYmxlIGltcHJlY2lzaW9uXG4gICAqIGluIHRoZSBmaW5hbCB1bnNjYWxlZCBzb2x1dGlvbiwgdGhlbiBBcmNUb2xlcmFuY2Ugc2hvdWxkIGJlIHNldCB0byAwLjEgw5cgc2NhbGluZ19mYWN0b3IgKDAuMSDDlyAxMDggKS4gTm93IGlmIHNjYWxpbmcgaXMgYXBwbGllZCBlcXVhbGx5IHRvIGJvdGhcbiAgICogQXJjVG9sZXJhbmNlIGFuZCB0byBEZWx0YSBPZmZzZXQsIHRoZW4gaW4gdGhpcyBleGFtcGxlIHRoZSBudW1iZXIgb2YgdmVydGljZXMgKHN0ZXBzKSBkZWZpbmluZyBlYWNoIGFyYyB3b3VsZCBiZSBhIGZyYWN0aW9uIG9mIDIzLlxuICAgKlxuICAgKiBUaGUgZm9ybXVsYSBmb3IgdGhlIG51bWJlciBvZiBzdGVwcyBpbiBhIGZ1bGwgY2lyY3VsYXIgYXJjIGlzIC4uLiBQaSAvIGFjb3MoMSAtIGFyY190b2xlcmFuY2UgLyBhYnMoZGVsdGEpKVxuICAgKlxuICAgKiBAcGFyYW0gdmFsdWUgLSBBcmMgdG9sZXJhbmNlIHRvIHNldC5cbiAgICovXG4gIHNldCBhcmNUb2xlcmFuY2UodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuX2NsaXBwZXJPZmZzZXQhLmFyY1RvbGVyYW5jZSA9IHZhbHVlO1xuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgcHJvcGVydHkgc2V0cyB0aGUgbWF4aW11bSBkaXN0YW5jZSBpbiBtdWx0aXBsZXMgb2YgZGVsdGEgdGhhdCB2ZXJ0aWNlcyBjYW4gYmUgb2Zmc2V0IGZyb20gdGhlaXIgb3JpZ2luYWwgcG9zaXRpb25zIGJlZm9yZSBzcXVhcmluZyBpcyBhcHBsaWVkLlxuICAgKiAoU3F1YXJpbmcgdHJ1bmNhdGVzIGEgbWl0ZXIgYnkgJ2N1dHRpbmcgaXQgb2ZmJyBhdCAxIMOXIGRlbHRhIGRpc3RhbmNlIGZyb20gdGhlIG9yaWdpbmFsIHZlcnRleC4pXG4gICAqXG4gICAqIFRoZSBkZWZhdWx0IHZhbHVlIGZvciBNaXRlckxpbWl0IGlzIDIgKGllIHR3aWNlIGRlbHRhKS4gVGhpcyBpcyBhbHNvIHRoZSBzbWFsbGVzdCBNaXRlckxpbWl0IHRoYXQncyBhbGxvd2VkLiBJZiBtaXRlcmluZyB3YXMgdW5yZXN0cmljdGVkIChpZSB3aXRob3V0XG4gICAqIGFueSBzcXVhcmluZyksIHRoZW4gb2Zmc2V0cyBhdCB2ZXJ5IGFjdXRlIGFuZ2xlcyB3b3VsZCBnZW5lcmF0ZSB1bmFjY2VwdGFibHkgbG9uZyAnc3Bpa2VzJy5cbiAgICpcbiAgICogQHJldHVybiB7bnVtYmVyfSAtIEN1cnJlbnQgbWl0ZXIgbGltaXRcbiAgICovXG4gIGdldCBtaXRlckxpbWl0KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX2NsaXBwZXJPZmZzZXQhLm1pdGVyTGltaXQ7XG4gIH1cblxuICAvKipcbiAgICogU2V0cyB0aGUgY3VycmVudCBtaXRlciBsaW1pdCAoc2VlIGdldHRlciBkb2NzIGZvciBtb3JlIGluZm8pLlxuICAgKlxuICAgKiBAcGFyYW0gdmFsdWUgLSBNaXQgbGltaXQgdG8gc2V0LlxuICAgKi9cbiAgc2V0IG1pdGVyTGltaXQodmFsdWU6IG51bWJlcikge1xuICAgIHRoaXMuX2NsaXBwZXJPZmZzZXQhLm1pdGVyTGltaXQgPSB2YWx1ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUaGUgQ2xpcHBlck9mZnNldCBjb25zdHJ1Y3RvciB0YWtlcyAyIG9wdGlvbmFsIHBhcmFtZXRlcnM6IE1pdGVyTGltaXQgYW5kIEFyY1RvbGVyYW5jZS4gVGhlIHR3byBwYXJhbWV0ZXJzIGNvcnJlc3BvbmRzIHRvIHByb3BlcnRpZXMgb2YgdGhlIHNhbWUgbmFtZS5cbiAgICogTWl0ZXJMaW1pdCBpcyBvbmx5IHJlbGV2YW50IHdoZW4gSm9pblR5cGUgaXMgTWl0ZXIsIGFuZCBBcmNUb2xlcmFuY2UgaXMgb25seSByZWxldmFudCB3aGVuIEpvaW5UeXBlIGlzIFJvdW5kIG9yIHdoZW4gRW5kVHlwZSBpcyBPcGVuUm91bmQuXG4gICAqXG4gICAqIEBwYXJhbSBfbmF0aXZlTGliIC0gTmF0aXZlIGNsaXBwZXIgbGliIGluc3RhbmNlIHRvIHVzZVxuICAgKiBAcGFyYW0gbWl0ZXJMaW1pdCAtIE1pdGVyIGxpbWl0XG4gICAqIEBwYXJhbSBhcmNUb2xlcmFuY2UgLSBBcmNUb2xlcmFuY2UgKHJvdW5kIHByZWNpc2lvbilcbiAgICovXG4gIGNvbnN0cnVjdG9yKFxuICAgIHByaXZhdGUgcmVhZG9ubHkgX25hdGl2ZUxpYjogTmF0aXZlQ2xpcHBlckxpYkluc3RhbmNlLFxuICAgIG1pdGVyTGltaXQgPSAyLFxuICAgIGFyY1RvbGVyYW5jZSA9IDAuMjVcbiAgKSB7XG4gICAgdGhpcy5fY2xpcHBlck9mZnNldCA9IG5ldyBfbmF0aXZlTGliLkNsaXBwZXJPZmZzZXQobWl0ZXJMaW1pdCwgYXJjVG9sZXJhbmNlKTtcbiAgICBuYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeT8ucmVnaXN0ZXIodGhpcywgdGhpcy5fY2xpcHBlck9mZnNldCwgdGhpcyk7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIFBhdGggdG8gYSBDbGlwcGVyT2Zmc2V0IG9iamVjdCBpbiBwcmVwYXJhdGlvbiBmb3Igb2Zmc2V0dGluZy5cbiAgICpcbiAgICogQW55IG51bWJlciBvZiBwYXRocyBjYW4gYmUgYWRkZWQsIGFuZCBlYWNoIGhhcyBpdHMgb3duIEpvaW5UeXBlIGFuZCBFbmRUeXBlLiBBbGwgJ291dGVyJyBQYXRocyBtdXN0IGhhdmUgdGhlIHNhbWUgb3JpZW50YXRpb24sIGFuZCBhbnkgJ2hvbGUnIHBhdGhzIG11c3RcbiAgICogaGF2ZSByZXZlcnNlIG9yaWVudGF0aW9uLiBDbG9zZWQgcGF0aHMgbXVzdCBoYXZlIGF0IGxlYXN0IDMgdmVydGljZXMuIE9wZW4gcGF0aHMgbWF5IGhhdmUgYXMgZmV3IGFzIG9uZSB2ZXJ0ZXguIE9wZW4gcGF0aHMgY2FuIG9ubHkgYmUgb2Zmc2V0XG4gICAqIHdpdGggcG9zaXRpdmUgZGVsdGFzLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFBhdGggdG8gYWRkXG4gICAqIEBwYXJhbSBqb2luVHlwZSAtIEpvaW4gdHlwZVxuICAgKiBAcGFyYW0gZW5kVHlwZSAtIEVuZCB0eXBlXG4gICAqL1xuICBhZGRQYXRoKHBhdGg6IFJlYWRvbmx5UGF0aCwgam9pblR5cGU6IEpvaW5UeXBlLCBlbmRUeXBlOiBFbmRUeXBlKTogdm9pZCB7XG4gICAgY29uc3QgbmF0aXZlUGF0aCA9IHBhdGhUb05hdGl2ZVBhdGgodGhpcy5fbmF0aXZlTGliLCBwYXRoKTtcbiAgICB0cnkge1xuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldCEuYWRkUGF0aChcbiAgICAgICAgbmF0aXZlUGF0aCxcbiAgICAgICAgam9pblR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIGpvaW5UeXBlKSxcbiAgICAgICAgZW5kVHlwZVRvTmF0aXZlKHRoaXMuX25hdGl2ZUxpYiwgZW5kVHlwZSlcbiAgICAgICk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIG5hdGl2ZVBhdGguZGVsZXRlKCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgUGF0aHMgdG8gYSBDbGlwcGVyT2Zmc2V0IG9iamVjdCBpbiBwcmVwYXJhdGlvbiBmb3Igb2Zmc2V0dGluZy5cbiAgICpcbiAgICogQW55IG51bWJlciBvZiBwYXRocyBjYW4gYmUgYWRkZWQsIGFuZCBlYWNoIHBhdGggaGFzIGl0cyBvd24gSm9pblR5cGUgYW5kIEVuZFR5cGUuIEFsbCAnb3V0ZXInIFBhdGhzIG11c3QgaGF2ZSB0aGUgc2FtZSBvcmllbnRhdGlvbiwgYW5kIGFueSAnaG9sZSdcbiAgICogcGF0aHMgbXVzdCBoYXZlIHJldmVyc2Ugb3JpZW50YXRpb24uIENsb3NlZCBwYXRocyBtdXN0IGhhdmUgYXQgbGVhc3QgMyB2ZXJ0aWNlcy4gT3BlbiBwYXRocyBtYXkgaGF2ZSBhcyBmZXcgYXMgb25lIHZlcnRleC4gT3BlbiBwYXRocyBjYW4gb25seSBiZVxuICAgKiBvZmZzZXQgd2l0aCBwb3NpdGl2ZSBkZWx0YXMuXG4gICAqXG4gICAqIEBwYXJhbSBwYXRocyAtIFBhdGhzIHRvIGFkZFxuICAgKiBAcGFyYW0gam9pblR5cGUgLSBKb2luIHR5cGVcbiAgICogQHBhcmFtIGVuZFR5cGUgLSBFbmQgdHlwZVxuICAgKi9cbiAgYWRkUGF0aHMocGF0aHM6IFJlYWRvbmx5UGF0aHMsIGpvaW5UeXBlOiBKb2luVHlwZSwgZW5kVHlwZTogRW5kVHlwZSk6IHZvaWQge1xuICAgIGNvbnN0IG5hdGl2ZVBhdGhzID0gcGF0aHNUb05hdGl2ZVBhdGhzKHRoaXMuX25hdGl2ZUxpYiwgcGF0aHMpO1xuICAgIHRyeSB7XG4gICAgICB0aGlzLl9jbGlwcGVyT2Zmc2V0IS5hZGRQYXRocyhcbiAgICAgICAgbmF0aXZlUGF0aHMsXG4gICAgICAgIGpvaW5UeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBqb2luVHlwZSksXG4gICAgICAgIGVuZFR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIGVuZFR5cGUpXG4gICAgICApO1xuICAgIH0gZmluYWxseSB7XG4gICAgICBuYXRpdmVQYXRocy5kZWxldGUoKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTmVnYXRpdmUgZGVsdGEgdmFsdWVzIHNocmluayBwb2x5Z29ucyBhbmQgcG9zaXRpdmUgZGVsdGEgZXhwYW5kIHRoZW0uXG4gICAqXG4gICAqIFRoaXMgbWV0aG9kIGNhbiBiZSBjYWxsZWQgbXVsdGlwbGUgdGltZXMsIG9mZnNldHRpbmcgdGhlIHNhbWUgcGF0aHMgYnkgZGlmZmVyZW50IGFtb3VudHMgKGllIHVzaW5nIGRpZmZlcmVudCBkZWx0YXMpLlxuICAgKlxuICAgKiBAcGFyYW0gZGVsdGEgLSBEZWx0YVxuICAgKiBAcGFyYW0gY2xlYW5EaXN0YW5jZSAtIENsZWFuIGRpc3RhbmNlIG92ZXIgdGhlIG91dHB1dCwgb3IgdW5kZWZpbmVkIGZvciBubyBjbGVhbmluZy5cbiAgICogQHJldHVybiB7UGF0aHN9IC0gU29sdXRpb24gcGF0aHNcbiAgICovXG4gIGV4ZWN1dGVUb1BhdGhzKGRlbHRhOiBudW1iZXIsIGNsZWFuRGlzdGFuY2U6IG51bWJlciB8IHVuZGVmaW5lZCk6IFBhdGhzIHtcbiAgICBjb25zdCBvdXROYXRpdmVQYXRocyA9IG5ldyB0aGlzLl9uYXRpdmVMaWIuUGF0aHMoKTtcbiAgICB0cnkge1xuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldCEuZXhlY3V0ZVBhdGhzKG91dE5hdGl2ZVBhdGhzLCBkZWx0YSk7XG4gICAgICBpZiAoY2xlYW5EaXN0YW5jZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRoaXMuX25hdGl2ZUxpYi5jbGVhblBvbHlnb25zKG91dE5hdGl2ZVBhdGhzLCBjbGVhbkRpc3RhbmNlKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBuYXRpdmVQYXRoc1RvUGF0aHModGhpcy5fbmF0aXZlTGliLCBvdXROYXRpdmVQYXRocywgdHJ1ZSk7IC8vIGZyZWVzIG91dE5hdGl2ZVBhdGhzXG4gICAgfSBmaW5hbGx5IHtcbiAgICAgIGlmICghb3V0TmF0aXZlUGF0aHMuaXNEZWxldGVkKCkpIHtcbiAgICAgICAgb3V0TmF0aXZlUGF0aHMuZGVsZXRlKCk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFRoaXMgbWV0aG9kIHRha2VzIHR3byBwYXJhbWV0ZXJzLiBUaGUgZmlyc3QgaXMgdGhlIHN0cnVjdHVyZSB0aGF0IHJlY2VpdmVzIHRoZSByZXN1bHQgb2YgdGhlIG9mZnNldCBvcGVyYXRpb24gKGEgUG9seVRyZWUgc3RydWN0dXJlKS4gVGhlIHNlY29uZCBwYXJhbWV0ZXJcbiAgICogaXMgdGhlIGFtb3VudCB0byB3aGljaCB0aGUgc3VwcGxpZWQgcGF0aHMgd2lsbCBiZSBvZmZzZXQuIE5lZ2F0aXZlIGRlbHRhIHZhbHVlcyBzaHJpbmsgcG9seWdvbnMgYW5kIHBvc2l0aXZlIGRlbHRhIGV4cGFuZCB0aGVtLlxuICAgKlxuICAgKiBUaGlzIG1ldGhvZCBjYW4gYmUgY2FsbGVkIG11bHRpcGxlIHRpbWVzLCBvZmZzZXR0aW5nIHRoZSBzYW1lIHBhdGhzIGJ5IGRpZmZlcmVudCBhbW91bnRzIChpZSB1c2luZyBkaWZmZXJlbnQgZGVsdGFzKS5cbiAgICpcbiAgICogQHBhcmFtIGRlbHRhIC0gRGVsdGFcbiAgICogQHJldHVybiB7UGF0aHN9IC0gU29sdXRpb24gcGF0aHNcbiAgICovXG4gIGV4ZWN1dGVUb1BvbHlUcmVlKGRlbHRhOiBudW1iZXIpOiBQb2x5VHJlZSB7XG4gICAgY29uc3Qgb3V0TmF0aXZlUG9seVRyZWUgPSBuZXcgdGhpcy5fbmF0aXZlTGliLlBvbHlUcmVlKCk7XG4gICAgdHJ5IHtcbiAgICAgIHRoaXMuX2NsaXBwZXJPZmZzZXQhLmV4ZWN1dGVQb2x5VHJlZShvdXROYXRpdmVQb2x5VHJlZSwgZGVsdGEpO1xuICAgICAgcmV0dXJuIFBvbHlUcmVlLmZyb21OYXRpdmVQb2x5VHJlZSh0aGlzLl9uYXRpdmVMaWIsIG91dE5hdGl2ZVBvbHlUcmVlLCB0cnVlKTsgLy8gZnJlZXMgb3V0TmF0aXZlUG9seVRyZWVcbiAgICB9IGZpbmFsbHkge1xuICAgICAgaWYgKCFvdXROYXRpdmVQb2x5VHJlZS5pc0RlbGV0ZWQoKSkge1xuICAgICAgICBvdXROYXRpdmVQb2x5VHJlZS5kZWxldGUoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGhpcyBtZXRob2QgY2xlYXJzIGFsbCBwYXRocyBmcm9tIHRoZSBDbGlwcGVyT2Zmc2V0IG9iamVjdCwgYWxsb3dpbmcgbmV3IHBhdGhzIHRvIGJlIGFzc2lnbmVkLlxuICAgKi9cbiAgY2xlYXIoKTogdm9pZCB7XG4gICAgdGhpcy5fY2xpcHBlck9mZnNldCEuY2xlYXIoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDaGVja3MgaWYgdGhlIG9iamVjdCBoYXMgYmVlbiBkaXNwb3NlZC5cbiAgICpcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gLSB0cnVlIGlmIGRpc3Bvc2VkLCBmYWxzZSBpZiBub3RcbiAgICovXG4gIGlzRGlzcG9zZWQoKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuX2NsaXBwZXJPZmZzZXQgPT09IHVuZGVmaW5lZCB8fCB0aGlzLl9jbGlwcGVyT2Zmc2V0LmlzRGVsZXRlZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFNpbmNlIHRoaXMgbGlicmFyeSB1c2VzIFdBU00vQVNNLkpTIGludGVybmFsbHkgZm9yIHNwZWVkIHRoaXMgbWVhbnMgdGhhdCB5b3UgbXVzdCBkaXNwb3NlIG9iamVjdHMgYWZ0ZXIgeW91IGFyZSBkb25lIHVzaW5nIHRoZW0gb3IgbWVtIGxlYWtzIHdpbGwgb2NjdXIuXG4gICAqIChJZiB0aGUgcnVudGltZSBzdXBwb3J0cyBGaW5hbGl6YXRpb25SZWdpc3RyeSB0aGVuIHRoaXMgYmVjb21lcyBub24tbWFuZGF0b3J5LCBidXQgc3RpbGwgcmVjb21tZW5kZWQpLlxuICAgKi9cbiAgZGlzcG9zZSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5fY2xpcHBlck9mZnNldCkge1xuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldC5kZWxldGUoKTtcbiAgICAgIG5hdGl2ZUZpbmFsaXphdGlvblJlZ2lzdHJ5Py51bnJlZ2lzdGVyKHRoaXMpO1xuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldCA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH1cbn1cbiJdfQ==