UNPKG

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
"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