UNPKG

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