kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
689 lines (653 loc) • 85.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CATEGORICAL_TEXTURE_WIDTH = void 0;
exports.bboxIntersects = bboxIntersects;
exports.computeZRange = computeZRange;
exports.dtypeMaxValue = void 0;
exports.filterAvailablePresets = filterAvailablePresets;
exports.findAssetWithName = findAssetWithName;
exports.generateCategoricalBitmapArray = generateCategoricalBitmapArray;
exports.getAssets = getAssets;
exports.getBandIdsForCommonNames = getBandIdsForCommonNames;
exports.getDataSourceParams = getDataSourceParams;
exports.getEOBands = getEOBands;
exports.getImageMinMax = getImageMinMax;
exports.getMaxRequests = getMaxRequests;
exports.getMinMaxFromTile2DHeaders = getMinMaxFromTile2DHeaders;
exports.getRasterStatisticsMinMax = getRasterStatisticsMinMax;
exports.getSTACBounds = getSTACBounds;
exports.getSingleBandPresetOptions = getSingleBandPresetOptions;
exports.getUsableAssets = getUsableAssets;
exports.isColormapAllowed = isColormapAllowed;
exports.isFilterAllowed = isFilterAllowed;
exports.isRescalingAllowed = isRescalingAllowed;
exports.isSearchableStac = isSearchableStac;
exports.timeRangeToStacTemporalInterval = timeRangeToStacTemporalInterval;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _core = require("@math.gl/core");
var _utils = require("@kepler.gl/utils");
var _config = require("./config");
var _types = require("./types");
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } // SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
/**
* Utility functions and constants for processing STAC metadata and other raster tile data
*/
var CATEGORICAL_TEXTURE_WIDTH = exports.CATEGORICAL_TEXTURE_WIDTH = 256;
function isColormapAllowed(bandCombination) {
return bandCombination !== _types.BandCombination.Rgb;
}
function isRescalingAllowed(bandCombination) {
return bandCombination === _types.BandCombination.Rgb || bandCombination === _types.BandCombination.Single;
}
function isFilterAllowed(bandCombination) {
return bandCombination !== _types.BandCombination.Rgb;
}
/**
* Max value for data type
*
* Values of null might be calculated in runtime
*/
var dtypeMaxValue = exports.dtypeMaxValue = {
uint8: Math.pow(2, 8) - 1,
uint16: Math.pow(2, 16) - 1,
uint32: Math.pow(2, 32) - 1,
uint64: null,
int8: Math.pow(2, 7) - 1,
int16: Math.pow(2, 15) - 1,
int32: Math.pow(2, 31) - 1,
int64: null,
float16: null,
float32: null,
float64: null,
cint16: null,
cint32: null,
cfloat32: null,
cfloat64: null,
other: null
};
/**
* Is this a STAC Collection that supports custom searching
* TODO: currently this is a custom hack to support Sentinel. It will need to be generalized before being accessible
* @param stac STAC object
* @return If True, supports searching
*/
function isSearchableStac(stac) {
return stac.type === 'Collection';
// return stac.type !== 'Feature' && stac?.providers.some(
// provider =>
// provider.name.toLowerCase() === 'microsoft' &&
// provider.url === 'https://planetarycomputer.microsoft.com'
// );
}
function getZoomRange(stac) {
if (_config.ZOOM_RANGES[stac.id]) {
return _config.ZOOM_RANGES[stac.id];
}
// For a single COG, having a full zoom range isn't really a problem.
// the /cog/info endpoint doesn't describe zoom levels because it doesn't know the projection to serve the image in.
// Default minzoom, maxzoom: [0, 20]
return [0, 20];
}
/**
* Infer data type from STAC item
* This uses the `raster` extension, which is not yet very common
* @param stac stac object
* @return Data type of the band
*/
function getDataType(usableAssets, typesToCheck) {
var dataTypes = new Set();
for (var assetName in usableAssets) {
var _asset$rasterBands;
var asset = usableAssets[assetName];
if (!asset) {
continue;
}
if (typesToCheck && !typesToCheck.includes(assetName)) {
continue;
}
(_asset$rasterBands = asset['raster:bands']) === null || _asset$rasterBands === void 0 || _asset$rasterBands.forEach(function (band) {
return band.data_type && dataTypes.add(band.data_type);
});
}
// TODO: support multiple data types across assets
if (dataTypes.size !== 1) {
return null;
}
return Array.from(dataTypes)[0];
}
/**
* Get min and max values of the band from the band's data type values range
* @param stac - stac metadata object
* @param dtype - data type of the raster band
* @returns min and max values for the band
*/
function getPixelRange(stac, dtype, _ref) {
var _ref2 = (0, _slicedToArray2["default"])(_ref, 2),
minRasterStatsValue = _ref2[0],
maxRasterStatsValue = _ref2[1];
if (!dtype) {
return null;
}
// TODO: might not always be desired to leave min pixel value at 0
var minPixelValue = 0;
var maxPixelValue = dtypeMaxValue[dtype];
// TODO check if this early return is expected
if (!maxPixelValue) {
return null;
}
if (!Number.isFinite(maxPixelValue) && typeof minRasterStatsValue === 'number' && typeof maxRasterStatsValue === 'number') {
return [minRasterStatsValue, maxRasterStatsValue];
}
return [minPixelValue, maxPixelValue];
}
/**
* Find min and max values throughout a raster band image
* @param imageData raster band image data
* @returns min and max values throughout the band
*/
function getImageMinMax(imageData) {
// We cannot calculate min/max for Image/ImageBitmap
if (!(0, _core.isArray)(imageData)) {
return [null, null];
}
var min = Infinity;
var max = -Infinity;
var _iterator = _createForOfIteratorHelper(imageData),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var value = _step.value;
if (!isNaN(value)) {
min = Math.min(min, value);
max = Math.max(max, value);
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return [min, max];
}
/**
* Find asset names and band indexes for given commonNames
* Right now in Kepler, our available methods of rendering raster data are quite simple. We only allow a user to choose among `presets`, and under the hood we select which data files to load.
* @param usableAssets [stac description]
* @param commonNames an array of eo:common_name to search within bands
* @return Band information or null if not all bands exist
*/
function getBandIdsForCommonNames(stac, usableAssets, commonNames) {
// An array of strings describing asset identifiers, e.g. the "key" in the assets object
var assetIds = [];
// An array of integers giving the index of the band within that asset's data
// In general, data sources either have a single band per asset or all assets in a single band
var bandIndexes = [];
var _iterator2 = _createForOfIteratorHelper(commonNames),
_step2;
try {
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
var commonName = _step2.value;
// Find asset that includes a band with this common name
var result = findAssetWithName(usableAssets, commonName, 'common_name');
if (result) {
var _result = (0, _slicedToArray2["default"])(result, 2),
assetName = _result[0],
bandIndex = _result[1];
assetIds.push(assetName);
bandIndexes.push(bandIndex);
}
}
} catch (err) {
_iterator2.e(err);
} finally {
_iterator2.f();
}
if (assetIds.length === 0) {
return null;
}
var _consolidateBandIndex = consolidateBandIndexes(stac, assetIds, bandIndexes),
loadAssetIds = _consolidateBandIndex.loadAssetIds,
loadBandIndexes = _consolidateBandIndex.loadBandIndexes,
renderBandIndexes = _consolidateBandIndex.renderBandIndexes;
return {
loadAssetIds: loadAssetIds,
loadBandIndexes: loadBandIndexes,
renderBandIndexes: renderBandIndexes
};
}
/**
* Consolidate asset/band info into load info and render info
* @param stac STAC object
* @param assetIds
* @param bandIndexes
* @return Asset information
*/
function consolidateBandIndexes(stac, assetIds, bandIndexes) {
var loadAssetIds;
var loadBandIndexes;
var renderBandIndexes;
// All bands we want to load are in a single asset
if (assetIds.length === 1 || Array.from(new Set(assetIds)).length === 1) {
var _asset$eoBands;
loadAssetIds = [assetIds[0]];
var asset = getAssets(stac)[loadAssetIds[0]];
// Request all bands in order, then reorder with renderBandIndexes
// NOTE: usually (e.g. with NAIP) if all bands are in a single asset, then there are only ~4
// bands. This approach of requesting all bands may be less efficient with more than 4 bands
// (because of larger download sizes). In the future may want separate loading paths if we
// encounter single asset objects with > 4 bands.
// TODO: eo:bands can be _either_ on each asset _or_ on the STAC's properties, which is not currently handled
loadBandIndexes = (_asset$eoBands = asset['eo:bands']) === null || _asset$eoBands === void 0 ? void 0 : _asset$eoBands.map(function (_, idx) {
return idx;
});
renderBandIndexes = bandIndexes;
} else {
loadAssetIds = assetIds;
loadBandIndexes = bandIndexes;
renderBandIndexes = null;
}
return {
loadAssetIds: loadAssetIds,
loadBandIndexes: loadBandIndexes,
renderBandIndexes: renderBandIndexes
};
}
/**
* Find the Asset in the STAC object that containing an eo:band with the desired common_name or name
* @param assets assets object. Keys should be asset name and values should be asset data
* @param name name or common_name in eo:bands
* @param property 'name' or 'common_name'
* @return name of asset containing desired band, index of band within asset
*/
function findAssetWithName(assets, name, property) {
for (var _i = 0, _Object$entries = Object.entries(assets); _i < _Object$entries.length; _i++) {
var _Object$entries$_i = (0, _slicedToArray2["default"])(_Object$entries[_i], 2),
assetName = _Object$entries$_i[0],
assetData = _Object$entries$_i[1];
var eoBands = assetData['eo:bands'] || [];
// Search bands array of this asset
for (var i = 0; i < eoBands.length; i++) {
var eoBand = eoBands[i];
if (name === eoBand[property]) {
return [assetName, i];
}
}
}
return null;
}
function getSingleBandInfo(usableAssets, singleBandInfo) {
var _singleBandInfo;
if (!((_singleBandInfo = singleBandInfo) !== null && _singleBandInfo !== void 0 && _singleBandInfo.assetId) && Object.keys(usableAssets).length === 1) {
singleBandInfo = {
assetId: Object.keys(usableAssets)[0]
};
}
if (!singleBandInfo) {
return null;
}
var _singleBandInfo2 = singleBandInfo,
assetId = _singleBandInfo2.assetId,
bandIndex = _singleBandInfo2.bandIndex;
if (!Object.keys(usableAssets).includes(assetId)) {
return null;
}
return {
loadAssetIds: [assetId],
loadBandIndexes: bandIndex ? [bandIndex] : [0],
// No reordering of bands on the GPU
renderBandIndexes: null
};
}
function getRasterStatisticsMinMax(stac, presetId, singleBandInfo) {
if (presetId !== 'singleBand') {
return [undefined, undefined];
}
var minCategoricalBandValue;
var maxCategoricalBandValue;
var usableAssets = getUsableAssets(stac);
var bandMetadata = usableAssets[(singleBandInfo === null || singleBandInfo === void 0 ? void 0 : singleBandInfo.assetId) || ''];
if (!bandMetadata) {
var assetId = Object.keys(usableAssets)[0];
bandMetadata = usableAssets[assetId];
}
if (bandMetadata) {
var _bandMetadata$raster, _bandMetadata$raster2;
minCategoricalBandValue = (_bandMetadata$raster = bandMetadata['raster:bands']) === null || _bandMetadata$raster === void 0 || (_bandMetadata$raster = _bandMetadata$raster[0].statistics) === null || _bandMetadata$raster === void 0 ? void 0 : _bandMetadata$raster.minimum;
maxCategoricalBandValue = (_bandMetadata$raster2 = bandMetadata['raster:bands']) === null || _bandMetadata$raster2 === void 0 || (_bandMetadata$raster2 = _bandMetadata$raster2[0].statistics) === null || _bandMetadata$raster2 === void 0 ? void 0 : _bandMetadata$raster2.maximum;
}
return [minCategoricalBandValue, maxCategoricalBandValue];
}
function getSingleBandPresetOptions(stac, singleBandName) {
var usableAssets = getUsableAssets(stac);
var assetData = findAssetWithName(usableAssets, singleBandName, 'name');
if (!assetData) {
// some collections have common_name instead of name
assetData = findAssetWithName(usableAssets, singleBandName, 'common_name');
}
var singleBand;
if (assetData) {
var _assetData = assetData,
_assetData2 = (0, _slicedToArray2["default"])(_assetData, 2),
assetId = _assetData2[0],
bandIndex = _assetData2[1];
singleBand = {
assetId: assetId,
bandIndex: bandIndex
};
}
return singleBand;
}
/**
* Calculate viewport-related min and max raster values from loaded tiles
* @param tiles - deck.gl tiles
* @returns [minPixelValue, maxPixelValue]
*/
function getMinMaxFromTile2DHeaders(tiles) {
var minValues = tiles.map(function (tile) {
if (!tile || !tile.data) {
return null;
}
var minPixelValue = tile.data.minPixelValue;
return minPixelValue;
}).filter(function (value) {
return value;
});
var maxValues = tiles.map(function (tile) {
if (!tile || !tile.data) {
return null;
}
var maxPixelValue = tile.data.maxPixelValue;
return maxPixelValue;
}).filter(function (value) {
return value;
});
// We can cast to number[] because we have filtered null values
return [Math.min.apply(Math, (0, _toConsumableArray2["default"])(minValues)), Math.max.apply(Math, (0, _toConsumableArray2["default"])(maxValues))];
}
/**
* Get loading params
* @param stac STAC Object
* @param preset Preset for display
* @return Parameters for loading data
*/
function getDataSourceParams(stac, presetId, presetOptions) {
var usableAssets = getUsableAssets(stac);
var _PRESET_OPTIONS$prese = _config.PRESET_OPTIONS[presetId],
commonNames = _PRESET_OPTIONS$prese.commonNames,
bandCombination = _PRESET_OPTIONS$prese.bandCombination;
var _getZoomRange = getZoomRange(stac),
_getZoomRange2 = (0, _slicedToArray2["default"])(_getZoomRange, 2),
minZoom = _getZoomRange2[0],
maxZoom = _getZoomRange2[1];
var bandInfo = null;
if (bandCombination === 'single') {
bandInfo = getSingleBandInfo(usableAssets, presetOptions === null || presetOptions === void 0 ? void 0 : presetOptions.singleBand);
} else if (commonNames) {
bandInfo = getBandIdsForCommonNames(stac, usableAssets, commonNames);
} else {
return null;
}
if (!bandInfo) return null;
var dtype = getDataType(usableAssets, bandInfo.loadAssetIds);
var _getRasterStatisticsM = getRasterStatisticsMinMax(stac, presetId, presetOptions === null || presetOptions === void 0 ? void 0 : presetOptions.singleBand),
_getRasterStatisticsM2 = (0, _slicedToArray2["default"])(_getRasterStatisticsM, 2),
minRasterStatsValue = _getRasterStatisticsM2[0],
maxRasterStatsValue = _getRasterStatisticsM2[1];
var pixelRange = getPixelRange(stac, dtype, [minRasterStatsValue, maxRasterStatsValue]);
if (!pixelRange || !dtype) {
return null;
}
var _pixelRange = (0, _slicedToArray2["default"])(pixelRange, 2),
minPixelValue = _pixelRange[0],
maxPixelValue = _pixelRange[1];
var _bandInfo = bandInfo,
loadAssetIds = _bandInfo.loadAssetIds,
loadBandIndexes = _bandInfo.loadBandIndexes,
renderBandIndexes = _bandInfo.renderBandIndexes;
var globalBounds = getSTACBounds(stac);
return {
loadAssetIds: loadAssetIds,
loadBandIndexes: loadBandIndexes,
renderBandIndexes: renderBandIndexes,
minZoom: minZoom,
maxZoom: maxZoom,
minPixelValue: minPixelValue,
maxPixelValue: maxPixelValue,
// use min and max statistics values for categorical re-scale
minCategoricalBandValue: minRasterStatsValue,
maxCategoricalBandValue: maxRasterStatsValue,
globalBounds: globalBounds,
dataType: dtype
};
}
/**
* Get Bounding Box of STAC object
* @param stac
* @return bounding box
*/
function getSTACBounds(stac) {
// Check if Item
if (stac.type === 'Feature') {
// bbox may be missing
return stac.bbox || null;
}
// Then must be collection
return stac.extent.spatial.bbox[0];
}
/**
* Get all eo:band objects from STAC object
* @param stac STAC object
* @return array of eo:band objects or null
*/
function getEOBands(stac) {
var assets = getAssets(stac);
if (!assets) {
return null;
}
var eoBands = [];
for (var _i2 = 0, _Object$values = Object.values(assets); _i2 < _Object$values.length; _i2++) {
var data = _Object$values[_i2];
var assetBands = data['eo:bands'];
if (Array.isArray(assetBands) && assetBands.length > 0) {
eoBands.push.apply(eoBands, (0, _toConsumableArray2["default"])(assetBands));
}
}
return eoBands;
}
/**
* Filter presets by those that can be used with the given stac item
* Each preset has a commonNames key, which is a list of eo `common_name` values. Some STAC items
* may not have assets that span all of these combinations of `common_name`, so this filters the
* input array.
* @param stac STAC object
* @param presetData object with requirements for each preset
* @return An array of preset option ids that can be used with the given STAC item
*/
function filterAvailablePresets(stac, presetData) {
if (!stac) {
return null;
}
var eoBands = getEOBands(stac);
if (!eoBands) {
return null;
}
var availablePresetIds = [];
for (var _i3 = 0, _Object$values2 = Object.values(presetData); _i3 < _Object$values2.length; _i3++) {
var preset = _Object$values2[_i3];
var commonNames = preset.commonNames,
bandCombination = preset.bandCombination;
// True if all required common names of preset exist in STAC object
var allBandsExist = commonNames === null || commonNames === void 0 ? void 0 : commonNames.every(function (commonName) {
return eoBands.some(function (eoBand) {
return eoBand.common_name === commonName;
});
});
if (allBandsExist || bandCombination === 'single') {
availablePresetIds.push(preset.id);
}
}
return availablePresetIds;
}
// TODO: would be better to have a generic here to relax the requirement that all input STACs have all extensions we list.
function getAssets(stac) {
// stac is an Item
if (stac.type === 'Feature') {
return stac.assets;
}
// stac is a Collection
// A STAC Collection optionally contains (if it includes the Item Assets Extension) an item_assets
// key that describes the assets included in every Item in the Collection.
return stac.item_assets;
}
/**
* Find usable assets in main assets object
* The `assets` object can point to many different objects, including original XML or JSON metadata,
* thumbnails, etc. These aren't usable as image data in Studio.
* @param stac STAC object
* @return asset mapping including only assets usable in Studio
*/
function getUsableAssets(stac) {
var allAssets = getAssets(stac);
var usableAssets = {};
for (var assetName in allAssets) {
var assetData = allAssets[assetName];
// We don't require data assets to have the "data" role, but if the asset has a non-data role,
// we exclude it
if (Array.isArray(assetData.roles) && assetData.roles.some(function (role) {
return ['thumbnail', 'overview', 'metadata', 'visual'].includes(role);
}) && !assetData.roles.includes('data')) {
continue;
}
// raster:bands array exists
if (assetData['raster:bands']) {
usableAssets[assetName] = assetData;
}
// TODO: Could also mark the asset as unusable if the asset has a non-GeoTIFF or COG Media Type
}
return usableAssets;
}
/**
* Get the max number of requests the TileLayer should be able to send at once
* With HTTP 1 under Chrome, you can make a max of 6 concurrent requests per domain, so this number
* should be 6 * number of domains tiles are loaded from.
* @param stac STAC object
* @return Number of permissible concurrent requests
*/
function getMaxRequests(rasterTileServerUrls) {
return (rasterTileServerUrls.length || 1) * 6;
}
/**
* Determine if two axis-aligned boxes intersect
* @param bbox1 axis-aligned box
* @param bbox2 axis-aligned box
* @return true if boxes intersect
*/
function bboxIntersects(bbox1, bbox2) {
if (!Array.isArray(bbox1) || bbox1.length !== 4 || !Array.isArray(bbox2) || bbox2.length !== 4) {
// Invalid input; can't make determination
return true;
}
return !(bbox2[0] > bbox1[2] || bbox2[2] < bbox1[0] || bbox2[3] < bbox1[1] || bbox2[1] > bbox1[3]);
}
/**
* Compute zRange of tiles in viewport.
* Derived from https://github.com/visgl/deck.gl/blob/8d824a4b836fee3bfebe6fc962e0f03d8c1dbd0d/modules/geo-layers/src/terrain-layer/terrain-layer.js#L173-L196
* @param tiles Array of tiles in current viewport
*/
function computeZRange(tiles) {
// Abort if no tiles visible
if (!tiles) {
return null;
}
// Since getTileData returns an object with either {terrain, images} in 3d mode or {images} in
// 2D mode, we can grab that object using tile.content.terrain, then grab the bounding box from
// the header.
var ranges = tiles.map(function (tile) {
var _tile$content;
return tile === null || tile === void 0 || (_tile$content = tile.content) === null || _tile$content === void 0 || (_tile$content = _tile$content.terrain) === null || _tile$content === void 0 || (_tile$content = _tile$content.header) === null || _tile$content === void 0 ? void 0 : _tile$content.boundingBox;
}).flatMap(function (bounds) {
return bounds ? [[bounds[0][2], bounds[1][2]]] : [];
});
if (ranges.length === 0) {
return null;
}
var minZ = Math.min.apply(Math, (0, _toConsumableArray2["default"])(ranges.map(function (x) {
return x[0];
})));
var maxZ = Math.max.apply(Math, (0, _toConsumableArray2["default"])(ranges.map(function (x) {
return x[1];
})));
return [minZ, maxZ];
}
/**
* Create RGBA bitmap array for categorical color scale from categorical color map
* @param categoricalOptions - color map configuration and min-max values of categorical band
* @returns typed array with bitmap data
*/
function generateCategoricalBitmapArray(categoricalOptions) {
var colorMap = categoricalOptions.colorMap,
_categoricalOptions$m = categoricalOptions.minValue,
minValue = _categoricalOptions$m === void 0 ? 0 : _categoricalOptions$m,
_categoricalOptions$m2 = categoricalOptions.maxValue,
maxValue = _categoricalOptions$m2 === void 0 ? CATEGORICAL_TEXTURE_WIDTH - 1 : _categoricalOptions$m2;
if (!colorMap) {
return null;
}
var colorScaleMaxValue = maxValue - minValue;
var step = CATEGORICAL_TEXTURE_WIDTH / (colorScaleMaxValue + 1);
var data = new Uint8Array(CATEGORICAL_TEXTURE_WIDTH * 4);
var _iterator3 = _createForOfIteratorHelper(colorMap),
_step3;
try {
for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
var _step3$value = (0, _slicedToArray2["default"])(_step3.value, 2),
value = _step3$value[0],
color = _step3$value[1];
if (typeof value !== 'number') {
continue;
}
var rgb = (0, _utils.hexToRgb)(color);
var minPixel = Math.floor(value * step);
var maxPixel = Math.floor((value + 1) * step - 1);
for (var i = minPixel; i <= maxPixel; i++) {
data.set([].concat((0, _toConsumableArray2["default"])(rgb), [CATEGORICAL_TEXTURE_WIDTH - 1]), i * 4);
}
}
} catch (err) {
_iterator3.e(err);
} finally {
_iterator3.f();
}
return data;
}
function timeRangeToStacTemporalInterval(stac, startDate, endDate) {
var _stac$extent;
// TODO support multiple temporal intervals
var interval = (_stac$extent = stac.extent) === null || _stac$extent === void 0 || (_stac$extent = _stac$extent.temporal) === null || _stac$extent === void 0 || (_stac$extent = _stac$extent.interval) === null || _stac$extent === void 0 ? void 0 : _stac$extent[0];
if (!interval || !interval[0]) return {
startDate: startDate,
endDate: endDate
};
var collectionStart = interval[0] ? new Date(interval[0]) : new Date();
var collectionEnd = interval[1] ? new Date(interval[1]) : new Date();
var layerStart = new Date(startDate);
var layerEnd = new Date(endDate);
var clippedStart = layerStart < collectionStart || layerStart > collectionEnd ? collectionStart : layerStart;
var clippedEnd = layerEnd > collectionEnd ? collectionEnd : layerEnd;
if (clippedStart > clippedEnd) {
clippedEnd = new Date(clippedStart);
clippedEnd.setDate(clippedEnd.getDate() + 1);
}
return {
startDate: clippedStart.toISOString().split('T')[0],
endDate: clippedEnd.toISOString().split('T')[0]
};
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29yZSIsInJlcXVpcmUiLCJfdXRpbHMiLCJfY29uZmlnIiwiX3R5cGVzIiwiX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIiLCJyIiwiZSIsInQiLCJTeW1ib2wiLCJpdGVyYXRvciIsIkFycmF5IiwiaXNBcnJheSIsIl91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSIsImxlbmd0aCIsIl9uIiwiRiIsInMiLCJuIiwiZG9uZSIsInZhbHVlIiwiZiIsIlR5cGVFcnJvciIsIm8iLCJhIiwidSIsImNhbGwiLCJuZXh0IiwiX2FycmF5TGlrZVRvQXJyYXkiLCJ0b1N0cmluZyIsInNsaWNlIiwiY29uc3RydWN0b3IiLCJuYW1lIiwiZnJvbSIsInRlc3QiLCJDQVRFR09SSUNBTF9URVhUVVJFX1dJRFRIIiwiZXhwb3J0cyIsImlzQ29sb3JtYXBBbGxvd2VkIiwiYmFuZENvbWJpbmF0aW9uIiwiQmFuZENvbWJpbmF0aW9uIiwiUmdiIiwiaXNSZXNjYWxpbmdBbGxvd2VkIiwiU2luZ2xlIiwiaXNGaWx0ZXJBbGxvd2VkIiwiZHR5cGVNYXhWYWx1ZSIsInVpbnQ4IiwiTWF0aCIsInBvdyIsInVpbnQxNiIsInVpbnQzMiIsInVpbnQ2NCIsImludDgiLCJpbnQxNiIsImludDMyIiwiaW50NjQiLCJmbG9hdDE2IiwiZmxvYXQzMiIsImZsb2F0NjQiLCJjaW50MTYiLCJjaW50MzIiLCJjZmxvYXQzMiIsImNmbG9hdDY0Iiwib3RoZXIiLCJpc1NlYXJjaGFibGVTdGFjIiwic3RhYyIsInR5cGUiLCJnZXRab29tUmFuZ2UiLCJaT09NX1JBTkdFUyIsImlkIiwiZ2V0RGF0YVR5cGUiLCJ1c2FibGVBc3NldHMiLCJ0eXBlc1RvQ2hlY2siLCJkYXRhVHlwZXMiLCJTZXQiLCJhc3NldE5hbWUiLCJfYXNzZXQkcmFzdGVyQmFuZHMiLCJhc3NldCIsImluY2x1ZGVzIiwiZm9yRWFjaCIsImJhbmQiLCJkYXRhX3R5cGUiLCJhZGQiLCJzaXplIiwiZ2V0UGl4ZWxSYW5nZSIsImR0eXBlIiwiX3JlZiIsIl9yZWYyIiwiX3NsaWNlZFRvQXJyYXkyIiwibWluUmFzdGVyU3RhdHNWYWx1ZSIsIm1heFJhc3RlclN0YXRzVmFsdWUiLCJtaW5QaXhlbFZhbHVlIiwibWF4UGl4ZWxWYWx1ZSIsIk51bWJlciIsImlzRmluaXRlIiwiZ2V0SW1hZ2VNaW5NYXgiLCJpbWFnZURhdGEiLCJtaW4iLCJJbmZpbml0eSIsIm1heCIsIl9pdGVyYXRvciIsIl9zdGVwIiwiaXNOYU4iLCJlcnIiLCJnZXRCYW5kSWRzRm9yQ29tbW9uTmFtZXMiLCJjb21tb25OYW1lcyIsImFzc2V0SWRzIiwiYmFuZEluZGV4ZXMiLCJfaXRlcmF0b3IyIiwiX3N0ZXAyIiwiY29tbW9uTmFtZSIsInJlc3VsdCIsImZpbmRBc3NldFdpdGhOYW1lIiwiX3Jlc3VsdCIsImJhbmRJbmRleCIsInB1c2giLCJfY29uc29saWRhdGVCYW5kSW5kZXgiLCJjb25zb2xpZGF0ZUJhbmRJbmRleGVzIiwibG9hZEFzc2V0SWRzIiwibG9hZEJhbmRJbmRleGVzIiwicmVuZGVyQmFuZEluZGV4ZXMiLCJfYXNzZXQkZW9CYW5kcyIsImdldEFzc2V0cyIsIm1hcCIsIl8iLCJpZHgiLCJhc3NldHMiLCJwcm9wZXJ0eSIsIl9pIiwiX09iamVjdCRlbnRyaWVzIiwiT2JqZWN0IiwiZW50cmllcyIsIl9PYmplY3QkZW50cmllcyRfaSIsImFzc2V0RGF0YSIsImVvQmFuZHMiLCJpIiwiZW9CYW5kIiwiZ2V0U2luZ2xlQmFuZEluZm8iLCJzaW5nbGVCYW5kSW5mbyIsIl9zaW5nbGVCYW5kSW5mbyIsImFzc2V0SWQiLCJrZXlzIiwiX3NpbmdsZUJhbmRJbmZvMiIsImdldFJhc3RlclN0YXRpc3RpY3NNaW5NYXgiLCJwcmVzZXRJZCIsInVuZGVmaW5lZCIsIm1pbkNhdGVnb3JpY2FsQmFuZFZhbHVlIiwibWF4Q2F0ZWdvcmljYWxCYW5kVmFsdWUiLCJnZXRVc2FibGVBc3NldHMiLCJiYW5kTWV0YWRhdGEiLCJfYmFuZE1ldGFkYXRhJHJhc3RlciIsIl9iYW5kTWV0YWRhdGEkcmFzdGVyMiIsInN0YXRpc3RpY3MiLCJtaW5pbXVtIiwibWF4aW11bSIsImdldFNpbmdsZUJhbmRQcmVzZXRPcHRpb25zIiwic2luZ2xlQmFuZE5hbWUiLCJzaW5nbGVCYW5kIiwiX2Fzc2V0RGF0YSIsIl9hc3NldERhdGEyIiwiZ2V0TWluTWF4RnJvbVRpbGUyREhlYWRlcnMiLCJ0aWxlcyIsIm1pblZhbHVlcyIsInRpbGUiLCJkYXRhIiwiZmlsdGVyIiwibWF4VmFsdWVzIiwiYXBwbHkiLCJfdG9Db25zdW1hYmxlQXJyYXkyIiwiZ2V0RGF0YVNvdXJjZVBhcmFtcyIsInByZXNldE9wdGlvbnMiLCJfUFJFU0VUX09QVElPTlMkcHJlc2UiLCJQUkVTRVRfT1BUSU9OUyIsIl9nZXRab29tUmFuZ2UiLCJfZ2V0Wm9vbVJhbmdlMiIsIm1pblpvb20iLCJtYXhab29tIiwiYmFuZEluZm8iLCJfZ2V0UmFzdGVyU3RhdGlzdGljc00iLCJfZ2V0UmFzdGVyU3RhdGlzdGljc00yIiwicGl4ZWxSYW5nZSIsIl9waXhlbFJhbmdlIiwiX2JhbmRJbmZvIiwiZ2xvYmFsQm91bmRzIiwiZ2V0U1RBQ0JvdW5kcyIsImRhdGFUeXBlIiwiYmJveCIsImV4dGVudCIsInNwYXRpYWwiLCJnZXRFT0JhbmRzIiwiX2kyIiwiX09iamVjdCR2YWx1ZXMiLCJ2YWx1ZXMiLCJhc3NldEJhbmRzIiwiZmlsdGVyQXZhaWxhYmxlUHJlc2V0cyIsInByZXNldERhdGEiLCJhdmFpbGFibGVQcmVzZXRJZHMiLCJfaTMiLCJfT2JqZWN0JHZhbHVlczIiLCJwcmVzZXQiLCJhbGxCYW5kc0V4aXN0IiwiZXZlcnkiLCJzb21lIiwiY29tbW9uX25hbWUiLCJpdGVtX2Fzc2V0cyIsImFsbEFzc2V0cyIsInJvbGVzIiwicm9sZSIsImdldE1heFJlcXVlc3RzIiwicmFzdGVyVGlsZVNlcnZlclVybHMiLCJiYm94SW50ZXJzZWN0cyIsImJib3gxIiwiYmJveDIiLCJjb21wdXRlWlJhbmdlIiwicmFuZ2VzIiwiX3RpbGUkY29udGVudCIsImNvbnRlbnQiLCJ0ZXJyYWluIiwiaGVhZGVyIiwiYm91bmRpbmdCb3giLCJmbGF0TWFwIiwiYm91bmRzIiwibWluWiIsIngiLCJtYXhaIiwiZ2VuZXJhdGVDYXRlZ29yaWNhbEJpdG1hcEFycmF5IiwiY2F0ZWdvcmljYWxPcHRpb25zIiwiY29sb3JNYXAiLCJfY2F0ZWdvcmljYWxPcHRpb25zJG0iLCJtaW5WYWx1ZSIsIl9jYXRlZ29yaWNhbE9wdGlvbnMkbTIiLCJtYXhWYWx1ZSIsImNvbG9yU2NhbGVNYXhWYWx1ZSIsInN0ZXAiLCJVaW50OEFycmF5IiwiX2l0ZXJhdG9yMyIsIl9zdGVwMyIsIl9zdGVwMyR2YWx1ZSIsImNvbG9yIiwicmdiIiwiaGV4VG9SZ2IiLCJtaW5QaXhlbCIsImZsb29yIiwibWF4UGl4ZWwiLCJzZXQiLCJjb25jYXQiLCJ0aW1lUmFuZ2VUb1N0YWNUZW1wb3JhbEludGVydmFsIiwic3RhcnREYXRlIiwiZW5kRGF0ZSIsIl9zdGFjJGV4dGVudCIsImludGVydmFsIiwidGVtcG9yYWwiLCJjb2xsZWN0aW9uU3RhcnQiLCJEYXRlIiwiY29sbGVjdGlvbkVuZCIsImxheWVyU3RhcnQiLCJsYXllckVuZCIsImNsaXBwZWRTdGFydCIsImNsaXBwZWRFbmQiLCJzZXREYXRlIiwiZ2V0RGF0ZSIsInRvSVNPU3RyaW5nIiwic3BsaXQiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvcmFzdGVyLXRpbGUvcmFzdGVyLXRpbGUtdXRpbHMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuLyoqXG4gKiBVdGlsaXR5IGZ1bmN0aW9ucyBhbmQgY29uc3RhbnRzIGZvciBwcm9jZXNzaW5nIFNUQUMgbWV0YWRhdGEgYW5kIG90aGVyIHJhc3RlciB0aWxlIGRhdGFcbiAqL1xuXG5pbXBvcnQge1R5cGVkQXJyYXl9IGZyb20gJ0Bsb2FkZXJzLmdsL2xvYWRlci11dGlscy9zcmMvdHlwZXMnO1xuaW1wb3J0IHtpc0FycmF5fSBmcm9tICdAbWF0aC5nbC9jb3JlJztcblxuaW1wb3J0IHtTdGFjVHlwZXN9IGZyb20gJ0BrZXBsZXIuZ2wvdHlwZXMnO1xuaW1wb3J0IHtoZXhUb1JnYn0gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5cbmltcG9ydCB7UFJFU0VUX09QVElPTlMsIFpPT01fUkFOR0VTfSBmcm9tICcuL2NvbmZpZyc7XG5pbXBvcnQge1xuICBEYXRhU291cmNlUGFyYW1zLFxuICBQcmVzZXREYXRhLFxuICBDb21wbGV0ZVNUQUNPYmplY3QsXG4gIENvbXBsZXRlU1RBQ0Fzc2V0TGlua3MsXG4gIEFzc2V0SWRzLFxuICBCYW5kSW5kZXhlcyxcbiAgUmVuZGVyQmFuZEluZGV4ZXMsXG4gIFRpbGUyREhlYWRlcixcbiAgQXNzZXRSZXF1ZXN0SW5mbyxcbiAgUHJlc2V0T3B0aW9uLFxuICBCYW5kQ29tYmluYXRpb24sXG4gIENhdGVnb3JpY2FsQ29sb3JtYXBPcHRpb25zXG59IGZyb20gJy4vdHlwZXMnO1xuXG50eXBlIEl0ZW0gPSBTdGFjVHlwZXMuU1RBQ0l0ZW07XG50eXBlIENvbGxlY3Rpb24gPSBTdGFjVHlwZXMuU1RBQ0NvbGxlY3Rpb247XG50eXBlIEVPQmFuZCA9IFN0YWNUeXBlcy5CYW5kO1xudHlwZSBEYXRhVHlwZU9mVGhlQmFuZCA9IFN0YWNUeXBlcy5EYXRhVHlwZU9mVGhlQmFuZDtcblxuZXhwb3J0IGNvbnN0IENBVEVHT1JJQ0FMX1RFWFRVUkVfV0lEVEggPSAyNTY7XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0NvbG9ybWFwQWxsb3dlZChiYW5kQ29tYmluYXRpb246IEJhbmRDb21iaW5hdGlvbik6IGJvb2xlYW4ge1xuICByZXR1cm4gYmFuZENvbWJpbmF0aW9uICE9PSBCYW5kQ29tYmluYXRpb24uUmdiO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNSZXNjYWxpbmdBbGxvd2VkKGJhbmRDb21iaW5hdGlvbjogQmFuZENvbWJpbmF0aW9uKTogYm9vbGVhbiB7XG4gIHJldHVybiBiYW5kQ29tYmluYXRpb24gPT09IEJhbmRDb21iaW5hdGlvbi5SZ2IgfHwgYmFuZENvbWJpbmF0aW9uID09PSBCYW5kQ29tYmluYXRpb24uU2luZ2xlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNGaWx0ZXJBbGxvd2VkKGJhbmRDb21iaW5hdGlvbjogQmFuZENvbWJpbmF0aW9uKTogYm9vbGVhbiB7XG4gIHJldHVybiBiYW5kQ29tYmluYXRpb24gIT09IEJhbmRDb21iaW5hdGlvbi5SZ2I7XG59XG5cbi8qKlxuICogTWF4IHZhbHVlIGZvciBkYXRhIHR5cGVcbiAqXG4gKiBWYWx1ZXMgb2YgbnVsbCBtaWdodCBiZSBjYWxjdWxhdGVkIGluIHJ1bnRpbWVcbiAqL1xuZXhwb3J0IGNvbnN0IGR0eXBlTWF4VmFsdWU6IFJlY29yZDxEYXRhVHlwZU9mVGhlQmFuZCwgbnVtYmVyIHwgbnVsbD4gPSB7XG4gIHVpbnQ4OiBNYXRoLnBvdygyLCA4KSAtIDEsXG4gIHVpbnQxNjogTWF0aC5wb3coMiwgMTYpIC0gMSxcbiAgdWludDMyOiBNYXRoLnBvdygyLCAzMikgLSAxLFxuICB1aW50NjQ6IG51bGwsXG4gIGludDg6IE1hdGgucG93KDIsIDcpIC0gMSxcbiAgaW50MTY6IE1hdGgucG93KDIsIDE1KSAtIDEsXG4gIGludDMyOiBNYXRoLnBvdygyLCAzMSkgLSAxLFxuICBpbnQ2NDogbnVsbCxcbiAgZmxvYXQxNjogbnVsbCxcbiAgZmxvYXQzMjogbnVsbCxcbiAgZmxvYXQ2NDogbnVsbCxcbiAgY2ludDE2OiBudWxsLFxuICBjaW50MzI6IG51bGwsXG4gIGNmbG9hdDMyOiBudWxsLFxuICBjZmxvYXQ2NDogbnVsbCxcbiAgb3RoZXI6IG51bGxcbn07XG5cbi8qKlxuICogSXMgdGhpcyBhIFNUQUMgQ29sbGVjdGlvbiB0aGF0IHN1cHBvcnRzIGN1c3RvbSBzZWFyY2hpbmdcbiAqIFRPRE86IGN1cnJlbnRseSB0aGlzIGlzIGEgY3VzdG9tIGhhY2sgdG8gc3VwcG9ydCBTZW50aW5lbC4gSXQgd2lsbCBuZWVkIHRvIGJlIGdlbmVyYWxpemVkIGJlZm9yZSBiZWluZyBhY2Nlc3NpYmxlXG4gKiBAcGFyYW0gc3RhYyAgU1RBQyBvYmplY3RcbiAqIEByZXR1cm4gSWYgVHJ1ZSwgc3VwcG9ydHMgc2VhcmNoaW5nXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc1NlYXJjaGFibGVTdGFjKHN0YWM6IENvbXBsZXRlU1RBQ09iamVjdCk6IGJvb2xlYW4ge1xuICByZXR1cm4gc3RhYy50eXBlID09PSAnQ29sbGVjdGlvbic7XG5cbiAgLy8gcmV0dXJuIHN0YWMudHlwZSAhPT0gJ0ZlYXR1cmUnICYmIHN0YWM/LnByb3ZpZGVycy5zb21lKFxuICAvLyAgIHByb3ZpZGVyID0+XG4gIC8vICAgICBwcm92aWRlci5uYW1lLnRvTG93ZXJDYXNlKCkgPT09ICdtaWNyb3NvZnQnICYmXG4gIC8vICAgICBwcm92aWRlci51cmwgPT09ICdodHRwczovL3BsYW5ldGFyeWNvbXB1dGVyLm1pY3Jvc29mdC5jb20nXG4gIC8vICk7XG59XG5cbmZ1bmN0aW9uIGdldFpvb21SYW5nZShzdGFjOiBDb21wbGV0ZVNUQUNPYmplY3QpOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgaWYgKFpPT01fUkFOR0VTW3N0YWMuaWRdKSB7XG4gICAgcmV0dXJuIFpPT01fUkFOR0VTW3N0YWMuaWRdO1xuICB9XG5cbiAgLy8gRm9yIGEgc2luZ2xlIENPRywgaGF2aW5nIGEgZnVsbCB6b29tIHJhbmdlIGlzbid0IHJlYWxseSBhIHByb2JsZW0uXG4gIC8vIHRoZSAvY29nL2luZm8gZW5kcG9pbnQgZG9lc24ndCBkZXNjcmliZSB6b29tIGxldmVscyBiZWNhdXNlIGl0IGRvZXNuJ3Qga25vdyB0aGUgcHJvamVjdGlvbiB0byBzZXJ2ZSB0aGUgaW1hZ2UgaW4uXG4gIC8vIERlZmF1bHQgbWluem9vbSwgbWF4em9vbTogWzAsIDIwXVxuICByZXR1cm4gWzAsIDIwXTtcbn1cblxuLyoqXG4gKiBJbmZlciBkYXRhIHR5cGUgZnJvbSBTVEFDIGl0ZW1cbiAqIFRoaXMgdXNlcyB0aGUgYHJhc3RlcmAgZXh0ZW5zaW9uLCB3aGljaCBpcyBub3QgeWV0IHZlcnkgY29tbW9uXG4gKiBAcGFyYW0gc3RhYyBzdGFjIG9iamVjdFxuICogQHJldHVybiBEYXRhIHR5cGUgb2YgdGhlIGJhbmRcbiAqL1xuZnVuY3Rpb24gZ2V0RGF0YVR5cGUoXG4gIHVzYWJsZUFzc2V0czogQ29tcGxldGVTVEFDQXNzZXRMaW5rcyxcbiAgdHlwZXNUb0NoZWNrPzogc3RyaW5nW11cbik6IERhdGFUeXBlT2ZUaGVCYW5kIHwgbnVsbCB7XG4gIGNvbnN0IGRhdGFUeXBlcyA9IG5ldyBTZXQ8RGF0YVR5cGVPZlRoZUJhbmQ+KCk7XG4gIGZvciAoY29uc3QgYXNzZXROYW1lIGluIHVzYWJsZUFzc2V0cykge1xuICAgIGNvbnN0IGFzc2V0ID0gdXNhYmxlQXNzZXRzW2Fzc2V0TmFtZV07XG4gICAgaWYgKCFhc3NldCkge1xuICAgICAgY29udGludWU7XG4gICAgfVxuICAgIGlmICh0eXBlc1RvQ2hlY2sgJiYgIXR5cGVzVG9DaGVjay5pbmNsdWRlcyhhc3NldE5hbWUpKSB7XG4gICAgICBjb250aW51ZTtcbiAgICB9XG5cbiAgICBhc3NldFsncmFzdGVyOmJhbmRzJ10/LmZvckVhY2goYmFuZCA9PiBiYW5kLmRhdGFfdHlwZSAmJiBkYXRhVHlwZXMuYWRkKGJhbmQuZGF0YV90eXBlKSk7XG4gIH1cblxuICAvLyBUT0RPOiBzdXBwb3J0IG11bHRpcGxlIGRhdGEgdHlwZXMgYWNyb3NzIGFzc2V0c1xuICBpZiAoZGF0YVR5cGVzLnNpemUgIT09IDEpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHJldHVybiBBcnJheS5mcm9tKGRhdGFUeXBlcylbMF07XG59XG5cbi8qKlxuICogR2V0IG1pbiBhbmQgbWF4IHZhbHVlcyBvZiB0aGUgYmFuZCBmcm9tIHRoZSBiYW5kJ3MgZGF0YSB0eXBlIHZhbHVlcyByYW5nZVxuICogQHBhcmFtIHN0YWMgLSBzdGFjIG1ldGFkYXRhIG9iamVjdFxuICogQHBhcmFtIGR0eXBlIC0gZGF0YSB0eXBlIG9mIHRoZSByYXN0ZXIgYmFuZFxuICogQHJldHVybnMgbWluIGFuZCBtYXggdmFsdWVzIGZvciB0aGUgYmFuZFxuICovXG5mdW5jdGlvbiBnZXRQaXhlbFJhbmdlKFxuICBzdGFjOiBDb21wbGV0ZVNUQUNPYmplY3QsXG4gIGR0eXBlOiBEYXRhVHlwZU9mVGhlQmFuZCB8IG51bGwsXG4gIFttaW5SYXN0ZXJTdGF0c1ZhbHVlLCBtYXhSYXN0ZXJTdGF0c1ZhbHVlXTogW251bWJlciB8IHVuZGVmaW5lZCwgbnVtYmVyIHwgdW5kZWZpbmVkXVxuKTogW251bWJlciwgbnVtYmVyXSB8IG51bGwge1xuICBpZiAoIWR0eXBlKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBUT0RPOiBtaWdodCBub3QgYWx3YXlzIGJlIGRlc2lyZWQgdG8gbGVhdmUgbWluIHBpeGVsIHZhbHVlIGF0IDBcbiAgY29uc3QgbWluUGl4ZWxWYWx1ZSA9IDA7XG4gIGNvbnN0IG1heFBpeGVsVmFsdWUgPSBkdHlwZU1heFZhbHVlW2R0eXBlXTtcblxuICAvLyBUT0RPIGNoZWNrIGlmIHRoaXMgZWFybHkgcmV0dXJuIGlzIGV4cGVjdGVkXG4gIGlmICghbWF4UGl4ZWxWYWx1ZSkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKFxuICAgICFOdW1iZXIuaXNGaW5pdGUobWF4UGl4ZWxWYWx1ZSkgJiZcbiAgICB0eXBlb2YgbWluUmFzdGVyU3RhdHNWYWx1ZSA9PT0gJ251bWJlcicgJiZcbiAgICB0eXBlb2YgbWF4UmFzdGVyU3RhdHNWYWx1ZSA9PT0gJ251bWJlcidcbiAgKSB7XG4gICAgcmV0dXJuIFttaW5SYXN0ZXJTdGF0c1ZhbHVlLCBtYXhSYXN0ZXJTdGF0c1ZhbHVlXTtcbiAgfVxuXG4gIHJldHVybiBbbWluUGl4ZWxWYWx1ZSwgbWF4UGl4ZWxWYWx1ZV07XG59XG5cbi8qKlxuICogRmluZCBtaW4gYW5kIG1heCB2YWx1ZXMgdGhyb3VnaG91dCBhIHJhc3RlciBiYW5kIGltYWdlXG4gKiBAcGFyYW0gaW1hZ2VEYXRhIHJhc3RlciBiYW5kIGltYWdlIGRhdGFcbiAqIEByZXR1cm5zIG1pbiBhbmQgbWF4IHZhbHVlcyB0aHJvdWdob3V0IHRoZSBiYW5kXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRJbWFnZU1pbk1heChpbWFnZURhdGE6IFR5cGVkQXJyYXkpOiBbbnVtYmVyIHwgbnVsbCwgbnVtYmVyIHwgbnVsbF0ge1xuICAvLyBXZSBjYW5ub3QgY2FsY3VsYXRlIG1pbi9tYXggZm9yIEltYWdlL0ltYWdlQml0bWFwXG4gIGlmICghaXNBcnJheShpbWFnZURhdGEpKSB7XG4gICAgcmV0dXJuIFtudWxsLCBudWxsXTtcbiAgfVxuICBsZXQgbWluID0gSW5maW5pdHk7XG4gIGxldCBtYXggPSAtSW5maW5pdHk7XG4gIGZvciAoY29uc3QgdmFsdWUgb2YgaW1hZ2VEYXRhKSB7XG4gICAgaWYgKCFpc05hTih2YWx1ZSkpIHtcbiAgICAgIG1pbiA9IE1hdGgubWluKG1pbiwgdmFsdWUpO1xuICAgICAgbWF4ID0gTWF0aC5tYXgobWF4LCB2YWx1ZSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBbbWluLCBtYXhdO1xufVxuXG4vKipcbiAqIEZpbmQgYXNzZXQgbmFtZXMgYW5kIGJhbmQgaW5kZXhlcyBmb3IgZ2l2ZW4gY29tbW9uTmFtZXNcbiAqIFJpZ2h0IG5vdyBpbiBLZXBsZXIsIG91ciBhdmFpbGFibGUgbWV0aG9kcyBvZiByZW5kZXJpbmcgcmFzdGVyIGRhdGEgYXJlIHF1aXRlIHNpbXBsZS4gV2Ugb25seSBhbGxvdyBhIHVzZXIgdG8gY2hvb3NlIGFtb25nIGBwcmVzZXRzYCwgYW5kIHVuZGVyIHRoZSBob29kIHdlIHNlbGVjdCB3aGljaCBkYXRhIGZpbGVzIHRvIGxvYWQuXG4gKiBAcGFyYW0gdXNhYmxlQXNzZXRzIFtzdGFjIGRlc2NyaXB0aW9uXVxuICogQHBhcmFtIGNvbW1vbk5hbWVzICBhbiBhcnJheSBvZiBlbzpjb21tb25fbmFtZSB0byBzZWFyY2ggd2l0aGluIGJhbmRzXG4gKiBAcmV0dXJuIEJhbmQgaW5mb3JtYXRpb24gb3IgbnVsbCBpZiBub3QgYWxsIGJhbmRzIGV4aXN0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRCYW5kSWRzRm9yQ29tbW9uTmFtZXMoXG4gIHN0YWM6IENvbXBsZXRlU1RBQ09iamVjdCxcbiAgdXNhYmxlQXNzZXRzOiBDb21wbGV0ZVNUQUNBc3NldExpbmtzLFxuICBjb21tb25OYW1lczogc3RyaW5nW11cbik6IEFzc2V0UmVxdWVzdEluZm8gfCBudWxsIHtcbiAgLy8gQW4gYXJyYXkgb2Ygc3RyaW5ncyBkZXNjcmliaW5nIGFzc2V0IGlkZW50aWZpZXJzLCBlLmcuIHRoZSBcImtleVwiIGluIHRoZSBhc3NldHMgb2JqZWN0XG4gIGNvbnN0IGFzc2V0SWRzOiBBc3NldElkcyA9IFtdO1xuICAvLyBBbiBhcnJheSBvZiBpbnRlZ2VycyBnaXZpbmcgdGhlIGluZGV4IG9mIHRoZSBiYW5kIHdpdGhpbiB0aGF0IGFzc2V0J3MgZGF0YVxuICAvLyBJbiBnZW5lcmFsLCBkYXRhIHNvdXJjZXMgZWl0aGVyIGhhdmUgYSBzaW5nbGUgYmFuZCBwZXIgYXNzZXQgb3IgYWxsIGFzc2V0cyBpbiBhIHNpbmdsZSBiYW5kXG4gIGNvbnN0IGJhbmRJbmRleGVzOiBCYW5kSW5kZXhlcyA9IFtdO1xuXG4gIGZvciAoY29uc3QgY29tbW9uTmFtZSBvZiBjb21tb25OYW1lcykge1xuICAgIC8vIEZpbmQgYXNzZXQgdGhhdCBpbmNsdWRlcyBhIGJhbmQgd2l0aCB0aGlzIGNvbW1vbiBuYW1lXG4gICAgY29uc3QgcmVzdWx0ID0gZmluZEFzc2V0V2l0aE5hbWUodXNhYmxlQXNzZXRzLCBjb21tb25OYW1lLCAnY29tbW9uX25hbWUnKTtcbiAgICBpZiAocmVzdWx0KSB7XG4gICAgICBjb25zdCBbYXNzZXROYW1lLCBiYW5kSW5kZXhdID0gcmVzdWx0O1xuICAgICAgYXNzZXRJZHMucHVzaChhc3NldE5hbWUpO1xuICAgICAgYmFuZEluZGV4ZXMucHVzaChiYW5kSW5kZXgpO1xuICAgIH1cbiAgfVxuXG4gIGlmIChhc3NldElkcy5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IHtsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVuZGVyQmFuZEluZGV4ZXN9ID0gY29uc29saWRhdGVCYW5kSW5kZXhlcyhcbiAgICBzdGFjLFxuICAgIGFzc2V0SWRzLFxuICAgIGJhbmRJbmRleGVzXG4gICk7XG5cbiAgcmV0dXJuIHtsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVuZGVyQmFuZEluZGV4ZXN9O1xufVxuXG4vKipcbiAqIENvbnNvbGlkYXRlIGFzc2V0L2JhbmQgaW5mbyBpbnRvIGxvYWQgaW5mbyBhbmQgcmVuZGVyIGluZm9cbiAqIEBwYXJhbSBzdGFjIFNUQUMgb2JqZWN0XG4gKiBAcGFyYW0gYXNzZXRJZHNcbiAqIEBwYXJhbSBiYW5kSW5kZXhlc1xuICogQHJldHVybiBBc3NldCBpbmZvcm1hdGlvblxuICovXG5mdW5jdGlvbiBjb25zb2xpZGF0ZUJhbmRJbmRleGVzKFxuICBzdGFjOiBDb21wbGV0ZVNUQUNPYmplY3QsXG4gIGFzc2V0SWRzOiBBc3NldElkcyxcbiAgYmFuZEluZGV4ZXM6IEJhbmRJbmRleGVzXG4pOiBBc3NldFJlcXVlc3RJbmZvIHtcbiAgbGV0IGxvYWRBc3NldElkczogQXNzZXRJZHM7XG4gIGxldCBsb2FkQmFuZEluZGV4ZXM6IEJhbmRJbmRleGVzO1xuICBsZXQgcmVuZGVyQmFuZEluZGV4ZXM6IFJlbmRlckJhbmRJbmRleGVzO1xuXG4gIC8vIEFsbCBiYW5kcyB3ZSB3YW50IHRvIGxvYWQgYXJlIGluIGEgc2luZ2xlIGFzc2V0XG4gIGlmIChhc3NldElkcy5sZW5ndGggPT09IDEgfHwgQXJyYXkuZnJvbShuZXcgU2V0KGFzc2V0SWRzKSkubGVuZ3RoID09PSAxKSB7XG4gICAgbG9hZEFzc2V0SWRzID0gW2Fzc2V0SWRzWzBdXTtcblxuICAgIGNvbnN0IGFzc2V0ID0gZ2V0QXNzZXRzKHN0YWMpW2xvYWRBc3NldElkc1swXV07XG5cbiAgICAvLyBSZXF1ZXN0IGFsbCBiYW5kcyBpbiBvcmRlciwgdGhlbiByZW9yZGVyIHdpdGggcmVuZGVyQmFuZEluZGV4ZXNcbiAgICAvLyBOT1RFOiB1c3VhbGx5IChlLmcuIHdpdGggTkFJUCkgaWYgYWxsIGJhbmRzIGFyZSBpbiBhIHNpbmdsZSBhc3NldCwgdGhlbiB0aGVyZSBhcmUgb25seSB+NFxuICAgIC8vIGJhbmRzLiBUaGlzIGFwcHJvYWNoIG9mIHJlcXVlc3RpbmcgYWxsIGJhbmRzIG1heSBiZSBsZXNzIGVmZmljaWVudCB3aXRoIG1vcmUgdGhhbiA0IGJhbmRzXG4gICAgLy8gKGJlY2F1c2Ugb2YgbGFyZ2VyIGRvd25sb2FkIHNpemVzKS4gSW4gdGhlIGZ1dHVyZSBtYXkgd2FudCBzZXBhcmF0ZSBsb2FkaW5nIHBhdGhzIGlmIHdlXG4gICAgLy8gZW5jb3VudGVyIHNpbmdsZSBhc3NldCBvYmplY3RzIHdpdGggPiA0IGJhbmRzLlxuXG4gICAgLy8gVE9ETzogZW86YmFuZHMgY2FuIGJlIF9laXRoZXJfIG9uIGVhY2ggYXNzZXQgX29yXyBvbiB0aGUgU1RBQydzIHByb3BlcnRpZXMsIHdoaWNoIGlzIG5vdCBjdXJyZW50bHkgaGFuZGxlZFxuICAgIGxvYWRCYW5kSW5kZXhlcyA9IChhc3NldFsnZW86YmFuZHMnXSBhcyBFT0JhbmRbXSk/Lm1hcCgoXywgaWR4KSA9PiBpZHgpO1xuICAgIHJlbmRlckJhbmRJbmRleGVzID0gYmFuZEluZGV4ZXM7XG4gIH0gZWxzZSB7XG4gICAgbG9hZEFzc2V0SWRzID0gYXNzZXRJZHM7XG4gICAgbG9hZEJhbmRJbmRleGVzID0gYmFuZEluZGV4ZXM7XG4gICAgcmVuZGVyQmFuZEluZGV4ZXMgPSBudWxsO1xuICB9XG5cbiAgcmV0dXJuIHtsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVuZGVyQmFuZEluZGV4ZXN9O1xufVxuXG4vKipcbiAqIEZpbmQgdGhlIEFzc2V0IGluIHRoZSBTVEFDIG9iamVjdCB0aGF0IGNvbnRhaW5pbmcgYW4gZW86YmFuZCB3aXRoIHRoZSBkZXNpcmVkIGNvbW1vbl9uYW1lIG9yIG5hbWVcbiAqIEBwYXJhbSBhc3NldHMgYXNzZXRzIG9iamVjdC4gS2V5cyBzaG91bGQgYmUgYXNzZXQgbmFtZSBhbmQgdmFsdWVzIHNob3VsZCBiZSBhc3NldCBkYXRhXG4gKiBAcGFyYW0gbmFtZSAgbmFtZSBvciBjb21tb25fbmFtZSBpbiBlbzpiYW5kc1xuICogQHBhcmFtIHByb3BlcnR5ICduYW1lJyBvciAnY29tbW9uX25hbWUnXG4gKiBAcmV0dXJuIG5hbWUgb2YgYXNzZXQgY29udGFpbmluZyBkZXNpcmVkIGJhbmQsIGluZGV4IG9mIGJhbmQgd2l0aGluIGFzc2V0XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmaW5kQXNzZXRXaXRoTmFtZShcbiAgYXNzZXRzOiBDb21wbGV0ZVNUQUNBc3NldExpbmtzLFxuICBuYW1lOiBzdHJpbmcsXG4gIHByb3BlcnR5OiAnbmFtZScgfCAnY29tbW9uX25hbWUnXG4pOiBbc3RyaW5nLCBudW1iZXJdIHwgbnVsbCB7XG4gIGZvciAoY29uc3QgW2Fzc2V0TmFtZSwgYXNzZXREYXRhXSBvZiBPYmplY3QuZW50cmllcyhhc3NldHMpKSB7XG4gICAgY29uc3QgZW9CYW5kcyA9IGFzc2V0RGF0YVsnZW86YmFuZHMnXSB8fCBbXTtcblxuICAgIC8vIFNlYXJjaCBiYW5kcyBhcnJheSBvZiB0aGlzIGFzc2V0XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBlb0JhbmRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBlb0JhbmQgPSBlb0JhbmRzW2ldO1xuICAgICAgaWYgKG5hbWUgPT09IGVvQmFuZFtwcm9wZXJ0eV0pIHtcbiAgICAgICAgcmV0dXJuIFthc3NldE5hbWUsIGldO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBudWxsO1xufVxuXG5mdW5jdGlvbiBnZXRTaW5nbGVCYW5kSW5mbyhcbiAgdXNhYmxlQXNzZXRzOiBDb21wbGV0ZVNUQUNBc3NldExpbmtzLFxuICBzaW5nbGVCYW5kSW5mbzogUHJlc2V0T3B0aW9uWydzaW5nbGVCYW5kJ11cbik6IEFzc2V0UmVxdWVzdEluZm8gfCBudWxsIHtcbiAgaWYgKCFzaW5nbGVCYW5kSW5mbz8uYXNzZXRJZCAmJiBPYmplY3Qua2V5cyh1c2FibGVBc3NldHMpLmxlbmd0aCA9PT0gMSkge1xuICAgIHNpbmdsZUJhbmRJbmZvID0ge1xuICAgICAgYXNzZXRJZDogT2JqZWN0LmtleXModXNhYmxlQXNzZXRzKVswXVxuICAgIH07XG4gIH1cblxuICBpZiAoIXNpbmdsZUJhbmRJbmZvKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICBjb25zdCB7YXNzZXRJZCwgYmFuZEluZGV4fSA9IHNpbmdsZUJhbmRJbmZvO1xuICBpZiAoIU9iamVjdC5rZXlzKHVzYWJsZUFzc2V0cykuaW5jbHVkZXMoYXNzZXRJZCkpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHJldHVybiB7XG4gICAgbG9hZEFzc2V0SWRzOiBbYXNzZXRJZF0sXG4gICAgbG9hZEJhbmRJbmRleGVzOiBiYW5kSW5kZXggPyBbYmFuZEluZGV4XSA6IFswXSxcbiAgICAvLyBObyByZW9yZGVyaW5nIG9mIGJhbmRzIG9uIHRoZSBHUFVcbiAgICByZW5kZXJCYW5kSW5kZXhlczogbnVsbFxuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0UmFzdGVyU3RhdGlzdGljc01pbk1heChcbiAgc3RhYzogQ29tcGxldGVTVEFDT2JqZWN0LFxuICBwcmVzZXRJZDogc3RyaW5nLFxuICBzaW5nbGVCYW5kSW5mbzogUHJlc2V0T3B0aW9uWydzaW5nbGVCYW5kJ11cbik6IFtudW1iZXIgfCB1bmRlZmluZWQsIG51bWJlciB8IHVuZGVmaW5lZF0ge1xuICBpZiAocHJlc2V0SWQgIT09ICdzaW5nbGVCYW5kJykge1xuICAgIHJldHVybiBbdW5kZWZpbmVkLCB1bmRlZmluZWRdO1xuICB9XG4gIGxldCBtaW5DYXRlZ29yaWNhbEJhbmRWYWx1ZTogbnVtYmVyIHwgdW5kZWZpbmVkO1xuICBsZXQgbWF4Q2F0ZWdvcmljYWxCYW5kVmFsdWU6IG51bWJlciB8IHVuZGVmaW5lZDtcbiAgY29uc3QgdXNhYmxlQXNzZXRzID0gZ2V0VXNhYmxlQXNzZXRzKHN0YWMpO1xuICBsZXQgYmFuZE1ldGFkYXRhID0gdXNhYmxlQXNzZXRzW3NpbmdsZUJhbmRJbmZvPy5hc3NldElkIHx8ICcnXTtcbiAgaWYgKCFiYW5kTWV0YWRhdGEpIHtcbiAgICBjb25zdCBhc3NldElkID0gT2JqZWN0LmtleXModXNhYmxlQXNzZXRzKVswXTtcbiAgICBiYW5kTWV0YWRhdGEgPSB1c2FibGVBc3NldHNbYXNzZXRJZF07XG4gIH1cblxuICBpZiAoYmFuZE1ldGFkYXRhKSB7XG4gICAgbWluQ2F0ZWdvcmljYWxCYW5kVmFsdWUgPSBiYW5kTWV0YWRhdGFbJ3Jhc3RlcjpiYW5kcyddPy5bMF0uc3RhdGlzdGljcz8ubWluaW11bTtcbiAgICBtYXhDYXRlZ29yaWNhbEJhbmRWYWx1ZSA9IGJhbmRNZXRhZGF0YVsncmFzdGVyOmJhbmRzJ10/LlswXS5zdGF0aXN0aWNzPy5tYXhpbXVtO1xuICB9XG5cbiAgcmV0dXJuIFttaW5DYXRlZ29yaWNhbEJhbmRWYWx1ZSwgbWF4Q2F0ZWdvcmljYWxCYW5kVmFsdWVdO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2luZ2xlQmFuZFByZXNldE9wdGlvbnMoXG4gIHN0YWM6IENvbXBsZXRlU1RBQ09iamVjdCxcbiAgc2luZ2xlQmFuZE5hbWU6IHN0cmluZ1xuKTogUHJlc2V0T3B0aW9uWydzaW5nbGVCYW5kJ10ge1xuICBjb25zdCB1c2FibGVBc3NldHMgPSBnZXRVc2FibGVBc3NldHMoc3RhYyk7XG4gIGxldCBhc3NldERhdGEgPSBmaW5kQXNzZXRXaXRoTmFtZSh1c2FibGVBc3NldHMsIHNpbmdsZUJhbmROYW1lLCAnbmFtZScpO1xuICBpZiAoIWFzc2V0RGF0YSkge1xuICAgIC8vIHNvbWUgY29sbGVjdGlvbnMgaGF2ZSBjb21tb25fbmFtZSBpbnN0ZWFkIG9mIG5hbWVcbiAgICBhc3NldERhdGEgPSBmaW5kQXNzZXRXaXRoTmFtZSh1c2FibGVBc3NldHMsIHNpbmdsZUJhbmROYW1lLCAnY29tbW9uX25hbWUnKTtcbiAgfVxuICBsZXQgc2luZ2xlQmFuZDogUHJlc2V0T3B0aW9uWydzaW5nbGVCYW5kJ107XG4gIGlmIChhc3NldERhdGEpIHtcbiAgICBjb25zdCBbYXNzZXRJZCwgYmFuZEluZGV4XSA9IGFzc2V0RGF0YTtcbiAgICBzaW5nbGVCYW5kID0ge2Fzc2V0SWQsIGJhbmRJbmRleH07XG4gIH1cbiAgcmV0dXJuIHNpbmdsZUJhbmQ7XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlIHZpZXdwb3J0LXJlbGF0ZWQgbWluIGFuZCBtYXggcmFzdGVyIHZhbHVlcyBmcm9tIGxvYWRlZCB0aWxlc1xuICogQHBhcmFtIHRpbGVzIC0gZGVjay5nbCB0aWxlc1xuICogQHJldHVybnMgW21pblBpeGVsVmFsdWUsIG1heFBpeGVsVmFsdWVdXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRNaW5NYXhGcm9tVGlsZTJESGVhZGVycyh0aWxlczogKFRpbGUyREhlYWRlciB8IG51bGwpW10pOiBbbnVtYmVyLCBudW1iZXJdIHtcbiAgY29uc3QgbWluVmFsdWVzOiAobnVtYmVyIHwgbnVsbClbXSA9IHRpbGVzXG4gICAgLm1hcCgodGlsZTogVGlsZTJESGVhZGVyIHwgbnVsbCkgPT4ge1xuICAgICAgaWYgKCF0aWxlIHx8ICF0aWxlLmRhdGEpIHtcbiAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICB9XG4gICAgICBjb25zdCB7bWluUGl4ZWxWYWx1ZX0gPSB0aWxlLmRhdGE7XG4gICAgICByZXR1cm4gbWluUGl4ZWxWYWx1ZTtcbiAgICB9KVxuICAgIC5maWx0ZXIodmFsdWUgPT4gdmFsdWUpO1xuICBjb25zdCBtYXhWYWx1ZXM6IChudW1iZXIgfCBudWxsKVtdID0gdGlsZXNcbiAgICAubWFwKCh0aWxlOiBUaWxlMkRIZWFkZXIgfCBudWxsKSA9PiB7XG4gICAgICBpZiAoIXRpbGUgfHwgIXRpbGUuZGF0YSkge1xuICAgICAgICByZXR1cm4gbnVsbDtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHttYXhQaXhlbFZhbHVlfSA9IHRpbGUuZGF0YTtcbiAgICAgIHJldHVybiBtYXhQaXhlbFZhbHVlO1xuICAgIH0pXG4gICAgLmZpbHRlcih2YWx1ZSA9PiB2YWx1ZSk7XG4gIC8vIFdlIGNhbiBjYXN0IHRvIG51bWJlcltdIGJlY2F1c2Ugd2UgaGF2ZSBmaWx0ZXJlZCBudWxsIHZhbHVlc1xuICByZXR1cm4gW01hdGgubWluKC4uLihtaW5WYWx1ZXMgYXMgbnVtYmVyW10pKSwgTWF0aC5tYXgoLi4uKG1heFZhbHVlcyBhcyBudW1iZXJbXSkpXTtcbn1cblxuLyoqXG4gKiBHZXQgbG9hZGluZyBwYXJhbXNcbiAqIEBwYXJhbSBzdGFjIFNUQUMgT2JqZWN0XG4gKiBAcGFyYW0gcHJlc2V0IFByZXNldCBmb3IgZGlzcGxheVxuICogQHJldHVybiBQYXJhbWV0ZXJzIGZvciBsb2FkaW5nIGRhdGFcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldERhdGFTb3VyY2VQYXJhbXMoXG4gIHN0YWM6IENvbXBsZXRlU1RBQ09iamVjdCxcbiAgcHJlc2V0SWQ6IHN0cmluZyxcbiAgcHJlc2V0T3B0aW9ucz86IFByZXNldE9wdGlvblxuKTogRGF0YVNvdXJjZVBhcmFtcyB8IG51bGwge1xuICBjb25zdCB1c2FibGVBc3NldHMgPSBnZXRVc2FibGVBc3NldHMoc3RhYyk7XG4gIGNvbnN0IHtjb21tb25OYW1lcywgYmFuZENvbWJpbmF0aW9ufSA9IFBSRVNFVF9PUFRJT05TW3ByZXNldElkXTtcbiAgY29uc3QgW21pblpvb20sIG1heFpvb21dID0gZ2V0Wm9vbVJhbmdlKHN0YWMpO1xuXG4gIGxldCBiYW5kSW5mbzogQXNzZXRSZXF1ZXN0SW5mbyB8IG51bGwgPSBudWxsO1xuICBpZiAoYmFuZENvbWJpbmF0aW9uID09PSAnc2luZ2xlJykge1xuICAgIGJhbmRJbmZvID0gZ2V0U2luZ2xlQmFuZEluZm8odXNhYmxlQXNzZXRzLCBwcmVzZXRPcHRpb25zPy5zaW5nbGVCYW5kKTtcbiAgfSBlbHNlIGlmIChjb21tb25OYW1lcykge1xuICAgIGJhbmRJbmZvID0gZ2V0QmFuZElkc0ZvckNvbW1vbk5hbWVzKHN0YWMsIHVzYWJsZUFzc2V0cywgY29tbW9uTmFtZXMpO1xuICB9IGVsc2Uge1xuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgaWYgKCFiYW5kSW5mbykgcmV0dXJuIG51bGw7XG5cbiAgY29uc3QgZHR5cGUgPSBnZXREYXRhVHlwZSh1c2FibGVBc3NldHMsIGJhbmRJbmZvLmxvYWRBc3NldElkcyk7XG4gIGNvbnN0IFttaW5SYXN0ZXJTdGF0c1ZhbHVlLCBtYXhSYXN0ZXJTdGF0c1ZhbHVlXSA9IGdldFJhc3RlclN0YXRpc3RpY3NNaW5NYXgoXG4gICAgc3RhYyxcbiAgICBwcmVzZXRJZCxcbiAgICBwcmVzZXRPcHRpb25zPy5zaW5nbGVCYW5kXG4gICk7XG4gIGNvbnN0IHBpeGVsUmFuZ2UgPSBnZXRQaXhlbFJhbmdlKHN0YWMsIGR0eXBlLCBbbWluUmFzdGVyU3RhdHNWYWx1ZSwgbWF4UmFzdGVyU3RhdHNWYWx1ZV0pO1xuXG4gIGlmICghcGl4ZWxSYW5nZSB8fCAhZHR5cGUpIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIGNvbnN0IFttaW5QaXhlbFZhbHVlLCBtYXhQaXhlbFZhbHVlXSA9IHBpeGVsUmFuZ2U7XG4gIGNvbnN0IHtsb2FkQXNzZXRJZHMsIGxvYWRCYW5kSW5kZXhlcywgcmVuZGVyQmFuZEluZGV4ZXN9ID0gYmFuZEluZm87XG4gIGNvbnN0IGdsb2JhbEJvdW5kcyA9IGdldFNUQUNCb3VuZHMoc3RhYyk7XG5cbiAgcmV0dXJuIHtcbiAgICBsb2FkQXNzZXRJZHMsXG4gICAgbG9hZEJhbmRJbmRleGVzLFxuICAgIHJlbmRlckJhbmRJbmRleGVzLFxuICAgIG1pblpvb20sXG4gICAgbWF4Wm9vbSxcbiAgICBtaW5QaXhlbFZhbHVlLFxuICAgIG1heFBpeGVsVmFsdWUsXG4gICAgLy8gdXNlIG1pbiBhbmQgbWF4IHN0YXRpc3RpY3MgdmFsdWVzIGZvciBjYXRlZ29yaWNhbCByZS1zY2FsZVxuICAgIG1pbkNhdGVnb3JpY2FsQmFuZFZhbHVlOiBtaW5SYXN0ZXJTdGF0c1ZhbHVlLFxuICAgIG1heENhdGVnb3JpY2FsQmFuZFZhbHVlOiBtYXhSYXN0ZXJTdGF0c1ZhbHVlLFxuICAgIGdsb2JhbEJvdW5kcyxcbiAgICBkYXRhVHlwZTogZHR5cGVcbiAgfTtcbn1cblxuLyoqXG4gKiBHZXQgQm91bmRpbmcgQm94IG9mIFNUQUMgb2JqZWN0XG4gKiBAcGFyYW0gc3RhY1xuICogQHJldHVybiBib3VuZGluZyBib3hcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFNUQUNCb3VuZHMoc3RhYzogSXRlbSB8IENvbGxlY3Rpb24pOiBudW1iZXJbXSB8IG51bGwge1xuICAvLyBDaGVjayBpZiBJdGVtXG4gIGlmIChzdGFjLnR5cGUgPT09ICdGZWF0dXJlJykge1xuICAgIC8vIGJib3ggbWF5IGJl