UNPKG

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
"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