kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
592 lines (585 loc) • 75.5 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);
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/IGRpbWVuc2lvbkRvbWFpbiA6IGRpbWVuc2lvblJhbmdlKTtcblxuICBpZiAodHlwZW9mIG9uU2V0ID09PSAnb2JqZWN0JyAmJiB0eXBlb2YgcHJvcHNbb25TZXQucHJvcHNdID09PSAnZnVuY3Rpb24nKSB7XG4gICAgY29uc3Qgc29ydGVkQmlucyA9IHRoaXMuc3RhdGUuZGltZW5zaW9uc1trZXldLnNvcnRlZEJpbnM7XG4gICAgcHJvcHNbb25TZXQucHJvcHNdKHtkb21haW46IHNjYWxlRnVuYy5kb21haW4oKSwgYWdncmVnYXRlZEJpbnM6IHNvcnRlZEJpbnMuYmluTWFwfSk7XG4gIH1cbiAgdGhpcy5fc2V0RGltZW5zaW9uU3RhdGUoa2V5LCB7c2NhbGVGdW5jfSk7XG59XG5cbmZ1bmN0aW9uIG5vcm1hbGl6ZVJlc3VsdChyZXN1bHQ6IHtoZXhhZ29ucz87IGxheWVyRGF0YT99ID0ge30pIHtcbiAgLy8gc3VwcG9ydCBwcmV2aW91cyBoZXhhZ29uQWdncmVnYXRvciBBUElcbiAgaWYgKHJlc3VsdC5oZXhhZ29ucykge1xuICAgIHJldHVybiBPYmplY3QuYXNzaWduKHtkYXRhOiByZXN1bHQuaGV4YWdvbnN9LCByZXN1bHQpO1xuICB9IGVsc2UgaWYgKHJlc3VsdC5sYXllckRhdGEpIHtcbiAgICByZXR1cm4gT2JqZWN0LmFzc2lnbih7ZGF0YTogcmVzdWx0LmxheWVyRGF0YX0sIHJlc3VsdCk7XG4gIH1cblxuICByZXR1cm4gcmVzdWx0O1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZ2V0QWdncmVnYXRlZERhdGEoXG4gIHRoaXM6IENQVUFnZ3JlZ2F0b3IsXG4gIHN0ZXAsXG4gIHByb3BzLFxuICBhZ2dyZWdhdGlvbixcbiAgYWdncmVnYXRpb25QYXJhbXNcbikge1xuICBjb25zdCB7XG4gICAgdHJpZ2dlcnM6IHthZ2dyZWdhdG9yOiBhZ2dyfVxuICB9ID0gc3RlcDtcbiAgY29uc3QgYWdncmVnYXRvciA9IHByb3BzW2FnZ3IucHJvcF07XG5cbiAgLy8gcmVzdWx0IHNob3VsZCBjb250YWluIGEgZGF0YSBhcnJheSBhbmQgb3RoZXIgcHJvcHNcbiAgLy8gcmVzdWx0ID0ge2RhdGE6IFtdLCAuLi5vdGhlciBwcm9wc31cbiAgY29uc3QgcmVzdWx0ID0gYWdncmVnYXRvcihwcm9wcywgYWdncmVnYXRpb25QYXJhbXMpO1xuICB0aGlzLnNldFN0YXRlKHtcbiAgICBsYXllckRhdGE6IG5vcm1hbGl6ZVJlc3VsdChyZXN1bHQpXG4gIH0pO1xufVxuXG5leHBvcnQgY29uc3QgZGVmYXVsdEFnZ3JlZ2F0aW9uOiBBZ2dyZWdhdGlvblR5cGUgPSB7XG4gIGtleTogJ3Bvc2l0aW9uJyxcbiAgdXBkYXRlU3RlcHM6IFtcbiAgICB7XG4gICAgICBrZXk6ICdhZ2dyZWdhdGUnLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgY2VsbFNpemU6IHtcbiAgICAgICAgICBwcm9wOiAnY2VsbFNpemUnXG4gICAgICAgIH0sXG4gICAgICAgIHBvc2l0aW9uOiB7XG4gICAgICAgICAgcHJvcDogJ2dldFBvc2l0aW9uJyxcbiAgICAgICAgICB1cGRhdGVUcmlnZ2VyOiAnZ2V0UG9zaXRpb24nXG4gICAgICAgIH0sXG4gICAgICAgIGFnZ3JlZ2F0b3I6IHtcbiAgICAgICAgICBwcm9wOiAnZ3JpZEFnZ3JlZ2F0b3InXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXRBZ2dyZWdhdGVkRGF0YVxuICAgIH1cbiAgXVxufTtcblxuZnVuY3Rpb24gZ2V0U3ViTGF5ZXJBY2Nlc3NvcihkaW1lbnNpb25TdGF0ZSwgZGltZW5zaW9uKSB7XG4gIHJldHVybiBjZWxsID0+IHtcbiAgICBjb25zdCB7c29ydGVkQmlucywgc2NhbGVGdW5jfSA9IGRpbWVuc2lvblN0YXRlO1xuICAgIGNvbnN0IGJpbiA9IHNvcnRlZEJpbnMuYmluTWFwW2NlbGwuaW5kZXhdO1xuXG4gICAgaWYgKGJpbiAmJiBiaW4uY291bnRzID09PSAwKSB7XG4gICAgICAvLyBubyBwb2ludHMgbGVmdCBpbiBiaW4gYWZ0ZXIgZmlsdGVyaW5nXG4gICAgICByZXR1cm4gZGltZW5zaW9uLm51bGxWYWx1ZTtcbiAgICB9XG5cbiAgICBjb25zdCBjdiA9IGJpbiAmJiBiaW4udmFsdWU7XG4gICAgY29uc3QgZG9tYWluID0gc2NhbGVGdW5jLmRvbWFpbigpO1xuXG4gICAgY29uc3QgaXNWYWx1ZUluRG9tYWluID1cbiAgICAgIHNjYWxlRnVuYy5zY2FsZVR5cGUgPT09ICdjdXN0b20nXG4gICAgICAgID8gY3YgPj0gc29ydGVkQmlucy5taW5WYWx1ZSAmJiBjdiA8PSBzb3J0ZWRCaW5zLm1heFZhbHVlXG4gICAgICAgIDogY3YgPj0gZG9tYWluWzBdICYmIGN2IDw9IGRvbWFpbltkb21haW4ubGVuZ3RoIC0gMV07XG5cbiAgICAvLyBpZiBjZWxsIHZhbHVlIGlzIG91dHNpZGUgZG9tYWluLCBzZXQgYWxwaGEgdG8gMFxuICAgIHJldHVybiBpc1ZhbHVlSW5Eb21haW4gPyBzY2FsZUZ1bmMoY3YpIDogZGltZW5zaW9uLm51bGxWYWx1ZTtcbiAgfTtcbn1cblxuZXhwb3J0IGNvbnN0IGRlZmF1bHRDb2xvckRpbWVuc2lvbjogRGltZW5zaW9uVHlwZTxSR0JBQ29sb3I+ID0ge1xuICBrZXk6ICdmaWxsQ29sb3InLFxuICBhY2Nlc3NvcjogJ2dldEZpbGxDb2xvcicsXG4gIGdldFBpY2tpbmdJbmZvOiAoZGltZW5zaW9uU3RhdGUsIGNlbGwpID0+IHtcbiAgICBpZiAoIWNlbGwpIHtcbiAgICAgIHJldHVybiB7fTtcbiAgICB9XG4gICAgY29uc3Qge3NvcnRlZEJpbnN9ID0gZGltZW5zaW9uU3RhdGU7XG4gICAgY29uc3QgY29sb3JWYWx1ZSA9IHNvcnRlZEJpbnMuYmluTWFwW2NlbGwuaW5kZXhdICYmIHNvcnRlZEJpbnMuYmluTWFwW2NlbGwuaW5kZXhdLnZhbHVlO1xuICAgIHJldHVybiB7Y29sb3JWYWx1ZX07XG4gIH0sXG4gIG51bGxWYWx1ZTogWzAsIDAsIDAsIDBdLFxuICB1cGRhdGVTdGVwczogW1xuICAgIHtcbiAgICAgIGtleTogJ2dldFZhbHVlJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIHZhbHVlOiB7XG4gICAgICAgICAgcHJvcDogJ2dldENvbG9yVmFsdWUnLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdnZXRDb2xvclZhbHVlJ1xuICAgICAgICB9LFxuICAgICAgICB3ZWlnaHQ6IHtcbiAgICAgICAgICBwcm9wOiAnZ2V0Q29sb3JXZWlnaHQnLFxuICAgICAgICAgIHVwZGF0ZVRyaWdnZXI6ICdnZXRDb2xvcldlaWdodCdcbiAgICAgICAgfSxcbiAgICAgICAgYWdncmVnYXRpb246IHtcbiAgICAgICAgICBwcm9wOiAnY29sb3JBZ2dyZWdhdGlvbidcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldEdldFZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICdnZXRCaW5zJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIF9maWx0ZXJEYXRhOiB7XG4gICAgICAgICAgcHJvcDogJ19maWx0ZXJEYXRhJyxcbiAgICAgICAgICB1cGRhdGVUcmlnZ2VyOiAnX2ZpbHRlckRhdGEnXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXREaW1lbnNpb25Tb3J0ZWRCaW5zXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICdnZXREb21haW4nLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgbG93ZXJQZXJjZW50aWxlOiB7XG4gICAgICAgICAgcHJvcDogJ2xvd2VyUGVyY2VudGlsZSdcbiAgICAgICAgfSxcbiAgICAgICAgdXBwZXJQZXJjZW50aWxlOiB7XG4gICAgICAgICAgcHJvcDogJ3VwcGVyUGVyY2VudGlsZSdcbiAgICAgICAgfSxcbiAgICAgICAgc2NhbGVUeXBlOiB7cHJvcDogJ2NvbG9yU2NhbGVUeXBlJ31cbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXREaW1lbnNpb25WYWx1ZURvbWFpblxuICAgIH0sXG4gICAge1xuICAgICAga2V5OiAnZ2V0U2NhbGVGdW5jJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIGRvbWFpbjoge3Byb3A6ICdjb2xvckRvbWFpbid9LFxuICAgICAgICByYW5nZToge3Byb3A6ICdjb2xvclJhbmdlJ30sXG4gICAgICAgIHNjYWxlVHlwZToge3Byb3A6ICdjb2xvclNjYWxlVHlwZSd9XG4gICAgICB9LFxuICAgICAgb25TZXQ6IHtcbiAgICAgICAgcHJvcHM6ICdvblNldENvbG9yRG9tYWluJ1xuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldERpbWVuc2lvblNjYWxlXG4gICAgfVxuICBdLFxuICBnZXRTdWJMYXllckFjY2Vzc29yXG59O1xuXG5leHBvcnQgY29uc3QgZGVmYXVsdEVsZXZhdGlvbkRpbWVuc2lvbjogRGltZW5zaW9uVHlwZTxudW1iZXI+ID0ge1xuICBrZXk6ICdlbGV2YXRpb24nLFxuICBhY2Nlc3NvcjogJ2dldEVsZXZhdGlvbicsXG4gIGdldFBpY2tpbmdJbmZvOiAoZGltZW5zaW9uU3RhdGUsIGNlbGwpID0+IHtcbiAgICBpZiAoIWNlbGwpIHtcbiAgICAgIHJldHVybiB7fTtcbiAgICB9XG4gICAgY29uc3Qge3NvcnRlZEJpbnN9ID0gZGltZW5zaW9uU3RhdGU7XG4gICAgY29uc3QgZWxldmF0aW9uVmFsdWUgPSBzb3J0ZWRCaW5zLmJpbk1hcFtjZWxsLmluZGV4XSAmJiBzb3J0ZWRCaW5zLmJpbk1hcFtjZWxsLmluZGV4XS52YWx1ZTtcbiAgICByZXR1cm4ge2VsZXZhdGlvblZhbHVlfTtcbiAgfSxcbiAgbnVsbFZhbHVlOiAtMSxcbiAgdXBkYXRlU3RlcHM6IFtcbiAgICB7XG4gICAgICBrZXk6ICdnZXRWYWx1ZScsXG4gICAgICB0cmlnZ2Vyczoge1xuICAgICAgICB2YWx1ZToge1xuICAgICAgICAgIHByb3A6ICdnZXRFbGV2YXRpb25WYWx1ZScsXG4gICAgICAgICAgdXBkYXRlVHJpZ2dlcjogJ2dldEVsZXZhdGlvblZhbHVlJ1xuICAgICAgICB9LFxuICAgICAgICB3ZWlnaHQ6IHtcbiAgICAgICAgICBwcm9wOiAnZ2V0RWxldmF0aW9uV2VpZ2h0JyxcbiAgICAgICAgICB1cGRhdGVUcmlnZ2VyOiAnZ2V0RWxldmF0aW9uV2VpZ2h0J1xuICAgICAgICB9LFxuICAgICAgICBhZ2dyZWdhdGlvbjoge1xuICAgICAgICAgIHByb3A6ICdlbGV2YXRpb25BZ2dyZWdhdGlvbidcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHVwZGF0ZXI6IGdldEdldFZhbHVlXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICdnZXRCaW5zJyxcbiAgICAgIHRyaWdnZXJzOiB7XG4gICAgICAgIF9maWx0ZXJEYXRhOiB7XG4gICAgICAgICAgcHJvcDogJ19maWx0ZXJEYXRhJyxcbiAgICAgICAgICB1cGRhdGVUcmlnZ2VyOiAnX2ZpbHRlckRhdGEnXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICB1cGRhdGVyOiBnZXREaW1lbnNpb25Tb3J0ZWRCaW5zXG4gICAgfSxcbiAgICB7XG4gICAgICBrZXk6ICdnZXREb21haW4nLFxuICAgICAgdHJpZ2dlcnM6IHtcbiAgICAgICAgbG93ZXJQZXJjZW50aWxlOiB7XG4gICAgICAgICAgcHJvcDogJ2VsZXZhdGlvbkxvd2VyUGVyY2VudGlsZSdcbiAgICAgICAgfSxcbiAgICAgICAgdXBwZXJQZXJjZW50aWxlOiB7XG4gICAgICAgICAgcHJvcDogJ2VsZXZhdGlvblVwcGVyUGVyY2VudGlsZSdcbiAgICAgICAgfSxcbiAgICAgICAgc2NhbGVUeXBlOiB7cHJvcDogJ2VsZXZhdGlvblNjYWxlVHlwZSd9XG4gICAgICB9LFxuICAgICAgdXBkYXRlcjogZ2V0RGltZW5zaW9uVmFsdWVEb21haW5cbiAgICB9LFxuICAgIHtcbiAgICAgIGtleTogJ2dldFNjYWxlRnVuYycsXG4gICAgICB0cmlnZ2Vyczoge1xuICAgICAgICBmaXhlZDoge3Byb3A6ICdlbGV2YXRpb25GaXhlZCd9LFxuICAgICAgICBkb21haW46IHtwcm9wOiAnZWxldmF0aW9uRG9tYWluJ30sXG4gICAgICAgIHJhbmdlOiB7cHJvcDogJ2VsZXZhdGlvblJhbmdlJ30sXG4gICAgICAgIHNjYWxlVHlwZToge3Byb3A6ICdlbGV2YXRpb25TY2FsZVR5cGUnfVxuICAgICAgfSxcbiAgICAgIG9uU2V0OiB7XG4gICAgICAgIHByb3BzOiAnb25TZXRFbGV2YXRpb25Eb21haW4nXG4gICAgICB9LFxuICAgICAgdXBkYXRlcjogZ2V0RGltZW5zaW9uU2NhbGVcbiAgICB9XG4gIF0sXG4gIGdldFN1YkxheWVyQWNjZXNzb3Jcbn07XG5cbmV4cG9ydCBjb25zdCBkZWZhdWx0RGltZW5zaW9ucyA9IFtkZWZhdWx0Q29sb3JEaW1lbnNpb24sIGRlZmF1bHRFbGV2YXRpb25EaW1lbnNpb25dO1xuXG5leHBvcnQgdHlwZSBDUFVBZ2dyZWdhdG9yU3RhdGUgPSB7XG4gIGxheWVyRGF0YToge2RhdGE/fTtcbiAgZGltZW5zaW9uczogb2JqZWN0O1xuICBnZW9KU09OPztcbiAgY2x1c3RlckJ1aWxkZXI/O1xufTtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ1BVQWdncmVnYXRvciB7XG4gIHN0YXRpYyBnZXREaW1lbnNpb25TY2FsZTogYW55O1xuICBzdGF0ZTogQ1BVQWdncmVnYXRvclN0YXRlO1xuICBkaW1lbnNpb25VcGRhdGVyczoge1trZXk6IHN0cmluZ106IERpbWVuc2lvblR5cGV9O1xuICBhZ2dyZWdhdGlvblVwZGF0ZXI6IEFnZ3JlZ2F0aW9uVHlwZTtcblxuICBjb25zdHJ1Y3RvcihcbiAgICBvcHRzOiB7XG4gICAgICBpbml0aWFsU3RhdGU/OiBDUFVBZ2dyZWdhdG9yU3RhdGU7XG4gICAgICBkaW1lbnNpb25zPzogRGltZW5zaW9uVHlwZVtdO1xuICAgICAgYWdncmVnYXRpb24/OiBBZ2dyZWdhdGlvblR5cGU7XG4gICAgfSA9IHt9XG4gICkge1xuICAgIHRoaXMuc3RhdGUgPSB7XG4gICAgICBsYXllckRhdGE6IHt9LFxuICAgICAgZGltZW5zaW9uczoge1xuICAgICAgICAvLyBjb2xvcjoge1xuICAgICAgICAvLyAgIGdldFZhbHVlOiBudWxsLFxuICAgICAgICAvLyAgIGRvbWFpbjogbnVsbCxcbiAgICAgICAgLy8gICBzb3J0ZWRCaW5zOiBudWxsLFxuICAgICAgICAvLyAgIHNjYWxlRnVuYzogbm9wXG4gICAgICAgIC8vIH0sXG4gICAgICAgIC8vIGVsZXZhdGlvbjoge1xuICAgICAgICAvLyAgIGdldFZhbHVlOiBudWxsLFxuICAgICAgICAvLyAgIGRvbWFpbjogbnVsbCxcbiAgICAgICAgLy8gICBzb3J0ZWRCaW5zOiBudWxsLFxuICAgICAgICAvLyAgIHNjYWxlRnVuYzogbm9wXG4gICAgICAgIC8vIH1cbiAgICAgIH0sXG4gICAgICAuLi5vcHRzLmluaXRpYWxTdGF0ZVxuICAgIH07XG5cbiAgICB0aGlzLmRpbWVuc2lvblVwZGF0ZXJzID0ge307XG4gICAgdGhpcy5hZ2dyZWdhdGlvblVwZGF0ZXIgPSBvcHRzLmFnZ3JlZ2F0aW9uIHx8IGRlZmF1bHRBZ2dyZWdhdGlvbjtcblxuICAgIHRoaXMuX2FkZERpbWVuc2lvbihvcHRzLmRpbWVuc2lvbnMgfHwgZGVmYXVsdERpbWVuc2lvbnMpO1xuICB9XG5cbiAgc3RhdGljIGRlZmF1bHREaW1lbnNpb25zKCkge1xuICAgIHJldHVybiBkZWZhdWx0RGltZW5zaW9ucztcbiAgfVxuXG4gIHVwZGF0ZUFsbERpbWVuc2lvbnMocHJvcHMpIHtcbiAgICBsZXQgZGltZW5zaW9uQ2hhbmdlczogQmluZGVkVXBkYXRlclR5cGVbXSA9IFtdO1xuICAgIC8vIHVwZGF0ZSBhbGwgZGltZW5zaW9uc1xuICAgIGZvciAoY29uc3QgZGltIGluIHRoaXMuZGltZW5zaW9uVXBkYXRlcnMpIHtcbiAgICAgIGNvbnN0IHVwZGF0ZXJzID0gdGhpcy5fYWNjdW11bGF0ZVVwZGF0ZXJzKDAsIHByb3BzLCB0aGlzLmRpbWVuc2lvblVwZGF0ZXJzW2RpbV0pO1xuICAgICAgZGltZW5zaW9uQ2hhbmdlcyA9IGRpbWVuc2lvbkNoYW5nZXMuY29uY2F0KHVwZGF0ZXJzKTtcbiAgICB9XG5cbiAgICBkaW1lbnNpb25DaGFuZ2VzLmZvckVhY2goZiA9PiB0eXBlb2YgZiA9PT0gJ2Z1bmN0aW9uJyAmJiBmKCkpO1xuICB9XG5cbiAgdXBkYXRlQWdncmVnYXRpb24ocHJvcHMsIGFnZ3JlZ2F0aW9uUGFyYW1zKSB7XG4gICAgY29uc3QgdXBkYXRlcnMgPSB0aGlzLl9hY2N1bXVsYXRlVXBkYXRlcnMoMCwgcHJvcHMsIHRoaXMuYWdncmVnYXRpb25VcGRhdGVyKTtcbiAgICB1cGRhdGVycy5mb3JFYWNoKGYgPT4gdHlwZW9mIGYgPT09ICdmdW5jdGlvbicgJiYgZihhZ2dyZWdhdGlvblBhcmFtcykpO1xuICB9XG5cbiAgdXBkYXRlU3RhdGUob3B0cywgYWdncmVnYXRpb25QYXJhbXMpIHtcbiAgICBjb25zdCB7b2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFnc30gPSBvcHRzO1xuICAgIGxldCBkaW1lbnNpb25DaGFuZ2VzOiBCaW5kZWRVcGRhdGVyVHlwZVtdID0gW107XG5cbiAgICBpZiAoY2hhbmdlRmxhZ3MuZGF0YUNoYW5nZWQpIHtcbiAgICAgIC8vIGlmIGRhdGEgY2hhbmdlZCB1cGRhdGUgZXZlcnl0aGluZ1xuICAgICAgdGhpcy51cGRhdGVBZ2dyZWdhdGlvbihwcm9wcywgYWdncmVnYXRpb25QYXJhbXMpO1xuICAgICAgdGhpcy51cGRhdGVBbGxEaW1lbnNpb25zKHByb3BzKTtcblxuICAgICAgcmV0dXJuIHRoaXMuc3RhdGU7XG4gICAgfVxuXG4gICAgY29uc3QgYWdncmVnYXRpb25DaGFuZ2VzID0gdGhpcy5fZ2V0QWdncmVnYXRpb25DaGFuZ2VzKG9sZFByb3BzLCBwcm9wcywgY2hhbmdlRmxhZ3MpO1xuXG4gICAgaWYgKGFnZ3JlZ2F0aW9uQ2hhbmdlcyAmJiBhZ2dyZWdhdGlvbkNoYW5nZXMubGVuZ3RoKSB7XG4gICAgICAvLyBnZXQgYWdncmVnYXRlZERhdGFcbiAgICAgIGFnZ3JlZ2F0aW9uQ2hhbmdlcy5mb3JFYWNoKGYgPT4gdHlwZW9mIGYgPT09ICdmdW5jdGlvbicgJiYgZihhZ2dyZWdhdGlvblBhcmFtcykpO1xuICAgICAgdGhpcy51cGRhdGVBbGxEaW1lbnNpb25zKHByb3BzKTtcbiAgICB9IGVsc2Uge1xuICAgICAgLy8gb25seSB1cGRhdGUgZGltZW5zaW9uc1xuICAgICAgZGltZW5zaW9uQ2hhbmdlcyA9IHRoaXMuX2dldERpbWVuc2lvbkNoYW5nZXMob2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFncykgfHwgW107XG4gICAgICBkaW1lbnNpb25DaGFuZ2VzLmZvckVhY2goZiA9PiB0eXBlb2YgZiA9PT0gJ2Z1bmN0aW9uJyAmJiBmKCkpO1xuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnN0YXRlO1xuICB9XG5cbiAgLy8gVXBkYXRlIHByaXZhdGUgc3RhdGVcbiAgc2V0U3RhdGUodXBkYXRlT2JqZWN0KSB7XG4gICAgdGhpcy5zdGF0ZSA9IE9iamVjdC5hc3NpZ24oe30sIHRoaXMuc3RhdGUsIHVwZGF0ZU9iamVjdCk7XG4gIH1cblxuICAvLyBVcGRhdGUgcHJpdmF0ZSBzdGF0ZS5kaW1lbnNpb25zXG4gIF9zZXREaW1lbnNpb25TdGF0ZShrZXksIHVwZGF0ZU9iamVjdCkge1xuICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgZGltZW5zaW9uczogT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5zdGF0ZS5kaW1lbnNpb25zLCB7XG4gICAgICAgIFtrZXldOiBPYmplY3QuYXNzaWduKHt9LCB0aGlzLnN0YXRlLmRpbWVuc2lvbnNba2V5XSwgdXBkYXRlT2JqZWN0KVxuICAgICAgfSlcbiAgICB9KTtcbiAgfVxuXG4gIF9hZGRBZ2dyZWdhdGlvbihhZ2dyZWdhdGlvbjogQWdncmVnYXRpb25UeXBlKSB7XG4gICAgdGhpcy5hZ2dyZWdhdGlvblVwZGF0ZXIgPSBhZ2dyZWdhdGlvbjtcbiAgfVxuXG4gIF9hZGREaW1lbnNpb24oZGltZW5zaW9uczogRGltZW5zaW9uVHlwZVtdID0gW10pIHtcbiAgICBkaW1lbnNpb25zLmZvckVhY2goZGltZW5zaW9uID0+IHtcbiAgICAgIGNvbnN0IHtrZXl9ID0gZGltZW5zaW9uO1xuICAgICAgdGhpcy5kaW1lbnNpb25VcGRhdGVyc1trZXldID0gZGltZW5zaW9uO1xuICAgIH0pO1xuICB9XG5cbiAgX25lZWRVcGRhdGVTdGVwKFxuICAgIGRpbWVuc2lvblN0ZXA6IFVwZGF0ZVN0ZXBzVHlwZSB8IEFnZ3JlZ2F0aW9uVXBkYXRlU3RlcHNUeXBlLFxuICAgIG9sZFByb3BzLFxuICAgIHByb3BzLFxuICAgIGNoYW5nZUZsYWdzXG4gICkge1xuICAgIC8vIHdoZXRoZXIgbmVlZCB0byB1cGRhdGUgY3VycmVudCBkaW1lbnNpb24gc3RlcFxuICAgIC8vIGRpbWVuc2lvbiBzdGVwIGlzIHRoZSB2YWx1ZSwgZG9tYWluLCBzY2FsZUZ1bmN0aW9uIG9mIGVhY2ggZGltZW5zaW9uXG4gICAgLy8gZWFjaCBzdGVwIGlzIGFuIG9iamVjdCB3aXRoIHByb3BlcnRpZXMgbGlua3MgdG8gbGF5ZXIgcHJvcCBhbmQgd2hldGhlciB0aGUgcHJvcCBpc1xuICAgIC8vIGNvbnRyb2xsZWQgYnkgdXBkYXRlVHJpZ2dlcnNcbiAgICByZXR1cm4gT2JqZWN0LnZhbHVlcyhkaW1lbnNpb25TdGVwLnRyaWdnZXJzKS5zb21lKGl0ZW0gPT4ge1xuICAgICAgaWYgKGl0ZW0udXBkYXRlVHJpZ2dlcikge1xuICAgICAgICAvLyBjaGVjayBiYXNlZCBvbiB1cGRhdGVUcmlnZ2VycyBjaGFuZ2UgZmlyc3RcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICBjaGFuZ2VGbGFncy51cGRhdGVUcmlnZ2Vyc0NoYW5nZWQgJiZcbiAgICAgICAgICAoY2hhbmdlRmxhZ3MudXBkYXRlVHJpZ2dlcnNDaGFuZ2VkLmFsbCB8fFxuICAgICAgICAgICAgY2hhbmdlRmxhZ3MudXBkYXRlVHJpZ2dlcnNDaGFuZ2VkW2l0ZW0udXBkYXRlVHJpZ2dlcl0pXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgICAvLyBmYWxsYmFjayB0byBkaXJlY3QgY29tcGFyaXNvblxuICAgICAgcmV0dXJuIG9sZFByb3BzW2l0ZW0ucHJvcF0gIT09IHByb3BzW2l0ZW0ucHJvcF07XG4gICAgfSk7XG4gIH1cblxuICBfYWNjdW11bGF0ZVVwZGF0ZXJzPFVwZGF0ZXJPYmplY3RUeXBlIGV4dGVuZHMgRGltZW5zaW9uVHlwZSB8IEFnZ3JlZ2F0aW9uVHlwZT4oXG4gICAgc3RlcCxcbiAgICBwcm9wcyxcbiAgICBkaW1lbnNpb246IFVwZGF0ZXJPYmplY3RUeXBlXG4gICkge1xuICAgIHR5cGUgTG9jYWxVcGRhdGVyVHlwZSA9IFVwZGF0ZXJPYmplY3RUeXBlIGV4dGVuZHMgRGltZW5zaW9uVHlwZVxuICAgICAgPyBCaW5kZWRVcGRhdGVyVHlwZVxuICAgICAgOiBCaW5kZWRBZ2dyZWdhdGVkVXBkYXRlclR5cGU7XG4gICAgY29uc3QgdXBkYXRlcnM6IExvY2FsVXBkYXRlclR5cGVbXSA9IFtdO1xuICAgIGZvciAobGV0IGkgPSBzdGVwOyBpIDwgZGltZW5zaW9uLnVwZGF0ZVN0ZXBzLmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCB1cGRhdGVyID0gZGltZW5zaW9uLnVwZGF0ZVN0ZXBzW2ldLnVwZGF0ZXI7XG4gICAgICBpZiAodHlwZW9mIHVwZGF0ZXIgPT09ICdmdW5jdGlvbicpIHtcbiAgICAgICAgdXBkYXRlcnMucHVzaChcbiAgICAgICAgICB1cGRhdGVyLmJpbmQodGhpcywgZGltZW5zaW9uLnVwZGF0ZVN0ZXBzW2ldLCBwcm9wcywgZGltZW5zaW9uKSBhcyBMb2NhbFVwZGF0ZXJUeXBlXG4gICAgICAgICk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIHVwZGF0ZXJzO1xuICB9XG5cbiAgX2dldEFsbFVwZGF0ZXJzPFVwZGF0ZXJPYmplY3RUeXBlIGV4dGVuZHMgRGltZW5zaW9uVHlwZSB8IEFnZ3JlZ2F0aW9uVHlwZT4oXG4gICAgZGltZW5zaW9uOiBVcGRhdGVyT2JqZWN0VHlwZSxcbiAgICBvbGRQcm9wcyxcbiAgICBwcm9wcyxcbiAgICBjaGFuZ2VGbGFnc1xuICApIHtcbiAgICB0eXBlIExvY2FsVXBkYXRlclR5cGUgPSBVcGRhdGVyT2JqZWN0VHlwZSBleHRlbmRzIERpbWVuc2lvblR5cGVcbiAgICAgID8gQmluZGVkVXBkYXRlclR5cGVcbiAgICAgIDogQmluZGVkQWdncmVnYXRlZFVwZGF0ZXJUeXBlO1xuICAgIGxldCB1cGRhdGVyczogTG9jYWxVcGRhdGVyVHlwZVtdID0gW107XG4gICAgY29uc3QgbmVlZFVwZGF0ZVN0ZXAgPSBkaW1lbnNpb24udXBkYXRlU3RlcHMuZmluZEluZGV4KHN0ZXAgPT5cbiAgICAgIHRoaXMuX25lZWRVcGRhdGVTdGVwKHN0ZXAsIG9sZFByb3BzLCBwcm9wcywgY2hhbmdlRmxhZ3MpXG4gICAgKTtcblxuICAgIGlmIChuZWVkVXBkYXRlU3RlcCA+IC0xKSB7XG4gICAgICB1cGRhdGVycyA9IHVwZGF0ZXJzLmNvbmNhdCh0aGlzLl9hY2N1bXVsYXRlVXBkYXRlcnMobmVlZFVwZGF0ZVN0ZXAsIHByb3BzLCBkaW1lbnNpb24pKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdXBkYXRlcnM7XG4gIH1cblxuICBfZ2V0QWdncmVnYXRpb25DaGFuZ2VzKG9sZFByb3BzLCBwcm9wcywgY2hhbmdlRmxhZ3MpIHtcbiAgICBjb25zdCB1cGRhdGVycyA9IHRoaXMuX2dldEFsbFVwZGF0ZXJzKHRoaXMuYWdncmVnYXRpb25VcGRhdGVyLCBvbGRQcm9wcywgcHJvcHMsIGNoYW5nZUZsYWdzKTtcbiAgICByZXR1cm4gdXBkYXRlcnMubGVuZ3RoID8gdXBkYXRlcnMgOiBudWxsO1xuICB9XG5cbiAgX2dldERpbWVuc2lvbkNoYW5nZXMob2xkUHJvcHMsIHByb3BzLCBjaGFuZ2VGbGFncykge1xuICAgIGxldCB1cGRhdGVyczogQmluZGVkVXBkYXRlclR5cGVbXSA9IFtdO1xuXG4gICAgLy8gZ2V0IGRpbWVuc2lvbiB0byBiZSB1cGRhdGVkXG4gICAgZm9yIChjb25zdCBrZXkgaW4gdGhpcy5kaW1lbnNpb25VcGRhdGVycykge1xuICAgICAgLy8gcmV0dXJuIHRoZSBmaXJzdCB0cmlnZ2VyZWQgdXBkYXRlciBmb3IgZWFjaCBkaW1lbnNpb25cbiAgICAgIGNvbnN0IGRpbWVuc2lvbiA9IHRoaXMuZGltZW5zaW9uVXBkYXRlcnNba2V5XTtcbiAgICAgIGNvbnN0IGRpbWVuc2lvblVwZGF0ZXJzID0gdGhpcy5fZ2V0QWxsVXBkYXRlcnMoZGltZW5zaW9uLCBvbGRQcm9wcywgcHJvcHMsIGNoYW5nZUZsYWdzKTtcbiAgICAgIHVwZGF0ZXJzID0gdXBkYXRlcnMuY29uY2F0KGRpbWVuc2lvblVwZGF0ZXJzKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdXBkYXRlcnMubGVuZ3RoID8gdXBkYXRlcnMgOiBudWxsO1xuICB9XG5cbiAgZ2V0VXBkYXRlVHJpZ2dlcnMocHJvcHMpIHtcbiAgICBjb25zdCBfdXBkYXRlVHJpZ2dlcnMgPSBwcm9wcy51cGRhdGVUcmlnZ2VycyB8fCB7fTtcbiAgICBjb25zdCB1cGRhdGVUcmlnZ2VycyA9IHt9O1xuXG4gICAgZm9yIChjb25zdCBrZXkgaW4gdGhpcy5kaW1lbnNpb25VcGRhdGVycykge1xuICAgICAgY29uc3Qge2FjY2Vzc29yLCB1cGRhdGVTdGVwc306IHthY2Nlc3NvcjsgdXBkYXRlU3RlcHM6IFVwZGF0ZVN0ZXBzVHlwZVtdfSA9XG4gICAgICAgIHRoaXMuZGltZW5zaW9uVXBkYXRlcnNba2V5XTtcbiAgICAgIC8vIGZvbGQgZGltZW5zaW9uIHRyaWdnZXJzIGludG8gZWFjaCBhY2Nlc3NvclxuICAgICAgdXBkYXRlVHJpZ2dlcnNbYWNjZXNzb3JdID0ge307XG5cbiAgICAgIHVwZGF0ZVN0ZXBzLmZvckVhY2goc3RlcCA9PiB7XG4gICAgICAgIE9iamVjdC52YWx1ZXMoc3RlcC50cmlnZ2VycyB8fCBbXSkuZm9yRWFjaCgoe3Byb3AsIHVwZGF0ZVRyaWdnZXJ9KSA9PiB7XG4gICAgICAgICAgaWYgKHVwZGF0ZVRyaWdnZXIpIHtcbiAgICAgICAgICAgIC8vIGlmIHByb3AgaXMgYmFzZWQgb24gdXBkYXRlVHJpZ2dlciBlLmcuIGdldENvbG9yVmFsdWUsIGdldENvbG9yV2VpZ2h0XG4gICAgICAgICAgICAvLyBhbmQgdXBkYXRlVHJpZ2dlcnMgaXMgcGFzc2VkIGluIGZyb20gbGF5ZXIgcHJvcFxuICAgICAgICAgICAgLy8gZm9sZCB0aGUgdXBkYXRlVHJpZ2dlcnMgaW50byBhY2Nlc3NvclxuICAgICAgICAgICAgY29uc3QgZnJvbVByb3AgPSBfdXBkYXRlVHJpZ2dlcnNbdXBkYXRlVHJpZ2dlcl07XG4gICAgICAgICAgICBpZiAodHlwZW9mIGZyb21Qcm9wID09PSAnb2JqZWN0JyAmJiAhQXJyYXkuaXNBcnJheShmcm9tUHJvcCkpIHtcbiAgICAgICAgICAgICAgLy8gaWYgdXBkYXRlVHJpZ2dlciBpcyBhbiBvYmplY3Qgc3ByZWFkIGl0XG4gICAgICAgICAgICAgIE9iamVjdC5hc3NpZ24odXBkYXRlVHJpZ2dlcnNbYWNjZXNzb3JdLCBmcm9tUHJvcCk7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKGZyb21Qcm9wICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgdXBkYXRlVHJpZ2dlcnNbYWNjZXNzb3JdW3Byb3BdID0gZnJvbVByb3A7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIC8vIGlmIHByb3AgaXMgbm90IGJhc2VkIG9uIHVwZGF0ZVRyaWdnZXJcbiAgICAgICAgICAgIHVwZGF0ZVRyaWdnZXJzW2FjY2Vzc29yXVtwcm9wXSA9IHByb3BzW3Byb3BdO1xuICAgICAgICAgIH1cbiAgICAgICAgfSk7XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gdXBkYXRlVHJpZ2dlcnM7XG4gIH1cblxuICBnZXRQaWNraW5nSW5mbyh7aW5mb30sIGxheWVyUHJvcHMpIHtcbiAgICBjb25zdCBpc1BpY2tlZCA9IGluZm8ucGlja2VkICYmIGluZm8uaW5kZXggPiAtMTtcbiAgICBsZXQgb2JqZWN0ID0gbnVsbDtcbiAgICBjb25zdCBjZWxsID0gaXNQaWNrZWQgPyB0aGlzLnN0YXRlLmxheWVyRGF0YS5kYXRhW2luZm8uaW5kZXhdIDogb