kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
409 lines (393 loc) • 61 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.assignPointPairToLayerColumn = assignPointPairToLayerColumn;
exports.createGeoArrowPointVector = createGeoArrowPointVector;
exports.getBoundsFromArrowMetadata = getBoundsFromArrowMetadata;
exports.getColumnModeRequiredColumns = getColumnModeRequiredColumns;
exports.getFilteredIndex = getFilteredIndex;
exports.getGeoArrowPointFields = getGeoArrowPointFields;
exports.getGeoArrowPointLayerProps = getGeoArrowPointLayerProps;
exports.getGeojsonLayerMetaFromArrow = getGeojsonLayerMetaFromArrow;
exports.getHoveredObjectFromArrow = getHoveredObjectFromArrow;
exports.getNeighbors = getNeighbors;
exports.getSatisfiedColumnMode = getSatisfiedColumnMode;
exports.isGeoArrowPointField = isGeoArrowPointField;
exports.isLayerHoveredFromArrow = isLayerHoveredFromArrow;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var arrow = _interopRequireWildcard(require("apache-arrow"));
var _gis = require("@loaders.gl/gis");
var _constants = require("@kepler.gl/constants");
var _arrow = require("@loaders.gl/arrow");
var _wkt = require("@loaders.gl/wkt");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
function assignPointPairToLayerColumn(pair, hasAlt) {
var _pair$pair = pair.pair,
lat = _pair$pair.lat,
lng = _pair$pair.lng,
altitude = _pair$pair.altitude;
if (!hasAlt) {
return {
lat: lat,
lng: lng
};
}
var defaultAltColumn = {
value: null,
fieldIdx: -1,
optional: true
};
return {
lat: lat,
lng: lng,
altitude: altitude ? _objectSpread(_objectSpread({}, defaultAltColumn), altitude) : defaultAltColumn
};
}
/**
* Converts a geoarrow.wkb vector into an array of BinaryFeatureCollections.
* @param geoColumn A vector column with geoarrow.wkb extension.
* @param options Options for geometry transformation.
* @returns
*/
function getBinaryGeometriesFromWKBArrow(geoColumn, options) {
var dataToFeature = [];
var featureTypes = {
point: false,
line: false,
polygon: false
};
var chunks = (options === null || options === void 0 ? void 0 : options.chunkIndex) !== undefined && (options === null || options === void 0 ? void 0 : options.chunkIndex) >= 0 ? [geoColumn.data[options === null || options === void 0 ? void 0 : options.chunkIndex]] : geoColumn.data;
var globalFeatureIdOffset = (options === null || options === void 0 ? void 0 : options.chunkOffset) || 0;
var featureIndex = globalFeatureIdOffset;
var bounds = [Infinity, Infinity, -Infinity, -Infinity];
chunks.forEach(function (chunk) {
var geojsonFeatures = [];
for (var i = 0; i < chunk.length; ++i) {
// ignore features without any geometry
if (chunk.valueOffsets[i + 1] - chunk.valueOffsets[i] > 0) {
var _WKBLoader$parseSync;
var valuesSlice = chunk.values.slice(chunk.valueOffsets[i], chunk.valueOffsets[i + 1]);
var geometry = _wkt.WKBLoader === null || _wkt.WKBLoader === void 0 || (_WKBLoader$parseSync = _wkt.WKBLoader.parseSync) === null || _WKBLoader$parseSync === void 0 ? void 0 : _WKBLoader$parseSync.call(_wkt.WKBLoader, valuesSlice.buffer, {
wkb: {
shape: 'geojson-geometry'
}
});
var feature = {
type: 'Feature',
geometry: geometry,
properties: {
index: featureIndex
}
};
geojsonFeatures.push(feature);
var type = geometry.type;
featureTypes.polygon = type === 'Polygon' || type === 'MultiPolygon';
featureTypes.point = type === 'Point' || type === 'MultiPoint';
featureTypes.line = type === 'LineString' || type === 'MultiLineString';
}
featureIndex++;
}
var geojsonToBinaryOptions = {
triangulate: true,
fixRingWinding: true
};
var binaryFeatures = (0, _gis.geojsonToBinary)(geojsonFeatures, geojsonToBinaryOptions);
// Need to update globalFeatureIds, to take into account previous batches,
// as geojsonToBinary doesn't have such option.
var featureTypesArr = ['points', 'lines', 'polygons'];
featureTypesArr.forEach(function (prop) {
var features = binaryFeatures[prop];
if (features) {
bounds = (0, _arrow.updateBoundsFromGeoArrowSamples)(features.positions.value, features.positions.size, bounds);
var globalFeatureIds = features.globalFeatureIds,
numericProps = features.numericProps;
var index = numericProps.index;
var len = globalFeatureIds.value.length;
for (var _i = 0; _i < len; ++_i) {
globalFeatureIds.value[_i] = index.value[_i];
}
}
});
dataToFeature.push(binaryFeatures);
});
return {
dataToFeature: dataToFeature,
featureTypes: featureTypes,
bounds: bounds,
fixedRadius: false
};
}
function getGeojsonLayerMetaFromArrow(_ref) {
var _geoField$metadata;
var geoColumn = _ref.geoColumn,
geoField = _ref.geoField,
chunkIndex = _ref.chunkIndex;
var encoding = geoField === null || geoField === void 0 || (_geoField$metadata = geoField.metadata) === null || _geoField$metadata === void 0 ? void 0 : _geoField$metadata.get(_constants.GEOARROW_METADATA_KEY);
var options = _objectSpread(_objectSpread({}, chunkIndex !== undefined && chunkIndex >= 0 ? {
chunkIndex: chunkIndex,
chunkOffset: geoColumn.data[0].length * chunkIndex
} : {}), {}, {
triangulate: true,
calculateMeanCenters: true
});
// getBinaryGeometriesFromArrow doesn't support geoarrow.wkb
if (encoding === _constants.GEOARROW_EXTENSIONS.WKB) {
return getBinaryGeometriesFromWKBArrow(geoColumn, options);
}
// create binary data from arrow data for GeoJsonLayer
var _getBinaryGeometriesF = (0, _arrow.getBinaryGeometriesFromArrow)(
// @ts-ignore
geoColumn, encoding, options),
binaryGeometries = _getBinaryGeometriesF.binaryGeometries,
featureTypes = _getBinaryGeometriesF.featureTypes,
bounds = _getBinaryGeometriesF.bounds,
meanCenters = _getBinaryGeometriesF.meanCenters;
// since there is no feature.properties.radius, we set fixedRadius to false
var fixedRadius = false;
return {
dataToFeature: binaryGeometries,
featureTypes: featureTypes,
bounds: bounds,
fixedRadius: fixedRadius,
centroids: meanCenters
};
}
function isLayerHoveredFromArrow(objectInfo, layerId) {
// there could be multiple deck.gl layers created from multiple chunks in arrow table
// the objectInfo.layer id should be `${this.id}-${i}`
if (objectInfo !== null && objectInfo !== void 0 && objectInfo.picked) {
var _objectInfo$layer;
var deckLayerId = objectInfo === null || objectInfo === void 0 || (_objectInfo$layer = objectInfo.layer) === null || _objectInfo$layer === void 0 || (_objectInfo$layer = _objectInfo$layer.props) === null || _objectInfo$layer === void 0 ? void 0 : _objectInfo$layer.id;
return deckLayerId.startsWith(layerId);
}
return false;
}
function getHoveredObjectFromArrow(objectInfo, dataContainer, layerId, columnAccessor, fieldAccessor) {
// hover object returns the index of the object in the data array
// NOTE: this could be done in Deck.gl getPickingInfo(params) and binaryToGeojson()
if (isLayerHoveredFromArrow(objectInfo, layerId) && objectInfo.index >= 0 && dataContainer) {
var _field$metadata;
var col = columnAccessor(dataContainer);
var rawGeometry = col === null || col === void 0 ? void 0 : col.get(objectInfo.index);
var field = fieldAccessor(dataContainer);
var encoding = field === null || field === void 0 || (_field$metadata = field.metadata) === null || _field$metadata === void 0 ? void 0 : _field$metadata.get(_constants.GEOARROW_METADATA_KEY);
var hoveredFeature = (0, _arrow.parseGeometryFromArrow)(rawGeometry, encoding);
var properties = dataContainer.rowAsArray(objectInfo.index).reduce(function (prev, cur, i) {
var _dataContainer$getFie;
var fieldName = dataContainer === null || dataContainer === void 0 || (_dataContainer$getFie = dataContainer.getField) === null || _dataContainer$getFie === void 0 ? void 0 : _dataContainer$getFie.call(dataContainer, i).name;
if (fieldName !== field.name) {
prev[fieldName] = cur;
}
return prev;
}, {});
return hoveredFeature ? {
type: 'Feature',
geometry: hoveredFeature,
properties: _objectSpread(_objectSpread({}, properties), {}, {
index: objectInfo.index
})
} : null;
}
return null;
}
/**
* find requiredColumns of supported column mode based on column mode
*/
function getColumnModeRequiredColumns(supportedColumnModes, columnMode) {
var _supportedColumnModes;
return supportedColumnModes === null || supportedColumnModes === void 0 || (_supportedColumnModes = supportedColumnModes.find(function (_ref2) {
var key = _ref2.key;
return key === columnMode;
})) === null || _supportedColumnModes === void 0 ? void 0 : _supportedColumnModes.requiredColumns;
}
/**
* Returns geoarrow fields with ARROW:extension:name POINT metadata
* @param fields Any fields
* @returns geoarrow fields with ARROW:extension:name POINT metadata
*/
function getGeoArrowPointFields(fields) {
return fields.filter(function (field) {
var _field$metadata2;
return field.type === 'geoarrow' && ((_field$metadata2 = field.metadata) === null || _field$metadata2 === void 0 ? void 0 : _field$metadata2.get(_constants.GEOARROW_METADATA_KEY)) === _constants.GEOARROW_EXTENSIONS.POINT;
});
}
/**
* Builds an arrow vector compatible with ARROW:extension:name geoarrow.point.
* @param getPosition Position accessor.
* @param numElements Number of elements in the vector.
* @returns An arrow vector compatible with ARROW:extension:name geoarrow.point.
*/
function createGeoArrowPointVector(dataContainer, getPosition) {
// TODO update/resize existing vector?
// TODO find an easier way to create point geo columns
// in a correct arrow format, as this approach seems too excessive for just a simple interleaved buffer.
var numElements = dataContainer.numRows();
var table = dataContainer.getTable();
var numCoords = numElements > 0 ? getPosition({
index: 0
}).length : 2;
var precision = 2;
var metadata = new Map();
metadata.set(_constants.GEOARROW_METADATA_KEY, _constants.GEOARROW_EXTENSIONS.POINT);
var childField = new arrow.Field('xyz', new arrow.Float(precision), false, metadata);
var fixedSizeList = new arrow.FixedSizeList(numCoords, childField);
var floatBuilder = new arrow.FloatBuilder({
type: new arrow.Float(precision)
});
var fixedSizeListBuilder = new arrow.FixedSizeListBuilder({
type: fixedSizeList
});
fixedSizeListBuilder.addChild(floatBuilder);
var assembledBatches = [];
var indexData = {
index: 0
};
for (var batchIndex = 0; batchIndex < table.batches.length; ++batchIndex) {
var numRowsInBatch = table.batches[batchIndex].numRows;
for (var i = 0; i < numRowsInBatch; ++i) {
var pos = getPosition(indexData);
fixedSizeListBuilder.append(pos);
++indexData.index;
}
assembledBatches.push(fixedSizeListBuilder.flush());
}
return arrow.makeVector(assembledBatches);
}
/**
* Builds a filtered index suitable for FilterArrowExtension.
* @param numElements Size for filtered index array.
* @param visibleIndices An array with indices of elements that aren't filtered out.
* @returns filteredIndex [0|1] array for GPU filtering
*/
function getFilteredIndex(numElements, visibleIndices, existingFilteredIndex) {
// contents are initialized with zeros by default, meaning not visible
var filteredIndex = existingFilteredIndex && existingFilteredIndex.length === numElements ? existingFilteredIndex : new Uint8ClampedArray(numElements);
filteredIndex.fill(0);
if (visibleIndices) {
for (var i = 0; i < visibleIndices.length; ++i) {
filteredIndex[visibleIndices[i]] = 1;
}
}
return filteredIndex;
}
/**
* Returns an array of neighbors to the specified index.
* @param neighborsField LayerColumn field with information about neighbors.
* @param dataContainer Data container.
* @param index Index of interest.
* @param getPosition Position accessor.
* @returns An array with information about neighbors.
*/
function getNeighbors(neighborsField, dataContainer, index, getPosition) {
if (!neighborsField || neighborsField.fieldIdx < 0) return [];
var neighborIndices = dataContainer.valueAt(index, neighborsField.fieldIdx);
// In case of arrow column with an array of indices.
if (neighborIndices.toArray) {
neighborIndices = Array.from(neighborIndices.toArray());
}
if (!Array.isArray(neighborIndices)) return [];
// find neighbor
var neighborsData = neighborIndices.map(function (idx) {
return {
index: idx,
position: getPosition({
index: idx
})
};
});
return neighborsData;
}
/**
* Returns bounds from a geoarrow field.
* TODO: refactor once metadata extraction from parquet to arrow vectors is in place.
* @param layerColumn Layer columns for which to check for a bounding box.
* @param dataContainer Data container with geoarrow metadata.
* @returns Returns bounding box if exists.
*/
function getBoundsFromArrowMetadata(layerColumn, dataContainer) {
try {
var field = dataContainer.getField(layerColumn.fieldIdx);
var table = dataContainer.getTable();
var geoMetadata = (0, _gis.getGeoMetadata)({
metadata: {
// @ts-expect-error
geo: table.schema.metadata.get('geo')
}
});
if (geoMetadata) {
var fieldMetadata = geoMetadata.columns[field.name];
if (fieldMetadata) {
var boundsFromMetadata = fieldMetadata['bbox'];
if (Array.isArray(boundsFromMetadata) && boundsFromMetadata.length === 4) {
return boundsFromMetadata;
}
}
}
} catch (error) {
// ignore for now
}
return false;
}
/**
* Finds and returns the first satisfied column mode based on the provided columns and fields.
* @param supportedColumnModes - An array of supported column modes to check.
* @param columns - The available columns.
* @param fields - Optional table fields to be used for extra verification.
* @returns The first column mode that satisfies the required conditions, or undefined if none match.
*/
function getSatisfiedColumnMode(columnModes, columns, fields) {
return columnModes === null || columnModes === void 0 ? void 0 : columnModes.find(function (mode) {
var _mode$requiredColumns;
return (_mode$requiredColumns = mode.requiredColumns) === null || _mode$requiredColumns === void 0 ? void 0 : _mode$requiredColumns.every(function (requriedCol) {
var column = columns === null || columns === void 0 ? void 0 : columns[requriedCol];
if (column !== null && column !== void 0 && column.value) {
if (mode.verifyField && fields !== null && fields !== void 0 && fields[column.fieldIdx]) {
var field = fields[column.fieldIdx];
return mode.verifyField(field);
}
return true;
}
return false;
});
});
}
/**
* Returns true if the field is of geoarrow point format.
* @param field A field.
* @returns Returns true if the field is of geoarrow point format.
*/
function isGeoArrowPointField(field) {
var _field$metadata3;
return field.type === 'geoarrow' && ((_field$metadata3 = field.metadata) === null || _field$metadata3 === void 0 ? void 0 : _field$metadata3.get(_constants.GEOARROW_METADATA_KEY)) === _constants.GEOARROW_EXTENSIONS.POINT;
}
/**
* Create default geoarrow column props based on the dataset.
* @param dataset A dataset to create layer props from.
* @returns geoarrow column props.
*/
function getGeoArrowPointLayerProps(dataset) {
var label = dataset.label;
var altProps = [];
dataset.fields.forEach(function (field) {
if (isGeoArrowPointField(field)) {
altProps.push({
label: typeof label === 'string' && label.replace(/\.[^/.]+$/, '') || field.name,
columns: {
geoarrow: {
value: field.name,
fieldIdx: field.fieldIdx
}
}
});
}
});
return altProps;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJhcnJvdyIsIl9pbnRlcm9wUmVxdWlyZVdpbGRjYXJkIiwicmVxdWlyZSIsIl9naXMiLCJfY29uc3RhbnRzIiwiX2Fycm93IiwiX3drdCIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiX3R5cGVvZiIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIm93bktleXMiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImFzc2lnblBvaW50UGFpclRvTGF5ZXJDb2x1bW4iLCJwYWlyIiwiaGFzQWx0IiwiX3BhaXIkcGFpciIsImxhdCIsImxuZyIsImFsdGl0dWRlIiwiZGVmYXVsdEFsdENvbHVtbiIsInZhbHVlIiwiZmllbGRJZHgiLCJvcHRpb25hbCIsImdldEJpbmFyeUdlb21ldHJpZXNGcm9tV0tCQXJyb3ciLCJnZW9Db2x1bW4iLCJvcHRpb25zIiwiZGF0YVRvRmVhdHVyZSIsImZlYXR1cmVUeXBlcyIsInBvaW50IiwibGluZSIsInBvbHlnb24iLCJjaHVua3MiLCJjaHVua0luZGV4IiwidW5kZWZpbmVkIiwiZGF0YSIsImdsb2JhbEZlYXR1cmVJZE9mZnNldCIsImNodW5rT2Zmc2V0IiwiZmVhdHVyZUluZGV4IiwiYm91bmRzIiwiSW5maW5pdHkiLCJjaHVuayIsImdlb2pzb25GZWF0dXJlcyIsInZhbHVlT2Zmc2V0cyIsIl9XS0JMb2FkZXIkcGFyc2VTeW5jIiwidmFsdWVzU2xpY2UiLCJ2YWx1ZXMiLCJzbGljZSIsImdlb21ldHJ5IiwiV0tCTG9hZGVyIiwicGFyc2VTeW5jIiwiYnVmZmVyIiwid2tiIiwic2hhcGUiLCJmZWF0dXJlIiwidHlwZSIsInByb3BlcnRpZXMiLCJpbmRleCIsImdlb2pzb25Ub0JpbmFyeU9wdGlvbnMiLCJ0cmlhbmd1bGF0ZSIsImZpeFJpbmdXaW5kaW5nIiwiYmluYXJ5RmVhdHVyZXMiLCJnZW9qc29uVG9CaW5hcnkiLCJmZWF0dXJlVHlwZXNBcnIiLCJwcm9wIiwiZmVhdHVyZXMiLCJ1cGRhdGVCb3VuZHNGcm9tR2VvQXJyb3dTYW1wbGVzIiwicG9zaXRpb25zIiwic2l6ZSIsImdsb2JhbEZlYXR1cmVJZHMiLCJudW1lcmljUHJvcHMiLCJsZW4iLCJmaXhlZFJhZGl1cyIsImdldEdlb2pzb25MYXllck1ldGFGcm9tQXJyb3ciLCJfcmVmIiwiX2dlb0ZpZWxkJG1ldGFkYXRhIiwiZ2VvRmllbGQiLCJlbmNvZGluZyIsIm1ldGFkYXRhIiwiR0VPQVJST1dfTUVUQURBVEFfS0VZIiwiY2FsY3VsYXRlTWVhbkNlbnRlcnMiLCJHRU9BUlJPV19FWFRFTlNJT05TIiwiV0tCIiwiX2dldEJpbmFyeUdlb21ldHJpZXNGIiwiZ2V0QmluYXJ5R2VvbWV0cmllc0Zyb21BcnJvdyIsImJpbmFyeUdlb21ldHJpZXMiLCJtZWFuQ2VudGVycyIsImNlbnRyb2lkcyIsImlzTGF5ZXJIb3ZlcmVkRnJvbUFycm93Iiwib2JqZWN0SW5mbyIsImxheWVySWQiLCJwaWNrZWQiLCJfb2JqZWN0SW5mbyRsYXllciIsImRlY2tMYXllcklkIiwibGF5ZXIiLCJwcm9wcyIsImlkIiwic3RhcnRzV2l0aCIsImdldEhvdmVyZWRPYmplY3RGcm9tQXJyb3ciLCJkYXRhQ29udGFpbmVyIiwiY29sdW1uQWNjZXNzb3IiLCJmaWVsZEFjY2Vzc29yIiwiX2ZpZWxkJG1ldGFkYXRhIiwiY29sIiwicmF3R2VvbWV0cnkiLCJmaWVsZCIsImhvdmVyZWRGZWF0dXJlIiwicGFyc2VHZW9tZXRyeUZyb21BcnJvdyIsInJvd0FzQXJyYXkiLCJyZWR1Y2UiLCJwcmV2IiwiY3VyIiwiX2RhdGFDb250YWluZXIkZ2V0RmllIiwiZmllbGROYW1lIiwiZ2V0RmllbGQiLCJuYW1lIiwiZ2V0Q29sdW1uTW9kZVJlcXVpcmVkQ29sdW1ucyIsInN1cHBvcnRlZENvbHVtbk1vZGVzIiwiY29sdW1uTW9kZSIsIl9zdXBwb3J0ZWRDb2x1bW5Nb2RlcyIsImZpbmQiLCJfcmVmMiIsImtleSIsInJlcXVpcmVkQ29sdW1ucyIsImdldEdlb0Fycm93UG9pbnRGaWVsZHMiLCJmaWVsZHMiLCJfZmllbGQkbWV0YWRhdGEyIiwiUE9JTlQiLCJjcmVhdGVHZW9BcnJvd1BvaW50VmVjdG9yIiwiZ2V0UG9zaXRpb24iLCJudW1FbGVtZW50cyIsIm51bVJvd3MiLCJ0YWJsZSIsImdldFRhYmxlIiwibnVtQ29vcmRzIiwicHJlY2lzaW9uIiwiTWFwIiwiY2hpbGRGaWVsZCIsIkZpZWxkIiwiRmxvYXQiLCJmaXhlZFNpemVMaXN0IiwiRml4ZWRTaXplTGlzdCIsImZsb2F0QnVpbGRlciIsIkZsb2F0QnVpbGRlciIsImZpeGVkU2l6ZUxpc3RCdWlsZGVyIiwiRml4ZWRTaXplTGlzdEJ1aWxkZXIiLCJhZGRDaGlsZCIsImFzc2VtYmxlZEJhdGNoZXMiLCJpbmRleERhdGEiLCJiYXRjaEluZGV4IiwiYmF0Y2hlcyIsIm51bVJvd3NJbkJhdGNoIiwicG9zIiwiYXBwZW5kIiwiZmx1c2giLCJtYWtlVmVjdG9yIiwiZ2V0RmlsdGVyZWRJbmRleCIsInZpc2libGVJbmRpY2VzIiwiZXhpc3RpbmdGaWx0ZXJlZEluZGV4IiwiZmlsdGVyZWRJbmRleCIsIlVpbnQ4Q2xhbXBlZEFycmF5IiwiZmlsbCIsImdldE5laWdoYm9ycyIsIm5laWdoYm9yc0ZpZWxkIiwibmVpZ2hib3JJbmRpY2VzIiwidmFsdWVBdCIsInRvQXJyYXkiLCJBcnJheSIsImZyb20iLCJpc0FycmF5IiwibmVpZ2hib3JzRGF0YSIsIm1hcCIsImlkeCIsInBvc2l0aW9uIiwiZ2V0Qm91bmRzRnJvbUFycm93TWV0YWRhdGEiLCJsYXllckNvbHVtbiIsImdlb01ldGFkYXRhIiwiZ2V0R2VvTWV0YWRhdGEiLCJnZW8iLCJzY2hlbWEiLCJmaWVsZE1ldGFkYXRhIiwiY29sdW1ucyIsImJvdW5kc0Zyb21NZXRhZGF0YSIsImVycm9yIiwiZ2V0U2F0aXNmaWVkQ29sdW1uTW9kZSIsImNvbHVtbk1vZGVzIiwibW9kZSIsIl9tb2RlJHJlcXVpcmVkQ29sdW1ucyIsImV2ZXJ5IiwicmVxdXJpZWRDb2wiLCJjb2x1bW4iLCJ2ZXJpZnlGaWVsZCIsImlzR2VvQXJyb3dQb2ludEZpZWxkIiwiX2ZpZWxkJG1ldGFkYXRhMyIsImdldEdlb0Fycm93UG9pbnRMYXllclByb3BzIiwiZGF0YXNldCIsImxhYmVsIiwiYWx0UHJvcHMiLCJyZXBsYWNlIiwiZ2VvYXJyb3ciXSwic291cmNlcyI6WyIuLi9zcmMvbGF5ZXItdXRpbHMudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuaW1wb3J0ICogYXMgYXJyb3cgZnJvbSAnYXBhY2hlLWFycm93JztcbmltcG9ydCB7RmVhdHVyZSwgQkJveH0gZnJvbSAnZ2VvanNvbic7XG5pbXBvcnQge2dldEdlb01ldGFkYXRhfSBmcm9tICdAbG9hZGVycy5nbC9naXMnO1xuXG5pbXBvcnQge0dFT0FSUk9XX0VYVEVOU0lPTlMsIEdFT0FSUk9XX01FVEFEQVRBX0tFWX0gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuaW1wb3J0IHtLZXBsZXJUYWJsZX0gZnJvbSAnQGtlcGxlci5nbC90YWJsZSc7XG5pbXBvcnQge1xuICBGaWVsZCxcbiAgUHJvdG9EYXRhc2V0RmllbGQsXG4gIEZpZWxkUGFpcixcbiAgU3VwcG9ydGVkQ29sdW1uTW9kZSxcbiAgTGF5ZXJDb2x1bW4sXG4gIExheWVyQ29sdW1ucyxcbiAgUkdCQ29sb3Jcbn0gZnJvbSAnQGtlcGxlci5nbC90eXBlcyc7XG5pbXBvcnQge0RhdGFDb250YWluZXJJbnRlcmZhY2UsIEFycm93RGF0YUNvbnRhaW5lcn0gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5pbXBvcnQge1xuICBnZXRCaW5hcnlHZW9tZXRyaWVzRnJvbUFycm93LFxuICBwYXJzZUdlb21ldHJ5RnJvbUFycm93LFxuICBCaW5hcnlHZW9tZXRyaWVzRnJvbUFycm93T3B0aW9ucyxcbiAgdXBkYXRlQm91bmRzRnJvbUdlb0Fycm93U2FtcGxlc1xufSBmcm9tICdAbG9hZGVycy5nbC9hcnJvdyc7XG5cbmltcG9ydCB7V0tCTG9hZGVyfSBmcm9tICdAbG9hZGVycy5nbC93a3QnO1xuaW1wb3J0IHtnZW9qc29uVG9CaW5hcnl9IGZyb20gJ0Bsb2FkZXJzLmdsL2dpcyc7XG5pbXBvcnQge1xuICBCaW5hcnlGZWF0dXJlQ29sbGVjdGlvbixcbiAgR2VvbWV0cnksXG4gIEJpbmFyeVBvaW50RmVhdHVyZSxcbiAgQmluYXJ5TGluZUZlYXR1cmUsXG4gIEJpbmFyeVBvbHlnb25GZWF0dXJlXG59IGZyb20gJ0Bsb2FkZXJzLmdsL3NjaGVtYSc7XG5cbmltcG9ydCB7RGVja0dsR2VvVHlwZXMsIEdlb2pzb25EYXRhTWFwc30gZnJvbSAnLi9nZW9qc29uLWxheWVyL2dlb2pzb24tdXRpbHMnO1xuXG5leHBvcnQgdHlwZSBGaW5kRGVmYXVsdExheWVyUHJvcHMgPSB7XG4gIGxhYmVsOiBzdHJpbmc7XG4gIGNvbG9yPzogUkdCQ29sb3I7XG4gIGlzVmlzaWJsZT86IGJvb2xlYW47XG4gIGNvbHVtbnM/OiBSZWNvcmQ8c3RyaW5nLCBMYXllckNvbHVtbj47XG59O1xuXG5leHBvcnQgdHlwZSBGaW5kRGVmYXVsdExheWVyUHJvcHNSZXR1cm5WYWx1ZSA9IHtcbiAgLyoqIExheWVyIHByb3BzIHRvIGNyZWF0ZSBsYXllcnMgYnkgZGVmYXVsdCB3aGVuIGEgZGF0YXNldCBpcyBhZGRlZCAqL1xuICBwcm9wczogRmluZERlZmF1bHRMYXllclByb3BzW107XG4gIC8qKiBsYXllciBwcm9wcyBvZiBwb3NzaWJsZSBhbHRlcm5hdGl2ZSBsYXllciBjb25maWd1cmF0aW9ucywgbm90IGNyZWF0ZWQgYnkgZGVmYXVsdCAqL1xuICBhbHRQcm9wcz86IEZpbmREZWZhdWx0TGF5ZXJQcm9wc1tdO1xuICAvKiogQWxyZWFkeSBmb3VuZCBsYXllciBjb25maWd1cmF0aW9ucyAqL1xuICBmb3VuZExheWVycz86IChGaW5kRGVmYXVsdExheWVyUHJvcHMgJiB7dHlwZTogc3RyaW5nfSlbXTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBhc3NpZ25Qb2ludFBhaXJUb0xheWVyQ29sdW1uKFxuICBwYWlyOiBGaWVsZFBhaXIsXG4gIGhhc0FsdDogYm9vbGVhblxuKTogUmVjb3JkPHN0cmluZywgTGF5ZXJDb2x1bW4+IHtcbiAgY29uc3Qge2xhdCwgbG5nLCBhbHRpdHVkZX0gPSBwYWlyLnBhaXI7XG4gIGlmICghaGFzQWx0KSB7XG4gICAgcmV0dXJuIHtsYXQsIGxuZ307XG4gIH1cblxuICBjb25zdCBkZWZhdWx0QWx0Q29sdW1uID0ge3ZhbHVlOiBudWxsLCBmaWVsZElkeDogLTEsIG9wdGlvbmFsOiB0cnVlfTtcblxuICByZXR1cm4ge1xuICAgIGxhdCxcbiAgICBsbmcsXG4gICAgYWx0aXR1ZGU6IGFsdGl0dWRlID8gey4uLmRlZmF1bHRBbHRDb2x1bW4sIC4uLmFsdGl0dWRlfSA6IGRlZmF1bHRBbHRDb2x1bW5cbiAgfTtcbn1cblxuZXhwb3J0IHR5cGUgR2VvanNvbkxheWVyTWV0YVByb3BzID0ge1xuICBkYXRhVG9GZWF0dXJlOiBHZW9qc29uRGF0YU1hcHM7XG4gIGZlYXR1cmVUeXBlczogRGVja0dsR2VvVHlwZXM7XG4gIGJvdW5kczogQkJveCB8IG51bGw7XG4gIGZpeGVkUmFkaXVzOiBib29sZWFuO1xuICBjZW50cm9pZHM/OiBBcnJheTxudW1iZXJbXSB8IG51bGw+O1xufTtcblxuLyoqXG4gKiBDb252ZXJ0cyBhIGdlb2Fycm93LndrYiB2ZWN0b3IgaW50byBhbiBhcnJheSBvZiBCaW5hcnlGZWF0dXJlQ29sbGVjdGlvbnMuXG4gKiBAcGFyYW0gZ2VvQ29sdW1uIEEgdmVjdG9yIGNvbHVtbiB3aXRoIGdlb2Fycm93LndrYiBleHRlbnNpb24uXG4gKiBAcGFyYW0gb3B0aW9ucyBPcHRpb25zIGZvciBnZW9tZXRyeSB0cmFuc2Zvcm1hdGlvbi5cbiAqIEByZXR1cm5zXG4gKi9cbmZ1bmN0aW9uIGdldEJpbmFyeUdlb21ldHJpZXNGcm9tV0tCQXJyb3coXG4gIGdlb0NvbHVtbjogYXJyb3cuVmVjdG9yLFxuICBvcHRpb25zOiB7Y2h1bmtJbmRleD86IG51bWJlcjsgY2h1bmtPZmZzZXQ/OiBudW1iZXJ9XG4pOiBHZW9qc29uTGF5ZXJNZXRhUHJvcHMge1xuICBjb25zdCBkYXRhVG9GZWF0dXJlOiBCaW5hcnlGZWF0dXJlQ29sbGVjdGlvbltdID0gW107XG4gIGNvbnN0IGZlYXR1cmVUeXBlczogR2VvanNvbkxheWVyTWV0YVByb3BzWydmZWF0dXJlVHlwZXMnXSA9IHtcbiAgICBwb2ludDogZmFsc2UsXG4gICAgbGluZTogZmFsc2UsXG4gICAgcG9seWdvbjogZmFsc2VcbiAgfTtcblxuICBjb25zdCBjaHVua3MgPVxuICAgIG9wdGlvbnM/LmNodW5rSW5kZXggIT09IHVuZGVmaW5lZCAmJiBvcHRpb25zPy5jaHVua0luZGV4ID49IDBcbiAgICAgID8gW2dlb0NvbHVtbi5kYXRhW29wdGlvbnM/LmNodW5rSW5kZXhdXVxuICAgICAgOiBnZW9Db2x1bW4uZGF0YTtcbiAgY29uc3QgZ2xvYmFsRmVhdHVyZUlkT2Zmc2V0ID0gb3B0aW9ucz8uY2h1bmtPZmZzZXQgfHwgMDtcbiAgbGV0IGZlYXR1cmVJbmRleCA9IGdsb2JhbEZlYXR1cmVJZE9mZnNldDtcbiAgbGV0IGJvdW5kczogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gPSBbSW5maW5pdHksIEluZmluaXR5LCAtSW5maW5pdHksIC1JbmZpbml0eV07XG5cbiAgY2h1bmtzLmZvckVhY2goY2h1bmsgPT4ge1xuICAgIGNvbnN0IGdlb2pzb25GZWF0dXJlczogRmVhdHVyZVtdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjaHVuay5sZW5ndGg7ICsraSkge1xuICAgICAgLy8gaWdub3JlIGZlYXR1cmVzIHdpdGhvdXQgYW55IGdlb21ldHJ5XG4gICAgICBpZiAoY2h1bmsudmFsdWVPZmZzZXRzW2kgKyAxXSAtIGNodW5rLnZhbHVlT2Zmc2V0c1tpXSA+IDApIHtcbiAgICAgICAgY29uc3QgdmFsdWVzU2xpY2UgPSBjaHVuay52YWx1ZXMuc2xpY2UoY2h1bmsudmFsdWVPZmZzZXRzW2ldLCBjaHVuay52YWx1ZU9mZnNldHNbaSArIDFdKTtcblxuICAgICAgICBjb25zdCBnZW9tZXRyeSA9IFdLQkxvYWRlcj8ucGFyc2VTeW5jPy4odmFsdWVzU2xpY2UuYnVmZmVyLCB7XG4gICAgICAgICAgd2tiOiB7c2hhcGU6ICdnZW9qc29uLWdlb21ldHJ5J31cbiAgICAgICAgfSkgYXMgR2VvbWV0cnk7XG4gICAgICAgIGNvbnN0IGZlYXR1cmU6IEZlYXR1cmUgPSB7XG4gICAgICAgICAgdHlwZTogJ0ZlYXR1cmUnLFxuICAgICAgICAgIGdlb21ldHJ5LFxuICAgICAgICAgIHByb3BlcnRpZXM6IHtpbmRleDogZmVhdHVyZUluZGV4fVxuICAgICAgICB9O1xuICAgICAgICBnZW9qc29uRmVhdHVyZXMucHVzaChmZWF0dXJlKTtcblxuICAgICAgICBjb25zdCB7dHlwZX0gPSBnZW9tZXRyeTtcbiAgICAgICAgZmVhdHVyZVR5cGVzLnBvbHlnb24gPSB0eXBlID09PSAnUG9seWdvbicgfHwgdHlwZSA9PT0gJ011bHRpUG9seWdvbic7XG4gICAgICAgIGZlYXR1cmVUeXBlcy5wb2ludCA9IHR5cGUgPT09ICdQb2ludCcgfHwgdHlwZSA9PT0gJ011bHRpUG9pbnQnO1xuICAgICAgICBmZWF0dXJlVHlwZXMubGluZSA9IHR5cGUgPT09ICdMaW5lU3RyaW5nJyB8fCB0eXBlID09PSAnTXVsdGlMaW5lU3RyaW5nJztcbiAgICAgIH1cblxuICAgICAgZmVhdHVyZUluZGV4Kys7XG4gICAgfVxuXG4gICAgY29uc3QgZ2VvanNvblRvQmluYXJ5T3B0aW9ucyA9IHtcbiAgICAgIHRyaWFuZ3VsYXRlOiB0cnVlLFxuICAgICAgZml4UmluZ1dpbmRpbmc6IHRydWVcbiAgICB9O1xuICAgIGNvbnN0IGJpbmFyeUZlYXR1cmVzID0gZ2VvanNvblRvQmluYXJ5KGdlb2pzb25GZWF0dXJlcywgZ2VvanNvblRvQmluYXJ5T3B0aW9ucyk7XG5cbiAgICAvLyBOZWVkIHRvIHVwZGF0ZSBnbG9iYWxGZWF0dXJlSWRzLCB0byB0YWtlIGludG8gYWNjb3VudCBwcmV2aW91cyBiYXRjaGVzLFxuICAgIC8vIGFzIGdlb2pzb25Ub0JpbmFyeSBkb2Vzbid0IGhhdmUgc3VjaCBvcHRpb24uXG4gICAgY29uc3QgZmVhdHVyZVR5cGVzQXJyID0gWydwb2ludHMnLCAnbGluZXMnLCAncG9seWdvbnMnXTtcbiAgICBmZWF0dXJlVHlwZXNBcnIuZm9yRWFjaChwcm9wID0+IHtcbiAgICAgIGNvbnN0IGZlYXR1cmVzID0gYmluYXJ5RmVhdHVyZXNbcHJvcF0gYXNcbiAgICAgICAgfCBCaW5hcnlQb2ludEZlYXR1cmVcbiAgICAgICAgfCBCaW5hcnlMaW5lRmVhdHVyZVxuICAgICAgICB8IEJpbmFyeVBvbHlnb25GZWF0dXJlO1xuICAgICAgaWYgKGZlYXR1cmVzKSB7XG4gICAgICAgIGJvdW5kcyA9IHVwZGF0ZUJvdW5kc0Zyb21HZW9BcnJvd1NhbXBsZXMoXG4gICAgICAgICAgZmVhdHVyZXMucG9zaXRpb25zLnZhbHVlIGFzIEZsb2F0NjRBcnJheSxcbiAgICAgICAgICBmZWF0dXJlcy5wb3NpdGlvbnMuc2l6ZSxcbiAgICAgICAgICBib3VuZHNcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCB7Z2xvYmFsRmVhdHVyZUlkcywgbnVtZXJpY1Byb3BzfSA9IGZlYXR1cmVzO1xuICAgICAgICBjb25zdCB7aW5kZXh9ID0gbnVtZXJpY1Byb3BzO1xuICAgICAgICBjb25zdCBsZW4gPSBnbG9iYWxGZWF0dXJlSWRzLnZhbHVlLmxlbmd0aDtcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBsZW47ICsraSkge1xuICAgICAgICAgIGdsb2JhbEZlYXR1cmVJZHMudmFsdWVbaV0gPSBpbmRleC52YWx1ZVtpXTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgIH0pO1xuXG4gICAgZGF0YVRvRmVhdHVyZS5wdXNoKGJpbmFyeUZlYXR1cmVzKTtcbiAgfSk7XG5cbiAgcmV0dXJuIHtcbiAgICBkYXRhVG9GZWF0dXJlOiBkYXRhVG9GZWF0dXJlLFxuICAgIGZlYXR1cmVUeXBlczogZmVhdHVyZVR5cGVzLFxuICAgIGJvdW5kcyxcbiAgICBmaXhlZFJhZGl1czogZmFsc2VcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEdlb2pzb25MYXllck1ldGFGcm9tQXJyb3coe1xuICBnZW9Db2x1bW4sXG4gIGdlb0ZpZWxkLFxuICBjaHVua0luZGV4XG59OiB7XG4gIGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2U7XG4gIGdlb0NvbHVtbjogYXJyb3cuVmVjdG9yO1xuICBnZW9GaWVsZDogUHJvdG9EYXRhc2V0RmllbGQ7XG4gIGNodW5rSW5kZXg/OiBudW1iZXI7XG59KTogR2VvanNvbkxheWVyTWV0YVByb3BzIHtcbiAgY29uc3QgZW5jb2RpbmcgPSBnZW9GaWVsZD8ubWV0YWRhdGE/LmdldChHRU9BUlJPV19NRVRBREFUQV9LRVkpO1xuICBjb25zdCBvcHRpb25zOiBCaW5hcnlHZW9tZXRyaWVzRnJvbUFycm93T3B0aW9ucyA9IHtcbiAgICAuLi4oY2h1bmtJbmRleCAhPT0gdW5kZWZpbmVkICYmIGNodW5rSW5kZXggPj0gMFxuICAgICAgPyB7XG4gICAgICAgICAgY2h1bmtJbmRleCxcbiAgICAgICAgICBjaHVua09mZnNldDogZ2VvQ29sdW1uLmRhdGFbMF0ubGVuZ3RoICogY2h1bmtJbmRleFxuICAgICAgICB9XG4gICAgICA6IHt9KSxcbiAgICB0cmlhbmd1bGF0ZTogdHJ1ZSxcbiAgICBjYWxjdWxhdGVNZWFuQ2VudGVyczogdHJ1ZVxuICB9O1xuXG4gIC8vIGdldEJpbmFyeUdlb21ldHJpZXNGcm9tQXJyb3cgZG9lc24ndCBzdXBwb3J0IGdlb2Fycm93LndrYlxuICBpZiAoZW5jb2RpbmcgPT09IEdFT0FSUk9XX0VYVEVOU0lPTlMuV0tCKSB7XG4gICAgcmV0dXJuIGdldEJpbmFyeUdlb21ldHJpZXNGcm9tV0tCQXJyb3coZ2VvQ29sdW1uLCBvcHRpb25zKTtcbiAgfVxuXG4gIC8vIGNyZWF0ZSBiaW5hcnkgZGF0YSBmcm9tIGFycm93IGRhdGEgZm9yIEdlb0pzb25MYXllclxuICBjb25zdCB7YmluYXJ5R2VvbWV0cmllcywgZmVhdHVyZVR5cGVzLCBib3VuZHMsIG1lYW5DZW50ZXJzfSA9IGdldEJpbmFyeUdlb21ldHJpZXNGcm9tQXJyb3coXG4gICAgLy8gQHRzLWlnbm9yZVxuICAgIGdlb0NvbHVtbixcbiAgICBlbmNvZGluZyxcbiAgICBvcHRpb25zXG4gICk7XG5cbiAgLy8gc2luY2UgdGhlcmUgaXMgbm8gZmVhdHVyZS5wcm9wZXJ0aWVzLnJhZGl1cywgd2Ugc2V0IGZpeGVkUmFkaXVzIHRvIGZhbHNlXG4gIGNvbnN0IGZpeGVkUmFkaXVzID0gZmFsc2U7XG5cbiAgcmV0dXJuIHtcbiAgICBkYXRhVG9GZWF0dXJlOiBiaW5hcnlHZW9tZXRyaWVzLFxuICAgIGZlYXR1cmVUeXBlcyxcbiAgICBib3VuZHMsXG4gICAgZml4ZWRSYWRpdXMsXG4gICAgY2VudHJvaWRzOiBtZWFuQ2VudGVyc1xuICB9O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gaXNMYXllckhvdmVyZWRGcm9tQXJyb3cob2JqZWN0SW5mbywgbGF5ZXJJZDogc3RyaW5nKTogYm9vbGVhbiB7XG4gIC8vIHRoZXJlIGNvdWxkIGJlIG11bHRpcGxlIGRlY2suZ2wgbGF5ZXJzIGNyZWF0ZWQgZnJvbSBtdWx0aXBsZSBjaHVua3MgaW4gYXJyb3cgdGFibGVcbiAgLy8gdGhlIG9iamVjdEluZm8ubGF5ZXIgaWQgc2hvdWxkIGJlIGAke3RoaXMuaWR9LSR7aX1gXG4gIGlmIChvYmplY3RJbmZvPy5waWNrZWQpIHtcbiAgICBjb25zdCBkZWNrTGF5ZXJJZCA9IG9iamVjdEluZm8/LmxheWVyPy5wcm9wcz8uaWQ7XG4gICAgcmV0dXJuIGRlY2tMYXllcklkLnN0YXJ0c1dpdGgobGF5ZXJJZCk7XG4gIH1cbiAgcmV0dXJuIGZhbHNlO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0SG92ZXJlZE9iamVjdEZyb21BcnJvdyhcbiAgb2JqZWN0SW5mbyxcbiAgZGF0YUNvbnRhaW5lcixcbiAgbGF5ZXJJZCxcbiAgY29sdW1uQWNjZXNzb3IsXG4gIGZpZWxkQWNjZXNzb3Jcbik6IEZlYXR1cmUgfCBudWxsIHtcbiAgLy8gaG92ZXIgb2JqZWN0IHJldHVybnMgdGhlIGluZGV4IG9mIHRoZSBvYmplY3QgaW4gdGhlIGRhdGEgYXJyYXlcbiAgLy8gTk9URTogdGhpcyBjb3VsZCBiZSBkb25lIGluIERlY2suZ2wgZ2V0UGlja2luZ0luZm8ocGFyYW1zKSBhbmQgYmluYXJ5VG9HZW9qc29uKClcbiAgaWYgKGlzTGF5ZXJIb3ZlcmVkRnJvbUFycm93KG9iamVjdEluZm8sIGxheWVySWQpICYmIG9iamVjdEluZm8uaW5kZXggPj0gMCAmJiBkYXRhQ29udGFpbmVyKSB7XG4gICAgY29uc3QgY29sID0gY29sdW1uQWNjZXNzb3IoZGF0YUNvbnRhaW5lcik7XG4gICAgY29uc3QgcmF3R2VvbWV0cnkgPSBjb2w/LmdldChvYmplY3RJbmZvLmluZGV4KTtcblxuICAgIGNvbnN0IGZpZWxkID0gZmllbGRBY2Nlc3NvcihkYXRhQ29udGFpbmVyKTtcbiAgICBjb25zdCBlbmNvZGluZyA9IGZpZWxkPy5tZXRhZGF0YT8uZ2V0KEdFT0FSUk9XX01FVEFEQVRBX0tFWSk7XG5cbiAgICBjb25zdCBob3ZlcmVkRmVhdHVyZSA9IHBhcnNlR2VvbWV0cnlGcm9tQXJyb3cocmF3R2VvbWV0cnksIGVuY29kaW5nKTtcblxuICAgIGNvbnN0IHByb3BlcnRpZXMgPSBkYXRhQ29udGFpbmVyLnJvd0FzQXJyYXkob2JqZWN0SW5mby5pbmRleCkucmVkdWNlKChwcmV2LCBjdXIsIGkpID0+IHtcbiAgICAgIGNvbnN0IGZpZWxkTmFtZSA9IGRhdGFDb250YWluZXI/LmdldEZpZWxkPy4oaSkubmFtZTtcbiAgICAgIGlmIChmaWVsZE5hbWUgIT09IGZpZWxkLm5hbWUpIHtcbiAgICAgICAgcHJldltmaWVsZE5hbWVdID0gY3VyO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHByZXY7XG4gICAgfSwge30pO1xuXG4gICAgcmV0dXJuIGhvdmVyZWRGZWF0dXJlXG4gICAgICA/IHtcbiAgICAgICAgICB0eXBlOiAnRmVhdHVyZScsXG4gICAgICAgICAgZ2VvbWV0cnk6IGhvdmVyZWRGZWF0dXJlLFxuICAgICAgICAgIHByb3BlcnRpZXM6IHtcbiAgICAgICAgICAgIC4uLnByb3BlcnRpZXMsXG4gICAgICAgICAgICBpbmRleDogb2JqZWN0SW5mby5pbmRleFxuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgOiBudWxsO1xuICB9XG4gIHJldHVybiBudWxsO1xufVxuXG4vKipcbiAqIGZpbmQgcmVxdWlyZWRDb2x1bW5zIG9mIHN1cHBvcnRlZCBjb2x1bW4gbW9kZSBiYXNlZCBvbiBjb2x1bW4gbW9kZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q29sdW1uTW9kZVJlcXVpcmVkQ29sdW1ucyhcbiAgc3VwcG9ydGVkQ29sdW1uTW9kZXM6IFN1cHBvcnRlZENvbHVtbk1vZGVbXSB8IG51bGwsXG4gIGNvbHVtbk1vZGU/OiBzdHJpbmdcbik6IHN0cmluZ1tdIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIHN1cHBvcnRlZENvbHVtbk1vZGVzPy5maW5kKCh7a2V5fSkgPT4ga2V5ID09PSBjb2x1bW5Nb2RlKT8ucmVxdWlyZWRDb2x1bW5zO1xufVxuXG4vKipcbiAqIFJldHVybnMgZ2VvYXJyb3cgZmllbGRzIHdpdGggQVJST1c6ZXh0ZW5zaW9uOm5hbWUgUE9JTlQgbWV0YWRhdGFcbiAqIEBwYXJhbSBmaWVsZHMgQW55IGZpZWxkc1xuICogQHJldHVybnMgZ2VvYXJyb3cgZmllbGRzIHdpdGggQVJST1c6ZXh0ZW5zaW9uOm5hbWUgUE9JTlQgbWV0YWRhdGFcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEdlb0Fycm93UG9pbnRGaWVsZHMoZmllbGRzOiBGaWVsZFtdKTogRmllbGRbXSB7XG4gIHJldHVybiBmaWVsZHMuZmlsdGVyKGZpZWxkID0+IHtcbiAgICByZXR1cm4gKFxuICAgICAgZmllbGQudHlwZSA9PT0gJ2dlb2Fycm93JyAmJlxuICAgICAgZmllbGQubWV0YWRhdGE/LmdldChHRU9BUlJPV19NRVRBREFUQV9LRVkpID09PSBHRU9BUlJPV19FWFRFTlNJT05TLlBPSU5UXG4gICAgKTtcbiAgfSk7XG59XG5cbi8qKlxuICogQnVpbGRzIGFuIGFycm93IHZlY3RvciBjb21wYXRpYmxlIHdpdGggQVJST1c6ZXh0ZW5zaW9uOm5hbWUgZ2VvYXJyb3cucG9pbnQuXG4gKiBAcGFyYW0gZ2V0UG9zaXRpb24gUG9zaXRpb24gYWNjZXNzb3IuXG4gKiBAcGFyYW0gbnVtRWxlbWVudHMgTnVtYmVyIG9mIGVsZW1lbnRzIGluIHRoZSB2ZWN0b3IuXG4gKiBAcmV0dXJucyBBbiBhcnJvdyB2ZWN0b3IgY29tcGF0aWJsZSB3aXRoIEFSUk9XOmV4dGVuc2lvbjpuYW1lIGdlb2Fycm93LnBvaW50LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlR2VvQXJyb3dQb2ludFZlY3RvcihcbiAgZGF0YUNvbnRhaW5lcjogQXJyb3dEYXRhQ29udGFpbmVyLFxuICBnZXRQb3NpdGlvbjogKHtpbmRleH06IHtpbmRleDogbnVtYmVyfSkgPT4gbnVtYmVyW11cbik6IGFycm93LlZlY3RvciB7XG4gIC8vIFRPRE8gdXBkYXRlL3Jlc2l6ZSBleGlzdGluZyB2ZWN0b3I/XG4gIC8vIFRPRE8gZmluZCBhbiBlYXNpZXIgd2F5IHRvIGNyZWF0ZSBwb2ludCBnZW8gY29sdW1uc1xuICAvLyBpbiBhIGNvcnJlY3QgYXJyb3cgZm9ybWF0LCBhcyB0aGlzIGFwcHJvYWNoIHNlZW1zIHRvbyBleGNlc3NpdmUgZm9yIGp1c3QgYSBzaW1wbGUgaW50ZXJsZWF2ZWQgYnVmZmVyLlxuXG4gIGNvbnN0IG51bUVsZW1lbnRzID0gZGF0YUNvbnRhaW5lci5udW1Sb3dzKCk7XG4gIGNvbnN0IHRhYmxlID0gZGF0YUNvbnRhaW5lci5nZXRUYWJsZSgpO1xuXG4gIGNvbnN0IG51bUNvb3JkcyA9IG51bUVsZW1lbnRzID4gMCA/IGdldFBvc2l0aW9uKHtpbmRleDogMH0pLmxlbmd0aCA6IDI7XG4gIGNvbnN0IHByZWNpc2lvbiA9IDI7XG5cbiAgY29uc3QgbWV0YWRhdGEgPSBuZXcgTWFwKCk7XG4gIG1ldGFkYXRhLnNldChHRU9BUlJPV19NRVRBREFUQV9LRVksIEdFT0FSUk9XX0VYVEVOU0lPTlMuUE9JTlQpO1xuXG4gIGNvbnN0IGNoaWxkRmllbGQgPSBuZXcgYXJyb3cuRmllbGQoJ3h5eicsIG5ldyBhcnJvdy5GbG9hdChwcmVjaXNpb24pLCBmYWxzZSwgbWV0YWRhdGEpO1xuICBjb25zdCBmaXhlZFNpemVMaXN0ID0gbmV3IGFycm93LkZpeGVkU2l6ZUxpc3QobnVtQ29vcmRzLCBjaGlsZEZpZWxkKTtcbiAgY29uc3QgZmxvYXRCdWlsZGVyID0gbmV3IGFycm93LkZsb2F0QnVpbGRlcih7dHlwZTogbmV3IGFycm93LkZsb2F0KHByZWNpc2lvbil9KTtcbiAgY29uc3QgZml4ZWRTaXplTGlzdEJ1aWxkZXIgPSBuZXcgYXJyb3cuRml4ZWRTaXplTGlzdEJ1aWxkZXIoe3R5cGU6IGZpeGVkU2l6ZUxpc3R9KTtcbiAgZml4ZWRTaXplTGlzdEJ1aWxkZXIuYWRkQ2hpbGQoZmxvYXRCdWlsZGVyKTtcblxuICBjb25zdCBhc3NlbWJsZWRCYXRjaGVzOiBhcnJvdy5EYXRhW10gPSBbXTtcbiAgY29uc3QgaW5kZXhEYXRhID0ge2luZGV4OiAwfTtcbiAgZm9yIChsZXQgYmF0Y2hJbmRleCA9IDA7IGJhdGNoSW5kZXggPCB0YWJsZS5iYXRjaGVzLmxlbmd0aDsgKytiYXRjaEluZGV4KSB7XG4gICAgY29uc3QgbnVtUm93c0luQmF0Y2ggPSB0YWJsZS5iYXRjaGVzW2JhdGNoSW5kZXhdLm51bVJvd3M7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IG51bVJvd3NJbkJhdGNoOyArK2kpIHtcbiAgICAgIGNvbnN0IHBvcyA9IGdldFBvc2l0aW9uKGluZGV4RGF0YSk7XG4gICAgICBmaXhlZFNpemVMaXN0QnVpbGRlci5hcHBlbmQocG9zKTtcblxuICAgICAgKytpbmRleERhdGEuaW5kZXg7XG4gICAgfVxuICAgIGFzc2VtYmxlZEJhdGNoZXMucHVzaChmaXhlZFNpemVMaXN0QnVpbGRlci5mbHVzaCgpKTtcbiAgfVxuXG4gIHJldHVybiBhcnJvdy5tYWtlVmVjdG9yKGFzc2VtYmxlZEJhdGNoZXMpO1xufVxuXG4vKipcbiAqIEJ1aWxkcyBhIGZpbHRlcmVkIGluZGV4IHN1aXRhYmxlIGZvciBGaWx0ZXJBcnJvd0V4dGVuc2lvbi5cbiAqIEBwYXJhbSBudW1FbGVtZW50cyBTaXplIGZvciBmaWx0ZXJlZCBpbmRleCBhcnJheS5cbiAqIEBwYXJhbSB2aXNpYmxlSW5kaWNlcyBBbiBhcnJheSB3aXRoIGluZGljZXMgb2YgZWxlbWVudHMgdGhhdCBhcmVuJ3QgZmlsdGVyZWQgb3V0LlxuICogQHJldHVybnMgZmlsdGVyZWRJbmRleCBbMHwxXSBhcnJheSBmb3IgR1BVIGZpbHRlcmluZ1xuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RmlsdGVyZWRJbmRleChcbiAgbnVtRWxlbWVudHM6IG51bWJlcixcbiAgdmlzaWJsZUluZGljZXM6IG51bWJlcltdLFxuICBleGlzdGluZ0ZpbHRlcmVkSW5kZXg6IFVpbnQ4Q2xhbXBlZEFycmF5IHwgbnVsbFxuKSB7XG4gIC8vIGNvbnRlbnRzIGFyZSBpbml0aWFsaXplZCB3aXRoIHplcm9zIGJ5IGRlZmF1bHQsIG1lYW5pbmcgbm90IHZpc2libGVcbiAgY29uc3QgZmlsdGVyZWRJbmRleCA9XG4gICAgZXhpc3RpbmdGaWx0ZXJlZEluZGV4ICYmIGV4aXN0aW5nRmlsdGVyZWRJbmRleC5sZW5ndGggPT09IG51bUVsZW1lbnRzXG4gICAgICA/IGV4aXN0aW5nRmlsdGVyZWRJbmRleFxuICAgICAgOiBuZXcgVWludDhDbGFtcGVkQXJyYXkobnVtRWxlbWVudHMpO1xuICBmaWx0ZXJlZEluZGV4LmZpbGwoMCk7XG5cbiAgaWYgKHZpc2libGVJbmRpY2VzKSB7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCB2aXNpYmxlSW5kaWNlcy5sZW5ndGg7ICsraSkge1xuICAgICAgZmlsdGVyZWRJbmRleFt2aXNpYmxlSW5kaWNlc1tpXV0gPSAxO1xuICAgIH1cbiAgfVxuICByZXR1cm4gZmlsdGVyZWRJbmRleDtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGFuIGFycmF5IG9mIG5laWdoYm9ycyB0byB0aGUgc3BlY2lmaWVkIGluZGV4LlxuICogQHBhcmFtIG5laWdoYm9yc0ZpZWxkIExheWVyQ29sdW1uIGZpZWxkIHdpdGggaW5mb3JtYXRpb24gYWJvdXQgbmVpZ2hib3JzLlxuICogQHBhcmFtIGRhdGFDb250YWluZXIgRGF0YSBjb250YWluZXIuXG4gKiBAcGFyYW0gaW5kZXggSW5kZXggb2YgaW50ZXJlc3QuXG4gKiBAcGFyYW0gZ2V0UG9zaXRpb24gUG9zaXRpb24gYWNjZXNzb3IuXG4gKiBAcmV0dXJucyBBbiBhcnJheSB3aXRoIGluZm9ybWF0aW9uIGFib3V0IG5laWdoYm9ycy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldE5laWdoYm9ycyhcbiAgbmVpZ2hib3JzRmllbGQ6IExheWVyQ29sdW1uIHwgdW5kZWZpbmVkLFxuICBkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlLFxuICBpbmRleDogbnVtYmVyLFxuICBnZXRQb3NpdGlvbjogKHtpbmRleH06IHtpbmRleDogbnVtYmVyfSkgPT4gbnVtYmVyW11cbik6IHtpbmRleDogbnVtYmVyOyBwb3NpdGlvbjogbnVtYmVyW119W10ge1xuICBpZiAoIW5laWdoYm9yc0ZpZWxkIHx8IG5laWdoYm9yc0ZpZWxkLmZpZWxkSWR4IDwgMCkgcmV0dXJuIFtdO1xuXG4gIGxldCBuZWlnaGJvckluZGljZXMgPSBkYXRhQ29udGFpbmVyLnZhbHVlQXQoaW5kZXgsIG5laWdoYm9yc0ZpZWxkLmZpZWxkSWR4KTtcbiAgLy8gSW4gY2FzZSBvZiBhcnJvdyBjb2x1bW4gd2l0aCBhbiBhcnJheSBvZiBpbmRpY2VzLlxuICBpZiAobmVpZ2hib3JJbmRpY2VzLnRvQXJyYXkpIHtcbiAgICBuZWlnaGJvckluZGljZXMgPSBBcnJheS5mcm9tKG5laWdoYm9ySW5kaWNlcy50b0FycmF5KCkpO1xuICB9XG4gIGlmICghQXJyYXkuaXNBcnJheShuZWlnaGJvckluZGljZXMpKSByZXR1cm4gW107XG5cbiAgLy8gZmluZCBuZWlnaGJvclxuICBjb25zdCBuZWlnaGJvcnNEYXRhID0gbmVpZ2hib3JJbmRpY2VzLm1hcChpZHggPT4gKHtcbiAgICBpbmRleDogaWR4LFxuICAgIHBvc2l0aW9uOiBnZXRQb3NpdGlvbih7aW5kZXg6IGlkeH0pXG4gIH0pKTtcblxuICByZXR1cm4gbmVpZ2hib3JzRGF0YTtcbn1cblxuLyoqXG4gKiBSZXR1cm5zIGJvdW5kcyBmcm9tIGEgZ2VvYXJyb3cgZmllbGQuXG4gKiBUT0RPOiByZWZhY3RvciBvbmNlIG1ldGFkYXRhIGV4dHJhY3Rpb24gZnJvbSBwYXJxdWV0IHRvIGFycm93IHZlY3RvcnMgaXMgaW4gcGxhY2UuXG4gKiBAcGFyYW0gbGF5ZXJDb2x1bW4gTGF5ZXIgY29sdW1ucyBmb3Igd2hpY2ggdG8gY2hlY2sgZm9yIGEgYm91bmRpbmcgYm94LlxuICogQHBhcmFtIGRhdGFDb250YWluZXIgRGF0YSBjb250YWluZXIgd2l0aCBnZW9hcnJvdyBtZXRhZGF0YS5cbiAqIEByZXR1cm5zIFJldHVybnMgYm91bmRpbmcgYm94IGlmIGV4aXN0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldEJvdW5kc0Zyb21BcnJvd01ldGFkYXRhKFxuICBsYXllckNvbHVtbjogTGF5ZXJDb2x1bW4sXG4gIGRhdGFDb250YWluZXI6IEFycm93RGF0YUNvbnRhaW5lclxuKTogW251bWJlciwgbnVtYmVyLCBudW1iZXIsIG51bWJlcl0gfCBmYWxzZSB7XG4gIHRyeSB7XG4gICAgY29uc3QgZmllbGQgPSBkYXRhQ29udGFpbmVyLmdldEZpZWxkKGxheWVyQ29sdW1uLmZpZWxkSWR4KTtcbiAgICBjb25zdCB0YWJsZSA9IGRhdGFDb250YWluZXIuZ2V0VGFibGUoKTtcblxuICAgIGNvbnN0IGdlb01ldGFkYXRhID0gZ2V0R2VvTWV0YWRhdGEoe1xuICAgICAgbWV0YWRhdGE6IHtcbiAgICAgICAgLy8gQHRzLWV4cGVjdC1lcnJvclxuICAgICAgICBnZW86IHRhYmxlLnNjaGVtYS5tZXRhZGF0YS5nZXQoJ2dlbycpXG4gICAgICB9XG4gICAgfSk7XG5cbiAgICBpZiAoZ2VvTWV0YWRhdGEpIHtcbiAgICAgIGNvbnN0IGZpZWxkTWV0YWRhdGEgPSBnZW9NZXRhZGF0YS5jb2x1bW5zW2ZpZWxkLm5hbWVdO1xuICAgICAgaWYgKGZpZWxkTWV0YWRhdGEpIHtcbiAgICAgICAgY29uc3QgYm91bmRzRnJvbU1ldGFkYXRhID0gZmllbGRNZXRhZGF0YVsnYmJveCddO1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShib3VuZHNGcm9tTWV0YWRhdGEpICYmIGJvdW5kc0Zyb21NZXRhZGF0YS5sZW5ndGggPT09IDQpIHtcbiAgICAgICAgICByZXR1cm4gYm91bmRzRnJvbU1ldGFkYXRhO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9IGNhdGNoIChlcnJvcikge1xuICAgIC8vIGlnbm9yZSBmb3Igbm93XG4gIH1cblxuICByZXR1cm4gZmFsc2U7XG59XG5cbi8qKlxuICogRmluZHMgYW5kIHJldHVybnMgdGhlIGZpcnN0IHNhdGlzZmllZCBjb2x1bW4gbW9kZSBiYXNlZCBvbiB0aGUgcHJvdmlkZWQgY29sdW1ucyBhbmQgZmllbGRzLlxuICogQHBhcmFtIHN1cHBvcnRlZENvbHVtbk1vZGVzIC0gQW4gYXJyYXkgb2Ygc3VwcG9ydGVkIGNvbHVtbiBtb2RlcyB0byBjaGVjay5cbiAqIEBwYXJhbSBjb2x1bW5zIC0gVGhlIGF2YWlsYWJsZSBjb2x1bW5zLlxuICogQHBhcmFtIGZpZWxkcyAtIE9wdGlvbmFsIHRhYmxlIGZpZWxkcyB0byBiZSB1c2VkIGZvciBleHRyYSB2ZXJpZmljYXRpb24uXG4gKiBAcmV0dXJucyBUaGUgZmlyc3QgY29sdW1uIG1vZGUgdGhhdCBzYXRpc2ZpZXMgdGhlIHJlcXVpcmVkIGNvbmRpdGlvbnMsIG9yIHVuZGVmaW5lZCBpZiBub25lIG1hdGNoLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0U2F0aXNmaWVkQ29sdW1uTW9kZShcbiAgY29sdW1uTW9kZXM6IFN1cHBvcnRlZENvbHVtbk1vZGVbXSB8IG51bGwsXG4gIGNvbHVtbnM6IExheWVyQ29sdW1ucyB8IHVuZGVmaW5lZCxcbiAgZmllbGRzPzogS2VwbGVyVGFibGVbJ2ZpZWxkcyddXG4pOiBTdXBwb3J0ZWRDb2x1bW5Nb2RlIHwgdW5kZWZpbmVkIHtcbiAgcmV0dXJuIGNvbHVtbk1vZGVzPy5maW5kKG1vZGUgPT4ge1xuICAgIHJldHVybiBtb2RlLnJlcXVpcmVkQ29sdW1ucz8uZXZlcnkocmVxdXJpZWRDb2wgPT4ge1xuICAgICAgY29uc3QgY29sdW1uID0gY29sdW1ucz8uW3JlcXVyaWVkQ29sXTtcbiAgICAgIGlmIChjb2x1bW4/LnZhbHVlKSB7XG4gICAgICAgIGlmIChtb2RlLnZlcmlmeUZpZWxkICYmIGZpZWxkcz8uW2NvbHVtbi5maWVsZElkeF0pIHtcbiAgICAgICAgICBjb25zdCBmaWVsZCA9IGZpZWxkc1tjb2x1bW4uZmllbGRJZHhdO1xuICAgICAgICAgIHJldHVybiBtb2RlLnZlcmlmeUZpZWxkKGZpZWxkKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbi8qKlxuICogUmV0dXJucyB0cnVlIGlmIHRoZSBmaWVsZCBpcyBvZiBnZW9hcnJvdyBwb2ludCBmb3JtYXQuXG4gKiBAcGFyYW0gZmllbGQgQSBmaWVsZC5cbiAqIEByZXR1cm5zIFJldHVybnMgdHJ1ZSBpZiB0aGUgZmllbGQgaXMgb2YgZ2VvYXJyb3cgcG9pbnQgZm9ybWF0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gaXNHZW9BcnJvd1BvaW50RmllbGQoZmllbGQ6IEZpZWxkKSB7XG4gIHJldHVybiAoXG4gICAgZmllbGQudHlwZSA9PT0gJ2dlb2Fycm93JyAmJlxuICAgIGZpZWxkLm1ldGFkYXRhPy5nZXQoR0VPQVJST1dfTUVUQURBVEFfS0VZKSA9PT0gR0VPQVJST1dfRVhURU5TSU9OUy5QT0lOVFxuICApO1xufVxuXG4vKipcbiAqIENyZWF0ZSBkZWZhdWx0IGdlb2Fycm93IGNvbHVtbiBwcm9wcyBiYXNlZCBvbiB0aGUgZGF0YXNldC5cbiAqIEBwYXJhbSBkYXRhc2V0IEEgZGF0YXNldCB0byBjcmVhdGUgbGF5ZXIgcHJvcHMgZnJvbS5cbiAqIEByZXR1cm5zICBnZW9hcnJvdyBjb2x1bW4gcHJvcHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRHZW9BcnJvd1BvaW50TGF5ZXJQcm9wcyhkYXRhc2V0OiBLZXBsZXJUYWJsZSkge1xuICBjb25zdCB7bGFiZWx9ID0gZGF0YXNldDtcbiAgY29uc3QgYWx0UHJvcHM6IEZpbmREZWZhdWx0TGF5ZXJQcm9wc1tdID0gW107XG4gIGRhdGFzZXQuZmllbGRzLmZvckVhY2goZmllbGQgPT4ge1xuICAgIGlmIChpc0dlb0Fycm93UG9pbnRGaWVsZChmaWVsZCkpIHtcbiAgICAgIGFsdFByb3BzLnB1c2goe1xuICAgICAgICBsYWJlbDogKHR5cGVvZiBsYWJlbCA9PT0gJ3N0cmluZycgJiYgbGFiZWwucmVwbGFjZSgvXFwuW14vLl0rJC8sICcnKSkgfHwgZmllbGQubmFtZSxcbiAgICAgICAgY29sdW1uczoge2dlb2Fycm93OiB7dmFsdWU6IGZpZWxkLm5hbWUsIGZpZWxkSWR4OiBmaWVsZC5maWVsZElkeH19XG4gICAgICB9KTtcbiAgICB9XG4gIH0pO1xuICByZXR1cm4gYWx0UHJvcHM7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUdBLElBQUFBLEtBQUEsR0FBQUMsdUJBQUEsQ0FBQUMsT0FBQTtBQUVBLElBQUFDLElBQUEsR0FBQUQsT0FBQTtBQUVBLElBQUFFLFVBQUEsR0FBQUYsT0FBQTtBQVlBLElBQUFHLE1BQUEsR0FBQUgsT0FBQTtBQU9BLElBQUFJLElBQUEsR0FBQUosT0FBQTtBQUEwQyxTQUFBSyx5QkFBQUMsQ0FBQSw2QkFBQUMsT0FBQSxtQkFBQUMsQ0FBQSxPQUFBRCxPQUFBLElBQUFFLENBQUEsT0FBQUYsT0FBQSxZQUFBRix3QkFBQSxZQUFBQSx5QkFBQUMsQ0FBQSxXQUFBQSxDQUFBLEdBQUFHLENBQUEsR0FBQUQsQ0FBQSxLQUFBRixDQUFBO0FBQUEsU0FBQVAsd0JBQUFPLENBQUEsRUFBQUUsQ0FBQSxTQUFBQSxDQUFBLElBQUFGLENBQUEsSUFBQUEsQ0FBQSxDQUFBSSxVQUFBLFNBQUFKLENBQUEsZUFBQUEsQ0FBQSxnQkFBQUssT0FBQSxDQUFBTCxDQUFBLDBCQUFBQSxDQUFBLHNCQUFBQSxDQUFBLFFBQUFHLENBQUEsR0FBQUosd0JBQUEsQ0FBQUcsQ0FBQSxPQUFBQyxDQUFBLElBQUFBLENBQUEsQ0FBQUcsR0FBQSxDQUFBTixDQUFBLFVBQUFHLENBQUEsQ0FBQUksR0FBQSxDQUFBUCxDQUFBLE9BQUFRLENBQUEsS0FBQUMsU0FBQSxVQUFBQyxDQUFBLEdBQUFDLE1BQUEsQ0FBQUMsY0FBQSxJQUFBRCxNQUFBLENBQUFFLHdCQUFBLFdBQUFDLENBQUEsSUFBQWQsQ0FBQSxvQkFBQWMsQ0FBQSxPQUFBQyxjQUFBLENBQUFDLElBQUEsQ0FBQWhCLENBQUEsRUFBQWMs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