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 • 37.2 kB
JavaScript
;
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 = PathToNativePath_1.pathToNativePath(this._nativeLib, path);
try {
this._clipperOffset.addPath(nativePath, nativeEnumConversion_1.joinTypeToNative(this._nativeLib, joinType), 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 = PathsToNativePaths_1.pathsToNativePaths(this._nativeLib, paths);
try {
this._clipperOffset.addPaths(nativePaths, nativeEnumConversion_1.joinTypeToNative(this._nativeLib, joinType), 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 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQ2xpcHBlck9mZnNldC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9DbGlwcGVyT2Zmc2V0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUdBLHNFQUFrRjtBQUNsRixrRUFBcUY7QUFDckYsOERBQTZEO0FBRzdELHVDQUFzQztBQUN0QywyRUFBMEU7QUFFMUU7Ozs7Ozs7Ozs7O0dBV0c7QUFDSDtJQTZGRTs7Ozs7OztPQU9HO0lBQ0gsdUJBQ21CLFVBQW9DLEVBQ3JELFVBQWMsRUFDZCxZQUFtQjtRQURuQiwyQkFBQSxFQUFBLGNBQWM7UUFDZCw2QkFBQSxFQUFBLG1CQUFtQjtRQUZGLGVBQVUsR0FBVixVQUFVLENBQTBCO1FBSXJELElBQUksQ0FBQyxjQUFjLEdBQUcsSUFBSSxVQUFVLENBQUMsYUFBYSxDQUFDLFVBQVUsRUFBRSxZQUFZLENBQUMsQ0FBQztRQUM3RSx1REFBMEIsYUFBMUIsdURBQTBCLHVCQUExQix1REFBMEIsQ0FBRSxRQUFRLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxjQUFjLEVBQUUsSUFBSSxFQUFFO0lBQ3hFLENBQUM7SUEzRUQsc0JBQUksdUNBQVk7UUE5QmhCOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztXQTZCRzthQUNIO1lBQ0UsT0FBTyxJQUFJLENBQUMsY0FBZSxDQUFDLFlBQVksQ0FBQztRQUMzQyxDQUFDO1FBRUQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O1dBNkJHO2FBQ0gsVUFBaUIsS0FBYTtZQUM1QixJQUFJLENBQUMsY0FBZSxDQUFDLFlBQVksR0FBRyxLQUFLLENBQUM7UUFDNUMsQ0FBQzs7O09BbENBO0lBNkNELHNCQUFJLHFDQUFVO1FBVGQ7Ozs7Ozs7O1dBUUc7YUFDSDtZQUNFLE9BQU8sSUFBSSxDQUFDLGNBQWUsQ0FBQyxVQUFVLENBQUM7UUFDekMsQ0FBQztRQUVEOzs7O1dBSUc7YUFDSCxVQUFlLEtBQWE7WUFDMUIsSUFBSSxDQUFDLGNBQWUsQ0FBQyxV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LCtCQUFPLEdBQVA7UUFDRSxJQUFJLElBQUksQ0FBQyxjQUFjLEVBQUU7WUFDdkIsSUFBSSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUM3Qix1REFBMEIsYUFBMUIsdURBQTBCLHVCQUExQix1REFBMEIsQ0FBRSxVQUFVLENBQUMsSUFBSSxFQUFFO1lBQzdDLElBQUksQ0FBQyxjQUFjLEdBQUcsU0FBUyxDQUFDO1NBQ2pDO0lBQ0gsQ0FBQztJQUNILG9CQUFDO0FBQUQsQ0FBQyxBQXRPRCxJQXNPQztBQXRPWSxzQ0FBYSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEVuZFR5cGUsIEpvaW5UeXBlIH0gZnJvbSBcIi4vZW51bXNcIjtcclxuaW1wb3J0IHsgTmF0aXZlQ2xpcHBlckxpYkluc3RhbmNlIH0gZnJvbSBcIi4vbmF0aXZlL05hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZVwiO1xyXG5pbXBvcnQgeyBOYXRpdmVDbGlwcGVyT2Zmc2V0IH0gZnJvbSBcIi4vbmF0aXZlL05hdGl2ZUNsaXBwZXJPZmZzZXRcIjtcclxuaW1wb3J0IHsgZW5kVHlwZVRvTmF0aXZlLCBqb2luVHlwZVRvTmF0aXZlIH0gZnJvbSBcIi4vbmF0aXZlL25hdGl2ZUVudW1Db252ZXJzaW9uXCI7XHJcbmltcG9ydCB7IG5hdGl2ZVBhdGhzVG9QYXRocywgcGF0aHNUb05hdGl2ZVBhdGhzIH0gZnJvbSBcIi4vbmF0aXZlL1BhdGhzVG9OYXRpdmVQYXRoc1wiO1xyXG5pbXBvcnQgeyBwYXRoVG9OYXRpdmVQYXRoIH0gZnJvbSBcIi4vbmF0aXZlL1BhdGhUb05hdGl2ZVBhdGhcIjtcclxuaW1wb3J0IHsgUmVhZG9ubHlQYXRoIH0gZnJvbSBcIi4vUGF0aFwiO1xyXG5pbXBvcnQgeyBQYXRocywgUmVhZG9ubHlQYXRocyB9IGZyb20gXCIuL1BhdGhzXCI7XHJcbmltcG9ydCB7IFBvbHlUcmVlIH0gZnJvbSBcIi4vUG9seVRyZWVcIjtcclxuaW1wb3J0IHsgbmF0aXZlRmluYWxpemF0aW9uUmVnaXN0cnkgfSBmcm9tIFwiLi9uYXRpdmVGaW5hbGl6YXRpb25SZWdpc3RyeVwiO1xyXG5cclxuLyoqXHJcbiAqIFRoZSBDbGlwcGVyT2Zmc2V0IGNsYXNzIGVuY2Fwc3VsYXRlcyB0aGUgcHJvY2VzcyBvZiBvZmZzZXR0aW5nIChpbmZsYXRpbmcvZGVmbGF0aW5nKSBib3RoIG9wZW4gYW5kIGNsb3NlZCBwYXRocyB1c2luZyBhIG51bWJlciBvZiBkaWZmZXJlbnQgam9pbiB0eXBlc1xyXG4gKiBhbmQgZW5kIHR5cGVzLlxyXG4gKlxyXG4gKiBQcmVjb25kaXRpb25zIGZvciBvZmZzZXR0aW5nOlxyXG4gKiAxLiBUaGUgb3JpZW50YXRpb25zIG9mIGNsb3NlZCBwYXRocyBtdXN0IGJlIGNvbnNpc3RlbnQgc3VjaCB0aGF0IG91dGVyIHBvbHlnb25zIHNoYXJlIHRoZSBzYW1lIG9yaWVudGF0aW9uLCBhbmQgYW55IGhvbGVzIGhhdmUgdGhlIG9wcG9zaXRlIG9yaWVudGF0aW9uXHJcbiAqIChpZSBub24temVybyBmaWxsaW5nKS4gT3BlbiBwYXRocyBtdXN0IGJlIG9yaWVudGVkIHdpdGggY2xvc2VkIG91dGVyIHBvbHlnb25zLlxyXG4gKiAyLiBQb2x5Z29ucyBtdXN0IG5vdCBzZWxmLWludGVyc2VjdC5cclxuICpcclxuICogTGltaXRhdGlvbnM6XHJcbiAqIFdoZW4gb2Zmc2V0dGluZywgc21hbGwgYXJ0ZWZhY3RzIG1heSBhcHBlYXIgd2hlcmUgcG9seWdvbnMgb3ZlcmxhcC4gVG8gYXZvaWQgdGhlc2UgYXJ0ZWZhY3RzLCBvZmZzZXQgb3ZlcmxhcHBpbmcgcG9seWdvbnMgc2VwYXJhdGVseS5cclxuICovXHJcbmV4cG9ydCBjbGFzcyBDbGlwcGVyT2Zmc2V0IHtcclxuICBwcml2YXRlIF9jbGlwcGVyT2Zmc2V0PzogTmF0aXZlQ2xpcHBlck9mZnNldDtcclxuXHJcbiAgLyoqXHJcbiAgICogRmlyc3RseSwgdGhpcyBmaWVsZC9wcm9wZXJ0eSBpcyBvbmx5IHJlbGV2YW50IHdoZW4gSm9pblR5cGUgPSBSb3VuZCBhbmQvb3IgRW5kVHlwZSA9IFJvdW5kLlxyXG4gICAqXHJcbiAgICogU2luY2UgZmxhdHRlbmVkIHBhdGhzIGNhbiBuZXZlciBwZXJmZWN0bHkgcmVwcmVzZW50IGFyY3MsIHRoaXMgZmllbGQvcHJvcGVydHkgc3BlY2lmaWVzIGEgbWF4aW11bSBhY2NlcHRhYmxlIGltcHJlY2lzaW9uICgndG9sZXJhbmNlJykgd2hlbiBhcmNzIGFyZVxyXG4gICAqIGFwcHJveGltYXRlZCBpbiBhbiBvZmZzZXR0aW5nIG9wZXJhdGlvbi4gU21hbGxlciB2YWx1ZXMgd2lsbCBpbmNyZWFzZSAnc21vb3RobmVzcycgdXAgdG8gYSBwb2ludCB0aG91Z2ggYXQgYSBjb3N0IG9mIHBlcmZvcm1hbmNlIGFuZCBpbiBjcmVhdGluZyBtb3JlXHJcbiAgICogdmVydGljZXMgdG8gY29uc3RydWN0IHRoZSBhcmMuXHJcbiAgICpcclxuICAgKiBUaGUgZGVmYXVsdCBBcmNUb2xlcmFuY2UgaXMgMC4yNSB1bml0cy4gVGhpcyBtZWFucyB0aGF0IHRoZSBtYXhpbXVtIGRpc3RhbmNlIHRoZSBmbGF0dGVuZWQgcGF0aCB3aWxsIGRldmlhdGUgZnJvbSB0aGUgJ3RydWUnIGFyYyB3aWxsIGJlIG5vIG1vcmVcclxuICAgKiB0aGFuIDAuMjUgdW5pdHMgKGJlZm9yZSByb3VuZGluZykuXHJcbiAgICpcclxuICAgKiBSZWR1Y2luZyB0b2xlcmFuY2VzIGJlbG93IDAuMjUgd2lsbCBub3QgaW1wcm92ZSBzbW9vdGhuZXNzIHNpbmNlIHZlcnRleCBjb29yZGluYXRlcyB3aWxsIHN0aWxsIGJlIHJvdW5kZWQgdG8gaW50ZWdlciB2YWx1ZXMuIFRoZSBvbmx5IHdheSB0byBhY2hpZXZlXHJcbiAgICogc3ViLWludGVnZXIgcHJlY2lzaW9uIGlzIHRocm91Z2ggY29vcmRpbmF0ZSBzY2FsaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgb2Zmc2V0dGluZyAoc2VlIGV4YW1wbGUgYmVsb3cpLlxyXG4gICAqXHJcbiAgICogSXQncyBpbXBvcnRhbnQgdG8gbWFrZSBBcmNUb2xlcmFuY2UgYSBzZW5zaWJsZSBmcmFjdGlvbiBvZiB0aGUgb2Zmc2V0IGRlbHRhIChhcmMgcmFkaXVzKS4gTGFyZ2UgdG9sZXJhbmNlcyByZWxhdGl2ZSB0byB0aGUgb2Zmc2V0IGRlbHRhIHdpbGwgcHJvZHVjZVxyXG4gICAqIHBvb3IgYXJjIGFwcHJveGltYXRpb25zIGJ1dCwganVzdCBhcyBpbXBvcnRhbnRseSwgdmVyeSBzbWFsbCB0b2xlcmFuY2VzIHdpbGwgc3Vic3RhbnRpYWxseSBzbG93IG9mZnNldHRpbmcgcGVyZm9ybWFuY2Ugd2hpbGUgcHJvdmlkaW5nIHVubmVjZXNzYXJ5XHJcbiAgICogZGVncmVlcyBvZiBwcmVjaXNpb24uIFRoaXMgaXMgbW9zdCBsaWtlbHkgdG8gYmUgYW4gaXNzdWUgd2hlbiBvZmZzZXR0aW5nIHBvbHlnb25zIHdob3NlIGNvb3JkaW5hdGVzIGhhdmUgYmVlbiBzY2FsZWQgdG8gcHJlc2VydmUgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLlxyXG4gICAqXHJcbiAgICogRXhhbXBsZTogSW1hZ2luZSBhIHNldCBvZiBwb2x5Z29ucyAoZGVmaW5lZCBpbiBmbG9hdGluZyBwb2ludCBjb29yZGluYXRlcykgdGhhdCBpcyB0byBiZSBvZmZzZXQgYnkgMTAgdW5pdHMgdXNpbmcgcm91bmQgam9pbnMsIGFuZCB0aGUgc29sdXRpb24gaXMgdG9cclxuICAgKiByZXRhaW4gZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uIHVwIHRvIGF0IGxlYXN0IDYgZGVjaW1hbCBwbGFjZXMuXHJcbiAgICogVG8gcHJlc2VydmUgdGhpcyBkZWdyZWUgb2YgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLCBhbmQgZ2l2ZW4gdGhhdCBDbGlwcGVyIGFuZCBDbGlwcGVyT2Zmc2V0IGJvdGggb3BlcmF0ZSBvbiBpbnRlZ2VyIGNvb3JkaW5hdGVzLCB0aGUgcG9seWdvblxyXG4gICAqIGNvb3JkaW5hdGVzIHdpbGwgYmUgc2NhbGVkIHVwIGJ5IDEwOCAoYW5kIHJvdW5kZWQgdG8gaW50ZWdlcnMpIHByaW9yIHRvIG9mZnNldHRpbmcuIEJvdGggb2Zmc2V0IGRlbHRhIGFuZCBBcmNUb2xlcmFuY2Ugd2lsbCBhbHNvIG5lZWQgdG8gYmUgc2NhbGVkXHJcbiAgICogYnkgdGhpcyBzYW1lIGZhY3Rvci4gSWYgQXJjVG9sZXJhbmNlIHdhcyBsZWZ0IHVuc2NhbGVkIGF0IHRoZSBkZWZhdWx0IDAuMjUgdW5pdHMsIGV2ZXJ5IGFyYyBpbiB0aGUgc29sdXRpb24gd291bGQgY29udGFpbiBhIGZyYWN0aW9uIG9mIDQ0IFRIT1VTQU5EXHJcbiAgICogdmVydGljZXMgd2hpbGUgdGhlIGZpbmFsIGFyYyBpbXByZWNpc2lvbiB3b3VsZCBiZSAwLjI1IMOXIDEwLTggdW5pdHMgKGllIG9uY2Ugc2NhbGluZyB3YXMgcmV2ZXJzZWQpLiBIb3dldmVyLCBpZiAwLjEgdW5pdHMgd2FzIGFuIGFjY2VwdGFibGUgaW1wcmVjaXNpb25cclxuICAgKiBpbiB0aGUgZmluYWwgdW5zY2FsZWQgc29sdXRpb24sIHRoZW4gQXJjVG9sZXJhbmNlIHNob3VsZCBiZSBzZXQgdG8gMC4xIMOXIHNjYWxpbmdfZmFjdG9yICgwLjEgw5cgMTA4ICkuIE5vdyBpZiBzY2FsaW5nIGlzIGFwcGxpZWQgZXF1YWxseSB0byBib3RoXHJcbiAgICogQXJjVG9sZXJhbmNlIGFuZCB0byBEZWx0YSBPZmZzZXQsIHRoZW4gaW4gdGhpcyBleGFtcGxlIHRoZSBudW1iZXIgb2YgdmVydGljZXMgKHN0ZXBzKSBkZWZpbmluZyBlYWNoIGFyYyB3b3VsZCBiZSBhIGZyYWN0aW9uIG9mIDIzLlxyXG4gICAqXHJcbiAgICogVGhlIGZvcm11bGEgZm9yIHRoZSBudW1iZXIgb2Ygc3RlcHMgaW4gYSBmdWxsIGNpcmN1bGFyIGFyYyBpcyAuLi4gUGkgLyBhY29zKDEgLSBhcmNfdG9sZXJhbmNlIC8gYWJzKGRlbHRhKSlcclxuICAgKlxyXG4gICAqIEByZXR1cm4ge251bWJlcn0gLSBDdXJyZW50IGFyYyB0b2xlcmFuY2VcclxuICAgKi9cclxuICBnZXQgYXJjVG9sZXJhbmNlKCk6IG51bWJlciB7XHJcbiAgICByZXR1cm4gdGhpcy5fY2xpcHBlck9mZnNldCEuYXJjVG9sZXJhbmNlO1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogRmlyc3RseSwgdGhpcyBmaWVsZC9wcm9wZXJ0eSBpcyBvbmx5IHJlbGV2YW50IHdoZW4gSm9pblR5cGUgPSBSb3VuZCBhbmQvb3IgRW5kVHlwZSA9IFJvdW5kLlxyXG4gICAqXHJcbiAgICogU2luY2UgZmxhdHRlbmVkIHBhdGhzIGNhbiBuZXZlciBwZXJmZWN0bHkgcmVwcmVzZW50IGFyY3MsIHRoaXMgZmllbGQvcHJvcGVydHkgc3BlY2lmaWVzIGEgbWF4aW11bSBhY2NlcHRhYmxlIGltcHJlY2lzaW9uICgndG9sZXJhbmNlJykgd2hlbiBhcmNzIGFyZVxyXG4gICAqIGFwcHJveGltYXRlZCBpbiBhbiBvZmZzZXR0aW5nIG9wZXJhdGlvbi4gU21hbGxlciB2YWx1ZXMgd2lsbCBpbmNyZWFzZSAnc21vb3RobmVzcycgdXAgdG8gYSBwb2ludCB0aG91Z2ggYXQgYSBjb3N0IG9mIHBlcmZvcm1hbmNlIGFuZCBpbiBjcmVhdGluZyBtb3JlXHJcbiAgICogdmVydGljZXMgdG8gY29uc3RydWN0IHRoZSBhcmMuXHJcbiAgICpcclxuICAgKiBUaGUgZGVmYXVsdCBBcmNUb2xlcmFuY2UgaXMgMC4yNSB1bml0cy4gVGhpcyBtZWFucyB0aGF0IHRoZSBtYXhpbXVtIGRpc3RhbmNlIHRoZSBmbGF0dGVuZWQgcGF0aCB3aWxsIGRldmlhdGUgZnJvbSB0aGUgJ3RydWUnIGFyYyB3aWxsIGJlIG5vIG1vcmVcclxuICAgKiB0aGFuIDAuMjUgdW5pdHMgKGJlZm9yZSByb3VuZGluZykuXHJcbiAgICpcclxuICAgKiBSZWR1Y2luZyB0b2xlcmFuY2VzIGJlbG93IDAuMjUgd2lsbCBub3QgaW1wcm92ZSBzbW9vdGhuZXNzIHNpbmNlIHZlcnRleCBjb29yZGluYXRlcyB3aWxsIHN0aWxsIGJlIHJvdW5kZWQgdG8gaW50ZWdlciB2YWx1ZXMuIFRoZSBvbmx5IHdheSB0byBhY2hpZXZlXHJcbiAgICogc3ViLWludGVnZXIgcHJlY2lzaW9uIGlzIHRocm91Z2ggY29vcmRpbmF0ZSBzY2FsaW5nIGJlZm9yZSBhbmQgYWZ0ZXIgb2Zmc2V0dGluZyAoc2VlIGV4YW1wbGUgYmVsb3cpLlxyXG4gICAqXHJcbiAgICogSXQncyBpbXBvcnRhbnQgdG8gbWFrZSBBcmNUb2xlcmFuY2UgYSBzZW5zaWJsZSBmcmFjdGlvbiBvZiB0aGUgb2Zmc2V0IGRlbHRhIChhcmMgcmFkaXVzKS4gTGFyZ2UgdG9sZXJhbmNlcyByZWxhdGl2ZSB0byB0aGUgb2Zmc2V0IGRlbHRhIHdpbGwgcHJvZHVjZVxyXG4gICAqIHBvb3IgYXJjIGFwcHJveGltYXRpb25zIGJ1dCwganVzdCBhcyBpbXBvcnRhbnRseSwgdmVyeSBzbWFsbCB0b2xlcmFuY2VzIHdpbGwgc3Vic3RhbnRpYWxseSBzbG93IG9mZnNldHRpbmcgcGVyZm9ybWFuY2Ugd2hpbGUgcHJvdmlkaW5nIHVubmVjZXNzYXJ5XHJcbiAgICogZGVncmVlcyBvZiBwcmVjaXNpb24uIFRoaXMgaXMgbW9zdCBsaWtlbHkgdG8gYmUgYW4gaXNzdWUgd2hlbiBvZmZzZXR0aW5nIHBvbHlnb25zIHdob3NlIGNvb3JkaW5hdGVzIGhhdmUgYmVlbiBzY2FsZWQgdG8gcHJlc2VydmUgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLlxyXG4gICAqXHJcbiAgICogRXhhbXBsZTogSW1hZ2luZSBhIHNldCBvZiBwb2x5Z29ucyAoZGVmaW5lZCBpbiBmbG9hdGluZyBwb2ludCBjb29yZGluYXRlcykgdGhhdCBpcyB0byBiZSBvZmZzZXQgYnkgMTAgdW5pdHMgdXNpbmcgcm91bmQgam9pbnMsIGFuZCB0aGUgc29sdXRpb24gaXMgdG9cclxuICAgKiByZXRhaW4gZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uIHVwIHRvIGF0IGxlYXN0IDYgZGVjaW1hbCBwbGFjZXMuXHJcbiAgICogVG8gcHJlc2VydmUgdGhpcyBkZWdyZWUgb2YgZmxvYXRpbmcgcG9pbnQgcHJlY2lzaW9uLCBhbmQgZ2l2ZW4gdGhhdCBDbGlwcGVyIGFuZCBDbGlwcGVyT2Zmc2V0IGJvdGggb3BlcmF0ZSBvbiBpbnRlZ2VyIGNvb3JkaW5hdGVzLCB0aGUgcG9seWdvblxyXG4gICAqIGNvb3JkaW5hdGVzIHdpbGwgYmUgc2NhbGVkIHVwIGJ5IDEwOCAoYW5kIHJvdW5kZWQgdG8gaW50ZWdlcnMpIHByaW9yIHRvIG9mZnNldHRpbmcuIEJvdGggb2Zmc2V0IGRlbHRhIGFuZCBBcmNUb2xlcmFuY2Ugd2lsbCBhbHNvIG5lZWQgdG8gYmUgc2NhbGVkXHJcbiAgICogYnkgdGhpcyBzYW1lIGZhY3Rvci4gSWYgQXJjVG9sZXJhbmNlIHdhcyBsZWZ0IHVuc2NhbGVkIGF0IHRoZSBkZWZhdWx0IDAuMjUgdW5pdHMsIGV2ZXJ5IGFyYyBpbiB0aGUgc29sdXRpb24gd291bGQgY29udGFpbiBhIGZyYWN0aW9uIG9mIDQ0IFRIT1VTQU5EXHJcbiAgICogdmVydGljZXMgd2hpbGUgdGhlIGZpbmFsIGFyYyBpbXByZWNpc2lvbiB3b3VsZCBiZSAwLjI1IMOXIDEwLTggdW5pdHMgKGllIG9uY2Ugc2NhbGluZyB3YXMgcmV2ZXJzZWQpLiBIb3dldmVyLCBpZiAwLjEgdW5pdHMgd2FzIGFuIGFjY2VwdGFibGUgaW1wcmVjaXNpb25cclxuICAgKiBpbiB0aGUgZmluYWwgdW5zY2FsZWQgc29sdXRpb24sIHRoZW4gQXJjVG9sZXJhbmNlIHNob3VsZCBiZSBzZXQgdG8gMC4xIMOXIHNjYWxpbmdfZmFjdG9yICgwLjEgw5cgMTA4ICkuIE5vdyBpZiBzY2FsaW5nIGlzIGFwcGxpZWQgZXF1YWxseSB0byBib3RoXHJcbiAgICogQXJjVG9sZXJhbmNlIGFuZCB0byBEZWx0YSBPZmZzZXQsIHRoZW4gaW4gdGhpcyBleGFtcGxlIHRoZSBudW1iZXIgb2YgdmVydGljZXMgKHN0ZXBzKSBkZWZpbmluZyBlYWNoIGFyYyB3b3VsZCBiZSBhIGZyYWN0aW9uIG9mIDIzLlxyXG4gICAqXHJcbiAgICogVGhlIGZvcm11bGEgZm9yIHRoZSBudW1iZXIgb2Ygc3RlcHMgaW4gYSBmdWxsIGNpcmN1bGFyIGFyYyBpcyAuLi4gUGkgLyBhY29zKDEgLSBhcmNfdG9sZXJhbmNlIC8gYWJzKGRlbHRhKSlcclxuICAgKlxyXG4gICAqIEBwYXJhbSB2YWx1ZSAtIEFyYyB0b2xlcmFuY2UgdG8gc2V0LlxyXG4gICAqL1xyXG4gIHNldCBhcmNUb2xlcmFuY2UodmFsdWU6IG51bWJlcikge1xyXG4gICAgdGhpcy5fY2xpcHBlck9mZnNldCEuYXJjVG9sZXJhbmNlID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIHByb3BlcnR5IHNldHMgdGhlIG1heGltdW0gZGlzdGFuY2UgaW4gbXVsdGlwbGVzIG9mIGRlbHRhIHRoYXQgdmVydGljZXMgY2FuIGJlIG9mZnNldCBmcm9tIHRoZWlyIG9yaWdpbmFsIHBvc2l0aW9ucyBiZWZvcmUgc3F1YXJpbmcgaXMgYXBwbGllZC5cclxuICAgKiAoU3F1YXJpbmcgdHJ1bmNhdGVzIGEgbWl0ZXIgYnkgJ2N1dHRpbmcgaXQgb2ZmJyBhdCAxIMOXIGRlbHRhIGRpc3RhbmNlIGZyb20gdGhlIG9yaWdpbmFsIHZlcnRleC4pXHJcbiAgICpcclxuICAgKiBUaGUgZGVmYXVsdCB2YWx1ZSBmb3IgTWl0ZXJMaW1pdCBpcyAyIChpZSB0d2ljZSBkZWx0YSkuIFRoaXMgaXMgYWxzbyB0aGUgc21hbGxlc3QgTWl0ZXJMaW1pdCB0aGF0J3MgYWxsb3dlZC4gSWYgbWl0ZXJpbmcgd2FzIHVucmVzdHJpY3RlZCAoaWUgd2l0aG91dFxyXG4gICAqIGFueSBzcXVhcmluZyksIHRoZW4gb2Zmc2V0cyBhdCB2ZXJ5IGFjdXRlIGFuZ2xlcyB3b3VsZCBnZW5lcmF0ZSB1bmFjY2VwdGFibHkgbG9uZyAnc3Bpa2VzJy5cclxuICAgKlxyXG4gICAqIEByZXR1cm4ge251bWJlcn0gLSBDdXJyZW50IG1pdGVyIGxpbWl0XHJcbiAgICovXHJcbiAgZ2V0IG1pdGVyTGltaXQoKTogbnVtYmVyIHtcclxuICAgIHJldHVybiB0aGlzLl9jbGlwcGVyT2Zmc2V0IS5taXRlckxpbWl0O1xyXG4gIH1cclxuXHJcbiAgLyoqXHJcbiAgICogU2V0cyB0aGUgY3VycmVudCBtaXRlciBsaW1pdCAoc2VlIGdldHRlciBkb2NzIGZvciBtb3JlIGluZm8pLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHZhbHVlIC0gTWl0IGxpbWl0IHRvIHNldC5cclxuICAgKi9cclxuICBzZXQgbWl0ZXJMaW1pdCh2YWx1ZTogbnVtYmVyKSB7XHJcbiAgICB0aGlzLl9jbGlwcGVyT2Zmc2V0IS5taXRlckxpbWl0ID0gdmFsdWU7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGUgQ2xpcHBlck9mZnNldCBjb25zdHJ1Y3RvciB0YWtlcyAyIG9wdGlvbmFsIHBhcmFtZXRlcnM6IE1pdGVyTGltaXQgYW5kIEFyY1RvbGVyYW5jZS4gVGhlIHR3byBwYXJhbWV0ZXJzIGNvcnJlc3BvbmRzIHRvIHByb3BlcnRpZXMgb2YgdGhlIHNhbWUgbmFtZS5cclxuICAgKiBNaXRlckxpbWl0IGlzIG9ubHkgcmVsZXZhbnQgd2hlbiBKb2luVHlwZSBpcyBNaXRlciwgYW5kIEFyY1RvbGVyYW5jZSBpcyBvbmx5IHJlbGV2YW50IHdoZW4gSm9pblR5cGUgaXMgUm91bmQgb3Igd2hlbiBFbmRUeXBlIGlzIE9wZW5Sb3VuZC5cclxuICAgKlxyXG4gICAqIEBwYXJhbSBfbmF0aXZlTGliIC0gTmF0aXZlIGNsaXBwZXIgbGliIGluc3RhbmNlIHRvIHVzZVxyXG4gICAqIEBwYXJhbSBtaXRlckxpbWl0IC0gTWl0ZXIgbGltaXRcclxuICAgKiBAcGFyYW0gYXJjVG9sZXJhbmNlIC0gQXJjVG9sZXJhbmNlIChyb3VuZCBwcmVjaXNpb24pXHJcbiAgICovXHJcbiAgY29uc3RydWN0b3IoXHJcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9uYXRpdmVMaWI6IE5hdGl2ZUNsaXBwZXJMaWJJbnN0YW5jZSxcclxuICAgIG1pdGVyTGltaXQgPSAyLFxyXG4gICAgYXJjVG9sZXJhbmNlID0gMC4yNVxyXG4gICkge1xyXG4gICAgdGhpcy5fY2xpcHBlck9mZnNldCA9IG5ldyBfbmF0aXZlTGliLkNsaXBwZXJPZmZzZXQobWl0ZXJMaW1pdCwgYXJjVG9sZXJhbmNlKTtcclxuICAgIG5hdGl2ZUZpbmFsaXphdGlvblJlZ2lzdHJ5Py5yZWdpc3Rlcih0aGlzLCB0aGlzLl9jbGlwcGVyT2Zmc2V0LCB0aGlzKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIEFkZHMgYSBQYXRoIHRvIGEgQ2xpcHBlck9mZnNldCBvYmplY3QgaW4gcHJlcGFyYXRpb24gZm9yIG9mZnNldHRpbmcuXHJcbiAgICpcclxuICAgKiBBbnkgbnVtYmVyIG9mIHBhdGhzIGNhbiBiZSBhZGRlZCwgYW5kIGVhY2ggaGFzIGl0cyBvd24gSm9pblR5cGUgYW5kIEVuZFR5cGUuIEFsbCAnb3V0ZXInIFBhdGhzIG11c3QgaGF2ZSB0aGUgc2FtZSBvcmllbnRhdGlvbiwgYW5kIGFueSAnaG9sZScgcGF0aHMgbXVzdFxyXG4gICAqIGhhdmUgcmV2ZXJzZSBvcmllbnRhdGlvbi4gQ2xvc2VkIHBhdGhzIG11c3QgaGF2ZSBhdCBsZWFzdCAzIHZlcnRpY2VzLiBPcGVuIHBhdGhzIG1heSBoYXZlIGFzIGZldyBhcyBvbmUgdmVydGV4LiBPcGVuIHBhdGhzIGNhbiBvbmx5IGJlIG9mZnNldFxyXG4gICAqIHdpdGggcG9zaXRpdmUgZGVsdGFzLlxyXG4gICAqXHJcbiAgICogQHBhcmFtIHBhdGggLSBQYXRoIHRvIGFkZFxyXG4gICAqIEBwYXJhbSBqb2luVHlwZSAtIEpvaW4gdHlwZVxyXG4gICAqIEBwYXJhbSBlbmRUeXBlIC0gRW5kIHR5cGVcclxuICAgKi9cclxuICBhZGRQYXRoKHBhdGg6IFJlYWRvbmx5UGF0aCwgam9pblR5cGU6IEpvaW5UeXBlLCBlbmRUeXBlOiBFbmRUeXBlKSB7XHJcbiAgICBjb25zdCBuYXRpdmVQYXRoID0gcGF0aFRvTmF0aXZlUGF0aCh0aGlzLl9uYXRpdmVMaWIsIHBhdGgpO1xyXG4gICAgdHJ5IHtcclxuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldCEuYWRkUGF0aChcclxuICAgICAgICBuYXRpdmVQYXRoLFxyXG4gICAgICAgIGpvaW5UeXBlVG9OYXRpdmUodGhpcy5fbmF0aXZlTGliLCBqb2luVHlwZSksXHJcbiAgICAgICAgZW5kVHlwZVRvTmF0aXZlKHRoaXMuX25hdGl2ZUxpYiwgZW5kVHlwZSlcclxuICAgICAgKTtcclxuICAgIH0gZmluYWxseSB7XHJcbiAgICAgIG5hdGl2ZVBhdGguZGVsZXRlKCk7XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBBZGRzIFBhdGhzIHRvIGEgQ2xpcHBlck9mZnNldCBvYmplY3QgaW4gcHJlcGFyYXRpb24gZm9yIG9mZnNldHRpbmcuXHJcbiAgICpcclxuICAgKiBBbnkgbnVtYmVyIG9mIHBhdGhzIGNhbiBiZSBhZGRlZCwgYW5kIGVhY2ggcGF0aCBoYXMgaXRzIG93biBKb2luVHlwZSBhbmQgRW5kVHlwZS4gQWxsICdvdXRlcicgUGF0aHMgbXVzdCBoYXZlIHRoZSBzYW1lIG9yaWVudGF0aW9uLCBhbmQgYW55ICdob2xlJ1xyXG4gICAqIHBhdGhzIG11c3QgaGF2ZSByZXZlcnNlIG9yaWVudGF0aW9uLiBDbG9zZWQgcGF0aHMgbXVzdCBoYXZlIGF0IGxlYXN0IDMgdmVydGljZXMuIE9wZW4gcGF0aHMgbWF5IGhhdmUgYXMgZmV3IGFzIG9uZSB2ZXJ0ZXguIE9wZW4gcGF0aHMgY2FuIG9ubHkgYmVcclxuICAgKiBvZmZzZXQgd2l0aCBwb3NpdGl2ZSBkZWx0YXMuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gcGF0aHMgLSBQYXRocyB0byBhZGRcclxuICAgKiBAcGFyYW0gam9pblR5cGUgLSBKb2luIHR5cGVcclxuICAgKiBAcGFyYW0gZW5kVHlwZSAtIEVuZCB0eXBlXHJcbiAgICovXHJcbiAgYWRkUGF0aHMocGF0aHM6IFJlYWRvbmx5UGF0aHMsIGpvaW5UeXBlOiBKb2luVHlwZSwgZW5kVHlwZTogRW5kVHlwZSkge1xyXG4gICAgY29uc3QgbmF0aXZlUGF0aHMgPSBwYXRoc1RvTmF0aXZlUGF0aHModGhpcy5fbmF0aXZlTGliLCBwYXRocyk7XHJcbiAgICB0cnkge1xyXG4gICAgICB0aGlzLl9jbGlwcGVyT2Zmc2V0IS5hZGRQYXRocyhcclxuICAgICAgICBuYXRpdmVQYXRocyxcclxuICAgICAgICBqb2luVHlwZVRvTmF0aXZlKHRoaXMuX25hdGl2ZUxpYiwgam9pblR5cGUpLFxyXG4gICAgICAgIGVuZFR5cGVUb05hdGl2ZSh0aGlzLl9uYXRpdmVMaWIsIGVuZFR5cGUpXHJcbiAgICAgICk7XHJcbiAgICB9IGZpbmFsbHkge1xyXG4gICAgICBuYXRpdmVQYXRocy5kZWxldGUoKTtcclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIE5lZ2F0aXZlIGRlbHRhIHZhbHVlcyBzaHJpbmsgcG9seWdvbnMgYW5kIHBvc2l0aXZlIGRlbHRhIGV4cGFuZCB0aGVtLlxyXG4gICAqXHJcbiAgICogVGhpcyBtZXRob2QgY2FuIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcywgb2Zmc2V0dGluZyB0aGUgc2FtZSBwYXRocyBieSBkaWZmZXJlbnQgYW1vdW50cyAoaWUgdXNpbmcgZGlmZmVyZW50IGRlbHRhcykuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZGVsdGEgLSBEZWx0YVxyXG4gICAqIEBwYXJhbSBjbGVhbkRpc3RhbmNlIC0gQ2xlYW4gZGlzdGFuY2Ugb3ZlciB0aGUgb3V0cHV0LCBvciB1bmRlZmluZWQgZm9yIG5vIGNsZWFuaW5nLlxyXG4gICAqIEByZXR1cm4ge1BhdGhzfSAtIFNvbHV0aW9uIHBhdGhzXHJcbiAgICovXHJcbiAgZXhlY3V0ZVRvUGF0aHMoZGVsdGE6IG51bWJlciwgY2xlYW5EaXN0YW5jZTogbnVtYmVyIHwgdW5kZWZpbmVkKTogUGF0aHMge1xyXG4gICAgY29uc3Qgb3V0TmF0aXZlUGF0aHMgPSBuZXcgdGhpcy5fbmF0aXZlTGliLlBhdGhzKCk7XHJcbiAgICB0cnkge1xyXG4gICAgICB0aGlzLl9jbGlwcGVyT2Zmc2V0IS5leGVjdXRlUGF0aHMob3V0TmF0aXZlUGF0aHMsIGRlbHRhKTtcclxuICAgICAgaWYgKGNsZWFuRGlzdGFuY2UgIT09IHVuZGVmaW5lZCkge1xyXG4gICAgICAgIHRoaXMuX25hdGl2ZUxpYi5jbGVhblBvbHlnb25zKG91dE5hdGl2ZVBhdGhzLCBjbGVhbkRpc3RhbmNlKTtcclxuICAgICAgfVxyXG4gICAgICByZXR1cm4gbmF0aXZlUGF0aHNUb1BhdGhzKHRoaXMuX25hdGl2ZUxpYiwgb3V0TmF0aXZlUGF0aHMsIHRydWUpOyAvLyBmcmVlcyBvdXROYXRpdmVQYXRoc1xyXG4gICAgfSBmaW5hbGx5IHtcclxuICAgICAgaWYgKCFvdXROYXRpdmVQYXRocy5pc0RlbGV0ZWQoKSkge1xyXG4gICAgICAgIG91dE5hdGl2ZVBhdGhzLmRlbGV0ZSgpO1xyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBUaGlzIG1ldGhvZCB0YWtlcyB0d28gcGFyYW1ldGVycy4gVGhlIGZpcnN0IGlzIHRoZSBzdHJ1Y3R1cmUgdGhhdCByZWNlaXZlcyB0aGUgcmVzdWx0IG9mIHRoZSBvZmZzZXQgb3BlcmF0aW9uIChhIFBvbHlUcmVlIHN0cnVjdHVyZSkuIFRoZSBzZWNvbmQgcGFyYW1ldGVyXHJcbiAgICogaXMgdGhlIGFtb3VudCB0byB3aGljaCB0aGUgc3VwcGxpZWQgcGF0aHMgd2lsbCBiZSBvZmZzZXQuIE5lZ2F0aXZlIGRlbHRhIHZhbHVlcyBzaHJpbmsgcG9seWdvbnMgYW5kIHBvc2l0aXZlIGRlbHRhIGV4cGFuZCB0aGVtLlxyXG4gICAqXHJcbiAgICogVGhpcyBtZXRob2QgY2FuIGJlIGNhbGxlZCBtdWx0aXBsZSB0aW1lcywgb2Zmc2V0dGluZyB0aGUgc2FtZSBwYXRocyBieSBkaWZmZXJlbnQgYW1vdW50cyAoaWUgdXNpbmcgZGlmZmVyZW50IGRlbHRhcykuXHJcbiAgICpcclxuICAgKiBAcGFyYW0gZGVsdGEgLSBEZWx0YVxyXG4gICAqIEByZXR1cm4ge1BhdGhzfSAtIFNvbHV0aW9uIHBhdGhzXHJcbiAgICovXHJcbiAgZXhlY3V0ZVRvUG9seVRyZWUoZGVsdGE6IG51bWJlcik6IFBvbHlUcmVlIHtcclxuICAgIGNvbnN0IG91dE5hdGl2ZVBvbHlUcmVlID0gbmV3IHRoaXMuX25hdGl2ZUxpYi5Qb2x5VHJlZSgpO1xyXG4gICAgdHJ5IHtcclxuICAgICAgdGhpcy5fY2xpcHBlck9mZnNldCEuZXhlY3V0ZVBvbHlUcmVlKG91dE5hdGl2ZVBvbHlUcmVlLCBkZWx0YSk7XHJcbiAgICAgIHJldHVybiBQb2x5VHJlZS5mcm9tTmF0aXZlUG9seVRyZWUodGhpcy5fbmF0aXZlTGliLCBvdXROYXRpdmVQb2x5VHJlZSwgdHJ1ZSk7IC8vIGZyZWVzIG91dE5hdGl2ZVBvbHlUcmVlXHJcbiAgICB9IGZpbmFsbHkge1xyXG4gICAgICBpZiAoIW91dE5hdGl2ZVBvbHlUcmVlLmlzRGVsZXRlZCgpKSB7XHJcbiAgICAgICAgb3V0TmF0aXZlUG9seVRyZWUuZGVsZXRlKCk7XHJcbiAgICAgIH1cclxuICAgIH1cclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFRoaXMgbWV0aG9kIGNsZWFycyBhbGwgcGF0aHMgZnJvbSB0aGUgQ2xpcHBlck9mZnNldCBvYmplY3QsIGFsbG93aW5nIG5ldyBwYXRocyB0byBiZSBhc3NpZ25lZC5cclxuICAgKi9cclxuICBjbGVhcigpOiB2b2lkIHtcclxuICAgIHRoaXMuX2NsaXBwZXJPZmZzZXQhLmNsZWFyKCk7XHJcbiAgfVxyXG5cclxuICAvKipcclxuICAgKiBDaGVja3MgaWYgdGhlIG9iamVjdCBoYXMgYmVlbiBkaXNwb3NlZC5cclxuICAgKlxyXG4gICAqIEByZXR1cm4ge2Jvb2xlYW59IC0gdHJ1ZSBpZiBkaXNwb3NlZCwgZmFsc2UgaWYgbm90XHJcbiAgICovXHJcbiAgaXNEaXNwb3NlZCgpOiBib29sZWFuIHtcclxuICAgIHJldHVybiB0aGlzLl9jbGlwcGVyT2Zmc2V0ID09PSB1bmRlZmluZWQgfHwgdGhpcy5fY2xpcHBlck9mZnNldC5pc0RlbGV0ZWQoKTtcclxuICB9XHJcblxyXG4gIC8qKlxyXG4gICAqIFNpbmNlIHRoaXMgbGlicmFyeSB1c2VzIFdBU00vQVNNLkpTIGludGVybmFsbHkgZm9yIHNwZWVkIHRoaXMgbWVhbnMgdGhhdCB5b3UgbXVzdCBkaXNwb3NlIG9iamVjdHMgYWZ0ZXIgeW91IGFyZSBkb25lIHVzaW5nIHRoZW0gb3IgbWVtIGxlYWtzIHdpbGwgb2NjdXIuXHJcbiAgICogKElmIHRoZSBydW50aW1lIHN1cHBvcnRzIEZpbmFsaXphdGlvblJlZ2lzdHJ5IHRoZW4gdGhpcyBiZWNvbWVzIG5vbi1tYW5kYXRvcnksIGJ1dCBzdGlsbCByZWNvbW1lbmRlZCkuXHJcbiAgICovXHJcbiAgZGlzcG9zZSgpOiB2b2lkIHtcclxuICAgIGlmICh0aGlzLl9jbGlwcGVyT2Zmc2V0KSB7XHJcbiAgICAgIHRoaXMuX2NsaXBwZXJPZmZzZXQuZGVsZXRlKCk7XHJcbiAgICAgIG5hdGl2ZUZpbmFsaXphdGlvblJlZ2lzdHJ5Py51bnJlZ2lzdGVyKHRoaXMpO1xyXG4gICAgICB0aGlzLl9jbGlwcGVyT2Zmc2V0ID0gdW5kZWZpbmVkO1xyXG4gICAgfVxyXG4gIH1cclxufVxyXG4iXX0=