kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
593 lines (586 loc) • 75.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.defaultElevationDimension = exports.defaultDimensions = exports.defaultColorDimension = exports.defaultAggregation = exports["default"] = exports.DECK_AGGREGATION_MAP = void 0;
exports.getAggregatedData = getAggregatedData;
exports.getDimensionScale = getDimensionScale;
exports.getDimensionSortedBins = getDimensionSortedBins;
exports.getDimensionValueDomain = getDimensionValueDomain;
exports.getGetValue = getGetValue;
exports.getScaleFunctor = getScaleFunctor;
exports.getValueFunc = getValueFunc;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _aggregationLayers = require("@deck.gl/aggregation-layers");
var _window = require("global/window");
var _utils = require("@kepler.gl/utils");
var _constants = require("@kepler.gl/constants");
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
/* eslint-disable guard-for-in */
var DECK_AGGREGATION_MAP = exports.DECK_AGGREGATION_MAP = (0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, _aggregationLayers.AGGREGATION_OPERATION.SUM, _constants.AGGREGATION_TYPES.sum), _aggregationLayers.AGGREGATION_OPERATION.MEAN, _constants.AGGREGATION_TYPES.average), _aggregationLayers.AGGREGATION_OPERATION.MIN, _constants.AGGREGATION_TYPES.minimum), _aggregationLayers.AGGREGATION_OPERATION.MAX, _constants.AGGREGATION_TYPES.maximum);
function getValueFunc(aggregation, accessor) {
if (!aggregation || !_aggregationLayers.AGGREGATION_OPERATION[aggregation.toUpperCase()]) {
_window.console.warn("Aggregation ".concat(aggregation, " is not supported"));
}
var op = _aggregationLayers.AGGREGATION_OPERATION[aggregation.toUpperCase()] || _aggregationLayers.AGGREGATION_OPERATION.SUM;
var keplerOp = DECK_AGGREGATION_MAP[op];
return function (pts) {
return (0, _utils.aggregate)(pts.map(accessor), keplerOp);
};
}
function getScaleFunctor(scaleType) {
if (!scaleType || !_constants.SCALE_FUNC[scaleType]) {
_window.console.warn("Scale ".concat(scaleType, " is not supported"));
}
return _constants.SCALE_FUNC[scaleType] || _constants.SCALE_FUNC.quantize;
}
function nop() {
return;
}
function getGetValue(step, props, dimensionUpdater) {
var key = dimensionUpdater.key;
var _step$triggers = step.triggers,
value = _step$triggers.value,
weight = _step$triggers.weight,
aggregation = _step$triggers.aggregation;
var getValue = props[value.prop];
if (getValue === null) {
// If `getValue` is not provided from props, build it with aggregation and weight.
getValue = getValueFunc(props[aggregation.prop], props[weight.prop]);
}
if (getValue) {
this._setDimensionState(key, {
getValue: getValue
});
}
}
function getDimensionSortedBins(step, props, dimensionUpdater) {
var key = dimensionUpdater.key;
var getValue = this.state.dimensions[key].getValue;
// @ts-expect-error
var sortedBins = new _aggregationLayers._BinSorter(this.state.layerData.data || [], {
getValue: getValue,
filterData: props._filterData
});
this._setDimensionState(key, {
sortedBins: sortedBins
});
}
function getDimensionValueDomain(step, props, dimensionUpdater) {
var key = dimensionUpdater.key;
var _step$triggers2 = step.triggers,
lowerPercentile = _step$triggers2.lowerPercentile,
upperPercentile = _step$triggers2.upperPercentile,
scaleType = _step$triggers2.scaleType;
if (!this.state.dimensions[key].sortedBins) {
// the previous step should set sortedBins, if not, something went wrong
return;
}
var valueDomain =
// for log and sqrt scale, returns linear domain by default
// TODO: support other scale function domain in bin sorter
this.state.dimensions[key].sortedBins.getValueDomainByScale(props[scaleType.prop], [props[lowerPercentile.prop], props[upperPercentile.prop]]);
if (props.colorScaleType === 'custom' && props.colorMap) {
// for custom scale, return custom breaks as value domain directly
valueDomain = props.colorMap.reduce(function (prev, cur) {
return Number.isFinite(cur[0]) ? prev.concat(cur[0]) : prev;
}, []);
}
this._setDimensionState(key, {
valueDomain: valueDomain
});
}
function getDimensionScale(step, props, dimensionUpdater) {
var key = dimensionUpdater.key;
var _step$triggers3 = step.triggers,
domain = _step$triggers3.domain,
range = _step$triggers3.range,
scaleType = _step$triggers3.scaleType,
fixed = _step$triggers3.fixed;
var onSet = step.onSet;
if (!this.state.dimensions[key].valueDomain) {
// the previous step should set valueDomain, if not, something went wrong
return;
}
var dimensionRange = props[range.prop];
var dimensionDomain = props[domain.prop] || this.state.dimensions[key].valueDomain;
var dimensionFixed = Boolean(fixed && props[fixed.prop]);
var scaleFunctor = getScaleFunctor(scaleType && props[scaleType.prop])();
var scaleFunc = scaleFunctor.domain(dimensionDomain).range(dimensionFixed ? dimensionDomain : dimensionRange);
scaleFunc.scaleType = props.colorScaleType;
if ((0, _typeof2["default"])(onSet) === 'object' && typeof props[onSet.props] === 'function') {
var sortedBins = this.state.dimensions[key].sortedBins;
props[onSet.props]({
domain: scaleFunc.domain(),
aggregatedBins: sortedBins.binMap
});
}
this._setDimensionState(key, {
scaleFunc: scaleFunc
});
}
function normalizeResult() {
var result = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
// support previous hexagonAggregator API
if (result.hexagons) {
return Object.assign({
data: result.hexagons
}, result);
} else if (result.layerData) {
return Object.assign({
data: result.layerData
}, result);
}
return result;
}
function getAggregatedData(step, props, aggregation, aggregationParams) {
var aggr = step.triggers.aggregator;
var aggregator = props[aggr.prop];
// result should contain a data array and other props
// result = {data: [], ...other props}
var result = aggregator(props, aggregationParams);
this.setState({
layerData: normalizeResult(result)
});
}
var defaultAggregation = exports.defaultAggregation = {
key: 'position',
updateSteps: [{
key: 'aggregate',
triggers: {
cellSize: {
prop: 'cellSize'
},
position: {
prop: 'getPosition',
updateTrigger: 'getPosition'
},
aggregator: {
prop: 'gridAggregator'
}
},
updater: getAggregatedData
}]
};
function getSubLayerAccessor(dimensionState, dimension) {
return function (cell) {
var sortedBins = dimensionState.sortedBins,
scaleFunc = dimensionState.scaleFunc;
var bin = sortedBins.binMap[cell.index];
if (bin && bin.counts === 0) {
// no points left in bin after filtering
return dimension.nullValue;
}
var cv = bin && bin.value;
var domain = scaleFunc.domain();
var isValueInDomain = scaleFunc.scaleType === 'custom' ? cv >= sortedBins.minValue && cv <= sortedBins.maxValue : cv >= domain[0] && cv <= domain[domain.length - 1];
// if cell value is outside domain, set alpha to 0
return isValueInDomain ? scaleFunc(cv) : dimension.nullValue;
};
}
var defaultColorDimension = exports.defaultColorDimension = {
key: 'fillColor',
accessor: 'getFillColor',
getPickingInfo: function getPickingInfo(dimensionState, cell) {
if (!cell) {
return {};
}
var sortedBins = dimensionState.sortedBins;
var colorValue = sortedBins.binMap[cell.index] && sortedBins.binMap[cell.index].value;
return {
colorValue: colorValue
};
},
nullValue: [0, 0, 0, 0],
updateSteps: [{
key: 'getValue',
triggers: {
value: {
prop: 'getColorValue',
updateTrigger: 'getColorValue'
},
weight: {
prop: 'getColorWeight',
updateTrigger: 'getColorWeight'
},
aggregation: {
prop: 'colorAggregation'
}
},
updater: getGetValue
}, {
key: 'getBins',
triggers: {
_filterData: {
prop: '_filterData',
updateTrigger: '_filterData'
}
},
updater: getDimensionSortedBins
}, {
key: 'getDomain',
triggers: {
lowerPercentile: {
prop: 'lowerPercentile'
},
upperPercentile: {
prop: 'upperPercentile'
},
scaleType: {
prop: 'colorScaleType'
}
},
updater: getDimensionValueDomain
}, {
key: 'getScaleFunc',
triggers: {
domain: {
prop: 'colorDomain'
},
range: {
prop: 'colorRange'
},
scaleType: {
prop: 'colorScaleType'
}
},
onSet: {
props: 'onSetColorDomain'
},
updater: getDimensionScale
}],
getSubLayerAccessor: getSubLayerAccessor
};
var defaultElevationDimension = exports.defaultElevationDimension = {
key: 'elevation',
accessor: 'getElevation',
getPickingInfo: function getPickingInfo(dimensionState, cell) {
if (!cell) {
return {};
}
var sortedBins = dimensionState.sortedBins;
var elevationValue = sortedBins.binMap[cell.index] && sortedBins.binMap[cell.index].value;
return {
elevationValue: elevationValue
};
},
nullValue: -1,
updateSteps: [{
key: 'getValue',
triggers: {
value: {
prop: 'getElevationValue',
updateTrigger: 'getElevationValue'
},
weight: {
prop: 'getElevationWeight',
updateTrigger: 'getElevationWeight'
},
aggregation: {
prop: 'elevationAggregation'
}
},
updater: getGetValue
}, {
key: 'getBins',
triggers: {
_filterData: {
prop: '_filterData',
updateTrigger: '_filterData'
}
},
updater: getDimensionSortedBins
}, {
key: 'getDomain',
triggers: {
lowerPercentile: {
prop: 'elevationLowerPercentile'
},
upperPercentile: {
prop: 'elevationUpperPercentile'
},
scaleType: {
prop: 'elevationScaleType'
}
},
updater: getDimensionValueDomain
}, {
key: 'getScaleFunc',
triggers: {
fixed: {
prop: 'elevationFixed'
},
domain: {
prop: 'elevationDomain'
},
range: {
prop: 'elevationRange'
},
scaleType: {
prop: 'elevationScaleType'
}
},
onSet: {
props: 'onSetElevationDomain'
},
updater: getDimensionScale
}],
getSubLayerAccessor: getSubLayerAccessor
};
var _defaultDimensions = exports.defaultDimensions = [defaultColorDimension, defaultElevationDimension];
var CPUAggregator = exports["default"] = /*#__PURE__*/function () {
function CPUAggregator() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
(0, _classCallCheck2["default"])(this, CPUAggregator);
(0, _defineProperty2["default"])(this, "state", void 0);
(0, _defineProperty2["default"])(this, "dimensionUpdaters", void 0);
(0, _defineProperty2["default"])(this, "aggregationUpdater", void 0);
this.state = _objectSpread({
layerData: {},
dimensions: {
// color: {
// getValue: null,
// domain: null,
// sortedBins: null,
// scaleFunc: nop
// },
// elevation: {
// getValue: null,
// domain: null,
// sortedBins: null,
// scaleFunc: nop
// }
}
}, opts.initialState);
this.dimensionUpdaters = {};
this.aggregationUpdater = opts.aggregation || defaultAggregation;
this._addDimension(opts.dimensions || _defaultDimensions);
}
return (0, _createClass2["default"])(CPUAggregator, [{
key: "updateAllDimensions",
value: function updateAllDimensions(props) {
var dimensionChanges = [];
// update all dimensions
for (var dim in this.dimensionUpdaters) {
var updaters = this._accumulateUpdaters(0, props, this.dimensionUpdaters[dim]);
dimensionChanges = dimensionChanges.concat(updaters);
}
dimensionChanges.forEach(function (f) {
return typeof f === 'function' && f();
});
}
}, {
key: "updateAggregation",
value: function updateAggregation(props, aggregationParams) {
var updaters = this._accumulateUpdaters(0, props, this.aggregationUpdater);
updaters.forEach(function (f) {
return typeof f === 'function' && f(aggregationParams);
});
}
}, {
key: "updateState",
value: function updateState(opts, aggregationParams) {
var oldProps = opts.oldProps,
props = opts.props,
changeFlags = opts.changeFlags;
var dimensionChanges = [];
if (changeFlags.dataChanged) {
// if data changed update everything
this.updateAggregation(props, aggregationParams);
this.updateAllDimensions(props);
return this.state;
}
var aggregationChanges = this._getAggregationChanges(oldProps, props, changeFlags);
if (aggregationChanges && aggregationChanges.length) {
// get aggregatedData
aggregationChanges.forEach(function (f) {
return typeof f === 'function' && f(aggregationParams);
});
this.updateAllDimensions(props);
} else {
// only update dimensions
dimensionChanges = this._getDimensionChanges(oldProps, props, changeFlags) || [];
dimensionChanges.forEach(function (f) {
return typeof f === 'function' && f();
});
}
return this.state;
}
// Update private state
}, {
key: "setState",
value: function setState(updateObject) {
this.state = Object.assign({}, this.state, updateObject);
}
// Update private state.dimensions
}, {
key: "_setDimensionState",
value: function _setDimensionState(key, updateObject) {
this.setState({
dimensions: Object.assign({}, this.state.dimensions, (0, _defineProperty2["default"])({}, key, Object.assign({}, this.state.dimensions[key], updateObject)))
});
}
}, {
key: "_addAggregation",
value: function _addAggregation(aggregation) {
this.aggregationUpdater = aggregation;
}
}, {
key: "_addDimension",
value: function _addDimension() {
var _this = this;
var dimensions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
dimensions.forEach(function (dimension) {
var key = dimension.key;
_this.dimensionUpdaters[key] = dimension;
});
}
}, {
key: "_needUpdateStep",
value: function _needUpdateStep(dimensionStep, oldProps, props, changeFlags) {
// whether need to update current dimension step
// dimension step is the value, domain, scaleFunction of each dimension
// each step is an object with properties links to layer prop and whether the prop is
// controlled by updateTriggers
return Object.values(dimensionStep.triggers).some(function (item) {
if (item.updateTrigger) {
// check based on updateTriggers change first
return changeFlags.updateTriggersChanged && (changeFlags.updateTriggersChanged.all || changeFlags.updateTriggersChanged[item.updateTrigger]);
}
// fallback to direct comparison
return oldProps[item.prop] !== props[item.prop];
});
}
}, {
key: "_accumulateUpdaters",
value: function _accumulateUpdaters(step, props, dimension) {
var updaters = [];
for (var i = step; i < dimension.updateSteps.length; i++) {
var updater = dimension.updateSteps[i].updater;
if (typeof updater === 'function') {
updaters.push(updater.bind(this, dimension.updateSteps[i], props, dimension));
}
}
return updaters;
}
}, {
key: "_getAllUpdaters",
value: function _getAllUpdaters(dimension, oldProps, props, changeFlags) {
var _this2 = this;
var updaters = [];
var needUpdateStep = dimension.updateSteps.findIndex(function (step) {
return _this2._needUpdateStep(step, oldProps, props, changeFlags);
});
if (needUpdateStep > -1) {
updaters = updaters.concat(this._accumulateUpdaters(needUpdateStep, props, dimension));
}
return updaters;
}
}, {
key: "_getAggregationChanges",
value: function _getAggregationChanges(oldProps, props, changeFlags) {
var updaters = this._getAllUpdaters(this.aggregationUpdater, oldProps, props, changeFlags);
return updaters.length ? updaters : null;
}
}, {
key: "_getDimensionChanges",
value: function _getDimensionChanges(oldProps, props, changeFlags) {
var updaters = [];
// get dimension to be updated
for (var key in this.dimensionUpdaters) {
// return the first triggered updater for each dimension
var dimension = this.dimensionUpdaters[key];
var dimensionUpdaters = this._getAllUpdaters(dimension, oldProps, props, changeFlags);
updaters = updaters.concat(dimensionUpdaters);
}
return updaters.length ? updaters : null;
}
}, {
key: "getUpdateTriggers",
value: function getUpdateTriggers(props) {
var _this3 = this;
var _updateTriggers = props.updateTriggers || {};
var updateTriggers = {};
var _loop = function _loop() {
var _this3$dimensionUpdat = _this3.dimensionUpdaters[key],
accessor = _this3$dimensionUpdat.accessor,
updateSteps = _this3$dimensionUpdat.updateSteps;
// fold dimension triggers into each accessor
updateTriggers[accessor] = {};
updateSteps.forEach(function (step) {
Object.values(step.triggers || []).forEach(function (_ref) {
var prop = _ref.prop,
updateTrigger = _ref.updateTrigger;
if (updateTrigger) {
// if prop is based on updateTrigger e.g. getColorValue, getColorWeight
// and updateTriggers is passed in from layer prop
// fold the updateTriggers into accessor
var fromProp = _updateTriggers[updateTrigger];
if ((0, _typeof2["default"])(fromProp) === 'object' && !Array.isArray(fromProp)) {
// if updateTrigger is an object spread it
Object.assign(updateTriggers[accessor], fromProp);
} else if (fromProp !== undefined) {
updateTriggers[accessor][prop] = fromProp;
}
} else {
// if prop is not based on updateTrigger
updateTriggers[accessor][prop] = props[prop];
}
});
});
};
for (var key in this.dimensionUpdaters) {
_loop();
}
return updateTriggers;
}
}, {
key: "getPickingInfo",
value: function getPickingInfo(_ref2, layerProps) {
var info = _ref2.info;
var isPicked = info.picked && info.index > -1;
var object = null;
var cell = isPicked ? this.state.layerData.data[info.index] : null;
if (cell) {
var binInfo = {};
for (var key in this.dimensionUpdaters) {
var _getPickingInfo = this.dimensionUpdaters[key].getPickingInfo;
if (typeof _getPickingInfo === 'function') {
binInfo = Object.assign({}, binInfo, _getPickingInfo(this.state.dimensions[key], cell, layerProps));
}
}
object = Object.assign(binInfo, cell, {
points: cell.filteredPoints || cell.points
});
}
// add bin and to info
return Object.assign(info, {
picked: Boolean(object),
// override object with picked cell
object: object
});
}
}, {
key: "getAccessor",
value: function getAccessor(dimensionKey, layerProps) {
if (!Object.prototype.hasOwnProperty.call(this.dimensionUpdaters, dimensionKey)) {
return nop;
}
return this.dimensionUpdaters[dimensionKey].getSubLayerAccessor(this.state.dimensions[dimensionKey], this.dimensionUpdaters[dimensionKey], layerProps);
}
}], [{
key: "defaultDimensions",
value: function defaultDimensions() {
return _defaultDimensions;
}
}]);
}();
(0, _defineProperty2["default"])(CPUAggregator, "getDimensionScale", void 0);
CPUAggregator.getDimensionScale = getDimensionScale;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYWdncmVnYXRpb25MYXllcnMiLCJyZXF1aXJlIiwiX3dpbmRvdyIsIl91dGlscyIsIl9jb25zdGFudHMiLCJvd25LZXlzIiwiZSIsInIiLCJ0IiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsIm8iLCJmaWx0ZXIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJlbnVtZXJhYmxlIiwicHVzaCIsImFwcGx5IiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJkZWZpbmVQcm9wZXJ0eSIsIkRFQ0tfQUdHUkVHQVRJT05fTUFQIiwiZXhwb3J0cyIsIkFHR1JFR0FUSU9OX09QRVJBVElPTiIsIlNVTSIsIkFHR1JFR0FUSU9OX1RZUEVTIiwic3VtIiwiTUVBTiIsImF2ZXJhZ2UiLCJNSU4iLCJtaW5pbXVtIiwiTUFYIiwibWF4aW11bSIsImdldFZhbHVlRnVuYyIsImFnZ3JlZ2F0aW9uIiwiYWNjZXNzb3IiLCJ0b1VwcGVyQ2FzZSIsIkNvbnNvbGUiLCJ3YXJuIiwiY29uY2F0Iiwib3AiLCJrZXBsZXJPcCIsInB0cyIsImFnZ3JlZ2F0ZSIsIm1hcCIsImdldFNjYWxlRnVuY3RvciIsInNjYWxlVHlwZSIsIlNDQUxFX0ZVTkMiLCJxdWFudGl6ZSIsIm5vcCIsImdldEdldFZhbHVlIiwic3RlcCIsInByb3BzIiwiZGltZW5zaW9uVXBkYXRlciIsImtleSIsIl9zdGVwJHRyaWdnZXJzIiwidHJpZ2dlcnMiLCJ2YWx1ZSIsIndlaWdodCIsImdldFZhbHVlIiwicHJvcCIsIl9zZXREaW1lbnNpb25TdGF0ZSIsImdldERpbWVuc2lvblNvcnRlZEJpbnMiLCJzdGF0ZSIsImRpbWVuc2lvbnMiLCJzb3J0ZWRCaW5zIiwiQmluU29ydGVyIiwibGF5ZXJEYXRhIiwiZGF0YSIsImZpbHRlckRhdGEiLCJfZmlsdGVyRGF0YSIsImdldERpbWVuc2lvblZhbHVlRG9tYWluIiwiX3N0ZXAkdHJpZ2dlcnMyIiwibG93ZXJQZXJjZW50aWxlIiwidXBwZXJQZXJjZW50aWxlIiwidmFsdWVEb21haW4iLCJnZXRWYWx1ZURvbWFpbkJ5U2NhbGUiLCJjb2xvclNjYWxlVHlwZSIsImNvbG9yTWFwIiwicmVkdWNlIiwicHJldiIsImN1ciIsIk51bWJlciIsImlzRmluaXRlIiwiZ2V0RGltZW5zaW9uU2NhbGUiLCJfc3RlcCR0cmlnZ2VyczMiLCJkb21haW4iLCJyYW5nZSIsImZpeGVkIiwib25TZXQiLCJkaW1lbnNpb25SYW5nZSIsImRpbWVuc2lvbkRvbWFpbiIsImRpbWVuc2lvbkZpeGVkIiwiQm9vbGVhbiIsInNjYWxlRnVuY3RvciIsInNjYWxlRnVuYyIsIl90eXBlb2YyIiwiYWdncmVnYXRlZEJpbnMiLCJiaW5NYXAiLCJub3JtYWxpemVSZXN1bHQiLCJyZXN1bHQiLCJ1bmRlZmluZWQiLCJoZXhhZ29ucyIsImFzc2lnbiIsImdldEFnZ3JlZ2F0ZWREYXRhIiwiYWdncmVnYXRpb25QYXJhbXMiLCJhZ2dyIiwiYWdncmVnYXRvciIsInNldFN0YXRlIiwiZGVmYXVsdEFnZ3JlZ2F0aW9uIiwidXBkYXRlU3RlcHMiLCJjZWxsU2l6ZSIsInBvc2l0aW9uIiwidXBkYXRlVHJpZ2dlciIsInVwZGF0ZXIiLCJnZXRTdWJMYXllckFjY2Vzc29yIiwiZGltZW5zaW9uU3RhdGUiLCJkaW1lbnNpb24iLCJjZWxsIiwiYmluIiwiaW5kZXgiLCJjb3VudHMiLCJudWxsVmFsdWUiLCJjdiIsImlzVmFsdWVJbkRvbWFpbiIsIm1pblZhbHVlIiwibWF4VmFsdWUiLCJkZWZhdWx0Q29sb3JEaW1lbnNpb24iLCJnZXRQaWNraW5nSW5mbyIsImNvbG9yVmFsdWUiLCJkZWZhdWx0RWxldmF0aW9uRGltZW5zaW9uIiwiZWxldmF0aW9uVmFsdWUiLCJkZWZhdWx0RGltZW5zaW9ucyIsIkNQVUFnZ3JlZ2F0b3IiLCJvcHRzIiwiX2NsYXNzQ2FsbENoZWNrMiIsImluaXRpYWxTdGF0ZSIsImRpbWVuc2lvblVwZGF0ZXJzIiwiYWdncmVnYXRpb25VcGRhdGVyIiwiX2FkZERpbWVuc2lvbiIsIl9jcmVhdGVDbGFzczIiLCJ1cGRhdGVBbGxEaW1lbnNpb25zIiwiZGltZW5zaW9uQ2hhbmdlcyIsImRpbSIsInVwZGF0ZXJzIiwiX2FjY3VtdWxhdGVVcGRhdGVycyIsImYiLCJ1cGRhdGVBZ2dyZWdhdGlvbiIsInVwZGF0ZVN0YXRlIiwib2xkUHJvcHMiLCJjaGFuZ2VGbGFncyIsImRhdGFDaGFuZ2VkIiwiYWdncmVnYXRpb25DaGFuZ2VzIiwiX2dldEFnZ3JlZ2F0aW9uQ2hhbmdlcyIsIl9nZXREaW1lbnNpb25DaGFuZ2VzIiwidXBkYXRlT2JqZWN0IiwiX2FkZEFnZ3JlZ2F0aW9uIiwiX3RoaXMiLCJfbmVlZFVwZGF0ZVN0ZXAiLCJkaW1lbnNpb25TdGVwIiwidmFsdWVzIiwic29tZSIsIml0ZW0iLCJ1cGRhdGVUcmlnZ2Vyc0NoYW5nZWQiLCJhbGwiLCJpIiwiYmluZCIsIl9nZXRBbGxVcGRhdGVycyIsIl90aGlzMiIsIm5lZWRVcGRhdGVTdGVwIiwiZmluZEluZGV4IiwiZ2V0VXBkYXRlVHJpZ2dlcnMiLCJfdGhpczMiLCJfdXBkYXRlVHJpZ2dlcnMiLCJ1cGRhdGVUcmlnZ2VycyIsIl9sb29wIiwiX3RoaXMzJGRpbWVuc2lvblVwZGF0IiwiX3JlZiIsImZyb21Qcm9wIiwiQXJyYXkiLCJpc0FycmF5IiwiX3JlZjIiLCJsYXllclByb3BzIiwiaW5mbyIsImlzUGlja2VkIiwicGlja2VkIiwib2JqZWN0IiwiYmluSW5mbyIsInBvaW50cyIsImZpbHRlcmVkUG9pbnRzIiwiZ2V0QWNjZXNzb3IiLCJkaW1lbnNpb25LZXkiLCJwcm90b3R5cGUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvbGF5ZXItdXRpbHMvY3B1LWFnZ3JlZ2F0b3IudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuLyogZXNsaW50LWRpc2FibGUgZ3VhcmQtZm9yLWluICovXG5pbXBvcnQge0FHR1JFR0FUSU9OX09QRVJBVElPTiwgX0JpblNvcnRlciBhcyBCaW5Tb3J0ZXJ9IGZyb20gJ0BkZWNrLmdsL2FnZ3JlZ2F0aW9uLWxheWVycyc7XG5pbXBvcnQge2NvbnNvbGUgYXMgQ29uc29sZX0gZnJvbSAnZ2xvYmFsL3dpbmRvdyc7XG5cbmltcG9ydCB7YWdncmVnYXRlfSBmcm9tICdAa2VwbGVyLmdsL3V0aWxzJztcbmltcG9ydCB7QUdHUkVHQVRJT05fVFlQRVMsIFNDQUxFX0ZVTkN9IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcbmltcG9ydCB7UkdCQUNvbG9yfSBmcm9tICdAa2VwbGVyLmdsL3R5cGVzJztcblxuZXhwb3J0IHR5cGUgVXBkYXRlclR5cGUgPSAodGhpczogQ1BVQWdncmVnYXRvciwgc3RlcCwgcHJvcHMsIGRpbWVuc2lvblVwZGF0ZXIpID0+IHZvaWQ7XG5leHBvcnQgdHlwZSBCaW5kZWRVcGRhdGVyVHlwZSA9ICgpID0+IHZvaWQ7XG5leHBvcnQgdHlwZSBBZ2dyZWdhdGVkVXBkYXRlclR5cGUgPSAoXG4gIHRoaXM6IENQVUFnZ3JlZ2F0b3IsXG4gIHN0ZXAsXG4gIHByb3BzLFxuICBhZ2dyZWdhdGlvbixcbiAgYWdncmVnYXRpb25QYXJhbXNcbikgPT4gdm9pZDtcbmV4cG9ydCB0eXBlIEJpbmRlZEFnZ3JlZ2F0ZWRVcGRhdGVyVHlwZSA9IChhZ2dyZWdhdGlvblBhcmFtcykgPT4gdm9pZDtcblxuZXhwb3J0IHR5cGUgVXBkYXRlU3RlcHNUeXBlID0ge1xuICBrZXk6IHN0cmluZztcbiAgdHJpZ2dlcnM6IHtcbiAgICBba2V5OiBzdHJpbmddOiB7XG4gICAgICBwcm9wOiBzdHJpbmc7XG4gICAgICB1cGRhdGVUcmlnZ2VyPzogc3RyaW5nO1xuICAgIH07XG4gIH07XG4gIG9uU2V0Pzoge1xuICAgIHByb3BzOiBzdHJpbmc7XG4gIH07XG4gIHVwZGF0ZXI6IFVwZGF0ZXJUeXBlO1xufTtcblxuZXhwb3J0IHR5cGUgRGltZW5zaW9uVHlwZTxWYWx1ZVR5cGUgPSBhbnk+ID0ge1xuICBrZXk6IHN0cmluZztcbiAgYWNjZXNzb3I6IHN0cmluZztcbiAgZ2V0UGlja2luZ0luZm86IChkaW1lbnNpb25TdGF0ZSwgY2VsbCwgbGF5ZXJQcm9wcz8pID0+IGFueTtcbiAgbnVsbFZhbHVlOiBWYWx1ZVR5cGU7XG4gIHVwZGF0ZVN0ZXBzOiBVcGRhdGVTdGVwc1R5cGVbXTtcbiAgZ2V0U3ViTGF5ZXJBY2Nlc3Nvcjtcbn07XG5cbmV4cG9ydCB0eXBlIEFnZ3JlZ2F0aW9uVXBkYXRlU3RlcHNUeXBlID0ge1xuICBrZXk6IHN0cmluZztcbiAgdHJpZ2dlcnM6IHtcbiAgICBba2V5OiBzdHJpbmddOiB7XG4gICAgICBwcm9wOiBzdHJpbmc7XG4gICAgICB1cGRhdGVUcmlnZ2VyPzogc3RyaW5nO1xuICAgIH07XG4gIH07XG4gIHVwZGF0ZXI6IEFnZ3JlZ2F0ZWRVcGRhdGVyVHlwZTtcbn07XG5cbmV4cG9ydCB0eXBlIEFnZ3JlZ2F0aW9uVHlwZSA9IHtcbiAga2V5OiBzdHJpbmc7XG4gIHVwZGF0ZVN0ZXBzOiBBZ2dyZWdhdGlvblVwZGF0ZVN0ZXBzVHlwZVtdO1xufTtcblxuZXhwb3J0IGNvbnN0IERFQ0tfQUdHUkVHQVRJT05fTUFQID0ge1xuICBbQUdHUkVHQVRJT05fT1BFUkFUSU9OLlNVTV06IEFHR1JFR0FUSU9OX1RZUEVTLnN1bSxcbiAgW0FHR1JFR0FUSU9OX09QRVJBVElPTi5NRUFOXTogQUdHUkVHQVRJT05fVFlQRVMuYXZlcmFnZSxcbiAgW0FHR1JFR0FUSU9OX09QRVJBVElPTi5NSU5dOiBBR0dSRUdBVElPTl9UWVBFUy5taW5pbXVtLFxuICBbQUdHUkVHQVRJT05fT1BFUkFUSU9OLk1BWF06IEFHR1JFR0FUSU9OX1RZUEVTLm1heGltdW1cbn07XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRWYWx1ZUZ1bmMoYWdncmVnYXRpb24sIGFjY2Vzc29yKSB7XG4gIGlmICghYWdncmVnYXRpb24gfHwgIUFHR1JFR0FUSU9OX09QRVJBVElPTlthZ2dyZWdhdGlvbi50b1VwcGVyQ2FzZSgpXSkge1xuICAgIENvbnNvbGUud2FybihgQWdncmVnYXRpb24gJHthZ2dyZWdhdGlvbn0gaXMgbm90IHN1cHBvcnRlZGApO1xuICB9XG5cbiAgY29uc3Qgb3AgPSBBR0dSRUdBVElPTl9PUEVSQVRJT05bYWdncmVnYXRpb24udG9VcHBlckNhc2UoKV0gfHwgQUdHUkVHQVRJT05fT1BFUkFUSU9OLlNVTTtcbiAgY29uc3Qga2VwbGVyT3AgPSBERUNLX0FHR1JFR0FUSU9OX01BUFtvcF07XG5cbiAgcmV0dXJuIHB0cyA9PiBhZ2dyZWdhdGUocHRzLm1hcChhY2Nlc3NvciksIGtlcGxlck9wKTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldFNjYWxlRnVuY3RvcihzY2FsZVR5cGUpIHtcbiAgaWYgKCFzY2FsZVR5cGUgfHwgIVNDQUxFX0ZVTkNbc2NhbGVUeXBlXSkge1xuICAgIENvbnNvbGUud2FybihgU2NhbGUgJHtzY2FsZVR5cGV9IGlzIG5vdCBzdXBwb3J0ZWRgKTtcbiAgfVxuICByZXR1cm4gU0NBTEVfRlVOQ1tzY2FsZVR5cGVdIHx8IFNDQUxFX0ZVTkMucXVhbnRpemU7XG59XG5cbmZ1bmN0aW9uIG5vcCgpIHtcbiAgcmV0dXJuO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0R2V0VmFsdWUodGhpczogQ1BVQWdncmVnYXRvciwgc3RlcCwgcHJvcHMsIGRpbWVuc2lvblVwZGF0ZXIpIHtcbiAgY29uc3Qge2tleX0gPSBkaW1lbnNpb25VcGRhdGVyO1xuICBjb25zdCB7dmFsdWUsIHdlaWdodCwgYWdncmVnYXRpb259ID0gc3RlcC50cmlnZ2VycztcblxuICBsZXQgZ2V0VmFsdWUgPSBwcm9wc1t2YWx1ZS5wcm9wXTtcblxuICBpZiAoZ2V0VmFsdWUgPT09IG51bGwpIHtcbiAgICAvLyBJZiBgZ2V0VmFsdWVgIGlzIG5vdCBwcm92aWRlZCBmcm9tIHByb3BzLCBidWlsZCBpdCB3aXRoIGFnZ3JlZ2F0aW9uIGFuZCB3ZWlnaHQuXG4gICAgZ2V0VmFsdWUgPSBnZXRWYWx1ZUZ1bmMocHJvcHNbYWdncmVnYXRpb24ucHJvcF0sIHByb3BzW3dlaWdodC5wcm9wXSk7XG4gIH1cblxuICBpZiAoZ2V0VmFsdWUpIHtcbiAgICB0aGlzLl9zZXREaW1lbnNpb25TdGF0ZShrZXksIHtnZXRWYWx1ZX0pO1xuICB9XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXREaW1lbnNpb25Tb3J0ZWRCaW5zKHRoaXM6IENQVUFnZ3JlZ2F0b3IsIHN0ZXAsIHByb3BzLCBkaW1lbnNpb25VcGRhdGVyKSB7XG4gIGNvbnN0IHtrZXl9ID0gZGltZW5zaW9uVXBkYXRlcjtcbiAgY29uc3Qge2dldFZhbHVlfSA9IHRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldO1xuICAvLyBAdHMtZXhwZWN0LWVycm9yXG4gIGNvbnN0IHNvcnRlZEJpbnMgPSBuZXcgQmluU29ydGVyKHRoaXMuc3RhdGUubGF5ZXJEYXRhLmRhdGEgfHwgW10sIHtcbiAgICBnZXRWYWx1ZSxcbiAgICBmaWx0ZXJEYXRhOiBwcm9wcy5fZmlsdGVyRGF0YVxuICB9KTtcbiAgdGhpcy5fc2V0RGltZW5zaW9uU3RhdGUoa2V5LCB7c29ydGVkQmluc30pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0RGltZW5zaW9uVmFsdWVEb21haW4odGhpczogQ1BVQWdncmVnYXRvciwgc3RlcCwgcHJvcHMsIGRpbWVuc2lvblVwZGF0ZXIpIHtcbiAgY29uc3Qge2tleX0gPSBkaW1lbnNpb25VcGRhdGVyO1xuICBjb25zdCB7XG4gICAgdHJpZ2dlcnM6IHtsb3dlclBlcmNlbnRpbGUsIHVwcGVyUGVyY2VudGlsZSwgc2NhbGVUeXBlfVxuICB9ID0gc3RlcDtcblxuICBpZiAoIXRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldLnNvcnRlZEJpbnMpIHtcbiAgICAvLyB0aGUgcHJldmlvdXMgc3RlcCBzaG91bGQgc2V0IHNvcnRlZEJpbnMsIGlmIG5vdCwgc29tZXRoaW5nIHdlbnQgd3JvbmdcbiAgICByZXR1cm47XG4gIH1cblxuICBsZXQgdmFsdWVEb21haW4gPVxuICAgIC8vIGZvciBsb2cgYW5kIHNxcnQgc2NhbGUsIHJldHVybnMgbGluZWFyIGRvbWFpbiBieSBkZWZhdWx0XG4gICAgLy8gVE9ETzogc3VwcG9ydCBvdGhlciBzY2FsZSBmdW5jdGlvbiBkb21haW4gaW4gYmluIHNvcnRlclxuICAgIHRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldLnNvcnRlZEJpbnMuZ2V0VmFsdWVEb21haW5CeVNjYWxlKHByb3BzW3NjYWxlVHlwZS5wcm9wXSwgW1xuICAgICAgcHJvcHNbbG93ZXJQZXJjZW50aWxlLnByb3BdLFxuICAgICAgcHJvcHNbdXBwZXJQZXJjZW50aWxlLnByb3BdXG4gICAgXSk7XG5cbiAgaWYgKHByb3BzLmNvbG9yU2NhbGVUeXBlID09PSAnY3VzdG9tJyAmJiBwcm9wcy5jb2xvck1hcCkge1xuICAgIC8vIGZvciBjdXN0b20gc2NhbGUsIHJldHVybiBjdXN0b20gYnJlYWtzIGFzIHZhbHVlIGRvbWFpbiBkaXJlY3RseVxuICAgIHZhbHVlRG9tYWluID0gcHJvcHMuY29sb3JNYXAucmVkdWNlKFxuICAgICAgKHByZXYsIGN1cikgPT4gKE51bWJlci5pc0Zpbml0ZShjdXJbMF0pID8gcHJldi5jb25jYXQoY3VyWzBdKSA6IHByZXYpLFxuICAgICAgW11cbiAgICApO1xuICB9XG5cbiAgdGhpcy5fc2V0RGltZW5zaW9uU3RhdGUoa2V5LCB7dmFsdWVEb21haW59KTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldERpbWVuc2lvblNjYWxlKHRoaXM6IENQVUFnZ3JlZ2F0b3IsIHN0ZXAsIHByb3BzLCBkaW1lbnNpb25VcGRhdGVyKSB7XG4gIGNvbnN0IHtrZXl9ID0gZGltZW5zaW9uVXBkYXRlcjtcbiAgY29uc3Qge2RvbWFpbiwgcmFuZ2UsIHNjYWxlVHlwZSwgZml4ZWR9ID0gc3RlcC50cmlnZ2VycztcbiAgY29uc3Qge29uU2V0fSA9IHN0ZXA7XG4gIGlmICghdGhpcy5zdGF0ZS5kaW1lbnNpb25zW2tleV0udmFsdWVEb21haW4pIHtcbiAgICAvLyB0aGUgcHJldmlvdXMgc3RlcCBzaG91bGQgc2V0IHZhbHVlRG9tYWluLCBpZiBub3QsIHNvbWV0aGluZyB3ZW50IHdyb25nXG4gICAgcmV0dXJuO1xuICB9XG5cbiAgY29uc3QgZGltZW5zaW9uUmFuZ2UgPSBwcm9wc1tyYW5nZS5wcm9wXTtcbiAgY29uc3QgZGltZW5zaW9uRG9tYWluID0gcHJvcHNbZG9tYWluLnByb3BdIHx8IHRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldLnZhbHVlRG9tYWluO1xuICBjb25zdCBkaW1lbnNpb25GaXhlZCA9IEJvb2xlYW4oZml4ZWQgJiYgcHJvcHNbZml4ZWQucHJvcF0pO1xuXG4gIGNvbnN0IHNjYWxlRnVuY3RvciA9IGdldFNjYWxlRnVuY3RvcihzY2FsZVR5cGUgJiYgcHJvcHNbc2NhbGVUeXBlLnByb3BdKSgpO1xuXG4gIGNvbnN0IHNjYWxlRnVuYyA9IHNjYWxlRnVuY3RvclxuICAgIC5kb21haW4oZGltZW5zaW9uRG9tYWluKVxuICAgIC5yYW5nZShkaW1lbnNpb25GaXhlZCA/IGRpbWVuc2lvbkRvbWFpbiA6IGRpbWVuc2lvblJhbmdlKTtcbiAgc2NhbGVGdW5jLnNjYWxlVHlwZSA9IHByb3BzLmNvbG9yU2NhbGVUeXBlO1xuXG4gIGlmICh0eXBlb2Ygb25TZXQgPT09ICdvYmplY3QnICYmIHR5cGVvZiBwcm9wc1tvblNldC5wcm9wc10gPT09ICdmdW5jdGlvbicpIHtcbiAgICBjb25zdCBzb3J0ZWRCaW5zID0gdGhpcy5zdGF0ZS5kaW1lbnNpb25zW2tleV0uc29ydGVkQmlucztcbiAgICBwcm9wc1tvblNldC5wcm9wc10oe2RvbWFpbjogc2NhbGVGdW5jLmRvbWFpbigpLCBhZ2dyZWdhdGVkQmluczogc29ydGVkQmlucy5iaW5NYXB9KTtcbiAgfVxuICB0aGlzLl9zZXREaW1lbnNpb25TdGF0ZShrZXksIHtzY2FsZUZ1bmN9KTtcbn1cblxuZnVuY3Rpb24gbm9ybWFsaXplUmVzdWx0KHJlc3VsdDoge2hleGFnb25zPzsgbGF5ZXJEYXRhP30gPSB7fSkge1xuICAvLyBzdXBwb3J0IHByZXZpb3VzIGhleGFnb25BZ2dyZWdhdG9yIEFQSVxuICBpZiAocmVzdWx0LmhleGFnb25zKSB7XG4gICAgcmV0dXJuIE9iamVjdC5hc3NpZ24oe2RhdGE6IHJlc3VsdC5oZXhhZ29uc30sIHJlc3VsdCk7XG4gIH0gZWxzZSBpZiAocmVzdWx0LmxheWVyRGF0YSkge1xuICAgIHJldHVybiBPYmplY3QuYXNzaWduKHtkYXRhOiByZXN1bHQubGF5ZXJEYXRhfSwgcmVzdWx0KTtcbiAgfVxuXG4gIHJldHVybiByZXN1bHQ7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBnZXRBZ2dyZWdhdGVkRGF0YShcbiAgdGhpczogQ1BVQWdncmVnYXRvcixcbiAgc3RlcCxcbiAgcHJvcHMsXG4gIGFnZ3JlZ2F0aW9uLFxuICBhZ2dyZWdhdGlvblBhcmFtc1xuKSB7XG4gIGNvbnN0IHtcbiAgICB0cmlnZ2Vyczoge2FnZ3JlZ2F0b3I6IGFnZ3J9XG4gIH0gPSBzdGVwO1xuICBjb25zdCBhZ2dyZWdhdG9yID0gcHJvcHNbYWdnci5wcm9wXTtcblxuICAvLyByZXN1bHQgc2hvdWxkIGNvbnRhaW4gYSBkYXRhIGFycmF5IGFuZCBvdGhlciBwcm9wc1xuICAvLyByZXN1bHQgPSB7ZGF0YTogW10sIC4uLm90aGVyIHByb3BzfVxuICBjb25zdCByZXN1bHQgPSBhZ2dyZWdhdG9yKHByb3BzLCBhZ2dyZWdhdGlvblBhcmFtcyk7XG4gIHRoaXMuc2V0U3RhdGUoe1xuICAgIGxheWVyRGF0YTogbm9ybWFsaXplUmVzdWx0KHJlc3VsdClcbiAgfSk7XG59XG5cbmV4cG9ydCBjb25zdCBkZWZhdWx0QWdncmVnYXRpb246IEFnZ3JlZ2F0aW9uVHlwZSA9IHtcbiAga2V5OiAncG9zaXRpb24nLFxuICB1cGRhdGVTdGVwczogW1xuICAgIHtcbiAgICAgIGtleTogJ2FnZ3JlZ2F0ZScsXG4gICAgICB0cmlnZ2Vyczoge1xuICAgICAgICBjZWxsU2l6ZToge1xuICAgICAgICAgIHByb3A6ICdjZWxsU2l6ZSdcbiAgICAgICAgfSxcbiAgICAgICAgcG9zaXRpb246IHtcbiAgICAgICAgICBwcm9wOiAnZ2V0UG9zaXRpb24nLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdnZXRQb3NpdGlvbidcbiAgICAgICAgfSxcbiAgICAgICAgYWdncmVnYXRvcjoge1xuICAgICAgICAgIHByb3A6ICdncmlkQWdncmVnYXRvcidcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldEFnZ3JlZ2F0ZWREYXRhXG4gICAgfVxuICBdXG59O1xuXG5mdW5jdGlvbiBnZXRTdWJMYXllckFjY2Vzc29yKGRpbWVuc2lvblN0YXRlLCBkaW1lbnNpb24pIHtcbiAgcmV0dXJuIGNlbGwgPT4ge1xuICAgIGNvbnN0IHtzb3J0ZWRCaW5zLCBzY2FsZUZ1bmN9ID0gZGltZW5zaW9uU3RhdGU7XG4gICAgY29uc3QgYmluID0gc29ydGVkQmlucy5iaW5NYXBbY2VsbC5pbmRleF07XG5cbiAgICBpZiAoYmluICYmIGJpbi5jb3VudHMgPT09IDApIHtcbiAgICAgIC8vIG5vIHBvaW50cyBsZWZ0IGluIGJpbiBhZnRlciBmaWx0ZXJpbmdcbiAgICAgIHJldHVybiBkaW1lbnNpb24ubnVsbFZhbHVlO1xuICAgIH1cblxuICAgIGNvbnN0IGN2ID0gYmluICYmIGJpbi52YWx1ZTtcbiAgICBjb25zdCBkb21haW4gPSBzY2FsZUZ1bmMuZG9tYWluKCk7XG5cbiAgICBjb25zdCBpc1ZhbHVlSW5Eb21haW4gPVxuICAgICAgc2NhbGVGdW5jLnNjYWxlVHlwZSA9PT0gJ2N1c3RvbSdcbiAgICAgICAgPyBjdiA+PSBzb3J0ZWRCaW5zLm1pblZhbHVlICYmIGN2IDw9IHNvcnRlZEJpbnMubWF4VmFsdWVcbiAgICAgICAgOiBjdiA+PSBkb21haW5bMF0gJiYgY3YgPD0gZG9tYWluW2RvbWFpbi5sZW5ndGggLSAxXTtcblxuICAgIC8vIGlmIGNlbGwgdmFsdWUgaXMgb3V0c2lkZSBkb21haW4sIHNldCBhbHBoYSB0byAwXG4gICAgcmV0dXJuIGlzVmFsdWVJbkRvbWFpbiA/IHNjYWxlRnVuYyhjdikgOiBkaW1lbnNpb24ubnVsbFZhbHVlO1xuICB9O1xufVxuXG5leHBvcnQgY29uc3QgZGVmYXVsdENvbG9yRGltZW5zaW9uOiBEaW1lbnNpb25UeXBlPFJHQkFDb2xvcj4gPSB7XG4gIGtleTogJ2ZpbGxDb2xvcicsXG4gIGFjY2Vzc29yOiAnZ2V0RmlsbENvbG9yJyxcbiAgZ2V0UGlja2luZ0luZm86IChkaW1lbnNpb25TdGF0ZSwgY2VsbCkgPT4ge1xuICAgIGlmICghY2VsbCkge1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cbiAgICBjb25zdCB7c29ydGVkQmluc30gPSBkaW1lbnNpb25TdGF0ZTtcbiAgICBjb25zdCBjb2xvclZhbHVlID0gc29ydGVkQmlucy5iaW5NYXBbY2VsbC5pbmRleF0gJiYgc29ydGVkQmlucy5iaW5NYXBbY2VsbC5pbmRleF0udmFsdWU7XG4gICAgcmV0dXJuIHtjb2xvclZhbHVlfTtcbiAgfSxcbiAgbnVsbFZhbHVlOiBbMCwgMCwgMCwgMF0sXG4gIHVwZGF0ZVN0ZXBzOiBbXG4gICAge1xuICAgICAga2V5OiAnZ2V0VmFsdWUnLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgdmFsdWU6IHtcbiAgICAgICAgICBwcm9wOiAnZ2V0Q29sb3JWYWx1ZScsXG4gICAgICAgICAgdXBkYXRlVHJpZ2dlcjogJ2dldENvbG9yVmFsdWUnXG4gICAgICAgIH0sXG4gICAgICAgIHdlaWdodDoge1xuICAgICAgICAgIHByb3A6ICdnZXRDb2xvcldlaWdodCcsXG4gICAgICAgICAgdXBkYXRlVHJpZ2dlcjogJ2dldENvbG9yV2VpZ2h0J1xuICAgICAgICB9LFxuICAgICAgICBhZ2dyZWdhdGlvbjoge1xuICAgICAgICAgIHByb3A6ICdjb2xvckFnZ3JlZ2F0aW9uJ1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgdXBkYXRlcjogZ2V0R2V0VmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJ2dldEJpbnMnLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgX2ZpbHRlckRhdGE6IHtcbiAgICAgICAgICBwcm9wOiAnX2ZpbHRlckRhdGEnLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdfZmlsdGVyRGF0YSdcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldERpbWVuc2lvblNvcnRlZEJpbnNcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJ2dldERvbWFpbicsXG4gICAgICB0cmlnZ2Vyczoge1xuICAgICAgICBsb3dlclBlcmNlbnRpbGU6IHtcbiAgICAgICAgICBwcm9wOiAnbG93ZXJQZXJjZW50aWxlJ1xuICAgICAgICB9LFxuICAgICAgICB1cHBlclBlcmNlbnRpbGU6IHtcbiAgICAgICAgICBwcm9wOiAndXBwZXJQZXJjZW50aWxlJ1xuICAgICAgICB9LFxuICAgICAgICBzY2FsZVR5cGU6IHtwcm9wOiAnY29sb3JTY2FsZVR5cGUnfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldERpbWVuc2lvblZhbHVlRG9tYWluXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICdnZXRTY2FsZUZ1bmMnLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgZG9tYWluOiB7cHJvcDogJ2NvbG9yRG9tYWluJ30sXG4gICAgICAgIHJhbmdlOiB7cHJvcDogJ2NvbG9yUmFuZ2UnfSxcbiAgICAgICAgc2NhbGVUeXBlOiB7cHJvcDogJ2NvbG9yU2NhbGVUeXBlJ31cbiAgICAgIH0sXG4gICAgICBvblNldDoge1xuICAgICAgICBwcm9wczogJ29uU2V0Q29sb3JEb21haW4nXG4gICAgICB9LFxuICAgICAgdXBkYXRlcjogZ2V0RGltZW5zaW9uU2NhbGVcbiAgICB9XG4gIF0sXG4gIGdldFN1YkxheWVyQWNjZXNzb3Jcbn07XG5cbmV4cG9ydCBjb25zdCBkZWZhdWx0RWxldmF0aW9uRGltZW5zaW9uOiBEaW1lbnNpb25UeXBlPG51bWJlcj4gPSB7XG4gIGtleTogJ2VsZXZhdGlvbicsXG4gIGFjY2Vzc29yOiAnZ2V0RWxldmF0aW9uJyxcbiAgZ2V0UGlja2luZ0luZm86IChkaW1lbnNpb25TdGF0ZSwgY2VsbCkgPT4ge1xuICAgIGlmICghY2VsbCkge1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cbiAgICBjb25zdCB7c29ydGVkQmluc30gPSBkaW1lbnNpb25TdGF0ZTtcbiAgICBjb25zdCBlbGV2YXRpb25WYWx1ZSA9IHNvcnRlZEJpbnMuYmluTWFwW2NlbGwuaW5kZXhdICYmIHNvcnRlZEJpbnMuYmluTWFwW2NlbGwuaW5kZXhdLnZhbHVlO1xuICAgIHJldHVybiB7ZWxldmF0aW9uVmFsdWV9O1xuICB9LFxuICBudWxsVmFsdWU6IC0xLFxuICB1cGRhdGVTdGVwczogW1xuICAgIHtcbiAgICAgIGtleTogJ2dldFZhbHVlJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAgcHJvcDogJ2dldEVsZXZhdGlvblZhbHVlJyxcbiAgICAgICAgICB1cGRhdGVUcmlnZ2VyOiAnZ2V0RWxldmF0aW9uVmFsdWUnXG4gICAgICAgIH0sXG4gICAgICAgIHdlaWdodDoge1xuICAgICAgICAgIHByb3A6ICdnZXRFbGV2YXRpb25XZWlnaHQnLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdnZXRFbGV2YXRpb25XZWlnaHQnXG4gICAgICAgIH0sXG4gICAgICAgIGFnZ3JlZ2F0aW9uOiB7XG4gICAgICAgICAgcHJvcDogJ2VsZXZhdGlvbkFnZ3JlZ2F0aW9uJ1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgdXBkYXRlcjogZ2V0R2V0VmFsdWVcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJ2dldEJpbnMnLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgX2ZpbHRlckRhdGE6IHtcbiAgICAgICAgICBwcm9wOiAnX2ZpbHRlckRhdGEnLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdfZmlsdGVyRGF0YSdcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldERpbWVuc2lvblNvcnRlZEJpbnNcbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJ2dldERvbWFpbicsXG4gICAgICB0cmlnZ2Vyczoge1xuICAgICAgICBsb3dlclBlcmNlbnRpbGU6IHtcbiAgICAgICAgICBwcm9wOiAnZWxldmF0aW9uTG93ZXJQZXJjZW50aWxlJ1xuICAgICAgICB9LFxuICAgICAgICB1cHBlclBlcmNlbnRpbGU6IHtcbiAgICAgICAgICBwcm9wOiAnZWxldmF0aW9uVXBwZXJQZXJjZW50aWxlJ1xuICAgICAgICB9LFxuICAgICAgICBzY2FsZVR5cGU6IHtwcm9wOiAnZWxldmF0aW9uU2NhbGVUeXBlJ31cbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXREaW1lbnNpb25WYWx1ZURvbWFpblxuICAgIH0sXG4gICAge1xuICAgICAga2V5OiAnZ2V0U2NhbGVGdW5jJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIGZpeGVkOiB7cHJvcDogJ2VsZXZhdGlvbkZpeGVkJ30sXG4gICAgICAgIGRvbWFpbjoge3Byb3A6ICdlbGV2YXRpb25Eb21haW4nfSxcbiAgICAgICAgcmFuZ2U6IHtwcm9wOiAnZWxldmF0aW9uUmFuZ2UnfSxcbiAgICAgICAgc2NhbGVUeXBlOiB7cHJvcDogJ2VsZXZhdGlvblNjYWxlVHlwZSd9XG4gICAgICB9LFxuICAgICAgb25TZXQ6IHtcbiAgICAgICAgcHJvcHM6ICdvblNldEVsZXZhdGlvbkRvbWFpbidcbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXREaW1lbnNpb25TY2FsZVxuICAgIH1cbiAgXSxcbiAgZ2V0U3ViTGF5ZXJBY2Nlc3NvclxufTtcblxuZXhwb3J0IGNvbnN0IGRlZmF1bHREaW1lbnNpb25zID0gW2RlZmF1bHRDb2xvckRpbWVuc2lvbiwgZGVmYXVsdEVsZXZhdGlvbkRpbWVuc2lvbl07XG5cbmV4cG9ydCB0eXBlIENQVUFnZ3JlZ2F0b3JTdGF0ZSA9IHtcbiAgbGF5ZXJEYXRhOiB7ZGF0YT99O1xuICBkaW1lbnNpb25zOiBvYmplY3Q7XG4gIGdlb0pTT04/O1xuICBjbHVzdGVyQnVpbGRlcj87XG59O1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBDUFVBZ2dyZWdhdG9yIHtcbiAgc3RhdGljIGdldERpbWVuc2lvblNjYWxlOiBhbnk7XG4gIHN0YXRlOiBDUFVBZ2dyZWdhdG9yU3RhdGU7XG4gIGRpbWVuc2lvblVwZGF0ZXJzOiB7W2tleTogc3RyaW5nXTogRGltZW5zaW9uVHlwZX07XG4gIGFnZ3JlZ2F0aW9uVXBkYXRlcjogQWdncmVnYXRpb25UeXBlO1xuXG4gIGNvbnN0cnVjdG9yKFxuICAgIG9wdHM6IHtcbiAgICAgIGluaXRpYWxTdGF0ZT86IENQVUFnZ3JlZ2F0b3JTdGF0ZTtcbiAgICAgIGRpbWVuc2lvbnM/OiBEaW1lbnNpb25UeXBlW107XG4gICAgICBhZ2dyZWdhdGlvbj86IEFnZ3JlZ2F0aW9uVHlwZTtcbiAgICB9ID0ge31cbiAgKSB7XG4gICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgIGxheWVyRGF0YToge30sXG4gICAgICBkaW1lbnNpb25zOiB7XG4gICAgICAgIC8vIGNvbG9yOiB7XG4gICAgICAgIC8vICAgZ2V0VmFsdWU6IG51bGwsXG4gICAgICAgIC8vICAgZG9tYWluOiBudWxsLFxuICAgICAgICAvLyAgIHNvcnRlZEJpbnM6IG51bGwsXG4gICAgICAgIC8vICAgc2NhbGVGdW5jOiBub3BcbiAgICAgICAgLy8gfSxcbiAgICAgICAgLy8gZWxldmF0aW9uOiB7XG4gICAgICAgIC8vICAgZ2V0VmFsdWU6IG51bGwsXG4gICAgICAgIC8vICAgZG9tYWluOiBudWxsLFxuICAgICAgICAvLyAgIHNvcnRlZEJpbnM6IG51bGwsXG4gICAgICAgIC8vICAgc2NhbGVGdW5jOiBub3BcbiAgICAgICAgLy8gfVxuICAgICAgfSxcbiAgICAgIC4uLm9wdHMuaW5pdGlhbFN0YXRlXG4gICAgfTtcblxuICAgIHRoaXMuZGltZW5zaW9uVXBkYXRlcnMgPSB7fTtcbiAgICB0aGlzLmFnZ3JlZ2F0aW9uVXBkYXRlciA9IG9wdHMuYWdncmVnYXRpb24gfHwgZGVmYXVsdEFnZ3JlZ2F0aW9uO1xuXG4gICAgdGhpcy5fYWRkRGltZW5zaW9uKG9wdHMuZGltZW5zaW9ucyB8fCBkZWZhdWx0RGltZW5zaW9ucyk7XG4gIH1cblxuICBzdGF0aWMgZGVmYXVsdERpbWVuc2lvbnMoKSB7XG4gICAgcmV0dXJuIGRlZmF1bHREaW1lbnNpb25zO1xuICB9XG5cbiAgdXBkYXRlQWxsRGltZW5zaW9ucyhwcm9wcykge1xuICAgIGxldCBkaW1lbnNpb25DaGFuZ2VzOiBCaW5kZWRVcGRhdGVyVHlwZVtdID0gW107XG4gICAgLy8gdXBkYXRlIGFsbCBkaW1lbnNpb25zXG4gICAgZm9yIChjb25zdCBkaW0gaW4gdGhpcy5kaW1lbnNpb25VcGRhdGVycykge1xuICAgICAgY29uc3QgdXBkYXRlcnMgPSB0aGlzLl9hY2N1bXVsYXRlVXBkYXRlcnMoMCwgcHJvcHMsIHRoaXMuZGltZW5zaW9uVXBkYXRlcnNbZGltXSk7XG4gICAgICBkaW1lbnNpb25DaGFuZ2VzID0gZGltZW5zaW9uQ2hhbmdlcy5jb25jYXQodXBkYXRlcnMpO1xuICAgIH1cblxuICAgIGRpbWVuc2lvbkNoYW5nZXMuZm9yRWFjaChmID0+IHR5cGVvZiBmID09PSAnZnVuY3Rpb24nICYmIGYoKSk7XG4gIH1cblxuICB1cGRhdGVBZ2dyZWdhdGlvbihwcm9wcywgYWdncmVnYXRpb25QYXJhbXMpIHtcbiAgICBjb25zdCB1cGRhdGVycyA9IHRoaXMuX2FjY3VtdWxhdGVVcGRhdGVycygwLCBwcm9wcywgdGhpcy5hZ2dyZWdhdGlvblVwZGF0ZXIpO1xuICAgIHVwZGF0ZXJzLmZvckVhY2goZiA9PiB0eXBlb2YgZiA9PT0gJ2Z1bmN0aW9uJyAmJiBmKGFnZ3JlZ2F0aW9uUGFyYW1zKSk7XG4gIH1cblxuICB1cGRhdGVTdGF0ZShvcHRzLCBhZ2dyZWdhdGlvblBhcmFtcykge1xuICAgIGNvbnN0IHtvbGRQcm9wcywgcHJvcHMsIGNoYW5nZUZsYWdzfSA9IG9wdHM7XG4gICAgbGV0IGRpbWVuc2lvbkNoYW5nZXM6IEJpbmRlZFVwZGF0ZXJUeXBlW10gPSBbXTtcblxuICAgIGlmIChjaGFuZ2VGbGFncy5kYXRhQ2hhbmdlZCkge1xuICAgICAgLy8gaWYgZGF0YSBjaGFuZ2VkIHVwZGF0ZSBldmVyeXRoaW5nXG4gICAgICB0aGlzLnVwZGF0ZUFnZ3JlZ2F0aW9uKHByb3BzLCBhZ2dyZWdhdGlvblBhcmFtcyk7XG4gICAgICB0aGlzLnVwZGF0ZUFsbERpbWVuc2lvbnMocHJvcHMpO1xuXG4gICAgICByZXR1cm4gdGhpcy5zdGF0ZTtcbiAgICB9XG5cbiAgICBjb25zdCBhZ2dyZWdhdGlvbkNoYW5nZXMgPSB0aGlzLl9nZXRBZ2dyZWdhdGlvbkNoYW5nZXMob2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFncyk7XG5cbiAgICBpZiAoYWdncmVnYXRpb25DaGFuZ2VzICYmIGFnZ3JlZ2F0aW9uQ2hhbmdlcy5sZW5ndGgpIHtcbiAgICAgIC8vIGdldCBhZ2dyZWdhdGVkRGF0YVxuICAgICAgYWdncmVnYXRpb25DaGFuZ2VzLmZvckVhY2goZiA9PiB0eXBlb2YgZiA9PT0gJ2Z1bmN0aW9uJyAmJiBmKGFnZ3JlZ2F0aW9uUGFyYW1zKSk7XG4gICAgICB0aGlzLnVwZGF0ZUFsbERpbWVuc2lvbnMocHJvcHMpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBvbmx5IHVwZGF0ZSBkaW1lbnNpb25zXG4gICAgICBkaW1lbnNpb25DaGFuZ2VzID0gdGhpcy5fZ2V0RGltZW5zaW9uQ2hhbmdlcyhvbGRQcm9wcywgcHJvcHMsIGNoYW5nZUZsYWdzKSB8fCBbXTtcbiAgICAgIGRpbWVuc2lvbkNoYW5nZXMuZm9yRWFjaChmID0+IHR5cGVvZiBmID09PSAnZnVuY3Rpb24nICYmIGYoKSk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuc3RhdGU7XG4gIH1cblxuICAvLyBVcGRhdGUgcHJpdmF0ZSBzdGF0ZVxuICBzZXRTdGF0ZSh1cGRhdGVPYmplY3QpIHtcbiAgICB0aGlzLnN0YXRlID0gT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5zdGF0ZSwgdXBkYXRlT2JqZWN0KTtcbiAgfVxuXG4gIC8vIFVwZGF0ZSBwcml2YXRlIHN0YXRlLmRpbWVuc2lvbnNcbiAgX3NldERpbWVuc2lvblN0YXRlKGtleSwgdXBkYXRlT2JqZWN0KSB7XG4gICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICBkaW1lbnNpb25zOiBPYmplY3QuYXNzaWduKHt9LCB0aGlzLnN0YXRlLmRpbWVuc2lvbnMsIHtcbiAgICAgICAgW2tleV06IE9iamVjdC5hc3NpZ24oe30sIHRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldLCB1cGRhdGVPYmplY3QpXG4gICAgICB9KVxuICAgIH0pO1xuICB9XG5cbiAgX2FkZEFnZ3JlZ2F0aW9uKGFnZ3JlZ2F0aW9uOiBBZ2dyZWdhdGlvblR5cGUpIHtcbiAgICB0aGlzLmFnZ3JlZ2F0aW9uVXBkYXRlciA9IGFnZ3JlZ2F0aW9uO1xuICB9XG5cbiAgX2FkZERpbWVuc2lvbihkaW1lbnNpb25zOiBEaW1lbnNpb25UeXBlW10gPSBbXSkge1xuICAgIGRpbWVuc2lvbnMuZm9yRWFjaChkaW1lbnNpb24gPT4ge1xuICAgICAgY29uc3Qge2tleX0gPSBkaW1lbnNpb247XG4gICAgICB0aGlzLmRpbWVuc2lvblVwZGF0ZXJzW2tleV0gPSBkaW1lbnNpb247XG4gICAgfSk7XG4gIH1cblxuICBfbmVlZFVwZGF0ZVN0ZXAoXG4gICAgZGltZW5zaW9uU3RlcDogVXBkYXRlU3RlcHNUeXBlIHwgQWdncmVnYXRpb25VcGRhdGVTdGVwc1R5cGUsXG4gICAgb2xkUHJvcHMsXG4gICAgcHJvcHMsXG4gICAgY2hhbmdlRmxhZ3NcbiAgKSB7XG4gICAgLy8gd2hldGhlciBuZWVkIHRvIHVwZGF0ZSBjdXJyZW50IGRpbWVuc2lvbiBzdGVwXG4gICAgLy8gZGltZW5zaW9uIHN0ZXAgaXMgdGhlIHZhbHVlLCBkb21haW4sIHNjYWxlRnVuY3Rpb24gb2YgZWFjaCBkaW1lbnNpb25cbiAgICAvLyBlYWNoIHN0ZXAgaXMgYW4gb2JqZWN0IHdpdGggcHJvcGVydGllcyBsaW5rcyB0byBsYXllciBwcm9wIGFuZCB3aGV0aGVyIHRoZSBwcm9wIGlzXG4gICAgLy8gY29udHJvbGxlZCBieSB1cGRhdGVUcmlnZ2Vyc1xuICAgIHJldHVybiBPYmplY3QudmFsdWVzKGRpbWVuc2lvblN0ZXAudHJpZ2dlcnMpLnNvbWUoaXRlbSA9PiB7XG4gICAgICBpZiAoaXRlbS51cGRhdGVUcmlnZ2VyKSB7XG4gICAgICAgIC8vIGNoZWNrIGJhc2VkIG9uIHVwZGF0ZVRyaWdnZXJzIGNoYW5nZSBmaXJzdFxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgIGNoYW5nZUZsYWdzLnVwZGF0ZVRyaWdnZXJzQ2hhbmdlZCAmJlxuICAgICAgICAgIChjaGFuZ2VGbGFncy51cGRhdGVUcmlnZ2Vyc0NoYW5nZWQuYWxsIHx8XG4gICAgICAgICAgICBjaGFuZ2VGbGFncy51cGRhdGVUcmlnZ2Vyc0NoYW5nZWRbaXRlbS51cGRhdGVUcmlnZ2VyXSlcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICAgIC8vIGZhbGxiYWNrIHRvIGRpcmVjdCBjb21wYXJpc29uXG4gICAgICByZXR1cm4gb2xkUHJvcHNbaXRlbS5wcm9wXSAhPT0gcHJvcHNbaXRlbS5wcm9wXTtcbiAgICB9KTtcbiAgfVxuXG4gIF9hY2N1bXVsYXRlVXBkYXRlcnM8VXBkYXRlck9iamVjdFR5cGUgZXh0ZW5kcyBEaW1lbnNpb25UeXBlIHwgQWdncmVnYXRpb25UeXBlPihcbiAgICBzdGVwLFxuICAgIHByb3BzLFxuICAgIGRpbWVuc2lvbjogVXBkYXRlck9iamVjdFR5cGVcbiAgKSB7XG4gICAgdHlwZSBMb2NhbFVwZGF0ZXJUeXBlID0gVXBkYXRlck9iamVjdFR5cGUgZXh0ZW5kcyBEaW1lbnNpb25UeXBlXG4gICAgICA/IEJpbmRlZFVwZGF0ZXJUeXBlXG4gICAgICA6IEJpbmRlZEFnZ3JlZ2F0ZWRVcGRhdGVyVHlwZTtcbiAgICBjb25zdCB1cGRhdGVyczogTG9jYWxVcGRhdGVyVHlwZVtdID0gW107XG4gICAgZm9yIChsZXQgaSA9IHN0ZXA7IGkgPCBkaW1lbnNpb24udXBkYXRlU3RlcHMubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IHVwZGF0ZXIgPSBkaW1lbnNpb24udXBkYXRlU3RlcHNbaV0udXBkYXRlcjtcbiAgICAgIGlmICh0eXBlb2YgdXBkYXRlciA9PT0gJ2Z1bmN0aW9uJykge1xuICAgICAgICB1cGRhdGVycy5wdXNoKFxuICAgICAgICAgIHVwZGF0ZXIuYmluZCh0aGlzLCBkaW1lbnNpb24udXBkYXRlU3RlcHNbaV0sIHByb3BzLCBkaW1lbnNpb24pIGFzIExvY2FsVXBkYXRlclR5cGVcbiAgICAgICAgKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gdXBkYXRlcnM7XG4gIH1cblxuICBfZ2V0QWxsVXBkYXRlcnM8VXBkYXRlck9iamVjdFR5cGUgZXh0ZW5kcyBEaW1lbnNpb25UeXBlIHwgQWdncmVnYXRpb25UeXBlPihcbiAgICBkaW1lbnNpb246IFVwZGF0ZXJPYmplY3RUeXBlLFxuICAgIG9sZFByb3BzLFxuICAgIHByb3BzLFxuICAgIGNoYW5nZUZsYWdzXG4gICkge1xuICAgIHR5cGUgTG9jYWxVcGRhdGVyVHlwZSA9IFVwZGF0ZXJPYmplY3RUeXBlIGV4dGVuZHMgRGltZW5zaW9uVHlwZVxuICAgICAgPyBCaW5kZWRVcGRhdGVyVHlwZVxuICAgICAgOiBCaW5kZWRBZ2dyZWdhdGVkVXBkYXRlclR5cGU7XG4gICAgbGV0IHVwZGF0ZXJzOiBMb2NhbFVwZGF0ZXJUeXBlW10gPSBbXTtcbiAgICBjb25zdCBuZWVkVXBkYXRlU3RlcCA9IGRpbWVuc2lvbi51cGRhdGVTdGVwcy5maW5kSW5kZXgoc3RlcCA9PlxuICAgICAgdGhpcy5fbmVlZFVwZGF0ZVN0ZXAoc3RlcCwgb2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFncylcbiAgICApO1xuXG4gICAgaWYgKG5lZWRVcGRhdGVTdGVwID4gLTEpIHtcbiAgICAgIHVwZGF0ZXJzID0gdXBkYXRlcnMuY29uY2F0KHRoaXMuX2FjY3VtdWxhdGVVcGRhdGVycyhuZWVkVXBkYXRlU3RlcCwgcHJvcHMsIGRpbWVuc2lvbikpO1xuICAgIH1cblxuICAgIHJldHVybiB1cGRhdGVycztcbiAgfVxuXG4gIF9nZXRBZ2dyZWdhdGlvbkNoYW5nZXMob2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFncykge1xuICAgIGNvbnN0IHVwZGF0ZXJzID0gdGhpcy5fZ2V0QWxsVXBkYXRlcnModGhpcy5hZ2dyZWdhdGlvblVwZGF0ZXIsIG9sZFByb3BzLCBwcm9wcywgY2hhbmdlRmxhZ3MpO1xuICAgIHJldHVybiB1cGRhdGVycy5sZW5ndGggPyB1cGRhdGVycyA6IG51bGw7XG4gIH1cblxuICBfZ2V0RGltZW5zaW9uQ2hhbmdlcyhvbGRQcm9wcywgcHJvcHMsIGNoYW5nZUZsYWdzKSB7XG4gICAgbGV0IHVwZGF0ZXJzOiBCaW5kZWRVcGRhdGVyVHlwZVtdID0gW107XG5cbiAgICAvLyBnZXQgZGltZW5zaW9uIHRvIGJlIHVwZGF0ZWRcbiAgICBmb3IgKGNvbnN0IGtleSBpbiB0aGlzLmRpbWVuc2lvblVwZGF0ZXJzKSB7XG4gICAgICAvLyByZXR1cm4gdGhlIGZpcnN0IHRyaWdnZXJlZCB1cGRhdGVyIGZvciBlYWNoIGRpbWVuc2lvblxuICAgICAgY29uc3QgZGltZW5zaW9uID0gdGhpcy5kaW1lbnNpb25VcGRhdGVyc1trZXldO1xuICAgICAgY29uc3QgZGltZW5zaW9uVXBkYXRlcnMgPSB0aGlzLl9nZXRBbGxVcGRhdGVycyhkaW1lbnNpb24sIG9sZFByb3BzLCBwcm9wcywgY2hhbmdlRmxhZ3MpO1xuICAgICAgdXBkYXRlcnMgPSB1cGRhdGVycy5jb25jYXQoZGltZW5zaW9uVXBkYXRlcnMpO1xuICAgIH1cblxuICAgIHJldHVybiB1cGRhdGVycy5sZW5ndGggPyB1cGRhdGVycyA6IG51bGw7XG4gIH1cblxuICBnZXRVcGRhdGVUcmlnZ2Vycyhwcm9wcykge1xuICAgIGNvbnN0IF91cGRhdGVUcmlnZ2VycyA9IHByb3BzLnVwZGF0ZVRyaWdnZXJzIHx8IHt9O1xuICAgIGNvbnN0IHVwZGF0ZVRyaWdnZXJzID0ge307XG5cbiAgICBmb3IgKGNvbnN0IGtleSBpbiB0aGlzLmRpbWVuc2lvblVwZGF0ZXJzKSB7XG4gICAgICBjb25zdCB7YWNjZXNzb3IsIHVwZGF0ZVN0ZXBzfToge2FjY2Vzc29yOyB1cGRhdGVTdGVwczogVXBkYXRlU3RlcHNUeXBlW119ID1cbiAgICAgICAgdGhpcy5kaW1lbnNpb25VcGRhdGVyc1trZXldO1xuICAgICAgLy8gZm9sZCBkaW1lbnNpb24gdHJpZ2dlcnMgaW50byBlYWNoIGFjY2Vzc29yXG4gICAgICB1cGRhdGVUcmlnZ2Vyc1thY2Nlc3Nvcl0gPSB7fTtcblxuICAgICAgdXBkYXRlU3RlcHMuZm9yRWFjaChzdGVwID0+IHtcbiAgICAgICAgT2JqZWN0LnZhbHVlcyhzdGVwLnRyaWdnZXJzIHx8IFtdKS5mb3JFYWNoKCh7cHJvcCwgdXBkYXRlVHJpZ2dlcn0pID0+IHtcbiAgICAgICAgICBpZiAodXBkYXRlVHJpZ2dlcikge1xuICAgICAgICAgICAgLy8gaWYgcHJvcCBpcyBiYXNlZCBvbiB1cGRhdGVUcmlnZ2VyIGUuZy4gZ2V0Q29sb3JWYWx1ZSwgZ2V0Q29sb3JXZWlnaHRcbiAgICAgICAgICAgIC8vIGFuZCB1cGRhdGVUcmlnZ2VycyBpcyBwYXNzZWQgaW4gZnJvbSBsYXllciBwcm9wXG4gICAgICAgICAgICAvLyBmb2xkIHRoZSB1cGRhdGVUcmlnZ2VycyBpbnRvIGFjY2Vzc29yXG4gICAgICAgICAgICBjb25zdCBmcm9tUHJvcCA9IF91cGRhdGVUcmlnZ2Vyc1t1cGRhdGVUcmlnZ2VyXTtcbiAgICAgICAgICAgIGlmICh0eXBlb2YgZnJvbVByb3AgPT09ICdvYmplY3QnICYmICFBcnJheS5pc0FycmF5KGZyb21Qcm9wKSkge1xuICAgICAgICAgICAgICAvLyBpZiB1cGRhdGVUcmlnZ2VyIGlzIGFuIG9iamVjdCBzcHJlYWQgaXRcbiAgICAgICAgICAgICAgT2JqZWN0LmFzc2lnbih1cGRhdGVUcmlnZ2Vyc1thY2Nlc3Nvcl0sIGZyb21Qcm9wKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAoZnJvbVByb3AgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICB1cGRhdGVUcmlnZ2Vyc1thY2Nlc3Nvcl1bcHJvcF0gPSBmcm9tUHJvcDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gaWYgcHJvcCBpcyBub3QgYmFzZWQgb24gdXBkYXRlVHJpZ2dlclxuICAgICAgICAgICAgdXBkYXRlVHJpZ2dlcnNbYWNjZXNzb3JdW3Byb3BdID0gcHJvcHNbcHJvcF07XG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiB1cGRhdGVUcmlnZ2VycztcbiAgfVxuXG4gIGdldFBpY2tpbmdJbmZvKHtpbmZvfSwgbGF5ZXJQcm9wcykge1xuICAgIGNvbnN0IGlzUGlja2VkID0gaW5mby5waWNrZWQgJiYgaW5mby5pbmRleCA+IC0xO1xuICAgIGxldCBvYmp