kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
503 lines (497 loc) • 67.9 kB
JavaScript
"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 _lodash = _interopRequireDefault(require("lodash.memoize"));
var _lodash2 = _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, _lodash["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;
if (this.config.columnMode === COLUMN_MODE_GEOJSON) {
valueAccessor = function valueAccessor(dc, f, fieldIndex) {
return dc.valueAt(f.properties.index, fieldIndex);
};
} else {
valueAccessor = getTableModeValueAccessor;
}
var indexAccessor = function indexAccessor(f) {
return f.properties.index;
};
var dataAccessor = function dataAccessor() {
return function (d) {
return {
index: d.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;
var geojsonColumns = fields.filter(function (f) {
return f.type === 'geojson';
}).map(function (f) {
return f.name;
});
var defaultColumns = {
geojson: (0, _lodash2["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;
});
})
};
}
return {
props: []
};
}
}]);
}(_baseLayer["default"]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9kYXNoIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfbG9kYXNoMiIsIl9iYXNlTGF5ZXIiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsIl9nZW9MYXllcnMiLCJfY29uc3RhbnRzIiwiX3RyaXBMYXllckljb24iLCJfZ2VvanNvblV0aWxzIiwiX3RyaXBVdGlscyIsIl90cmlwSW5mb01vZGFsIiwiX2QzQXJyYXkiLCJfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUiLCJlIiwiV2Vha01hcCIsInIiLCJ0IiwiX19lc01vZHVsZSIsIl90eXBlb2YiLCJoYXMiLCJnZXQiLCJuIiwiX19wcm90b19fIiwiYSIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwidSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsImkiLCJzZXQiLCJvd25LZXlzIiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsIm8iLCJmaWx0ZXIiLCJlbnVtZXJhYmxlIiwicHVzaCIsImFwcGx5IiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJfY2FsbFN1cGVyIiwiX2dldFByb3RvdHlwZU9mMiIsIl9wb3NzaWJsZUNvbnN0cnVjdG9yUmV0dXJuMiIsIl9pc05hdGl2ZVJlZmxlY3RDb25zdHJ1Y3QiLCJSZWZsZWN0IiwiY29uc3RydWN0IiwiY29uc3RydWN0b3IiLCJCb29sZWFuIiwicHJvdG90eXBlIiwidmFsdWVPZiIsIl9zdXBlclByb3BHZXQiLCJwIiwiX2dldDIiLCJ6b29tRmFjdG9yVmFsdWUiLCJkZWZhdWx0VGhpY2tuZXNzIiwiZXhwb3J0cyIsImRlZmF1bHRMaW5lV2lkdGgiLCJ0cmlwVmlzQ29uZmlncyIsIm9wYWNpdHkiLCJ0aGlja25lc3MiLCJ0eXBlIiwiZGVmYXVsdFZhbHVlIiwibGFiZWwiLCJpc1JhbmdlZCIsInJhbmdlIiwic3RlcCIsImdyb3VwIiwicHJvcGVydHkiLCJjb2xvclJhbmdlIiwidHJhaWxMZW5ndGgiLCJmYWRlVHJhaWwiLCJiaWxsYm9hcmQiLCJzaXplUmFuZ2UiLCJmZWF0dXJlQWNjZXNzb3IiLCJfcmVmIiwiZ2VvanNvbiIsImRjIiwiZCIsInZhbHVlQXQiLCJpbmRleCIsImZpZWxkSWR4IiwiZmVhdHVyZVJlc29sdmVyIiwiX3JlZjIiLCJnZXRUYWJsZU1vZGVWYWx1ZUFjY2Vzc29yIiwiZiIsImZpZWxkIiwicHJvcGVydGllcyIsInZhbHVlcyIsIm1hcCIsInYiLCJ2YWx1ZUFjY2Vzc29yIiwiZ2V0VGFibGVNb2RlRmllbGRWYWx1ZSIsImRhdGEiLCJydiIsImRlZmF1bHRHZXRGaWVsZFZhbHVlIiwiQ09MVU1OX01PREVfR0VPSlNPTiIsIkNPTFVNTl9NT0RFX1RBQkxFIiwiU1VQUE9SVEVEX0NPTFVNTl9NT0RFUyIsImtleSIsInJlcXVpcmVkQ29sdW1ucyIsIm9wdGlvbmFsQ29sdW1ucyIsIkRFRkFVTFRfQ09MVU1OX01PREUiLCJUcmlwTGF5ZXIiLCJfTGF5ZXIiLCJwcm9wcyIsIl90aGlzIiwiX2NsYXNzQ2FsbENoZWNrMiIsImRhdGFUb0ZlYXR1cmUiLCJkYXRhVG9UaW1lU3RhbXAiLCJkYXRhQ29udGFpbmVyIiwicmVnaXN0ZXJWaXNDb25maWciLCJnZXRGZWF0dXJlIiwibWVtb2l6ZSIsIl9sYXllckluZm9Nb2RhbCIsIlRyaXBJbmZvTW9kYWxGYWN0b3J5IiwiX2luaGVyaXRzMiIsIl9jcmVhdGVDbGFzczIiLCJUcmlwTGF5ZXJJY29uIiwiZGVmYXVsdFBvaW50Q29sdW1uUGFpcnMiLCJ2YWx1ZSIsImFjY2Vzc1ZTRmllbGRWYWx1ZSIsImNvbmZpZyIsImNvbHVtbk1vZGUiLCJ2aXN1YWxDaGFubmVscyIsImNvbG9yIiwiYWNjZXNzb3IiLCJudWxsVmFsdWUiLCJnZXRBdHRyaWJ1dGVWYWx1ZSIsImxpbmVDb2xvciIsInNpemUiLCJjb25kaXRpb24iLCJ2aXNDb25maWciLCJzdHJva2VkIiwibGluZVdpZHRoIiwiYW5pbWF0aW9uIiwiZG9tYWluIiwiaWQiLCJ0ZW1wbGF0ZSIsIm1vZGFsUHJvcHMiLCJ0aXRsZSIsImdldFBvc2l0aW9uQWNjZXNzb3IiLCJjb2x1bW5zIiwiZ2V0RGVmYXVsdExheWVyQ29uZmlnIiwiX3Byb3BzJGNvbHVtbk1vZGUiLCJlbmFibGVkIiwiZ2V0SG92ZXJEYXRhIiwib2JqZWN0IiwiZmllbGRzIiwiYW5pbWF0aW9uQ29uZmlnIiwiX3RoaXMkX2ZpbmRDb2x1bW5Nb2RlIiwicm93IiwiX2ZpbmRDb2x1bW5Nb2RlRGF0dW1Gb3JGZWF0dXJlIiwiY3VycmVudFRpbWUiLCJkYXR1bSIsImNhbGN1bGF0ZURhdGFBdHRyaWJ1dGUiLCJkYXRhc2V0IiwiX3RoaXMyIiwiZmlsdGVyZWRJbmRleCIsIl9nZW9tZXRyeSIsImdlb21ldHJ5IiwiYXBwbHlGaWx0ZXJzVG9UYWJsZUNvbHVtbnMiLCJmb3JtYXRMYXllckRhdGEiLCJkYXRhc2V0cyIsIm9sZExheWVyRGF0YSIsIl90aGlzMyIsImRhdGFJZCIsIl9kYXRhc2V0cyR0aGlzJGNvbmZpZyIsImdwdUZpbHRlciIsIl90aGlzJHVwZGF0ZURhdGEiLCJ1cGRhdGVEYXRhIiwiZmllbGRJbmRleCIsImluZGV4QWNjZXNzb3IiLCJkYXRhQWNjZXNzb3IiLCJhY2Nlc3NvcnMiLCJnZXRBdHRyaWJ1dGVBY2Nlc3NvcnMiLCJnZXRGaWx0ZXJWYWx1ZSIsImZpbHRlclZhbHVlQWNjZXNzb3IiLCJnZXRQYXRoIiwiY29vcmRpbmF0ZXMiLCJnZXRUaW1lc3RhbXBzIiwidXBkYXRlQW5pbWF0aW9uRG9tYWluIiwidXBkYXRlTGF5ZXJDb25maWciLCJ1cGRhdGVMYXllck1ldGEiLCJtZXRhIiwiZ2V0R2VvanNvbkRhdGFNYXBzIiwiZ3JvdXBDb2x1bW5zQXNHZW9Kc29uIiwiX3BhcnNlVHJpcEdlb0pzb25UaW1lIiwicGFyc2VUcmlwR2VvSnNvblRpbWVzdGFtcCIsImFuaW1hdGlvbkRvbWFpbiIsImJvdW5kcyIsImdldEdlb2pzb25Cb3VuZHMiLCJmZWF0dXJlVHlwZXMiLCJnZXRHZW9qc29uRmVhdHVyZVR5cGVzIiwidXBkYXRlTWV0YSIsInNldEluaXRpYWxMYXllckNvbmZpZyIsIm51bVJvd3MiLCJjb2x1bW5Db25maWciLCJkZXRlY3RUYWJsZUNvbHVtbnMiLCJyZW5kZXJMYXllciIsIm9wdHMiLCJfYW5pbWF0aW9uQ29uZmlnJGRvbWEiLCJfbWFwU3RhdGUkZ2xvYmUiLCJtYXBTdGF0ZSIsInpvb21GYWN0b3IiLCJnZXRab29tRmFjdG9yIiwiaXNWYWxpZFRpbWUiLCJBcnJheSIsImlzQXJyYXkiLCJldmVyeSIsIk51bWJlciIsImlzRmluaXRlIiwiZG9tYWluMCIsImdwdUZpbHRlclVwZGF0ZVRyaWdnZXJzIiwiZmlsdGVyVmFsdWVVcGRhdGVUcmlnZ2VycyIsInVwZGF0ZVRyaWdnZXJzIiwiZ2V0VmlzdWFsQ2hhbm5lbFVwZGF0ZVRyaWdnZXJzIiwiZGVmYXVsdExheWVyUHJvcHMiLCJnZXREZWZhdWx0RGVja0xheWVyUHJvcHMiLCJiaWxsYm9hcmRXaWR0aEZhY3RvciIsIlBST0pFQ1RFRF9QSVhFTF9TSVpFX01VTFRJUExJRVIiLCJsYXllclByb3BzIiwidHMiLCJ3aWR0aFNjYWxlIiwiY2FwUm91bmRlZCIsImpvaW50Um91bmRlZCIsIndyYXBMb25naXR1ZGUiLCJwYXJhbWV0ZXJzIiwiZGVwdGhUZXN0IiwiZHJhZ1JvdGF0ZSIsImRlcHRoTWFzayIsImNvbmNhdCIsImdsb2JlIiwiRGVja0dMVHJpcHNMYXllciIsImZlYXR1cmVJbmRleCIsInRpbWUiLCJpZHgiLCJiaXNlY3RSaWdodCIsIl9yZWY0IiwiY29vcmRzIiwiZmluZERlZmF1bHRMYXllclByb3BzIiwiX3JlZjUiLCJmb3VuZExheWVycyIsIl90aGlzNCIsIl9yZWY1JGZpZWxkcyIsImdlb2pzb25Db2x1bW5zIiwibmFtZSIsImRlZmF1bHRDb2x1bW5zIiwidW5pcSIsIl90b0NvbnN1bWFibGVBcnJheTIiLCJHRU9KU09OX0ZJRUxEUyIsImdlb0pzb25Db2x1bW5zIiwiZmluZERlZmF1bHRDb2x1bW5GaWVsZCIsInRyaXBHZW9qc29uQ29sdW1ucyIsImNvbCIsImlzVHJpcEdlb0pzb25GaWVsZCIsInJlcGxhY2UiLCJpc1Zpc2libGUiLCJwcm9wIiwiZmluZCIsImMiLCJMYXllciJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90cmlwLWxheWVyL3RyaXAtbGF5ZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuaW1wb3J0IG1lbW9pemUgZnJvbSAnbG9kYXNoLm1lbW9pemUnO1xuaW1wb3J0IHVuaXEgZnJvbSAnbG9kYXNoLnVuaXEnO1xuaW1wb3J0IExheWVyLCB7TGF5ZXJCYXNlQ29uZmlnLCBkZWZhdWx0R2V0RmllbGRWYWx1ZX0gZnJvbSAnLi4vYmFzZS1sYXllcic7XG5pbXBvcnQge1RyaXBzTGF5ZXIgYXMgRGVja0dMVHJpcHNMYXllcn0gZnJvbSAnQGRlY2suZ2wvZ2VvLWxheWVycyc7XG5cbmltcG9ydCB7R0VPSlNPTl9GSUVMRFMsIFBST0pFQ1RFRF9QSVhFTF9TSVpFX01VTFRJUExJRVJ9IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcbmltcG9ydCBUcmlwTGF5ZXJJY29uIGZyb20gJy4vdHJpcC1sYXllci1pY29uJztcblxuaW1wb3J0IHtcbiAgZ2V0R2VvanNvbkRhdGFNYXBzLFxuICBnZXRHZW9qc29uQm91bmRzLFxuICBnZXRHZW9qc29uRmVhdHVyZVR5cGVzLFxuICBHZW9qc29uRGF0YU1hcHMsXG4gIGRldGVjdFRhYmxlQ29sdW1ucyxcbiAgZ3JvdXBDb2x1bW5zQXNHZW9Kc29uLFxuICBhcHBseUZpbHRlcnNUb1RhYmxlQ29sdW1uc1xufSBmcm9tICcuLi9nZW9qc29uLWxheWVyL2dlb2pzb24tdXRpbHMnO1xuXG5pbXBvcnQge2lzVHJpcEdlb0pzb25GaWVsZCwgcGFyc2VUcmlwR2VvSnNvblRpbWVzdGFtcH0gZnJvbSAnLi90cmlwLXV0aWxzJztcbmltcG9ydCBUcmlwSW5mb01vZGFsRmFjdG9yeSBmcm9tICcuL3RyaXAtaW5mby1tb2RhbCc7XG5pbXBvcnQge2Jpc2VjdFJpZ2h0fSBmcm9tICdkMy1hcnJheSc7XG5pbXBvcnQge1xuICBDb2xvclJhbmdlLFxuICBNZXJnZSxcbiAgVmlzQ29uZmlnQ29sb3JSYW5nZSxcbiAgVmlzQ29uZmlnTnVtYmVyLFxuICBWaXNDb25maWdSYW5nZSxcbiAgTGF5ZXJDb2x1bW5cbn0gZnJvbSAnQGtlcGxlci5nbC90eXBlcyc7XG5pbXBvcnQge2RlZmF1bHQgYXMgS2VwbGVyVGFibGUsIERhdGFzZXRzfSBmcm9tICdAa2VwbGVyLmdsL3RhYmxlJztcbmltcG9ydCB7RGF0YUNvbnRhaW5lckludGVyZmFjZX0gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5cbmV4cG9ydCB0eXBlIFRyaXBMYXllclZpc0NvbmZpZ1NldHRpbmdzID0ge1xuICBvcGFjaXR5OiBWaXNDb25maWdOdW1iZXI7XG4gIHRoaWNrbmVzczogVmlzQ29uZmlnTnVtYmVyO1xuICBjb2xvclJhbmdlOiBWaXNDb25maWdDb2xvclJhbmdlO1xuICB0cmFpbExlbmd0aDogVmlzQ29uZmlnTnVtYmVyO1xuICBzaXplUmFuZ2U6IFZpc0NvbmZpZ1JhbmdlO1xufTtcblxuZXhwb3J0IHR5cGUgVHJpcExheWVyQ29sdW1uc0NvbmZpZyA9IHtcbiAgZ2VvanNvbjogTGF5ZXJDb2x1bW47XG59O1xuXG5leHBvcnQgdHlwZSBUcmlwTGF5ZXJWaXNDb25maWcgPSB7XG4gIG9wYWNpdHk6IG51bWJlcjtcbiAgdGhpY2tuZXNzOiBudW1iZXI7XG4gIGNvbG9yUmFuZ2U6IENvbG9yUmFuZ2U7XG4gIHRyYWlsTGVuZ3RoOiBudW1iZXI7XG4gIHNpemVSYW5nZTogW251bWJlciwgbnVtYmVyXTtcbiAgYmlsbGJvYXJkOiBib29sZWFuO1xuICBmYWRlVHJhaWw6IGJvb2xlYW47XG59O1xuXG5leHBvcnQgdHlwZSBUcmlwTGF5ZXJDb25maWcgPSBNZXJnZTxcbiAgTGF5ZXJCYXNlQ29uZmlnLFxuICB7Y29sdW1uczogVHJpcExheWVyQ29sdW1uc0NvbmZpZzsgdmlzQ29uZmlnOiBUcmlwTGF5ZXJWaXNDb25maWd9XG4+O1xuXG5leHBvcnQgdHlwZSBUcmlwTGF5ZXJNZXRhID0ge1xuICBnZXRGZWF0dXJlOiBhbnk7XG59O1xuXG5jb25zdCB6b29tRmFjdG9yVmFsdWUgPSA4O1xuXG5leHBvcnQgY29uc3QgZGVmYXVsdFRoaWNrbmVzcyA9IDAuNTtcbmV4cG9ydCBjb25zdCBkZWZhdWx0TGluZVdpZHRoID0gMTtcblxuZXhwb3J0IGNvbnN0IHRyaXBWaXNDb25maWdzOiB7XG4gIG9wYWNpdHk6ICdvcGFjaXR5JztcbiAgdGhpY2tuZXNzOiBWaXNDb25maWdOdW1iZXI7XG4gIGNvbG9yUmFuZ2U6ICdjb2xvclJhbmdlJztcbiAgdHJhaWxMZW5ndGg6ICd0cmFpbExlbmd0aCc7XG4gIGZhZGVUcmFpbDogJ2ZhZGVUcmFpbCc7XG4gIGJpbGxib2FyZDogJ2JpbGxib2FyZCc7XG4gIHNpemVSYW5nZTogJ3N0cm9rZVdpZHRoUmFuZ2UnO1xufSA9IHtcbiAgb3BhY2l0eTogJ29wYWNpdHknLFxuICB0aGlja25lc3M6IHtcbiAgICB0eXBlOiAnbnVtYmVyJyxcbiAgICBkZWZhdWx0VmFsdWU6IGRlZmF1bHRUaGlja25lc3MsXG4gICAgbGFiZWw6ICdTdHJva2UgV2lkdGgnLFxuICAgIGlzUmFuZ2VkOiBmYWxzZSxcbiAgICByYW5nZTogWzAsIDEwMF0sXG4gICAgc3RlcDogMC4xLFxuICAgIGdyb3VwOiAnc3Ryb2tlJyxcbiAgICBwcm9wZXJ0eTogJ3RoaWNrbmVzcydcbiAgfSxcbiAgY29sb3JSYW5nZTogJ2NvbG9yUmFuZ2UnLFxuICB0cmFpbExlbmd0aDogJ3RyYWlsTGVuZ3RoJyxcbiAgZmFkZVRyYWlsOiAnZmFkZVRyYWlsJyxcbiAgYmlsbGJvYXJkOiAnYmlsbGJvYXJkJyxcbiAgc2l6ZVJhbmdlOiAnc3Ryb2tlV2lkdGhSYW5nZSdcbn07XG5cbmV4cG9ydCBjb25zdCBmZWF0dXJlQWNjZXNzb3IgPVxuICAoe2dlb2pzb259OiBUcmlwTGF5ZXJDb2x1bW5zQ29uZmlnKSA9PlxuICAoZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+XG4gIGQgPT5cbiAgICBkYy52YWx1ZUF0KGQuaW5kZXgsIGdlb2pzb24uZmllbGRJZHgpO1xuZXhwb3J0IGNvbnN0IGZlYXR1cmVSZXNvbHZlciA9ICh7Z2VvanNvbn06IFRyaXBMYXllckNvbHVtbnNDb25maWcpID0+IGdlb2pzb24uZmllbGRJZHg7XG5jb25zdCBnZXRUYWJsZU1vZGVWYWx1ZUFjY2Vzc29yID0gZiA9PiB7XG4gIC8vIENhbGxlZCBmcm9tIGdwdS1maWx0ZXItdXRpbHMuZ2V0RmlsdGVyVmFsdWVBY2Nlc3NvcigpXG4gIHJldHVybiBmaWVsZCA9PiBmLnByb3BlcnRpZXMudmFsdWVzLm1hcCh2ID0+IGZpZWxkLnZhbHVlQWNjZXNzb3IodikpO1xufTtcbmNvbnN0IGdldFRhYmxlTW9kZUZpZWxkVmFsdWUgPSAoZmllbGQsIGRhdGEpID0+IHtcbiAgbGV0IHJ2O1xuICBpZiAodHlwZW9mIGRhdGEgPT09ICdmdW5jdGlvbicpIHtcbiAgICBydiA9IGRhdGEoZmllbGQpO1xuICB9IGVsc2Uge1xuICAgIHJ2ID0gZGVmYXVsdEdldEZpZWxkVmFsdWUoZmllbGQsIGRhdGEpO1xuICB9XG4gIHJldHVybiBydjtcbn07XG5cbmV4cG9ydCBjb25zdCBDT0xVTU5fTU9ERV9HRU9KU09OID0gJ2dlb2pzb24nO1xuZXhwb3J0IGNvbnN0IENPTFVNTl9NT0RFX1RBQkxFID0gJ3RhYmxlJztcbmNvbnN0IFNVUFBPUlRFRF9DT0xVTU5fTU9ERVMgPSBbXG4gIHtcbiAgICBrZXk6IENPTFVNTl9NT0RFX0dFT0pTT04sXG4gICAgbGFiZWw6ICdHZW9KU09OJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IFsnZ2VvanNvbiddXG4gIH0sXG4gIHtcbiAgICBrZXk6IENPTFVNTl9NT0RFX1RBQkxFLFxuICAgIGxhYmVsOiAnVGFibGUgY29sdW1ucycsXG4gICAgcmVxdWlyZWRDb2x1bW5zOiBbJ2lkJywgJ2xhdCcsICdsbmcnLCAndGltZXN0YW1wJ10sXG4gICAgb3B0aW9uYWxDb2x1bW5zOiBbJ2FsdGl0dWRlJ11cbiAgfVxuXTtcbmNvbnN0IERFRkFVTFRfQ09MVU1OX01PREUgPSBDT0xVTU5fTU9ERV9HRU9KU09OO1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUcmlwTGF5ZXIgZXh0ZW5kcyBMYXllciB7XG4gIGRlY2xhcmUgdmlzQ29uZmlnU2V0dGluZ3M6IFRyaXBMYXllclZpc0NvbmZpZ1NldHRpbmdzO1xuICBkZWNsYXJlIGNvbmZpZzogVHJpcExheWVyQ29uZmlnO1xuICBkZWNsYXJlIG1ldGE6IFRyaXBMYXllck1ldGE7XG4gIGRlY2xhcmUgZGF0YUNvbnRhaW5lcjogRGF0YUNvbnRhaW5lckludGVyZmFjZSB8IG51bGw7XG5cbiAgZGF0YVRvRmVhdHVyZTogR2VvanNvbkRhdGFNYXBzO1xuICBkYXRhVG9UaW1lU3RhbXA6IGFueVtdO1xuICBnZXRGZWF0dXJlOiAoY29sdW1uczogVHJpcExheWVyQ29sdW1uc0NvbmZpZykgPT4gKGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+IGFueTtcbiAgX2xheWVySW5mb01vZGFsOiBSZWNvcmQ8c3RyaW5nLCAoKSA9PiBKU1guRWxlbWVudD47XG5cbiAgY29uc3RydWN0b3IocHJvcHMpIHtcbiAgICBzdXBlcihwcm9wcyk7XG5cbiAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBbXTtcbiAgICB0aGlzLmRhdGFUb1RpbWVTdGFtcCA9IFtdO1xuICAgIHRoaXMuZGF0YUNvbnRhaW5lciA9IG51bGw7XG4gICAgdGhpcy5yZWdpc3RlclZpc0NvbmZpZyh0cmlwVmlzQ29uZmlncyk7XG4gICAgdGhpcy5nZXRGZWF0dXJlID0gbWVtb2l6ZShmZWF0dXJlQWNjZXNzb3IsIGZlYXR1cmVSZXNvbHZlcik7XG4gICAgdGhpcy5fbGF5ZXJJbmZvTW9kYWwgPSB7XG4gICAgICBbQ09MVU1OX01PREVfVEFCTEVdOiBUcmlwSW5mb01vZGFsRmFjdG9yeShDT0xVTU5fTU9ERV9UQUJMRSksXG4gICAgICBbQ09MVU1OX01PREVfR0VPSlNPTl06IFRyaXBJbmZvTW9kYWxGYWN0b3J5KENPTFVNTl9NT0RFX0dFT0pTT04pXG4gICAgfTtcbiAgfVxuXG4gIGdldCBzdXBwb3J0ZWRDb2x1bW5Nb2RlcygpIHtcbiAgICByZXR1cm4gU1VQUE9SVEVEX0NPTFVNTl9NT0RFUztcbiAgfVxuXG4gIHN0YXRpYyBnZXQgdHlwZSgpOiAndHJpcCcge1xuICAgIHJldHVybiAndHJpcCc7XG4gIH1cbiAgZ2V0IHR5cGUoKSB7XG4gICAgcmV0dXJuIFRyaXBMYXllci50eXBlO1xuICB9XG5cbiAgZ2V0IG5hbWUoKTogJ1RyaXAnIHtcbiAgICByZXR1cm4gJ1RyaXAnO1xuICB9XG5cbiAgZ2V0IGxheWVySWNvbigpIHtcbiAgICByZXR1cm4gVHJpcExheWVySWNvbjtcbiAgfVxuXG4gIGdldCBjb2x1bW5QYWlycygpIHtcbiAgICByZXR1cm4gdGhpcy5kZWZhdWx0UG9pbnRDb2x1bW5QYWlycztcbiAgfVxuXG4gIGFjY2Vzc1ZTRmllbGRWYWx1ZSgpIHtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfR0VPSlNPTikge1xuICAgICAgcmV0dXJuIGRlZmF1bHRHZXRGaWVsZFZhbHVlO1xuICAgIH1cbiAgICByZXR1cm4gZ2V0VGFibGVNb2RlRmllbGRWYWx1ZTtcbiAgfVxuXG4gIGdldCB2aXN1YWxDaGFubmVscygpIHtcbiAgICBjb25zdCB2aXN1YWxDaGFubmVscyA9IHN1cGVyLnZpc3VhbENoYW5uZWxzO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLnZpc3VhbENoYW5uZWxzLFxuICAgICAgY29sb3I6IHtcbiAgICAgICAgLi4udmlzdWFsQ2hhbm5lbHMuY29sb3IsXG4gICAgICAgIGFjY2Vzc29yOiAnZ2V0Q29sb3InLFxuICAgICAgICBudWxsVmFsdWU6IHZpc3VhbENoYW5uZWxzLmNvbG9yLm51bGxWYWx1ZSxcbiAgICAgICAgZ2V0QXR0cmlidXRlVmFsdWU6IGNvbmZpZyA9PiBkID0+IGQucHJvcGVydGllcy5saW5lQ29sb3IgfHwgY29uZmlnLmNvbG9yLFxuICAgICAgICAvLyB1c2VkIHRoaXMgdG8gZ2V0IHVwZGF0ZVRyaWdnZXJzXG4gICAgICAgIGRlZmF1bHRWYWx1ZTogY29uZmlnID0+IGNvbmZpZy5jb2xvclxuICAgICAgfSxcbiAgICAgIHNpemU6IHtcbiAgICAgICAgLi4udmlzdWFsQ2hhbm5lbHMuc2l6ZSxcbiAgICAgICAgcHJvcGVydHk6ICdzdHJva2UnLFxuICAgICAgICBhY2Nlc3NvcjogJ2dldFdpZHRoJyxcbiAgICAgICAgY29uZGl0aW9uOiBjb25maWcgPT4gY29uZmlnLnZpc0NvbmZpZy5zdHJva2VkLFxuICAgICAgICBudWxsVmFsdWU6IDAsXG4gICAgICAgIGdldEF0dHJpYnV0ZVZhbHVlOiAoKSA9PiBkID0+IGQucHJvcGVydGllcy5saW5lV2lkdGggfHwgZGVmYXVsdExpbmVXaWR0aFxuICAgICAgfVxuICAgIH07XG4gIH1cblxuICBnZXQgYW5pbWF0aW9uRG9tYWluKCkge1xuICAgIHJldHVybiB0aGlzLmNvbmZpZy5hbmltYXRpb24uZG9tYWluO1xuICB9XG5cbiAgZ2V0IGxheWVySW5mb01vZGFsKCkge1xuICAgIHJldHVybiB7XG4gICAgICBbQ09MVU1OX01PREVfR0VPSlNPTl06IHtcbiAgICAgICAgaWQ6ICdpY29uSW5mbycsXG4gICAgICAgIHRlbXBsYXRlOiB0aGlzLl9sYXllckluZm9Nb2RhbFtDT0xVTU5fTU9ERV9HRU9KU09OXSxcbiAgICAgICAgbW9kYWxQcm9wczoge1xuICAgICAgICAgIHRpdGxlOiAnbW9kYWwudHJpcEluZm8udGl0bGUnXG4gICAgICAgIH1cbiAgICAgIH0sXG4gICAgICBbQ09MVU1OX01PREVfVEFCTEVdOiB7XG4gICAgICAgIGlkOiAnaWNvbkluZm8nLFxuICAgICAgICB0ZW1wbGF0ZTogdGhpcy5fbGF5ZXJJbmZvTW9kYWxbQ09MVU1OX01PREVfVEFCTEVdLFxuICAgICAgICBtb2RhbFByb3BzOiB7XG4gICAgICAgICAgdGl0bGU6ICdtb2RhbC50cmlwSW5mby50aXRsZVRhYmxlJ1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIGdldFBvc2l0aW9uQWNjZXNzb3IoZGF0YUNvbnRhaW5lcjogRGF0YUNvbnRhaW5lckludGVyZmFjZSkge1xuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9KU09OKSB7XG4gICAgICByZXR1cm4gdGhpcy5nZXRGZWF0dXJlKHRoaXMuY29uZmlnLmNvbHVtbnMpKGRhdGFDb250YWluZXIpO1xuICAgIH1cbiAgICByZXR1cm4gbnVsbDtcbiAgfVxuXG4gIHN0YXRpYyBmaW5kRGVmYXVsdExheWVyUHJvcHMoXG4gICAge2xhYmVsLCBmaWVsZHMgPSBbXSwgZGF0YUNvbnRhaW5lciwgaWR9OiBLZXBsZXJUYWJsZSxcbiAgICBmb3VuZExheWVycz86IGFueVtdXG4gICkge1xuICAgIGNvbnN0IGdlb2pzb25Db2x1bW5zID0gZmllbGRzLmZpbHRlcihmID0+IGYudHlwZSA9PT0gJ2dlb2pzb24nKS5tYXAoZiA9PiBmLm5hbWUpO1xuXG4gICAgY29uc3QgZGVmYXVsdENvbHVtbnMgPSB7XG4gICAgICBnZW9qc29uOiB1bmlxKFsuLi5HRU9KU09OX0ZJRUxEUy5nZW9qc29uLCAuLi5nZW9qc29uQ29sdW1uc10pXG4gICAgfTtcblxuICAgIGNvbnN0IGdlb0pzb25Db2x1bW5zID0gdGhpcy5maW5kRGVmYXVsdENvbHVtbkZpZWxkKGRlZmF1bHRDb2x1bW5zLCBmaWVsZHMpO1xuXG4gICAgY29uc3QgdHJpcEdlb2pzb25Db2x1bW5zID0gKGdlb0pzb25Db2x1bW5zIHx8IFtdKS5maWx0ZXIoY29sID0+XG4gICAgICBpc1RyaXBHZW9Kc29uRmllbGQoZGF0YUNvbnRhaW5lciwgZmllbGRzW2NvbC5nZW9qc29uLmZpZWxkSWR4XSlcbiAgICApO1xuXG4gICAgaWYgKHRyaXBHZW9qc29uQ29sdW1ucy5sZW5ndGgpIHtcbiAgICAgIHJldHVybiB7XG4gICAgICAgIHByb3BzOiB0cmlwR2VvanNvbkNvbHVtbnMubWFwKGNvbHVtbnMgPT4gKHtcbiAgICAgICAgICBsYWJlbDogKHR5cGVvZiBsYWJlbCA9PT0gJ3N0cmluZycgJiYgbGFiZWwucmVwbGFjZSgvXFwuW14vLl0rJC8sICcnKSkgfHwgdGhpcy50eXBlLFxuICAgICAgICAgIGNvbHVtbnMsXG4gICAgICAgICAgaXNWaXNpYmxlOiB0cnVlLFxuICAgICAgICAgIGNvbHVtbk1vZGU6IENPTFVNTl9NT0RFX0dFT0pTT05cbiAgICAgICAgfSkpLFxuXG4gICAgICAgIC8vIGlmIGEgZ2VvanNvbiBsYXllciBpcyBjcmVhdGVkIGZyb20gdGhpcyBjb2x1bW4sIGRlbGV0ZSBpdFxuICAgICAgICBmb3VuZExheWVyczogZm91bmRMYXllcnM/LmZpbHRlcihcbiAgICAgICAgICBwcm9wID0+XG4gICAgICAgICAgICBwcm9wLnR5cGUgIT09ICdnZW9qc29uJyB8fFxuICAgICAgICAgICAgcHJvcC5kYXRhSWQgIT09IGlkIHx8XG4gICAgICAgICAgICAhdHJpcEdlb2pzb25Db2x1bW5zLmZpbmQoYyA9PiBwcm9wLmNvbHVtbnMuZ2VvanNvbi5uYW1lID09PSBjLmdlb2pzb24ubmFtZSlcbiAgICAgICAgKVxuICAgICAgfTtcbiAgICB9XG5cbiAgICByZXR1cm4ge3Byb3BzOiBbXX07XG4gIH1cblxuICBnZXREZWZhdWx0TGF5ZXJDb25maWcocHJvcHMpIHtcbiAgICByZXR1cm4ge1xuICAgICAgLi4uc3VwZXIuZ2V0RGVmYXVsdExheWVyQ29uZmlnKHByb3BzKSxcbiAgICAgIGNvbHVtbk1vZGU6IHByb3BzPy5jb2x1bW5Nb2RlID8/IERFRkFVTFRfQ09MVU1OX01PREUsXG4gICAgICBhbmltYXRpb246IHtcbiAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgZG9tYWluOiBudWxsXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIGdldEhvdmVyRGF0YShvYmplY3QsIGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2UsIGZpZWxkcywgYW5pbWF0aW9uQ29uZmlnKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbHVtbk1vZGUgPT09IENPTFVNTl9NT0RFX0dFT0pTT04pIHtcbiAgICAgIC8vIGluZGV4IGZvciBkYXRhQ29udGFpbmVyIGlzIHNhdmVkIHRvIGZlYXR1cmUucHJvcGVydGllc1xuICAgICAgcmV0dXJuIGRhdGFDb250YWluZXIucm93KG9iamVjdC5wcm9wZXJ0aWVzLmluZGV4KTtcbiAgICB9XG4gICAgcmV0dXJuIHRoaXMuX2ZpbmRDb2x1bW5Nb2RlRGF0dW1Gb3JGZWF0dXJlKG9iamVjdC5wcm9wZXJ0aWVzLmluZGV4LCBhbmltYXRpb25Db25maWcuY3VycmVudFRpbWUpXG4gICAgICA/LmRhdHVtO1xuICB9XG5cbiAgY2FsY3VsYXRlRGF0YUF0dHJpYnV0ZShkYXRhc2V0OiBLZXBsZXJUYWJsZSkge1xuICAgIHN3aXRjaCAodGhpcy5jb25maWcuY29sdW1uTW9kZSkge1xuICAgICAgY2FzZSBDT0xVTU5fTU9ERV9HRU9KU09OOiB7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgZGF0YXNldC5maWx0ZXJlZEluZGV4XG4gICAgICAgICAgICAubWFwKGkgPT4gdGhpcy5kYXRhVG9GZWF0dXJlW2ldKVxuICAgICAgICAgICAgLy8gVE9ETyBkIGNhbiBiZSBCaW5hcnlGZWF0dXJlQ29sbGVjdGlvbiwgZml4IGxvZ2ljXG4gICAgICAgICAgICAuZmlsdGVyKGQgPT4gZCAmJiAoZCBhcyBhbnkpLmdlb21ldHJ5Py50eXBlID09PSAnTGluZVN0cmluZycpXG4gICAgICAgICk7XG4gICAgICB9XG5cbiAgICAgIGNhc2UgQ09MVU1OX01PREVfVEFCTEU6XG4gICAgICAgIHJldHVybiBhcHBseUZpbHRlcnNUb1RhYmxlQ29sdW1ucyhkYXRhc2V0LCB0aGlzLmRhdGFUb0ZlYXR1cmUpO1xuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICByZXR1cm4gW107XG4gICAgfVxuICB9XG5cbiAgZm9ybWF0TGF5ZXJEYXRhKGRhdGFzZXRzOiBEYXRhc2V0cywgb2xkTGF5ZXJEYXRhKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmRhdGFJZCA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cbiAgICAvLyB0by1kbzogcGFyc2Ugc2VnbWVudCBmcm9tIGRhdGFDb250YWluZXJcblxuICAgIGNvbnN0IHtkYXRhQ29udGFpbmVyLCBncHVGaWx0ZXJ9ID0gZGF0YXNldHNbdGhpcy5jb25maWcuZGF0YUlkXTtcbiAgICBjb25zdCB7ZGF0YX0gPSB0aGlzLnVwZGF0ZURhdGEoZGF0YXNldHMsIG9sZExheWVyRGF0YSk7XG5cbiAgICBsZXQgdmFsdWVBY2Nlc3NvcjtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfR0VPSlNPTikge1xuICAgICAgdmFsdWVBY2Nlc3NvciA9IChkYzogRGF0YUNvbnRhaW5lckludGVyZmFjZSwgZiwgZmllbGRJbmRleDogbnVtYmVyKSA9PiB7XG4gICAgICAgIHJldHVybiBkYy52YWx1ZUF0KGYucHJvcGVydGllcy5pbmRleCwgZmllbGRJbmRleCk7XG4gICAgICB9O1xuICAgIH0gZWxzZSB7XG4gICAgICB2YWx1ZUFjY2Vzc29yID0gZ2V0VGFibGVNb2RlVmFsdWVBY2Nlc3NvcjtcbiAgICB9XG4gICAgY29uc3QgaW5kZXhBY2Nlc3NvciA9IGYgPT4gZi5wcm9wZXJ0aWVzLmluZGV4O1xuICAgIGNvbnN0IGRhdGFBY2Nlc3NvciA9ICgpID0+IGQgPT4gKHtpbmRleDogZC5wcm9wZXJ0aWVzLmluZGV4fSk7XG4gICAgY29uc3QgYWNjZXNzb3JzID0gdGhpcy5nZXRBdHRyaWJ1dGVBY2Nlc3NvcnMoe2RhdGFBY2Nlc3NvciwgZGF0YUNvbnRhaW5lcn0pO1xuICAgIGNvbnN0IGdldEZpbHRlclZhbHVlID0gZ3B1RmlsdGVyLmZpbHRlclZhbHVlQWNjZXNzb3IoZGF0YUNvbnRhaW5lcikoXG4gICAgICBpbmRleEFjY2Vzc29yLFxuICAgICAgdmFsdWVBY2Nlc3NvclxuICAgICk7XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YSxcbiAgICAgIGdldEZpbHRlclZhbHVlLFxuICAgICAgZ2V0UGF0aDogZCA9PiBkLmdlb21ldHJ5LmNvb3JkaW5hdGVzLFxuICAgICAgZ2V0VGltZXN0YW1wczogZCA9PiB0aGlzLmRhdGFUb1RpbWVTdGFtcFtkLnByb3BlcnRpZXMuaW5kZXhdLFxuICAgICAgLi4uYWNjZXNzb3JzXG4gICAgfTtcbiAgfVxuXG4gIHVwZGF0ZUFuaW1hdGlvbkRvbWFpbihkb21haW4pIHtcbiAgICB0aGlzLnVwZGF0ZUxheWVyQ29uZmlnKHtcbiAgICAgIGFuaW1hdGlvbjoge1xuICAgICAgICAuLi50aGlzLmNvbmZpZy5hbmltYXRpb24sXG4gICAgICAgIGRvbWFpblxuICAgICAgfVxuICAgIH0pO1xuICB9XG5cbiAgdXBkYXRlTGF5ZXJNZXRhKGRhdGFzZXQ6IEtlcGxlclRhYmxlKSB7XG4gICAgY29uc3Qge2RhdGFDb250YWluZXJ9ID0gZGF0YXNldDtcbiAgICBsZXQgZ2V0RmVhdHVyZTtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfR0VPSlNPTikge1xuICAgICAgZ2V0RmVhdHVyZSA9IHRoaXMuZ2V0UG9zaXRpb25BY2Nlc3NvcihkYXRhQ29udGFpbmVyKTtcbiAgICAgIGlmIChnZXRGZWF0dXJlID09PSB0aGlzLm1ldGEuZ2V0RmVhdHVyZSkge1xuICAgICAgICAvLyBUT0RPOiByZXZpc2l0IHRoaXMgYWZ0ZXIgZ3B1IGZpbHRlcmluZ1xuICAgICAgICByZXR1cm47XG4gICAgICB9XG4gICAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBnZXRHZW9qc29uRGF0YU1hcHMoZGF0YUNvbnRhaW5lciwgZ2V0RmVhdHVyZSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHRoaXMuZGF0YUNvbnRhaW5lciA9IGRhdGFDb250YWluZXI7XG4gICAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBncm91cENvbHVtbnNBc0dlb0pzb24oZGF0YUNvbnRhaW5lciwgdGhpcy5jb25maWcuY29sdW1ucywgJ3RpbWVzdGFtcCcpO1xuICAgIH1cblxuICAgIGNvbnN0IHtkYXRhVG9UaW1lU3RhbXAsIGFuaW1hdGlvbkRvbWFpbn0gPSBwYXJzZVRyaXBHZW9Kc29uVGltZXN0YW1wKHRoaXMuZGF0YVRvRmVhdHVyZSk7XG5cbiAgICB0aGlzLmRhdGFUb1RpbWVTdGFtcCA9IGRhdGFUb1RpbWVTdGFtcDtcbiAgICB0aGlzLnVwZGF0ZUFuaW1hdGlvbkRvbWFpbihhbmltYXRpb25Eb21haW4pO1xuXG4gICAgLy8gZ2V0IGJvdW5kcyBmcm9tIGZlYXR1cmVzXG4gICAgY29uc3QgYm91bmRzID0gZ2V0R2VvanNvbkJvdW5kcyh0aGlzLmRhdGFUb0ZlYXR1cmUpO1xuXG4gICAgLy8ga2VlcCBhIHJlY29yZCBvZiB3aGF0IHR5cGUgb2YgZ2VvbWV0cnkgdGhlIGNvbGxlY3Rpb24gaGFzXG4gICAgY29uc3QgZmVhdHVyZVR5cGVzID0gZ2V0R2VvanNvbkZlYXR1cmVUeXBlcyh0aGlzLmRhdGFUb0ZlYXR1cmUpO1xuXG4gICAgdGhpcy51cGRhdGVNZXRhKHtib3VuZHMsIGZlYXR1cmVUeXBlcywgZ2V0RmVhdHVyZX0pO1xuICB9XG5cbiAgc2V0SW5pdGlhbExheWVyQ29uZmlnKGRhdGFzZXQpIHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lcn0gPSBkYXRhc2V0O1xuICAgIGlmICghZGF0YUNvbnRhaW5lci5udW1Sb3dzKCkpIHtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8vIGRlZmVmYXVsdExheWVyUHJvcHMgd2lsbCBhdXRvbWF0aWNhbGx5IGZpbmQgZ2VvanNvbiBjb2x1bW5cbiAgICAvLyBpZiBub3QgZm91bmQsIHdlIHRyeSB0byBzZXQgaXQgdG8gaWQgLyBsYXQgL2xuZyAvdHNcbiAgICBpZiAoIXRoaXMuY29uZmlnLmNvbHVtbnMuZ2VvanNvbi52YWx1ZSkge1xuICAgICAgLy8gZmluZCBjb2x1bW5zIGZyb20gbGF0LCBsbmcsIGlkLCBhbmQgdHNcbiAgICAgIGNvbnN0IGNvbHVtbkNvbmZpZyA9IGRldGVjdFRhYmxlQ29sdW1ucyhkYXRhc2V0LCB0aGlzLmNvbmZpZy5jb2x1bW5zKTtcbiAgICAgIGlmIChjb2x1bW5Db25maWcpIHtcbiAgICAgICAgdGhpcy51cGRhdGVMYXllckNvbmZpZyh7XG4gICAgICAgICAgLi4uY29sdW1uQ29uZmlnLFxuICAgICAgICAgIGNvbHVtbk1vZGU6IENPTFVNTl9NT0RFX1RBQkxFXG4gICAgICAgIH0pO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRoaXM7XG4gICAgICB9XG4gICAgfVxuXG4gICAgdGhpcy51cGRhdGVMYXllck1ldGEoZGF0YXNldCk7XG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICByZW5kZXJMYXllcihvcHRzKSB7XG4gICAgY29uc3Qge2RhdGEsIGdwdUZpbHRlciwgbWFwU3RhdGUsIGFuaW1hdGlvbkNvbmZpZ30gPSBvcHRzO1xuICAgIGNvbnN0IHt2aXNDb25maWd9ID0gdGhpcy5jb25maWc7XG4gICAgY29uc3Qgem9vbUZhY3RvciA9IHRoaXMuZ2V0Wm9vbUZhY3RvcihtYXBTdGF0ZSk7XG4gICAgY29uc3QgaXNWYWxpZFRpbWUgPVxuICAgICAgYW5pbWF0aW9uQ29uZmlnICYmXG4gICAgICBBcnJheS5pc0FycmF5KGFuaW1hdGlvbkNvbmZpZy5kb21haW4pICYmXG4gICAgICBhbmltYXRpb25Db25maWcuZG9tYWluLmV2ZXJ5KE51bWJlci5pc0Zpbml0ZSkgJiZcbiAgICAgIE51bWJlci5pc0Zpbml0ZShhbmltYXRpb25Db25maWcuY3VycmVudFRpbWUpO1xuXG4gICAgaWYgKCFpc1ZhbGlkVGltZSkge1xuICAgICAgcmV0dXJuIFtdO1xuICAgIH1cblxuICAgIGNvbnN0IGRvbWFpbjAgPSBhbmltYXRpb25Db25maWcuZG9tYWluPy5bMF07XG5cbiAgICBjb25zdCBncHVGaWx0ZXJVcGRhdGVUcmlnZ2VycyA9IHtnZXRGaWx0ZXJWYWx1ZTogZ3B1RmlsdGVyLmZpbHRlclZhbHVlVXBkYXRlVHJpZ2dlcnN9O1xuICAgIGNvbnN0IHVwZGF0ZVRyaWdnZXJzID0ge1xuICAgICAgLi4udGhpcy5nZXRWaXN1YWxDaGFubmVsVXBkYXRlVHJpZ2dlcnMoKSxcbiAgICAgIGdldFRpbWVzdGFtcHM6IHtcbiAgICAgICAgY29sdW1uczogdGhpcy5jb25maWcuY29sdW1ucyxcbiAgICAgICAgZG9tYWluMFxuICAgICAgfSxcbiAgICAgIC4uLmdwdUZpbHRlclVwZGF0ZVRyaWdnZXJzXG4gICAgfTtcbiAgICBjb25zdCBkZWZhdWx0TGF5ZXJQcm9wcyA9IHRoaXMuZ2V0RGVmYXVsdERlY2tMYXllclByb3BzKG9wdHMpO1xuXG4gICAgY29uc3QgYmlsbGJvYXJkV2lkdGhGYWN0b3IgPSB2aXNDb25maWcuYmlsbGJvYXJkID8gUFJPSkVDVEVEX1BJWEVMX1NJWkVfTVVMVElQTElFUiA6IDE7XG5cbiAgICBjb25zdCBsYXllclByb3BzID0ge1xuICAgICAgLi4uZGVmYXVsdExheWVyUHJvcHMsXG4gICAgICAuLi5kYXRhLFxuICAgICAgZ2V0VGltZXN0YW1wczogZCA9PiAoZGF0YS5nZXRUaW1lc3RhbXBzKGQpIHx8IFtdKS5tYXAodHMgPT4gdHMgLSBkb21haW4wKSxcbiAgICAgIHdpZHRoU2NhbGU6IHZpc0NvbmZpZy50aGlja25lc3MgKiB6b29tRmFjdG9yICogem9vbUZhY3RvclZhbHVlICogYmlsbGJvYXJkV2lkdGhGYWN0b3IsXG4gICAgICBjYXBSb3VuZGVkOiB0cnVlLFxuICAgICAgam9pbnRSb3VuZGVkOiB0cnVlLFxuICAgICAgd3JhcExvbmdpdHVkZTogZmFsc2UsXG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGRlcHRoVGVzdDogbWFwU3RhdGUuZHJhZ1JvdGF0ZSxcbiAgICAgICAgZGVwdGhNYXNrOiBmYWxzZVxuICAgICAgfSxcbiAgICAgIHRyYWlsTGVuZ3RoOiB2aXNDb25maWcudHJhaWxMZW5ndGggKiAxMDAwLFxuICAgICAgZmFkZVRyYWlsOiB2aXNDb25maWcuZmFkZVRyYWlsLFxuICAgICAgYmlsbGJvYXJkOiB2aXNDb25maWcuYmlsbGJvYXJkLFxuICAgICAgLy8gVE9ETzogZ2l1c2VwcGUgdGhpcyB2YWx1ZXMgYmVjb21lcyBuZWdhdGl2ZVxuICAgICAgY3VycmVudFRpbWU6IGFuaW1hdGlvbkNvbmZpZy5jdXJyZW50VGltZSAtIGRvbWFpbjAsXG4gICAgICB1cGRhdGVUcmlnZ2VycyxcbiAgICAgIGlkOiBgJHtkZWZhdWx0TGF5ZXJQcm9wcy5pZH0ke21hcFN0YXRlLmdsb2JlPy5lbmFibGVkID8gJy1nbG9iZScgOiAnJ31gXG4gICAgfTtcbiAgICByZXR1cm4gW25ldyBEZWNrR0xUcmlwc0xheWVyKGxheWVyUHJvcHMpXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBGaW5kcyBjb29yZGluYXRlcyBhbmQgZGF0dW0gYXQgdGhlIGN1cnJlbnQgYW5pbWF0aW9uIHRpbWUgYnkgdGhlIHNwZWNpZmllZCBmZWF0dXJlIGluZGV4LlxuICAgKiBAcGFyYW0gZmVhdHVyZUluZGV4XG4gICAqIEBwYXJhbSB0aW1lXG4gICAqIEByZXR1cm5zIHt7ZGF0dW06IChudWxsfHN0cmluZ3wqKSwgaWR4OiAqLCBjb29yZHN9fHtkYXR1bTogbnVsbCwgaWR4OiBudW1iZXIsIGNvb3JkczogbnVsbH19XG4gICAqL1xuICBwcml2YXRlIF9maW5kQ29sdW1uTW9kZURhdHVtRm9yRmVhdHVyZShcbiAgICBmZWF0dXJlSW5kZXg6IG51bWJlcixcbiAgICB0aW1lOiBudW1iZXJcbiAgKToge1xuICAgIGlkeDogbnVtYmVyO1xuICAgIGNvb3JkczogbnVtYmVyW10gfCBudWxsO1xuICAgIGRhdHVtOiBhbnk7XG4gIH0ge1xuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9UQUJMRSkge1xuICAgICAgY29uc3Qgb2JqZWN0ID0gdGhpcy5kYXRhVG9GZWF0dXJlW2ZlYXR1cmVJbmRleF07XG4gICAgICBjb25zdCBpZHggPSBiaXNlY3RSaWdodCh0aGlzLmRhdGFUb1RpbWVTdGFtcFtmZWF0dXJlSW5kZXhdLCB0aW1lKTtcbiAgICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgdHlwZSBnZW9tZXRyeT9cbiAgICAgIGNvbnN0IHtjb29yZGluYXRlc30gPSBvYmplY3Q/Lmdlb21ldHJ5IHx8IHtjb29yZGluYXRlczogW119O1xuICAgICAgaWYgKGlkeCA+PSAwICYmIGlkeCA8IGNvb3JkaW5hdGVzLmxlbmd0aCkge1xuICAgICAgICBjb25zdCBjb29yZHMgPSBjb29yZGluYXRlc1tpZHhdO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIGlkeCxcbiAgICAgICAgICBjb29yZHMsXG4gICAgICAgICAgZGF0dW06IGNvb3Jkcz8uZGF0dW1cbiAgICAgICAgfTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIHtcbiAgICAgIGlkeDogLTEsXG4gICAgICBjb29yZHM6IG51bGwsXG4gICAgICBkYXR1bTogbnVsbFxuICAgIH07XG4gIH1cbn1cbiJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7OztBQUdBLElBQUFBLE9BQUEsR0FBQUMsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLFFBQUEsR0FBQUYsc0JBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFFLFVBQUEsR0FBQUMsdUJBQUEsQ0FBQUgsT0FBQTtBQUNBLElBQUFJLFVBQUEsR0FBQUosT0FBQTtBQUVBLElBQUFLLFVBQUEsR0FBQUwsT0FBQTtBQUNBLElBQUFNLGNBQUEsR0FBQVAsc0JBQUEsQ0FBQUMsT0FBQTtBQUVBLElBQUFPLGFBQUEsR0FB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