UNPKG

kepler.gl

Version:

kepler.gl is a webgl based application to visualize large scale location data in the browser

563 lines (556 loc) 73.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _typeof = require("@babel/runtime/helpers/typeof"); Object.defineProperty(exports, "__esModule", { value: true }); exports.tripVisConfigs = exports.featureResolver = exports.featureAccessor = exports.defaultThickness = exports.defaultLineWidth = exports["default"] = exports.COLUMN_MODE_TABLE = exports.COLUMN_MODE_GEOJSON = void 0; var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn")); var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf")); var _get2 = _interopRequireDefault(require("@babel/runtime/helpers/get")); var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _memoize = _interopRequireDefault(require("lodash/memoize")); var _uniq = _interopRequireDefault(require("lodash/uniq")); var _baseLayer = _interopRequireWildcard(require("../base-layer")); var _geoLayers = require("@deck.gl/geo-layers"); var _constants = require("@kepler.gl/constants"); var _tripLayerIcon = _interopRequireDefault(require("./trip-layer-icon")); var _geojsonUtils = require("../geojson-layer/geojson-utils"); var _tripUtils = require("./trip-utils"); var _tripInfoModal = _interopRequireDefault(require("./trip-info-modal")); var _d3Array = require("d3-array"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 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; } function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2["default"])(o), (0, _possibleConstructorReturn2["default"])(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2["default"])(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } function _superPropGet(t, e, r, o) { var p = (0, _get2["default"])((0, _getPrototypeOf2["default"])(1 & o ? t.prototype : t), e, r); return 2 & o ? function (t) { return p.apply(r, t); } : p; } // SPDX-License-Identifier: MIT // Copyright contributors to the kepler.gl project var zoomFactorValue = 8; var defaultThickness = exports.defaultThickness = 0.5; var defaultLineWidth = exports.defaultLineWidth = 1; var tripVisConfigs = exports.tripVisConfigs = { opacity: 'opacity', thickness: { type: 'number', defaultValue: defaultThickness, label: 'Stroke Width', isRanged: false, range: [0, 100], step: 0.1, group: 'stroke', property: 'thickness' }, colorRange: 'colorRange', trailLength: 'trailLength', fadeTrail: 'fadeTrail', billboard: 'billboard', sizeRange: 'strokeWidthRange' }; var featureAccessor = exports.featureAccessor = function featureAccessor(_ref) { var geojson = _ref.geojson; return function (dc) { return function (d) { return dc.valueAt(d.index, geojson.fieldIdx); }; }; }; var featureResolver = exports.featureResolver = function featureResolver(_ref2) { var geojson = _ref2.geojson; return geojson.fieldIdx; }; var getTableModeValueAccessor = function getTableModeValueAccessor(f) { // Called from gpu-filter-utils.getFilterValueAccessor() return function (field) { return f.properties.values.map(function (v) { return field.valueAccessor(v); }); }; }; var getTableModeFieldValue = function getTableModeFieldValue(field, data) { var rv; if (typeof data === 'function') { rv = data(field); } else { rv = (0, _baseLayer.defaultGetFieldValue)(field, data); } return rv; }; var COLUMN_MODE_GEOJSON = exports.COLUMN_MODE_GEOJSON = 'geojson'; var COLUMN_MODE_TABLE = exports.COLUMN_MODE_TABLE = 'table'; var SUPPORTED_COLUMN_MODES = [{ key: COLUMN_MODE_GEOJSON, label: 'GeoJSON', requiredColumns: ['geojson'] }, { key: COLUMN_MODE_TABLE, label: 'Table columns', requiredColumns: ['id', 'lat', 'lng', 'timestamp'], optionalColumns: ['altitude'] }]; var DEFAULT_COLUMN_MODE = COLUMN_MODE_GEOJSON; var TripLayer = exports["default"] = /*#__PURE__*/function (_Layer) { function TripLayer(props) { var _this; (0, _classCallCheck2["default"])(this, TripLayer); _this = _callSuper(this, TripLayer, [props]); (0, _defineProperty2["default"])(_this, "dataToFeature", void 0); (0, _defineProperty2["default"])(_this, "dataToTimeStamp", void 0); (0, _defineProperty2["default"])(_this, "getFeature", void 0); (0, _defineProperty2["default"])(_this, "_layerInfoModal", void 0); _this.dataToFeature = []; _this.dataToTimeStamp = []; _this.dataContainer = null; _this.registerVisConfig(tripVisConfigs); _this.getFeature = (0, _memoize["default"])(featureAccessor, featureResolver); _this._layerInfoModal = (0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, COLUMN_MODE_TABLE, (0, _tripInfoModal["default"])(COLUMN_MODE_TABLE)), COLUMN_MODE_GEOJSON, (0, _tripInfoModal["default"])(COLUMN_MODE_GEOJSON)); return _this; } (0, _inherits2["default"])(TripLayer, _Layer); return (0, _createClass2["default"])(TripLayer, [{ key: "supportedColumnModes", get: function get() { return SUPPORTED_COLUMN_MODES; } }, { key: "type", get: function get() { return TripLayer.type; } }, { key: "name", get: function get() { return 'Trip'; } }, { key: "layerIcon", get: function get() { return _tripLayerIcon["default"]; } }, { key: "columnPairs", get: function get() { return this.defaultPointColumnPairs; } }, { key: "accessVSFieldValue", value: function accessVSFieldValue() { if (this.config.columnMode === COLUMN_MODE_GEOJSON) { return _baseLayer.defaultGetFieldValue; } return getTableModeFieldValue; } }, { key: "visualChannels", get: function get() { var visualChannels = _superPropGet(TripLayer, "visualChannels", this, 1); return _objectSpread(_objectSpread({}, visualChannels), {}, { color: _objectSpread(_objectSpread({}, visualChannels.color), {}, { accessor: 'getColor', nullValue: visualChannels.color.nullValue, getAttributeValue: function getAttributeValue(config) { return function (d) { return d.properties.lineColor || config.color; }; }, // used this to get updateTriggers defaultValue: function defaultValue(config) { return config.color; } }), size: _objectSpread(_objectSpread({}, visualChannels.size), {}, { property: 'stroke', accessor: 'getWidth', condition: function condition(config) { return config.visConfig.stroked; }, nullValue: 0, getAttributeValue: function getAttributeValue() { return function (d) { return d.properties.lineWidth || defaultLineWidth; }; } }) }); } }, { key: "animationDomain", get: function get() { return this.config.animation.domain; } }, { key: "layerInfoModal", get: function get() { return (0, _defineProperty2["default"])((0, _defineProperty2["default"])({}, COLUMN_MODE_GEOJSON, { id: 'iconInfo', template: this._layerInfoModal[COLUMN_MODE_GEOJSON], modalProps: { title: 'modal.tripInfo.title' } }), COLUMN_MODE_TABLE, { id: 'iconInfo', template: this._layerInfoModal[COLUMN_MODE_TABLE], modalProps: { title: 'modal.tripInfo.titleTable' } }); } }, { key: "getPositionAccessor", value: function getPositionAccessor(dataContainer) { if (this.config.columnMode === COLUMN_MODE_GEOJSON) { return this.getFeature(this.config.columns)(dataContainer); } return null; } }, { key: "getDefaultLayerConfig", value: function getDefaultLayerConfig(props) { var _props$columnMode; return _objectSpread(_objectSpread({}, _superPropGet(TripLayer, "getDefaultLayerConfig", this, 3)([props])), {}, { columnMode: (_props$columnMode = props === null || props === void 0 ? void 0 : props.columnMode) !== null && _props$columnMode !== void 0 ? _props$columnMode : DEFAULT_COLUMN_MODE, animation: { enabled: true, domain: null } }); } }, { key: "getHoverData", value: function getHoverData(object, dataContainer, fields, animationConfig) { var _this$_findColumnMode; if (this.config.columnMode === COLUMN_MODE_GEOJSON) { // index for dataContainer is saved to feature.properties return dataContainer.row(object.properties.index); } return (_this$_findColumnMode = this._findColumnModeDatumForFeature(object.properties.index, animationConfig.currentTime)) === null || _this$_findColumnMode === void 0 ? void 0 : _this$_findColumnMode.datum; } }, { key: "calculateDataAttribute", value: function calculateDataAttribute(dataset) { var _this2 = this; switch (this.config.columnMode) { case COLUMN_MODE_GEOJSON: { return dataset.filteredIndex.map(function (i) { return _this2.dataToFeature[i]; }) // TODO d can be BinaryFeatureCollection, fix logic .filter(function (d) { var _geometry; return d && ((_geometry = d.geometry) === null || _geometry === void 0 ? void 0 : _geometry.type) === 'LineString'; }); } case COLUMN_MODE_TABLE: return (0, _geojsonUtils.applyFiltersToTableColumns)(dataset, this.dataToFeature); default: return []; } } }, { key: "formatLayerData", value: function formatLayerData(datasets, oldLayerData) { var _this3 = this; if (this.config.dataId === null) { return {}; } // to-do: parse segment from dataContainer var _datasets$this$config = datasets[this.config.dataId], dataContainer = _datasets$this$config.dataContainer, gpuFilter = _datasets$this$config.gpuFilter; var _this$updateData = this.updateData(datasets, oldLayerData), data = _this$updateData.data; var valueAccessor; var dataAccessor; if (this.config.columnMode === COLUMN_MODE_GEOJSON) { valueAccessor = function valueAccessor(dc, f, fieldIndex) { return dc.valueAt(f.properties.index, fieldIndex); }; // For GEOJSON mode, properties.index is the row index in the data container dataAccessor = function dataAccessor() { return function (d) { return { index: d.properties.index }; }; }; } else { valueAccessor = getTableModeValueAccessor; // For TABLE mode, properties.index is the feature index (not row index). // Use the first row from properties.values to get field values for color/size. dataAccessor = function dataAccessor() { return function (d) { return d.properties.values[0]; }; }; } var indexAccessor = function indexAccessor(f) { return f.properties.index; }; var accessors = this.getAttributeAccessors({ dataAccessor: dataAccessor, dataContainer: dataContainer }); var getFilterValue = gpuFilter.filterValueAccessor(dataContainer)(indexAccessor, valueAccessor); return _objectSpread({ data: data, getFilterValue: getFilterValue, getPath: function getPath(d) { return d.geometry.coordinates; }, getTimestamps: function getTimestamps(d) { return _this3.dataToTimeStamp[d.properties.index]; } }, accessors); } }, { key: "updateAnimationDomain", value: function updateAnimationDomain(domain) { this.updateLayerConfig({ animation: _objectSpread(_objectSpread({}, this.config.animation), {}, { domain: domain }) }); } }, { key: "updateLayerMeta", value: function updateLayerMeta(dataset) { var dataContainer = dataset.dataContainer; var getFeature; if (this.config.columnMode === COLUMN_MODE_GEOJSON) { getFeature = this.getPositionAccessor(dataContainer); if (getFeature === this.meta.getFeature) { // TODO: revisit this after gpu filtering return; } this.dataToFeature = (0, _geojsonUtils.getGeojsonDataMaps)(dataContainer, getFeature); } else { this.dataContainer = dataContainer; this.dataToFeature = (0, _geojsonUtils.groupColumnsAsGeoJson)(dataContainer, this.config.columns, 'timestamp'); } var _parseTripGeoJsonTime = (0, _tripUtils.parseTripGeoJsonTimestamp)(this.dataToFeature), dataToTimeStamp = _parseTripGeoJsonTime.dataToTimeStamp, animationDomain = _parseTripGeoJsonTime.animationDomain; this.dataToTimeStamp = dataToTimeStamp; this.updateAnimationDomain(animationDomain); // get bounds from features var bounds = (0, _geojsonUtils.getGeojsonBounds)(this.dataToFeature); // keep a record of what type of geometry the collection has var featureTypes = (0, _geojsonUtils.getGeojsonFeatureTypes)(this.dataToFeature); this.updateMeta({ bounds: bounds, featureTypes: featureTypes, getFeature: getFeature }); } }, { key: "setInitialLayerConfig", value: function setInitialLayerConfig(dataset) { var dataContainer = dataset.dataContainer; if (!dataContainer.numRows()) { return this; } // defefaultLayerProps will automatically find geojson column // if not found, we try to set it to id / lat /lng /ts if (!this.config.columns.geojson.value) { // find columns from lat, lng, id, and ts var columnConfig = (0, _geojsonUtils.detectTableColumns)(dataset, this.config.columns); if (columnConfig) { this.updateLayerConfig(_objectSpread(_objectSpread({}, columnConfig), {}, { columnMode: COLUMN_MODE_TABLE })); } else { return this; } } this.updateLayerMeta(dataset); return this; } }, { key: "renderLayer", value: function renderLayer(opts) { var _animationConfig$doma, _mapState$globe; var data = opts.data, gpuFilter = opts.gpuFilter, mapState = opts.mapState, animationConfig = opts.animationConfig; var visConfig = this.config.visConfig; var zoomFactor = this.getZoomFactor(mapState); var isValidTime = animationConfig && Array.isArray(animationConfig.domain) && animationConfig.domain.every(Number.isFinite) && Number.isFinite(animationConfig.currentTime); if (!isValidTime) { return []; } var domain0 = (_animationConfig$doma = animationConfig.domain) === null || _animationConfig$doma === void 0 ? void 0 : _animationConfig$doma[0]; var gpuFilterUpdateTriggers = { getFilterValue: gpuFilter.filterValueUpdateTriggers }; var updateTriggers = _objectSpread(_objectSpread({}, this.getVisualChannelUpdateTriggers()), {}, { getTimestamps: { columns: this.config.columns, domain0: domain0 } }, gpuFilterUpdateTriggers); var defaultLayerProps = this.getDefaultDeckLayerProps(opts); var billboardWidthFactor = visConfig.billboard ? _constants.PROJECTED_PIXEL_SIZE_MULTIPLIER : 1; var layerProps = _objectSpread(_objectSpread(_objectSpread({}, defaultLayerProps), data), {}, { getTimestamps: function getTimestamps(d) { return (data.getTimestamps(d) || []).map(function (ts) { return ts - domain0; }); }, widthScale: visConfig.thickness * zoomFactor * zoomFactorValue * billboardWidthFactor, capRounded: true, jointRounded: true, wrapLongitude: false, parameters: { depthTest: mapState.dragRotate, depthMask: false }, trailLength: visConfig.trailLength * 1000, fadeTrail: visConfig.fadeTrail, billboard: visConfig.billboard, // TODO: giuseppe this values becomes negative currentTime: animationConfig.currentTime - domain0, updateTriggers: updateTriggers, id: "".concat(defaultLayerProps.id).concat((_mapState$globe = mapState.globe) !== null && _mapState$globe !== void 0 && _mapState$globe.enabled ? '-globe' : '') }); return [new _geoLayers.TripsLayer(layerProps)]; } /** * Finds coordinates and datum at the current animation time by the specified feature index. * @param featureIndex * @param time * @returns {{datum: (null|string|*), idx: *, coords}|{datum: null, idx: number, coords: null}} */ }, { key: "_findColumnModeDatumForFeature", value: function _findColumnModeDatumForFeature(featureIndex, time) { if (this.config.columnMode === COLUMN_MODE_TABLE) { var object = this.dataToFeature[featureIndex]; var idx = (0, _d3Array.bisectRight)(this.dataToTimeStamp[featureIndex], time); // @ts-expect-error type geometry? var _ref4 = (object === null || object === void 0 ? void 0 : object.geometry) || { coordinates: [] }, coordinates = _ref4.coordinates; if (idx >= 0 && idx < coordinates.length) { var coords = coordinates[idx]; return { idx: idx, coords: coords, datum: coords === null || coords === void 0 ? void 0 : coords.datum }; } } return { idx: -1, coords: null, datum: null }; } }], [{ key: "type", get: function get() { return 'trip'; } }, { key: "findDefaultLayerProps", value: function findDefaultLayerProps(_ref5, foundLayers) { var _this4 = this; var label = _ref5.label, _ref5$fields = _ref5.fields, fields = _ref5$fields === void 0 ? [] : _ref5$fields, dataContainer = _ref5.dataContainer, id = _ref5.id, _ref5$fieldPairs = _ref5.fieldPairs, fieldPairs = _ref5$fieldPairs === void 0 ? [] : _ref5$fieldPairs; var geojsonColumns = fields.filter(function (f) { return f.type === 'geojson' || f.type === 'geoarrow'; }).map(function (f) { return f.name; }); var defaultColumns = { geojson: (0, _uniq["default"])([].concat((0, _toConsumableArray2["default"])(_constants.GEOJSON_FIELDS.geojson), (0, _toConsumableArray2["default"])(geojsonColumns))) }; var geoJsonColumns = this.findDefaultColumnField(defaultColumns, fields); var tripGeojsonColumns = (geoJsonColumns || []).filter(function (col) { return (0, _tripUtils.isTripGeoJsonField)(dataContainer, fields[col.geojson.fieldIdx]); }); if (tripGeojsonColumns.length) { return { props: tripGeojsonColumns.map(function (columns) { return { label: typeof label === 'string' && label.replace(/\.[^/.]+$/, '') || _this4.type, columns: columns, isVisible: true, columnMode: COLUMN_MODE_GEOJSON }; }), // if a geojson layer is created from this column, delete it foundLayers: foundLayers === null || foundLayers === void 0 ? void 0 : foundLayers.filter(function (prop) { return prop.type !== 'geojson' || prop.dataId !== id || !tripGeojsonColumns.find(function (c) { return prop.columns.geojson.name === c.geojson.name; }); }) }; } // Try to detect table columns (id/lat/lng/timestamp) for table column mode // This allows creating trip layers from tabular data without GeoJSON if (fieldPairs.length && fields.length) { // Default layer columns for table mode var defaultTableColumns = { id: { value: null, fieldIdx: -1 }, lat: { value: null, fieldIdx: -1 }, lng: { value: null, fieldIdx: -1 }, timestamp: { value: null, fieldIdx: -1 }, altitude: { value: null, fieldIdx: -1, optional: true }, geojson: { value: null, fieldIdx: -1 } }; var tableColumns = (0, _geojsonUtils.detectTableColumns)({ fields: fields, fieldPairs: fieldPairs }, defaultTableColumns, 'timestamp'); if (tableColumns) { // Found required columns for table mode return { props: [{ label: tableColumns.label || typeof label === 'string' && label.replace(/\.[^/.]+$/, '') || this.type, columns: tableColumns.columns, isVisible: true, columnMode: COLUMN_MODE_TABLE }], foundLayers: foundLayers }; } } return { props: [] }; } }]); }(_baseLayer["default"]); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbWVtb2l6ZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX3VuaXEiLCJfYmFzZUxheWVyIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfZ2VvTGF5ZXJzIiwiX2NvbnN0YW50cyIsIl90cmlwTGF5ZXJJY29uIiwiX2dlb2pzb25VdGlscyIsIl90cmlwVXRpbHMiLCJfdHJpcEluZm9Nb2RhbCIsIl9kM0FycmF5IiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJfdHlwZW9mIiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0Iiwib3duS2V5cyIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiX2NhbGxTdXBlciIsIl9nZXRQcm90b3R5cGVPZjIiLCJfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybjIiLCJfaXNOYXRpdmVSZWZsZWN0Q29uc3RydWN0IiwiUmVmbGVjdCIsImNvbnN0cnVjdCIsImNvbnN0cnVjdG9yIiwiQm9vbGVhbiIsInByb3RvdHlwZSIsInZhbHVlT2YiLCJfc3VwZXJQcm9wR2V0IiwicCIsIl9nZXQyIiwiem9vbUZhY3RvclZhbHVlIiwiZGVmYXVsdFRoaWNrbmVzcyIsImV4cG9ydHMiLCJkZWZhdWx0TGluZVdpZHRoIiwidHJpcFZpc0NvbmZpZ3MiLCJvcGFjaXR5IiwidGhpY2tuZXNzIiwidHlwZSIsImRlZmF1bHRWYWx1ZSIsImxhYmVsIiwiaXNSYW5nZWQiLCJyYW5nZSIsInN0ZXAiLCJncm91cCIsInByb3BlcnR5IiwiY29sb3JSYW5nZSIsInRyYWlsTGVuZ3RoIiwiZmFkZVRyYWlsIiwiYmlsbGJvYXJkIiwic2l6ZVJhbmdlIiwiZmVhdHVyZUFjY2Vzc29yIiwiX3JlZiIsImdlb2pzb24iLCJkYyIsImQiLCJ2YWx1ZUF0IiwiaW5kZXgiLCJmaWVsZElkeCIsImZlYXR1cmVSZXNvbHZlciIsIl9yZWYyIiwiZ2V0VGFibGVNb2RlVmFsdWVBY2Nlc3NvciIsImYiLCJmaWVsZCIsInByb3BlcnRpZXMiLCJ2YWx1ZXMiLCJtYXAiLCJ2IiwidmFsdWVBY2Nlc3NvciIsImdldFRhYmxlTW9kZUZpZWxkVmFsdWUiLCJkYXRhIiwicnYiLCJkZWZhdWx0R2V0RmllbGRWYWx1ZSIsIkNPTFVNTl9NT0RFX0dFT0pTT04iLCJDT0xVTU5fTU9ERV9UQUJMRSIsIlNVUFBPUlRFRF9DT0xVTU5fTU9ERVMiLCJrZXkiLCJyZXF1aXJlZENvbHVtbnMiLCJvcHRpb25hbENvbHVtbnMiLCJERUZBVUxUX0NPTFVNTl9NT0RFIiwiVHJpcExheWVyIiwiX0xheWVyIiwicHJvcHMiLCJfdGhpcyIsIl9jbGFzc0NhbGxDaGVjazIiLCJkYXRhVG9GZWF0dXJlIiwiZGF0YVRvVGltZVN0YW1wIiwiZGF0YUNvbnRhaW5lciIsInJlZ2lzdGVyVmlzQ29uZmlnIiwiZ2V0RmVhdHVyZSIsIm1lbW9pemUiLCJfbGF5ZXJJbmZvTW9kYWwiLCJUcmlwSW5mb01vZGFsRmFjdG9yeSIsIl9pbmhlcml0czIiLCJfY3JlYXRlQ2xhc3MyIiwiVHJpcExheWVySWNvbiIsImRlZmF1bHRQb2ludENvbHVtblBhaXJzIiwidmFsdWUiLCJhY2Nlc3NWU0ZpZWxkVmFsdWUiLCJjb25maWciLCJjb2x1bW5Nb2RlIiwidmlzdWFsQ2hhbm5lbHMiLCJjb2xvciIsImFjY2Vzc29yIiwibnVsbFZhbHVlIiwiZ2V0QXR0cmlidXRlVmFsdWUiLCJsaW5lQ29sb3IiLCJzaXplIiwiY29uZGl0aW9uIiwidmlzQ29uZmlnIiwic3Ryb2tlZCIsImxpbmVXaWR0aCIsImFuaW1hdGlvbiIsImRvbWFpbiIsImlkIiwidGVtcGxhdGUiLCJtb2RhbFByb3BzIiwidGl0bGUiLCJnZXRQb3NpdGlvbkFjY2Vzc29yIiwiY29sdW1ucyIsImdldERlZmF1bHRMYXllckNvbmZpZyIsIl9wcm9wcyRjb2x1bW5Nb2RlIiwiZW5hYmxlZCIsImdldEhvdmVyRGF0YSIsIm9iamVjdCIsImZpZWxkcyIsImFuaW1hdGlvbkNvbmZpZyIsIl90aGlzJF9maW5kQ29sdW1uTW9kZSIsInJvdyIsIl9maW5kQ29sdW1uTW9kZURhdHVtRm9yRmVhdHVyZSIsImN1cnJlbnRUaW1lIiwiZGF0dW0iLCJjYWxjdWxhdGVEYXRhQXR0cmlidXRlIiwiZGF0YXNldCIsIl90aGlzMiIsImZpbHRlcmVkSW5kZXgiLCJfZ2VvbWV0cnkiLCJnZW9tZXRyeSIsImFwcGx5RmlsdGVyc1RvVGFibGVDb2x1bW5zIiwiZm9ybWF0TGF5ZXJEYXRhIiwiZGF0YXNldHMiLCJvbGRMYXllckRhdGEiLCJfdGhpczMiLCJkYXRhSWQiLCJfZGF0YXNldHMkdGhpcyRjb25maWciLCJncHVGaWx0ZXIiLCJfdGhpcyR1cGRhdGVEYXRhIiwidXBkYXRlRGF0YSIsImRhdGFBY2Nlc3NvciIsImZpZWxkSW5kZXgiLCJpbmRleEFjY2Vzc29yIiwiYWNjZXNzb3JzIiwiZ2V0QXR0cmlidXRlQWNjZXNzb3JzIiwiZ2V0RmlsdGVyVmFsdWUiLCJmaWx0ZXJWYWx1ZUFjY2Vzc29yIiwiZ2V0UGF0aCIsImNvb3JkaW5hdGVzIiwiZ2V0VGltZXN0YW1wcyIsInVwZGF0ZUFuaW1hdGlvbkRvbWFpbiIsInVwZGF0ZUxheWVyQ29uZmlnIiwidXBkYXRlTGF5ZXJNZXRhIiwibWV0YSIsImdldEdlb2pzb25EYXRhTWFwcyIsImdyb3VwQ29sdW1uc0FzR2VvSnNvbiIsIl9wYXJzZVRyaXBHZW9Kc29uVGltZSIsInBhcnNlVHJpcEdlb0pzb25UaW1lc3RhbXAiLCJhbmltYXRpb25Eb21haW4iLCJib3VuZHMiLCJnZXRHZW9qc29uQm91bmRzIiwiZmVhdHVyZVR5cGVzIiwiZ2V0R2VvanNvbkZlYXR1cmVUeXBlcyIsInVwZGF0ZU1ldGEiLCJzZXRJbml0aWFsTGF5ZXJDb25maWciLCJudW1Sb3dzIiwiY29sdW1uQ29uZmlnIiwiZGV0ZWN0VGFibGVDb2x1bW5zIiwicmVuZGVyTGF5ZXIiLCJvcHRzIiwiX2FuaW1hdGlvbkNvbmZpZyRkb21hIiwiX21hcFN0YXRlJGdsb2JlIiwibWFwU3RhdGUiLCJ6b29tRmFjdG9yIiwiZ2V0Wm9vbUZhY3RvciIsImlzVmFsaWRUaW1lIiwiQXJyYXkiLCJpc0FycmF5IiwiZXZlcnkiLCJOdW1iZXIiLCJpc0Zpbml0ZSIsImRvbWFpbjAiLCJncHVGaWx0ZXJVcGRhdGVUcmlnZ2VycyIsImZpbHRlclZhbHVlVXBkYXRlVHJpZ2dlcnMiLCJ1cGRhdGVUcmlnZ2VycyIsImdldFZpc3VhbENoYW5uZWxVcGRhdGVUcmlnZ2VycyIsImRlZmF1bHRMYXllclByb3BzIiwiZ2V0RGVmYXVsdERlY2tMYXllclByb3BzIiwiYmlsbGJvYXJkV2lkdGhGYWN0b3IiLCJQUk9KRUNURURfUElYRUxfU0laRV9NVUxUSVBMSUVSIiwibGF5ZXJQcm9wcyIsInRzIiwid2lkdGhTY2FsZSIsImNhcFJvdW5kZWQiLCJqb2ludFJvdW5kZWQiLCJ3cmFwTG9uZ2l0dWRlIiwicGFyYW1ldGVycyIsImRlcHRoVGVzdCIsImRyYWdSb3RhdGUiLCJkZXB0aE1hc2siLCJjb25jYXQiLCJnbG9iZSIsIkRlY2tHTFRyaXBzTGF5ZXIiLCJmZWF0dXJlSW5kZXgiLCJ0aW1lIiwiaWR4IiwiYmlzZWN0UmlnaHQiLCJfcmVmNCIsImNvb3JkcyIsImZpbmREZWZhdWx0TGF5ZXJQcm9wcyIsIl9yZWY1IiwiZm91bmRMYXllcnMiLCJfdGhpczQiLCJfcmVmNSRmaWVsZHMiLCJfcmVmNSRmaWVsZFBhaXJzIiwiZmllbGRQYWlycyIsImdlb2pzb25Db2x1bW5zIiwibmFtZSIsImRlZmF1bHRDb2x1bW5zIiwidW5pcSIsIl90b0NvbnN1bWFibGVBcnJheTIiLCJHRU9KU09OX0ZJRUxEUyIsImdlb0pzb25Db2x1bW5zIiwiZmluZERlZmF1bHRDb2x1bW5GaWVsZCIsInRyaXBHZW9qc29uQ29sdW1ucyIsImNvbCIsImlzVHJpcEdlb0pzb25GaWVsZCIsInJlcGxhY2UiLCJpc1Zpc2libGUiLCJwcm9wIiwiZmluZCIsImMiLCJkZWZhdWx0VGFibGVDb2x1bW5zIiwibGF0IiwibG5nIiwidGltZXN0YW1wIiwiYWx0aXR1ZGUiLCJvcHRpb25hbCIsInRhYmxlQ29sdW1ucyIsIkxheWVyIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3RyaXAtbGF5ZXIvdHJpcC1sYXllci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQgbWVtb2l6ZSBmcm9tICdsb2Rhc2gvbWVtb2l6ZSc7XG5pbXBvcnQgdW5pcSBmcm9tICdsb2Rhc2gvdW5pcSc7XG5pbXBvcnQgTGF5ZXIsIHtMYXllckJhc2VDb25maWcsIGRlZmF1bHRHZXRGaWVsZFZhbHVlfSBmcm9tICcuLi9iYXNlLWxheWVyJztcbmltcG9ydCB7VHJpcHNMYXllciBhcyBEZWNrR0xUcmlwc0xheWVyfSBmcm9tICdAZGVjay5nbC9nZW8tbGF5ZXJzJztcblxuaW1wb3J0IHtHRU9KU09OX0ZJRUxEUywgUFJPSkVDVEVEX1BJWEVMX1NJWkVfTVVMVElQTElFUn0gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuaW1wb3J0IFRyaXBMYXllckljb24gZnJvbSAnLi90cmlwLWxheWVyLWljb24nO1xuXG5pbXBvcnQge1xuICBnZXRHZW9qc29uRGF0YU1hcHMsXG4gIGdldEdlb2pzb25Cb3VuZHMsXG4gIGdldEdlb2pzb25GZWF0dXJlVHlwZXMsXG4gIEdlb2pzb25EYXRhTWFwcyxcbiAgZGV0ZWN0VGFibGVDb2x1bW5zLFxuICBncm91cENvbHVtbnNBc0dlb0pzb24sXG4gIGFwcGx5RmlsdGVyc1RvVGFibGVDb2x1bW5zXG59IGZyb20gJy4uL2dlb2pzb24tbGF5ZXIvZ2VvanNvbi11dGlscyc7XG5cbmltcG9ydCB7aXNUcmlwR2VvSnNvbkZpZWxkLCBwYXJzZVRyaXBHZW9Kc29uVGltZXN0YW1wfSBmcm9tICcuL3RyaXAtdXRpbHMnO1xuaW1wb3J0IFRyaXBJbmZvTW9kYWxGYWN0b3J5IGZyb20gJy4vdHJpcC1pbmZvLW1vZGFsJztcbmltcG9ydCB7YmlzZWN0UmlnaHR9IGZyb20gJ2QzLWFycmF5JztcbmltcG9ydCB7XG4gIENvbG9yUmFuZ2UsXG4gIE1lcmdlLFxuICBWaXNDb25maWdDb2xvclJhbmdlLFxuICBWaXNDb25maWdOdW1iZXIsXG4gIFZpc0NvbmZpZ1JhbmdlLFxuICBMYXllckNvbHVtblxufSBmcm9tICdAa2VwbGVyLmdsL3R5cGVzJztcbmltcG9ydCB7ZGVmYXVsdCBhcyBLZXBsZXJUYWJsZSwgRGF0YXNldHN9IGZyb20gJ0BrZXBsZXIuZ2wvdGFibGUnO1xuaW1wb3J0IHtEYXRhQ29udGFpbmVySW50ZXJmYWNlfSBmcm9tICdAa2VwbGVyLmdsL3V0aWxzJztcblxuZXhwb3J0IHR5cGUgVHJpcExheWVyVmlzQ29uZmlnU2V0dGluZ3MgPSB7XG4gIG9wYWNpdHk6IFZpc0NvbmZpZ051bWJlcjtcbiAgdGhpY2tuZXNzOiBWaXNDb25maWdOdW1iZXI7XG4gIGNvbG9yUmFuZ2U6IFZpc0NvbmZpZ0NvbG9yUmFuZ2U7XG4gIHRyYWlsTGVuZ3RoOiBWaXNDb25maWdOdW1iZXI7XG4gIHNpemVSYW5nZTogVmlzQ29uZmlnUmFuZ2U7XG59O1xuXG5leHBvcnQgdHlwZSBUcmlwTGF5ZXJDb2x1bW5zQ29uZmlnID0ge1xuICBnZW9qc29uOiBMYXllckNvbHVtbjtcbn07XG5cbmV4cG9ydCB0eXBlIFRyaXBMYXllclZpc0NvbmZpZyA9IHtcbiAgb3BhY2l0eTogbnVtYmVyO1xuICB0aGlja25lc3M6IG51bWJlcjtcbiAgY29sb3JSYW5nZTogQ29sb3JSYW5nZTtcbiAgdHJhaWxMZW5ndGg6IG51bWJlcjtcbiAgc2l6ZVJhbmdlOiBbbnVtYmVyLCBudW1iZXJdO1xuICBiaWxsYm9hcmQ6IGJvb2xlYW47XG4gIGZhZGVUcmFpbDogYm9vbGVhbjtcbn07XG5cbmV4cG9ydCB0eXBlIFRyaXBMYXllckNvbmZpZyA9IE1lcmdlPFxuICBMYXllckJhc2VDb25maWcsXG4gIHtjb2x1bW5zOiBUcmlwTGF5ZXJDb2x1bW5zQ29uZmlnOyB2aXNDb25maWc6IFRyaXBMYXllclZpc0NvbmZpZ31cbj47XG5cbmV4cG9ydCB0eXBlIFRyaXBMYXllck1ldGEgPSB7XG4gIGdldEZlYXR1cmU6IGFueTtcbn07XG5cbmNvbnN0IHpvb21GYWN0b3JWYWx1ZSA9IDg7XG5cbmV4cG9ydCBjb25zdCBkZWZhdWx0VGhpY2tuZXNzID0gMC41O1xuZXhwb3J0IGNvbnN0IGRlZmF1bHRMaW5lV2lkdGggPSAxO1xuXG5leHBvcnQgY29uc3QgdHJpcFZpc0NvbmZpZ3M6IHtcbiAgb3BhY2l0eTogJ29wYWNpdHknO1xuICB0aGlja25lc3M6IFZpc0NvbmZpZ051bWJlcjtcbiAgY29sb3JSYW5nZTogJ2NvbG9yUmFuZ2UnO1xuICB0cmFpbExlbmd0aDogJ3RyYWlsTGVuZ3RoJztcbiAgZmFkZVRyYWlsOiAnZmFkZVRyYWlsJztcbiAgYmlsbGJvYXJkOiAnYmlsbGJvYXJkJztcbiAgc2l6ZVJhbmdlOiAnc3Ryb2tlV2lkdGhSYW5nZSc7XG59ID0ge1xuICBvcGFjaXR5OiAnb3BhY2l0eScsXG4gIHRoaWNrbmVzczoge1xuICAgIHR5cGU6ICdudW1iZXInLFxuICAgIGRlZmF1bHRWYWx1ZTogZGVmYXVsdFRoaWNrbmVzcyxcbiAgICBsYWJlbDogJ1N0cm9rZSBXaWR0aCcsXG4gICAgaXNSYW5nZWQ6IGZhbHNlLFxuICAgIHJhbmdlOiBbMCwgMTAwXSxcbiAgICBzdGVwOiAwLjEsXG4gICAgZ3JvdXA6ICdzdHJva2UnLFxuICAgIHByb3BlcnR5OiAndGhpY2tuZXNzJ1xuICB9LFxuICBjb2xvclJhbmdlOiAnY29sb3JSYW5nZScsXG4gIHRyYWlsTGVuZ3RoOiAndHJhaWxMZW5ndGgnLFxuICBmYWRlVHJhaWw6ICdmYWRlVHJhaWwnLFxuICBiaWxsYm9hcmQ6ICdiaWxsYm9hcmQnLFxuICBzaXplUmFuZ2U6ICdzdHJva2VXaWR0aFJhbmdlJ1xufTtcblxuZXhwb3J0IGNvbnN0IGZlYXR1cmVBY2Nlc3NvciA9XG4gICh7Z2VvanNvbn06IFRyaXBMYXllckNvbHVtbnNDb25maWcpID0+XG4gIChkYzogRGF0YUNvbnRhaW5lckludGVyZmFjZSkgPT5cbiAgZCA9PlxuICAgIGRjLnZhbHVlQXQoZC5pbmRleCwgZ2VvanNvbi5maWVsZElkeCk7XG5leHBvcnQgY29uc3QgZmVhdHVyZVJlc29sdmVyID0gKHtnZW9qc29ufTogVHJpcExheWVyQ29sdW1uc0NvbmZpZykgPT4gZ2VvanNvbi5maWVsZElkeDtcbmNvbnN0IGdldFRhYmxlTW9kZVZhbHVlQWNjZXNzb3IgPSBmID0+IHtcbiAgLy8gQ2FsbGVkIGZyb20gZ3B1LWZpbHRlci11dGlscy5nZXRGaWx0ZXJWYWx1ZUFjY2Vzc29yKClcbiAgcmV0dXJuIGZpZWxkID0+IGYucHJvcGVydGllcy52YWx1ZXMubWFwKHYgPT4gZmllbGQudmFsdWVBY2Nlc3Nvcih2KSk7XG59O1xuY29uc3QgZ2V0VGFibGVNb2RlRmllbGRWYWx1ZSA9IChmaWVsZCwgZGF0YSkgPT4ge1xuICBsZXQgcnY7XG4gIGlmICh0eXBlb2YgZGF0YSA9PT0gJ2Z1bmN0aW9uJykge1xuICAgIHJ2ID0gZGF0YShmaWVsZCk7XG4gIH0gZWxzZSB7XG4gICAgcnYgPSBkZWZhdWx0R2V0RmllbGRWYWx1ZShmaWVsZCwgZGF0YSk7XG4gIH1cbiAgcmV0dXJuIHJ2O1xufTtcblxuZXhwb3J0IGNvbnN0IENPTFVNTl9NT0RFX0dFT0pTT04gPSAnZ2VvanNvbic7XG5leHBvcnQgY29uc3QgQ09MVU1OX01PREVfVEFCTEUgPSAndGFibGUnO1xuY29uc3QgU1VQUE9SVEVEX0NPTFVNTl9NT0RFUyA9IFtcbiAge1xuICAgIGtleTogQ09MVU1OX01PREVfR0VPSlNPTixcbiAgICBsYWJlbDogJ0dlb0pTT04nLFxuICAgIHJlcXVpcmVkQ29sdW1uczogWydnZW9qc29uJ11cbiAgfSxcbiAge1xuICAgIGtleTogQ09MVU1OX01PREVfVEFCTEUsXG4gICAgbGFiZWw6ICdUYWJsZSBjb2x1bW5zJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IFsnaWQnLCAnbGF0JywgJ2xuZycsICd0aW1lc3RhbXAnXSxcbiAgICBvcHRpb25hbENvbHVtbnM6IFsnYWx0aXR1ZGUnXVxuICB9XG5dO1xuY29uc3QgREVGQVVMVF9DT0xVTU5fTU9ERSA9IENPTFVNTl9NT0RFX0dFT0pTT047XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRyaXBMYXllciBleHRlbmRzIExheWVyIHtcbiAgZGVjbGFyZSB2aXNDb25maWdTZXR0aW5nczogVHJpcExheWVyVmlzQ29uZmlnU2V0dGluZ3M7XG4gIGRlY2xhcmUgY29uZmlnOiBUcmlwTGF5ZXJDb25maWc7XG4gIGRlY2xhcmUgbWV0YTogVHJpcExheWVyTWV0YTtcbiAgZGVjbGFyZSBkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlIHwgbnVsbDtcblxuICBkYXRhVG9GZWF0dXJlOiBHZW9qc29uRGF0YU1hcHM7XG4gIGRhdGFUb1RpbWVTdGFtcDogYW55W107XG4gIGdldEZlYXR1cmU6IChjb2x1bW5zOiBUcmlwTGF5ZXJDb2x1bW5zQ29uZmlnKSA9PiAoZGF0YUNvbnRhaW5lcjogRGF0YUNvbnRhaW5lckludGVyZmFjZSkgPT4gYW55O1xuICBfbGF5ZXJJbmZvTW9kYWw6IFJlY29yZDxzdHJpbmcsICgpID0+IEpTWC5FbGVtZW50PjtcblxuICBjb25zdHJ1Y3Rvcihwcm9wcykge1xuICAgIHN1cGVyKHByb3BzKTtcblxuICAgIHRoaXMuZGF0YVRvRmVhdHVyZSA9IFtdO1xuICAgIHRoaXMuZGF0YVRvVGltZVN0YW1wID0gW107XG4gICAgdGhpcy5kYXRhQ29udGFpbmVyID0gbnVsbDtcbiAgICB0aGlzLnJlZ2lzdGVyVmlzQ29uZmlnKHRyaXBWaXNDb25maWdzKTtcbiAgICB0aGlzLmdldEZlYXR1cmUgPSBtZW1vaXplKGZlYXR1cmVBY2Nlc3NvciwgZmVhdHVyZVJlc29sdmVyKTtcbiAgICB0aGlzLl9sYXllckluZm9Nb2RhbCA9IHtcbiAgICAgIFtDT0xVTU5fTU9ERV9UQUJMRV06IFRyaXBJbmZvTW9kYWxGYWN0b3J5KENPTFVNTl9NT0RFX1RBQkxFKSxcbiAgICAgIFtDT0xVTU5fTU9ERV9HRU9KU09OXTogVHJpcEluZm9Nb2RhbEZhY3RvcnkoQ09MVU1OX01PREVfR0VPSlNPTilcbiAgICB9O1xuICB9XG5cbiAgZ2V0IHN1cHBvcnRlZENvbHVtbk1vZGVzKCkge1xuICAgIHJldHVybiBTVVBQT1JURURfQ09MVU1OX01PREVTO1xuICB9XG5cbiAgc3RhdGljIGdldCB0eXBlKCk6ICd0cmlwJyB7XG4gICAgcmV0dXJuICd0cmlwJztcbiAgfVxuICBnZXQgdHlwZSgpIHtcbiAgICByZXR1cm4gVHJpcExheWVyLnR5cGU7XG4gIH1cblxuICBnZXQgbmFtZSgpOiAnVHJpcCcge1xuICAgIHJldHVybiAnVHJpcCc7XG4gIH1cblxuICBnZXQgbGF5ZXJJY29uKCkge1xuICAgIHJldHVybiBUcmlwTGF5ZXJJY29uO1xuICB9XG5cbiAgZ2V0IGNvbHVtblBhaXJzKCkge1xuICAgIHJldHVybiB0aGlzLmRlZmF1bHRQb2ludENvbHVtblBhaXJzO1xuICB9XG5cbiAgYWNjZXNzVlNGaWVsZFZhbHVlKCkge1xuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9KU09OKSB7XG4gICAgICByZXR1cm4gZGVmYXVsdEdldEZpZWxkVmFsdWU7XG4gICAgfVxuICAgIHJldHVybiBnZXRUYWJsZU1vZGVGaWVsZFZhbHVlO1xuICB9XG5cbiAgZ2V0IHZpc3VhbENoYW5uZWxzKCkge1xuICAgIGNvbnN0IHZpc3VhbENoYW5uZWxzID0gc3VwZXIudmlzdWFsQ2hhbm5lbHM7XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4udmlzdWFsQ2hhbm5lbHMsXG4gICAgICBjb2xvcjoge1xuICAgICAgICAuLi52aXN1YWxDaGFubmVscy5jb2xvcixcbiAgICAgICAgYWNjZXNzb3I6ICdnZXRDb2xvcicsXG4gICAgICAgIG51bGxWYWx1ZTogdmlzdWFsQ2hhbm5lbHMuY29sb3IubnVsbFZhbHVlLFxuICAgICAgICBnZXRBdHRyaWJ1dGVWYWx1ZTogY29uZmlnID0+IGQgPT4gZC5wcm9wZXJ0aWVzLmxpbmVDb2xvciB8fCBjb25maWcuY29sb3IsXG4gICAgICAgIC8vIHVzZWQgdGhpcyB0byBnZXQgdXBkYXRlVHJpZ2dlcnNcbiAgICAgICAgZGVmYXVsdFZhbHVlOiBjb25maWcgPT4gY29uZmlnLmNvbG9yXG4gICAgICB9LFxuICAgICAgc2l6ZToge1xuICAgICAgICAuLi52aXN1YWxDaGFubmVscy5zaXplLFxuICAgICAgICBwcm9wZXJ0eTogJ3N0cm9rZScsXG4gICAgICAgIGFjY2Vzc29yOiAnZ2V0V2lkdGgnLFxuICAgICAgICBjb25kaXRpb246IGNvbmZpZyA9PiBjb25maWcudmlzQ29uZmlnLnN0cm9rZWQsXG4gICAgICAgIG51bGxWYWx1ZTogMCxcbiAgICAgICAgZ2V0QXR0cmlidXRlVmFsdWU6ICgpID0+IGQgPT4gZC5wcm9wZXJ0aWVzLmxpbmVXaWR0aCB8fCBkZWZhdWx0TGluZVdpZHRoXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIGdldCBhbmltYXRpb25Eb21haW4oKSB7XG4gICAgcmV0dXJuIHRoaXMuY29uZmlnLmFuaW1hdGlvbi5kb21haW47XG4gIH1cblxuICBnZXQgbGF5ZXJJbmZvTW9kYWwoKSB7XG4gICAgcmV0dXJuIHtcbiAgICAgIFtDT0xVTU5fTU9ERV9HRU9KU09OXToge1xuICAgICAgICBpZDogJ2ljb25JbmZvJyxcbiAgICAgICAgdGVtcGxhdGU6IHRoaXMuX2xheWVySW5mb01vZGFsW0NPTFVNTl9NT0RFX0dFT0pTT05dLFxuICAgICAgICBtb2RhbFByb3BzOiB7XG4gICAgICAgICAgdGl0bGU6ICdtb2RhbC50cmlwSW5mby50aXRsZSdcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIFtDT0xVTU5fTU9ERV9UQUJMRV06IHtcbiAgICAgICAgaWQ6ICdpY29uSW5mbycsXG4gICAgICAgIHRlbXBsYXRlOiB0aGlzLl9sYXllckluZm9Nb2RhbFtDT0xVTU5fTU9ERV9UQUJMRV0sXG4gICAgICAgIG1vZGFsUHJvcHM6IHtcbiAgICAgICAgICB0aXRsZTogJ21vZGFsLnRyaXBJbmZvLnRpdGxlVGFibGUnXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgZ2V0UG9zaXRpb25BY2Nlc3NvcihkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbHVtbk1vZGUgPT09IENPTFVNTl9NT0RFX0dFT0pTT04pIHtcbiAgICAgIHJldHVybiB0aGlzLmdldEZlYXR1cmUodGhpcy5jb25maWcuY29sdW1ucykoZGF0YUNvbnRhaW5lcik7XG4gICAgfVxuICAgIHJldHVybiBudWxsO1xuICB9XG5cbiAgc3RhdGljIGZpbmREZWZhdWx0TGF5ZXJQcm9wcyhcbiAgICB7bGFiZWwsIGZpZWxkcyA9IFtdLCBkYXRhQ29udGFpbmVyLCBpZCwgZmllbGRQYWlycyA9IFtdfTogS2VwbGVyVGFibGUsXG4gICAgZm91bmRMYXllcnM/OiBhbnlbXVxuICApIHtcbiAgICBjb25zdCBnZW9qc29uQ29sdW1ucyA9IGZpZWxkc1xuICAgICAgLmZpbHRlcihmID0+IGYudHlwZSA9PT0gJ2dlb2pzb24nIHx8IGYudHlwZSA9PT0gJ2dlb2Fycm93JylcbiAgICAgIC5tYXAoZiA9PiBmLm5hbWUpO1xuXG4gICAgY29uc3QgZGVmYXVsdENvbHVtbnMgPSB7XG4gICAgICBnZW9qc29uOiB1bmlxKFsuLi5HRU9KU09OX0ZJRUxEUy5nZW9qc29uLCAuLi5nZW9qc29uQ29sdW1uc10pXG4gICAgfTtcblxuICAgIGNvbnN0IGdlb0pzb25Db2x1bW5zID0gdGhpcy5maW5kRGVmYXVsdENvbHVtbkZpZWxkKGRlZmF1bHRDb2x1bW5zLCBmaWVsZHMpO1xuXG4gICAgY29uc3QgdHJpcEdlb2pzb25Db2x1bW5zID0gKGdlb0pzb25Db2x1bW5zIHx8IFtdKS5maWx0ZXIoY29sID0+XG4gICAgICBpc1RyaXBHZW9Kc29uRmllbGQoZGF0YUNvbnRhaW5lciwgZmllbGRzW2NvbC5nZW9qc29uLmZpZWxkSWR4XSlcbiAgICApO1xuXG4gICAgaWYgKHRyaXBHZW9qc29uQ29sdW1ucy5sZW5ndGgpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHByb3BzOiB0cmlwR2VvanNvbkNvbHVtbnMubWFwKGNvbHVtbnMgPT4gKHtcbiAgICAgICAgICBsYWJlbDogKHR5cGVvZiBsYWJlbCA9PT0gJ3N0cmluZycgJiYgbGFiZWwucmVwbGFjZSgvXFwuW14vLl0rJC8sICcnKSkgfHwgdGhpcy50eXBlLFxuICAgICAgICAgIGNvbHVtbnMsXG4gICAgICAgICAgaXNWaXNpYmxlOiB0cnVlLFxuICAgICAgICAgIGNvbHVtbk1vZGU6IENPTFVNTl9NT0RFX0dFT0pTT05cbiAgICAgICAgfSkpLFxuXG4gICAgICAgIC8vIGlmIGEgZ2VvanNvbiBsYXllciBpcyBjcmVhdGVkIGZyb20gdGhpcyBjb2x1bW4sIGRlbGV0ZSBpdFxuICAgICAgICBmb3VuZExheWVyczogZm91bmRMYXllcnM/LmZpbHRlcihcbiAgICAgICAgICBwcm9wID0+XG4gICAgICAgICAgICBwcm9wLnR5cGUgIT09ICdnZW9qc29uJyB8fFxuICAgICAgICAgICAgcHJvcC5kYXRhSWQgIT09IGlkIHx8XG4gICAgICAgICAgICAhdHJpcEdlb2pzb25Db2x1bW5zLmZpbmQoYyA9PiBwcm9wLmNvbHVtbnMuZ2VvanNvbi5uYW1lID09PSBjLmdlb2pzb24ubmFtZSlcbiAgICAgICAgKVxuICAgICAgfTtcbiAgICB9XG5cbiAgICAvLyBUcnkgdG8gZGV0ZWN0IHRhYmxlIGNvbHVtbnMgKGlkL2xhdC9sbmcvdGltZXN0YW1wKSBmb3IgdGFibGUgY29sdW1uIG1vZGVcbiAgICAvLyBUaGlzIGFsbG93cyBjcmVhdGluZyB0cmlwIGxheWVycyBmcm9tIHRhYnVsYXIgZGF0YSB3aXRob3V0IEdlb0pTT05cbiAgICBpZiAoZmllbGRQYWlycy5sZW5ndGggJiYgZmllbGRzLmxlbmd0aCkge1xuICAgICAgLy8gRGVmYXVsdCBsYXllciBjb2x1bW5zIGZvciB0YWJsZSBtb2RlXG4gICAgICBjb25zdCBkZWZhdWx0VGFibGVDb2x1bW5zID0ge1xuICAgICAgICBpZDoge3ZhbHVlOiBudWxsLCBmaWVsZElkeDogLTF9LFxuICAgICAgICBsYXQ6IHt2YWx1ZTogbnVsbCwgZmllbGRJZHg6IC0xfSxcbiAgICAgICAgbG5nOiB7dmFsdWU6IG51bGwsIGZpZWxkSWR4OiAtMX0sXG4gICAgICAgIHRpbWVzdGFtcDoge3ZhbHVlOiBudWxsLCBmaWVsZElkeDogLTF9LFxuICAgICAgICBhbHRpdHVkZToge3ZhbHVlOiBudWxsLCBmaWVsZElkeDogLTEsIG9wdGlvbmFsOiB0cnVlfSxcbiAgICAgICAgZ2VvanNvbjoge3ZhbHVlOiBudWxsLCBmaWVsZElkeDogLTF9XG4gICAgICB9O1xuXG4gICAgICBjb25zdCB0YWJsZUNvbHVtbnMgPSBkZXRlY3RUYWJsZUNvbHVtbnMoXG4gICAgICAgIHtmaWVsZHMsIGZpZWxkUGFpcnN9IGFzIEtlcGxlclRhYmxlLFxuICAgICAgICBkZWZhdWx0VGFibGVDb2x1bW5zLFxuICAgICAgICAndGltZXN0YW1wJ1xuICAgICAgKTtcblxuICAgICAgaWYgKHRhYmxlQ29sdW1ucykge1xuICAgICAgICAvLyBGb3VuZCByZXF1aXJlZCBjb2x1bW5zIGZvciB0YWJsZSBtb2RlXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgcHJvcHM6IFtcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgbGFiZWw6XG4gICAgICAgICAgICAgICAgdGFibGVDb2x1bW5zLmxhYmVsIHx8XG4gICAgICAgICAgICAgICAgKHR5cGVvZiBsYWJlbCA9PT0gJ3N0cmluZycgJiYgbGFiZWwucmVwbGFjZSgvXFwuW14vLl0rJC8sICcnKSkgfHxcbiAgICAgICAgICAgICAgICB0aGlzLnR5cGUsXG4gICAgICAgICAgICAgIGNvbHVtbnM6IHRhYmxlQ29sdW1ucy5jb2x1bW5zLFxuICAgICAgICAgICAgICBpc1Zpc2libGU6IHRydWUsXG4gICAgICAgICAgICAgIGNvbHVtbk1vZGU6IENPTFVNTl9NT0RFX1RBQkxFXG4gICAgICAgICAgICB9XG4gICAgICAgICAgXSxcbiAgICAgICAgICBmb3VuZExheWVyc1xuICAgICAgICB9O1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiB7cHJvcHM6IFtdfTtcbiAgfVxuXG4gIGdldERlZmF1bHRMYXllckNvbmZpZyhwcm9wcykge1xuICAgIHJldHVybiB7XG4gICAgICAuLi5zdXBlci5nZXREZWZhdWx0TGF5ZXJDb25maWcocHJvcHMpLFxuICAgICAgY29sdW1uTW9kZTogcHJvcHM/LmNvbHVtbk1vZGUgPz8gREVGQVVMVF9DT0xVTU5fTU9ERSxcbiAgICAgIGFuaW1hdGlvbjoge1xuICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICBkb21haW46IG51bGxcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgZ2V0SG92ZXJEYXRhKG9iamVjdCwgZGF0YUNvbnRhaW5lcjogRGF0YUNvbnRhaW5lckludGVyZmFjZSwgZmllbGRzLCBhbmltYXRpb25Db25maWcpIHtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfR0VPSlNPTikge1xuICAgICAgLy8gaW5kZXggZm9yIGRhdGFDb250YWluZXIgaXMgc2F2ZWQgdG8gZmVhdHVyZS5wcm9wZXJ0aWVzXG4gICAgICByZXR1cm4gZGF0YUNvbnRhaW5lci5yb3cob2JqZWN0LnByb3BlcnRpZXMuaW5kZXgpO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5fZmluZENvbHVtbk1vZGVEYXR1bUZvckZlYXR1cmUob2JqZWN0LnByb3BlcnRpZXMuaW5kZXgsIGFuaW1hdGlvbkNvbmZpZy5jdXJyZW50VGltZSlcbiAgICAgID8uZGF0dW07XG4gIH1cblxuICBjYWxjdWxhdGVEYXRhQXR0cmlidXRlKGRhdGFzZXQ6IEtlcGxlclRhYmxlKSB7XG4gICAgc3dpdGNoICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlKSB7XG4gICAgICBjYXNlIENPTFVNTl9NT0RFX0dFT0pTT046IHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICBkYXRhc2V0LmZpbHRlcmVkSW5kZXhcbiAgICAgICAgICAgIC5tYXAoaSA9PiB0aGlzLmRhdGFUb0ZlYXR1cmVbaV0pXG4gICAgICAgICAgICAvLyBUT0RPIGQgY2FuIGJlIEJpbmFyeUZlYXR1cmVDb2xsZWN0aW9uLCBmaXggbG9naWNcbiAgICAgICAgICAgIC5maWx0ZXIoZCA9PiBkICYmIChkIGFzIGFueSkuZ2VvbWV0cnk/LnR5cGUgPT09ICdMaW5lU3RyaW5nJylcbiAgICAgICAgKTtcbiAgICAgIH1cblxuICAgICAgY2FzZSBDT0xVTU5fTU9ERV9UQUJMRTpcbiAgICAgICAgcmV0dXJuIGFwcGx5RmlsdGVyc1RvVGFibGVDb2x1bW5zKGRhdGFzZXQsIHRoaXMuZGF0YVRvRmVhdHVyZSk7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gIH1cblxuICBmb3JtYXRMYXllckRhdGEoZGF0YXNldHM6IERhdGFzZXRzLCBvbGRMYXllckRhdGEpIHtcbiAgICBpZiAodGhpcy5jb25maWcuZGF0YUlkID09PSBudWxsKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuICAgIC8vIHRvLWRvOiBwYXJzZSBzZWdtZW50IGZyb20gZGF0YUNvbnRhaW5lclxuXG4gICAgY29uc3Qge2RhdGFDb250YWluZXIsIGdwdUZpbHRlcn0gPSBkYXRhc2V0c1t0aGlzLmNvbmZpZy5kYXRhSWRdO1xuICAgIGNvbnN0IHtkYXRhfSA9IHRoaXMudXBkYXRlRGF0YShkYXRhc2V0cywgb2xkTGF5ZXJEYXRhKTtcblxuICAgIGxldCB2YWx1ZUFjY2Vzc29yO1xuICAgIGxldCBkYXRhQWNjZXNzb3I7XG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbHVtbk1vZGUgPT09IENPTFVNTl9NT0RFX0dFT0pTT04pIHtcbiAgICAgIHZhbHVlQWNjZXNzb3IgPSAoZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2UsIGYsIGZpZWxkSW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgICByZXR1cm4gZGMudmFsdWVBdChmLnByb3BlcnRpZXMuaW5kZXgsIGZpZWxkSW5kZXgpO1xuICAgICAgfTtcbiAgICAgIC8vIEZvciBHRU9KU09OIG1vZGUsIHByb3BlcnRpZXMuaW5kZXggaXMgdGhlIHJvdyBpbmRleCBpbiB0aGUgZGF0YSBjb250YWluZXJcbiAgICAgIGRhdGFBY2Nlc3NvciA9ICgpID0+IGQgPT4gKHtpbmRleDogZC5wcm9wZXJ0aWVzLmluZGV4fSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHZhbHVlQWNjZXNzb3IgPSBnZXRUYWJsZU1vZGVWYWx1ZUFjY2Vzc29yO1xuICAgICAgLy8gRm9yIFRBQkxFIG1vZGUsIHByb3BlcnRpZXMuaW5kZXggaXMgdGhlIGZlYXR1cmUgaW5kZXggKG5vdCByb3cgaW5kZXgpLlxuICAgICAgLy8gVXNlIHRoZSBmaXJzdCByb3cgZnJvbSBwcm9wZXJ0aWVzLnZhbHVlcyB0byBnZXQgZmllbGQgdmFsdWVzIGZvciBjb2xvci9zaXplLlxuICAgICAgZGF0YUFjY2Vzc29yID0gKCkgPT4gZCA9PiBkLnByb3BlcnRpZXMudmFsdWVzWzBdO1xuICAgIH1cbiAgICBjb25zdCBpbmRleEFjY2Vzc29yID0gZiA9PiBmLnByb3BlcnRpZXMuaW5kZXg7XG4gICAgY29uc3QgYWNjZXNzb3JzID0gdGhpcy5nZXRBdHRyaWJ1dGVBY2Nlc3NvcnMoe2RhdGFBY2Nlc3NvciwgZGF0YUNvbnRhaW5lcn0pO1xuICAgIGNvbnN0IGdldEZpbHRlclZhbHVlID0gZ3B1RmlsdGVyLmZpbHRlclZhbHVlQWNjZXNzb3IoZGF0YUNvbnRhaW5lcikoXG4gICAgICBpbmRleEFjY2Vzc29yLFxuICAgICAgdmFsdWVBY2Nlc3NvclxuICAgICk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YSxcbiAgICAgIGdldEZpbHRlclZhbHVlLFxuICAgICAgZ2V0UGF0aDogZCA9PiBkLmdlb21ldHJ5LmNvb3JkaW5hdGVzLFxuICAgICAgZ2V0VGltZXN0YW1wczogZCA9PiB0aGlzLmRhdGFUb1RpbWVTdGFtcFtkLnByb3BlcnRpZXMuaW5kZXhdLFxuICAgICAgLi4uYWNjZXNzb3JzXG4gICAgfTtcbiAgfVxuXG4gIHVwZGF0ZUFuaW1hdGlvbkRvbWFpbihkb21haW4pIHtcbiAgICB0aGlzLnVwZGF0ZUxheWVyQ29uZmlnKHtcbiAgICAgIGFuaW1hdGlvbjoge1xuICAgICAgICAuLi50aGlzLmNvbmZpZy5hbmltYXRpb24sXG4gICAgICAgIGRvbWFpblxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgdXBkYXRlTGF5ZXJNZXRhKGRhdGFzZXQ6IEtlcGxlclRhYmxlKSB7XG4gICAgY29uc3Qge2RhdGFDb250YWluZXJ9ID0gZGF0YXNldDtcbiAgICBsZXQgZ2V0RmVhdHVyZTtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfR0VPSlNPTikge1xuICAgICAgZ2V0RmVhdHVyZSA9IHRoaXMuZ2V0UG9zaXRpb25BY2Nlc3NvcihkYXRhQ29udGFpbmVyKTtcbiAgICAgIGlmIChnZXRGZWF0dXJlID09PSB0aGlzLm1ldGEuZ2V0RmVhdHVyZSkge1xuICAgICAgICAvLyBUT0RPOiByZXZpc2l0IHRoaXMgYWZ0ZXIgZ3B1IGZpbHRlcmluZ1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBnZXRHZW9qc29uRGF0YU1hcHMoZGF0YUNvbnRhaW5lciwgZ2V0RmVhdHVyZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZGF0YUNvbnRhaW5lciA9IGRhdGFDb250YWluZXI7XG4gICAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBncm91cENvbHVtbnNBc0dlb0pzb24oZGF0YUNvbnRhaW5lciwgdGhpcy5jb25maWcuY29sdW1ucywgJ3RpbWVzdGFtcCcpO1xuICAgIH1cblxuICAgIGNvbnN0IHtkYXRhVG9UaW1lU3RhbXAsIGFuaW1hdGlvbkRvbWFpbn0gPSBwYXJzZVRyaXBHZW9Kc29uVGltZXN0YW1wKHRoaXMuZGF0YVRvRmVhdHVyZSk7XG5cbiAgICB0aGlzLmRhdGFUb1RpbWVTdGFtcCA9IGRhdGFUb1RpbWVTdGFtcDtcbiAgICB0aGlzLnVwZGF0ZUFuaW1hdGlvbkRvbWFpbihhbmltYXRpb25Eb21haW4pO1xuXG4gICAgLy8gZ2V0IGJvdW5kcyBmcm9tIGZlYXR1cmVzXG4gICAgY29uc3QgYm91bmRzID0gZ2V0R2VvanNvbkJvdW5kcyh0aGlzLmRhdGFUb0ZlYXR1cmUpO1xuXG4gICAgLy8ga2VlcCBhIHJlY29yZCBvZiB3aGF0IHR5cGUgb2YgZ2VvbWV0cnkgdGhlIGNvbGxlY3Rpb24gaGFzXG4gICAgY29uc3QgZmVhdHVyZVR5cGVzID0gZ2V0R2VvanNvbkZlYXR1cmVUeXBlcyh0aGlzLmRhdGFUb0ZlYXR1cmUpO1xuXG4gICAgdGhpcy51cGRhdGVNZXRhKHtib3VuZHMsIGZlYXR1cmVUeXBlcywgZ2V0RmVhdHVyZX0pO1xuICB9XG5cbiAgc2V0SW5pdGlhbExheWVyQ29uZmlnKGRhdGFzZXQpIHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lcn0gPSBkYXRhc2V0O1xuICAgIGlmICghZGF0YUNvbnRhaW5lci5udW1Sb3dzKCkpIHtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8vIGRlZmVmYXVsdExheWVyUHJvcHMgd2lsbCBhdXRvbWF0aWNhbGx5IGZpbmQgZ2VvanNvbiBjb2x1bW5cbiAgICAvLyBpZiBub3QgZm91bmQsIHdlIHRyeSB0byBzZXQgaXQgdG8gaWQgLyBsYXQgL2xuZyAvdHNcbiAgICBpZiAoIXRoaXMuY29uZmlnLmNvbHVtbnMuZ2VvanNvbi52YWx1ZSkge1xuICAgICAgLy8gZmluZCBjb2x1bW5zIGZyb20gbGF0LCBsbmcsIGlkLCBhbmQgdHNcbiAgICAgIGNvbnN0IGNvbHVtbkNvbmZpZyA9IGRldGVjdFRhYmxlQ29sdW1ucyhkYXRhc2V0LCB0aGlzLmNvbmZpZy5jb2x1bW5zKTtcbiAgICAgIGlmIChjb2x1bW5Db25maWcpIHtcbiAgICAgICAgdGhpcy51cGRhdGVMYXllckNvbmZpZyh7XG4gICAgICAgICAgLi4uY29sdW1uQ29uZmlnLFxuICAgICAgICAgIGNvbHVtbk1vZGU6IENPTFVNTl9NT0RFX1RBQkxFXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy51cGRhdGVMYXllck1ldGEoZGF0YXNldCk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICByZW5kZXJMYXllcihvcHRzKSB7XG4gICAgY29uc3Qge2RhdGEsIGdwdUZpbHRlciwgbWFwU3RhdGUsIGFuaW1hdGlvbkNvbmZpZ30gPSBvcHRzO1xuICAgIGNvbnN0IHt2aXNDb25maWd9ID0gdGhpcy5jb25maWc7XG4gICAgY29uc3Qgem9vbUZhY3RvciA9IHRoaXMuZ2V0Wm9vbUZhY3RvcihtYXBTdGF0ZSk7XG4gICAgY29uc3QgaXNWYWxpZFRpbWUgPVxuICAgICAgYW5pbWF0aW9uQ29uZmlnICYmXG4gICAgICBBcnJheS5pc0FycmF5KGFuaW1hdGlvbkNvbmZpZy5kb21haW4pICYmXG4gICAgICBhbmltYXRpb25Db25maWcuZG9tYWluLmV2ZXJ5KE51bWJlci5pc0Zpbml0ZSkgJiZcbiAgICAgIE51bWJlci5pc0Zpbml0ZShhbmltYXRpb25Db25maWcuY3VycmVudFRpbWUpO1xuXG4gICAgaWYgKCFpc1ZhbGlkVGltZSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cblxuICAgIGNvbnN0IGRvbWFpbjAgPSBhbmltYXRpb25Db25maWcuZG9tYWluPy5bMF07XG5cbiAgICBjb25zdCBncHVGaWx0ZXJVcGRhdGVUcmlnZ2VycyA9IHtnZXRGaWx0ZXJWYWx1ZTogZ3B1RmlsdGVyLmZpbHRlclZhbHVlVXBkYXRlVHJpZ2dlcnN9O1xuICAgIGNvbnN0IHVwZGF0ZVRyaWdnZXJzID0ge1xuICAgICAgLi4udGhpcy5nZXRWaXN1YWxDaGFubmVsVXBkYXRlVHJpZ2dlcnMoKSxcbiAgICAgIGdldFRpbWVzdGFtcHM6IHtcbiAgICAgICAgY29sdW1uczogdGhpcy5jb25maWcuY29sdW1ucyxcbiAgICAgICAgZG9tYWluMFxuICAgICAgfSxcbiAgICAgIC4uLmdwdUZpbHRlclVwZGF0ZVRyaWdnZXJzXG4gICAgfTtcbiAgICBjb25zdCBkZWZhdWx0TGF5ZXJQcm9wcyA9IHRoaXMuZ2V0RGVmYXVsdERlY2tMYXllclByb3BzKG9wdHMpO1xuXG4gICAgY29uc3QgYmlsbGJvYXJkV2lkdGhGYWN0b3IgPSB2aXNDb25maWcuYmlsbGJvYXJkID8gUFJPSkVDVEVEX1BJWEVMX1NJWkVfTVVMVElQTElFUiA6IDE7XG5cbiAgICBjb25zdCBsYXllclByb3BzID0ge1xuICAgICAgLi4uZGVmYXVsdExheWVyUHJvcHMsXG4gICAgICAuLi5kYXRhLFxuICAgICAgZ2V0VGltZXN0YW1wczogZCA9PiAoZGF0YS5nZXRUaW1lc3RhbXBzKGQpIHx8IFtdKS5tYXAodHMgPT4gdHMgLSBkb21haW4wKSxcbiAgICAgIHdpZHRoU2NhbGU6IHZpc0NvbmZpZy50aGlja25lc3MgKiB6b29tRmFjdG9yICogem9vbUZhY3RvclZhbHVlICogYmlsbGJvYXJkV2lkdGhGYWN0b3IsXG4gICAgICBjYXBSb3VuZGVkOiB0cnVlLFxuICAgICAgam9pbnRSb3VuZGVkOiB0cnVlLFxuICAgICAgd3JhcExvbmdpdHVkZTogZmFsc2UsXG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGRlcHRoVGVzdDogbWFwU3RhdGUuZHJhZ1JvdGF0ZSxcbiAgICAgICAgZGVwdGhNYXNrOiBmYWxzZVxuICAgICAgfSxcbiAgICAgIHRyYWlsTGVuZ3RoOiB2aXNDb25maWcudHJhaWxMZW5ndGggKiAxMDAwLFxuICAgICAgZmFkZVRyYWlsOiB2aXNDb25maWcuZmFkZVRyYWlsLFxuICAgICAgYmlsbGJvYXJkOiB2aXNDb25maWcuYmlsbGJvYXJkLFxuICAgICAgLy8gVE9ETzogZ2l1c2VwcGUgdGhpcyB2YWx1ZXMgYmVjb21lcyBuZWdhdGl2ZVxuICAgICAgY3VycmVudFRpbWU6IGFuaW1hdGlvbkNvbmZpZy5jdXJyZW50VGltZSAtIGRvbWFpbjAsXG4gICAgICB1cGRhdGVUcmlnZ2VycyxcbiAgICAgIGlkOiBgJHtkZWZhdWx0TGF5ZXJQcm9wcy5pZH0ke21hcFN0YXRlLmdsb2JlPy5lbmFibGVkID8gJy1nbG9iZScgOiAnJ31gXG4gICAgfTtcbiAgICByZXR1cm4gW25ldyBEZWNrR0xUcmlwc0xheWVyKGxheWVyUHJvcHMpXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBjb29yZGluYXRlcyBhbmQgZGF0dW0gYXQgdGhlIGN1cnJlbnQgYW5pbWF0aW9uIHRpbWUgYnkgdGhlIHNwZWNpZmllZCBmZWF0dXJlIGluZGV4LlxuICAgKiBAcGFyYW0gZmVhdHVyZUluZGV4XG4gICAqIEBwYXJhbSB0aW1lXG4gICAqIEByZXR1cm5zIHt7ZGF0dW06IChudWxsfHN0cmluZ3wqKSwgaWR4OiAqLCBjb29yZHN9fHtkYXR1bTogbnVsbCwgaWR4OiBudW1iZXIsIGNvb3JkczogbnVsbH19XG4gICAqL1xuICBwcml2YXRlIF9maW5kQ29sdW1uTW9kZURhdHVtRm9yRmVhdHVyZShcbiAgICBmZWF0dXJlSW5kZXg6IG51bWJlcixcbiAgICB0aW1lOiBudW1iZXJcbiAgKToge1xuICAgIGlkeDogbnVtYmVyO1xuICAgIGNvb3JkczogbnVtYmVyW10gfCBudWxsO1xuICAgIGRhdHVtOiBhbnk7XG4gIH0ge1xuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9UQUJMRSkge1xuICAgICAgY29uc3Qgb2JqZWN0ID0gdGhpcy5kYXRhVG9GZWF0dXJlW2ZlYXR1cmVJbmRleF07XG4gICAgICBjb25zdCBpZHggPSBiaXNlY3RSaWdodCh0aGlzLmRhdGFUb1RpbWVTdGFtcFtmZWF0d