kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
601 lines (569 loc) • 77.8 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 _lodash = _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) {
// colorMap: [string | string[], hexstring]
var colorToUniqueValues = {};
var uniqueValues = (0, _dataUtils.unique)(domain).filter(_commonUtils.notNullorUndefined).sort();
// each unique value assign to a color, the rest unique values assign to last color
var lastColor = colors[colors.length - 1];
for (var i = 0; i < uniqueValues.length; ++i) {
if (i < colors.length) {
colorToUniqueValues[colors[i]] = uniqueValues[i];
} else {
colorToUniqueValues[lastColor] = [].concat((0, _toConsumableArray2["default"])(Array.isArray(colorToUniqueValues[lastColor]) ? colorToUniqueValues[lastColor] : [colorToUniqueValues[lastColor]]), [uniqueValues[i]]);
}
}
var colorMap = colors.map(function (color) {
if (color in colorToUniqueValues) {
return [colorToUniqueValues[color], color];
}
return [null, color];
});
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, _lodash["default"])(colorBreaks.map(function (cb) {
return cb.data;
}));
var values = (0, _lodash["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, _lodash["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.name);
// 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,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZDNBcnJheSIsInJlcXVpcmUiLCJfbG9kYXNoIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9tb21lbnQiLCJfY29tbW9uVXRpbHMiLCJfY29uc3RhbnRzIiwiX2NvbG9yVXRpbHMiLCJfZGF0YVV0aWxzIiwiX2ZpbHRlclV0aWxzIiwiX3V0aWxzIiwib3duS2V5cyIsImUiLCJyIiwidCIsIk9iamVjdCIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiZGVmaW5lUHJvcGVydHkiLCJnZXRRdWFudGlsZURvbWFpbiIsImRhdGEiLCJ2YWx1ZUFjY2Vzc29yIiwic29ydEZ1bmMiLCJ2YWx1ZXMiLCJtYXAiLCJub3ROdWxsb3JVbmRlZmluZWQiLCJzb3J0IiwiZ2V0T3JkaW5hbERvbWFpbiIsImRhdGFDb250YWluZXIiLCJtYXBJbmRleCIsInVuaXF1ZSIsImdldExpbmVhckRvbWFpbiIsInJhbmdlIiwiZXh0ZW50IiwiZCIsImkiLCJ1bmRlZmluZWQiLCJnZXRMb2dEb21haW4iLCJfZ2V0TGluZWFyRG9tYWluIiwiX2dldExpbmVhckRvbWFpbjIiLCJfc2xpY2VkVG9BcnJheTIiLCJkMCIsImQxIiwiaXNEb21haW5TdG9wcyIsImRvbWFpbiIsImlzUGxhaW5PYmplY3QiLCJBcnJheSIsImlzQXJyYXkiLCJzdG9wcyIsInoiLCJpc0RvbWFpblF1YW50aWxlIiwicXVhbnRpbGVzIiwiZ2V0VGhyZXNob2xkc0Zyb21RdWFudGlsZXMiLCJidWNrZXRzIiwidGhyZXNob2xkcyIsIk51bWJlciIsImlzRmluaXRlIiwicG9zaXRpb24iLCJkM1F1YW50aWxlIiwiZ2V0RG9tYWluU3RlcHNieVpvb20iLCJzdGVwcyIsImJpc2VjdExlZnQiLCJNYXRoIiwibWF4IiwiZ2V0U2NhbGVGdW5jdGlvbiIsInNjYWxlIiwiZml4ZWQiLCJzY2FsZUZ1bmN0aW9uIiwiU0NBTEVfRlVOQyIsInNjYWxlVHlwZSIsImdldFRocmVzaG9sZExhYmVscyIsImxhYmVsRm9ybWF0IiwiZ2VuTGVuZ3RoIiwiaW52ZXJ0IiwiaW52ZXJ0RXh0ZW50IiwiaW5wdXRzIiwicmV2ZXJzZUZvcm1hdE51bWJlciIsImxhYmVsIiwiY29uY2F0IiwiZ2V0U2NhbGVMYWJlbHMiLCJjdXN0b21TY2FsZUxhYmVsRm9ybWF0IiwibiIsImZvcm1hdE51bWJlciIsImdldFF1YW50TGVnZW5kcyIsInRocmVzaG9sZExhYmVsRm9ybWF0IiwidHlwZSIsImxhYmVscyIsImluZGV4IiwicmdiVG9IZXgiLCJnZXRPcmRpbmFsTGVnZW5kcyIsImRhdHVtIiwiaXNSZ2JDb2xvciIsImRlZmF1bHRGb3JtYXQiLCJnZXRUaW1lTGFiZWxGb3JtYXQiLCJmb3JtYXR0ZXIiLCJnZXRUaW1lV2lkZ2V0SGludEZvcm1hdHRlciIsInZhbCIsIm1vbWVudCIsInV0YyIsImZvcm1hdCIsImdldFF1YW50TGFiZWxGb3JtYXQiLCJmaWVsZFR5cGUiLCJBTExfRklFTERfVFlQRVMiLCJ0aW1lc3RhbXAiLCJpc051bWJlciIsImdldExlZ2VuZE9mU2NhbGUiLCJfcmVmIiwiYnlab29tIiwiU0NBTEVfVFlQRVMiLCJvcmRpbmFsIiwiY3VzdG9tT3JkaW5hbCIsInN0cmluZyIsImZvcm1hdExhYmVsIiwiZ2V0TGF5ZXJDb2xvclNjYWxlIiwiX3JlZjIiLCJsYXllciIsImdldENvbG9yU2NhbGUiLCJpbml0aWFsaXplTGF5ZXJDb2xvck1hcCIsInZpc3VhbENoYW5uZWwiLCJjb25maWciLCJ2aXNDb25maWciLCJmaWVsZCIsImNvbG9yQnJlYWtzIiwiY29sb3JCcmVha3NUb0NvbG9yTWFwIiwiZ2V0VmlzdWFsQ2hhbm5lbFNjYWxlQnlab29tIiwiX3JlZjMiLCJfc2NhbGUiLCJtYXBTdGF0ZSIsIl9sYXllciRtZXRhIiwibWV0YSIsImdldFpvb20iLCJ6b29tIiwiZ2V0Q2F0ZWdvcmljYWxDb2xvck1hcCIsImNvbG9ycyIsImNvbG9yVG9VbmlxdWVWYWx1ZXMiLCJ1bmlxdWVWYWx1ZXMiLCJsYXN0Q29sb3IiLCJfdG9Db25zdW1hYmxlQXJyYXkyIiwiY29sb3JNYXAiLCJjb2xvciIsImNvbG9yTWFwVG9DYXRlZ29yaWNhbENvbG9yQnJlYWtzIiwiX3JlZjQiLCJfcmVmNSIsInZhbHVlIiwiY29sb3JCcmVha3NUb0NhdGVnb3JpY2FsQ29sb3JNYXAiLCJ1bmlxIiwiY2IiLCJjb2xvckJyZWFrIiwiY29sb3JNYXBUb0NvbG9yQnJlYWtzIiwiX3JlZjYiLCJfcmVmNyIsIkluZmluaXR5IiwiaXNOdW1lcmljQ29sb3JCcmVha3MiLCJCb29sZWFuIiwiZ2V0SGlzdG9ncmFtRG9tYWluIiwiX3JlZjgiLCJhZ2dyZWdhdGVkQmlucyIsImNvbHVtblN0YXRzIiwiZGF0YXNldCIsImZpZWxkVmFsdWVBY2Nlc3NvciIsImRvbWFpbk1pbiIsIlBPU0lUSVZFX0lORklOSVRZIiwiZG9tYWluTWF4IiwiTkVHQVRJVkVfSU5GSU5JVFkiLCJuVmFsaWQiLCJkb21haW5TdW0iLCJiaW4iLCJtZWFuIiwiYWxsSW5kZXhlcyIsIngiLCJoaXN0b2dyYW1NZWFuIiwicmVzZXRDYXRlZ29yaWNhbENvbG9yTWFwQnlJbmRleCIsIm5ld0NvbG9yTWFwIiwiY20iLCJzZWxlY3RSZXN0Q2F0ZWdvcmljYWxDb2xvck1hcEJ5SW5kZXgiLCJ1bmlxVmFsdWVEaWN0IiwiZnJvbUVudHJpZXMiLCJ0b0FycmF5IiwidiIsInJlc3QiLCJyZW1vdmVDYXRlZ29yaWNhbFZhbHVlRnJvbUNvbG9yTWFwIiwiaXRlbSIsImN1cnJlbnRVbmlxdWVWYWx1ZXMiLCJ1cGRhdGVkVW5pcXVlVmFsdWVzIiwiYWRkQ2F0ZWdvcmljYWxWYWx1ZXNUb0NvbG9yTWFwIiwiaXRlbXMiLCJpbmNsdWRlcyIsImdldENhdGVnb3JpY2FsQ29sb3JTY2FsZSIsImNvbG9yRG9tYWluIiwiY29sb3JSYW5nZSIsInVzZVJnYiIsImNNYXAiLCJoZXhUb1JnYiIsInVua25vd24iLCJOT19WQUxVRV9DT0xPUiIsImluaXRDdXN0b21QYWxldHRlQnlDdXN0b21TY2FsZSIsIl9yZWY5Iiwib3JkaW5hbERvbWFpbiIsImN1c3RvbVBhbGV0dGVOYW1lIiwibmFtZSIsInJldXNlQ29sb3JNYXAiLCJjdXN0b21QYWxldHRlIiwiY2F0ZWdvcnkiXSwic291cmNlcyI6WyIuLi9zcmMvZGF0YS1zY2FsZS11dGlscy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQge2Jpc2VjdExlZnQsIHF1YW50aWxlU29ydGVkIGFzIGQzUXVhbnRpbGUsIGV4dGVudH0gZnJvbSAnZDMtYXJyYXknO1xuaW1wb3J0IHVuaXEgZnJvbSAnbG9kYXNoLnVuaXEnO1xuaW1wb3J0IG1vbWVudCBmcm9tICdtb21lbnQnO1xuXG5pbXBvcnQge25vdE51bGxvclVuZGVmaW5lZCwgdG9BcnJheX0gZnJvbSAnQGtlcGxlci5nbC9jb21tb24tdXRpbHMnO1xuaW1wb3J0IHtBTExfRklFTERfVFlQRVMsIFNDQUxFX0ZVTkMsIFNDQUxFX1RZUEVTLCBOT19WQUxVRV9DT0xPUn0gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuLy8gaW1wb3J0IHtGaWx0ZXJQcm9wcywgS2VwbGVyVGFibGV9IGZyb20gJ0BrZXBsZXIuZ2wvbGF5ZXJzJztcbmltcG9ydCB7XG4gIEFnZ3JlZ2F0ZWRCaW4sXG4gIENvbG9yTWFwLFxuICBDb2xvclJhbmdlLFxuICBIZXhDb2xvcixcbiAgS2VwbGVyTGF5ZXIgYXMgTGF5ZXIsXG4gIE1hcFN0YXRlLFxuICBWaXN1YWxDaGFubmVsLFxuICBWaXN1YWxDaGFubmVsRG9tYWluLFxuICBSR0JDb2xvcixcbiAgUkdCQUNvbG9yLFxuICBDb2xvclVJLFxuICBGaWVsZFxufSBmcm9tICdAa2VwbGVyLmdsL3R5cGVzJztcblxuaW1wb3J0IHtpc1JnYkNvbG9yLCByZ2JUb0hleCwgaGV4VG9SZ2J9IGZyb20gJy4vY29sb3ItdXRpbHMnO1xuaW1wb3J0IHtEYXRhQ29udGFpbmVySW50ZXJmYWNlfSBmcm9tICcuL2RhdGEtY29udGFpbmVyLWludGVyZmFjZSc7XG5pbXBvcnQge2Zvcm1hdE51bWJlciwgaXNOdW1iZXIsIHJldmVyc2VGb3JtYXROdW1iZXIsIHVuaXF1ZX0gZnJvbSAnLi9kYXRhLXV0aWxzJztcbmltcG9ydCB7Z2V0VGltZVdpZGdldEhpbnRGb3JtYXR0ZXJ9IGZyb20gJy4vZmlsdGVyLXV0aWxzJztcbmltcG9ydCB7aXNQbGFpbk9iamVjdH0gZnJvbSAnLi91dGlscyc7XG5cbmV4cG9ydCB0eXBlIENvbG9yQnJlYWsgPSB7XG4gIGRhdGE6IEhleENvbG9yO1xuICBsYWJlbDogc3RyaW5nO1xuICByYW5nZTogbnVtYmVyW107XG4gIGlucHV0czogbnVtYmVyW107XG59O1xuZXhwb3J0IHR5cGUgQ29sb3JCcmVha09yZGluYWwgPSB7XG4gIGRhdGE6IEhleENvbG9yO1xuICBsYWJlbDogc3RyaW5nIHwgbnVtYmVyIHwgc3RyaW5nW10gfCBudW1iZXJbXSB8IG51bGw7XG59O1xuXG5leHBvcnQgdHlwZSBEM1NjYWxlRnVuY3Rpb24gPSBSZWNvcmQ8c3RyaW5nLCBhbnk+ICYgKCh4OiBhbnkpID0+IGFueSk7XG5cbi8vIFRPRE8gaXNvbGF0ZSB0eXBlcyAtIGRlcGVuZHMgb24gQGtlcGxlci5nbC9sYXllcnNcbnR5cGUgRmlsdGVyUHJvcHMgPSBhbnk7XG50eXBlIEtlcGxlclRhYmxlID0gYW55O1xuXG5leHBvcnQgdHlwZSBMYWJlbEZvcm1hdCA9IChuOiBudW1iZXIsIHR5cGU/OiBzdHJpbmcpID0+IHN0cmluZztcbnR5cGUgZGF0YVZhbHVlQWNjZXNzb3IgPSA8VD4ocGFyYW06IFQpID0+IFQ7XG50eXBlIGRhdGFDb250YWluZXJWYWx1ZUFjY2Vzc29yID0gKGQ6IHtpbmRleDogbnVtYmVyfSwgZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+IGFueTtcbnR5cGUgc29ydCA9IChhOiBhbnksIGI6IGFueSkgPT4gYW55O1xuLyoqXG4gKiByZXR1cm4gcXVhbnRpbGUgZG9tYWluIGZvciBhbiBhcnJheSBvZiBkYXRhXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRRdWFudGlsZURvbWFpbihcbiAgZGF0YTogYW55W10sXG4gIHZhbHVlQWNjZXNzb3I/OiBkYXRhVmFsdWVBY2Nlc3NvcixcbiAgc29ydEZ1bmM/OiBzb3J0XG4pOiBudW1iZXJbXSB7XG4gIGNvbnN0IHZhbHVlcyA9IHR5cGVvZiB2YWx1ZUFjY2Vzc29yID09PSAnZnVuY3Rpb24nID8gZGF0YS5tYXAodmFsdWVBY2Nlc3NvcikgOiBkYXRhO1xuXG4gIHJldHVybiB2YWx1ZXMuZmlsdGVyKG5vdE51bGxvclVuZGVmaW5lZCkuc29ydChzb3J0RnVuYyk7XG59XG5cbi8qKlxuICogcmV0dXJuIG9yZGluYWwgZG9tYWluIGZvciBhIGRhdGEgY29udGFpbmVyXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRpbmFsRG9tYWluKFxuICBkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlLFxuICB2YWx1ZUFjY2Vzc29yOiBkYXRhQ29udGFpbmVyVmFsdWVBY2Nlc3NvclxuKTogc3RyaW5nW10ge1xuICBjb25zdCB2YWx1ZXMgPSBkYXRhQ29udGFpbmVyLm1hcEluZGV4KHZhbHVlQWNjZXNzb3IpO1xuXG4gIHJldHVybiB1bmlxdWUodmFsdWVzKS5maWx0ZXIobm90TnVsbG9yVW5kZWZpbmVkKS5zb3J0KCk7XG59XG5cbi8qKlxuICogcmV0dXJuIGxpbmVhciBkb21haW4gZm9yIGFuIGFycmF5IG9mIGRhdGFcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldExpbmVhckRvbWFpbihcbiAgZGF0YTogbnVtYmVyW10sXG4gIHZhbHVlQWNjZXNzb3I/OiBkYXRhVmFsdWVBY2Nlc3NvclxuKTogW251bWJlciwgbnVtYmVyXSB7XG4gIGNvbnN0IHJhbmdlID0gdHlwZW9mIHZhbHVlQWNjZXNzb3IgPT09ICdmdW5jdGlvbicgPyBleHRlbnQoZGF0YSwgdmFsdWVBY2Nlc3NvcikgOiBleHRlbnQoZGF0YSk7XG4gIHJldHVybiByYW5nZS5tYXAoKGQ6IHVuZGVmaW5lZCB8IG51bWJlciwgaTogbnVtYmVyKSA9PiAoZCA9PT0gdW5kZWZpbmVkID8gaSA6IGQpKSBhcyBbXG4gICAgbnVtYmVyLFxuICAgIG51bWJlclxuICBdO1xufVxuXG4vKipcbiAqIHJldHVybiBsaW5lYXIgZG9tYWluIGZvciBhbiBhcnJheSBvZiBkYXRhLiBBIGxvZyBzY2FsZSBkb21haW4gY2Fubm90IGNvbnRhaW4gMFxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TG9nRG9tYWluKGRhdGE6IGFueVtdLCB2YWx1ZUFjY2Vzc29yOiBkYXRhVmFsdWVBY2Nlc3Nvcik6IFtudW1iZXIsIG51bWJlcl0ge1xuICBjb25zdCBbZDAsIGQxXSA9IGdldExpbmVhckRvbWFpbihkYXRhLCB2YWx1ZUFjY2Vzc29yKTtcbiAgcmV0dXJuIFtkMCA9PT0gMCA/IDFlLTUgOiBkMCwgZDFdO1xufVxuXG5leHBvcnQgdHlwZSBEb21haW5TdG9wcyA9IHtcbiAgc3RvcHM6IG51bWJlcltdO1xuICB6OiBudW1iZXJbXTtcbn07XG5cbi8qKlxuICogd2hldGhlciBmaWVsZCBkb21haW4gaXMgc3RvcHNcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzRG9tYWluU3RvcHMoZG9tYWluOiB1bmtub3duKTogZG9tYWluIGlzIERvbWFpblN0b3BzIHtcbiAgcmV0dXJuIGlzUGxhaW5PYmplY3QoZG9tYWluKSAmJiBBcnJheS5pc0FycmF5KGRvbWFpbi5zdG9wcykgJiYgQXJyYXkuaXNBcnJheShkb21haW4ueik7XG59XG5cbmV4cG9ydCB0eXBlIERvbWFpblF1YW50aWxlcyA9IHtcbiAgcXVhbnRpbGVzOiBudW1iZXJbXTtcbiAgejogbnVtYmVyW107XG59O1xuXG4vKipcbiAqIHdoZXRoZXIgZmllbGQgZG9tYWluIGlzIHF1YW50aWxlc1xuICovXG5leHBvcnQgZnVuY3Rpb24gaXNEb21haW5RdWFudGlsZShkb21haW46IGFueSk6IGRvbWFpbiBpcyBEb21haW5RdWFudGlsZXMge1xuICByZXR1cm4gaXNQbGFpbk9iamVjdChkb21haW4pICYmIEFycmF5LmlzQXJyYXkoZG9tYWluLnF1YW50aWxlcykgJiYgQXJyYXkuaXNBcnJheShkb21haW4ueik7XG59XG5cbi8qKlxuICogZ2V0IHRoZSBkb21haW4gYXQgem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VGhyZXNob2xkc0Zyb21RdWFudGlsZXMoXG4gIHF1YW50aWxlczogbnVtYmVyW10sXG4gIGJ1Y2tldHM6IG51bWJlclxuKTogKG51bWJlciB8IHVuZGVmaW5lZClbXSB7XG4gIGNvbnN0IHRocmVzaG9sZHMgPSBbXTtcbiAgaWYgKCFOdW1iZXIuaXNGaW5pdGUoYnVja2V0cykgfHwgYnVja2V0cyA8IDEpIHtcbiAgICByZXR1cm4gW3F1YW50aWxlc1swXSwgcXVhbnRpbGVzW3F1YW50aWxlcy5sZW5ndGggLSAxXV07XG4gIH1cbiAgZm9yIChsZXQgaSA9IDE7IGkgPCBidWNrZXRzOyBpKyspIHtcbiAgICAvLyBwb3NpdGlvbiBpbiBzb3J0ZWQgYXJyYXlcbiAgICBjb25zdCBwb3NpdGlvbiA9IGkgLyBidWNrZXRzO1xuICAgIC8vIEB0cy1pZ25vcmVcbiAgICB0aHJlc2hvbGRzLnB1c2goZDNRdWFudGlsZShxdWFudGlsZXMsIHBvc2l0aW9uKSk7XG4gIH1cblxuICByZXR1cm4gdGhyZXNob2xkcztcbn1cblxuLyoqXG4gKiBnZXQgdGhlIGRvbWFpbiBhdCB6b29tXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXREb21haW5TdGVwc2J5Wm9vbShkb21haW46IGFueVtdLCBzdGVwczogbnVtYmVyW10sIHo6IG51bWJlcik6IGFueSB7XG4gIGNvbnN0IGkgPSBiaXNlY3RMZWZ0KHN0ZXBzLCB6KTtcblxuICBpZiAoc3RlcHNbaV0gPT09IHopIHtcbiAgICAvLyBJZiB6IGlzIGFuIGludGVnZXIgdmFsdWUgZXhhY3RseSBtYXRjaGluZyBhIHN0ZXAsIHJldHVybiB0aGUgY29ycmVzcG9uZGluZyBkb21haW5cbiAgICByZXR1cm4gZG9tYWluW2ldO1xuICB9XG4gIC8vIE90aGVyd2lzZSwgcmV0dXJuIHRoZSBuZXh0IGNvYXJzZXN0IGRvbWFpblxuICByZXR1cm4gZG9tYWluW01hdGgubWF4KGkgLSAxLCAwKV07XG59XG5cbi8qKlxuICogR2V0IGQzIHNjYWxlIGZ1bmN0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRTY2FsZUZ1bmN0aW9uKFxuICBzY2FsZTogc3RyaW5nLFxuICByYW5nZTogYW55W10gfCBJdGVyYWJsZUl0ZXJhdG9yPGFueT4sXG4gIGRvbWFpbjogKG51bWJlciB8IHVuZGVmaW5lZClbXSB8IHN0cmluZ1tdIHwgSXRlcmFibGVJdGVyYXRvcjxhbnk+LFxuICBmaXhlZD86IGJvb2xlYW5cbik6IEQzU2NhbGVGdW5jdGlvbiB7XG4gIGNvbnN0IHNjYWxlRnVuY3Rpb24gPSBTQ0FMRV9GVU5DW2ZpeGVkID8gJ2xpbmVhcicgOiBzY2FsZV0oKVxuICAgIC5kb21haW4oZG9tYWluKVxuICAgIC5yYW5nZShmaXhlZCA/IGRvbWFpbiA6IHJhbmdlKTtcbiAgc2NhbGVGdW5jdGlvbi5zY2FsZVR5cGUgPSBmaXhlZCA/ICdsaW5lYXInIDogc2NhbGU7XG4gIHJldHVybiBzY2FsZUZ1bmN0aW9uO1xufVxuXG4vKipcbiAqIEdldCB0aHJlc2hvbGQgc2NhbGUgY29sb3IgbGFiZWxzXG4gKi9cbmZ1bmN0aW9uIGdldFRocmVzaG9sZExhYmVscyhcbiAgc2NhbGU6IEQzU2NhbGVGdW5jdGlvbixcbiAgbGFiZWxGb3JtYXQ6IExhYmVsRm9ybWF0XG4pOiBPbWl0PENvbG9yQnJlYWssICdkYXRhJz5bXSB7XG4gIGNvbnN0IGdlbkxlbmd0aCA9IHNjYWxlLnJhbmdlKCkubGVuZ3RoO1xuICByZXR1cm4gc2NhbGUucmFuZ2UoKS5tYXAoKGQsIGkpID0+IHtcbiAgICBjb25zdCBpbnZlcnQgPSBzY2FsZS5pbnZlcnRFeHRlbnQoZCk7XG4gICAgY29uc3QgaW5wdXRzID0gW1xuICAgICAgaSA9PT0gMCA/IG51bGwgOiByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFswXSkpLFxuICAgICAgaSA9PT0gZ2VuTGVuZ3RoIC0gMSA/IG51bGwgOiByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFsxXSkpXG4gICAgXTtcbiAgICByZXR1cm4ge1xuICAgICAgLy8gcmF3IHZhbHVlXG4gICAgICByYW5nZTogaW52ZXJ0LFxuICAgICAgLy8gZm9ybWF0dGVkIHZhbHVlXG4gICAgICBpbnB1dHMsXG4gICAgICBsYWJlbDpcbiAgICAgICAgaSA9PT0gMFxuICAgICAgICAgID8gYExlc3MgdGhhbiAke2xhYmVsRm9ybWF0KGludmVydFsxXSl9YFxuICAgICAgICAgIDogaSA9PT0gZ2VuTGVuZ3RoIC0gMVxuICAgICAgICAgID8gYCR7bGFiZWxGb3JtYXQoaW52ZXJ0WzBdKX0gb3IgbW9yZWBcbiAgICAgICAgICA6IGAke2xhYmVsRm9ybWF0KGludmVydFswXSl9IHRvICR7bGFiZWxGb3JtYXQoaW52ZXJ0WzFdKX1gXG4gICAgfTtcbiAgfSk7XG59XG5cbi8qKlxuICogR2V0IGxpbmVhciAvIHF1YW50IHNjYWxlIGNvbG9yIGxhYmVsc1xuICovXG5mdW5jdGlvbiBnZXRTY2FsZUxhYmVscyhcbiAgc2NhbGU6IEQzU2NhbGVGdW5jdGlvbixcbiAgbGFiZWxGb3JtYXQ6IExhYmVsRm9ybWF0XG4pOiBPbWl0PENvbG9yQnJlYWssICdkYXRhJz5bXSB7XG4gIHJldHVybiBzY2FsZS5yYW5nZSgpLm1hcChkID0+IHtcbiAgICAvLyBAdHMtaWdub3JlXG4gICAgY29uc3QgaW52ZXJ0ID0gc2NhbGUuaW52ZXJ0RXh0ZW50KGQpO1xuICAgIGNvbnN0IGlucHV0cyA9IFtcbiAgICAgIHJldmVyc2VGb3JtYXROdW1iZXIobGFiZWxGb3JtYXQoaW52ZXJ0WzBdKSksXG4gICAgICByZXZlcnNlRm9ybWF0TnVtYmVyKGxhYmVsRm9ybWF0KGludmVydFsxXSkpXG4gICAgXTtcblxuICAgIHJldHVybiB7XG4gICAgICBsYWJlbDogYCR7bGFiZWxGb3JtYXQoaW52ZXJ0WzBdKX0gdG8gJHtsYWJlbEZvcm1hdChpbnZlcnRbMV0pfWAsXG4gICAgICAvLyByYXcgdmFsdWVcbiAgICAgIHJhbmdlOiBpbnZlcnQsXG4gICAgICAvLyBmb3JtYXR0ZWQgdmFsdWVcbiAgICAgIGlucHV0c1xuICAgIH07XG4gIH0pO1xufVxuXG5jb25zdCBjdXN0b21TY2FsZUxhYmVsRm9ybWF0ID0gbiA9PiAobiA/IGZvcm1hdE51bWJlcihuLCAncmVhbCcpIDogJ25vIHZhbHVlJyk7XG4vKipcbiAqIEdldCBsaW5lYXIgLyBxdWFudCBzY2FsZSBjb2xvciBicmVha3NcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFF1YW50TGVnZW5kcyhzY2FsZTogRDNTY2FsZUZ1bmN0aW9uLCBsYWJlbEZvcm1hdDogTGFiZWxGb3JtYXQpOiBDb2xvckJyZWFrW10ge1xuICBpZiAodHlwZW9mIHNjYWxlLmludmVydEV4dGVudCAhPT0gJ2Z1bmN0aW9uJykge1xuICAgIHJldHVybiBbXTtcbiAgfVxuICBjb25zdCB0aHJlc2hvbGRMYWJlbEZvcm1hdCA9IChuLCB0eXBlKSA9PlxuICAgIG4gJiYgbGFiZWxGb3JtYXQgPyBsYWJlbEZvcm1hdChuKSA6IG4gPyBmb3JtYXROdW1iZXIobiwgdHlwZSkgOiAnbm8gdmFsdWUnO1xuICBjb25zdCBsYWJlbHMgPVxuICAgIHNjYWxlLnNjYWxlVHlwZSA9PT0gJ3RocmVzaG9sZCdcbiAgICAgID8gZ2V0VGhyZXNob2xkTGFiZWxzKHNjYWxlLCB0aHJlc2hvbGRMYWJlbEZvcm1hdClcbiAgICAgIDogc2NhbGUuc2NhbGVUeXBlID09PSAnY3VzdG9tJ1xuICAgICAgPyBnZXRUaHJlc2hvbGRMYWJlbHMoc2NhbGUsIGN1c3RvbVNjYWxlTGFiZWxGb3JtYXQpXG4gICAgICA6IGdldFNjYWxlTGFiZWxzKHNjYWxlLCBsYWJlbEZvcm1hdCk7XG5cbiAgY29uc3QgZGF0YSA9IHNjYWxlLnJhbmdlKCk7XG5cbiAgcmV0dXJuIGxhYmVscy5tYXAoKGxhYmVsLCBpbmRleCkgPT4gKHtcbiAgICBkYXRhOiBBcnJheS5pc0FycmF5KGRhdGFbaW5kZXhdKSA/IHJnYlRvSGV4KGRhdGFbaW5kZXhdKSA6IGRhdGFbaW5kZXhdLFxuICAgIC4uLmxhYmVsXG4gIH0pKTtcbn1cblxuLyoqXG4gKiBHZXQgb3JkaW5hbCBjb2xvciBzY2FsZSBsZWdlbmRzXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRPcmRpbmFsTGVnZW5kcyhzY2FsZTogRDNTY2FsZUZ1bmN0aW9uKTogQ29sb3JCcmVha09yZGluYWxbXSB7XG4gIGNvbnN0IGRvbWFpbiA9IHNjYWxlLmRvbWFpbigpO1xuICBjb25zdCBsYWJlbHMgPSBzY2FsZS5kb21haW4oKTtcbiAgY29uc3QgZGF0YSA9IGRvbWFpbi5tYXAoc2NhbGUpO1xuXG4gIHJldHVybiBkYXRhLm1hcCgoZGF0dW0sIGluZGV4KSA9PiAoe1xuICAgIGRhdGE6IGlzUmdiQ29sb3IoZGF0dW0pID8gcmdiVG9IZXgoZGF0dW0pIDogZGF0dW0sXG4gICAgbGFiZWw6IGxhYmVsc1tpbmRleF1cbiAgfSkpO1xufVxuXG5jb25zdCBkZWZhdWx0Rm9ybWF0ID0gZCA9PiBkO1xuXG5jb25zdCBnZXRUaW1lTGFiZWxGb3JtYXQgPSBkb21haW4gPT4ge1xuICBjb25zdCBmb3JtYXR0ZXIgPSBnZXRUaW1lV2lkZ2V0SGludEZvcm1hdHRlcihkb21haW4pO1xuICByZXR1cm4gdmFsID0+IG1vbWVudC51dGModmFsKS5mb3JtYXQoZm9ybWF0dGVyKTtcbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRRdWFudExhYmVsRm9ybWF0KGRvbWFpbiwgZmllbGRUeXBlKSB7XG4gIC8vIHF1YW50IHNjYWxlIGNhbiBvbmx5IGJlIGFzc2lnbmVkIHRvIGxpbmVhciBGaWVsZHM6IHJlYWwsIHRpbWVzdGFtcCwgaW50ZWdlclxuICByZXR1cm4gZmllbGRUeXBlID09PSBBTExfRklFTERfVFlQRVMudGltZXN0YW1wXG4gICAgPyBnZXRUaW1lTGFiZWxGb3JtYXQoZG9tYWluKVxuICAgIDogIWZpZWxkVHlwZVxuICAgID8gZGVmYXVsdEZvcm1hdFxuICAgIDogbiA9PiAoaXNOdW1iZXIobikgPyBmb3JtYXROdW1iZXIobiwgZmllbGRUeXBlKSA6ICdubyB2YWx1ZScpO1xufVxuXG4vKipcbiAqIEdldCBsZWdlbmRzIGZvciBzY2FsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGVnZW5kT2ZTY2FsZSh7XG4gIHNjYWxlLFxuICBzY2FsZVR5cGUsXG4gIGxhYmVsRm9ybWF0LFxuICBmaWVsZFR5cGVcbn06IHtcbiAgc2NhbGU/OiBEM1NjYWxlRnVuY3Rpb24gfCBudWxsO1xuICBzY2FsZVR5cGU6IHN0cmluZztcbiAgbGFiZWxGb3JtYXQ/OiBMYWJlbEZvcm1hdDtcbiAgZmllbGRUeXBlOiBzdHJpbmcgfCBudWxsIHwgdW5kZWZpbmVkO1xufSk6IENvbG9yQnJlYWtbXSB8IENvbG9yQnJlYWtPcmRpbmFsW10ge1xuICBpZiAoIXNjYWxlIHx8IHNjYWxlLmJ5Wm9vbSkge1xuICAgIHJldHVybiBbXTtcbiAgfVxuICBpZiAoXG4gICAgc2NhbGVUeXBlID09PSBTQ0FMRV9UWVBFUy5vcmRpbmFsIHx8XG4gICAgc2NhbGVUeXBlID09PSBTQ0FMRV9UWVBFUy5jdXN0b21PcmRpbmFsIHx8XG4gICAgZmllbGRUeXBlID09PSBBTExfRklFTERfVFlQRVMuc3RyaW5nXG4gICkge1xuICAgIHJldHVybiBnZXRPcmRpbmFsTGVnZW5kcyhzY2FsZSk7XG4gIH1cblxuICBjb25zdCBmb3JtYXRMYWJlbCA9IGxhYmVsRm9ybWF0IHx8IGdldFF1YW50TGFiZWxGb3JtYXQoc2NhbGUuZG9tYWluKCksIGZpZWxkVHlwZSk7XG5cbiAgcmV0dXJuIGdldFF1YW50TGVnZW5kcyhzY2FsZSwgZm9ybWF0TGFiZWwpO1xufVxuXG4vKipcbiAqIEdldCBjb2xvciBzY2FsZSBmdW5jdGlvblxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0TGF5ZXJDb2xvclNjYWxlKHtcbiAgcmFuZ2UsXG4gIGRvbWFpbixcbiAgc2NhbGVUeXBlLFxuICBsYXllclxufToge1xuICByYW5nZTogQ29sb3JSYW5nZSB8IG51bGwgfCB1bmRlZmluZWQ7XG4gIGRvbWFpbjogVmlzdWFsQ2hhbm5lbERvbWFpbjtcbiAgc2NhbGVUeXBlOiBzdHJpbmc7XG4gIGxheWVyOiBMYXllcjtcbiAgaXNGaXhlZD86IGJvb2xlYW47XG59KTogRDNTY2FsZUZ1bmN0aW9uIHwgbnVsbCB7XG4gIGlmIChyYW5nZSAmJiBkb21haW4gJiYgc2NhbGVUeXBlKSB7XG4gICAgcmV0dXJuIGxheWVyLmdldENvbG9yU2NhbGUoc2NhbGVUeXBlLCBkb21haW4sIHJhbmdlKTtcbiAgfVxuICByZXR1cm4gbnVsbDtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGNvbG9yUmFuZ2UuY29sb3JNYXAgaW50byBjb2xvciBicmVha3MgVUkgaW5wdXRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGluaXRpYWxpemVMYXllckNvbG9yTWFwKGxheWVyOiBMYXllciwgdmlzdWFsQ2hhbm5lbDogVmlzdWFsQ2hhbm5lbCk6IENvbG9yTWFwIHtcbiAgY29uc3QgZG9tYWluID0gbGF5ZXIuY29uZmlnW3Zpc3VhbENoYW5uZWwuZG9tYWluXTtcbiAgY29uc3QgcmFuZ2UgPSBsYXllci5jb25maWcudmlzQ29uZmlnW3Zpc3VhbENoYW5uZWwucmFuZ2VdO1xuICBjb25zdCBzY2FsZVR5cGUgPSBsYXllci5jb25maWdbdmlzdWFsQ2hhbm5lbC5zY2FsZV07XG4gIGNvbnN0IGZpZWxkID0gbGF5ZXIuY29uZmlnW3Zpc3VhbENoYW5uZWwuZmllbGRdO1xuXG4gIGNvbnN0IHNjYWxlID0gZ2V0TGF5ZXJDb2xvclNjYWxlKHtcbiAgICByYW5nZSxcbiAgICBkb21haW4sXG4gICAgc2NhbGVUeXBlLFxuICAgIGxheWVyXG4gIH0pO1xuXG4gIGNvbnN0IGNvbG9yQnJlYWtzID0gZ2V0TGVnZW5kT2ZTY2FsZSh7XG4gICAgc2NhbGU6IHNjYWxlPy5ieVpvb20gPyBzY2FsZSgwKSA6IHNjYWxlLFxuICAgIHNjYWxlVHlwZSxcbiAgICBmaWVsZFR5cGU6IGZpZWxkLnR5cGVcbiAgfSk7XG4gIHJldHVybiBjb2xvckJyZWFrc1RvQ29sb3JNYXAoY29sb3JCcmVha3MpO1xufVxuXG4vKipcbiAqIEdldCB2aXN1YWwgY2hhbmVsIHNjYWxlIGZ1bmN0aW9uIGlmIGl0J3MgYmFzZWQgb24gem9vbVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0VmlzdWFsQ2hhbm5lbFNjYWxlQnlab29tKHtcbiAgc2NhbGUsXG4gIGxheWVyLFxuICBtYXBTdGF0ZVxufToge1xuICBzY2FsZTogRDNTY2FsZUZ1bmN0aW9uIHwgbnVsbDtcbiAgbGF5ZXI6IExheWVyO1xuICBtYXBTdGF0ZT86IE1hcFN0YXRlO1xufSk6IEQzU2NhbGVGdW5jdGlvbiB8IG51bGwge1xuICBpZiAoc2NhbGU/LmJ5Wm9vbSkge1xuICAgIGNvbnN0IHogPSBsYXllci5tZXRhPy5nZXRab29tID8gbGF5ZXIubWV0YS5nZXRab29tKG1hcFN0YXRlKSA6IG1hcFN0YXRlPy56b29tO1xuICAgIHNjYWxlID0gTnVtYmVyLmlzRmluaXRlKHopID8gc2NhbGUoeikgOiBudWxsO1xuICB9XG4gIHJldHVybiBzY2FsZTtcbn1cblxuLyoqXG4gKiBHZXQgY2F0ZWdvcmljYWwgY29sb3JNYXAgZnJvbSBjb2xvcnMgYW5kIGRvbWFpbiAodW5pcXVlIHZhbHVlcylcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldENhdGVnb3JpY2FsQ29sb3JNYXAoXG4gIGNvbG9yczogc3RyaW5nW10sXG4gIGRvbWFpbjogKHN0cmluZyB8IG51bWJlciB8IHN0cmluZ1tdIHwgbnVtYmVyW10gfCBudWxsKVtdXG4pOiBhbnkge1xuICAvLyBjb2xvck1hcDogW3N0cmluZyB8IHN0cmluZ1tdLCBoZXhzdHJpbmddXG4gIGNvbnN0IGNvbG9yVG9VbmlxdWVWYWx1ZXMgPSB7fTtcbiAgY29uc3QgdW5pcXVlVmFsdWVzID0gdW5pcXVlKGRvbWFpbikuZmlsdGVyKG5vdE51bGxvclVuZGVmaW5lZCkuc29ydCgpO1xuICAvLyBlYWNoIHVuaXF1ZSB2YWx1ZSBhc3NpZ24gdG8gYSBjb2xvciwgdGhlIHJlc3QgdW5pcXVlIHZhbHVlcyBhc3NpZ24gdG8gbGFzdCBjb2xvclxuICBjb25zdCBsYXN0Q29sb3IgPSBjb2xvcnNbY29sb3JzLmxlbmd0aCAtIDFdO1xuICBmb3IgKGxldCBpID0gMDsgaSA8IHVuaXF1ZVZhbHVlcy5sZW5ndGg7ICsraSkge1xuICAgIGlmIChpIDwgY29sb3JzLmxlbmd0aCkge1xuICAgICAgY29sb3JUb1VuaXF1ZVZhbHVlc1tjb2xvcnNbaV1dID0gdW5pcXVlVmFsdWVzW2ldO1xuICAgIH0gZWxzZSB7XG4gICAgICBjb2xvclRvVW5pcXVlVmFsdWVzW2xhc3RDb2xvcl0gPSBbXG4gICAgICAgIC4uLihBcnJheS5pc0FycmF5KGNvbG9yVG9VbmlxdWVWYWx1ZXNbbGFzdENvbG9yXSlcbiAgICAgICAgICA/IGNvbG9yVG9VbmlxdWVWYWx1ZXNbbGFzdENvbG9yXVxuICAgICAgICAgIDogW2NvbG9yVG9VbmlxdWVWYWx1ZXNbbGFzdENvbG9yXV0pLFxuICAgICAgICB1bmlxdWVWYWx1ZXNbaV1cbiAgICAgIF07XG4gICAgfVxuICB9XG5cbiAgY29uc3QgY29sb3JNYXAgPSBjb2xvcnMubWFwKGNvbG9yID0+IHtcbiAgICBpZiAoY29sb3IgaW4gY29sb3JUb1VuaXF1ZVZhbHVlcykge1xuICAgICAgcmV0dXJuIFtjb2xvclRvVW5pcXVlVmFsdWVzW2NvbG9yXSwgY29sb3JdO1xuICAgIH1cbiAgICByZXR1cm4gW251bGwsIGNvbG9yXTtcbiAgfSk7XG5cbiAgcmV0dXJuIGNvbG9yTWFwO1xufVxuXG4vKipcbiAqIEdldCBjYXRlZ29yaWNhbCBjb2xvckJyZWFrcyBmcm9tIGNvbG9yTWFwXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBjb2xvck1hcFRvQ2F0ZWdvcmljYWxDb2xvckJyZWFrcyhcbiAgY29sb3JNYXA/OiBDb2xvck1hcCB8IG51bGxcbik6IENvbG9yQnJlYWtPcmRpbmFsW10gfCBudWxsIHtcbiAgaWYgKCFjb2xvck1hcCkge1xuICAgIHJldHVybiBudWxsO1xuICB9XG4gIGNvbnN0IGNvbG9yQnJlYWtzID0gY29sb3JNYXAubWFwKChbdmFsdWUsIGNvbG9yXSkgPT4ge1xuICAgIHJldHVybiB7XG4gICAgICBkYXRhOiBjb2xvcixcbiAgICAgIGxhYmVsOiB2YWx1ZVxuICAgIH07XG4gIH0pO1xuXG4gIHJldHVybiBjb2xvckJyZWFrcztcbn1cblxuLyoqXG4gKiBjcmVhdGUgY2F0ZWdvcmljYWwgY29sb3JNYXAgZnJvbSBjb2xvckJyZWFrc1xuICovXG5leHBvcnQgZnVuY3Rpb24gY29sb3JCcmVha3NUb0NhdGVnb3JpY2FsQ29sb3JNYXAoY29sb3JCcmVha3M6IENvbG9yQnJlYWtPcmRpbmFsW10pOiBDb2xvck1hcCB7XG4gIC8vIGNvbG9yTWFwOiBbc3RyaW5nIHwgc3RyaW5nW10sIGhleHN0cmluZ11cbiAgY29uc3QgY29sb3JzID0gdW5pcShjb2xvckJyZWFrcy5tYXAoY2IgPT4gY2IuZGF0YSkpO1xuICBjb25zdCB2YWx1ZXMgPSB1bmlxKGNvbG9yQnJlYWtzLm1hcChjYiA9PiBjYi5sYWJlbCkpO1xuXG4gIHJldHVybiBnZXRDYXRlZ29yaWNhbENvbG9yTWFwKGNvbG9ycywgdmFsdWVzKTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IGNvbG9yIGJyZWFrcyBVSSBpbnB1dCBpbnRvIGNvbG9yUmFuZ2UuY29sb3JNYXBcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbG9yQnJlYWtzVG9Db2xvck1hcChjb2xvckJyZWFrczogQ29sb3JCcmVha1tdIHwgQ29sb3JCcmVha09yZGluYWxbXSk6IENvbG9yTWFwIHtcbiAgY29uc3QgY29sb3JNYXAgPSBjb2xvckJyZWFrcy5tYXAoKGNvbG9yQnJlYWssIGkpID0+IHtcbiAgICAvLyBbdmFsdWUsIGhleF1cbiAgICByZXR1cm4gW1xuICAgICAgY29sb3JCcmVhay5pbnB1dHNcbiAgICAgICAgPyBpID09PSBjb2xvckJyZWFrcy5sZW5ndGggLSAxXG4gICAgICAgICAgPyBudWxsIC8vIGxhc3RcbiAgICAgICAgICA6IGNvbG9yQnJlYWsuaW5wdXRzWzFdXG4gICAgICAgIDogY29sb3JCcmVhay5sYWJlbCxcbiAgICAgIGNvbG9yQnJlYWsuZGF0YVxuICAgIF07XG4gIH0pO1xuXG4gIC8vIEB0cy1pZ25vcmUgdHVwbGVcbiAgcmV0dXJuIGNvbG9yTWFwO1xufVxuXG4vKipcbiAqIENvbnZlcnQgY29sb3JSYW5nZS5jb2xvck1hcCBpbnRvIGNvbG9yIGJyZWFrcyBVSSBpbnB1dFxuICovXG5leHBvcnQgZnVuY3Rpb24gY29sb3JNYXBUb0NvbG9yQnJlYWtzKGNvbG9yTWFwPzogQ29sb3JNYXAgfCBudWxsKTogQ29sb3JCcmVha1tdIHwgbnVsbCB7XG4gIGlmICghY29sb3JNYXApIHtcbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuICBjb25zdCBjb2xvckJyZWFrcyA9IGNvbG9yTWFwLm1hcCgoW3ZhbHVlLCBjb2xvcl0sIGkpID0+IHtcbiAgICBjb25zdCByYW5nZSA9XG4gICAgICBpID09PSAwXG4gICAgICAgID8gLy8gZmlyc3RcbiAgICAgICAgICBbLUluZmluaXR5LCB2YWx1ZV1cbiAgICAgICAgOiAvLyBsYXN0XG4gICAgICAgIGkgPT09IGNvbG9yTWFwLmxlbmd0aCAtIDFcbiAgICAgICAgPyBbY29sb3JNYXBbaSAtIDFdWzBdLCBJbmZpbml0eV1cbiAgICAgICAgOiAvLyBlbHNlXG4gICAgICAgICAgW2NvbG9yTWFwW2kgLSAxXVswXSwgdmFsdWVdO1xuICAgIHJldHVybiB7XG4gICAgICBkYXRhOiBjb2xvcixcbiAgICAgIHJhbmdlLFxuICAgICAgaW5wdXRzOiByYW5nZSxcbiAgICAgIGxhYmVsOlxuICAgICAgICAvLyBmaXJzdFxuICAgICAgICBpID09PSAwXG4gICAgICAgICAgPyBgTGVzcyB0aGFuICR7dmFsdWV9YFxuICAgICAgICAgIDogLy8gbGFzdFxuICAgICAgICAgIGkgPT09IGNvbG9yTWFwLmxlbmd0aCAtIDFcbiAgICAgICAgICA/IGAke2NvbG9yTWFwW2kgLSAxXVswXX0gb3IgbW9yZWBcbiAgICAgICAgICA6IGAke2NvbG9yTWFwW2kgLSAxXVswXX0gdG8gJHt2YWx1ZX1gXG4gICAgfTtcbiAgfSk7XG5cbiAgLy8gQHRzLWlnbm9yZSBpbXBsZW1lbnQgY29udmVyc2lvbiBmb3Igb3JkaW5hbFxuICByZXR1cm4gY29sb3JCcmVha3M7XG59XG5cbi8qKlxuICogV2hldGhlciBjb2xvciBicmVha3MgaXMgZm9yIG51bWVyaWMgZmllbGRcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGlzTnVtZXJpY0NvbG9yQnJlYWtzKGNvbG9yQnJlYWtzOiB1bmtub3duKTogY29sb3JCcmVha3MgaXMgQ29sb3JCcmVha1tdIHtcbiAgcmV0dXJuIEJvb2xlYW4oQXJyYXkuaXNBcnJheShjb2xvckJyZWFrcykgJiYgY29sb3JCcmVha3MubGVuZ3RoICYmIGNvbG9yQnJlYWtzWzBdLmlucHV0cyk7XG59XG5cbi8vIHJldHVybiBkb21haW5NaW4sIGRvbWFpbk1heCwgaGlzdG9ncmFtTWVhblxuZXhwb3J0IGZ1bmN0aW9uIGdldEhpc3RvZ3JhbURvbWFpbih7XG4gIGFnZ3JlZ2F0ZWRCaW5zLFxuICBjb2x1bW5TdGF0cyxcbiAgZGF0YXNldCxcbiAgZmllbGRWYWx1ZUFjY2Vzc29yXG59OiB7XG4gIGFnZ3JlZ2F0ZWRCaW5zPzogQWdncmVnYXRlZEJpbltdO1xuICBjb2x1bW5TdGF0cz86IEZpbHRlclByb3BzWydjb2x1bW5TdGF0cyddO1xuICBkYXRhc2V0PzogS2VwbGVyVGFibGU7XG4gIGZpZWxkVmFsdWVBY2Nlc3NvcjogKGlkeDogdW5rbm93bikgPT4gbnVtYmVyO1xufSkge1xuICBsZXQgZG9tYWluTWluID0gTnVtYmVyLlBPU0lUSVZFX0lORklOSVRZO1xuICBsZXQgZG9tYWluTWF4ID0gTnVtYmVyLk5FR0FUSVZFX0lORklOSVRZO1xuICBsZXQgblZhbGlkID0gMDtcbiAgbGV0IGRvbWFpblN1bSA9IDA7XG5cbiAgaWYgKGFnZ3JlZ2F0ZWRCaW5zKSB7XG4gICAgT2JqZWN0LnZhbHVlcyhhZ2dyZWdhdGVkQmlucykuZm9yRWFjaChiaW4gPT4ge1xuICAgICAgY29uc3QgdmFsID0gYmluLnZhbHVlO1xuICAgICAgaWYgKGlzTnVtYmVyKHZhbCkpIHtcbiAgICAgICAgaWYgKHZhbCA8IGRvbWFpbk1pbikgZG9tYWluTWluID0gdmFsO1xuICAgICAgICBpZiAodmFsID4gZG9tYWluTWF4KSBkb21haW5NYXggPSB2YWw7XG4gICAgICAgIGRvbWFpblN1bSArPSB2YWw7XG4gICAgICAgIG5WYWxpZCArPSAxO1xuICAgICAgfVxuICAgIH0pO1xuICB9IGVsc2Uge1xuICAgIGlmIChjb2x1bW5TdGF0cyAmJiBjb2x1bW5TdGF0cy5xdWFudGlsZXMgJiYgY29sdW1uU3RhdHMubWVhbikge1xuICAgICAgLy8gbm8gbmVlZCB0byByZWNhbGN1YXRlIG1pbi9tYXgvbWVhbiBpZiBpdHMgYWxyZWFkeSBpbiBjb2x1bW5TdGF0c1xuICAgICAgcmV0dXJuIFtcbiAgICAgICAgY29sdW1uU3RhdHMucXVhbnRpbGVzWzBdLnZhbHVlLFxuICAgICAgICBjb2x1bW5TdGF0cy5xdWFudGlsZXNbY29sdW1uU3RhdHMucXVhbnRpbGVzLmxlbmd0aCAtIDFdLnZhbHVlLFxuICAgICAgICBjb2x1bW5TdGF0cy5tZWFuXG4gICAgICBdO1xuICAgIH1cbiAgICBpZiAoZGF0YXNldCAmJiBmaWVsZFZhbHVlQWNjZXNzb3IpIHtcbiAgICAgIGRhdGFzZXQuYWxsSW5kZXhlcy5mb3JFYWNoKHggPT4ge1xuICAgICAgICBjb25zdCB2YWwgPSBmaWVsZFZhbHVlQWNjZXNzb3IoeCk7XG4gICAgICAgIGlmIChpc051bWJlcih2YWwpKSB7XG4gICAgICAgICAgaWYgKHZhbCA8IGRvbWFpbk1pbikgZG9tYWluTWluID0gdmFsO1xuICAgICAgICAgIGlmICh2YWwgPiBkb21haW5NYXgpIGRvbWFpbk1heCA9IHZhbDtcbiAgICAgICAgICBkb21haW5TdW0gKz0gdmFsO1xuICAgICAgICAgIG5WYWxpZCArPSAxO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG4gIH1cbiAgY29uc3QgaGlzdG9ncmFtTWVhbiA9IG5WYWxpZCA+IDAgPyBkb21haW5TdW0gLyBuVmFsaWQgOiAwO1xuICByZXR1cm4gW25WYWxpZCA+IDAgPyBkb21haW5NaW4gOiAwLCBuVmFsaWQgPiAwID8gZG9tYWluTWF4IDogMCwgaGlzdG9ncmFtTWVhbl07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiByZXNldENhdGVnb3JpY2FsQ29sb3JNYXBCeUluZGV4KGNvbG9yTWFwOiBDb2xvck1hcCwgaW5kZXg6IG51bWJlcik6IGFueSB7XG4gIGlmICghY29sb3JNYXApIHtcbiAgICByZXR1cm4gY29sb3JNYXA7XG4gIH1cbiAgY29uc3QgbmV3Q29sb3JNYXAgPSBjb2xvck1hcC5tYXAoKGNtLCBpKSA9PiB7XG4gICAgaWYgKGkgPT09IGluZGV4KSB7XG4gICAgICByZXR1cm4gW251bGwsIGNtWzFdXTtcbiAgICB9XG4gICAgcmV0dXJuIGNtO1xuICB9KTtcbiAgcmV0dXJuIG5ld0NvbG9yTWFwO1xufVxuXG4vKipcbiAqIHNlbGVjdCByZXN0IGNhdGVnb3JpY2FsIHZhbHVlcyBmb3IgYSBjb2xvck1hcCBieSBpdHMgaW5kZXhcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHNlbGVjdFJlc3RDYXRlZ29yaWNhbENvbG9yTWFwQnlJbmRleChcbiAgY29sb3JNYXA6IENvbG9yTWFwIHwgbnVsbCxcbiAgaW5kZXg6IG51bWJlcixcbiAgdW5pcXVlVmFsdWVzPzogbnVtYmVyW10gfCBzdHJpbmdbXVxuKTogQ29sb3JNYXAgfCB1bmRlZmluZWQgfCBudWxsIHtcbiAgaWYgKCFjb2xvck1hcCB8fCAhdW5pcXVlVmFsdWVzKSB7XG4gICAgcmV0dXJuIGNvbG9yTWFwO1xuICB9XG5cbiAgLy8gZmluZCB1bmlxdWUgdmFsdWVzIHRoYXQgaGFzIG5vdCBiZWVuIHVzZWQgaW4gY3VycmVudCBjb2xvck1hcFxuICBjb25zdCB1bmlxVmFsdWVEaWN0ID0gT2JqZWN0LmZyb21FbnRyaWVzKHVuaXF1ZVZhbHVlcy5tYXAodmFsID0+IFt2YWwsIGZhbHNlXSkpO1xuICBjb2xvck1hcC5mb3JFYWNoKGNtID0+IHtcbiAgICB0b0FycmF5KGNtWzBdKS5mb3JFYWNoKHYgPT4ge1xuICAgICAgaWYgKHYpIHVuaXFWYWx1ZURpY3Rbdl0gPSB0cnVlO1xuICAgIH0pO1xuICB9KTtcbiAgY29uc3QgcmVzdCA9IE9iamVjdC5rZXlzKHVuaXFWYWx1ZURpY3QpLmZpbHRlcih2ID0+ICF1bmlxVmFsdWVEaWN0W3ZdKTtcblxuICAvLyB1c2UgdGhlIG5vdCB1c2VkIHVuaXF1ZSB2YWx1ZXMgaW4gdGhlIHNlbGVjdGVkIGNvbG9yIG1hcFxuICBjb25zdCBuZXdDb2xvck1hcCA9IGNvbG9yTWFwLm1hcCgoY20sIGkpID0+IHtcbiAgICBpZiAoaSA9PT0gaW5kZXgpIHtcbiAgICAgIHJldHVybiBbWy4uLnJlc3QsIC4uLnRvQXJyYXkoY21bMF0pXSwgY21bMV1dO1xuICAgIH1cbiAgICByZXR1cm4gY207XG4gIH0pIGFzIENvbG9yTWFwO1xuXG4gIHJldHVybiBuZXdDb2xvck1hcDtcbn1cblxuLyoqXG4gKiByZW1vdmUgYSBjYXRlZ29yaWNhbCB2YWx1ZSBmcm9tIGEgY29sb3JNYXAgYnkgaXRzIGluZGV4XG4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW1vdmVDYXRlZ29yaWNhbFZhbHVlRnJvbUNvbG9yTWFwKFxuICBjb2xvck1hcDogQ29sb3JNYXAgfCBudWxsIHwgdW5kZWZpbmVkLFxuICBpdGVtOiBudW1iZXIgfCBzdHJpbmcsXG4gIGluZGV4OiBudW1iZXJcbik6IENvbG9yTWFwIHwgbnVsbCB8IHVuZGVmaW5lZCB7XG4gIGlmICghY29sb3JNYXApIHtcbiAgICByZXR1cm4gY29sb3JNYXA7XG4gIH1cbiAgY29uc3QgbmV3Q29sb3JNYXAgPSBjb2xvck1hcC5tYXAoKGNtLCBpKSA9PiB7XG4gICAgaWYgKGkgPT09IGluZGV4KSB7XG4gICAgICBpZiAoIWNtWzBdKSB7XG4gICAgICAgIHJldHVybiBbbnVsbCwgY21bMV1dO1xuICAgICAgfVxuICAgICAgY29uc3QgY3VycmVudFVuaXF1ZVZhbHVlcyA9IHRvQXJyYXkoY21bMF0pO1xuICAgICAgY29uc3QgdXBkYXRlZFVuaXF1ZVZhbHVlcyA9IGN1cnJlbnRVbmlxdWVWYWx1ZXMuZmlsdGVyKHYgPT4gdiAhPT0gaXRlbSk7XG4gICAgICByZXR1cm4gW3VwZGF0ZWRVbmlxdWVWYWx1ZXMsIGNtWzFdXTtcbiAgICB9XG4gICAgcmV0dXJuIGNtO1xuICB9KSBhcyBDb2xvck1hcDtcblxuICByZXR1cm4gbmV3Q29sb3JNYXA7XG59XG5cbi8qKlxuICogYWRkIGNhdGVnb3JpY2FsIHZhbHVlcyAoZnJvbSBtdWx0aXNlbCBkcm9wZG93bikgdG8gYSBjb2xvck1hcCBieSBpdHMgaW5kZXhcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGFkZENhdGVnb3JpY2FsVmFsdWVzVG9Db2xvck1hcChcbiAgY29sb3JNYXA6IENvbG9yTWFwLFxuICBpdGVtczogKHN0cmluZyB8IG51bWJlcilbXSxcbiAgaW5kZXg6IG51bWJlclxuKTogQ29sb3JNYXAge1xuICBpZiAoIWNvbG9yTWFwKSB7XG4gICAgcmV0dXJuIGNvbG9yTWFwO1xuICB9XG5cbiAgY29uc3QgbmV3Q29sb3JNYXAgPSBjb2xvck1hcC5tYXAoKGNtLCBpKSA9PiB7XG4gICAgaWYgKGkgPT09IGluZGV4KSB7XG4gICAgICBpZiAoIWNtWzBdKSB7XG4gICAgICAgIHJldHVybiBbaXRlbXMsIGNtWzFdXTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGN1cnJlbnRVbmlxdWVWYWx1ZXMgPSB0b0FycmF5KGNtWzBdKTtcbiAgICAgIGNvbnN0IHVwZGF0ZWRVbmlxdWVWYWx1ZXMgPSB1bmlxKGN1cnJlbnRVbmlxdWVWYWx1ZXMuY29uY2F0KGl0ZW1zKSk7XG4gICAgICByZXR1cm4gW3VwZGF0ZWRVbmlxdWVWYWx1ZXMsIGNtWzFdXTtcbiAgICB9XG4gICAgLy8gcmVtb3ZlIHZhbHVlIGZyb20gb3RoZXIgY29sb3JNYXBcbiAgICBjb25zdCBjdXJyZW50VW5pcXVlVmFsdWVzID0gY21bMF07XG4gICAgaWYgKEFycmF5LmlzQXJyYXkoY3VycmVudFVuaXF1ZVZhbHVlcykpIHtcbiAgICAgIGNvbnN0IHVwZGF0ZWRVbmlxdWVWYWx1ZXM6IHN0cmluZ1tdIHwgbnVtYmVyW10gPSAoY3VycmVudFVuaXF1ZVZhbHVlcyBhcyBhbnlbXSkuZmlsdGVyKFxuICAgICAgICB2ID0+ICFpdGVtcy5pbmNsdWRlcyh2KVxuICAgICAgKTtcbiAgICAgIHJldHVybiBbdXBkYXRlZFVuaXF1ZVZhbHVlcywgY21bMV1dO1xuICAgIH0gZWxzZSBpZiAoY3VycmVudFVuaXF1ZVZhbHVlcyAmJiBpdGVtcy5pbmNsdWRlcyhjdXJyZW50VW5pcXVlVmFsdWVzKSkge1xuICAgICAgcmV0dXJuIFtudWxsLCBjbVsxXV07XG4gICAgfVxuXG4gICAgcmV0dXJuIGNtO1xuICB9KSBhcyBDb2xvck1hcDtcblxuICByZXR1cm4gbmV3Q29sb3JNYXA7XG59XG5cbi8qKlxuICogZ2V0IGEgY29sb3Igc2NhbGUgZnVuYyBmb3IgY2F0ZWdvcmljYWwgKGN1c3RvbSBvcmRpbmFsKSBzY2FsZVxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0Q2F0ZWdvcmljYWxDb2xvclNjYWxlKFxuICBjb2xvckRvbWFpbjogbnVtYmVyW10gfCBzdHJpbmdbXSxcbiAgY29sb3JSYW5nZTogQ29sb3JSYW5nZSxcbiAgdXNlUmdiID0gdHJ1ZVxuKTogKGNhdGVnb3J5VmFsdWU6IHN0cmluZyB8IG51bWJlcikgPT4gUkdCQ29sb3IgfCBSR0JBQ29sb3Ige1xuICBjb25zdCBjTWFwID0gY29sb3JSYW5nZS5jb2xvck1hcFxuICAgID8gY29sb3JSYW5nZS5jb2xvck1hcFxuICAgIDogZ2V0Q2F0ZWdvcmljYWxDb2xvck1hcChjb2xvcl