kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
648 lines (639 loc) • 76 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 _commonUtils = require("@kepler.gl/common-utils");
var _constants = require("@kepler.gl/constants");
var _utils = require("@kepler.gl/utils");
var _config = require("./config");
var _gpuUtils = require("./gpu-utils");
var _rasterTileUtils = require("./raster-tile-utils");
var _url = require("./url");
var _requestThrottle = require("./request-throttle");
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;
/**
* Multiplier used to calculate the height of terrain mesh skirts.
* The skirt is an extension of the terrain mesh that helps prevent gaps between terrain tiles
* by extending the mesh edges downward. This multiplier is applied to the mesh's maximum error
* to determine the skirt height, ensuring no gaps are visible.
*/
var SKIRT_HEIGHT_MULTIPLIER = 3;
/**
* 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),
rasterServerUrl: '',
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, rasterServerUrl, 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, rasterServerUrl = _ref.rasterServerUrl, 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,
rasterServerUrl: rasterServerUrl,
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, urlInfo, 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
urlInfo = 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
});
urlInfo = (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
});
urlInfo = (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 (urlInfo.url) {
_context6.next = 19;
break;
}
return _context6.abrupt("return", null);
case 19:
_context6.next = 21;
return getAssetRequest(_objectSpread(_objectSpread({}, urlInfo), {}, {
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 urlInfo = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
return _objectSpread(_objectSpread({}, urlInfo), {}, {
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 urlInfo = (0, _url.getTitilerUrl)({
stac: stac,
useSTACSearching: useSTACSearching,
x: x,
y: y,
z: z
});
return _objectSpread(_objectSpread({}, urlInfo), {}, {
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 _callee11(props) {
var _props$index, x, y, z, boundsForGeometry, signal, rasterTileServerUrls, meshMaxError, terrainUrlInfo, loaderOptions, numAttempts, mesh;
return _regenerator["default"].wrap(function _callee11$(_context11) {
while (1) switch (_context11.prev = _context11.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);
terrainUrlInfo = (0, _url.getTerrainUrl)(rasterTileServerUrls, x, y, z, meshMaxError);
loaderOptions = (0, _constants.getLoaderOptions)();
numAttempts = 1 + (0, _utils.getApplicationConfig)().rasterServerMaxRetries;
_context11.next = 7;
return (0, _requestThrottle.getRequestThrottle)().throttleRequest(terrainUrlInfo.rasterServerUrl, /*#__PURE__*/(0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee10() {
var attempt, _getApplicationConfig, _error$response;
return _regenerator["default"].wrap(function _callee10$(_context10) {
while (1) switch (_context10.prev = _context10.next) {
case 0:
attempt = 0;
case 1:
if (!(attempt < numAttempts)) {
_context10.next = 17;
break;
}
_context10.prev = 2;
_context10.next = 5;
return (0, _core.load)(terrainUrlInfo.url, _terrain.QuantizedMeshLoader, {
fetch: {
signal: signal
},
'quantized-mesh': _objectSpread(_objectSpread({}, loaderOptions['quantized-mesh']), {}, {
bounds: boundsForGeometry,
skirtHeight: meshMaxError * SKIRT_HEIGHT_MULTIPLIER
})
});
case 5:
return _context10.abrupt("return", _context10.sent);
case 8:
_context10.prev = 8;
_context10.t0 = _context10["catch"](2);
if (!(attempt < numAttempts && _context10.t0 instanceof _core.FetchError && (_getApplicationConfig = (0, _utils.getApplicationConfig)().rasterServerServerErrorsToRetry) !== null && _getApplicationConfig !== void 0 && _getApplicationConfig.includes((_error$response = _context10.t0.response) === null || _error$response === void 0 ? void 0 : _error$response.status))) {
_context10.next = 14;
break;
}
_context10.next = 13;
return (0, _commonUtils.sleep)((0, _utils.getApplicationConfig)().rasterServerRetryDelay);
case 13:
return _context10.abrupt("continue", 14);
case 14:
attempt++;
_context10.next = 1;
break;
case 17:
return _context10.abrupt("return", null);
case 18:
case "end":
return _context10.stop();
}
}, _callee10, null, [[2, 8]]);
})));
case 7:
mesh = _context11.sent;
return _context11.abrupt("return", mesh);
case 9:
case "end":
return _context11.stop();
}
}, _callee11);
}));
return _loadTerrain.apply(this, arguments);
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29yZSIsInJlcXVpcmUiLCJfdGVycmFpbiIsIl9tZW1vaXplIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9jb21tb25VdGlscyIsIl9jb25zdGFudHMiLCJfdXRpbHMiLCJfY29uZmlnIiwiX2dwdVV0aWxzIiwiX3Jhc3RlclRpbGVVdGlscyIsIl91cmwiLCJfcmVxdWVzdFRocm90dGxlIiwib3duS2V5cyIsImUiLCJyIiwidCIsIk9iamVjdCIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJnZXRJbWFnZURhdGFVUkwiLCJiaXRtYXAiLCJ3aWR0aCIsImhlaWdodCIsImNhbnZhcyIsImRvY3VtZW50IiwiY3JlYXRlRWxlbWVudCIsImdldENvbnRleHQiLCJzZXRBdHRyaWJ1dGUiLCJjb25jYXQiLCJjdHgiLCJpIiwiX2JpdG1hcCRzdWJhcnJheSIsInN1YmFycmF5IiwiX2JpdG1hcCRzdWJhcnJheTIiLCJfc2xpY2VkVG9BcnJheTIiLCJnIiwiYiIsImEiLCJmaWxsU3R5bGUiLCJmaWxsUmVjdCIsInRvRGF0YVVSTCIsIk1FU0hfTVVMVElQTElFUiIsIlNLSVJUX0hFSUdIVF9NVUxUSVBMSUVSIiwibG9hZEltYWdlcyIsIl94IiwiX3gyIiwiX3gzIiwiX2xvYWRJbWFnZXMiLCJfYXN5bmNUb0dlbmVyYXRvcjIiLCJfcmVnZW5lcmF0b3IiLCJtYXJrIiwiX2NhbGxlZSIsImFzc2V0UmVxdWVzdHMiLCJjb2xvcm1hcElkIiwiY2F0ZWdvcmljYWxPcHRpb25zIiwiX3lpZWxkJFByb21pc2UkYWxsIiwiX3lpZWxkJFByb21pc2UkYWxsMiIsInN0YWNJbWFnZXMiLCJjb2xvcm1hcCIsImltYWdlQ29sb3JtYXAiLCJ3cmFwIiwiX2NhbGxlZSQiLCJfY29udGV4dCIsInByZXYiLCJuZXh0IiwiUHJvbWlzZSIsImFsbCIsImxvYWRTVEFDSW1hZ2VEYXRhIiwiQ0FURUdPUklDQUxfQ09MT1JNQVBfSUQiLCJtZW1vaXplZExvYWRDb2xvcm1hcCIsInNlbnQiLCJnZW5lcmF0ZUNhdGVnb3JpY2FsQ29sb3JtYXBUZXh0dXJlIiwiYWJydXB0Iiwic3RvcCIsIm1lbW9pemUiLCJsb2FkQ29sb3JtYXAiLCJfeDQiLCJfbG9hZENvbG9ybWFwIiwiX2NhbGxlZTIiLCJyZXF1ZXN0IiwiX2NhbGxlZTIkIiwiX2NvbnRleHQyIiwiZ2V0QXNzZXRSZXF1ZXN0IiwidXJsIiwiUmFzdGVyTGF5ZXJSZXNvdXJjZXMiLCJyYXN0ZXJDb2xvck1hcCIsInJhc3RlclNlcnZlclVybCIsIm9wdGlvbnMiLCJsb2FkSW1hZ2UiLCJDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlMiLCJnZXRTVEFDSW1hZ2VSZXF1ZXN0cyIsIl94NSIsIl9nZXRTVEFDSW1hZ2VSZXF1ZXN0cyIsIl9jYWxsZWUzIiwibG9hZEFzc2V0SWRzIiwiX2NhbGxlZTMkIiwiX2NvbnRleHQzIiwiZ2V0U2luZ2xlQXNzZXRTVEFDUmVxdWVzdCIsImdldE11bHRpQXNzZXRTVEFDUmVxdWVzdCIsIl94NiIsIl9sb2FkU1RBQ0ltYWdlRGF0YSIsIl9jYWxsZWU0IiwicmVxdWVzdHMiLCJfY2FsbGVlNCQiLCJfY29udGV4dDQiLCJsb2FkU2luZ2xlQXNzZXRTVEFDIiwibG9hZE11bHRpQXNzZXRTVEFDIiwiX3g3IiwiX2dldEFzc2V0UmVxdWVzdCIsIl9jYWxsZWU1IiwiX3JlZiIsInBhcmFtcyIsIl9yZWYkdXNlTWFzayIsInVzZU1hc2siLCJyZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXMiLCJyZXF1ZXN0VXJsIiwicmVxdWVzdFBhcmFtcyIsInJlcXVlc3RPcHRpb25zIiwiYXNzZXRVcmwiLCJfY2FsbGVlNSQiLCJfY29udGV4dDUiLCJVUkxTZWFyY2hQYXJhbXMiLCJ0b1N0cmluZyIsIl94OCIsIl9nZXRTaW5nbGVBc3NldFNUQUNSZXF1ZXN0IiwiX2NhbGxlZTYiLCJzdGFjIiwibG9hZEJhbmRJbmRleGVzIiwic2lnbmFsIiwidXNlU1RBQ1NlYXJjaGluZyIsIl9vcHRpb25zJGluZGV4IiwieCIsInkiLCJ6Iiwic3RhY1NlYXJjaFByb3ZpZGVyIiwic3RhcnREYXRlIiwiZW5kRGF0ZSIsIl9zdGFjUXVlcnkiLCJ1cmxJbmZvIiwidXJsUGFyYW1zIiwiX2NhbGxlZTYkIiwiX2NvbnRleHQ2IiwiaW5kZXgiLCJ0eXBlIiwiZ2V0U3RhY0FwaVVybFBhcmFtcyIsIm1hc2siLCJnZXRUaXRpbGVyVXJsIiwiZ2V0U2luZ2xlQ09HVXJsUGFyYW1zIiwibG9hZEFzc2V0SWQiLCJfeDkiLCJfZ2V0TXVsdGlBc3NldFNUQUNSZXF1ZXN0IiwiX2NhbGxlZTciLCJfb3B0aW9ucyRpbmRleDIiLCJyZXF1ZXN0RGF0YSIsInJlcXVlc3RNYXNrIiwiX2NhbGxlZTckIiwiX2NvbnRleHQ3IiwiQXJyYXkiLCJmaWxsIiwiemlwIiwibWFwIiwiX3JlZjIiLCJfcmVmMyIsImFzc2V0SWQiLCJ6aXAzIiwiX3JlZjQiLCJfcmVmNSIsImJhbmRJbmRleCIsImlzVmFsaWRSZXF1ZXN0RGF0YSIsIl9yZXF1ZXN0JG9wdGlvbnMiLCJfeDEwIiwiX2xvYWRTaW5nbGVBc3NldFNUQUMiLCJfY2FsbGVlOCIsImltYWdlQmFuZHMiLCJpbWFnZU1hc2siLCJtYXBwZWRJbWFnZUJhbmRzIiwiX2NhbGxlZTgkIiwiX2NvbnRleHQ4IiwibG9hZE5weUFycmF5IiwicG9wIiwiX3gxMSIsIl9sb2FkTXVsdGlBc3NldFNUQUMiLCJfY2FsbGVlOSIsIl9yZXN1bHRzJCIsInJlc3VsdHMiLCJfY2FsbGVlOSQiLCJfY29udGV4dDkiLCJmbGF0IiwiYmFuZCIsImsiLCJjIiwiYXJyIiwiZXZlcnkiLCJnZXRDYXRlZ29yaWNhbENvbG9ybWFwRGF0YVVybCIsImdlbmVyYXRlQ2F0ZWdvcmljYWxCaXRtYXBBcnJheSIsIkNBVEVHT1JJQ0FMX1RFWFRVUkVfV0lEVEgiLCJsb2FkVGVycmFpbiIsIl94MTIiLCJfbG9hZFRlcnJhaW4iLCJfY2FsbGVlMTEiLCJwcm9wcyIsIl9wcm9wcyRpbmRleCIsImJvdW5kc0Zvckdlb21ldHJ5IiwicmFzdGVyVGlsZVNlcnZlclVybHMiLCJtZXNoTWF4RXJyb3IiLCJ0ZXJyYWluVXJsSW5mbyIsImxvYWRlck9wdGlvbnMiLCJudW1BdHRlbXB0cyIsIm1lc2giLCJfY2FsbGVlMTEkIiwiX2NvbnRleHQxMSIsImdldE1lc2hNYXhFcnJvciIsImdldFRlcnJhaW5VcmwiLCJnZXRMb2FkZXJPcHRpb25zIiwiZ2V0QXBwbGljYXRpb25Db25maWciLCJyYXN0ZXJTZXJ2ZXJNYXhSZXRyaWVzIiwiZ2V0UmVxdWVzdFRocm90dGxlIiwidGhyb3R0bGVSZXF1ZXN0IiwiX2NhbGxlZTEwIiwiYXR0ZW1wdCIsIl9nZXRBcHBsaWNhdGlvbkNvbmZpZyIsIl9lcnJvciRyZXNwb25zZSIsIl9jYWxsZWUxMCQiLCJfY29udGV4dDEwIiwibG9hZCIsIlF1YW50aXplZE1lc2hMb2FkZXIiLCJmZXRjaCIsImJvdW5kcyIsInNraXJ0SGVpZ2h0IiwidDAiLCJGZXRjaEVycm9yIiwicmFzdGVyU2VydmVyU2VydmVyRXJyb3JzVG9SZXRyeSIsImluY2x1ZGVzIiwicmVzcG9uc2UiLCJzdGF0dXMiLCJzbGVlcCIsInJhc3RlclNlcnZlclJldHJ5RGVsYXkiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvcmFzdGVyLXRpbGUvaW1hZ2UudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyB0byBjcmVhdGUgb2JqZWN0cyB0byBwYXNzIHRvIGRlY2suZ2wtcmFzdGVyXG4gKi9cblxuaW1wb3J0IHtsb2FkLCBGZXRjaEVycm9yfSBmcm9tICdAbG9hZGVycy5nbC9jb3JlJztcbmltcG9ydCB7UXVhbnRpemVkTWVzaExvYWRlcn0gZnJvbSAnQGxvYWRlcnMuZ2wvdGVycmFpbic7XG5pbXBvcnQgbWVtb2l6ZSBmcm9tICdsb2Rhc2gvbWVtb2l6ZSc7XG5cbmltcG9ydCB7c2xlZXB9IGZyb20gJ0BrZXBsZXIuZ2wvY29tbW9uLXV0aWxzJztcbmltcG9ydCB7Z2V0TG9hZGVyT3B0aW9uc30gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuaW1wb3J0IHtnZXRBcHBsaWNhdGlvbkNvbmZpZ30gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5cbmltcG9ydCB7Q0FURUdPUklDQUxfQ09MT1JNQVBfSUR9IGZyb20gJy4vY29uZmlnJztcbmltcG9ydCB7XG4gIGxvYWROcHlBcnJheSxcbiAgZ2VuZXJhdGVDYXRlZ29yaWNhbENvbG9ybWFwVGV4dHVyZSxcbiAgbG9hZEltYWdlLFxuICBDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlNcbn0gZnJvbSAnLi9ncHUtdXRpbHMnO1xuaW1wb3J0IHtDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRILCBnZW5lcmF0ZUNhdGVnb3JpY2FsQml0bWFwQXJyYXl9IGZyb20gJy4vcmFzdGVyLXRpbGUtdXRpbHMnO1xuaW1wb3J0IHtcbiAgR2V0VGlsZURhdGFQcm9wcyxcbiAgSW1hZ2VEYXRhLFxuICBUZXJyYWluRGF0YSxcbiAgQXNzZXRSZXF1ZXN0RGF0YSxcbiAgQ2F0ZWdvcmljYWxDb2xvcm1hcE9wdGlvbnMsXG4gIENvbG9ybWFwSW1hZ2VEYXRhXG59IGZyb20gJy4vdHlwZXMnO1xuaW1wb3J0IHtcbiAgZ2V0VGl0aWxlclVybCxcbiAgZ2V0VGVycmFpblVybCxcbiAgZ2V0U2luZ2xlQ09HVXJsUGFyYW1zLFxuICBnZXRTdGFjQXBpVXJsUGFyYW1zLFxuICBnZXRNZXNoTWF4RXJyb3IsXG4gIFJhc3RlckxheWVyUmVzb3VyY2VzXG59IGZyb20gJy4vdXJsJztcbmltcG9ydCB7Z2V0UmVxdWVzdFRocm90dGxlfSBmcm9tICcuL3JlcXVlc3QtdGhyb3R0bGUnO1xuXG4vKipcbiAqIENyZWF0ZSBkYXRhVXJsIGltYWdlIGZyb20gYml0bWFwIGFycmF5XG4gKiBAcGFyYW0gYml0bWFwIC0gUkdCQSBieXRlIGJpdG1hcCBhcnJheVxuICogQHBhcmFtIHdpZHRoIC0gd2lkdGggb2YgdGhlIGltYWdlXG4gKiBAcGFyYW0gaGVpZ2h0IC0gaGVpZ2h0IG9mIHRoZSBpbWFnZVxuICogQHJldHVybnMgYmFzZTY0IGRhdGEgdXJsIG9mIHRoZSBpbWFnZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0SW1hZ2VEYXRhVVJMKGJpdG1hcDogVWludDhBcnJheSwgd2lkdGg6IG51bWJlciwgaGVpZ2h0OiBudW1iZXIpOiBzdHJpbmcgfCBudWxsIHtcbiAgY29uc3QgY2FudmFzID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnY2FudmFzJyk7XG4gIGlmICghY2FudmFzLmdldENvbnRleHQpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjYW52YXMuc2V0QXR0cmlidXRlKCd3aWR0aCcsIGAke3dpZHRofWApO1xuICBjYW52YXMuc2V0QXR0cmlidXRlKCdoZWlnaHQnLCBgJHtoZWlnaHR9YCk7XG4gIGNvbnN0IGN0eCA9IGNhbnZhcy5nZXRDb250ZXh0KCcyZCcpO1xuICBpZiAoIWN0eCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGZvciAobGV0IGkgPSAwOyBpIDwgd2lkdGg7IGkrKykge1xuICAgIGNvbnN0IFtyLCBnLCBiLCBhXSA9IGJpdG1hcC5zdWJhcnJheShpICogNCwgaSAqIDQgKyA0KTtcbiAgICBpZiAoYSAhPT0gMCkge1xuICAgICAgY3R4LmZpbGxTdHlsZSA9IGByZ2IoJHtyfSwgJHtnfSwgJHtifSlgO1xuICAgICAgY3R4LmZpbGxSZWN0KGksIDAsIDEsIDEpO1xuICAgIH1cbiAgfVxuICByZXR1cm4gY2FudmFzLnRvRGF0YVVSTCgnaW1hZ2UvcG5nJyk7XG59XG5cbi8vIEdsb2JhbCBtdWx0aXBsaWVyIHRvIGJlIGFwcGxpZWQgdG8gdGVycmFpbiBtZXNoIHJlc29sdXRpb25cbi8vIEEgc21hbGxlciBudW1iZXIgdG93YXJkcyAwIHdpbGwgY2F1c2UgZ2VuZXJhdGlvbiBvZiBtZXNoIGRhdGEgd2l0aCBhIHNtYWxsZXIgbWF4IGVycm9yIGFuZCB0aHVzIGFcbi8vIGhpZ2hlciByZXNvbHV0aW9uIG1lc2hcbmNvbnN0IE1FU0hfTVVMVElQTElFUiA9IDAuODtcblxuLyoqXG4gKiBNdWx0aXBsaWVyIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBoZWlnaHQgb2YgdGVycmFpbiBtZXNoIHNraXJ0cy5cbiAqIFRoZSBza2lydCBpcyBhbiBleHRlbnNpb24gb2YgdGhlIHRlcnJhaW4gbWVzaCB0aGF0IGhlbHBzIHByZXZlbnQgZ2FwcyBiZXR3ZWVuIHRlcnJhaW4gdGlsZXNcbiAqIGJ5IGV4dGVuZGluZyB0aGUgbWVzaCBlZGdlcyBkb3dud2FyZC4gVGhpcyBtdWx0aXBsaWVyIGlzIGFwcGxpZWQgdG8gdGhlIG1lc2gncyBtYXhpbXVtIGVycm9yXG4gKiB0byBkZXRlcm1pbmUgdGhlIHNraXJ0IGhlaWdodCwgZW5zdXJpbmcgbm8gZ2FwcyBhcmUgdmlzaWJsZS5cbiAqL1xuY29uc3QgU0tJUlRfSEVJR0hUX01VTFRJUExJRVIgPSAzO1xuXG4vKipcbiAqIExvYWQgaW1hZ2VzIHJlcXVpcmVkIGZvciByYXN0ZXIgdGlsZVxuICogQHBhcmFtIGFzc2V0UmVxdWVzdHMgLSBTVEFDIGFzc2V0IHJlcXVlc3RzXG4gKiBAcGFyYW0gY29sb3JtYXBJZCAtIGNvbG9ybWFwIGlkXG4gKiBAcGFyYW0gY2F0ZWdvcmljYWxPcHRpb25zIC0gY2F0ZWdvcmljYWwgb3B0aW9ucyByZXF1cmVkIGZvciBtYWtpbmcgb2YgY2F0ZWdvcmljYWwgY29sb3JtYXBcbiAqIEByZXR1cm5zIGltYWdlcyBtYXBcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRJbWFnZXMoXG4gIGFzc2V0UmVxdWVzdHM6IEFzc2V0UmVxdWVzdERhdGFbXSxcbiAgY29sb3JtYXBJZDogc3RyaW5nLFxuICBjYXRlZ29yaWNhbE9wdGlvbnM6IENhdGVnb3JpY2FsQ29sb3JtYXBPcHRpb25zXG4pOiBQcm9taXNlPEltYWdlRGF0YT4ge1xuICAvLyBXZSBsb2FkIGltYWdlIGRhdGEgKHNpbmdsZSBvciBtdWx0aSBhc3NldCBiYXNlZCBvbiBudW1iZXIgb2YgcmVxdWVzdHMpIGFuZCBjb2xvcm1hcCBpbiBwYXJhbGxlbFxuICBjb25zdCBbc3RhY0ltYWdlcywgY29sb3JtYXBdID0gYXdhaXQgUHJvbWlzZS5hbGwoW1xuICAgIGxvYWRTVEFDSW1hZ2VEYXRhKGFzc2V0UmVxdWVzdHMpLFxuICAgIChjb2xvcm1hcElkICE9PSBDQVRFR09SSUNBTF9DT0xPUk1BUF9JRCAmJiBtZW1vaXplZExvYWRDb2xvcm1hcChjb2xvcm1hcElkKSkgfHwgbnVsbFxuICBdKTtcbiAgbGV0IGltYWdlQ29sb3JtYXAgPSBjb2xvcm1hcDtcbiAgaWYgKGNvbG9ybWFwSWQgPT09IENBVEVHT1JJQ0FMX0NPTE9STUFQX0lEKSB7XG4gICAgaW1hZ2VDb2xvcm1hcCA9IGdlbmVyYXRlQ2F0ZWdvcmljYWxDb2xvcm1hcFRleHR1cmUoY2F0ZWdvcmljYWxPcHRpb25zKTtcbiAgfVxuICByZXR1cm4ge1xuICAgIC4uLnN0YWNJbWFnZXMsXG4gICAgaW1hZ2VDb2xvcm1hcFxuICB9O1xufVxuXG4vKipcbiAqIENhY2hlIGxvYWRpbmcgb2YgY29sb3JtYXAuIEVhY2ggY29sb3JtYXAgZmlsZSBpcyB2ZXJ5IHNtYWxsLCBvbiB0aGUgb3JkZXIgb2YgYSBmZXcgaHVuZHJlZCBieXRlcyxcbiAqIHNvIGEgZ2xvYmFsIGNhY2hlIGxpa2UgdGhpcyBpcyBub3QgZXhwZWN0ZWQgdG8gZWF0IHVwIHRvbyBtdWNoIG1lbW9yeS5cbiAqIE1vc3QgY29sb3JtYXBzIGFyZSBsb2FkZWQgZnJvbSBDRE4uIEluIGNhc2UgY29sb3JtYXBJZCBpcyBjYXRlZ29yaWNhbCwgdGhlIGltYWdlIGlzIGNyZWF0ZWQgZnJvbVxuICogY2F0ZWdvcmljYWwgY29sb3JtYXAgb3B0aW9ucy5cbiAqIE5PVEU6IHRoaXMgaW1wbGVtZW50YXRpb24gd2lsbCBjYWNoZSBwcm9taXNlIHJlamVjdGlvbnMgYXMgd2VsbCwgYnV0IHNpbmNlIHRoZXNlIGFyZSBzbWFsbCBmaWxlc1xuICogb24gdGhlIENETiwgdGhhdCdzIGFuIGFjY2VwdGFibGUgcmlzayBmb3IgdGhlIHRpbWUgYmVpbmcuXG4gKi9cbmNvbnN0IG1lbW9pemVkTG9hZENvbG9ybWFwOiAoY29sb3JtYXBJZDogc3RyaW5nKSA9PiBQcm9taXNlPENvbG9ybWFwSW1hZ2VEYXRhIHwgbnVsbD4gfCBudWxsID1cbiAgbWVtb2l6ZSgoY29sb3JtYXBJZDogc3RyaW5nKSA9PiB7XG4gICAgaWYgKGNvbG9ybWFwSWQgPT09IENBVEVHT1JJQ0FMX0NPTE9STUFQX0lEKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIGxvYWRDb2xvcm1hcChjb2xvcm1hcElkKTtcbiAgICB9XG4gIH0pO1xuXG4vKipcbiAqIExvYWQgUE5HIGNvbG9ybWFwLlxuICogU2luY2UgdGhlIGNvbG9ybWFwIGZpbGVzIGFyZSBzbyBzbWFsbCAoY291cGxlIGh1bmRyZWQgYnl0ZXMpLCB3ZSBsb2FkIHRoZW0gYWx3YXlzLCBldmVuIGZvciBhblxuICogUkdCIGNvbXBvc2l0ZSB3aGVyZSB0aGUgY29sb3JtYXAgd29uJ3QgYmUgdXNlZC4gQWZ0ZXIgdGhlIGZpcnN0IGxvYWQsIHRoZSBjb2xvcm1hcCBzaG91bGQgYmVcbiAqIGxvYWRlZCBmcm9tIHRoZSBkaXNrIGNhY2hlLiBCeSBsb2FkaW5nIGFsd2F5cywgd2UgY2FuIHByZXZlbnQgdW5uZWNlc3NhcmlseSB0cmlnZ2VyaW5nXG4gKiBnZXRUaWxlRGF0YSwgd2hpY2ggcmVkdWNlcyBmbGFzaGluZyBmcm9tIHJlbG9hZGluZyB0aGUgaW1hZ2VzIG9uIHRoZSBHUFUuXG4gKlxuICogQHBhcmFtIGNvbG9ybWFwSWQgICAgICBJRCBvZiBjb2xvcm1hcCBQTkdcbiAqXG4gKiBAcmV0dXJucyBpbWFnZSBvYmplY3QgdG8gcGFzcyB0byBUZXh0dXJlMkQgY29uc3RydWN0b3JcbiAqL1xuYXN5bmMgZnVuY3Rpb24gbG9hZENvbG9ybWFwKGNvbG9ybWFwSWQ6IHN0cmluZyk6IFByb21pc2U8Q29sb3JtYXBJbWFnZURhdGEgfCBudWxsPiB7XG4gIGlmICghY29sb3JtYXBJZCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgY29uc3QgcmVxdWVzdCA9IGF3YWl0IGdldEFzc2V0UmVxdWVzdCh7XG4gICAgdXJsOiBSYXN0ZXJMYXllclJlc291cmNlcy5yYXN0ZXJDb2xvck1hcChjb2xvcm1hcElkKSxcbiAgICByYXN0ZXJTZXJ2ZXJVcmw6ICcnLFxuICAgIG9wdGlvbnM6IHt9XG4gIH0pO1xuICByZXR1cm4gbG9hZEltYWdlKHJlcXVlc3QudXJsLCBDT0xPUk1BUF9URVhUVVJFX1BBUkFNRVRFUlMsIHJlcXVlc3Qub3B0aW9ucyk7XG59XG5cbi8qKlxuICogR2V0IHRoZSByZXF1ZXN0IGRhdGEgZm9yIGxvYWRpbmcgYSBzaW5nbGUgb3IgbXVsdGkgYXNzZXQgU1RBQyBtZXRhZGF0YSBvYmplY3QuXG4gKiBAcGFyYW0gb3B0aW9ucyBTVEFDIG1ldGFkYXRhIG9iamVjdCBhbmQgdXNlci1kZWZpbmVkIHBhcmFtZXRlcnMuXG4gKiBAcmV0dXJucyBJbWFnZSBkYXRhLCBvciBudWxsIGlmIHRoZSBTVEFDIG1ldGFkYXRhIG9iamVjdCBpcyBub3Qgc3VwcG9ydGVkLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0U1RBQ0ltYWdlUmVxdWVzdHMoXG4gIG9wdGlvbnM6IEdldFRpbGVEYXRhUHJvcHNcbik6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YVtdIHwgbnVsbD4ge1xuICBjb25zdCB7bG9hZEFzc2V0SWRzfSA9IG9wdGlvbnM7XG5cbiAgaWYgKGxvYWRBc3NldElkcy5sZW5ndGggPT09IDEpIHtcbiAgICBjb25zdCByZXF1ZXN0ID0gYXdhaXQgZ2V0U2luZ2xlQXNzZXRTVEFDUmVxdWVzdChvcHRpb25zKTtcbiAgICByZXR1cm4gcmVxdWVzdCA/IFtyZXF1ZXN0XSA6IG51bGw7XG4gIH1cblxuICByZXR1cm4gYXdhaXQgZ2V0TXVsdGlBc3NldFNUQUNSZXF1ZXN0KG9wdGlvbnMpO1xufVxuXG4vLyBUT0RPOiBpbWFnZSBsb2FkaW5nIHNob3VsZCByZWFsbHkgYmUgX2RyaXZlbiBieSB0aGUgbW9kdWxlc19cbi8vIEUuZy4gdGhlIHVzZXIgY2hvb3NlcyB3aGF0IGtpbmQgb2YgcGlwZWxpbmUgdGhleSB3YW50IG9uIHRoZSBHUFUsIHRoZW4gZnJvbSB0aG9zZSBtb2R1bGVzIHdlIGNhblxuLy8gc2VlIHdoZXRoZXIgdGhlIHVzZXIgd2FudHMgdGhlIHBhbnNoYXJwZW5lZCBiYW5kLCBjb2xvcm1hcCwgZXRjXG4vKipcbiAqIExvYWQgaW1hZ2VzIGRlZmluZWQgYnkgYSBTVEFDIG1ldGFkYXRhIG9iamVjdCBwbHVzIHVzZXItZGVmaW5lZCBwYXJhbWV0ZXJzXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBsb2FkU1RBQ0ltYWdlRGF0YShyZXF1ZXN0czogQXNzZXRSZXF1ZXN0RGF0YVtdKTogUHJvbWlzZTxJbWFnZURhdGE+IHtcbiAgcmV0dXJuIHJlcXVlc3RzLmxlbmd0aCA9PT0gMSA/IGxvYWRTaW5nbGVBc3NldFNUQUMocmVxdWVzdHNbMF0pIDogbG9hZE11bHRpQXNzZXRTVEFDKHJlcXVlc3RzKTtcbn1cblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9uIHRoYXQgZm9ybXMgYW4gYXNzZXQgcmVxdWVzdCBiYXNlZCBvbiBnaXZlbiBwYXJhbWV0ZXJzLlxuICovXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gZ2V0QXNzZXRSZXF1ZXN0KHtcbiAgdXJsLFxuICByYXN0ZXJTZXJ2ZXJVcmwsXG4gIHBhcmFtcyxcbiAgb3B0aW9ucyxcbiAgdXNlTWFzayA9IHRydWUsXG4gIHJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlc1xufToge1xuICB1cmw6IHN0cmluZztcbiAgcmFzdGVyU2VydmVyVXJsOiBzdHJpbmc7XG4gIHBhcmFtcz86IFVSTFNlYXJjaFBhcmFtcyB8IG51bGw7XG4gIG9wdGlvbnM6IFJlcXVlc3RJbml0O1xuICB1c2VNYXNrPzogYm9vbGVhbjtcbiAgcmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzPzogbnVtYmVyW10gfCBudWxsO1xufSk6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YT4ge1xuICBjb25zdCByZXF1ZXN0VXJsID0gdXJsO1xuICBjb25zdCByZXF1ZXN0UGFyYW1zID0gcGFyYW1zID8/IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgY29uc3QgcmVxdWVzdE9wdGlvbnMgPSBvcHRpb25zO1xuXG4gIGNvbnN0IGFzc2V0VXJsID0gcmVxdWVzdFBhcmFtcyA/IGAke3JlcXVlc3RVcmx9PyR7cmVxdWVzdFBhcmFtcy50b1N0cmluZygpfWAgOiByZXF1ZXN0VXJsO1xuICByZXR1cm4ge1xuICAgIHVybDogYXNzZXRVcmwsXG4gICAgcmFzdGVyU2VydmVyVXJsLFxuICAgIG9wdGlvbnM6IHJlcXVlc3RPcHRpb25zLFxuICAgIHVzZU1hc2ssXG4gICAgcmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzXG4gIH07XG59XG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbiBmb3IgZm9ybWluZyBhIHJlcXVlc3QgZm9yIGEgc2luZ2xlIGFzc2V0IFNUQUMgbWV0YWRhdGEgb2JqZWN0LlxuICovXG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eSwgbWF4LXN0YXRlbWVudHNcbmFzeW5jIGZ1bmN0aW9uIGdldFNpbmdsZUFzc2V0U1RBQ1JlcXVlc3QoXG4gIG9wdGlvbnM6IEdldFRpbGVEYXRhUHJvcHNcbik6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YSB8IG51bGw+IHtcbiAgY29uc3Qge1xuICAgIHN0YWMsXG4gICAgbG9hZEFzc2V0SWRzLFxuICAgIGxvYWRCYW5kSW5kZXhlcyxcbiAgICBzaWduYWwsXG4gICAgdXNlU1RBQ1NlYXJjaGluZyxcbiAgICBpbmRleDoge3gsIHksIHp9LFxuICAgIHN0YWNTZWFyY2hQcm92aWRlcixcbiAgICBzdGFydERhdGUsXG4gICAgZW5kRGF0ZSxcbiAgICBfc3RhY1F1ZXJ5XG4gIH0gPSBvcHRpb25zO1xuXG4gIGNvbnN0IHVzZU1hc2sgPSB0cnVlO1xuXG4gIC8vIE9ubHkgYSBzaW5nbGUgVVJMIGJlY2F1c2Ugb25seSBhIHNpbmdsZSBhc3NldFxuICBsZXQgdXJsSW5mbzoge3VybDogc3RyaW5nOyByYXN0ZXJTZXJ2ZXJVcmw6IHN0cmluZ30gfCBudWxsID0gbnVsbDtcbiAgbGV0IHVybFBhcmFtczogVVJMU2VhcmNoUGFyYW1zIHwgbnVsbCA9IG5ldyBVUkxTZWFyY2hQYXJhbXMoKTtcbiAgbGV0IHJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlczogbnVtYmVyW10gfCBudWxsID0gbG9hZEJhbmRJbmRleGVzO1xuICBpZiAodXNlU1RBQ1NlYXJjaGluZyAmJiBzdGFjLnR5cGUgIT09ICdGZWF0dXJlJykge1xuICAgIHVybFBhcmFtcyA9IGdldFN0YWNBcGlVcmxQYXJhbXMoe1xuICAgICAgc3RhYyxcbiAgICAgIHN0YWNTZWFyY2hQcm92aWRlcixcbiAgICAgIHN0YXJ0RGF0ZSxcbiAgICAgIGVuZERhdGUsXG4gICAgICBtYXNrOiB1c2VNYXNrLFxuICAgICAgbG9hZEFzc2V0SWRzLFxuICAgICAgX3N0YWNRdWVyeVxuICAgIH0pO1xuICAgIHVybEluZm8gPSBnZXRUaXRpbGVyVXJsKHtzdGFjLCB1c2VTVEFDU2VhcmNoaW5nLCB4LCB5LCB6fSk7XG4gIH0gZWxzZSBpZiAoc3RhYy50eXBlID09PSAnRmVhdHVyZScpIHtcbiAgICAvLyBzdGFjIGlzIGFuIEl0ZW1cbiAgICB1cmxQYXJhbXMgPSBnZXRTaW5nbGVDT0dVcmxQYXJhbXMoe1xuICAgICAgc3RhYyxcbiAgICAgIGxvYWRBc3NldElkOiBsb2FkQXNzZXRJZHNbMF0sXG4gICAgICBsb2FkQmFuZEluZGV4ZXMsXG4gICAgICBtYXNrOiB1c2VNYXNrXG4gICAgfSk7XG4gICAgdXJsSW5mbyA9IGdldFRpdGlsZXJVcmwoe3N0YWMsIHVzZVNUQUNTZWFyY2hpbmcsIHgsIHksIHp9KTtcbiAgICByZXNwb25zZVJlcXVpcmVkQmFuZEluZGljZXMgPSBudWxsO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKCF1cmxJbmZvLnVybCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgcmV0dXJuIGF3YWl0IGdldEFzc2V0UmVxdWVzdCh7XG4gICAgLi4udXJsSW5mbyxcbiAgICBwYXJhbXM6IHVybFBhcmFtcyxcbiAgICBvcHRpb25zOiB7c2lnbmFsfSxcbiAgICB1c2VNYXNrLFxuICAgIHJlc3BvbnNlUmVxdWlyZWRCYW5kSW5kaWNlc1xuICB9KTtcbn1cblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9uIGZvciBmb3JtaW5nIHJlcXVlc3RzIGZvciBhIG11bHRpIGFzc2V0IFNUQUMgbWV0YWRhdGEgb2JqZWN0LlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRNdWx0aUFzc2V0U1RBQ1JlcXVlc3QoXG4gIG9wdGlvbnM6IEdldFRpbGVEYXRhUHJvcHNcbik6IFByb21pc2U8QXNzZXRSZXF1ZXN0RGF0YVtdIHwgbnVsbD4ge1xuICBjb25zdCB7XG4gICAgc3RhYyxcbiAgICBsb2FkQXNzZXRJZHMsXG4gICAgbG9hZEJhbmRJbmRleGVzLFxuICAgIHNpZ25hbCxcbiAgICB1c2VTVEFDU2VhcmNoaW5nLFxuICAgIHN0YWNTZWFyY2hQcm92aWRlcixcbiAgICBzdGFydERhdGUsXG4gICAgZW5kRGF0ZSxcbiAgICBpbmRleDoge3gsIHksIHp9LFxuICAgIF9zdGFjUXVlcnlcbiAgfSA9IG9wdGlvbnM7XG5cbiAgLy8gTXVsdGlwbGUgdXJscywgb25lIGZvciBlYWNoIGFzc2V0XG4gIGxldCByZXF1ZXN0RGF0YToge1xuICAgIHVybDogc3RyaW5nO1xuICAgIHJhc3RlclNlcnZlclVybDogc3RyaW5nO1xuICAgIHBhcmFtczogVVJMU2VhcmNoUGFyYW1zIHwgbnVsbDtcbiAgICBvcHRpb25zPzogUmVxdWVzdEluaXQ7XG4gIH1bXSA9IFtdO1xuXG4gIC8vIFdlIGFzc3VtZSB0aGF0IHRoZXJlJ3Mgb25seSBvbmUgdmFsaWRpdHkgbWFzayBmb3IgdGhlIGVudGlyZSBhc3NldCwgYW5kIHRoZXJlZm9yZSBhbnkgb2YgdGhlXG4gIC8vIHJlcXVlc3RzIHdvdWxkIHJldHVybiB0aGUgc2FtZSB2YWxpZGl0eSBiaXRtYXAuIFRoZXJlZm9yZSB3ZSBvbmx5IHJlcXVlc3QgYSBtYXNrIGZvciB0aGUgZmlyc3RcbiAgLy8gYXNzZXRcbiAgY29uc3QgcmVxdWVzdE1hc2s6IGJvb2xlYW5bXSA9IG5ldyBBcnJheShsb2FkQXNzZXRJZHMubGVuZ3RoKS5maWxsKGZhbHNlKTtcbiAgcmVxdWVzdE1hc2tbMF0gPSB0cnVlO1xuXG4gIGlmICh1c2VTVEFDU2VhcmNoaW5nICYmIHN0YWMudHlwZSAhPT0gJ0ZlYXR1cmUnKSB7XG4gICAgcmVxdWVzdERhdGEgPSB6aXAobG9hZEFzc2V0SWRzLCByZXF1ZXN0TWFzaykubWFwKChbYXNzZXRJZCwgbWFza10pID0+IHtcbiAgICAgIGNvbnN0IHBhcmFtcyA9IGdldFN0YWNBcGlVcmxQYXJhbXMoe1xuICAgICAgICBzdGFjLFxuICAgICAgICBzdGFjU2VhcmNoUHJvdmlkZXIsXG4gICAgICAgIHN0YXJ0RGF0ZSxcbiAgICAgICAgZW5kRGF0ZSxcbiAgICAgICAgbWFzayxcbiAgICAgICAgbG9hZEFzc2V0SWRzOiBbYXNzZXRJZF0sXG4gICAgICAgIF9zdGFjUXVlcnlcbiAgICAgIH0pO1xuICAgICAgY29uc3QgdXJsSW5mbyA9IGdldFRpdGlsZXJVcmwoe3N0YWMsIHVzZVNUQUNTZWFyY2hpbmcsIHgsIHksIHp9KTtcbiAgICAgIHJldHVybiB7Li4udXJsSW5mbywgcGFyYW1zfTtcbiAgICB9KTtcbiAgfSBlbHNlIGlmIChzdGFjLnR5cGUgPT09ICdGZWF0dXJlJykge1xuICAgIHJlcXVlc3REYXRhID0gemlwMyhsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVxdWVzdE1hc2spLm1hcChcbiAgICAgIChbYXNzZXRJZCwgYmFuZEluZGV4LCBtYXNrXSkgPT4ge1xuICAgICAgICBjb25zdCBwYXJhbXMgPSBnZXRTaW5nbGVDT0dVcmxQYXJhbXMoe1xuICAgICAgICAgIHN0YWMsXG4gICAgICAgICAgbG9hZEFzc2V0SWQ6IGFzc2V0SWQsXG4gICAgICAgICAgbG9hZEJhbmRJbmRleGVzOiBbYmFuZEluZGV4XSxcbiAgICAgICAgICBtYXNrXG4gICAgICAgIH0pO1xuICAgICAgICBjb25zdCB1cmxJbmZvID0gZ2V0VGl0aWxlclVybCh7c3RhYywgdXNlU1RBQ1NlYXJjaGluZywgeCwgeSwgen0pO1xuICAgICAgICByZXR1cm4gey4uLnVybEluZm8sIHBhcmFtc307XG4gICAgICB9XG4gICAgKTtcbiAgfVxuXG4gIGlmIChpc1ZhbGlkUmVxdWVzdERhdGEocmVxdWVzdERhdGEpKSB7XG4gICAgcmV0dXJuIGF3YWl0IFByb21pc2UuYWxsKFxuICAgICAgcmVxdWVzdERhdGEubWFwKHJlcXVlc3QgPT5cbiAgICAgICAgZ2V0QXNzZXRSZXF1ZXN0KHsuLi5yZXF1ZXN0LCBvcHRpb25zOiByZXF1ZXN0Lm9wdGlvbnMgPz8ge3NpZ25hbH19KVxuICAgICAgKVxuICAgICk7XG4gIH1cblxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBMb2FkIGltYWdlIGRhdGEgd2hlbiBjb25zb2xpZGF0ZWQgaW4gYmFuZHMgd2l0aGluIGEgc2luZ2xlIFNUQUMgQXNzZXRcbiAqL1xuYXN5bmMgZnVuY3Rpb24gbG9hZFNpbmdsZUFzc2V0U1RBQyhyZXF1ZXN0OiBBc3NldFJlcXVlc3REYXRhKTogUHJvbWlzZTxJbWFnZURhdGE+IHtcbiAgY29uc3QgaW1hZ2VCYW5kcyA9IGF3YWl0IGxvYWROcHlBcnJheShyZXF1ZXN0LCB0cnVlKTtcblxuICBpZiAoIWltYWdlQmFuZHMpIHtcbiAgICByZXR1cm4ge1xuICAgICAgaW1hZ2VCYW5kc1xuICAgIH07XG4gIH1cblxuICBjb25zdCBpbWFnZU1hc2sgPSAocmVxdWVzdC51c2VNYXNrICYmIGltYWdlQmFuZHMucG9wKCkpIHx8IG51bGw7XG4gIGxldCBtYXBwZWRJbWFnZUJhbmRzID0gaW1hZ2VCYW5kcztcbiAgaWYgKHJlcXVlc3QucmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzKSB7XG4gICAgbWFwcGVkSW1hZ2VCYW5kcyA9IHJlcXVlc3QucmVzcG9uc2VSZXF1aXJlZEJhbmRJbmRpY2VzLm1hcChpID0+IGltYWdlQmFuZHNbaV0pO1xuICB9XG4gIHJldHVybiB7aW1hZ2VCYW5kczogbWFwcGVkSW1hZ2VCYW5kcywgaW1hZ2VNYXNrfTtcbn1cblxuLyoqXG4gKiBMb2FkIGltYWdlIGRhdGEgd2hlbiBzcGxpdCBhbW9uZyBtdWx0aXBsZSBTVEFDIEFzc2V0c1xuICovXG5hc3luYyBmdW5jdGlvbiBsb2FkTXVsdGlBc3NldFNUQUMocmVxdWVzdHM6IEFzc2V0UmVxdWVzdERhdGFbXSk6IFByb21pc2U8SW1hZ2VEYXRhPiB7XG4gIGNvbnN0IHJlc3VsdHMgPSBhd2FpdCBQcm9taXNlLmFsbChyZXF1ZXN0cy5tYXAocmVxdWVzdCA9PiBsb2FkTnB5QXJyYXkocmVxdWVzdCwgdHJ1ZSkpKTtcbiAgLy8gVGhlIGZpcnN0IHJlcXVlc3QgaW5jbHVkZXMgYSBtYXNrXG4gIGNvbnN0IGltYWdlTWFzayA9IHJlc3VsdHNbMF0/LnBvcCgpIHx8IG51bGw7XG4gIGNvbnN0IGltYWdlQmFuZHMgPSByZXN1bHRzLmZsYXQoKS5maWx0ZXIoYmFuZCA9PiBiYW5kICE9PSBudWxsKSBhcyBJbWFnZURhdGFbJ2ltYWdlQmFuZHMnXTtcbiAgcmV0dXJuIHtpbWFnZUJhbmRzLCBpbWFnZU1hc2t9O1xufVxuXG4vKipcbiAqIEl0ZXJhdGUgb3ZlciB0d28gYXJyYXlzIHNpbXVsdGFuZW91c2x5LCBzaW1pbGFyIHRvIFB5dGhvbidzIHppcCBidWlsdGluXG4gKi9cbmZ1bmN0aW9uIHppcDxUMSwgVDI+KGE6IFQxW10sIGI6IFQyW10pOiBbVDEsIFQyXVtdIHtcbiAgcmV0dXJuIGEubWFwKChrLCBpKSA9PiBbaywgYltpXV0pO1xufVxuXG4vKipcbiAqIEl0ZXJhdGUgb3ZlciB0aHJlZSBhcnJheXMgc2ltdWx0YW5lb3VzbHksIHNpbWlsYXIgdG8gUHl0aG9uJ3MgemlwIGJ1aWx0aW5cbiAqL1xuZnVuY3Rpb24gemlwMzxUMSwgVDIsIFQzPihhOiBUMVtdLCBiOiBUMltdLCBjOiBUM1tdKTogW1QxLCBUMiwgVDNdW10ge1xuICByZXR1cm4gYS5tYXAoKGssIGkpID0+IFtrLCBiW2ldLCBjW2ldXSk7XG59XG5cbi8qKlxuICogVHlwZSBndWFyZCB0byBjaGVjayBpZiBhbGwgYXJyYXkgZWxlbWVudHMgYXJlIHN0cmluZ3NcbiAqL1xuZnVuY3Rpb24gaXNWYWxpZFJlcXVlc3REYXRhKFxuICBhcnI6IHt1cmw6IHVua25vd247IHBhcmFtczogdW5rbm93bjsgb3B0aW9ucz86IHVua25vd259W11cbik6IGFyciBpcyB7dXJsOiBzdHJpbmc7IHBhcmFtczogVVJMU2VhcmNoUGFyYW1zOyBvcHRpb25zOiB1bmtub3dufVtdIHtcbiAgcmV0dXJuIGFyci5ldmVyeSh4ID0+IHR5cGVvZiB4Py51cmwgPT09ICdzdHJpbmcnICYmIHg/LnBhcmFtcyAhPT0gbnVsbCk7XG59XG5cbi8qKlxuICogQ3JlYXRlIGJhc2U2NCBpbWFnZSBkYXRhIHVybCBmb3IgY2F0ZWdvcmljYWwgY29sb3JtYXBcbiAqIEBwYXJhbSBjYXRlZ29yaWNhbE9wdGlvbnMgLSBjb2xvciBtYXAgY29uZmlndXJhdGlvbiBhbmQgbWluLW1heCB2YWx1ZXMgb2YgY2F0ZWdvcmljYWwgYmFuZFxuICogQHJldHVybnMgYmFzZTY0IGltYWdlIGRhdGEgdXJsXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRDYXRlZ29yaWNhbENvbG9ybWFwRGF0YVVybChcbiAgY2F0ZWdvcmljYWxPcHRpb25zOiBDYXRlZ29yaWNhbENvbG9ybWFwT3B0aW9uc1xuKTogc3RyaW5nIHwgbnVsbCB7XG4gIGNvbnN0IGJpdG1hcCA9IGdlbmVyYXRlQ2F0ZWdvcmljYWxCaXRtYXBBcnJheShjYXRlZ29yaWNhbE9wdGlvbnMpO1xuICBpZiAoIWJpdG1hcCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIHJldHVybiBnZXRJbWFnZURhdGFVUkwoYml0bWFwLCBDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRILCAxKTtcbn1cblxuLyoqXG4gKiBMb2FkIHRlcnJhaW4gbWVzaCBmcm9tIHJhc3RlciB0aWxlIHNlcnZlclxuICogQHBhcmFtIHByb3BzIC0gcHJvcGVydGllcyB0byBsb2FkIHRlcnJhaW4gZGF0YVxuICogQHJldHVybnMgdGVycmFpbiBtZXNoIGRhdGFcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGxvYWRUZXJyYWluKHByb3BzOiB7XG4gIGluZGV4OiB7eDogbnVtYmVyOyB5OiBudW1iZXI7IHo6IG51bWJlcn07XG4gIHNpZ25hbDogQWJvcnRTaWduYWw7XG4gIHJhc3RlclRpbGVTZXJ2ZXJVcmxzOiBzdHJpbmdbXTtcbiAgYm91bmRzRm9yR2VvbWV0cnk/OiBbbnVtYmVyLCBudW1iZXIsIG51bWJlciwgbnVtYmVyXTtcbn0pOiBQcm9taXNlPFRlcnJhaW5EYXRhIHwgbnVsbD4ge1xuICBjb25zdCB7XG4gICAgaW5kZXg6IHt4LCB5LCB6fSxc