kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
595 lines (587 loc) • 67.9 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAssetRequest = getAssetRequest;
exports.getCategoricalColormapDataUrl = getCategoricalColormapDataUrl;
exports.getImageDataURL = getImageDataURL;
exports.getSTACImageRequests = getSTACImageRequests;
exports.loadImages = loadImages;
exports.loadSTACImageData = loadSTACImageData;
exports.loadTerrain = loadTerrain;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _core = require("@loaders.gl/core");
var _terrain = require("@loaders.gl/terrain");
var _memoize = _interopRequireDefault(require("lodash/memoize"));
var _constants = require("@kepler.gl/constants");
var _config = require("./config");
var _gpuUtils = require("./gpu-utils");
var _rasterTileUtils = require("./raster-tile-utils");
var _url = require("./url");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
/**
* Utility functions to create objects to pass to deck.gl-raster
*/
/**
* Create dataUrl image from bitmap array
* @param bitmap - RGBA byte bitmap array
* @param width - width of the image
* @param height - height of the image
* @returns base64 data url of the image
*/
function getImageDataURL(bitmap, width, height) {
var canvas = document.createElement('canvas');
if (!canvas.getContext) {
return null;
}
canvas.setAttribute('width', "".concat(width));
canvas.setAttribute('height', "".concat(height));
var ctx = canvas.getContext('2d');
if (!ctx) {
return null;
}
for (var i = 0; i < width; i++) {
var _bitmap$subarray = bitmap.subarray(i * 4, i * 4 + 4),
_bitmap$subarray2 = (0, _slicedToArray2["default"])(_bitmap$subarray, 4),
r = _bitmap$subarray2[0],
g = _bitmap$subarray2[1],
b = _bitmap$subarray2[2],
a = _bitmap$subarray2[3];
if (a !== 0) {
ctx.fillStyle = "rgb(".concat(r, ", ").concat(g, ", ").concat(b, ")");
ctx.fillRect(i, 0, 1, 1);
}
}
return canvas.toDataURL('image/png');
}
// Global multiplier to be applied to terrain mesh resolution
// A smaller number towards 0 will cause generation of mesh data with a smaller max error and thus a
// higher resolution mesh
var MESH_MULTIPLIER = 0.8;
/**
* Load images required for raster tile
* @param assetRequests - STAC asset requests
* @param colormapId - colormap id
* @param categoricalOptions - categorical options requred for making of categorical colormap
* @returns images map
*/
function loadImages(_x, _x2, _x3) {
return _loadImages.apply(this, arguments);
}
/**
* Cache loading of colormap. Each colormap file is very small, on the order of a few hundred bytes,
* so a global cache like this is not expected to eat up too much memory.
* Most colormaps are loaded from CDN. In case colormapId is categorical, the image is created from
* categorical colormap options.
* NOTE: this implementation will cache promise rejections as well, but since these are small files
* on the CDN, that's an acceptable risk for the time being.
*/
function _loadImages() {
_loadImages = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(assetRequests, colormapId, categoricalOptions) {
var _yield$Promise$all, _yield$Promise$all2, stacImages, colormap, imageColormap;
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return Promise.all([loadSTACImageData(assetRequests), colormapId !== _config.CATEGORICAL_COLORMAP_ID && memoizedLoadColormap(colormapId) || null]);
case 2:
_yield$Promise$all = _context.sent;
_yield$Promise$all2 = (0, _slicedToArray2["default"])(_yield$Promise$all, 2);
stacImages = _yield$Promise$all2[0];
colormap = _yield$Promise$all2[1];
imageColormap = colormap;
if (colormapId === _config.CATEGORICAL_COLORMAP_ID) {
imageColormap = (0, _gpuUtils.generateCategoricalColormapTexture)(categoricalOptions);
}
return _context.abrupt("return", _objectSpread(_objectSpread({}, stacImages), {}, {
imageColormap: imageColormap
}));
case 9:
case "end":
return _context.stop();
}
}, _callee);
}));
return _loadImages.apply(this, arguments);
}
var memoizedLoadColormap = (0, _memoize["default"])(function (colormapId) {
if (colormapId === _config.CATEGORICAL_COLORMAP_ID) {
return null;
} else {
return loadColormap(colormapId);
}
});
/**
* Load PNG colormap.
* Since the colormap files are so small (couple hundred bytes), we load them always, even for an
* RGB composite where the colormap won't be used. After the first load, the colormap should be
* loaded from the disk cache. By loading always, we can prevent unnecessarily triggering
* getTileData, which reduces flashing from reloading the images on the GPU.
*
* @param colormapId ID of colormap PNG
*
* @returns image object to pass to Texture2D constructor
*/
function loadColormap(_x4) {
return _loadColormap.apply(this, arguments);
}
/**
* Get the request data for loading a single or multi asset STAC metadata object.
* @param options STAC metadata object and user-defined parameters.
* @returns Image data, or null if the STAC metadata object is not supported.
*/
function _loadColormap() {
_loadColormap = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(colormapId) {
var request;
return _regenerator["default"].wrap(function _callee2$(_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
if (colormapId) {
_context2.next = 2;
break;
}
return _context2.abrupt("return", null);
case 2:
_context2.next = 4;
return getAssetRequest({
url: _url.RasterLayerResources.rasterColorMap(colormapId),
options: {}
});
case 4:
request = _context2.sent;
return _context2.abrupt("return", (0, _gpuUtils.loadImage)(request.url, _gpuUtils.COLORMAP_TEXTURE_PARAMETERS, request.options));
case 6:
case "end":
return _context2.stop();
}
}, _callee2);
}));
return _loadColormap.apply(this, arguments);
}
function getSTACImageRequests(_x5) {
return _getSTACImageRequests.apply(this, arguments);
} // TODO: image loading should really be _driven by the modules_
// E.g. the user chooses what kind of pipeline they want on the GPU, then from those modules we can
// see whether the user wants the pansharpened band, colormap, etc
/**
* Load images defined by a STAC metadata object plus user-defined parameters
*/
function _getSTACImageRequests() {
_getSTACImageRequests = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(options) {
var loadAssetIds, request;
return _regenerator["default"].wrap(function _callee3$(_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
loadAssetIds = options.loadAssetIds;
if (!(loadAssetIds.length === 1)) {
_context3.next = 6;
break;
}
_context3.next = 4;
return getSingleAssetSTACRequest(options);
case 4:
request = _context3.sent;
return _context3.abrupt("return", request ? [request] : null);
case 6:
_context3.next = 8;
return getMultiAssetSTACRequest(options);
case 8:
return _context3.abrupt("return", _context3.sent);
case 9:
case "end":
return _context3.stop();
}
}, _callee3);
}));
return _getSTACImageRequests.apply(this, arguments);
}
function loadSTACImageData(_x6) {
return _loadSTACImageData.apply(this, arguments);
}
/**
* Utility function that forms an asset request based on given parameters.
*/
function _loadSTACImageData() {
_loadSTACImageData = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee4(requests) {
return _regenerator["default"].wrap(function _callee4$(_context4) {
while (1) switch (_context4.prev = _context4.next) {
case 0:
return _context4.abrupt("return", requests.length === 1 ? loadSingleAssetSTAC(requests[0]) : loadMultiAssetSTAC(requests));
case 1:
case "end":
return _context4.stop();
}
}, _callee4);
}));
return _loadSTACImageData.apply(this, arguments);
}
function getAssetRequest(_x7) {
return _getAssetRequest.apply(this, arguments);
}
/**
* Utility function for forming a request for a single asset STAC metadata object.
*/
// eslint-disable-next-line complexity, max-statements
function _getAssetRequest() {
_getAssetRequest = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee5(_ref) {
var url, params, options, _ref$useMask, useMask, responseRequiredBandIndices, requestUrl, requestParams, requestOptions, assetUrl;
return _regenerator["default"].wrap(function _callee5$(_context5) {
while (1) switch (_context5.prev = _context5.next) {
case 0:
url = _ref.url, params = _ref.params, options = _ref.options, _ref$useMask = _ref.useMask, useMask = _ref$useMask === void 0 ? true : _ref$useMask, responseRequiredBandIndices = _ref.responseRequiredBandIndices;
requestUrl = url;
requestParams = params !== null && params !== void 0 ? params : new URLSearchParams();
requestOptions = options;
assetUrl = requestParams ? "".concat(requestUrl, "?").concat(requestParams.toString()) : requestUrl;
return _context5.abrupt("return", {
url: assetUrl,
options: requestOptions,
useMask: useMask,
responseRequiredBandIndices: responseRequiredBandIndices
});
case 6:
case "end":
return _context5.stop();
}
}, _callee5);
}));
return _getAssetRequest.apply(this, arguments);
}
function getSingleAssetSTACRequest(_x8) {
return _getSingleAssetSTACRequest.apply(this, arguments);
}
/**
* Utility function for forming requests for a multi asset STAC metadata object.
*/
function _getSingleAssetSTACRequest() {
_getSingleAssetSTACRequest = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee6(options) {
var stac, loadAssetIds, loadBandIndexes, signal, useSTACSearching, _options$index, x, y, z, stacSearchProvider, startDate, endDate, _stacQuery, useMask, url, urlParams, responseRequiredBandIndices;
return _regenerator["default"].wrap(function _callee6$(_context6) {
while (1) switch (_context6.prev = _context6.next) {
case 0:
stac = options.stac, loadAssetIds = options.loadAssetIds, loadBandIndexes = options.loadBandIndexes, signal = options.signal, useSTACSearching = options.useSTACSearching, _options$index = options.index, x = _options$index.x, y = _options$index.y, z = _options$index.z, stacSearchProvider = options.stacSearchProvider, startDate = options.startDate, endDate = options.endDate, _stacQuery = options._stacQuery;
useMask = true; // Only a single URL because only a single asset
url = null;
urlParams = new URLSearchParams();
responseRequiredBandIndices = loadBandIndexes;
if (!(useSTACSearching && stac.type !== 'Feature')) {
_context6.next = 10;
break;
}
urlParams = (0, _url.getStacApiUrlParams)({
stac: stac,
stacSearchProvider: stacSearchProvider,
startDate: startDate,
endDate: endDate,
mask: useMask,
loadAssetIds: loadAssetIds,
_stacQuery: _stacQuery
});
url = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
_context6.next = 17;
break;
case 10:
if (!(stac.type === 'Feature')) {
_context6.next = 16;
break;
}
// stac is an Item
urlParams = (0, _url.getSingleCOGUrlParams)({
stac: stac,
loadAssetId: loadAssetIds[0],
loadBandIndexes: loadBandIndexes,
mask: useMask
});
url = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
responseRequiredBandIndices = null;
_context6.next = 17;
break;
case 16:
return _context6.abrupt("return", null);
case 17:
if (url) {
_context6.next = 19;
break;
}
return _context6.abrupt("return", null);
case 19:
_context6.next = 21;
return getAssetRequest({
url: url,
params: urlParams,
options: {
signal: signal
},
useMask: useMask,
responseRequiredBandIndices: responseRequiredBandIndices
});
case 21:
return _context6.abrupt("return", _context6.sent);
case 22:
case "end":
return _context6.stop();
}
}, _callee6);
}));
return _getSingleAssetSTACRequest.apply(this, arguments);
}
function getMultiAssetSTACRequest(_x9) {
return _getMultiAssetSTACRequest.apply(this, arguments);
}
/**
* Load image data when consolidated in bands within a single STAC Asset
*/
function _getMultiAssetSTACRequest() {
_getMultiAssetSTACRequest = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee7(options) {
var stac, loadAssetIds, loadBandIndexes, signal, useSTACSearching, stacSearchProvider, startDate, endDate, _options$index2, x, y, z, _stacQuery, requestData, requestMask;
return _regenerator["default"].wrap(function _callee7$(_context7) {
while (1) switch (_context7.prev = _context7.next) {
case 0:
stac = options.stac, loadAssetIds = options.loadAssetIds, loadBandIndexes = options.loadBandIndexes, signal = options.signal, useSTACSearching = options.useSTACSearching, stacSearchProvider = options.stacSearchProvider, startDate = options.startDate, endDate = options.endDate, _options$index2 = options.index, x = _options$index2.x, y = _options$index2.y, z = _options$index2.z, _stacQuery = options._stacQuery; // Multiple urls, one for each asset
requestData = []; // We assume that there's only one validity mask for the entire asset, and therefore any of the
// requests would return the same validity bitmap. Therefore we only request a mask for the first
// asset
requestMask = new Array(loadAssetIds.length).fill(false);
requestMask[0] = true;
if (useSTACSearching && stac.type !== 'Feature') {
requestData = zip(loadAssetIds, requestMask).map(function (_ref2) {
var _ref3 = (0, _slicedToArray2["default"])(_ref2, 2),
assetId = _ref3[0],
mask = _ref3[1];
var params = (0, _url.getStacApiUrlParams)({
stac: stac,
stacSearchProvider: stacSearchProvider,
startDate: startDate,
endDate: endDate,
mask: mask,
loadAssetIds: [assetId],
_stacQuery: _stacQuery
});
var url = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
return {
url: url,
params: params
};
});
} else if (stac.type === 'Feature') {
requestData = zip3(loadAssetIds, loadBandIndexes, requestMask).map(function (_ref4) {
var _ref5 = (0, _slicedToArray2["default"])(_ref4, 3),
assetId = _ref5[0],
bandIndex = _ref5[1],
mask = _ref5[2];
var params = (0, _url.getSingleCOGUrlParams)({
stac: stac,
loadAssetId: assetId,
loadBandIndexes: [bandIndex],
mask: mask
});
var url = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
return {
url: url,
params: params
};
});
}
if (!isValidRequestData(requestData)) {
_context7.next = 9;
break;
}
_context7.next = 8;
return Promise.all(requestData.map(function (request) {
var _request$options;
return getAssetRequest(_objectSpread(_objectSpread({}, request), {}, {
options: (_request$options = request.options) !== null && _request$options !== void 0 ? _request$options : {
signal: signal
}
}));
}));
case 8:
return _context7.abrupt("return", _context7.sent);
case 9:
return _context7.abrupt("return", null);
case 10:
case "end":
return _context7.stop();
}
}, _callee7);
}));
return _getMultiAssetSTACRequest.apply(this, arguments);
}
function loadSingleAssetSTAC(_x10) {
return _loadSingleAssetSTAC.apply(this, arguments);
}
/**
* Load image data when split among multiple STAC Assets
*/
function _loadSingleAssetSTAC() {
_loadSingleAssetSTAC = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee8(request) {
var imageBands, imageMask, mappedImageBands;
return _regenerator["default"].wrap(function _callee8$(_context8) {
while (1) switch (_context8.prev = _context8.next) {
case 0:
_context8.next = 2;
return (0, _gpuUtils.loadNpyArray)(request, true);
case 2:
imageBands = _context8.sent;
if (imageBands) {
_context8.next = 5;
break;
}
return _context8.abrupt("return", {
imageBands: imageBands
});
case 5:
imageMask = request.useMask && imageBands.pop() || null;
mappedImageBands = imageBands;
if (request.responseRequiredBandIndices) {
mappedImageBands = request.responseRequiredBandIndices.map(function (i) {
return imageBands[i];
});
}
return _context8.abrupt("return", {
imageBands: mappedImageBands,
imageMask: imageMask
});
case 9:
case "end":
return _context8.stop();
}
}, _callee8);
}));
return _loadSingleAssetSTAC.apply(this, arguments);
}
function loadMultiAssetSTAC(_x11) {
return _loadMultiAssetSTAC.apply(this, arguments);
}
/**
* Iterate over two arrays simultaneously, similar to Python's zip builtin
*/
function _loadMultiAssetSTAC() {
_loadMultiAssetSTAC = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee9(requests) {
var _results$;
var results, imageMask, imageBands;
return _regenerator["default"].wrap(function _callee9$(_context9) {
while (1) switch (_context9.prev = _context9.next) {
case 0:
_context9.next = 2;
return Promise.all(requests.map(function (request) {
return (0, _gpuUtils.loadNpyArray)(request, true);
}));
case 2:
results = _context9.sent;
// The first request includes a mask
imageMask = ((_results$ = results[0]) === null || _results$ === void 0 ? void 0 : _results$.pop()) || null;
imageBands = results.flat().filter(function (band) {
return band !== null;
});
return _context9.abrupt("return", {
imageBands: imageBands,
imageMask: imageMask
});
case 6:
case "end":
return _context9.stop();
}
}, _callee9);
}));
return _loadMultiAssetSTAC.apply(this, arguments);
}
function zip(a, b) {
return a.map(function (k, i) {
return [k, b[i]];
});
}
/**
* Iterate over three arrays simultaneously, similar to Python's zip builtin
*/
function zip3(a, b, c) {
return a.map(function (k, i) {
return [k, b[i], c[i]];
});
}
/**
* Type guard to check if all array elements are strings
*/
function isValidRequestData(arr) {
return arr.every(function (x) {
return typeof (x === null || x === void 0 ? void 0 : x.url) === 'string' && (x === null || x === void 0 ? void 0 : x.params) !== null;
});
}
/**
* Create base64 image data url for categorical colormap
* @param categoricalOptions - color map configuration and min-max values of categorical band
* @returns base64 image data url
*/
function getCategoricalColormapDataUrl(categoricalOptions) {
var bitmap = (0, _rasterTileUtils.generateCategoricalBitmapArray)(categoricalOptions);
if (!bitmap) {
return null;
}
return getImageDataURL(bitmap, _rasterTileUtils.CATEGORICAL_TEXTURE_WIDTH, 1);
}
/**
* Load terrain mesh from raster tile server
* @param props - properties to load terrain data
* @returns terrain mesh data
*/
function loadTerrain(_x12) {
return _loadTerrain.apply(this, arguments);
}
function _loadTerrain() {
_loadTerrain = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10(props) {
var _props$index, x, y, z, boundsForGeometry, signal, rasterTileServerUrls, meshMaxError, terrainUrl, loaderOptions;
return _regenerator["default"].wrap(function _callee10$(_context10) {
while (1) switch (_context10.prev = _context10.next) {
case 0:
_props$index = props.index, x = _props$index.x, y = _props$index.y, z = _props$index.z, boundsForGeometry = props.boundsForGeometry, signal = props.signal, rasterTileServerUrls = props.rasterTileServerUrls;
meshMaxError = (0, _url.getMeshMaxError)(z, MESH_MULTIPLIER);
terrainUrl = (0, _url.getTerrainUrl)(rasterTileServerUrls, x, y, z, meshMaxError);
loaderOptions = (0, _constants.getLoaderOptions)();
return _context10.abrupt("return", (0, _core.load)(terrainUrl, _terrain.QuantizedMeshLoader, {
fetch: {
signal: signal
},
'quantized-mesh': _objectSpread(_objectSpread({}, loaderOptions['quantized-mesh']), {}, {
bounds: boundsForGeometry,
skirtHeight: meshMaxError * 2
})
}));
case 5:
case "end":
return _context10.stop();
}
}, _callee10);
}));
return _loadTerrain.apply(this, arguments);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29yZSIsInJlcXVpcmUiLCJfdGVycmFpbiIsIl9tZW1vaXplIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9jb25zdGFudHMiLCJfY29uZmlnIiwiX2dwdVV0aWxzIiwiX3Jhc3RlclRpbGVVdGlscyIsIl91cmwiLCJvd25LZXlzIiwiZSIsInIiLCJ0IiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsIm8iLCJmaWx0ZXIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJlbnVtZXJhYmxlIiwicHVzaCIsImFwcGx5IiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJkZWZpbmVQcm9wZXJ0eSIsImdldEltYWdlRGF0YVVSTCIsImJpdG1hcCIsIndpZHRoIiwiaGVpZ2h0IiwiY2FudmFzIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50IiwiZ2V0Q29udGV4dCIsInNldEF0dHJpYnV0ZSIsImNvbmNhdCIsImN0eCIsImkiLCJfYml0bWFwJHN1YmFycmF5Iiwic3ViYXJyYXkiLCJfYml0bWFwJHN1YmFycmF5MiIsIl9zbGljZWRUb0FycmF5MiIsImciLCJiIiwiYSIsImZpbGxTdHlsZSIsImZpbGxSZWN0IiwidG9EYXRhVVJMIiwiTUVTSF9NVUxUSVBMSUVSIiwibG9hZEltYWdlcyIsIl94IiwiX3gyIiwiX3gzIiwiX2xvYWRJbWFnZXMiLCJfYXN5bmNUb0dlbmVyYXRvcjIiLCJfcmVnZW5lcmF0b3IiLCJtYXJrIiwiX2NhbGxlZSIsImFzc2V0UmVxdWVzdHMiLCJjb2xvcm1hcElkIiwiY2F0ZWdvcmljYWxPcHRpb25zIiwiX3lpZWxkJFByb21pc2UkYWxsIiwiX3lpZWxkJFByb21pc2UkYWxsMiIsInN0YWNJbWFnZXMiLCJjb2xvcm1hcCIsImltYWdlQ29sb3JtYXAiLCJ3cmFwIiwiX2NhbGxlZSQiLCJfY29udGV4dCIsInByZXYiLCJuZXh0IiwiUHJvbWlzZSIsImFsbCIsImxvYWRTVEFDSW1hZ2VEYXRhIiwiQ0FURUdPUklDQUxfQ09MT1JNQVBfSUQiLCJtZW1vaXplZExvYWRDb2xvcm1hcCIsInNlbnQiLCJnZW5lcmF0ZUNhdGVnb3JpY2FsQ29sb3JtYXBUZXh0dXJlIiwiYWJydXB0Iiwic3RvcCIsIm1lbW9pemUiLCJsb2FkQ29sb3JtYXAiLCJfeDQiLCJfbG9hZENvbG9ybWFwIiwiX2NhbGxlZTIiLCJyZXF1ZXN0IiwiX2NhbGxlZTIkIiwiX2NvbnRleHQyIiwiZ2V0QXNzZXRSZXF1ZXN0IiwidXJsIiwiUmFzdGVyTGF5ZXJSZXNvdXJjZXMiLCJyYXN0ZXJDb2xvck1hcCIsIm9wdGlvbnMiLCJsb2FkSW1hZ2UiLCJDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlMiLCJnZXRTVEFDSW1hZ2VSZXF1ZXN0cyIsIl94NSIsIl9nZXRTVEFDSW1hZ2VSZXF1ZXN0cyIsIl9jYWxsZWUzIiwibG9hZEFzc2V0SWRzIiwiX2NhbGxlZTMkIiwiX2NvbnRleHQzIiwiZ2V0U2luZ2xlQXNzZXRTVEFDUmVxdWVzdCIsImdldE11bHRpQXNzZXRTVEFDUmVxdWVzdCIsIl94NiIsIl9sb2FkU1RBQ0ltYWdlRGF0YSIsIl9jYWxsZWU0IiwicmVxdWVzdHMiLCJfY2FsbGVlNCQiLCJfY29udGV4dDQiLCJsb2FkU2luZ2xlQXNzZXRTVEFDIiwibG9hZE11bHRpQXNzZXRTVEFDIiwiX3g3IiwiX2dldEFzc2V0UmVxdWVzdCIsIl9jYWxsZWU1IiwiX3JlZiIsInBhcmFtcyIsIl9yZWYkdXNlTWFzayIsInVzZU1hc2siLCJyZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXMiLCJyZXF1ZXN0VXJsIiwicmVxdWVzdFBhcmFtcyIsInJlcXVlc3RPcHRpb25zIiwiYXNzZXRVcmwiLCJfY2FsbGVlNSQiLCJfY29udGV4dDUiLCJVUkxTZWFyY2hQYXJhbXMiLCJ0b1N0cmluZyIsIl94OCIsIl9nZXRTaW5nbGVBc3NldFNUQUNSZXF1ZXN0IiwiX2NhbGxlZTYiLCJzdGFjIiwibG9hZEJhbmRJbmRleGVzIiwic2lnbmFsIiwidXNlU1RBQ1NlYXJjaGluZyIsIl9vcHRpb25zJGluZGV4IiwieCIsInkiLCJ6Iiwic3RhY1NlYXJjaFByb3ZpZGVyIiwic3RhcnREYXRlIiwiZW5kRGF0ZSIsIl9zdGFjUXVlcnkiLCJ1cmxQYXJhbXMiLCJfY2FsbGVlNiQiLCJfY29udGV4dDYiLCJpbmRleCIsInR5cGUiLCJnZXRTdGFjQXBpVXJsUGFyYW1zIiwibWFzayIsImdldFRpdGlsZXJVcmwiLCJnZXRTaW5nbGVDT0dVcmxQYXJhbXMiLCJsb2FkQXNzZXRJZCIsIl94OSIsIl9nZXRNdWx0aUFzc2V0U1RBQ1JlcXVlc3QiLCJfY2FsbGVlNyIsIl9vcHRpb25zJGluZGV4MiIsInJlcXVlc3REYXRhIiwicmVxdWVzdE1hc2siLCJfY2FsbGVlNyQiLCJfY29udGV4dDciLCJBcnJheSIsImZpbGwiLCJ6aXAiLCJtYXAiLCJfcmVmMiIsIl9yZWYzIiwiYXNzZXRJZCIsInppcDMiLCJfcmVmNCIsIl9yZWY1IiwiYmFuZEluZGV4IiwiaXNWYWxpZFJlcXVlc3REYXRhIiwiX3JlcXVlc3Qkb3B0aW9ucyIsIl94MTAiLCJfbG9hZFNpbmdsZUFzc2V0U1RBQyIsIl9jYWxsZWU4IiwiaW1hZ2VCYW5kcyIsImltYWdlTWFzayIsIm1hcHBlZEltYWdlQmFuZHMiLCJfY2FsbGVlOCQiLCJfY29udGV4dDgiLCJsb2FkTnB5QXJyYXkiLCJwb3AiLCJfeDExIiwiX2xvYWRNdWx0aUFzc2V0U1RBQyIsIl9jYWxsZWU5IiwiX3Jlc3VsdHMkIiwicmVzdWx0cyIsIl9jYWxsZWU5JCIsIl9jb250ZXh0OSIsImZsYXQiLCJiYW5kIiwiayIsImMiLCJhcnIiLCJldmVyeSIsImdldENhdGVnb3JpY2FsQ29sb3JtYXBEYXRhVXJsIiwiZ2VuZXJhdGVDYXRlZ29yaWNhbEJpdG1hcEFycmF5IiwiQ0FURUdPUklDQUxfVEVYVFVSRV9XSURUSCIsImxvYWRUZXJyYWluIiwiX3gxMiIsIl9sb2FkVGVycmFpbiIsIl9jYWxsZWUxMCIsInByb3BzIiwiX3Byb3BzJGluZGV4IiwiYm91bmRzRm9yR2VvbWV0cnkiLCJyYXN0ZXJUaWxlU2VydmVyVXJscyIsIm1lc2hNYXhFcnJvciIsInRlcnJhaW5VcmwiLCJsb2FkZXJPcHRpb25zIiwiX2NhbGxlZTEwJCIsIl9jb250ZXh0MTAiLCJnZXRNZXNoTWF4RXJyb3IiLCJnZXRUZXJyYWluVXJsIiwiZ2V0TG9hZGVyT3B0aW9ucyIsImxvYWQiLCJRdWFudGl6ZWRNZXNoTG9hZGVyIiwiZmV0Y2giLCJib3VuZHMiLCJza2lydEhlaWdodCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yYXN0ZXItdGlsZS9pbWFnZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb25zIHRvIGNyZWF0ZSBvYmplY3RzIHRvIHBhc3MgdG8gZGVjay5nbC1yYXN0ZXJcbiAqL1xuXG5pbXBvcnQge2xvYWR9IGZyb20gJ0Bsb2FkZXJzLmdsL2NvcmUnO1xuaW1wb3J0IHtRdWFudGl6ZWRNZXNoTG9hZGVyfSBmcm9tICdAbG9hZGVycy5nbC90ZXJyYWluJztcbmltcG9ydCBtZW1vaXplIGZyb20gJ2xvZGFzaC9tZW1vaXplJztcblxuaW1wb3J0IHtnZXRMb2FkZXJPcHRpb25zfSBmcm9tICdAa2VwbGVyLmdsL2NvbnN0YW50cyc7XG5cbmltcG9ydCB7Q0FURUdPUklDQUxfQ09MT1JNQVBfSUR9IGZyb20gJy4vY29uZmlnJztcbmltcG9ydCB7XG4gIGxvYWROcHlBcnJheSxcbiAgZ2VuZXJhdGVDYXRlZ29yaWNhbENvbG9ybWFwVGV4dHVyZSxcbiAgbG9hZEltYWdlLFxuICBDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlNcbn0gZnJvbSAnLi9ncHUtdXRpbHMnO1xuaW1wb3J0IHtDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRILCBnZW5lcmF0ZUNhdGVnb3JpY2FsQml0bWFwQXJyYXl9IGZyb20gJy4vcmFzdGVyLXRpbGUtdXRpbHMnO1xuaW1wb3J0IHtcbiAgR2V0VGlsZURhdGFQcm9wcyxcbiAgSW1hZ2VEYXRhLFxuICBUZXJyYWluRGF0YSxcbiAgQXNzZXRSZXF1ZXN0RGF0YSxcbiAgQ2F0ZWdvcmljYWxDb2xvcm1hcE9wdGlvbnMsXG4gIENvbG9ybWFwSW1hZ2VEYXRhXG59IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHtcbiAgZ2V0VGl0aWxlclVybCxcbiAgZ2V0VGVycmFpblVybCxcbiAgZ2V0U2luZ2xlQ09HVXJsUGFyYW1zLFxuICBnZXRTdGFjQXBpVXJsUGFyYW1zLFxuICBnZXRNZXNoTWF4RXJyb3IsXG4gIFJhc3RlckxheWVyUmVzb3VyY2VzXG59IGZyb20gJy4vdXJsJztcblxuLyoqXG4gKiBDcmVhdGUgZGF0YVVybCBpbWFnZSBmcm9tIGJpdG1hcCBhcnJheVxuICogQHBhcmFtIGJpdG1hcCAtIFJHQkEgYnl0ZSBiaXRtYXAgYXJyYXlcbiAqIEBwYXJhbSB3aWR0aCAtIHdpZHRoIG9mIHRoZSBpbWFnZVxuICogQHBhcmFtIGhlaWdodCAtIGhlaWdodCBvZiB0aGUgaW1hZ2VcbiAqIEByZXR1cm5zIGJhc2U2NCBkYXRhIHVybCBvZiB0aGUgaW1hZ2VcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEltYWdlRGF0YVVSTChiaXRtYXA6IFVpbnQ4QXJyYXksIHdpZHRoOiBudW1iZXIsIGhlaWdodDogbnVtYmVyKTogc3RyaW5nIHwgbnVsbCB7XG4gIGNvbnN0IGNhbnZhcyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoJ2NhbnZhcycpO1xuICBpZiAoIWNhbnZhcy5nZXRDb250ZXh0KSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY2FudmFzLnNldEF0dHJpYnV0ZSgnd2lkdGgnLCBgJHt3aWR0aH1gKTtcbiAgY2FudmFzLnNldEF0dHJpYnV0ZSgnaGVpZ2h0JywgYCR7aGVpZ2h0fWApO1xuICBjb25zdCBjdHggPSBjYW52YXMuZ2V0Q29udGV4dCgnMmQnKTtcbiAgaWYgKCFjdHgpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBmb3IgKGxldCBpID0gMDsgaSA8IHdpZHRoOyBpKyspIHtcbiAgICBjb25zdCBbciwgZywgYiwgYV0gPSBiaXRtYXAuc3ViYXJyYXkoaSAqIDQsIGkgKiA0ICsgNCk7XG4gICAgaWYgKGEgIT09IDApIHtcbiAgICAgIGN0eC5maWxsU3R5bGUgPSBgcmdiKCR7cn0sICR7Z30sICR7Yn0pYDtcbiAgICAgIGN0eC5maWxsUmVjdChpLCAwLCAxLCAxKTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGNhbnZhcy50b0RhdGFVUkwoJ2ltYWdlL3BuZycpO1xufVxuXG4vLyBHbG9iYWwgbXVsdGlwbGllciB0byBiZSBhcHBsaWVkIHRvIHRlcnJhaW4gbWVzaCByZXNvbHV0aW9uXG4vLyBBIHNtYWxsZXIgbnVtYmVyIHRvd2FyZHMgMCB3aWxsIGNhdXNlIGdlbmVyYXRpb24gb2YgbWVzaCBkYXRhIHdpdGggYSBzbWFsbGVyIG1heCBlcnJvciBhbmQgdGh1cyBhXG4vLyBoaWdoZXIgcmVzb2x1dGlvbiBtZXNoXG5jb25zdCBNRVNIX01VTFRJUExJRVIgPSAwLjg7XG5cbi8qKlxuICogTG9hZCBpbWFnZXMgcmVxdWlyZWQgZm9yIHJhc3RlciB0aWxlXG4gKiBAcGFyYW0gYXNzZXRSZXF1ZXN0cyAtIFNUQUMgYXNzZXQgcmVxdWVzdHNcbiAqIEBwYXJhbSBjb2xvcm1hcElkIC0gY29sb3JtYXAgaWRcbiAqIEBwYXJhbSBjYXRlZ29yaWNhbE9wdGlvbnMgLSBjYXRlZ29yaWNhbCBvcHRpb25zIHJlcXVyZWQgZm9yIG1ha2luZyBvZiBjYXRlZ29yaWNhbCBjb2xvcm1hcFxuICogQHJldHVybnMgaW1hZ2VzIG1hcFxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gbG9hZEltYWdlcyhcbiAgYXNzZXRSZXF1ZXN0czogQXNzZXRSZXF1ZXN0RGF0YVtdLFxuICBjb2xvcm1hcElkOiBzdHJpbmcsXG4gIGNhdGVnb3JpY2FsT3B0aW9uczogQ2F0ZWdvcmljYWxDb2xvcm1hcE9wdGlvbnNcbik6IFByb21pc2U8SW1hZ2VEYXRhPiB7XG4gIC8vIFdlIGxvYWQgaW1hZ2UgZGF0YSAoc2luZ2xlIG9yIG11bHRpIGFzc2V0IGJhc2VkIG9uIG51bWJlciBvZiByZXF1ZXN0cykgYW5kIGNvbG9ybWFwIGluIHBhcmFsbGVsXG4gIGNvbnN0IFtzdGFjSW1hZ2VzLCBjb2xvcm1hcF0gPSBhd2FpdCBQcm9taXNlLmFsbChbXG4gICAgbG9hZFNUQUNJbWFnZURhdGEoYXNzZXRSZXF1ZXN0cyksXG4gICAgKGNvbG9ybWFwSWQgIT09IENBVEVHT1JJQ0FMX0NPTE9STUFQX0lEICYmIG1lbW9pemVkTG9hZENvbG9ybWFwKGNvbG9ybWFwSWQpKSB8fCBudWxsXG4gIF0pO1xuICBsZXQgaW1hZ2VDb2xvcm1hcCA9IGNvbG9ybWFwO1xuICBpZiAoY29sb3JtYXBJZCA9PT0gQ0FURUdPUklDQUxfQ09MT1JNQVBfSUQpIHtcbiAgICBpbWFnZUNvbG9ybWFwID0gZ2VuZXJhdGVDYXRlZ29yaWNhbENvbG9ybWFwVGV4dHVyZShjYXRlZ29yaWNhbE9wdGlvbnMpO1xuICB9XG4gIHJldHVybiB7XG4gICAgLi4uc3RhY0ltYWdlcyxcbiAgICBpbWFnZUNvbG9ybWFwXG4gIH07XG59XG5cbi8qKlxuICogQ2FjaGUgbG9hZGluZyBvZiBjb2xvcm1hcC4gRWFjaCBjb2xvcm1hcCBmaWxlIGlzIHZlcnkgc21hbGwsIG9uIHRoZSBvcmRlciBvZiBhIGZldyBodW5kcmVkIGJ5dGVzLFxuICogc28gYSBnbG9iYWwgY2FjaGUgbGlrZSB0aGlzIGlzIG5vdCBleHBlY3RlZCB0byBlYXQgdXAgdG9vIG11Y2ggbWVtb3J5LlxuICogTW9zdCBjb2xvcm1hcHMgYXJlIGxvYWRlZCBmcm9tIENETi4gSW4gY2FzZSBjb2xvcm1hcElkIGlzIGNhdGVnb3JpY2FsLCB0aGUgaW1hZ2UgaXMgY3JlYXRlZCBmcm9tXG4gKiBjYXRlZ29yaWNhbCBjb2xvcm1hcCBvcHRpb25zLlxuICogTk9URTogdGhpcyBpbXBsZW1lbnRhdGlvbiB3aWxsIGNhY2hlIHByb21pc2UgcmVqZWN0aW9ucyBhcyB3ZWxsLCBidXQgc2luY2UgdGhlc2UgYXJlIHNtYWxsIGZpbGVzXG4gKiBvbiB0aGUgQ0ROLCB0aGF0J3MgYW4gYWNjZXB0YWJsZSByaXNrIGZvciB0aGUgdGltZSBiZWluZy5cbiAqL1xuY29uc3QgbWVtb2l6ZWRMb2FkQ29sb3JtYXA6IChjb2xvcm1hcElkOiBzdHJpbmcpID0+IFByb21pc2U8Q29sb3JtYXBJbWFnZURhdGEgfCBudWxsPiB8IG51bGwgPVxuICBtZW1vaXplKChjb2xvcm1hcElkOiBzdHJpbmcpID0+IHtcbiAgICBpZiAoY29sb3JtYXBJZCA9PT0gQ0FURUdPUklDQUxfQ09MT1JNQVBfSUQpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gbG9hZENvbG9ybWFwKGNvbG9ybWFwSWQpO1xuICAgIH1cbiAgfSk7XG5cbi8qKlxuICogTG9hZCBQTkcgY29sb3JtYXAuXG4gKiBTaW5jZSB0aGUgY29sb3JtYXAgZmlsZXMgYXJlIHNvIHNtYWxsIChjb3VwbGUgaHVuZHJlZCBieXRlcyksIHdlIGxvYWQgdGhlbSBhbHdheXMsIGV2ZW4gZm9yIGFuXG4gKiBSR0IgY29tcG9zaXRlIHdoZXJlIHRoZSBjb2xvcm1hcCB3b24ndCBiZSB1c2VkLiBBZnRlciB0aGUgZmlyc3QgbG9hZCwgdGhlIGNvbG9ybWFwIHNob3VsZCBiZVxuICogbG9hZGVkIGZyb20gdGhlIGRpc2sgY2FjaGUuIEJ5IGxvYWRpbmcgYWx3YXlzLCB3ZSBjYW4gcHJldmVudCB1bm5lY2Vzc2FyaWx5IHRyaWdnZXJpbmdcbiAqIGdldFRpbGVEYXRhLCB3aGljaCByZWR1Y2VzIGZsYXNoaW5nIGZyb20gcmVsb2FkaW5nIHRoZSBpbWFnZXMgb24gdGhlIEdQVS5cbiAqXG4gKiBAcGFyYW0gY29sb3JtYXBJZCAgICAgIElEIG9mIGNvbG9ybWFwIFBOR1xuICpcbiAqIEByZXR1cm5zIGltYWdlIG9iamVjdCB0byBwYXNzIHRvIFRleHR1cmUyRCBjb25zdHJ1Y3RvclxuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkQ29sb3JtYXAoY29sb3JtYXBJZDogc3RyaW5nKTogUHJvbWlzZTxDb2xvcm1hcEltYWdlRGF0YSB8IG51bGw+IHtcbiAgaWYgKCFjb2xvcm1hcElkKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgZ2V0QXNzZXRSZXF1ZXN0KHtcbiAgICB1cmw6IFJhc3RlckxheWVyUmVzb3VyY2VzLnJhc3RlckNvbG9yTWFwKGNvbG9ybWFwSWQpLFxuICAgIG9wdGlvbnM6IHt9XG4gIH0pO1xuICByZXR1cm4gbG9hZEltYWdlKHJlcXVlc3QudXJsLCBDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlMsIHJlcXVlc3Qub3B0aW9ucyk7XG59XG5cbi8qKlxuICogR2V0IHRoZSByZXF1ZXN0IGRhdGEgZm9yIGxvYWRpbmcgYSBzaW5nbGUgb3IgbXVsdGkgYXNzZXQgU1RBQyBtZXRhZGF0YSBvYmplY3QuXG4gKiBAcGFyYW0gb3B0aW9ucyBTVEFDIG1ldGFkYXRhIG9iamVjdCBhbmQgdXNlci1kZWZpbmVkIHBhcmFtZXRlcnMuXG4gKiBAcmV0dXJucyBJbWFnZSBkYXRhLCBvciBudWxsIGlmIHRoZSBTVEFDIG1ldGFkYXRhIG9iamVjdCBpcyBub3Qgc3VwcG9ydGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0U1RBQ0ltYWdlUmVxdWVzdHMoXG4gIG9wdGlvbnM6IEdldFRpbGVEYXRhUHJvcHNcbik6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YVtdIHwgbnVsbD4ge1xuICBjb25zdCB7bG9hZEFzc2V0SWRzfSA9IG9wdGlvbnM7XG5cbiAgaWYgKGxvYWRBc3NldElkcy5sZW5ndGggPT09IDEpIHtcbiAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgZ2V0U2luZ2xlQXNzZXRTVEFDUmVxdWVzdChvcHRpb25zKTtcbiAgICByZXR1cm4gcmVxdWVzdCA/IFtyZXF1ZXN0XSA6IG51bGw7XG4gIH1cblxuICByZXR1cm4gYXdhaXQgZ2V0TXVsdGlBc3NldFNUQUNSZXF1ZXN0KG9wdGlvbnMpO1xufVxuXG4vLyBUT0RPOiBpbWFnZSBsb2FkaW5nIHNob3VsZCByZWFsbHkgYmUgX2RyaXZlbiBieSB0aGUgbW9kdWxlc19cbi8vIEUuZy4gdGhlIHVzZXIgY2hvb3NlcyB3aGF0IGtpbmQgb2YgcGlwZWxpbmUgdGhleSB3YW50IG9uIHRoZSBHUFUsIHRoZW4gZnJvbSB0aG9zZSBtb2R1bGVzIHdlIGNhblxuLy8gc2VlIHdoZXRoZXIgdGhlIHVzZXIgd2FudHMgdGhlIHBhbnNoYXJwZW5lZCBiYW5kLCBjb2xvcm1hcCwgZXRjXG4vKipcbiAqIExvYWQgaW1hZ2VzIGRlZmluZWQgYnkgYSBTVEFDIG1ldGFkYXRhIG9iamVjdCBwbHVzIHVzZXItZGVmaW5lZCBwYXJhbWV0ZXJzXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkU1RBQ0ltYWdlRGF0YShyZXF1ZXN0czogQXNzZXRSZXF1ZXN0RGF0YVtdKTogUHJvbWlzZTxJbWFnZURhdGE+IHtcbiAgcmV0dXJuIHJlcXVlc3RzLmxlbmd0aCA9PT0gMSA/IGxvYWRTaW5nbGVBc3NldFNUQUMocmVxdWVzdHNbMF0pIDogbG9hZE11bHRpQXNzZXRTVEFDKHJlcXVlc3RzKTtcbn1cblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9uIHRoYXQgZm9ybXMgYW4gYXNzZXQgcmVxdWVzdCBiYXNlZCBvbiBnaXZlbiBwYXJhbWV0ZXJzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0QXNzZXRSZXF1ZXN0KHtcbiAgdXJsLFxuICBwYXJhbXMsXG4gIG9wdGlvbnMsXG4gIHVzZU1hc2sgPSB0cnVlLFxuICByZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXNcbn06IHtcbiAgdXJsOiBzdHJpbmc7XG4gIHBhcmFtcz86IFVSTFNlYXJjaFBhcmFtcyB8IG51bGw7XG4gIG9wdGlvbnM6IFJlcXVlc3RJbml0O1xuICB1c2VNYXNrPzogYm9vbGVhbjtcbiAgcmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzPzogbnVtYmVyW10gfCBudWxsO1xufSk6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YT4ge1xuICBjb25zdCByZXF1ZXN0VXJsID0gdXJsO1xuICBjb25zdCByZXF1ZXN0UGFyYW1zID0gcGFyYW1zID8/IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBvcHRpb25zO1xuXG4gIGNvbnN0IGFzc2V0VXJsID0gcmVxdWVzdFBhcmFtcyA/IGAke3JlcXVlc3RVcmx9PyR7cmVxdWVzdFBhcmFtcy50b1N0cmluZygpfWAgOiByZXF1ZXN0VXJsO1xuICByZXR1cm4ge3VybDogYXNzZXRVcmwsIG9wdGlvbnM6IHJlcXVlc3RPcHRpb25zLCB1c2VNYXNrLCByZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXN9O1xufVxuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb24gZm9yIGZvcm1pbmcgYSByZXF1ZXN0IGZvciBhIHNpbmdsZSBhc3NldCBTVEFDIG1ldGFkYXRhIG9iamVjdC5cbiAqL1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHksIG1heC1zdGF0ZW1lbnRzXG5hc3luYyBmdW5jdGlvbiBnZXRTaW5nbGVBc3NldFNUQUNSZXF1ZXN0KFxuICBvcHRpb25zOiBHZXRUaWxlRGF0YVByb3BzXG4pOiBQcm9taXNlPEFzc2V0UmVxdWVzdERhdGEgfCBudWxsPiB7XG4gIGNvbnN0IHtcbiAgICBzdGFjLFxuICAgIGxvYWRBc3NldElkcyxcbiAgICBsb2FkQmFuZEluZGV4ZXMsXG4gICAgc2lnbmFsLFxuICAgIHVzZVNUQUNTZWFyY2hpbmcsXG4gICAgaW5kZXg6IHt4LCB5LCB6fSxcbiAgICBzdGFjU2VhcmNoUHJvdmlkZXIsXG4gICAgc3RhcnREYXRlLFxuICAgIGVuZERhdGUsXG4gICAgX3N0YWNRdWVyeVxuICB9ID0gb3B0aW9ucztcblxuICBjb25zdCB1c2VNYXNrID0gdHJ1ZTtcblxuICAvLyBPbmx5IGEgc2luZ2xlIFVSTCBiZWNhdXNlIG9ubHkgYSBzaW5nbGUgYXNzZXRcbiAgbGV0IHVybDogc3RyaW5nIHwgbnVsbCA9IG51bGw7XG4gIGxldCB1cmxQYXJhbXM6IFVSTFNlYXJjaFBhcmFtcyB8IG51bGwgPSBuZXcgVVJMU2VhcmNoUGFyYW1zKCk7XG4gIGxldCByZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXM6IG51bWJlcltdIHwgbnVsbCA9IGxvYWRCYW5kSW5kZXhlcztcbiAgaWYgKHVzZVNUQUNTZWFyY2hpbmcgJiYgc3RhYy50eXBlICE9PSAnRmVhdHVyZScpIHtcbiAgICB1cmxQYXJhbXMgPSBnZXRTdGFjQXBpVXJsUGFyYW1zKHtcbiAgICAgIHN0YWMsXG4gICAgICBzdGFjU2VhcmNoUHJvdmlkZXIsXG4gICAgICBzdGFydERhdGUsXG4gICAgICBlbmREYXRlLFxuICAgICAgbWFzazogdXNlTWFzayxcbiAgICAgIGxvYWRBc3NldElkcyxcbiAgICAgIF9zdGFjUXVlcnlcbiAgICB9KTtcbiAgICB1cmwgPSBnZXRUaXRpbGVyVXJsKHtzdGFjLCB1c2VTVEFDU2VhcmNoaW5nLCB4LCB5LCB6fSk7XG4gIH0gZWxzZSBpZiAoc3RhYy50eXBlID09PSAnRmVhdHVyZScpIHtcbiAgICAvLyBzdGFjIGlzIGFuIEl0ZW1cbiAgICB1cmxQYXJhbXMgPSBnZXRTaW5nbGVDT0dVcmxQYXJhbXMoe1xuICAgICAgc3RhYyxcbiAgICAgIGxvYWRBc3NldElkOiBsb2FkQXNzZXRJZHNbMF0sXG4gICAgICBsb2FkQmFuZEluZGV4ZXMsXG4gICAgICBtYXNrOiB1c2VNYXNrXG4gICAgfSk7XG4gICAgdXJsID0gZ2V0VGl0aWxlclVybCh7c3RhYywgdXNlU1RBQ1NlYXJjaGluZywgeCwgeSwgen0pO1xuICAgIHJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlcyA9IG51bGw7XG4gIH0gZWxzZSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBpZiAoIXVybCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV0dXJuIGF3YWl0IGdldEFzc2V0UmVxdWVzdCh7XG4gICAgdXJsLFxuICAgIHBhcmFtczogdXJsUGFyYW1zLFxuICAgIG9wdGlvbnM6IHtzaWduYWx9LFxuICAgIHVzZU1hc2ssXG4gICAgcmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzXG4gIH0pO1xufVxuXG4vKipcbiAqIFV0aWxpdHkgZnVuY3Rpb24gZm9yIGZvcm1pbmcgcmVxdWVzdHMgZm9yIGEgbXVsdGkgYXNzZXQgU1RBQyBtZXRhZGF0YSBvYmplY3QuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGdldE11bHRpQXNzZXRTVEFDUmVxdWVzdChcbiAgb3B0aW9uczogR2V0VGlsZURhdGFQcm9wc1xuKTogUHJvbWlzZTxBc3NldFJlcXVlc3REYXRhW10gfCBudWxsPiB7XG4gIGNvbnN0IHtcbiAgICBzdGFjLFxuICAgIGxvYWRBc3NldElkcyxcbiAgICBsb2FkQmFuZEluZGV4ZXMsXG4gICAgc2lnbmFsLFxuICAgIHVzZVNUQUNTZWFyY2hpbmcsXG4gICAgc3RhY1NlYXJjaFByb3ZpZGVyLFxuICAgIHN0YXJ0RGF0ZSxcbiAgICBlbmREYXRlLFxuICAgIGluZGV4OiB7eCwgeSwgen0sXG4gICAgX3N0YWNRdWVyeVxuICB9ID0gb3B0aW9ucztcblxuICAvLyBNdWx0aXBsZSB1cmxzLCBvbmUgZm9yIGVhY2ggYXNzZXRcbiAgbGV0IHJlcXVlc3REYXRhOiB7dXJsOiBzdHJpbmc7IHBhcmFtczogVVJMU2VhcmNoUGFyYW1zIHwgbnVsbDsgb3B0aW9ucz86IFJlcXVlc3RJbml0fVtdID0gW107XG5cbiAgLy8gV2UgYXNzdW1lIHRoYXQgdGhlcmUncyBvbmx5IG9uZSB2YWxpZGl0eSBtYXNrIGZvciB0aGUgZW50aXJlIGFzc2V0LCBhbmQgdGhlcmVmb3JlIGFueSBvZiB0aGVcbiAgLy8gcmVxdWVzdHMgd291bGQgcmV0dXJuIHRoZSBzYW1lIHZhbGlkaXR5IGJpdG1hcC4gVGhlcmVmb3JlIHdlIG9ubHkgcmVxdWVzdCBhIG1hc2sgZm9yIHRoZSBmaXJzdFxuICAvLyBhc3NldFxuICBjb25zdCByZXF1ZXN0TWFzazogYm9vbGVhbltdID0gbmV3IEFycmF5KGxvYWRBc3NldElkcy5sZW5ndGgpLmZpbGwoZmFsc2UpO1xuICByZXF1ZXN0TWFza1swXSA9IHRydWU7XG5cbiAgaWYgKHVzZVNUQUNTZWFyY2hpbmcgJiYgc3RhYy50eXBlICE9PSAnRmVhdHVyZScpIHtcbiAgICByZXF1ZXN0RGF0YSA9IHppcChsb2FkQXNzZXRJZHMsIHJlcXVlc3RNYXNrKS5tYXAoKFthc3NldElkLCBtYXNrXSkgPT4ge1xuICAgICAgY29uc3QgcGFyYW1zID0gZ2V0U3RhY0FwaVVybFBhcmFtcyh7XG4gICAgICAgIHN0YWMsXG4gICAgICAgIHN0YWNTZWFyY2hQcm92aWRlcixcbiAgICAgICAgc3RhcnREYXRlLFxuICAgICAgICBlbmREYXRlLFxuICAgICAgICBtYXNrLFxuICAgICAgICBsb2FkQXNzZXRJZHM6IFthc3NldElkXSxcbiAgICAgICAgX3N0YWNRdWVyeVxuICAgICAgfSk7XG4gICAgICBjb25zdCB1cmwgPSBnZXRUaXRpbGVyVXJsKHtzdGFjLCB1c2VTVEFDU2VhcmNoaW5nLCB4LCB5LCB6fSk7XG4gICAgICByZXR1cm4ge3VybCwgcGFyYW1zfTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmIChzdGFjLnR5cGUgPT09ICdGZWF0dXJlJykge1xuICAgIHJlcXVlc3REYXRhID0gemlwMyhsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVxdWVzdE1hc2spLm1hcChcbiAgICAgIChbYXNzZXRJZCwgYmFuZEluZGV4LCBtYXNrXSkgPT4ge1xuICAgICAgICBjb25zdCBwYXJhbXMgPSBnZXRTaW5nbGVDT0dVcmxQYXJhbXMoe1xuICAgICAgICAgIHN0YWMsXG4gICAgICAgICAgbG9hZEFzc2V0SWQ6IGFzc2V0SWQsXG4gICAgICAgICAgbG9hZEJhbmRJbmRleGVzOiBbYmFuZEluZGV4XSxcbiAgICAgICAgICBtYXNrXG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB1cmwgPSBnZXRUaXRpbGVyVXJsKHtzdGFjLCB1c2VTVEFDU2VhcmNoaW5nLCB4LCB5LCB6fSk7XG4gICAgICAgIHJldHVybiB7dXJsLCBwYXJhbXN9O1xuICAgICAgfVxuICAgICk7XG4gIH1cblxuICBpZiAoaXNWYWxpZFJlcXVlc3REYXRhKHJlcXVlc3REYXRhKSkge1xuICAgIHJldHVybiBhd2FpdCBQcm9taXNlLmFsbChcbiAgICAgIHJlcXVlc3REYXRhLm1hcChyZXF1ZXN0ID0+XG4gICAgICAgIGdldEFzc2V0UmVxdWVzdCh7Li4ucmVxdWVzdCwgb3B0aW9uczogcmVxdWVzdC5vcHRpb25zID8/IHtzaWduYWx9fSlcbiAgICAgIClcbiAgICApO1xuICB9XG5cbiAgcmV0dXJuIG51bGw7XG59XG5cbi8qKlxuICogTG9hZCBpbWFnZSBkYXRhIHdoZW4gY29uc29saWRhdGVkIGluIGJhbmRzIHdpdGhpbiBhIHNpbmdsZSBTVEFDIEFzc2V0XG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGxvYWRTaW5nbGVBc3NldFNUQUMocmVxdWVzdDogQXNzZXRSZXF1ZXN0RGF0YSk6IFByb21pc2U8SW1hZ2VEYXRhPiB7XG4gIGNvbnN0IGltYWdlQmFuZHMgPSBhd2FpdCBsb2FkTnB5QXJyYXkocmVxdWVzdCwgdHJ1ZSk7XG5cbiAgaWYgKCFpbWFnZUJhbmRzKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIGltYWdlQmFuZHNcbiAgICB9O1xuICB9XG5cbiAgY29uc3QgaW1hZ2VNYXNrID0gKHJlcXVlc3QudXNlTWFzayAmJiBpbWFnZUJhbmRzLnBvcCgpKSB8fCBudWxsO1xuICBsZXQgbWFwcGVkSW1hZ2VCYW5kcyA9IGltYWdlQmFuZHM7XG4gIGlmIChyZXF1ZXN0LnJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlcykge1xuICAgIG1hcHBlZEltYWdlQmFuZHMgPSByZXF1ZXN0LnJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlcy5tYXAoaSA9PiBpbWFnZUJhbmRzW2ldKTtcbiAgfVxuICByZXR1cm4ge2ltYWdlQmFuZHM6IG1hcHBlZEltYWdlQmFuZHMsIGltYWdlTWFza307XG59XG5cbi8qKlxuICogTG9hZCBpbWFnZSBkYXRhIHdoZW4gc3BsaXQgYW1vbmcgbXVsdGlwbGUgU1RBQyBBc3NldHNcbiAqL1xuYXN5bmMgZnVuY3Rpb24gbG9hZE11bHRpQXNzZXRTVEFDKHJlcXVlc3RzOiBBc3NldFJlcXVlc3REYXRhW10pOiBQcm9taXNlPEltYWdlRGF0YT4ge1xuICBjb25zdCByZXN1bHRzID0gYXdhaXQgUHJvbWlzZS5hbGwocmVxdWVzdHMubWFwKHJlcXVlc3QgPT4gbG9hZE5weUFycmF5KHJlcXVlc3QsIHRydWUpKSk7XG4gIC8vIFRoZSBmaXJzdCByZXF1ZXN0IGluY2x1ZGVzIGEgbWFza1xuICBjb25zdCBpbWFnZU1hc2sgPSByZXN1bHRzWzBdPy5wb3AoKSB8fCBudWxsO1xuICBjb25zdCBpbWFnZUJhbmRzID0gcmVzdWx0cy5mbGF0KCkuZmlsdGVyKGJhbmQgPT4gYmFuZCAhPT0gbnVsbCkgYXMgSW1hZ2VEYXRhWydpbWFnZUJhbmRzJ107XG4gIHJldHVybiB7aW1hZ2VCYW5kcywgaW1hZ2VNYXNrfTtcbn1cblxuLyoqXG4gKiBJdGVyYXRlIG92ZXIgdHdvIGFycmF5cyBzaW11bHRhbmVvdXNseSwgc2ltaWxhciB0byBQeXRob24ncyB6aXAgYnVpbHRpblxuICovXG5mdW5jdGlvbiB6aXA8VDEsIFQyPihhOiBUMVtdLCBiOiBUMltdKTogW1QxLCBUMl1bXSB7XG4gIHJldHVybiBhLm1hcCgoaywgaSkgPT4gW2ssIGJbaV1dKTtcbn1cblxuLyoqXG4gKiBJdGVyYXRlIG92ZXIgdGhyZWUgYXJyYXlzIHNpbXVsdGFuZW91c2x5LCBzaW1pbGFyIHRvIFB5dGhvbidzIHppcCBidWlsdGluXG4gKi9cbmZ1bmN0aW9uIHppcDM8VDEsIFQyLCBUMz4oYTogVDFbXSwgYjogVDJbXSwgYzogVDNbXSk6IFtUMSwgVDIsIFQzXVtdIHtcbiAgcmV0dXJuIGEubWFwKChrLCBpKSA9PiBbaywgYltpXSwgY1tpXV0pO1xufVxuXG4vKipcbiAqIFR5cGUgZ3VhcmQgdG8gY2hlY2sgaWYgYWxsIGFycmF5IGVsZW1lbnRzIGFyZSBzdHJpbmdzXG4gKi9cbmZ1bmN0aW9uIGlzVmFsaWRSZXF1ZXN0RGF0YShcbiAgYXJyOiB7dXJsOiB1bmtub3duOyBwYXJhbXM6IHVua25vd247IG9wdGlvbnM/OiB1bmtub3dufVtdXG4pOiBhcnIgaXMge3VybDogc3RyaW5nOyBwYXJhbXM6IFVSTFNlYXJjaFBhcmFtczsgb3B0aW9uczogdW5rbm93bn1bXSB7XG4gIHJldHVybiBhcnIuZXZlcnkoeCA9PiB0eXBlb2YgeD8udXJsID09PSAnc3RyaW5nJyAmJiB4Py5wYXJhbXMgIT09IG51bGwpO1xufVxuXG4vKipcbiAqIENyZWF0ZSBiYXNlNjQgaW1hZ2UgZGF0YSB1cmwgZm9yIGNhdGVnb3JpY2FsIGNvbG9ybWFwXG4gKiBAcGFyYW0gY2F0ZWdvcmljYWxPcHRpb25zIC0gY29sb3IgbWFwIGNvbmZpZ3VyYXRpb24gYW5kIG1pbi1tYXggdmFsdWVzIG9mIGNhdGVnb3JpY2FsIGJhbmRcbiAqIEByZXR1cm5zIGJhc2U2NCBpbWFnZSBkYXRhIHVybFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2F0ZWdvcmljYWxDb2xvcm1hcERhdGFVcmwoXG4gIGNhdGVnb3JpY2FsT3B0aW9uczogQ2F0ZWdvcmljYWxDb2xvcm1hcE9wdGlvbnNcbik6IHN0cmluZyB8IG51bGwge1xuICBjb25zdCBiaXRtYXAgPSBnZW5lcmF0ZUNhdGVnb3JpY2FsQml0bWFwQXJyYXkoY2F0ZWdvcmljYWxPcHRpb25zKTtcbiAgaWYgKCFiaXRtYXApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICByZXR1cm4gZ2V0SW1hZ2VEYXRhVVJMKGJpdG1hcCwgQ0FURUdPUklDQUxfVEVYVFVSRV9XSURUSCwgMSk7XG59XG5cbi8qKlxuICogTG9hZCB0ZXJyYWluIG1lc2ggZnJvbSByYXN0ZXIgdGlsZSBzZXJ2ZXJcbiAqIEBwYXJhbSBwcm9wcyAtIHByb3BlcnRpZXMgdG8gbG9hZCB0ZXJyYWluIGRhdGFcbiAqIEByZXR1cm5zIHRlcnJhaW4gbWVzaCBkYXRhXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkVGVycmFpbihwcm9wczoge1xuICBpbmRleDoge3g6IG51bWJlcjsgeTogbnVtYmVyOyB6OiBudW1iZXJ9O1xuICBzaWduYWw6IEFib3J0U2lnbmFsO1xuICByYXN0ZXJUaWxlU2VydmVyVXJsczogc3RyaW5nW107XG4gIGJvdW5kc0Zvckdlb21ldHJ5PzogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl07XG59KTogUHJvbWlzZTxUZXJyYWluRGF0YT4ge1xuICBjb25zdCB7XG4gICAgaW5kZXg6IHt4LCB5LCB6fSxcbiAgICBib3VuZHNGb3JHZW9tZXRyeSxcbiAgICBzaWduYWwsXG4gICAgcmFzdGVyVGlsZVNlcnZlclVybHNcbiAgfSA9IHByb3BzO1xuXG4gIGNvbnN0IG1lc2hNYXhFcnJvciA9IGdldE1lc2hNYXhFcnJvcih6LCBNRVNIX01VTFRJUExJRVIpO1xuICBjb25zdCB0ZXJyYWluVXJsID0gZ2V0VGVycmFpblVybChyYXN0ZXJUaWxlU2VydmVyVXJscywgeCwgeSwgeiwgbWVzaE1heEVycm9yKTtcbiAgY29uc3QgbG9hZGVyT3B0aW9ucyA9IGdldExvYWRlck9wdGlvbnMoKTtcblxuICByZXR1cm4gbG9hZCh0ZXJyYWluVXJsLCBRdWFudGl6ZWRNZXNoTG9hZGVyLCB7XG4gICAgZmV0Y2g6IHtzaWduYWx9LFxuICAgICdxdWFudGl6ZWQtbWVzaCc6IHtcbiAgICAgIC4uLmxvYWRlck9wdGlvbnNbJ3F1YW50aXplZC1tZXNoJ10sXG4gICAgICBib3VuZHM6IGJvdW5kc0Zvckdlb21ldHJ5LFxuICAgICAgc2tpcnRIZWlnaHQ6IG1lc2hNYXhFcnJvciAqIDJcbiAgICB9XG4gIH0pIGFzIFByb21pc2U8VGVycmFpbkRhdGE+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