@kepler.gl/utils
Version:
kepler.gl constants used by kepler.gl components, actions and reducers
620 lines (586 loc) • 79.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.addCategoricalValuesToColorMap = addCategoricalValuesToColorMap;
exports.colorBreaksToCategoricalColorMap = colorBreaksToCategoricalColorMap;
exports.colorBreaksToColorMap = colorBreaksToColorMap;
exports.colorMapToCategoricalColorBreaks = colorMapToCategoricalColorBreaks;
exports.colorMapToColorBreaks = colorMapToColorBreaks;
exports.getCategoricalColorMap = getCategoricalColorMap;
exports.getCategoricalColorScale = getCategoricalColorScale;
exports.getDomainStepsbyZoom = getDomainStepsbyZoom;
exports.getHistogramDomain = getHistogramDomain;
exports.getLayerColorScale = getLayerColorScale;
exports.getLegendOfScale = getLegendOfScale;
exports.getLinearDomain = getLinearDomain;
exports.getLogDomain = getLogDomain;
exports.getOrdinalDomain = getOrdinalDomain;
exports.getOrdinalLegends = getOrdinalLegends;
exports.getQuantLabelFormat = getQuantLabelFormat;
exports.getQuantLegends = getQuantLegends;
exports.getQuantileDomain = getQuantileDomain;
exports.getScaleFunction = getScaleFunction;
exports.getThresholdsFromQuantiles = getThresholdsFromQuantiles;
exports.getVisualChannelScaleByZoom = getVisualChannelScaleByZoom;
exports.initCustomPaletteByCustomScale = initCustomPaletteByCustomScale;
exports.initializeLayerColorMap = initializeLayerColorMap;
exports.isDomainQuantile = isDomainQuantile;
exports.isDomainStops = isDomainStops;
exports.isNumericColorBreaks = isNumericColorBreaks;
exports.removeCategoricalValueFromColorMap = removeCategoricalValueFromColorMap;
exports.resetCategoricalColorMapByIndex = resetCategoricalColorMapByIndex;
exports.selectRestCategoricalColorMapByIndex = selectRestCategoricalColorMapByIndex;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
var _d3Array = require("d3-array");
var _uniq = _interopRequireDefault(require("lodash/uniq"));
var _moment = _interopRequireDefault(require("moment"));
var _commonUtils = require("@kepler.gl/common-utils");
var _constants = require("@kepler.gl/constants");
var _colorUtils = require("./color-utils");
var _dataUtils = require("./data-utils");
var _filterUtils = require("./filter-utils");
var _utils = require("./utils");
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
// import {FilterProps, KeplerTable} from '@kepler.gl/layers';
// TODO isolate types - depends on @kepler.gl/layers
/**
* return quantile domain for an array of data
*/
function getQuantileDomain(data, valueAccessor, sortFunc) {
var values = typeof valueAccessor === 'function' ? data.map(valueAccessor) : data;
return values.filter(_commonUtils.notNullorUndefined).sort(sortFunc);
}
/**
* return ordinal domain for a data container
*/
function getOrdinalDomain(dataContainer, valueAccessor) {
var values = dataContainer.mapIndex(valueAccessor);
return (0, _dataUtils.unique)(values).filter(_commonUtils.notNullorUndefined).sort();
}
/**
* return linear domain for an array of data
*/
function getLinearDomain(data, valueAccessor) {
var range = typeof valueAccessor === 'function' ? (0, _d3Array.extent)(data, valueAccessor) : (0, _d3Array.extent)(data);
return range.map(function (d, i) {
return d === undefined ? i : d;
});
}
/**
* return linear domain for an array of data. A log scale domain cannot contain 0
*/
function getLogDomain(data, valueAccessor) {
var _getLinearDomain = getLinearDomain(data, valueAccessor),
_getLinearDomain2 = (0, _slicedToArray2["default"])(_getLinearDomain, 2),
d0 = _getLinearDomain2[0],
d1 = _getLinearDomain2[1];
return [d0 === 0 ? 1e-5 : d0, d1];
}
/**
* whether field domain is stops
*/
function isDomainStops(domain) {
return (0, _utils.isPlainObject)(domain) && Array.isArray(domain.stops) && Array.isArray(domain.z);
}
/**
* whether field domain is quantiles
*/
function isDomainQuantile(domain) {
return (0, _utils.isPlainObject)(domain) && Array.isArray(domain.quantiles) && Array.isArray(domain.z);
}
/**
* get the domain at zoom
*/
function getThresholdsFromQuantiles(quantiles, buckets) {
var thresholds = [];
if (!Number.isFinite(buckets) || buckets < 1) {
return [quantiles[0], quantiles[quantiles.length - 1]];
}
for (var i = 1; i < buckets; i++) {
// position in sorted array
var position = i / buckets;
// @ts-ignore
thresholds.push((0, _d3Array.quantileSorted)(quantiles, position));
}
return thresholds;
}
/**
* get the domain at zoom
*/
function getDomainStepsbyZoom(domain, steps, z) {
var i = (0, _d3Array.bisectLeft)(steps, z);
if (steps[i] === z) {
// If z is an integer value exactly matching a step, return the corresponding domain
return domain[i];
}
// Otherwise, return the next coarsest domain
return domain[Math.max(i - 1, 0)];
}
/**
* Get d3 scale function
*/
function getScaleFunction(scale, range, domain, fixed) {
var scaleFunction = _constants.SCALE_FUNC[fixed ? 'linear' : scale]().domain(domain).range(fixed ? domain : range);
scaleFunction.scaleType = fixed ? 'linear' : scale;
return scaleFunction;
}
/**
* Get threshold scale color labels
*/
function getThresholdLabels(scale, labelFormat) {
var genLength = scale.range().length;
return scale.range().map(function (d, i) {
var invert = scale.invertExtent(d);
var inputs = [i === 0 ? null : (0, _dataUtils.reverseFormatNumber)(labelFormat(invert[0])), i === genLength - 1 ? null : (0, _dataUtils.reverseFormatNumber)(labelFormat(invert[1]))];
return {
// raw value
range: invert,
// formatted value
inputs: inputs,
label: i === 0 ? "Less than ".concat(labelFormat(invert[1])) : i === genLength - 1 ? "".concat(labelFormat(invert[0]), " or more") : "".concat(labelFormat(invert[0]), " to ").concat(labelFormat(invert[1]))
};
});
}
/**
* Get linear / quant scale color labels
*/
function getScaleLabels(scale, labelFormat) {
return scale.range().map(function (d) {
// @ts-ignore
var invert = scale.invertExtent(d);
var inputs = [(0, _dataUtils.reverseFormatNumber)(labelFormat(invert[0])), (0, _dataUtils.reverseFormatNumber)(labelFormat(invert[1]))];
return {
label: "".concat(labelFormat(invert[0]), " to ").concat(labelFormat(invert[1])),
// raw value
range: invert,
// formatted value
inputs: inputs
};
});
}
var customScaleLabelFormat = function customScaleLabelFormat(n) {
return n ? (0, _dataUtils.formatNumber)(n, 'real') : 'no value';
};
/**
* Get linear / quant scale color breaks
*/
function getQuantLegends(scale, labelFormat) {
if (typeof scale.invertExtent !== 'function') {
return [];
}
var thresholdLabelFormat = function thresholdLabelFormat(n, type) {
return n && labelFormat ? labelFormat(n) : n ? (0, _dataUtils.formatNumber)(n, type) : 'no value';
};
var labels = scale.scaleType === 'threshold' ? getThresholdLabels(scale, thresholdLabelFormat) : scale.scaleType === 'custom' ? getThresholdLabels(scale, customScaleLabelFormat) : getScaleLabels(scale, labelFormat);
var data = scale.range();
return labels.map(function (label, index) {
return _objectSpread({
data: Array.isArray(data[index]) ? (0, _colorUtils.rgbToHex)(data[index]) : data[index]
}, label);
});
}
/**
* Get ordinal color scale legends
*/
function getOrdinalLegends(scale) {
var domain = scale.domain();
var labels = scale.domain();
var data = domain.map(scale);
return data.map(function (datum, index) {
return {
data: (0, _colorUtils.isRgbColor)(datum) ? (0, _colorUtils.rgbToHex)(datum) : datum,
label: labels[index]
};
});
}
var defaultFormat = function defaultFormat(d) {
return d;
};
var getTimeLabelFormat = function getTimeLabelFormat(domain) {
var formatter = (0, _filterUtils.getTimeWidgetHintFormatter)(domain);
return function (val) {
return _moment["default"].utc(val).format(formatter);
};
};
function getQuantLabelFormat(domain, fieldType) {
// quant scale can only be assigned to linear Fields: real, timestamp, integer
return fieldType === _constants.ALL_FIELD_TYPES.timestamp ? getTimeLabelFormat(domain) : !fieldType ? defaultFormat : function (n) {
return (0, _dataUtils.isNumber)(n) ? (0, _dataUtils.formatNumber)(n, fieldType) : 'no value';
};
}
/**
* Get legends for scale
*/
function getLegendOfScale(_ref) {
var scale = _ref.scale,
scaleType = _ref.scaleType,
labelFormat = _ref.labelFormat,
fieldType = _ref.fieldType;
if (!scale || scale.byZoom) {
return [];
}
if (scaleType === _constants.SCALE_TYPES.ordinal || scaleType === _constants.SCALE_TYPES.customOrdinal || fieldType === _constants.ALL_FIELD_TYPES.string) {
return getOrdinalLegends(scale);
}
var formatLabel = labelFormat || getQuantLabelFormat(scale.domain(), fieldType);
return getQuantLegends(scale, formatLabel);
}
/**
* Get color scale function
*/
function getLayerColorScale(_ref2) {
var range = _ref2.range,
domain = _ref2.domain,
scaleType = _ref2.scaleType,
layer = _ref2.layer;
if (range && domain && scaleType) {
return layer.getColorScale(scaleType, domain, range);
}
return null;
}
/**
* Convert colorRange.colorMap into color breaks UI input
*/
function initializeLayerColorMap(layer, visualChannel) {
var domain = layer.config[visualChannel.domain];
var range = layer.config.visConfig[visualChannel.range];
var scaleType = layer.config[visualChannel.scale];
var field = layer.config[visualChannel.field];
var scale = getLayerColorScale({
range: range,
domain: domain,
scaleType: scaleType,
layer: layer
});
var colorBreaks = getLegendOfScale({
scale: scale !== null && scale !== void 0 && scale.byZoom ? scale(0) : scale,
scaleType: scaleType,
fieldType: field.type
});
return colorBreaksToColorMap(colorBreaks);
}
/**
* Get visual chanel scale function if it's based on zoom
*/
function getVisualChannelScaleByZoom(_ref3) {
var _scale;
var scale = _ref3.scale,
layer = _ref3.layer,
mapState = _ref3.mapState;
if ((_scale = scale) !== null && _scale !== void 0 && _scale.byZoom) {
var _layer$meta;
var z = (_layer$meta = layer.meta) !== null && _layer$meta !== void 0 && _layer$meta.getZoom ? layer.meta.getZoom(mapState) : mapState === null || mapState === void 0 ? void 0 : mapState.zoom;
scale = Number.isFinite(z) ? scale(z) : null;
}
return scale;
}
/**
* Get categorical colorMap from colors and domain (unique values)
*/
function getCategoricalColorMap(colors, domain) {
var uniqueValues = (0, _dataUtils.unique)(domain).sort();
var colorMap = colors.map(function (color) {
return [null, color];
});
if (colors.length === 0 || uniqueValues.length === 0) {
return colorMap;
}
var lastIndex = colors.length - 1;
var assignCount = Math.min(lastIndex, uniqueValues.length);
// Assign first values one-to-one up to the penultimate color (if any)
for (var i = 0; i < assignCount; i++) {
// @ts-ignore tuple
colorMap[i][0] = uniqueValues[i];
}
if (uniqueValues.length > colors.length) {
// Aggregate the rest (including the value that would have gone to last color)
// Build aggregated array incrementally to match legacy behavior
var aggregatedValues = [];
for (var _i = lastIndex; _i < uniqueValues.length; _i++) {
var value = uniqueValues[_i];
if (Array.isArray(value)) {
// Spread array elements to match legacy flattening behavior
aggregatedValues.push.apply(aggregatedValues, (0, _toConsumableArray2["default"])(value));
} else {
aggregatedValues.push(value);
}
}
// @ts-ignore tuple
colorMap[lastIndex][0] = aggregatedValues;
} else if (uniqueValues.length === colors.length) {
// Exactly one per color
// @ts-ignore tuple
colorMap[lastIndex][0] = uniqueValues[lastIndex];
}
// @ts-ignore tuple
return colorMap;
}
/**
* Get categorical colorBreaks from colorMap
*/
function colorMapToCategoricalColorBreaks(colorMap) {
if (!colorMap) {
return null;
}
var colorBreaks = colorMap.map(function (_ref4) {
var _ref5 = (0, _slicedToArray2["default"])(_ref4, 2),
value = _ref5[0],
color = _ref5[1];
return {
data: color,
label: value
};
});
return colorBreaks;
}
/**
* create categorical colorMap from colorBreaks
*/
function colorBreaksToCategoricalColorMap(colorBreaks) {
// colorMap: [string | string[], hexstring]
var colors = (0, _uniq["default"])(colorBreaks.map(function (cb) {
return cb.data;
}));
var values = (0, _uniq["default"])(colorBreaks.map(function (cb) {
return cb.label;
}));
return getCategoricalColorMap(colors, values);
}
/**
* Convert color breaks UI input into colorRange.colorMap
*/
function colorBreaksToColorMap(colorBreaks) {
var colorMap = colorBreaks.map(function (colorBreak, i) {
// [value, hex]
return [colorBreak.inputs ? i === colorBreaks.length - 1 ? null // last
: colorBreak.inputs[1] : colorBreak.label, colorBreak.data];
});
// @ts-ignore tuple
return colorMap;
}
/**
* Convert colorRange.colorMap into color breaks UI input
*/
function colorMapToColorBreaks(colorMap) {
if (!colorMap) {
return null;
}
var colorBreaks = colorMap.map(function (_ref6, i) {
var _ref7 = (0, _slicedToArray2["default"])(_ref6, 2),
value = _ref7[0],
color = _ref7[1];
var range = i === 0 ?
// first
[-Infinity, value] :
// last
i === colorMap.length - 1 ? [colorMap[i - 1][0], Infinity] :
// else
[colorMap[i - 1][0], value];
return {
data: color,
range: range,
inputs: range,
label:
// first
i === 0 ? "Less than ".concat(value) :
// last
i === colorMap.length - 1 ? "".concat(colorMap[i - 1][0], " or more") : "".concat(colorMap[i - 1][0], " to ").concat(value)
};
});
// @ts-ignore implement conversion for ordinal
return colorBreaks;
}
/**
* Whether color breaks is for numeric field
*/
function isNumericColorBreaks(colorBreaks) {
return Boolean(Array.isArray(colorBreaks) && colorBreaks.length && colorBreaks[0].inputs);
}
// return domainMin, domainMax, histogramMean
function getHistogramDomain(_ref8) {
var aggregatedBins = _ref8.aggregatedBins,
columnStats = _ref8.columnStats,
dataset = _ref8.dataset,
fieldValueAccessor = _ref8.fieldValueAccessor;
var domainMin = Number.POSITIVE_INFINITY;
var domainMax = Number.NEGATIVE_INFINITY;
var nValid = 0;
var domainSum = 0;
if (aggregatedBins) {
Object.values(aggregatedBins).forEach(function (bin) {
var val = bin.value;
if ((0, _dataUtils.isNumber)(val)) {
if (val < domainMin) domainMin = val;
if (val > domainMax) domainMax = val;
domainSum += val;
nValid += 1;
}
});
} else {
if (columnStats && columnStats.quantiles && columnStats.mean) {
// no need to recalcuate min/max/mean if its already in columnStats
return [columnStats.quantiles[0].value, columnStats.quantiles[columnStats.quantiles.length - 1].value, columnStats.mean];
}
if (dataset && fieldValueAccessor) {
dataset.allIndexes.forEach(function (x) {
var val = fieldValueAccessor(x);
if ((0, _dataUtils.isNumber)(val)) {
if (val < domainMin) domainMin = val;
if (val > domainMax) domainMax = val;
domainSum += val;
nValid += 1;
}
});
}
}
var histogramMean = nValid > 0 ? domainSum / nValid : 0;
return [nValid > 0 ? domainMin : 0, nValid > 0 ? domainMax : 0, histogramMean];
}
function resetCategoricalColorMapByIndex(colorMap, index) {
if (!colorMap) {
return colorMap;
}
var newColorMap = colorMap.map(function (cm, i) {
if (i === index) {
return [null, cm[1]];
}
return cm;
});
return newColorMap;
}
/**
* select rest categorical values for a colorMap by its index
*/
function selectRestCategoricalColorMapByIndex(colorMap, index, uniqueValues) {
if (!colorMap || !uniqueValues) {
return colorMap;
}
// find unique values that has not been used in current colorMap
var uniqValueDict = Object.fromEntries(uniqueValues.map(function (val) {
return [val, false];
}));
colorMap.forEach(function (cm) {
(0, _commonUtils.toArray)(cm[0]).forEach(function (v) {
if (v) uniqValueDict[v] = true;
});
});
var rest = Object.keys(uniqValueDict).filter(function (v) {
return !uniqValueDict[v];
});
// use the not used unique values in the selected color map
var newColorMap = colorMap.map(function (cm, i) {
if (i === index) {
return [[].concat((0, _toConsumableArray2["default"])(rest), (0, _toConsumableArray2["default"])((0, _commonUtils.toArray)(cm[0]))), cm[1]];
}
return cm;
});
return newColorMap;
}
/**
* remove a categorical value from a colorMap by its index
*/
function removeCategoricalValueFromColorMap(colorMap, item, index) {
if (!colorMap) {
return colorMap;
}
var newColorMap = colorMap.map(function (cm, i) {
if (i === index) {
if (!cm[0]) {
return [null, cm[1]];
}
var currentUniqueValues = (0, _commonUtils.toArray)(cm[0]);
var updatedUniqueValues = currentUniqueValues.filter(function (v) {
return v !== item;
});
return [updatedUniqueValues, cm[1]];
}
return cm;
});
return newColorMap;
}
/**
* add categorical values (from multisel dropdown) to a colorMap by its index
*/
function addCategoricalValuesToColorMap(colorMap, items, index) {
if (!colorMap) {
return colorMap;
}
var newColorMap = colorMap.map(function (cm, i) {
if (i === index) {
if (!cm[0]) {
return [items, cm[1]];
}
var _currentUniqueValues = (0, _commonUtils.toArray)(cm[0]);
var updatedUniqueValues = (0, _uniq["default"])(_currentUniqueValues.concat(items));
return [updatedUniqueValues, cm[1]];
}
// remove value from other colorMap
var currentUniqueValues = cm[0];
if (Array.isArray(currentUniqueValues)) {
var _updatedUniqueValues = currentUniqueValues.filter(function (v) {
return !items.includes(v);
});
return [_updatedUniqueValues, cm[1]];
} else if (currentUniqueValues && items.includes(currentUniqueValues)) {
return [null, cm[1]];
}
return cm;
});
return newColorMap;
}
/**
* get a color scale func for categorical (custom ordinal) scale
*/
function getCategoricalColorScale(colorDomain, colorRange) {
var useRgb = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
var cMap = colorRange.colorMap ? colorRange.colorMap : getCategoricalColorMap(colorRange.colors, colorDomain);
var range = [];
var domain = [];
cMap.forEach(function (cm) {
if (Array.isArray(cm[0])) {
cm[0].forEach(function (val) {
domain.push(val);
range.push(useRgb ? (0, _colorUtils.hexToRgb)(cm[1]) : cm[1]);
});
} else {
domain.push(cm[0]);
range.push(useRgb ? (0, _colorUtils.hexToRgb)(cm[1]) : cm[1]);
}
});
var scale = getScaleFunction(_constants.SCALE_TYPES.customOrdinal, range, domain, false);
scale.unknown(_constants.NO_VALUE_COLOR);
return scale;
}
/**
* initialize customPalette by custom scale or customOrdinal scale
*/
function initCustomPaletteByCustomScale(_ref9) {
var scale = _ref9.scale,
field = _ref9.field,
ordinalDomain = _ref9.ordinalDomain,
range = _ref9.range,
colorBreaks = _ref9.colorBreaks;
var customPaletteName = "color.customPalette.".concat(scale, ".").concat((field === null || field === void 0 ? void 0 : field.name) || 'point-count');
// reuse range.colorMap if the field and scale not changed
var reuseColorMap = range.colorMap && range.name === customPaletteName && range.type === scale;
var colorMap = reuseColorMap ? range.colorMap : scale === _constants.SCALE_TYPES.customOrdinal && ordinalDomain ? getCategoricalColorMap(range.colors, ordinalDomain) : colorBreaks && isNumericColorBreaks(colorBreaks) ? colorBreaksToColorMap(colorBreaks) : null;
var colors = reuseColorMap ? range.colors : colorMap ? colorMap.map(function (cm) {
return cm[1];
}) : range.colors;
// update custom breaks
var customPalette = {
category: 'Custom',
name: customPaletteName,
type: scale,
colorMap: colorMap,
colors: colors || []
};
return customPalette;
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZDNBcnJheSIsInJlcXVpcmUiLCJfdW5pcSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfbW9tZW50IiwiX2NvbW1vblV0aWxzIiwiX2NvbnN0YW50cyIsIl9jb2xvclV0aWxzIiwiX2RhdGFVdGlscyIsIl9maWx0ZXJVdGlscyIsIl91dGlscyIsIm93bktleXMiLCJlIiwiciIsInQiLCJPYmplY3QiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImRlZmluZVByb3BlcnR5IiwiZ2V0UXVhbnRpbGVEb21haW4iLCJkYXRhIiwidmFsdWVBY2Nlc3NvciIsInNvcnRGdW5jIiwidmFsdWVzIiwibWFwIiwibm90TnVsbG9yVW5kZWZpbmVkIiwic29ydCIsImdldE9yZGluYWxEb21haW4iLCJkYXRhQ29udGFpbmVyIiwibWFwSW5kZXgiLCJ1bmlxdWUiLCJnZXRMaW5lYXJEb21haW4iLCJyYW5nZSIsImV4dGVudCIsImQiLCJpIiwidW5kZWZpbmVkIiwiZ2V0TG9nRG9tYWluIiwiX2dldExpbmVhckRvbWFpbiIsIl9nZXRMaW5lYXJEb21haW4yIiwiX3NsaWNlZFRvQXJyYXkyIiwiZDAiLCJkMSIsImlzRG9tYWluU3RvcHMiLCJkb21haW4iLCJpc1BsYWluT2JqZWN0IiwiQXJyYXkiLCJpc0FycmF5Iiwic3RvcHMiLCJ6IiwiaXNEb21haW5RdWFudGlsZSIsInF1YW50aWxlcyIsImdldFRocmVzaG9sZHNGcm9tUXVhbnRpbGVzIiwiYnVja2V0cyIsInRocmVzaG9sZHMiLCJOdW1iZXIiLCJpc0Zpbml0ZSIsInBvc2l0aW9uIiwiZDNRdWFudGlsZSIsImdldERvbWFpblN0ZXBzYnlab29tIiwic3RlcHMiLCJiaXNlY3RMZWZ0IiwiTWF0aCIsIm1heCIsImdldFNjYWxlRnVuY3Rpb24iLCJzY2FsZSIsImZpeGVkIiwic2NhbGVGdW5jdGlvbiIsIlNDQUxFX0ZVTkMiLCJzY2FsZVR5cGUiLCJnZXRUaHJlc2hvbGRMYWJlbHMiLCJsYWJlbEZvcm1hdCIsImdlbkxlbmd0aCIsImludmVydCIsImludmVydEV4dGVudCIsImlucHV0cyIsInJldmVyc2VGb3JtYXROdW1iZXIiLCJsYWJlbCIsImNvbmNhdCIsImdldFNjYWxlTGFiZWxzIiwiY3VzdG9tU2NhbGVMYWJlbEZvcm1hdCIsIm4iLCJmb3JtYXROdW1iZXIiLCJnZXRRdWFudExlZ2VuZHMiLCJ0aHJlc2hvbGRMYWJlbEZvcm1hdCIsInR5cGUiLCJsYWJlbHMiLCJpbmRleCIsInJnYlRvSGV4IiwiZ2V0T3JkaW5hbExlZ2VuZHMiLCJkYXR1bSIsImlzUmdiQ29sb3IiLCJkZWZhdWx0Rm9ybWF0IiwiZ2V0VGltZUxhYmVsRm9ybWF0IiwiZm9ybWF0dGVyIiwiZ2V0VGltZVdpZGdldEhpbnRGb3JtYXR0ZXIiLCJ2YWwiLCJtb21lbnQiLCJ1dGMiLCJmb3JtYXQiLCJnZXRRdWFudExhYmVsRm9ybWF0IiwiZmllbGRUeXBlIiwiQUxMX0ZJRUxEX1RZUEVTIiwidGltZXN0YW1wIiwiaXNOdW1iZXIiLCJnZXRMZWdlbmRPZlNjYWxlIiwiX3JlZiIsImJ5Wm9vbSIsIlNDQUxFX1RZUEVTIiwib3JkaW5hbCIsImN1c3RvbU9yZGluYWwiLCJzdHJpbmciLCJmb3JtYXRMYWJlbCIsImdldExheWVyQ29sb3JTY2FsZSIsIl9yZWYyIiwibGF5ZXIiLCJnZXRDb2xvclNjYWxlIiwiaW5pdGlhbGl6ZUxheWVyQ29sb3JNYXAiLCJ2aXN1YWxDaGFubmVsIiwiY29uZmlnIiwidmlzQ29uZmlnIiwiZmllbGQiLCJjb2xvckJyZWFrcyIsImNvbG9yQnJlYWtzVG9Db2xvck1hcCIsImdldFZpc3VhbENoYW5uZWxTY2FsZUJ5Wm9vbSIsIl9yZWYzIiwiX3NjYWxlIiwibWFwU3RhdGUiLCJfbGF5ZXIkbWV0YSIsIm1ldGEiLCJnZXRab29tIiwiem9vbSIsImdldENhdGVnb3JpY2FsQ29sb3JNYXAiLCJjb2xvcnMiLCJ1bmlxdWVWYWx1ZXMiLCJjb2xvck1hcCIsImNvbG9yIiwibGFzdEluZGV4IiwiYXNzaWduQ291bnQiLCJtaW4iLCJhZ2dyZWdhdGVkVmFsdWVzIiwidmFsdWUiLCJfdG9Db25zdW1hYmxlQXJyYXkyIiwiY29sb3JNYXBUb0NhdGVnb3JpY2FsQ29sb3JCcmVha3MiLCJfcmVmNCIsIl9yZWY1IiwiY29sb3JCcmVha3NUb0NhdGVnb3JpY2FsQ29sb3JNYXAiLCJ1bmlxIiwiY2IiLCJjb2xvckJyZWFrIiwiY29sb3JNYXBUb0NvbG9yQnJlYWtzIiwiX3JlZjYiLCJfcmVmNyIsIkluZmluaXR5IiwiaXNOdW1lcmljQ29sb3JCcmVha3MiLCJCb29sZWFuIiwiZ2V0SGlzdG9ncmFtRG9tYWluIiwiX3JlZjgiLCJhZ2dyZWdhdGVkQmlucyIsImNvbHVtblN0YXRzIiwiZGF0YXNldCIsImZpZWxkVmFsdWVBY2Nlc3NvciIsImRvbWFpbk1pbiIsIlBPU0lUSVZFX0lORklOSVRZIiwiZG9tYWluTWF4IiwiTkVHQVRJVkVfSU5GSU5JVFkiLCJuVmFsaWQiLCJkb21haW5TdW0iLCJiaW4iLCJtZWFuIiwiYWxsSW5kZXhlcyIsIngiLCJoaXN0b2dyYW1NZWFuIiwicmVzZXRDYXRlZ29yaWNhbENvbG9yTWFwQnlJbmRleCIsIm5ld0NvbG9yTWFwIiwiY20iLCJzZWxlY3RSZXN0Q2F0ZWdvcmljYWxDb2xvck1hcEJ5SW5kZXgiLCJ1bmlxVmFsdWVEaWN0IiwiZnJvbUVudHJpZXMiLCJ0b0FycmF5IiwidiIsInJlc3QiLCJyZW1vdmVDYXRlZ29yaWNhbFZhbHVlRnJvbUNvbG9yTWFwIiwiaXRlbSIsImN1cnJlbnRVbmlxdWVWYWx1ZXMiLCJ1cGRhdGVkVW5pcXVlVmFsdWVzIiwiYWRkQ2F0ZWdvcmljYWxWYWx1ZXNUb0NvbG9yTWFwIiwiaXRlbXMiLCJpbmNsdWRlcyIsImdldENhdGVnb3JpY2FsQ29sb3JTY2FsZSIsImNvbG9yRG9tYWluIiwiY29sb3JSYW5nZSIsInVzZVJnYiIsImNNYXAiLCJoZXhUb1JnYiIsInVua25vd24iLCJOT19WQUxVRV9DT0xPUiIsImluaXRDdXN0b21QYWxldHRlQnlDdXN0b21TY2FsZSIsIl9yZWY5Iiwib3JkaW5hbERvbWFpbiIsImN1c3RvbVBhbGV0dGVOYW1lIiwibmFtZSIsInJldXNlQ29sb3JNYXAiLCJjdXN0b21QYWxldHRlIiwiY2F0ZWdvcnkiXSwic291cmNlcyI6WyIuLi9zcmMvZGF0YS1zY2FsZS11dGlscy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQge2Jpc2VjdExlZnQsIHF1YW50aWxlU29ydGVkIGFzIGQzUXVhbnRpbGUsIGV4dGVudH0gZnJvbSAnZDMtYXJyYXknO1xuaW1wb3J0IHVuaXEgZnJvbSAnbG9kYXNoL3VuaXEnO1xuaW1wb3J0IG1vbWVudCBmcm9tICdtb21lbnQnO1xuXG5pbXBvcnQge25vdE51bGxvclVuZGVmaW5lZCwgdG9BcnJheX0gZnJvbSAnQGtlcGxlci5nbC9jb21tb24tdXRpbHMnO1xuaW1wb3J0IHtBTExfRklFTERfVFlQRVMsIFNDQUxFX0ZVTkMsIFNDQUxFX1RZUEVTLCBOT19WQUxVRV9DT0xPUn0gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuLy8gaW1wb3J0IHtGaWx0ZXJQcm9wcywgS2VwbGVyVGFibGV9IGZyb20gJ0BrZXBsZXIuZ2wvbGF5ZXJzJztcbmltcG9ydCB7XG4gIEFnZ3JlZ2F0ZWRCaW4sXG4gIENvbG9yTWFwLFxuICBDb2xvclJhbmdlLFxuICBIZXhDb2xvcixcbiAgS2VwbGVyTGF5ZXIgYXMgTGF5ZXIsXG4gIE1hcFN0YXRlLFxuICBWaXN1YWxDaGFubmVsLFxuICBWaXN1YWxDaGFubmVsRG9tYWluLFxuICBSR0JDb2xvcixcbiAgUkdCQUNvbG9yLFxuICBDb2xvclVJLFxuICBGaWVsZFxufSBmcm9tICdAa2VwbGVyLmdsL3R5cGVzJztcblxuaW1wb3J0IHtpc1JnYkNvbG9yLCByZ2JUb0hleCwgaGV4VG9SZ2J9IGZyb20gJy4vY29sb3ItdXRpbHMnO1xuaW1wb3J0IHtEYXRhQ29udGFpbmVySW50ZXJmYWNlfSBmcm9tICcuL2RhdGEtY29udGFpbmVyLWludGVyZmFjZSc7XG5pbXBvcnQge2Zvcm1hdE51bWJlciwgaXNOdW1iZXIsIHJldmVyc2VGb3JtYXROdW1iZXIsIHVuaXF1ZX0gZnJvbSAnLi9kYXRhLXV0aWxzJztcbmltcG9ydCB7Z2V0VGltZVdpZGdldEhpbnRGb3JtYXR0ZXJ9IGZyb20gJy4vZmlsdGVyLXV0aWxzJztcbmltcG9ydCB7aXNQbGFpbk9iamVjdH0gZnJvbSAnLi91dGlscyc7XG5cbmV4cG9ydCB0eXBlIENvbG9yQnJlYWsgPSB7XG4gIGRhdGE6IEhleENvbG9yO1xuICBsYWJlbDogc3RyaW5nO1xuICByYW5nZTogbnVtYmVyW107XG4gIGlucHV0czogbnVtYmVyW107XG59O1xuZXhwb3J0IHR5cGUgQ29sb3JCcmVha09yZGluYWwgPSB7XG4gIGRhdGE6IEhleENvbG9yO1xuICBsYWJlbDogc3RyaW5nIHwgbnVtYmVyIHwgc3RyaW5nW10gfCBudW1iZXJbXSB8IG51bGw7XG59O1xuXG5leHBvcnQgdHlwZSBEM1NjYWxlRnVuY3Rpb24gPSBSZWNvcmQ8c3RyaW5nLCBhbnk+ICYgKCh4OiBhbnkpID0+IGFueSk7XG5cbi8vIFRPRE8gaXNvbGF0ZSB0eXBlcyAtIGRlcGVuZHMgb24gQGtlcGxlci5nbC9sYXllcnNcbnR5cGUgRmlsdGVyUHJvcHMgPSBhbnk7XG50eXBlIEtlcGxlclRhYmxlID0gYW55O1xuXG5leHBvcnQgdHlwZSBMYWJlbEZvcm1hdCA9IChuOiBudW1iZXIsIHR5cGU/OiBzdHJpbmcpID0+IHN0cmluZztcbnR5cGUgZGF0YVZhbHVlQWNjZXNzb3IgPSA8VD4ocGFyYW06IFQpID0+IFQ7XG50eXBlIGRhdGFDb250YWluZXJWYWx1ZUFjY2Vzc29yID0gKGQ6IHtpbmRleDogbnVtYmVyfSwgZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+IGFueTtcbnR5cGUgc29ydCA9IChhOiBhbnksIGI6IGFueSkgPT4gYW55O1xuLyoqXG4gKiByZXR1cm4gcXVhbnRpbGUgZG9tYWluIGZvciBhbiBhcnJheSBvZiBkYXRhXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRRdWFudGlsZURvbWFpbihcbiAgZGF0YTogYW55W10sXG4gIHZhbHVlQWNjZXNzb3I/OiBkYXRhVmFsdWVBY2Nlc3NvcixcbiAgc29ydEZ1bmM/OiBzb3J0XG4pOiBudW1iZXJbXSB7XG4gIGNvbnN0IHZhbHVlcyA9IHR5cGVvZiB2YWx1ZUFjY2Vzc29yID09PSAnZnVuY3Rpb24nID8gZGF0YS5tYXAodmFsdWVBY2Nlc3NvcikgOiBkYXRhO1xuXG4gIHJldHVybiB2YWx1ZXMuZmlsdGVyKG5vdE51bGxvclVuZGVmaW5lZCkuc29ydChzb3J0RnVuYyk7XG59XG5cbi8qKlxuICogcmV0dXJuIG9yZGluYWwgZG9tYWluIGZvciBhIGRhdGEgY29udGFpbmVyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRpbmFsRG9tYWluKFxuICBkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlLFxuICB2YWx1ZUFjY2Vzc29yOiBkYXRhQ29udGFpbmVyVmFsdWVBY2Nlc3NvclxuKTogc3RyaW5nW10ge1xuICBjb25zdCB2YWx1ZXMgPSBkYXRhQ29udGFpbmVyLm1hcEluZGV4KHZhbHVlQWNjZXNzb3IpO1xuXG4gIHJldHVybiB1bmlxdWUodmFsdWVzKS5maWx0ZXIobm90TnVsbG9yVW5kZWZpbmVkKS5zb3J0KCk7XG59XG5cbi8qKlxuICogcmV0dXJuIGxpbmVhciBkb21haW4gZm9yIGFuIGFycmF5IG9mIGRhdGFcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVhckRvbWFpbihcbiAgZGF0YTogbnVtYmVyW10sXG4gIHZhbHVlQWNjZXNzb3I/OiBkYXRhVmFsdWVBY2Nlc3NvclxuKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGNvbnN0IHJhbmdlID0gdHlwZW9mIHZhbHVlQWNjZXNzb3IgPT09ICdmdW5jdGlvbicgPyBleHRlbnQoZGF0YSwgdmFsdWVBY2Nlc3NvcikgOiBleHRlbnQoZGF0YSk7XG4gIHJldHVybiByYW5nZS5tYXAoKGQ6IHVuZGVmaW5lZCB8IG51bWJlciwgaTogbnVtYmVyKSA9PiAoZCA9PT0gdW5kZWZpbmVkID8gaSA6IGQpKSBhcyBbXG4gICAgbnVtYmVyLFxuICAgIG51bWJlclxuICBdO1xufVxuXG4vKipcbiAqIHJldHVybiBsaW5lYXIgZG9tYWluIGZvciBhbiBhcnJheSBvZiBkYXRhLiBBIGxvZyBzY2FsZSBkb21haW4gY2Fubm90IGNvbnRhaW4gMFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TG9nRG9tYWluKGRhdGE6IGFueVtdLCB2YWx1ZUFjY2Vzc29yOiBkYXRhVmFsdWVBY2Nlc3Nvcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBbZDAsIGQxXSA9IGdldExpbmVhckRvbWFpbihkYXRhLCB2YWx1ZUFjY2Vzc29yKTtcbiAgcmV0dXJuIFtkMCA9PT0gMCA/IDFlLTUgOiBkMCwgZDFdO1xufVxuXG5leHBvcnQgdHlwZSBEb21haW5TdG9wcyA9IHtcbiAgc3RvcHM6IG51bWJlcltdO1xuICB6OiBudW1iZXJbXTtcbn07XG5cbi8qKlxuICogd2hldGhlciBmaWVsZCBkb21haW4gaXMgc3RvcHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRG9tYWluU3RvcHMoZG9tYWluOiB1bmtub3duKTogZG9tYWluIGlzIERvbWFpblN0b3BzIHtcbiAgcmV0dXJuIGlzUGxhaW5PYmplY3QoZG9tYWluKSAmJiBBcnJheS5pc0FycmF5KGRvbWFpbi5zdG9wcykgJiYgQXJyYXkuaXNBcnJheShkb21haW4ueik7XG59XG5cbmV4cG9ydCB0eXBlIERvbWFpblF1YW50aWxlcyA9IHtcbiAgcXVhbnRpbGVzOiBudW1iZXJbXTtcbiAgejogbnVtYmVyW107XG59O1xuXG4vKipcbiAqIHdoZXRoZXIgZmllbGQgZG9tYWluIGlzIHF1YW50aWxlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gaXNEb21haW5RdWFudGlsZShkb21haW46IGFueSk6IGRvbWFpbiBpcyBEb21haW5RdWFudGlsZXMge1xuICByZXR1cm4gaXNQbGFpbk9iamVjdChkb21haW4pICYmIEFycmF5LmlzQXJyYXkoZG9tYWluLnF1YW50aWxlcykgJiYgQXJyYXkuaXNBcnJheShkb21haW4ueik7XG59XG5cbi8qKlxuICogZ2V0IHRoZSBkb21haW4gYXQgem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGhyZXNob2xkc0Zyb21RdWFudGlsZXMoXG4gIHF1YW50aWxlczogbnVtYmVyW10sXG4gIGJ1Y2tldHM6IG51bWJlclxuKTogKG51bWJlciB8IHVuZGVmaW5lZClbXSB7XG4gIGNvbnN0IHRocmVzaG9sZHMgPSBbXTtcbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoYnVja2V0cykgfHwgYnVja2V0cyA8IDEpIHtcbiAgICByZXR1cm4gW3F1YW50aWxlc1swXSwgcXVhbnRpbGVzW3F1YW50aWxlcy5sZW5ndGggLSAxXV07XG4gIH1cbiAgZm9yIChsZXQgaSA9IDE7IGkgPCBidWNrZXRzOyBpKyspIHtcbiAgICAvLyBwb3NpdGlvbiBpbiBzb3J0ZWQgYXJyYXlcbiAgICBjb25zdCBwb3NpdGlvbiA9IGkgLyBidWNrZXRzO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aHJlc2hvbGRzLnB1c2goZDNRdWFudGlsZShxdWFudGlsZXMsIHBvc2l0aW9uKSk7XG4gIH1cblxuICByZXR1cm4gdGhyZXNob2xkcztcbn1cblxuLyoqXG4gKiBnZXQgdGhlIGRvbWFpbiBhdCB6b29tXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXREb21haW5TdGVwc2J5Wm9vbShkb21haW46IGFueVtdLCBzdGVwczogbnVtYmVyW10sIHo6IG51bWJlcik6IGFueSB7XG4gIGNvbnN0IGkgPSBiaXNlY3RMZWZ0KHN0ZXBzLCB6KTtcblxuICBpZiAoc3RlcHNbaV0gPT09IHopIHtcbiAgICAvLyBJZiB6IGlzIGFuIGludGVnZXIgdmFsdWUgZXhhY3RseSBtYXRjaGluZyBhIHN0ZXAsIHJldHVybiB0aGUgY29ycmVzcG9uZGluZyBkb21haW5cbiAgICByZXR1cm4gZG9tYWluW2ldO1xuICB9XG4gIC8vIE90aGVyd2lzZSwgcmV0dXJuIHRoZSBuZXh0IGNvYXJzZXN0IGRvbWFpblxuICByZXR1cm4gZG9tYWluW01hdGgubWF4KGkgLSAxLCAwKV07XG59XG5cbi8qKlxuICogR2V0IGQzIHNjYWxlIGZ1bmN0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRTY2FsZUZ1bmN0aW9uKFxuICBzY2FsZTogc3RyaW5nLFxuICByYW5nZTogYW55W10gfCBJdGVyYWJsZUl0ZXJhdG9yPGFueT4sXG4gIGRvbWFpbjogKG51bWJlciB8IHVuZGVmaW5lZClbXSB8IHN0cmluZ1tdIHwgSXRlcmFibGVJdGVyYXRvcjxhbnk+LFxuICBmaXhlZD86IGJvb2xlYW5cbik6IEQzU2NhbGVGdW5jdGlvbiB7XG4gIGNvbnN0IHNjYWxlRnVuY3Rpb24gPSBTQ0FMRV9GVU5DW2ZpeGVkID8gJ2xpbmVhcicgOiBzY2FsZV0oKVxuICAgIC5kb21haW4oZG9tYWluKVxuICAgIC5yYW5nZShmaXhlZCA/IGRvbWFpbiA6IHJhbmdlKTtcbiAgc2NhbGVGdW5jdGlvbi5zY2FsZVR5cGUgPSBmaXhlZCA/ICdsaW5lYXInIDogc2NhbGU7XG4gIHJldHVybiBzY2FsZUZ1bmN0aW9uO1xufVxuXG4vKipcbiAqIEdldCB0aHJlc2hvbGQgc2NhbGUgY29sb3IgbGFiZWxzXG4gKi9cbmZ1bmN0aW9uIGdldFRocmVzaG9sZExhYmVscyhcbiAgc2NhbGU6IEQzU2NhbGVGdW5jdGlvbixcbiAgbGFiZWxGb3JtYXQ6IExhYmVsRm9ybWF0XG4pOiBPbWl0PENvbG9yQnJlYWssICdkYXRhJz5bXSB7XG4gIGNvbnN0IGdlbkxlbmd0aCA9IHNjYWxlLnJhbmdlKCkubGVuZ3RoO1xuICByZXR1cm4gc2NhbGUucmFuZ2UoKS5tYXAoKGQsIGkpID0+IHtcbiAgICBjb25zdCBpbnZlcnQgPSBzY2FsZS5pbnZlcnRFeHRlbnQoZCk7XG4gICAgY29uc3QgaW5wdXRzID0gW1xuICAgICAgaSA9PT0gMCA/IG51bGwgOiByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFswXSkpLFxuICAgICAgaSA9PT0gZ2VuTGVuZ3RoIC0gMSA/IG51bGwgOiByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFsxXSkpXG4gICAgXTtcbiAgICByZXR1cm4ge1xuICAgICAgLy8gcmF3IHZhbHVlXG4gICAgICByYW5nZTogaW52ZXJ0LFxuICAgICAgLy8gZm9ybWF0dGVkIHZhbHVlXG4gICAgICBpbnB1dHMsXG4gICAgICBsYWJlbDpcbiAgICAgICAgaSA9PT0gMFxuICAgICAgICAgID8gYExlc3MgdGhhbiAke2xhYmVsRm9ybWF0KGludmVydFsxXSl9YFxuICAgICAgICAgIDogaSA9PT0gZ2VuTGVuZ3RoIC0gMVxuICAgICAgICAgID8gYCR7bGFiZWxGb3JtYXQoaW52ZXJ0WzBdKX0gb3IgbW9yZWBcbiAgICAgICAgICA6IGAke2xhYmVsRm9ybWF0KGludmVydFswXSl9IHRvICR7bGFiZWxGb3JtYXQoaW52ZXJ0WzFdKX1gXG4gICAgfTtcbiAgfSk7XG59XG5cbi8qKlxuICogR2V0IGxpbmVhciAvIHF1YW50IHNjYWxlIGNvbG9yIGxhYmVsc1xuICovXG5mdW5jdGlvbiBnZXRTY2FsZUxhYmVscyhcbiAgc2NhbGU6IEQzU2NhbGVGdW5jdGlvbixcbiAgbGFiZWxGb3JtYXQ6IExhYmVsRm9ybWF0XG4pOiBPbWl0PENvbG9yQnJlYWssICdkYXRhJz5bXSB7XG4gIHJldHVybiBzY2FsZS5yYW5nZSgpLm1hcChkID0+IHtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgY29uc3QgaW52ZXJ0ID0gc2NhbGUuaW52ZXJ0RXh0ZW50KGQpO1xuICAgIGNvbnN0IGlucHV0cyA9IFtcbiAgICAgIHJldmVyc2VGb3JtYXROdW1iZXIobGFiZWxGb3JtYXQoaW52ZXJ0WzBdKSksXG4gICAgICByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFsxXSkpXG4gICAgXTtcblxuICAgIHJldHVybiB7XG4gICAgICBsYWJlbDogYCR7bGFiZWxGb3JtYXQoaW52ZXJ0WzBdKX0gdG8gJHtsYWJlbEZvcm1hdChpbnZlcnRbMV0pfWAsXG4gICAgICAvLyByYXcgdmFsdWVcbiAgICAgIHJhbmdlOiBpbnZlcnQsXG4gICAgICAvLyBmb3JtYXR0ZWQgdmFsdWVcbiAgICAgIGlucHV0c1xuICAgIH07XG4gIH0pO1xufVxuXG5jb25zdCBjdXN0b21TY2FsZUxhYmVsRm9ybWF0ID0gbiA9PiAobiA/IGZvcm1hdE51bWJlcihuLCAncmVhbCcpIDogJ25vIHZhbHVlJyk7XG4vKipcbiAqIEdldCBsaW5lYXIgLyBxdWFudCBzY2FsZSBjb2xvciBicmVha3NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFF1YW50TGVnZW5kcyhzY2FsZTogRDNTY2FsZUZ1bmN0aW9uLCBsYWJlbEZvcm1hdDogTGFiZWxGb3JtYXQpOiBDb2xvckJyZWFrW10ge1xuICBpZiAodHlwZW9mIHNjYWxlLmludmVydEV4dGVudCAhPT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuICBjb25zdCB0aHJlc2hvbGRMYWJlbEZvcm1hdCA9IChuLCB0eXBlKSA9PlxuICAgIG4gJiYgbGFiZWxGb3JtYXQgPyBsYWJlbEZvcm1hdChuKSA6IG4gPyBmb3JtYXROdW1iZXIobiwgdHlwZSkgOiAnbm8gdmFsdWUnO1xuICBjb25zdCBsYWJlbHMgPVxuICAgIHNjYWxlLnNjYWxlVHlwZSA9PT0gJ3RocmVzaG9sZCdcbiAgICAgID8gZ2V0VGhyZXNob2xkTGFiZWxzKHNjYWxlLCB0aHJlc2hvbGRMYWJlbEZvcm1hdClcbiAgICAgIDogc2NhbGUuc2NhbGVUeXBlID09PSAnY3VzdG9tJ1xuICAgICAgPyBnZXRUaHJlc2hvbGRMYWJlbHMoc2NhbGUsIGN1c3RvbVNjYWxlTGFiZWxGb3JtYXQpXG4gICAgICA6IGdldFNjYWxlTGFiZWxzKHNjYWxlLCBsYWJlbEZvcm1hdCk7XG5cbiAgY29uc3QgZGF0YSA9IHNjYWxlLnJhbmdlKCk7XG5cbiAgcmV0dXJuIGxhYmVscy5tYXAoKGxhYmVsLCBpbmRleCkgPT4gKHtcbiAgICBkYXRhOiBBcnJheS5pc0FycmF5KGRhdGFbaW5kZXhdKSA/IHJnYlRvSGV4KGRhdGFbaW5kZXhdKSA6IGRhdGFbaW5kZXhdLFxuICAgIC4uLmxhYmVsXG4gIH0pKTtcbn1cblxuLyoqXG4gKiBHZXQgb3JkaW5hbCBjb2xvciBzY2FsZSBsZWdlbmRzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRpbmFsTGVnZW5kcyhzY2FsZTogRDNTY2FsZUZ1bmN0aW9uKTogQ29sb3JCcmVha09yZGluYWxbXSB7XG4gIGNvbnN0IGRvbWFpbiA9IHNjYWxlLmRvbWFpbigpO1xuICBjb25zdCBsYWJlbHMgPSBzY2FsZS5kb21haW4oKTtcbiAgY29uc3QgZGF0YSA9IGRvbWFpbi5tYXAoc2NhbGUpO1xuXG4gIHJldHVybiBkYXRhLm1hcCgoZGF0dW0sIGluZGV4KSA9PiAoe1xuICAgIGRhdGE6IGlzUmdiQ29sb3IoZGF0dW0pID8gcmdiVG9IZXgoZGF0dW0pIDogZGF0dW0sXG4gICAgbGFiZWw6IGxhYmVsc1tpbmRleF1cbiAgfSkpO1xufVxuXG5jb25zdCBkZWZhdWx0Rm9ybWF0ID0gZCA9PiBkO1xuXG5jb25zdCBnZXRUaW1lTGFiZWxGb3JtYXQgPSBkb21haW4gPT4ge1xuICBjb25zdCBmb3JtYXR0ZXIgPSBnZXRUaW1lV2lkZ2V0SGludEZvcm1hdHRlcihkb21haW4pO1xuICByZXR1cm4gdmFsID0+IG1vbWVudC51dGModmFsKS5mb3JtYXQoZm9ybWF0dGVyKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRRdWFudExhYmVsRm9ybWF0KGRvbWFpbiwgZmllbGRUeXBlKSB7XG4gIC8vIHF1YW50IHNjYWxlIGNhbiBvbmx5IGJlIGFzc2lnbmVkIHRvIGxpbmVhciBGaWVsZHM6IHJlYWwsIHRpbWVzdGFtcCwgaW50ZWdlclxuICByZXR1cm4gZmllbGRUeXBlID09PSBBTExfRklFTERfVFlQRVMudGltZXN0YW1wXG4gICAgPyBnZXRUaW1lTGFiZWxGb3JtYXQoZG9tYWluKVxuICAgIDogIWZpZWxkVHlwZVxuICAgID8gZGVmYXVsdEZvcm1hdFxuICAgIDogbiA9PiAoaXNOdW1iZXIobikgPyBmb3JtYXROdW1iZXIobiwgZmllbGRUeXBlKSA6ICdubyB2YWx1ZScpO1xufVxuXG4vKipcbiAqIEdldCBsZWdlbmRzIGZvciBzY2FsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGVnZW5kT2ZTY2FsZSh7XG4gIHNjYWxlLFxuICBzY2FsZVR5cGUsXG4gIGxhYmVsRm9ybWF0LFxuICBmaWVsZFR5cGVcbn06IHtcbiAgc2NhbGU/OiBEM1NjYWxlRnVuY3Rpb24gfCBudWxsO1xuICBzY2FsZVR5cGU6IHN0cmluZztcbiAgbGFiZWxGb3JtYXQ/OiBMYWJlbEZvcm1hdDtcbiAgZmllbGRUeXBlOiBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkO1xufSk6IENvbG9yQnJlYWtbXSB8IENvbG9yQnJlYWtPcmRpbmFsW10ge1xuICBpZiAoIXNjYWxlIHx8IHNjYWxlLmJ5Wm9vbSkge1xuICAgIHJldHVybiBbXTtcbiAgfVxuICBpZiAoXG4gICAgc2NhbGVUeXBlID09PSBTQ0FMRV9UWVBFUy5vcmRpbmFsIHx8XG4gICAgc2NhbGVUeXBlID09PSBTQ0FMRV9UWVBFUy5jdXN0b21PcmRpbmFsIHx8XG4gICAgZmllbGRUeXBlID09PSBBTExfRklFTERfVFlQRVMuc3RyaW5nXG4gICkge1xuICAgIHJldHVybiBnZXRPcmRpbmFsTGVnZW5kcyhzY2FsZSk7XG4gIH1cblxuICBjb25zdCBmb3JtYXRMYWJlbCA9IGxhYmVsRm9ybWF0IHx8IGdldFF1YW50TGFiZWxGb3JtYXQoc2NhbGUuZG9tYWluKCksIGZpZWxkVHlwZSk7XG5cbiAgcmV0dXJuIGdldFF1YW50TGVnZW5kcyhzY2FsZSwgZm9ybWF0TGFiZWwpO1xufVxuXG4vKipcbiAqIEdldCBjb2xvciBzY2FsZSBmdW5jdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGF5ZXJDb2xvclNjYWxlKHtcbiAgcmFuZ2UsXG4gIGRvbWFpbixcbiAgc2NhbGVUeXBlLFxuICBsYXllclxufToge1xuICByYW5nZTogQ29sb3JSYW5nZSB8IG51bGwgfCB1bmRlZmluZWQ7XG4gIGRvbWFpbjogVmlzdWFsQ2hhbm5lbERvbWFpbjtcbiAgc2NhbGVUeXBlOiBzdHJpbmc7XG4gIGxheWVyOiBMYXllcjtcbiAgaXNGaXhlZD86IGJvb2xlYW47XG59KTogRDNTY2FsZUZ1bmN0aW9uIHwgbnVsbCB7XG4gIGlmIChyYW5nZSAmJiBkb21haW4gJiYgc2NhbGVUeXBlKSB7XG4gICAgcmV0dXJuIGxheWVyLmdldENvbG9yU2NhbGUoc2NhbGVUeXBlLCBkb21haW4sIHJhbmdlKTtcbiAgfVxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGNvbG9yUmFuZ2UuY29sb3JNYXAgaW50byBjb2xvciBicmVha3MgVUkgaW5wdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGluaXRpYWxpemVMYXllckNvbG9yTWFwKGxheWVyOiBMYXllciwgdmlzdWFsQ2hhbm5lbDogVmlzdWFsQ2hhbm5lbCk6IENvbG9yTWFwIHtcbiAgY29uc3QgZG9tYWluID0gbGF5ZXIuY29uZmlnW3Zpc3VhbENoYW5uZWwuZG9tYWluXTtcbiAgY29uc3QgcmFuZ2UgPSBsYXllci5jb25maWcudmlzQ29uZmlnW3Zpc3VhbENoYW5uZWwucmFuZ2VdO1xuICBjb25zdCBzY2FsZVR5cGUgPSBsYXllci5jb25maWdbdmlzdWFsQ2hhbm5lbC5zY2FsZV07XG4gIGNvbnN0IGZpZWxkID0gbGF5ZXIuY29uZmlnW3Zpc3VhbENoYW5uZWwuZmllbGRdO1xuXG4gIGNvbnN0IHNjYWxlID0gZ2V0TGF5ZXJDb2xvclNjYWxlKHtcbiAgICByYW5nZSxcbiAgICBkb21haW4sXG4gICAgc2NhbGVUeXBlLFxuICAgIGxheWVyXG4gIH0pO1xuXG4gIGNvbnN0IGNvbG9yQnJlYWtzID0gZ2V0TGVnZW5kT2ZTY2FsZSh7XG4gICAgc2NhbGU6IHNjYWxlPy5ieVpvb20gPyBzY2FsZSgwKSA6IHNjYWxlLFxuICAgIHNjYWxlVHlwZSxcbiAgICBmaWVsZFR5cGU6IGZpZWxkLnR5cGVcbiAgfSk7XG4gIHJldHVybiBjb2xvckJyZWFrc1RvQ29sb3JNYXAoY29sb3JCcmVha3MpO1xufVxuXG4vKipcbiAqIEdldCB2aXN1YWwgY2hhbmVsIHNjYWxlIGZ1bmN0aW9uIGlmIGl0J3MgYmFzZWQgb24gem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VmlzdWFsQ2hhbm5lbFNjYWxlQnlab29tKHtcbiAgc2NhbGUsXG4gIGxheWVyLFxuICBtYXBTdGF0ZVxufToge1xuICBzY2FsZTogRDNTY2FsZUZ1bmN0aW9uIHwgbnVsbDtcbiAgbGF5ZXI6IExheWVyO1xuICBtYXBTdGF0ZT86IE1hcFN0YXRlO1xufSk6IEQzU2NhbGVGdW5jdGlvbiB8IG51bGwge1xuICBpZiAoc2NhbGU/LmJ5Wm9vbSkge1xuICAgIGNvbnN0IHogPSBsYXllci5tZXRhPy5nZXRab29tID8gbGF5ZXIubWV0YS5nZXRab29tKG1hcFN0YXRlKSA6IG1hcFN0YXRlPy56b29tO1xuICAgIHNjYWxlID0gTnVtYmVyLmlzRmluaXRlKHopID8gc2NhbGUoeikgOiBudWxsO1xuICB9XG4gIHJldHVybiBzY2FsZTtcbn1cblxuLyoqXG4gKiBHZXQgY2F0ZWdvcmljYWwgY29sb3JNYXAgZnJvbSBjb2xvcnMgYW5kIGRvbWFpbiAodW5pcXVlIHZhbHVlcylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldENhdGVnb3JpY2FsQ29sb3JNYXAoXG4gIGNvbG9yczogc3RyaW5nW10sXG4gIGRvbWFpbjogKHN0cmluZyB8IG51bWJlciB8IHN0cmluZ1tdIHwgbnVtYmVyW10gfCBudWxsKVtdXG4pOiBhbnkge1xuICBjb25zdCB1bmlxdWVWYWx1ZXMgPSB1bmlxdWUoZG9tYWluKS5zb3J0KCk7XG4gIGNvbnN0IGNvbG9yTWFwID0gY29sb3JzLm1hcChjb2xvciA9PiBbbnVsbCwgY29sb3JdKTtcblxuICBpZiAoY29sb3JzLmxlbmd0aCA9PT0gMCB8fCB1bmlxdWVWYWx1ZXMubGVuZ3RoID09PSAwKSB7XG4gICAgcmV0dXJuIGNvbG9yTWFwO1xuICB9XG5cbiAgY29uc3QgbGFzdEluZGV4ID0gY29sb3JzLmxlbmd0aCAtIDE7XG4gIGNvbnN0IGFzc2lnbkNvdW50ID0gTWF0aC5taW4obGFzdEluZGV4LCB1bmlxdWVWYWx1ZXMubGVuZ3RoKTtcblxuICAvLyBBc3NpZ24gZmlyc3QgdmFsdWVzIG9uZS10by1vbmUgdXAgdG8gdGhlIHBlbnVsdGltYXRlIGNvbG9yIChpZiBhbnkpXG4gIGZvciAobGV0IGkgPSAwOyBpIDwgYXNzaWduQ291bnQ7IGkrKykge1xuICAgIC8vIEB0cy1pZ25vcmUgdHVwbGVcbiAgICBjb2xvck1hcFtpXVswXSA9IHVuaXF1ZVZhbHVlc1tpXTtcbiAgfVxuXG4gIGlmICh1bmlxdWVWYWx1ZXMubGVuZ3RoID4gY29sb3JzLmxlbmd0aCkge1xuICAgIC8vIEFnZ3JlZ2F0ZSB0aGUgcmVzdCAoaW5jbHVkaW5nIHRoZSB2YWx1ZSB0aGF0IHdvdWxkIGhhdmUgZ29uZSB0byBsYXN0IGNvbG9yKVxuICAgIC8vIEJ1aWxkIGFnZ3JlZ2F0ZWQgYXJyYXkgaW5jcmVtZW50YWxseSB0byBtYXRjaCBsZWdhY3kgYmVoYXZpb3JcbiAgICBjb25zdCBhZ2dyZWdhdGVkVmFsdWVzOiBhbnlbXSA9IFtdO1xuICAgIGZvciAobGV0IGkgPSBsYXN0SW5kZXg7IGkgPCB1bmlxdWVWYWx1ZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gdW5pcXVlVmFsdWVzW2ldO1xuICAgICAgaWYgKEFycmF5LmlzQXJyYXkodmFsdWUpKSB7XG4gICAgICAgIC8vIFNwcmVhZCBhcnJheSBlbGVtZW50cyB0byBtYXRjaCBsZWdhY3kgZmxhdHRlbmluZyBiZWhhdmlvclxuICAgICAgICBhZ2dyZWdhdGVkVmFsdWVzLnB1c2goLi4uKHZhbHVlIGFzIGFueVtdKSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBhZ2dyZWdhdGVkVmFsdWVzLnB1c2godmFsdWUpO1xuICAgICAgfVxuICAgIH1cbiAgICAvLyBAdHMtaWdub3JlIHR1cGxlXG4gICAgY29sb3JNYXBbbGFzdEluZGV4XVswXSA9IGFnZ3JlZ2F0ZWRWYWx1ZXM7XG4gIH0gZWxzZSBpZiAodW5pcXVlVmFsdWVzLmxlbmd0aCA9PT0gY29sb3JzLmxlbmd0aCkge1xuICAgIC8vIEV4YWN0bHkgb25lIHBlciBjb2xvclxuICAgIC8vIEB0cy1pZ25vcmUgdHVwbGVcbiAgICBjb2xvck1hcFtsYXN0SW5kZXhdWzBdID0gdW5pcXVlVmFsdWVzW2xhc3RJbmRleF07XG4gIH1cblxuICAvLyBAdHMtaWdub3JlIHR1cGxlXG4gIHJldHVybiBjb2xvck1hcDtcbn1cblxuLyoqXG4gKiBHZXQgY2F0ZWdvcmljYWwgY29sb3JCcmVha3MgZnJvbSBjb2xvck1hcFxuICovXG5leHBvcnQgZnVuY3Rpb24gY29sb3JNYXBUb0NhdGVnb3JpY2FsQ29sb3JCcmVha3MoXG4gIGNvbG9yTWFwPzogQ29sb3JNYXAgfCBudWxsXG4pOiBDb2xvckJyZWFrT3JkaW5hbFtdIHwgbnVsbCB7XG4gIGlmICghY29sb3JNYXApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBjb2xvckJyZWFrcyA9IGNvbG9yTWFwLm1hcCgoW3ZhbHVlLCBjb2xvcl0pID0+IHtcbiAgICByZXR1cm4ge1xuICAgICAgZGF0YTogY29sb3IsXG4gICAgICBsYWJlbDogdmFsdWVcbiAgICB9O1xuICB9KTtcblxuICByZXR1cm4gY29sb3JCcmVha3M7XG59XG5cbi8qKlxuICogY3JlYXRlIGNhdGVnb3JpY2FsIGNvbG9yTWFwIGZyb20gY29sb3JCcmVha3NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yQnJlYWtzVG9DYXRlZ29yaWNhbENvbG9yTWFwKGNvbG9yQnJlYWtzOiBDb2xvckJyZWFrT3JkaW5hbFtdKTogQ29sb3JNYXAge1xuICAvLyBjb2xvck1hcDogW3N0cmluZyB8IHN0cmluZ1tdLCBoZXhzdHJpbmddXG4gIGNvbnN0IGNvbG9ycyA9IHVuaXEoY29sb3JCcmVha3MubWFwKGNiID0+IGNiLmRhdGEpKTtcbiAgY29uc3QgdmFsdWVzID0gdW5pcShjb2xvckJyZWFrcy5tYXAoY2IgPT4gY2IubGFiZWwpKTtcblxuICByZXR1cm4gZ2V0Q2F0ZWdvcmljYWxDb2xvck1hcChjb2xvcnMsIHZhbHVlcyk7XG59XG5cbi8qKlxuICogQ29udmVydCBjb2xvciBicmVha3MgVUkgaW5wdXQgaW50byBjb2xvclJhbmdlLmNvbG9yTWFwXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb2xvckJyZWFrc1RvQ29sb3JNYXAoY29sb3JCcmVha3M6IENvbG9yQnJlYWtbXSB8IENvbG9yQnJlYWtPcmRpbmFsW10pOiBDb2xvck1hcCB7XG4gIGNvbnN0IGNvbG9yTWFwID0gY29sb3JCcmVha3MubWFwKChjb2xvckJyZWFrLCBpKSA9PiB7XG4gICAgLy8gW3ZhbHVlLCBoZXhdXG4gICAgcmV0dXJuIFtcbiAgICAgIGNvbG9yQnJlYWsuaW5wdXRzXG4gICAgICAgID8gaSA9PT0gY29sb3JCcmVha3MubGVuZ3RoIC0gMVxuICAgICAgICAgID8gbnVsbCAvLyBsYXN0XG4gICAgICAgICAgOiBjb2xvckJyZWFrLmlucHV0c1sxXVxuICAgICAgICA6IGNvbG9yQnJlYWsubGFiZWwsXG4gICAgICBjb2xvckJyZWFrLmRhdGFcbiAgICBdO1xuICB9KTtcblxuICAvLyBAdHMtaWdub3JlIHR1cGxlXG4gIHJldHVybiBjb2xvck1hcDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGNvbG9yUmFuZ2UuY29sb3JNYXAgaW50byBjb2xvciBicmVha3MgVUkgaW5wdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yTWFwVG9Db2xvckJyZWFrcyhjb2xvck1hcD86IENvbG9yTWFwIHwgbnVsbCk6IENvbG9yQnJlYWtbXSB8IG51bGwge1xuICBpZiAoIWNvbG9yTWFwKSB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbiAgY29uc3QgY29sb3JCcmVha3MgPSBjb2xvck1hcC5tYXAoKFt2YWx1ZSwgY29sb3JdLCBpKSA9PiB7XG4gICAgY29uc3QgcmFuZ2UgPVxuICAgICAgaSA9PT0gMFxuICAgICAgICA/IC8vIGZpcnN0XG4gICAgICAgICAgWy1JbmZpbml0eSwgdmFsdWVdXG4gICAgICAgIDogLy8gbGFzdFxuICAgICAgICBpID09PSBjb2xvck1hcC5sZW5ndGggLSAxXG4gICAgICAgID8gW2NvbG9yTWFwW2kgLSAxXVswXSwgSW5maW5pdHldXG4gICAgICAgIDogLy8gZWxzZVxuICAgICAgICAgIFtjb2xvck1hcFtpIC0gMV1bMF0sIHZhbHVlXTtcbiAgICByZXR1cm4ge1xuICAgICAgZGF0YTogY29sb3IsXG4gICAgICByYW5nZSxcbiAgICAgIGlucHV0czogcmFuZ2UsXG4gICAgICBsYWJlbDpcbiAgICAgICAgLy8gZmlyc3RcbiAgICAgICAgaSA9PT0gMFxuICAgICAgICAgID8gYExlc3MgdGhhbiAke3ZhbHVlfWBcbiAgICAgICAgICA6IC8vIGxhc3RcbiAgICAgICAgICBpID09PSBjb2xvck1hcC5sZW5ndGggLSAxXG4gICAgICAgICAgPyBgJHtjb2xvck1hcFtpIC0gMV1bMF19IG9yIG1vcmVgXG4gICAgICAgICAgOiBgJHtjb2xvck1hcFtpIC0gMV1bMF19IHRvICR7dmFsdWV9YFxuICAgIH07XG4gIH0pO1xuXG4gIC8vIEB0cy1pZ25vcmUgaW1wbGVtZW50IGNvbnZlcnNpb24gZm9yIG9yZGluYWxcbiAgcmV0dXJuIGNvbG9yQnJlYWtzO1xufVxuXG4vKipcbiAqIFdoZXRoZXIgY29sb3IgYnJlYWtzIGlzIGZvciBudW1lcmljIGZpZWxkXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBpc051bWVyaWNDb2xvckJyZWFrcyhjb2xvckJyZWFrczogdW5rbm93bik6IGNvbG9yQnJlYWtzIGlzIENvbG9yQnJlYWtbXSB7XG4gIHJldHVybiBCb29sZWFuKEFycmF5LmlzQXJyYXkoY29sb3JCcmVha3MpICYmIGNvbG9yQnJlYWtzLmxlbmd0aCAmJiBjb2xvckJyZWFrc1swXS5pbnB1dHMpO1xufVxuXG4vLyByZXR1cm4gZG9tYWluTWluLCBkb21haW5NYXgsIGhpc3RvZ3JhbU1lYW5cbmV4cG9ydCBmdW5jdGlvbiBnZXRIaXN0b2dyYW1Eb21haW4oe1xuICBhZ2dyZWdhdGVkQmlucyxcbiAgY29sdW1uU3RhdHMsXG4gIGRhdGFzZXQsXG4gIGZpZWxkVmFsdWVBY2Nlc3NvclxufToge1xuICBhZ2dyZWdhdGVkQmlucz86IEFnZ3JlZ2F0ZWRCaW5bXTtcbiAgY29sdW1uU3RhdHM/OiBGaWx0ZXJQcm9wc1snY29sdW1uU3RhdHMnXTtcbiAgZGF0YXNldD86IEtlcGxlclRhYmxlO1xuICBmaWVsZFZhbHVlQWNjZXNzb3I6IChpZHg6IHVua25vd24pID0+IG51bWJlcjtcbn0pIHtcbiAgbGV0IGRvbWFpbk1pbiA9IE51bWJlci5QT1NJVElWRV9JTkZJTklUWTtcbiAgbGV0IGRvbWFpbk1heCA9IE51bWJlci5ORUdBVElWRV9JTkZJTklUWTtcbiAgbGV0IG5WYWxpZCA9IDA7XG4gIGxldCBkb21haW5TdW0gPSAwO1xuXG4gIGlmIChhZ2dyZWdhdGVkQmlucykge1xuICAgIE9iamVjdC52YWx1ZXMoYWdncmVnYXRlZEJpbnMpLmZvckVhY2goYmluID0+IHtcbiAgICAgIGNvbnN0IHZhbCA9IGJpbi52YWx1ZTtcbiAgICAgIGlmIChpc051bWJlcih2YWwpKSB7XG4gICAgICAgIGlmICh2YWwgPCBkb21haW5NaW4pIGRvbWFpbk1pbiA9IHZhbDtcbiAgICAgICAgaWYgKHZhbCA+IGRvbWFpbk1heCkgZG9tYWluTWF4ID0gdmFsO1xuICAgICAgICBkb21haW5TdW0gKz0gdmFsO1xuICAgICAgICBuVmFsaWQgKz0gMTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfSBlbHNlIHtcbiAgICBpZiAoY29sdW1uU3RhdHMgJiYgY29sdW1uU3RhdHMucXVhbnRpbGVzICYmIGNvbHVtblN0YXRzLm1lYW4pIHtcbiAgICAgIC8vIG5vIG5lZWQgdG8gcmVjYWxjdWF0ZSBtaW4vbWF4L21lYW4gaWYgaXRzIGFscmVhZHkgaW4gY29sdW1uU3RhdHNcbiAgICAgIHJldHVybiBbXG4gICAgICAgIGNvbHVtblN0YXRzLnF1YW50aWxlc1swXS52YWx1ZSxcbiAgICAgICAgY29sdW1uU3RhdHMucXVhbnRpbGVzW2NvbHVtblN0YXRzLnF1YW50aWxlcy5sZW5ndGggLSAxXS52YWx1ZSxcbiAgICAgICAgY29sdW1uU3RhdHMubWVhblxuICAgICAgXTtcbiAgICB9XG4gICAgaWYgKGRhdGFzZXQgJiYgZmllbGRWYWx1ZUFjY2Vzc29yKSB7XG4gICAgICBkYXRhc2V0LmFsbEluZGV4ZXMuZm9yRWFjaCh4ID0+IHtcbiAgICAgICAgY29uc3QgdmFsID0gZmllbGRWYWx1ZUFjY2Vzc29yKHgpO1xuICAgICAgICBpZiAoaXNOdW1iZXIodmFsKSkge1xuICAgICAgICAgIGlmICh2YWwgPCBkb21haW5NaW4pIGRvbWFpbk1pbiA9IHZhbDtcbiAgICAgICAgICBpZiAodmFsID4gZG9tYWluTWF4KSBkb21haW5NYXggPSB2YWw7XG4gICAgICAgICAgZG9tYWluU3VtICs9IHZhbDtcbiAgICAgICAgICBuVmFsaWQgKz0gMTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgfVxuICB9XG4gIGNvbnN0IGhpc3RvZ3JhbU1lYW4gPSBuVmFsaWQgPiAwID8gZG9tYWluU3VtIC8gblZhbGlkIDogMDtcbiAgcmV0dXJuIFtuVmFsaWQgPiAwID8gZG9tYWluTWluIDogMCwgblZhbGlkID4gMCA/IGRvbWFpbk1heCA6IDAsIGhpc3RvZ3JhbU1lYW5dO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gcmVzZXRDYXRlZ29yaWNhbENvbG9yTWFwQnlJbmRleChjb2xvck1hcDogQ29sb3JNYXAsIGluZGV4OiBudW1iZXIpOiBhbnkge1xuICBpZiAoIWNvbG9yTWFwKSB7XG4gICAgcmV0dXJuIGNvbG9yTWFwO1xuICB9XG4gIGNvbnN0IG5ld0NvbG9yTWFwID0gY29sb3JNYXAubWFwKChjbSwgaSkgPT4ge1xuICAgIGlmIChpID09PSBpbmRleCkge1xuICAgICAgcmV0dXJuIFtudWxsLCBjbVsxXV07XG4gICAgfVxuICAgIHJldHVybiBjbTtcbiAgfSk7XG4gIHJldHVybiBuZXdDb2xvck1hcDtcbn1cblxuLyoqXG4gKiBzZWxlY3QgcmVzdCBjYXRlZ29yaWNhbCB2YWx1ZXMgZm9yIGEgY29sb3JNYXAgYnkgaXRzIGluZGV4XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBzZWxlY3RSZXN0Q2F0ZWdvcmljYWxDb2xvck1hcEJ5SW5kZXgoXG4gIGNvbG9yTWFwOiBDb2xvck1hcCB8IG51bGwsXG4gIGluZGV4OiBudW1iZXIsXG4gIHVuaXF1ZVZhbHVlcz86IG51bWJlcltdIHwgc3RyaW5nW11cbik6IENvbG9yTWFwIHwgdW5kZWZpbmVkIHwgbnVsbCB7XG4gIGlmICghY29sb3JNYXAgfHwgIXVuaXF1ZVZhbHVlcykge1xuICAgIHJldHVybiBjb2xvck1hcDtcbiAgfVxuXG4gIC8vIGZpbmQgdW5pcXVlIHZhbHVlcyB0aGF0IGhhcyBub3QgYmVlbiB1c2VkIGluIGN1cnJlbnQgY29sb3JNYXBcbiAgY29uc3QgdW5pcVZhbHVlRGljdCA9IE9iamVjdC5mcm9tRW50cmllcyh1bmlxdWVWYWx1ZXMubWFwKHZhbCA9PiBbdmFsLCBmYWxzZV0pKTtcbiAgY29sb3JNYXAuZm9yRWFjaChjbSA9PiB7XG4gICAgdG9BcnJheShjbVswXSkuZm9yRWFjaCh2ID0+IHtcbiAgICAgIGlmICh2KSB1bmlxVmFsdWVEaWN0W3ZdID0gdHJ1ZTtcbiAgICB9KTtcbiAgfSk7XG4gIGNvbnN0IHJlc3QgPSBPYmplY3Qua2V5cyh1bmlxVmFsdWVEaWN0KS5maWx0ZXIodiA9PiAhdW5pcVZhbHVlRGljdFt2XSk7XG5cbiAgLy8gdXNlIHRoZSBub3QgdXNlZCB1bmlxdWUgdmFsdWVzIGluIHRoZSBzZWxlY3RlZCBjb2xvciBtYXBcbiAgY29uc3QgbmV3Q29sb3JNYXAgPSBjb2xvck1hcC5tYXAoKGNtLCBpKSA9PiB7XG4gICAgaWYgKGkgPT09IGluZGV4KSB7XG4gICAgICByZXR1cm4gW1suLi5yZXN0LCAuLi50b0FycmF5KGNtWzBdKV0sIGNtWzFdXTtcbiAgICB9XG4gICAgcmV0dXJuIGNtO1xuICB9KSBhcyBDb2xvck1hcDtcblxuICByZXR1cm4gbmV3Q29sb3JNYXA7XG59XG5cbi8qKlxuICogcmVtb3ZlIGEgY2F0ZWdvcmljYWwgdmFsdWUgZnJvbSBhIGNvbG9yTWFwIGJ5IGl0cyBpbmRleFxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVtb3ZlQ2F0ZWdvcmljYWxWYWx1ZUZyb21Db2xvck1hcChcbiAgY29sb3JNYXA6IENvbG9yTWFwIHwgbnVsbCB8IHVuZGVmaW5lZCxcbiAgaXRlbTogbnVtYmVyIHwgc3RyaW5nLFxuICBpbmRleDogbnVtYmVyXG4pOiBDb2xvck1hcCB8IG51bGwgfCB1bmRlZmluZWQge1xuICBpZiAoIWNvbG9yTWFwKSB7XG4gICAgcmV0dXJuIGNvbG9yTWFwO1xuICB9XG4gIGNvbnN0IG5ld0NvbG9yTWFwID0gY29sb3JNYXAubWFwKChjbSwgaSkgPT4ge1xuICAgIGlmIChpID09PSBpbmRleCkge1xuICAgICAgaWYgKCFjbVswXSkge1xuICAgICAgICByZXR1cm4gW251bGwsIGNtWzFdXTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGN1cnJlbnRVbmlxdWVWYWx1ZXMgPSB0b0FycmF5KGNtWzBdKTtcbiAgICAgIGNvbnN0IHVwZGF0ZWRVbmlxdWVWYWx1ZXMgPSBjdXJyZW50VW5pcXVlVmFsdWVzLmZpbHRlcih2ID0+IHYgIT09IGl0ZW0pO1xuICAgICAgcmV0dXJuIFt1cGRhdGVkVW5pcXVlVmFsdWVzLCBjbVsxXV07XG4gICAgfVxuICAgIHJldHVybiBjbTtcbiAgfSkgYXMgQ29sb3JNYXA7XG5cbiAgcmV0dXJuIG5ld0NvbG9yTWFwO1xufVxuXG4vKipcbiAqIGFkZCBjYXRlZ29yaWNhbCB2YWx1ZXMgKGZyb20gbXVsdGlzZWwgZHJvcGRvd24pIHRvIGEgY29sb3JNYXAgYnkgaXRzIGluZGV4XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhZGRDYXRlZ29yaWNhbFZhbHVlc1RvQ29sb3JNYXAoXG4gIGNvbG9yTWFwOiBDb2xvck1hcCxcbiAgaXRlbXM6IChzdHJpbmcgfCBudW1iZXIpW10sXG4gIGluZGV4OiBudW1iZXJcbik6IENvbG9yTWFwIHtcbiAgaWYgKCFjb2xvck1hcCkge1xuICAgIHJldHVybiBjb2xvck1hcDtcbiAgfVxuXG4gIGNvbnN0IG5ld0NvbG9yTWFwID0gY29sb3JNYXAubWFwKChjbSwgaSkgPT4ge1xuICAgIGlmIChpID09PSBpbmRleCkge1xuICAgICAgaWYgKCFjbVswXSkge1xuICAgICAgICByZXR1cm4gW2l0ZW1zLCBjbVsxXV07XG4gICAgICB9XG4gICAgICBjb25zdCBjdXJyZW50VW5pcXVlVmFsdWVzID0gdG9BcnJheShjbVswXSk7XG4gICAgICBjb25zdCB1cGRhdGVkVW5pcXVlVmFsdWVzID0gdW5pcShjdXJyZW50VW5pcXVlVmFsdWVzLmNvbmNhdChpdGVtcykpO1xuICAgICAgcmV0dX