kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
542 lines (532 loc) • 81.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.pointVisConfigs = exports.pointRequiredColumns = exports.pointPosAccessor = exports.pointOptionalColumns = exports.geojsonRequiredColumns = exports.geojsonPosAccessor = exports.geoarrowRequiredColumns = exports.geoarrowPosAccessor = exports["default"] = exports.COLUMN_MODE_POINTS = exports.COLUMN_MODE_GEOJSON = exports.COLUMN_MODE_GEOARROW = 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 _extensions = require("@deck.gl/extensions");
var _layers = require("@deck.gl/layers");
var _deckglArrowLayers = require("@kepler.gl/deckgl-arrow-layers");
var _deckglLayers = require("@kepler.gl/deckgl-layers");
var _baseLayer = _interopRequireDefault(require("../base-layer"));
var _utils = require("@kepler.gl/utils");
var _pointLayerIcon = _interopRequireDefault(require("./point-layer-icon"));
var _constants = require("@kepler.gl/constants");
var _layerTextLabel = require("../layer-text-label");
var _layerUtils = require("../layer-utils");
var _geojsonUtils = require("../geojson-layer/geojson-utils");
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; }
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
var pointPosAccessor = exports.pointPosAccessor = function pointPosAccessor(_ref) {
var lat = _ref.lat,
lng = _ref.lng,
altitude = _ref.altitude;
return function (dc) {
return function (d) {
return [dc.valueAt(d.index, lng.fieldIdx), dc.valueAt(d.index, lat.fieldIdx), altitude && altitude.fieldIdx > -1 ? dc.valueAt(d.index, altitude.fieldIdx) : 0];
};
};
};
var geojsonPosAccessor = exports.geojsonPosAccessor = function geojsonPosAccessor(_ref2) {
var geojson = _ref2.geojson;
return function (d) {
return d[geojson.fieldIdx];
};
};
var geoarrowPosAccessor = exports.geoarrowPosAccessor = function geoarrowPosAccessor(_ref3) {
var geoarrow = _ref3.geoarrow;
return function (dataContainer) {
return function (d) {
var row = dataContainer.valueAt(d.index, geoarrow.fieldIdx);
return [row.get(0), row.get(1), 0];
};
};
};
var COLUMN_MODE_POINTS = exports.COLUMN_MODE_POINTS = 'points';
var COLUMN_MODE_GEOJSON = exports.COLUMN_MODE_GEOJSON = 'geojson';
var COLUMN_MODE_GEOARROW = exports.COLUMN_MODE_GEOARROW = 'geoarrow';
var pointRequiredColumns = exports.pointRequiredColumns = ['lat', 'lng'];
var pointOptionalColumns = exports.pointOptionalColumns = ['altitude', 'neighbors'];
var geojsonRequiredColumns = exports.geojsonRequiredColumns = ['geojson'];
var geoarrowRequiredColumns = exports.geoarrowRequiredColumns = ['geoarrow'];
var SUPPORTED_COLUMN_MODES = [{
key: COLUMN_MODE_POINTS,
label: 'Point Columns',
requiredColumns: pointRequiredColumns,
optionalColumns: pointOptionalColumns
}, {
key: COLUMN_MODE_GEOJSON,
label: 'GeoJSON Feature',
requiredColumns: geojsonRequiredColumns,
verifyField: function verifyField(f) {
return !(0, _layerUtils.isGeoArrowPointField)(f);
}
}, {
key: COLUMN_MODE_GEOARROW,
label: 'Geoarrow Points',
requiredColumns: geoarrowRequiredColumns,
verifyField: function verifyField(f) {
return (0, _layerUtils.isGeoArrowPointField)(f);
}
}];
var DEFAULT_COLUMN_MODE = COLUMN_MODE_POINTS;
var brushingExtension = new _extensions.BrushingExtension();
var arrowCPUFilterExtension = new _deckglLayers.FilterArrowExtension();
function pushPointPosition(data, pos, index, neighbors) {
if (pos.every(Number.isFinite)) {
data.push(_objectSpread({
position: pos,
// index is important for filter
index: index
}, neighbors ? {
neighbors: neighbors
} : {}));
}
}
var pointVisConfigs = exports.pointVisConfigs = {
radius: 'radius',
fixedRadius: 'fixedRadius',
opacity: 'opacity',
outline: 'outline',
thickness: 'thickness',
strokeColor: 'strokeColor',
colorRange: 'colorRange',
strokeColorRange: 'strokeColorRange',
radiusRange: 'radiusRange',
filled: _objectSpread(_objectSpread({}, _constants.LAYER_VIS_CONFIGS.filled), {}, {
type: 'boolean',
label: 'layer.fillColor',
defaultValue: true,
property: 'filled'
}),
billboard: 'billboard',
allowHover: 'allowHover',
showNeighborOnHover: 'showNeighborOnHover',
showHighlightColor: 'showHighlightColor'
};
var PointLayer = exports["default"] = /*#__PURE__*/function (_Layer) {
function PointLayer(props) {
var _this;
(0, _classCallCheck2["default"])(this, PointLayer);
_this = _callSuper(this, PointLayer, [props]);
(0, _defineProperty2["default"])(_this, "dataToFeature", []);
(0, _defineProperty2["default"])(_this, "dataContainer", null);
(0, _defineProperty2["default"])(_this, "geoArrowVector", undefined);
/*
* CPU filtering an arrow table by values and assembling a partial copy of the raw table is expensive
* so we will use filteredIndex to create an attribute e.g. filteredIndex [0|1] for GPU filtering
* in deck.gl layer, see: FilterArrowExtension in @kepler.gl/deckgl-layers.
* Note that this approach can create visible lags in case of a lot of discarted geometry.
*/
(0, _defineProperty2["default"])(_this, "filteredIndex", null);
(0, _defineProperty2["default"])(_this, "filteredIndexTrigger", []);
_this.registerVisConfig(pointVisConfigs);
_this.getPositionAccessor = function (dataContainer) {
switch (_this.config.columnMode) {
case COLUMN_MODE_GEOARROW:
return geoarrowPosAccessor(_this.config.columns)(dataContainer);
case COLUMN_MODE_GEOJSON:
return geojsonPosAccessor(_this.config.columns);
default:
// COLUMN_MODE_POINTS
return pointPosAccessor(_this.config.columns)(dataContainer);
}
};
return _this;
}
(0, _inherits2["default"])(PointLayer, _Layer);
return (0, _createClass2["default"])(PointLayer, [{
key: "type",
get: function get() {
return 'point';
}
}, {
key: "isAggregated",
get: function get() {
return false;
}
}, {
key: "layerIcon",
get: function get() {
return _pointLayerIcon["default"];
}
}, {
key: "optionalColumns",
get: function get() {
return pointOptionalColumns;
}
}, {
key: "columnPairs",
get: function get() {
return this.defaultPointColumnPairs;
}
}, {
key: "supportedColumnModes",
get: function get() {
return SUPPORTED_COLUMN_MODES;
}
}, {
key: "noneLayerDataAffectingProps",
get: function get() {
return [].concat((0, _toConsumableArray2["default"])(_superPropGet(PointLayer, "noneLayerDataAffectingProps", this, 1)), ['radius']);
}
}, {
key: "visualChannels",
get: function get() {
return {
color: _objectSpread(_objectSpread({}, _superPropGet(PointLayer, "visualChannels", this, 1).color), {}, {
accessor: 'getFillColor',
condition: function condition(config) {
return config.visConfig.filled;
},
defaultValue: function defaultValue(config) {
return config.color;
}
}),
strokeColor: {
property: 'strokeColor',
key: 'strokeColor',
field: 'strokeColorField',
scale: 'strokeColorScale',
domain: 'strokeColorDomain',
range: 'strokeColorRange',
channelScaleType: _constants.CHANNEL_SCALES.color,
accessor: 'getLineColor',
condition: function condition(config) {
return config.visConfig.outline;
},
defaultValue: function defaultValue(config) {
return config.visConfig.strokeColor || config.color;
}
},
size: _objectSpread(_objectSpread({}, _superPropGet(PointLayer, "visualChannels", this, 1).size), {}, {
property: 'radius',
range: 'radiusRange',
fixed: 'fixedRadius',
channelScaleType: 'radius',
accessor: 'getRadius',
defaultValue: 1
})
};
}
}, {
key: "setInitialLayerConfig",
value: function setInitialLayerConfig(dataset) {
if (!dataset.dataContainer.numRows()) {
return this;
}
var defaultColorField = (0, _utils.findDefaultColorField)(dataset);
if (defaultColorField) {
this.updateLayerConfig({
colorField: defaultColorField
});
this.updateLayerVisualChannel(dataset, 'color');
}
return this;
}
}, {
key: "getDefaultLayerConfig",
value: function getDefaultLayerConfig(props) {
var _props$columnMode;
var defaultLayerConfig = _superPropGet(PointLayer, "getDefaultLayerConfig", this, 3)([props !== null && props !== void 0 ? props : {}]);
return _objectSpread(_objectSpread({}, defaultLayerConfig), {}, {
columnMode: (_props$columnMode = props === null || props === void 0 ? void 0 : props.columnMode) !== null && _props$columnMode !== void 0 ? _props$columnMode : DEFAULT_COLUMN_MODE,
// add stroke color visual channel
strokeColorField: null,
strokeColorDomain: [0, 1],
strokeColorScale: 'quantile',
colorUI: _objectSpread(_objectSpread({}, defaultLayerConfig.colorUI), {}, {
strokeColorRange: _constants.DEFAULT_COLOR_UI
})
});
}
}, {
key: "calculateDataAttribute",
value: function calculateDataAttribute(_ref4, getPosition) {
var _this2 = this;
var filteredIndex = _ref4.filteredIndex,
dataContainer = _ref4.dataContainer;
var columnMode = this.config.columnMode;
// 1) COLUMN_MODE_GEOARROW - when we have a geoarrow point column
// 2) COLUMN_MODE_POINTS + ArrowDataContainer > create geoarrow point column on the fly
if (dataContainer instanceof _utils.ArrowDataContainer && (columnMode === COLUMN_MODE_GEOARROW || columnMode === COLUMN_MODE_POINTS)) {
this.filteredIndex = (0, _layerUtils.getFilteredIndex)(dataContainer.numRows(), filteredIndex, this.filteredIndex);
this.filteredIndexTrigger = filteredIndex;
if (this.config.columnMode === COLUMN_MODE_GEOARROW) {
this.geoArrowVector = dataContainer.getColumn(this.config.columns.geoarrow.fieldIdx);
} else {
// generate a column compatible with geoarrow point
this.geoArrowVector = (0, _layerUtils.createGeoArrowPointVector)(dataContainer, getPosition);
}
return dataContainer.getTable();
}
// we don't need these in non-Arrow modes atm.
this.geoArrowVector = undefined;
this.filteredIndex = null;
var data = [];
var _loop = function _loop() {
var index = filteredIndex[i];
var neighbors;
if (_this2.config.columnMode === COLUMN_MODE_POINTS) {
var _this2$config$columns;
if ((_this2$config$columns = _this2.config.columns.neighbors) !== null && _this2$config$columns !== void 0 && _this2$config$columns.value) {
var fieldIdx = _this2.config.columns.neighbors.fieldIdx;
neighbors = Array.isArray(dataContainer.valueAt(index, fieldIdx)) ? dataContainer.valueAt(index, fieldIdx) : [];
}
var pos = getPosition({
index: index
});
// if doesn't have point lat or lng, do not add the point
// deck.gl can't handle position = null
pushPointPosition(data, pos, index, neighbors);
} else {
// COLUMN_MODE_GEOJSON mode - point from geojson coordinates
var coordinates = _this2.dataToFeature[i];
// if multi points
if (coordinates && Array.isArray(coordinates[0])) {
coordinates.forEach(function (coord) {
pushPointPosition(data, coord, index);
});
} else if (coordinates && Number.isFinite(coordinates[0])) {
pushPointPosition(data, coordinates, index);
}
}
};
for (var i = 0; i < filteredIndex.length; i++) {
_loop();
}
return data;
}
}, {
key: "formatLayerData",
value: function formatLayerData(datasets, oldLayerData) {
var _this3 = this;
if (this.config.dataId === null) {
return {};
}
var textLabel = this.config.textLabel;
var _datasets$this$config = datasets[this.config.dataId],
gpuFilter = _datasets$this$config.gpuFilter,
dataContainer = _datasets$this$config.dataContainer;
var _this$updateData = this.updateData(datasets, oldLayerData),
data = _this$updateData.data,
triggerChanged = _this$updateData.triggerChanged;
var getPosition = function getPosition(d) {
return d.position;
};
// get all distinct characters in the text labels
var textLabels = (0, _layerTextLabel.formatTextLabelData)({
textLabel: textLabel,
triggerChanged: triggerChanged,
oldLayerData: oldLayerData,
data: data,
dataContainer: dataContainer,
filteredIndex: this.filteredIndex
});
var accessors = this.getAttributeAccessors({
dataContainer: dataContainer
});
var isFilteredAccessor = function isFilteredAccessor(data) {
return _this3.filteredIndex ? _this3.filteredIndex[data.index] : 1;
};
return _objectSpread({
data: data,
getPosition: getPosition,
getFilterValue: gpuFilter.filterValueAccessor(dataContainer)(),
getFiltered: isFilteredAccessor,
textLabels: textLabels
}, accessors);
}
/* eslint-enable complexity */
}, {
key: "updateLayerMeta",
value: function updateLayerMeta(dataset) {
var dataContainer = dataset.dataContainer;
this.dataContainer = dataContainer;
if (this.config.columnMode === COLUMN_MODE_GEOJSON) {
var getFeature = this.getPositionAccessor();
this.dataToFeature = (0, _geojsonUtils.getGeojsonPointDataMaps)(dataContainer, getFeature);
} else if (this.config.columnMode === COLUMN_MODE_GEOARROW) {
var boundsFromMetadata = (0, _layerUtils.getBoundsFromArrowMetadata)(this.config.columns.geoarrow, dataContainer);
if (boundsFromMetadata) {
this.updateMeta({
bounds: boundsFromMetadata
});
} else {
var getPosition = this.getPositionAccessor(dataContainer);
var bounds = this.getPointsBounds(dataContainer, getPosition);
this.updateMeta({
bounds: bounds
});
}
} else {
var _getPosition = this.getPositionAccessor(dataContainer);
var _bounds = this.getPointsBounds(dataContainer, _getPosition);
this.updateMeta({
bounds: _bounds
});
}
}
// eslint-disable-next-line complexity
}, {
key: "renderLayer",
value: function renderLayer(opts) {
var _this$config$columns$;
var data = opts.data,
gpuFilter = opts.gpuFilter,
objectHovered = opts.objectHovered,
mapState = opts.mapState,
interactionConfig = opts.interactionConfig,
dataset = opts.dataset;
// if no field size is defined we need to pass fixed radius = false
var fixedRadius = this.config.visConfig.fixedRadius && Boolean(this.config.sizeField);
var radiusScale = this.getRadiusScaleByZoom(mapState, fixedRadius);
var layerProps = _objectSpread({
stroked: this.config.visConfig.outline,
filled: this.config.visConfig.filled,
lineWidthScale: this.config.visConfig.thickness,
billboard: this.config.visConfig.billboard,
radiusScale: radiusScale
}, this.config.visConfig.fixedRadius ? {} : {
radiusMaxPixels: 500
});
var updateTriggers = _objectSpread({
getPosition: this.config.columns,
getFilterValue: gpuFilter.filterValueUpdateTriggers,
getFiltered: this.filteredIndexTrigger
}, this.getVisualChannelUpdateTriggers());
var useArrowLayer = Boolean(this.geoArrowVector);
var defaultLayerProps = this.getDefaultDeckLayerProps(opts);
var brushingProps = this.getBrushingExtensionProps(interactionConfig);
var getPixelOffset = (0, _layerTextLabel.getTextOffsetByRadius)(radiusScale, data.getRadius, mapState);
var extensions = [].concat((0, _toConsumableArray2["default"])(defaultLayerProps.extensions), [brushingExtension], (0, _toConsumableArray2["default"])(useArrowLayer ? [arrowCPUFilterExtension] : []));
var sharedProps = _objectSpread({
getFilterValue: data.getFilterValue,
extensions: extensions,
filterRange: defaultLayerProps.filterRange,
visible: defaultLayerProps.visible
}, brushingProps);
var hoveredObject = this.hasHoveredObject(objectHovered);
var _this$config$visConfi = this.config.visConfig,
showNeighborOnHover = _this$config$visConfi.showNeighborOnHover,
allowHover = _this$config$visConfi.allowHover;
var neighborsData = [];
if (allowHover && showNeighborOnHover && hoveredObject) {
// find neighbors
neighborsData = (0, _layerUtils.getNeighbors)(this.config.columns.neighbors, dataset.dataContainer, hoveredObject.index, this.getPositionAccessor(dataset.dataContainer));
}
var ScatterplotLayerClass = _layers.ScatterplotLayer;
var deckLayerData = data.data;
var getPosition = data.getPosition;
if (useArrowLayer) {
ScatterplotLayerClass = _deckglArrowLayers.GeoArrowScatterplotLayer;
deckLayerData = dataset.dataContainer.getTable();
getPosition = this.geoArrowVector;
}
return [
// @ts-expect-error
new ScatterplotLayerClass(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, defaultLayerProps), brushingProps), layerProps), data), {}, {
data: deckLayerData,
getPosition: getPosition,
parameters: {
// circles will be flat on the map when the altitude column is not used
depthTest: ((_this$config$columns$ = this.config.columns.altitude) === null || _this$config$columns$ === void 0 ? void 0 : _this$config$columns$.fieldIdx) > -1
},
lineWidthUnits: 'pixels',
updateTriggers: updateTriggers,
extensions: extensions,
opacity: hoveredObject && showNeighborOnHover ? 0.2 : this.config.visConfig.opacity,
pickable: allowHover,
autoHighlight: false
}))].concat((0, _toConsumableArray2["default"])(hoveredObject ? [new _layers.ScatterplotLayer(_objectSpread(_objectSpread(_objectSpread({}, this.getDefaultHoverLayerProps()), layerProps), {}, {
visible: defaultLayerProps.visible,
data: [].concat((0, _toConsumableArray2["default"])(neighborsData), [hoveredObject]),
getLineColor: this.config.visConfig.showHighlightColor ? this.config.highlightColor : data.getLineColor,
getFillColor: this.config.visConfig.showHighlightColor ? this.config.highlightColor : data.getFillColor,
getRadius: data.getRadius,
getPosition: data.getPosition
}))] : []), (0, _toConsumableArray2["default"])(this.renderTextLabelLayer({
getPosition: getPosition,
sharedProps: sharedProps,
getPixelOffset: getPixelOffset,
updateTriggers: updateTriggers,
getFiltered: data.getFiltered
}, this.geoArrowVector ? _objectSpread(_objectSpread({}, opts), {}, {
data: _objectSpread(_objectSpread({}, opts.data), {}, {
getPosition: getPosition
})
}) : opts)));
}
}, {
key: "hasHoveredObject",
value: function hasHoveredObject(objectInfo) {
if ((0, _layerUtils.isLayerHoveredFromArrow)(objectInfo, this.id) && objectInfo.index >= 0 && this.dataContainer) {
return {
index: objectInfo.index,
position: this.getPositionAccessor(this.dataContainer)(objectInfo)
};
}
return _superPropGet(PointLayer, "hasHoveredObject", this, 3)([objectInfo]);
}
}, {
key: "getHoverData",
value: function getHoverData(object, dataContainer, fields, animationConfig, hoverInfo) {
// for arrow format, `object` is the Arrow row object Proxy,
// and index is passed in `hoverInfo`.
var index = this.geoArrowVector ? hoverInfo === null || hoverInfo === void 0 ? void 0 : hoverInfo.index : object.index;
if (index >= 0) {
return dataContainer.row(index);
}
return null;
}
}], [{
key: "findDefaultLayerProps",
value: function findDefaultLayerProps(dataset) {
var _dataset$fieldPairs = dataset.fieldPairs,
fieldPairs = _dataset$fieldPairs === void 0 ? [] : _dataset$fieldPairs,
type = dataset.type;
var props = [];
if (type === _constants.DatasetType.VECTOR_TILE) {
return {
props: props
};
}
// Make layer for each pair
fieldPairs.forEach(function (pair) {
var latField = pair.pair.lat;
var prop = {
label: pair.defaultName || 'Point'
};
// default layer color for begintrip and dropoff point
if (latField.value in _constants.DEFAULT_LAYER_COLOR) {
prop.color = (0, _utils.hexToRgb)(_constants.DEFAULT_LAYER_COLOR[latField.value]);
}
// set the first layer to be visible
if (props.length === 0) {
prop.isVisible = true;
}
// @ts-expect-error logically separate geojson column type?
prop.columns = (0, _layerUtils.assignPointPairToLayerColumn)(pair, true);
props.push(prop);
});
var altProps = (0, _layerUtils.getGeoArrowPointLayerProps)(dataset);
return {
props: props,
altProps: altProps
};
}
}]);
}(_baseLayer["default"]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfZXh0ZW5zaW9ucyIsInJlcXVpcmUiLCJfbGF5ZXJzIiwiX2RlY2tnbEFycm93TGF5ZXJzIiwiX2RlY2tnbExheWVycyIsIl9iYXNlTGF5ZXIiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiX3V0aWxzIiwiX3BvaW50TGF5ZXJJY29uIiwiX2NvbnN0YW50cyIsIl9sYXllclRleHRMYWJlbCIsIl9sYXllclV0aWxzIiwiX2dlb2pzb25VdGlscyIsIl9jYWxsU3VwZXIiLCJ0IiwibyIsImUiLCJfZ2V0UHJvdG90eXBlT2YyIiwiX3Bvc3NpYmxlQ29uc3RydWN0b3JSZXR1cm4yIiwiX2lzTmF0aXZlUmVmbGVjdENvbnN0cnVjdCIsIlJlZmxlY3QiLCJjb25zdHJ1Y3QiLCJjb25zdHJ1Y3RvciIsImFwcGx5IiwiQm9vbGVhbiIsInByb3RvdHlwZSIsInZhbHVlT2YiLCJjYWxsIiwiX3N1cGVyUHJvcEdldCIsInIiLCJwIiwiX2dldDIiLCJvd25LZXlzIiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiX29iamVjdFNwcmVhZCIsImFyZ3VtZW50cyIsImxlbmd0aCIsImZvckVhY2giLCJfZGVmaW5lUHJvcGVydHkyIiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJkZWZpbmVQcm9wZXJ0eSIsInBvaW50UG9zQWNjZXNzb3IiLCJleHBvcnRzIiwiX3JlZiIsImxhdCIsImxuZyIsImFsdGl0dWRlIiwiZGMiLCJkIiwidmFsdWVBdCIsImluZGV4IiwiZmllbGRJZHgiLCJnZW9qc29uUG9zQWNjZXNzb3IiLCJfcmVmMiIsImdlb2pzb24iLCJnZW9hcnJvd1Bvc0FjY2Vzc29yIiwiX3JlZjMiLCJnZW9hcnJvdyIsImRhdGFDb250YWluZXIiLCJyb3ciLCJnZXQiLCJDT0xVTU5fTU9ERV9QT0lOVFMiLCJDT0xVTU5fTU9ERV9HRU9KU09OIiwiQ09MVU1OX01PREVfR0VPQVJST1ciLCJwb2ludFJlcXVpcmVkQ29sdW1ucyIsInBvaW50T3B0aW9uYWxDb2x1bW5zIiwiZ2VvanNvblJlcXVpcmVkQ29sdW1ucyIsImdlb2Fycm93UmVxdWlyZWRDb2x1bW5zIiwiU1VQUE9SVEVEX0NPTFVNTl9NT0RFUyIsImtleSIsImxhYmVsIiwicmVxdWlyZWRDb2x1bW5zIiwib3B0aW9uYWxDb2x1bW5zIiwidmVyaWZ5RmllbGQiLCJmIiwiaXNHZW9BcnJvd1BvaW50RmllbGQiLCJERUZBVUxUX0NPTFVNTl9NT0RFIiwiYnJ1c2hpbmdFeHRlbnNpb24iLCJCcnVzaGluZ0V4dGVuc2lvbiIsImFycm93Q1BVRmlsdGVyRXh0ZW5zaW9uIiwiRmlsdGVyQXJyb3dFeHRlbnNpb24iLCJwdXNoUG9pbnRQb3NpdGlvbiIsImRhdGEiLCJwb3MiLCJuZWlnaGJvcnMiLCJldmVyeSIsIk51bWJlciIsImlzRmluaXRlIiwicG9zaXRpb24iLCJwb2ludFZpc0NvbmZpZ3MiLCJyYWRpdXMiLCJmaXhlZFJhZGl1cyIsIm9wYWNpdHkiLCJvdXRsaW5lIiwidGhpY2tuZXNzIiwic3Ryb2tlQ29sb3IiLCJjb2xvclJhbmdlIiwic3Ryb2tlQ29sb3JSYW5nZSIsInJhZGl1c1JhbmdlIiwiZmlsbGVkIiwiTEFZRVJfVklTX0NPTkZJR1MiLCJ0eXBlIiwiZGVmYXVsdFZhbHVlIiwicHJvcGVydHkiLCJiaWxsYm9hcmQiLCJhbGxvd0hvdmVyIiwic2hvd05laWdoYm9yT25Ib3ZlciIsInNob3dIaWdobGlnaHRDb2xvciIsIlBvaW50TGF5ZXIiLCJfTGF5ZXIiLCJwcm9wcyIsIl90aGlzIiwiX2NsYXNzQ2FsbENoZWNrMiIsInVuZGVmaW5lZCIsInJlZ2lzdGVyVmlzQ29uZmlnIiwiZ2V0UG9zaXRpb25BY2Nlc3NvciIsImNvbmZpZyIsImNvbHVtbk1vZGUiLCJjb2x1bW5zIiwiX2luaGVyaXRzMiIsIl9jcmVhdGVDbGFzczIiLCJQb2ludExheWVySWNvbiIsImRlZmF1bHRQb2ludENvbHVtblBhaXJzIiwiY29uY2F0IiwiX3RvQ29uc3VtYWJsZUFycmF5MiIsImNvbG9yIiwiYWNjZXNzb3IiLCJjb25kaXRpb24iLCJ2aXNDb25maWciLCJmaWVsZCIsInNjYWxlIiwiZG9tYWluIiwicmFuZ2UiLCJjaGFubmVsU2NhbGVUeXBlIiwiQ0hBTk5FTF9TQ0FMRVMiLCJzaXplIiwiZml4ZWQiLCJ2YWx1ZSIsInNldEluaXRpYWxMYXllckNvbmZpZyIsImRhdGFzZXQiLCJudW1Sb3dzIiwiZGVmYXVsdENvbG9yRmllbGQiLCJmaW5kRGVmYXVsdENvbG9yRmllbGQiLCJ1cGRhdGVMYXllckNvbmZpZyIsImNvbG9yRmllbGQiLCJ1cGRhdGVMYXllclZpc3VhbENoYW5uZWwiLCJnZXREZWZhdWx0TGF5ZXJDb25maWciLCJfcHJvcHMkY29sdW1uTW9kZSIsImRlZmF1bHRMYXllckNvbmZpZyIsInN0cm9rZUNvbG9yRmllbGQiLCJzdHJva2VDb2xvckRvbWFpbiIsInN0cm9rZUNvbG9yU2NhbGUiLCJjb2xvclVJIiwiREVGQVVMVF9DT0xPUl9VSSIsImNhbGN1bGF0ZURhdGFBdHRyaWJ1dGUiLCJfcmVmNCIsImdldFBvc2l0aW9uIiwiX3RoaXMyIiwiZmlsdGVyZWRJbmRleCIsIkFycm93RGF0YUNvbnRhaW5lciIsImdldEZpbHRlcmVkSW5kZXgiLCJmaWx0ZXJlZEluZGV4VHJpZ2dlciIsImdlb0Fycm93VmVjdG9yIiwiZ2V0Q29sdW1uIiwiY3JlYXRlR2VvQXJyb3dQb2ludFZlY3RvciIsImdldFRhYmxlIiwiX2xvb3AiLCJpIiwiX3RoaXMyJGNvbmZpZyRjb2x1bW5zIiwiQXJyYXkiLCJpc0FycmF5IiwiY29vcmRpbmF0ZXMiLCJkYXRhVG9GZWF0dXJlIiwiY29vcmQiLCJmb3JtYXRMYXllckRhdGEiLCJkYXRhc2V0cyIsIm9sZExheWVyRGF0YSIsIl90aGlzMyIsImRhdGFJZCIsInRleHRMYWJlbCIsIl9kYXRhc2V0cyR0aGlzJGNvbmZpZyIsImdwdUZpbHRlciIsIl90aGlzJHVwZGF0ZURhdGEiLCJ1cGRhdGVEYXRhIiwidHJpZ2dlckNoYW5nZWQiLCJ0ZXh0TGFiZWxzIiwiZm9ybWF0VGV4dExhYmVsRGF0YSIsImFjY2Vzc29ycyIsImdldEF0dHJpYnV0ZUFjY2Vzc29ycyIsImlzRmlsdGVyZWRBY2Nlc3NvciIsImdldEZpbHRlclZhbHVlIiwiZmlsdGVyVmFsdWVBY2Nlc3NvciIsImdldEZpbHRlcmVkIiwidXBkYXRlTGF5ZXJNZXRhIiwiZ2V0RmVhdHVyZSIsImdldEdlb2pzb25Qb2ludERhdGFNYXBzIiwiYm91bmRzRnJvbU1ldGFkYXRhIiwiZ2V0Qm91bmRzRnJvbUFycm93TWV0YWRhdGEiLCJ1cGRhdGVNZXRhIiwiYm91bmRzIiwiZ2V0UG9pbnRzQm91bmRzIiwicmVuZGVyTGF5ZXIiLCJvcHRzIiwiX3RoaXMkY29uZmlnJGNvbHVtbnMkIiwib2JqZWN0SG92ZXJlZCIsIm1hcFN0YXRlIiwiaW50ZXJhY3Rpb25Db25maWciLCJzaXplRmllbGQiLCJyYWRpdXNTY2FsZSIsImdldFJhZGl1c1NjYWxlQnlab29tIiwibGF5ZXJQcm9wcyIsInN0cm9rZWQiLCJsaW5lV2lkdGhTY2FsZSIsInJhZGl1c01heFBpeGVscyIsInVwZGF0ZVRyaWdnZXJzIiwiZmlsdGVyVmFsdWVVcGRhdGVUcmlnZ2VycyIsImdldFZpc3VhbENoYW5uZWxVcGRhdGVUcmlnZ2VycyIsInVzZUFycm93TGF5ZXIiLCJkZWZhdWx0TGF5ZXJQcm9wcyIsImdldERlZmF1bHREZWNrTGF5ZXJQcm9wcyIsImJydXNoaW5nUHJvcHMiLCJnZXRCcnVzaGluZ0V4dGVuc2lvblByb3BzIiwiZ2V0UGl4ZWxPZmZzZXQiLCJnZXRUZXh0T2Zmc2V0QnlSYWRpdXMiLCJnZXRSYWRpdXMiLCJleHRlbnNpb25zIiwic2hhcmVkUHJvcHMiLCJmaWx0ZXJSYW5nZSIsInZpc2libGUiLCJob3ZlcmVkT2JqZWN0IiwiaGFzSG92ZXJlZE9iamVjdCIsIl90aGlzJGNvbmZpZyR2aXNDb25maSIsIm5laWdoYm9yc0RhdGEiLCJnZXROZWlnaGJvcnMiLCJTY2F0dGVycGxvdExheWVyQ2xhc3MiLCJTY2F0dGVycGxvdExheWVyIiwiZGVja0xheWVyRGF0YSIsIkdlb0Fycm93U2NhdHRlcnBsb3RMYXllciIsInBhcmFtZXRlcnMiLCJkZXB0aFRlc3QiLCJsaW5lV2lkdGhVbml0cyIsInBpY2thYmxlIiwiYXV0b0hpZ2hsaWdodCIsImdldERlZmF1bHRIb3ZlckxheWVyUHJvcHMiLCJnZXRMaW5lQ29sb3IiLCJoaWdobGlnaHRDb2xvciIsImdldEZpbGxDb2xvciIsInJlbmRlclRleHRMYWJlbExheWVyIiwib2JqZWN0SW5mbyIsImlzTGF5ZXJIb3ZlcmVkRnJvbUFycm93IiwiaWQiLCJnZXRIb3ZlckRhdGEiLCJvYmplY3QiLCJmaWVsZHMiLCJhbmltYXRpb25Db25maWciLCJob3ZlckluZm8iLCJmaW5kRGVmYXVsdExheWVyUHJvcHMiLCJfZGF0YXNldCRmaWVsZFBhaXJzIiwiZmllbGRQYWlycyIsIkRhdGFzZXRUeXBlIiwiVkVDVE9SX1RJTEUiLCJwYWlyIiwibGF0RmllbGQiLCJwcm9wIiwiZGVmYXVsdE5hbWUiLCJERUZBVUxUX0xBWUVSX0NPTE9SIiwiaGV4VG9SZ2IiLCJpc1Zpc2libGUiLCJhc3NpZ25Qb2ludFBhaXJUb0xheWVyQ29sdW1uIiwiYWx0UHJvcHMiLCJnZXRHZW9BcnJvd1BvaW50TGF5ZXJQcm9wcyIsIkxheWVyIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL3BvaW50LWxheWVyL3BvaW50LWxheWVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBNSVRcbi8vIENvcHlyaWdodCBjb250cmlidXRvcnMgdG8gdGhlIGtlcGxlci5nbCBwcm9qZWN0XG5cbmltcG9ydCAqIGFzIGFycm93IGZyb20gJ2FwYWNoZS1hcnJvdyc7XG5cbmltcG9ydCB7QnJ1c2hpbmdFeHRlbnNpb259IGZyb20gJ0BkZWNrLmdsL2V4dGVuc2lvbnMnO1xuaW1wb3J0IHtTY2F0dGVycGxvdExheWVyfSBmcm9tICdAZGVjay5nbC9sYXllcnMnO1xuXG5pbXBvcnQge0dlb0Fycm93U2NhdHRlcnBsb3RMYXllcn0gZnJvbSAnQGtlcGxlci5nbC9kZWNrZ2wtYXJyb3ctbGF5ZXJzJztcbmltcG9ydCB7RmlsdGVyQXJyb3dFeHRlbnNpb259IGZyb20gJ0BrZXBsZXIuZ2wvZGVja2dsLWxheWVycyc7XG5cbmltcG9ydCBMYXllciwge1xuICBMYXllckJhc2VDb25maWcsXG4gIExheWVyQmFzZUNvbmZpZ1BhcnRpYWwsXG4gIExheWVyQ29sb3JDb25maWcsXG4gIExheWVyU2l6ZUNvbmZpZyxcbiAgTGF5ZXJTdHJva2VDb2xvckNvbmZpZ1xufSBmcm9tICcuLi9iYXNlLWxheWVyJztcbmltcG9ydCB7XG4gIGhleFRvUmdiLFxuICBmaW5kRGVmYXVsdENvbG9yRmllbGQsXG4gIERhdGFDb250YWluZXJJbnRlcmZhY2UsXG4gIEFycm93RGF0YUNvbnRhaW5lclxufSBmcm9tICdAa2VwbGVyLmdsL3V0aWxzJztcbmltcG9ydCB7ZGVmYXVsdCBhcyBLZXBsZXJUYWJsZX0gZnJvbSAnQGtlcGxlci5nbC90YWJsZSc7XG5pbXBvcnQgUG9pbnRMYXllckljb24gZnJvbSAnLi9wb2ludC1sYXllci1pY29uJztcbmltcG9ydCB7XG4gIERhdGFzZXRUeXBlLFxuICBMQVlFUl9WSVNfQ09ORklHUyxcbiAgREVGQVVMVF9MQVlFUl9DT0xPUixcbiAgQ0hBTk5FTF9TQ0FMRVMsXG4gIERFRkFVTFRfQ09MT1JfVUlcbn0gZnJvbSAnQGtlcGxlci5nbC9jb25zdGFudHMnO1xuaW1wb3J0IHtnZXRUZXh0T2Zmc2V0QnlSYWRpdXMsIGZvcm1hdFRleHRMYWJlbERhdGF9IGZyb20gJy4uL2xheWVyLXRleHQtbGFiZWwnO1xuaW1wb3J0IHtcbiAgYXNzaWduUG9pbnRQYWlyVG9MYXllckNvbHVtbixcbiAgaXNMYXllckhvdmVyZWRGcm9tQXJyb3csXG4gIGdldEJvdW5kc0Zyb21BcnJvd01ldGFkYXRhLFxuICBnZXRHZW9BcnJvd1BvaW50TGF5ZXJQcm9wcyxcbiAgaXNHZW9BcnJvd1BvaW50RmllbGQsXG4gIGNyZWF0ZUdlb0Fycm93UG9pbnRWZWN0b3IsXG4gIGdldEZpbHRlcmVkSW5kZXgsXG4gIGdldE5laWdoYm9ycyxcbiAgRmluZERlZmF1bHRMYXllclByb3BzXG59IGZyb20gJy4uL2xheWVyLXV0aWxzJztcbmltcG9ydCB7Z2V0R2VvanNvblBvaW50RGF0YU1hcHMsIEdlb2pzb25Qb2ludERhdGFNYXBzfSBmcm9tICcuLi9nZW9qc29uLWxheWVyL2dlb2pzb24tdXRpbHMnO1xuaW1wb3J0IHtcbiAgQ29sb3JSYW5nZSxcbiAgTWVyZ2UsXG4gIFJHQkNvbG9yLFxuICBWaXNDb25maWdCb29sZWFuLFxuICBWaXNDb25maWdDb2xvclJhbmdlLFxuICBWaXNDb25maWdDb2xvclNlbGVjdCxcbiAgVmlzQ29uZmlnTnVtYmVyLFxuICBWaXNDb25maWdSYW5nZSxcbiAgTGF5ZXJDb2x1bW4sXG4gIEZpZWxkLFxuICBBbmltYXRpb25Db25maWdcbn0gZnJvbSAnQGtlcGxlci5nbC90eXBlcyc7XG5cbmV4cG9ydCB0eXBlIFBvaW50TGF5ZXJWaXNDb25maWdTZXR0aW5ncyA9IHtcbiAgcmFkaXVzOiBWaXNDb25maWdOdW1iZXI7XG4gIGZpeGVkUmFkaXVzOiBWaXNDb25maWdCb29sZWFuO1xuICBvcGFjaXR5OiBWaXNDb25maWdOdW1iZXI7XG4gIG91dGxpbmU6IFZpc0NvbmZpZ0Jvb2xlYW47XG4gIHRoaWNrbmVzczogVmlzQ29uZmlnTnVtYmVyO1xuICBzdHJva2VDb2xvcjogVmlzQ29uZmlnQ29sb3JTZWxlY3Q7XG4gIGNvbG9yUmFuZ2U6IFZpc0NvbmZpZ0NvbG9yUmFuZ2U7XG4gIHN0cm9rZUNvbG9yUmFuZ2U6IFZpc0NvbmZpZ0NvbG9yUmFuZ2U7XG4gIHJhZGl1c1JhbmdlOiBWaXNDb25maWdSYW5nZTtcbiAgZmlsbGVkOiBWaXNDb25maWdCb29sZWFuO1xufTtcblxuZXhwb3J0IHR5cGUgUG9pbnRMYXllckNvbHVtbnNDb25maWcgPSB7XG4gIGxhdDogTGF5ZXJDb2x1bW47XG4gIGxuZzogTGF5ZXJDb2x1bW47XG4gIGFsdGl0dWRlPzogTGF5ZXJDb2x1bW47XG4gIG5laWdoYm9ycz86IExheWVyQ29sdW1uO1xuICBnZW9qc29uOiBMYXllckNvbHVtbjtcbiAgZ2VvYXJyb3c6IExheWVyQ29sdW1uO1xufTtcblxuZXhwb3J0IHR5cGUgUG9pbnRMYXllclZpc0NvbmZpZyA9IHtcbiAgcmFkaXVzOiBudW1iZXI7XG4gIGZpeGVkUmFkaXVzOiBib29sZWFuO1xuICBvcGFjaXR5OiBudW1iZXI7XG4gIG91dGxpbmU6IGJvb2xlYW47XG4gIHRoaWNrbmVzczogbnVtYmVyO1xuICBzdHJva2VDb2xvcjogUkdCQ29sb3I7XG4gIGNvbG9yUmFuZ2U6IENvbG9yUmFuZ2U7XG4gIHN0cm9rZUNvbG9yUmFuZ2U6IENvbG9yUmFuZ2U7XG4gIHJhZGl1c1JhbmdlOiBbbnVtYmVyLCBudW1iZXJdO1xuICBmaWxsZWQ6IGJvb2xlYW47XG4gIGJpbGxib2FyZDogYm9vbGVhbjtcbiAgYWxsb3dIb3ZlcjogYm9vbGVhbjtcbiAgc2hvd05laWdoYm9yT25Ib3ZlcjogYm9vbGVhbjtcbiAgc2hvd0hpZ2hsaWdodENvbG9yOiBib29sZWFuO1xufTtcbmV4cG9ydCB0eXBlIFBvaW50TGF5ZXJWaXN1YWxDaGFubmVsQ29uZmlnID0gTGF5ZXJDb2xvckNvbmZpZyAmXG4gIExheWVyU2l6ZUNvbmZpZyAmXG4gIExheWVyU3Ryb2tlQ29sb3JDb25maWc7XG5leHBvcnQgdHlwZSBQb2ludExheWVyQ29uZmlnID0gTWVyZ2U8XG4gIExheWVyQmFzZUNvbmZpZyxcbiAge2NvbHVtbnM6IFBvaW50TGF5ZXJDb2x1bW5zQ29uZmlnOyB2aXNDb25maWc6IFBvaW50TGF5ZXJWaXNDb25maWd9XG4+ICZcbiAgUG9pbnRMYXllclZpc3VhbENoYW5uZWxDb25maWc7XG5cbmV4cG9ydCB0eXBlIFBvaW50TGF5ZXJEYXRhID0ge1xuICBwb3NpdGlvbjogbnVtYmVyW107XG4gIGluZGV4OiBudW1iZXI7XG4gIG5laWdoYm9yczogYW55W107XG59O1xuXG5leHBvcnQgY29uc3QgcG9pbnRQb3NBY2Nlc3NvciA9XG4gICh7bGF0LCBsbmcsIGFsdGl0dWRlfTogUG9pbnRMYXllckNvbHVtbnNDb25maWcpID0+XG4gIChkYzogRGF0YUNvbnRhaW5lckludGVyZmFjZSkgPT5cbiAgKGQ6IHtpbmRleDogbnVtYmVyfSkgPT5cbiAgICBbXG4gICAgICBkYy52YWx1ZUF0KGQuaW5kZXgsIGxuZy5maWVsZElkeCksXG4gICAgICBkYy52YWx1ZUF0KGQuaW5kZXgsIGxhdC5maWVsZElkeCksXG4gICAgICBhbHRpdHVkZSAmJiBhbHRpdHVkZS5maWVsZElkeCA+IC0xID8gZGMudmFsdWVBdChkLmluZGV4LCBhbHRpdHVkZS5maWVsZElkeCkgOiAwXG4gICAgXTtcblxuZXhwb3J0IGNvbnN0IGdlb2pzb25Qb3NBY2Nlc3NvciA9XG4gICh7Z2VvanNvbn06IHtnZW9qc29uOiBMYXllckNvbHVtbn0pID0+XG4gIGQgPT5cbiAgICBkW2dlb2pzb24uZmllbGRJZHhdO1xuXG5leHBvcnQgY29uc3QgZ2VvYXJyb3dQb3NBY2Nlc3NvciA9XG4gICh7Z2VvYXJyb3d9OiBQb2ludExheWVyQ29sdW1uc0NvbmZpZykgPT5cbiAgKGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+XG4gIChkOiB7aW5kZXg6IG51bWJlcn0pID0+IHtcbiAgICBjb25zdCByb3cgPSBkYXRhQ29udGFpbmVyLnZhbHVlQXQoZC5pbmRleCwgZ2VvYXJyb3cuZmllbGRJZHgpO1xuICAgIHJldHVybiBbcm93LmdldCgwKSwgcm93LmdldCgxKSwgMF07XG4gIH07XG5cbmV4cG9ydCBjb25zdCBDT0xVTU5fTU9ERV9QT0lOVFMgPSAncG9pbnRzJztcbmV4cG9ydCBjb25zdCBDT0xVTU5fTU9ERV9HRU9KU09OID0gJ2dlb2pzb24nO1xuZXhwb3J0IGNvbnN0IENPTFVNTl9NT0RFX0dFT0FSUk9XID0gJ2dlb2Fycm93JztcblxuZXhwb3J0IGNvbnN0IHBvaW50UmVxdWlyZWRDb2x1bW5zOiBbJ2xhdCcsICdsbmcnXSA9IFsnbGF0JywgJ2xuZyddO1xuZXhwb3J0IGNvbnN0IHBvaW50T3B0aW9uYWxDb2x1bW5zOiBbJ2FsdGl0dWRlJywgJ25laWdoYm9ycyddID0gWydhbHRpdHVkZScsICduZWlnaGJvcnMnXTtcbmV4cG9ydCBjb25zdCBnZW9qc29uUmVxdWlyZWRDb2x1bW5zOiBbJ2dlb2pzb24nXSA9IFsnZ2VvanNvbiddO1xuZXhwb3J0IGNvbnN0IGdlb2Fycm93UmVxdWlyZWRDb2x1bW5zOiBbJ2dlb2Fycm93J10gPSBbJ2dlb2Fycm93J107XG5cbmNvbnN0IFNVUFBPUlRFRF9DT0xVTU5fTU9ERVMgPSBbXG4gIHtcbiAgICBrZXk6IENPTFVNTl9NT0RFX1BPSU5UUyxcbiAgICBsYWJlbDogJ1BvaW50IENvbHVtbnMnLFxuICAgIHJlcXVpcmVkQ29sdW1uczogcG9pbnRSZXF1aXJlZENvbHVtbnMsXG4gICAgb3B0aW9uYWxDb2x1bW5zOiBwb2ludE9wdGlvbmFsQ29sdW1uc1xuICB9LFxuICB7XG4gICAga2V5OiBDT0xVTU5fTU9ERV9HRU9KU09OLFxuICAgIGxhYmVsOiAnR2VvSlNPTiBGZWF0dXJlJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IGdlb2pzb25SZXF1aXJlZENvbHVtbnMsXG4gICAgdmVyaWZ5RmllbGQ6IGYgPT4gIWlzR2VvQXJyb3dQb2ludEZpZWxkKGYpXG4gIH0sXG4gIHtcbiAgICBrZXk6IENPTFVNTl9NT0RFX0dFT0FSUk9XLFxuICAgIGxhYmVsOiAnR2VvYXJyb3cgUG9pbnRzJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IGdlb2Fycm93UmVxdWlyZWRDb2x1bW5zLFxuICAgIHZlcmlmeUZpZWxkOiBmID0+IGlzR2VvQXJyb3dQb2ludEZpZWxkKGYpXG4gIH1cbl07XG5jb25zdCBERUZBVUxUX0NPTFVNTl9NT0RFID0gQ09MVU1OX01PREVfUE9JTlRTO1xuXG5jb25zdCBicnVzaGluZ0V4dGVuc2lvbiA9IG5ldyBCcnVzaGluZ0V4dGVuc2lvbigpO1xuY29uc3QgYXJyb3dDUFVGaWx0ZXJFeHRlbnNpb24gPSBuZXcgRmlsdGVyQXJyb3dFeHRlbnNpb24oKTtcblxuZnVuY3Rpb24gcHVzaFBvaW50UG9zaXRpb24oZGF0YTogYW55W10sIHBvczogbnVtYmVyW10sIGluZGV4OiBudW1iZXIsIG5laWdoYm9ycz86IG51bWJlcltdKSB7XG4gIGlmIChwb3MuZXZlcnkoTnVtYmVyLmlzRmluaXRlKSkge1xuICAgIGRhdGEucHVzaCh7XG4gICAgICBwb3NpdGlvbjogcG9zLFxuICAgICAgLy8gaW5kZXggaXMgaW1wb3J0YW50IGZvciBmaWx0ZXJcbiAgICAgIGluZGV4LFxuICAgICAgLi4uKG5laWdoYm9ycyA/IHtuZWlnaGJvcnN9IDoge30pXG4gICAgfSk7XG4gIH1cbn1cblxuZXhwb3J0IGNvbnN0IHBvaW50VmlzQ29uZmlnczoge1xuICByYWRpdXM6ICdyYWRpdXMnO1xuICBmaXhlZFJhZGl1czogJ2ZpeGVkUmFkaXVzJztcbiAgb3BhY2l0eTogJ29wYWNpdHknO1xuICBvdXRsaW5lOiAnb3V0bGluZSc7XG4gIHRoaWNrbmVzczogJ3RoaWNrbmVzcyc7XG4gIHN0cm9rZUNvbG9yOiAnc3Ryb2tlQ29sb3InO1xuICBjb2xvclJhbmdlOiAnY29sb3JSYW5nZSc7XG4gIHN0cm9rZUNvbG9yUmFuZ2U6ICdzdHJva2VDb2xvclJhbmdlJztcbiAgcmFkaXVzUmFuZ2U6ICdyYWRpdXNSYW5nZSc7XG4gIGZpbGxlZDogVmlzQ29uZmlnQm9vbGVhbjtcbiAgYmlsbGJvYXJkOiAnYmlsbGJvYXJkJztcbiAgYWxsb3dIb3ZlcjogJ2FsbG93SG92ZXInO1xuICBzaG93TmVpZ2hib3JPbkhvdmVyOiAnc2hvd05laWdoYm9yT25Ib3Zlcic7XG4gIHNob3dIaWdobGlnaHRDb2xvcjogJ3Nob3dIaWdobGlnaHRDb2xvcic7XG59ID0ge1xuICByYWRpdXM6ICdyYWRpdXMnLFxuICBmaXhlZFJhZGl1czogJ2ZpeGVkUmFkaXVzJyxcbiAgb3BhY2l0eTogJ29wYWNpdHknLFxuICBvdXRsaW5lOiAnb3V0bGluZScsXG4gIHRoaWNrbmVzczogJ3RoaWNrbmVzcycsXG4gIHN0cm9rZUNvbG9yOiAnc3Ryb2tlQ29sb3InLFxuICBjb2xvclJhbmdlOiAnY29sb3JSYW5nZScsXG4gIHN0cm9rZUNvbG9yUmFuZ2U6ICdzdHJva2VDb2xvclJhbmdlJyxcbiAgcmFkaXVzUmFuZ2U6ICdyYWRpdXNSYW5nZScsXG4gIGZpbGxlZDoge1xuICAgIC4uLkxBWUVSX1ZJU19DT05GSUdTLmZpbGxlZCxcbiAgICB0eXBlOiAnYm9vbGVhbicsXG4gICAgbGFiZWw6ICdsYXllci5maWxsQ29sb3InLFxuICAgIGRlZmF1bHRWYWx1ZTogdHJ1ZSxcbiAgICBwcm9wZXJ0eTogJ2ZpbGxlZCdcbiAgfSxcbiAgYmlsbGJvYXJkOiAnYmlsbGJvYXJkJyxcbiAgYWxsb3dIb3ZlcjogJ2FsbG93SG92ZXInLFxuICBzaG93TmVpZ2hib3JPbkhvdmVyOiAnc2hvd05laWdoYm9yT25Ib3ZlcicsXG4gIHNob3dIaWdobGlnaHRDb2xvcjogJ3Nob3dIaWdobGlnaHRDb2xvcidcbn07XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBvaW50TGF5ZXIgZXh0ZW5kcyBMYXllciB7XG4gIGRlY2xhcmUgY29uZmlnOiBQb2ludExheWVyQ29uZmlnO1xuICBkZWNsYXJlIHZpc0NvbmZpZ1NldHRpbmdzOiBQb2ludExheWVyVmlzQ29uZmlnU2V0dGluZ3M7XG4gIGRhdGFUb0ZlYXR1cmU6IEdlb2pzb25Qb2ludERhdGFNYXBzID0gW107XG5cbiAgZGF0YUNvbnRhaW5lcjogRGF0YUNvbnRhaW5lckludGVyZmFjZSB8IG51bGwgPSBudWxsO1xuICBnZW9BcnJvd1ZlY3RvcjogYXJyb3cuVmVjdG9yIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG4gIC8qXG4gICAqIENQVSBmaWx0ZXJpbmcgYW4gYXJyb3cgdGFibGUgYnkgdmFsdWVzIGFuZCBhc3NlbWJsaW5nIGEgcGFydGlhbCBjb3B5IG9mIHRoZSByYXcgdGFibGUgaXMgZXhwZW5zaXZlXG4gICAqIHNvIHdlIHdpbGwgdXNlIGZpbHRlcmVkSW5kZXggdG8gY3JlYXRlIGFuIGF0dHJpYnV0ZSBlLmcuIGZpbHRlcmVkSW5kZXggWzB8MV0gZm9yIEdQVSBmaWx0ZXJpbmdcbiAgICogaW4gZGVjay5nbCBsYXllciwgc2VlOiBGaWx0ZXJBcnJvd0V4dGVuc2lvbiBpbiBAa2VwbGVyLmdsL2RlY2tnbC1sYXllcnMuXG4gICAqIE5vdGUgdGhhdCB0aGlzIGFwcHJvYWNoIGNhbiBjcmVhdGUgdmlzaWJsZSBsYWdzIGluIGNhc2Ugb2YgYSBsb3Qgb2YgZGlzY2FydGVkIGdlb21ldHJ5LlxuICAgKi9cbiAgZmlsdGVyZWRJbmRleDogVWludDhDbGFtcGVkQXJyYXkgfCBudWxsID0gbnVsbDtcbiAgZmlsdGVyZWRJbmRleFRyaWdnZXI6IG51bWJlcltdID0gW107XG5cbiAgY29uc3RydWN0b3IocHJvcHMpIHtcbiAgICBzdXBlcihwcm9wcyk7XG5cbiAgICB0aGlzLnJlZ2lzdGVyVmlzQ29uZmlnKHBvaW50VmlzQ29uZmlncyk7XG4gICAgdGhpcy5nZXRQb3NpdGlvbkFjY2Vzc29yID0gKGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2UpID0+IHtcbiAgICAgIHN3aXRjaCAodGhpcy5jb25maWcuY29sdW1uTW9kZSkge1xuICAgICAgICBjYXNlIENPTFVNTl9NT0RFX0dFT0FSUk9XOlxuICAgICAgICAgIHJldHVybiBnZW9hcnJvd1Bvc0FjY2Vzc29yKHRoaXMuY29uZmlnLmNvbHVtbnMpKGRhdGFDb250YWluZXIpO1xuICAgICAgICBjYXNlIENPTFVNTl9NT0RFX0dFT0pTT046XG4gICAgICAgICAgcmV0dXJuIGdlb2pzb25Qb3NBY2Nlc3Nvcih0aGlzLmNvbmZpZy5jb2x1bW5zKTtcbiAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAvLyBDT0xVTU5fTU9ERV9QT0lOVFNcbiAgICAgICAgICByZXR1cm4gcG9pbnRQb3NBY2Nlc3Nvcih0aGlzLmNvbmZpZy5jb2x1bW5zKShkYXRhQ29udGFpbmVyKTtcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgZ2V0IHR5cGUoKTogJ3BvaW50JyB7XG4gICAgcmV0dXJuICdwb2ludCc7XG4gIH1cblxuICBnZXQgaXNBZ2dyZWdhdGVkKCk6IGZhbHNlIHtcbiAgICByZXR1cm4gZmFsc2U7XG4gIH1cblxuICBnZXQgbGF5ZXJJY29uKCkge1xuICAgIHJldHVybiBQb2ludExheWVySWNvbjtcbiAgfVxuXG4gIGdldCBvcHRpb25hbENvbHVtbnMoKSB7XG4gICAgcmV0dXJuIHBvaW50T3B0aW9uYWxDb2x1bW5zO1xuICB9XG5cbiAgZ2V0IGNvbHVtblBhaXJzKCkge1xuICAgIHJldHVybiB0aGlzLmRlZmF1bHRQb2ludENvbHVtblBhaXJzO1xuICB9XG5cbiAgZ2V0IHN1cHBvcnRlZENvbHVtbk1vZGVzKCkge1xuICAgIHJldHVybiBTVVBQT1JURURfQ09MVU1OX01PREVTO1xuICB9XG5cbiAgZ2V0IG5vbmVMYXllckRhdGFBZmZlY3RpbmdQcm9wcygpIHtcbiAgICByZXR1cm4gWy4uLnN1cGVyLm5vbmVMYXllckRhdGFBZmZlY3RpbmdQcm9wcywgJ3JhZGl1cyddO1xuICB9XG5cbiAgZ2V0IHZpc3VhbENoYW5uZWxzKCkge1xuICAgIHJldHVybiB7XG4gICAgICBjb2xvcjoge1xuICAgICAgICAuLi5zdXBlci52aXN1YWxDaGFubmVscy5jb2xvcixcbiAgICAgICAgYWNjZXNzb3I6ICdnZXRGaWxsQ29sb3InLFxuICAgICAgICBjb25kaXRpb246IGNvbmZpZyA9PiBjb25maWcudmlzQ29uZmlnLmZpbGxlZCxcbiAgICAgICAgZGVmYXVsdFZhbHVlOiBjb25maWcgPT4gY29uZmlnLmNvbG9yXG4gICAgICB9LFxuICAgICAgc3Ryb2tlQ29sb3I6IHtcbiAgICAgICAgcHJvcGVydHk6ICdzdHJva2VDb2xvcicsXG4gICAgICAgIGtleTogJ3N0cm9rZUNvbG9yJyxcbiAgICAgICAgZmllbGQ6ICdzdHJva2VDb2xvckZpZWxkJyxcbiAgICAgICAgc2NhbGU6ICdzdHJva2VDb2xvclNjYWxlJyxcbiAgICAgICAgZG9tYWluOiAnc3Ryb2tlQ29sb3JEb21haW4nLFxuICAgICAgICByYW5nZTogJ3N0cm9rZUNvbG9yUmFuZ2UnLFxuICAgICAgICBjaGFubmVsU2NhbGVUeXBlOiBDSEFOTkVMX1NDQUxFUy5jb2xvcixcbiAgICAgICAgYWNjZXNzb3I6ICdnZXRMaW5lQ29sb3InLFxuICAgICAgICBjb25kaXRpb246IGNvbmZpZyA9PiBjb25maWcudmlzQ29uZmlnLm91dGxpbmUsXG4gICAgICAgIGRlZmF1bHRWYWx1ZTogY29uZmlnID0+IGNvbmZpZy52aXNDb25maWcuc3Ryb2tlQ29sb3IgfHwgY29uZmlnLmNvbG9yXG4gICAgICB9LFxuICAgICAgc2l6ZToge1xuICAgICAgICAuLi5zdXBlci52aXN1YWxDaGFubmVscy5zaXplLFxuICAgICAgICBwcm9wZXJ0eTogJ3JhZGl1cycsXG4gICAgICAgIHJhbmdlOiAncmFkaXVzUmFuZ2UnLFxuICAgICAgICBmaXhlZDogJ2ZpeGVkUmFkaXVzJyxcbiAgICAgICAgY2hhbm5lbFNjYWxlVHlwZTogJ3JhZGl1cycsXG4gICAgICAgIGFjY2Vzc29yOiAnZ2V0UmFkaXVzJyxcbiAgICAgICAgZGVmYXVsdFZhbHVlOiAxXG4gICAgICB9XG4gICAgfTtcbiAgfVxuXG4gIHNldEluaXRpYWxMYXllckNvbmZpZyhkYXRhc2V0KSB7XG4gICAgaWYgKCFkYXRhc2V0LmRhdGFDb250YWluZXIubnVtUm93cygpKSB7XG4gICAgICByZXR1cm4gdGhpcztcbiAgICB9XG4gICAgY29uc3QgZGVmYXVsdENvbG9yRmllbGQgPSBmaW5kRGVmYXVsdENvbG9yRmllbGQoZGF0YXNldCk7XG5cbiAgICBpZiAoZGVmYXVsdENvbG9yRmllbGQpIHtcbiAgICAgIHRoaXMudXBkYXRlTGF5ZXJDb25maWcoe1xuICAgICAgICBjb2xvckZpZWxkOiBkZWZhdWx0Q29sb3JGaWVsZFxuICAgICAgfSk7XG4gICAgICB0aGlzLnVwZGF0ZUxheWVyVmlzdWFsQ2hhbm5lbChkYXRhc2V0LCAnY29sb3InKTtcbiAgICB9XG5cbiAgICByZXR1cm4gdGhpcztcbiAgfVxuXG4gIHN0YXRpYyBmaW5kRGVmYXVsdExheWVyUHJvcHMoZGF0YXNldDogS2VwbGVyVGFibGUpIHtcbiAgICBjb25zdCB7ZmllbGRQYWlycyA9IFtdLCB0eXBlfSA9IGRhdGFzZXQ7XG5cbiAgICBjb25zdCBwcm9wczogRmluZERlZmF1bHRMYXllclByb3BzW10gPSBbXTtcblxuICAgIGlmICh0eXBlID09PSBEYXRhc2V0VHlwZS5WRUNUT1JfVElMRSkge1xuICAgICAgcmV0dXJuIHtwcm9wc307XG4gICAgfVxuXG4gICAgLy8gTWFrZSBsYXllciBmb3IgZWFjaCBwYWlyXG4gICAgZmllbGRQYWlycy5mb3JFYWNoKHBhaXIgPT4ge1xuICAgICAgY29uc3QgbGF0RmllbGQgPSBwYWlyLnBhaXIubGF0O1xuXG4gICAgICBjb25zdCBwcm9wOiB7XG4gICAgICAgIGxhYmVsOiBzdHJpbmc7XG4gICAgICAgIGNvbG9yPzogUkdCQ29sb3I7XG4gICAgICAgIGlzVmlzaWJsZT86IGJvb2xlYW47XG4gICAgICAgIGNvbHVtbnM/OiBQb2ludExheWVyQ29sdW1uc0NvbmZpZztcbiAgICAgIH0gPSB7XG4gICAgICAgIGxhYmVsOiBwYWlyLmRlZmF1bHROYW1lIHx8ICdQb2ludCdcbiAgICAgIH07XG5cbiAgICAgIC8vIGRlZmF1bHQgbGF5ZXIgY29sb3IgZm9yIGJlZ2ludHJpcCBhbmQgZHJvcG9mZiBwb2ludFxuICAgICAgaWYgKGxhdEZpZWxkLnZhbHVlIGluIERFRkFVTFRfTEFZRVJfQ09MT1IpIHtcbiAgICAgICAgcHJvcC5jb2xvciA9IGhleFRvUmdiKERFRkFVTFRfTEFZRVJfQ09MT1JbbGF0RmllbGQudmFsdWVdKTtcbiAgICAgIH1cblxuICAgICAgLy8gc2V0IHRoZSBmaXJzdCBsYXllciB0byBiZSB2aXNpYmxlXG4gICAgICBpZiAocHJvcHMubGVuZ3RoID09PSAwKSB7XG4gICAgICAgIHByb3AuaXNWaXNpYmxlID0gdHJ1ZTtcbiAgICAgIH1cbiAgICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgbG9naWNhbGx5IHNlcGFyYXRlIGdlb2pzb24gY29sdW1uIHR5cGU/XG4gICAgICBwcm9wLmNvbHVtbnMgPSBhc3NpZ25Qb2ludFBhaXJUb0xheWVyQ29sdW1uKHBhaXIsIHRydWUpO1xuXG4gICAgICBwcm9wcy5wdXNoKHByb3ApO1xuICAgIH0pO1xuXG4gICAgY29uc3QgYWx0UHJvcHMgPSBnZXRHZW9BcnJvd1BvaW50TGF5ZXJQcm9wcyhkYXRhc2V0KTtcblxuICAgIHJldHVybiB7cHJvcHMsIGFsdFByb3BzfTtcbiAgfVxuXG4gIGdldERlZmF1bHRMYXllckNvbmZpZyhwcm9wczogTGF5ZXJCYXNlQ29uZmlnUGFydGlhbCkge1xuICAgIGNvbnN0IGRlZmF1bHRMYXllckNvbmZpZyA9IHN1cGVyLmdldERlZmF1bHRMYXllckNvbmZpZyhwcm9wcyA/PyB7fSk7XG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLmRlZmF1bHRMYXllckNvbmZpZyxcblxuICAgICAgY29sdW1uTW9kZTogcHJvcHM/LmNvbHVtbk1vZGUgPz8gREVGQVVMVF9DT0xVTU5fTU9ERSxcblxuICAgICAgLy8gYWRkIHN0cm9rZSBjb2xvciB2aXN1YWwgY2hhbm5lbFxuICAgICAgc3Ryb2tlQ29sb3JGaWVsZDogbnVsbCxcbiAgICAgIHN0cm9rZUNvbG9yRG9tYWluOiBbMCwgMV0sXG4gICAgICBzdHJva2VDb2xvclNjYWxlOiAncXVhbnRpbGUnLFxuICAgICAgY29sb3JVSToge1xuICAgICAgICAuLi5kZWZhdWx0TGF5ZXJDb25maWcuY29sb3JVSSxcbiAgICAgICAgc3Ryb2tlQ29sb3JSYW5nZTogREVGQVVMVF9DT0xPUl9VSVxuICAgICAgfVxuICAgIH07XG4gIH1cblxuICBjYWxjdWxhdGVEYXRhQXR0cmlidXRlKHtmaWx0ZXJlZEluZGV4LCBkYXRhQ29udGFpbmVyfTogS2VwbGVyVGFibGUsIGdldFBvc2l0aW9uKSB7XG4gICAgY29uc3Qge2NvbHVtbk1vZGV9ID0gdGhpcy5jb25maWc7XG5cbiAgICAvLyAxKSBDT0xVTU5fTU9ERV9HRU9BUlJPVyAtIHdoZW4gd2UgaGF2ZSBhIGdlb2Fycm93IHBvaW50IGNvbHVtblxuICAgIC8vIDIpIENPTFVNTl9NT0RFX1BPSU5UUyArIEFycm93RGF0YUNvbnRhaW5lciA+IGNyZWF0ZSBnZW9hcnJvdyBwb2ludCBjb2x1bW4gb24gdGhlIGZseVxuICAgIGlmIChcbiAgICAgIGRhdGFDb250YWluZXIgaW5zdGFuY2VvZiBBcnJvd0RhdGFDb250YWluZXIgJiZcbiAgICAgIChjb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9BUlJPVyB8fCBjb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9QT0lOVFMpXG4gICAgKSB7XG4gICAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSBnZXRGaWx0ZXJlZEluZGV4KFxuICAgICAgICBkYXRhQ29udGFpbmVyLm51bVJvd3MoKSxcbiAgICAgICAgZmlsdGVyZWRJbmRleCxcbiAgICAgICAgdGhpcy5maWx0ZXJlZEluZGV4XG4gICAgICApO1xuICAgICAgdGhpcy5maWx0ZXJlZEluZGV4VHJpZ2dlciA9IGZpbHRlcmVkSW5kZXg7XG5cbiAgICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9BUlJPVykge1xuICAgICAgICB0aGlzLmdlb0Fycm93VmVjdG9yID0gZGF0YUNvbnRhaW5lci5nZXRDb2x1bW4odGhpcy5jb25maWcuY29sdW1ucy5nZW9hcnJvdy5maWVsZElkeCk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBnZW5lcmF0ZSBhIGNvbHVtbiBjb21wYXRpYmxlIHdpdGggZ2VvYXJyb3cgcG9pbnRcbiAgICAgICAgdGhpcy5nZW9BcnJvd1ZlY3RvciA9IGNyZWF0ZUdlb0Fycm93UG9pbnRWZWN0b3IoZGF0YUNvbnRhaW5lciwgZ2V0UG9zaXRpb24pO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gZGF0YUNvbnRhaW5lci5nZXRUYWJsZSgpO1xuICAgIH1cblxuICAgIC8vIHdlIGRvbid0IG5lZWQgdGhlc2UgaW4gbm9uLUFycm93IG1vZGVzIGF0bS5cbiAgICB0aGlzLmdlb0Fycm93VmVjdG9yID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleCA9IG51bGw7XG5cbiAgICBjb25zdCBkYXRhOiBQb2ludExheWVyRGF0YVtdID0gW107XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGZpbHRlcmVkSW5kZXgubGVuZ3RoOyBpKyspIHtcbiAgICAgIGNvbnN0IGluZGV4ID0gZmlsdGVyZWRJbmRleFtpXTtcbiAgICAgIGxldCBuZWlnaGJvcnM7XG5cbiAgICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9QT0lOVFMpIHtcbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmNvbHVtbnMubmVpZ2hib3JzPy52YWx1ZSkge1xuICAgICAgICAgIGNvbnN0IHtmaWVsZElkeH0gPSB0aGlzLmNvbmZpZy5jb2x1bW5zLm5laWdoYm9ycztcbiAgICAgICAgICBuZWlnaGJvcnMgPSBBcnJheS5pc0FycmF5KGRhdGFDb250YWluZXIudmFsdWVBdChpbmRleCwgZmllbGRJZHgpKVxuICAgICAgICAgICAgPyBkYXRhQ29udGFpbmVyLnZhbHVlQXQoaW5kZXgsIGZpZWxkSWR4KVxuICAgICAgICAgICAgOiBbXTtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBwb3MgPSBnZXRQb3NpdGlvbih7aW5kZXh9KTtcblxuICAgICAgICAvLyBpZiBkb2Vzbid0IGhhdmUgcG9pbnQgbGF0IG9yIGxuZywgZG8gbm90IGFkZCB0aGUgcG9pbnRcbiAgICAgICAgLy8gZGVjay5nbCBjYW4ndCBoYW5kbGUgcG9zaXRpb24gPSBudWxsXG4gICAgICAgIHB1c2hQb2ludFBvc2l0aW9uKGRhdGEsIHBvcywgaW5kZXgsIG5laWdoYm9ycyk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBDT0xVTU5fTU9ERV9HRU9KU09OIG1vZGUgLSBwb2ludCBmcm9tIGdlb2pzb24gY29vcmRpbmF0ZXNcbiAgICAgICAgY29uc3QgY29vcmRpbmF0ZXMgPSB0aGlzLmRhdGFUb0ZlYXR1cmVbaV07XG4gICAgICAgIC8vIGlmIG11bHRpIHBvaW50c1xuICAgICAgICBpZiAoY29vcmRpbmF0ZXMgJiYgQXJyYXkuaXNBcnJheShjb29yZGluYXRlc1swXSkpIHtcbiAgICAgICAgICBjb29yZGluYXRlcy5mb3JFYWNoKGNvb3JkID0+IHtcbiAgICAgICAgICAgIHB1c2hQb2ludFBvc2l0aW9uKGRhdGEsIGNvb3JkLCBpbmRleCk7XG4gICAgICAgICAgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoY29vcmRpbmF0ZXMgJiYgTnVtYmVyLmlzRmluaXRlKGNvb3JkaW5hdGVzWzBdKSkge1xuICAgICAgICAgIHB1c2hQb2ludFBvc2l0aW9uKGRhdGEsIGNvb3JkaW5hdGVzIGFzIG51bWJlcltdLCBpbmRleCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gZGF0YTtcbiAgfVxuXG4gIGZvcm1hdExheWVyRGF0YShkYXRhc2V0cywgb2xkTGF5ZXJEYXRhKSB7XG4gICAgaWYgKHRoaXMuY29uZmlnLmRhdGFJZCA9PT0gbnVsbCkge1xuICAgICAgcmV0dXJuIHt9O1xuICAgIH1cbiAgICBjb25zdCB7dGV4dExhYmVsfSA9IHRoaXMuY29uZmlnO1xuICAgIGNvbnN0IHtncHVGaWx0ZXIsIGRhdGFDb250YWluZXJ9ID0gZGF0YXNldHNbdGhpcy5jb25maWcuZGF0YUlkXTtcbiAgICBjb25zdCB7ZGF0YSwgdHJpZ2dlckNoYW5nZWR9ID0gdGhpcy51cGRhdGVEYXRhKGRhdGFzZXRzLCBvbGRMYXllckRhdGEpO1xuICAgIGNvbnN0IGdldFBvc2l0aW9uID0gZCA9PiBkLnBvc2l0aW9uO1xuXG4gICAgLy8gZ2V0IGFsbCBkaXN0aW5jdCBjaGFyYWN0ZXJzIGluIHRoZSB0ZXh0IGxhYmVsc1xuICAgIGNvbnN0IHRleHRMYWJlbHMgPSBmb3JtYXRUZXh0TGFiZWxEYXRhKHtcbiAgICAgIHRleHRMYWJlbCxcbiAgICAgIHRyaWdnZXJDaGFuZ2VkLFxuICAgICAgb2xkTGF5ZXJEYXRhLFxuICAgICAgZGF0YSxcbiAgICAgIGRhdGFDb250YWluZXIsXG4gICAgICBmaWx0ZXJlZEluZGV4OiB0aGlzLmZpbHRlcmVkSW5kZXhcbiAgICB9KTtcblxuICAgIGNvbnN0IGFjY2Vzc29ycyA9IHRoaXMuZ2V0QXR0cmlidXRlQWNjZXNzb3JzKHtkYXRhQ29udGFpbmVyfSk7XG5cbiAgICBjb25zdCBpc0ZpbHRlcmVkQWNjZXNzb3IgPSAoZGF0YToge2luZGV4OiBudW1iZXJ9KSA9PiB7XG4gICAgICByZXR1cm4gdGhpcy5maWx0ZXJlZEluZGV4ID8gdGhpcy5maWx0ZXJlZEluZGV4W2RhdGEuaW5kZXhdIDogMTtcbiAgICB9O1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIGRhdGEsXG4gICAgICBnZXRQb3NpdGlvbixcbiAgICAgIGdldEZpbHRlclZhbHVlOiBncHVGaWx0ZXIuZmlsdGVyVmFsdWVBY2Nlc3NvcihkYXRhQ29udGFpbmVyKSgpLFxuICAgICAgZ2V0RmlsdGVyZWQ6IGlzRmlsdGVyZWRBY2Nlc3NvcixcbiAgICAgIHRleHRMYWJlbHMsXG4gICAgICAuLi5hY2Nlc3NvcnNcbiAgICB9O1xuICB9XG4gIC8qIGVzbGludC1lbmFibGUgY29tcGxleGl0eSAqL1xuXG4gIHVwZGF0ZUxheWVyTWV0YShkYXRhc2V0OiBLZXBsZXJUYWJsZSkge1xuICAgIGNvbnN0IHtkYXRhQ29udGFpbmVyfSA9IGRhdGFzZXQ7XG4gICAgdGhpcy5kYXRhQ29udGFpbmVyID0gZGF0YUNvbnRhaW5lcjtcblxuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9KU09OKSB7XG4gICAgICBjb25zdCBnZXRGZWF0dXJlID0gdGhpcy5nZXRQb3NpdGlvbkFjY2Vzc29yKCk7XG4gICAgICB0aGlzLmRhdGFUb0ZlYXR1cmUgPSBnZXRHZW9qc29uUG9pbnREYXRhTWFwcyhkYXRhQ29udGFpbmVyLCBnZXRGZWF0dXJlKTtcbiAgICB9IGVsc2UgaWYgKHRoaXMuY29uZmlnLmNvbHVtbk1vZGUgPT09IENPTFVNTl9NT0RFX0dFT0FSUk9XKSB7XG4gICAgICBjb25zdCBib3VuZHNGcm9tTWV0YWRhdGEgPSBnZXRCb3VuZHNGcm9tQXJyb3dNZXRhZGF0YShcbiAgICAgICAgdGhpcy5jb25maWcuY29sdW1ucy5nZW9hcnJvdyxcbiAgICAgICAgZGF0YUNvbnRhaW5lciBhcyBBcnJvd0RhdGFDb250YWluZXJcbiAgICAgICk7XG4gICAgICBpZiAoYm91bmRzRnJvbU1ldGFkYXRhKSB7XG4gICAgICAgIHRoaXMudXBkYXRlTWV0YSh7Ym91bmRzOiBib3VuZHNGcm9tTWV0YWRhdGF9KTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGNvbnN0IGdldFBvc2l0aW9uID0gdGhpcy5nZXRQb3NpdGlvbkFjY2Vzc29yKGRhdGFDb250YWluZXIpO1xuICAgICAgICBjb25zdCBib3VuZHMgPSB0aGlzLmdldFBvaW50c0JvdW5kcyhkYXRhQ29udGFpbmVyLCBnZXRQb3NpdGlvbik7XG4gICAgICAgIHRo