kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
515 lines (509 loc) • 78.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.neighborRequiredColumns = exports.geoarrowRequiredColumns = exports["default"] = exports.arcVisConfigs = exports.arcRequiredColumns = exports.arcPosAccessor = exports.arcColumnLabels = exports.COLUMN_MODE_POINTS = exports.COLUMN_MODE_NEIGHBORS = 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 _baseLayer = _interopRequireDefault(require("../base-layer"));
var _extensions = require("@deck.gl/extensions");
var _deckglArrowLayers = require("@kepler.gl/deckgl-arrow-layers");
var _deckglLayers = require("@kepler.gl/deckgl-layers");
var _layers = require("@deck.gl/layers");
var _utils = require("@kepler.gl/utils");
var _arcLayerIcon = _interopRequireDefault(require("./arc-layer-icon"));
var _layerUtils = require("../layer-utils");
var _constants = require("@kepler.gl/constants");
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
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 arcRequiredColumns = exports.arcRequiredColumns = ['lat0', 'lng0', 'lat1', 'lng1'];
var neighborRequiredColumns = exports.neighborRequiredColumns = ['lat', 'lng', 'neighbors'];
var geoarrowRequiredColumns = exports.geoarrowRequiredColumns = ['geoarrow0', 'geoarrow1'];
var arcColumnLabels = exports.arcColumnLabels = {
lat0: 'arc.lat0',
lng0: 'arc.lng0',
lat1: 'arc.lat1',
lng1: 'arc.lng1',
neighbors: 'neighbors'
};
var arcVisConfigs = exports.arcVisConfigs = {
opacity: 'opacity',
thickness: 'thickness',
colorRange: 'colorRange',
sizeRange: 'strokeWidthRange',
targetColor: 'targetColor'
};
var COLUMN_MODE_POINTS = exports.COLUMN_MODE_POINTS = 'points';
var COLUMN_MODE_NEIGHBORS = exports.COLUMN_MODE_NEIGHBORS = 'neighbors';
var COLUMN_MODE_GEOARROW = exports.COLUMN_MODE_GEOARROW = 'geoarrow';
var SUPPORTED_COLUMN_MODES = [{
key: COLUMN_MODE_POINTS,
label: 'Points',
requiredColumns: arcRequiredColumns
}, {
key: COLUMN_MODE_NEIGHBORS,
label: 'Point and Neighbors',
requiredColumns: neighborRequiredColumns
}, {
key: COLUMN_MODE_GEOARROW,
label: 'Geoarrow Points',
requiredColumns: geoarrowRequiredColumns
}];
var DEFAULT_COLUMN_MODE = COLUMN_MODE_POINTS;
var brushingExtension = new _extensions.BrushingExtension();
var arrowCPUFilterExtension = new _deckglLayers.FilterArrowExtension();
function isH3Field(columns, allFields, key) {
var field = allFields[columns[key].fieldIdx];
return (field === null || field === void 0 ? void 0 : field.type) === _constants.ALL_FIELD_TYPES.h3;
}
var arcPosAccessor = exports.arcPosAccessor = function arcPosAccessor(_ref, columnMode) {
var lat0 = _ref.lat0,
lng0 = _ref.lng0,
lat1 = _ref.lat1,
lng1 = _ref.lng1,
lat = _ref.lat,
lng = _ref.lng,
geoarrow0 = _ref.geoarrow0,
geoarrow1 = _ref.geoarrow1;
return function (dc) {
switch (columnMode) {
case COLUMN_MODE_GEOARROW:
return function (d) {
var start = dc.valueAt(d.index, geoarrow0.fieldIdx);
var end = dc.valueAt(d.index, geoarrow1.fieldIdx);
return [start.get(0), start.get(1), 0, end.get(2), end.get(3), 0];
};
case COLUMN_MODE_NEIGHBORS:
return function (d) {
var startPos = (0, _utils.maybeHexToGeo)(dc, d, lat, lng);
// only return source point if columnMode is COLUMN_MODE_NEIGHBORS
return [startPos ? startPos[0] : dc.valueAt(d.index, lng.fieldIdx), startPos ? startPos[1] : dc.valueAt(d.index, lat.fieldIdx), 0];
};
default:
// COLUMN_MODE_POINTS
return function (d) {
// lat or lng column could be hex column
// we assume string value is hex and try to convert it to geo lat lng
var startPos = (0, _utils.maybeHexToGeo)(dc, d, lat0, lng0);
var endPos = (0, _utils.maybeHexToGeo)(dc, d, lat1, lng1);
return [startPos ? startPos[0] : dc.valueAt(d.index, lng0.fieldIdx), startPos ? startPos[1] : dc.valueAt(d.index, lat0.fieldIdx), 0, endPos ? endPos[0] : dc.valueAt(d.index, lng1.fieldIdx), endPos ? endPos[1] : dc.valueAt(d.index, lat1.fieldIdx), 0];
};
}
};
};
var ArcLayer = exports["default"] = /*#__PURE__*/function (_Layer) {
function ArcLayer(props) {
var _this;
(0, _classCallCheck2["default"])(this, ArcLayer);
_this = _callSuper(this, ArcLayer, [props]);
(0, _defineProperty2["default"])(_this, "dataContainer", null);
(0, _defineProperty2["default"])(_this, "geoArrowVector0", undefined);
(0, _defineProperty2["default"])(_this, "geoArrowVector1", 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(arcVisConfigs);
_this.getPositionAccessor = function (dataContainer) {
return arcPosAccessor(_this.config.columns, _this.config.columnMode)(dataContainer);
};
return _this;
}
(0, _inherits2["default"])(ArcLayer, _Layer);
return (0, _createClass2["default"])(ArcLayer, [{
key: "type",
get: function get() {
return 'arc';
}
}, {
key: "isAggregated",
get: function get() {
return false;
}
}, {
key: "layerIcon",
get: function get() {
return _arcLayerIcon["default"];
}
}, {
key: "columnLabels",
get: function get() {
return arcColumnLabels;
}
}, {
key: "columnPairs",
get: function get() {
return this.defaultLinkColumnPairs;
}
}, {
key: "supportedColumnModes",
get: function get() {
return SUPPORTED_COLUMN_MODES;
}
}, {
key: "visualChannels",
get: function get() {
return {
sourceColor: _objectSpread(_objectSpread({}, _superPropGet(ArcLayer, "visualChannels", this, 1).color), {}, {
property: 'color',
key: 'sourceColor',
accessor: 'getSourceColor',
defaultValue: function defaultValue(config) {
return config.color;
}
}),
targetColor: _objectSpread(_objectSpread({}, _superPropGet(ArcLayer, "visualChannels", this, 1).color), {}, {
property: 'targetColor',
key: 'targetColor',
accessor: 'getTargetColor',
defaultValue: function defaultValue(config) {
return config.visConfig.targetColor || config.color;
}
}),
size: _objectSpread(_objectSpread({}, _superPropGet(ArcLayer, "visualChannels", this, 1).size), {}, {
accessor: 'getWidth',
property: 'stroke'
})
};
}
}, {
key: "columnValidators",
get: function get() {
// if one of the lat or lng column is string type, we allow it
// will try to pass it as hex
return {
lat0: function lat0(column, columns, allFields) {
return isH3Field(columns, allFields, 'lng0');
},
lng0: function lng0(column, columns, allFields) {
return isH3Field(columns, allFields, 'lat0');
},
lat1: function lat1(column, columns, allFields) {
return isH3Field(columns, allFields, 'lng1');
},
lng1: function lng1(column, columns, allFields) {
return isH3Field(columns, allFields, 'lat1');
},
lat: function lat(column, columns, allFields) {
return isH3Field(columns, allFields, 'lng');
},
lng: function lng(column, columns, allFields) {
return isH3Field(columns, allFields, 'lat');
}
};
}
}, {
key: "hasAllColumns",
value: function hasAllColumns() {
var columns = this.config.columns;
if (this.config.columnMode === COLUMN_MODE_GEOARROW) {
return this.hasColumnValue(columns.geoarrow0) && this.hasColumnValue(columns.geoarrow1);
}
if (this.config.columnMode === COLUMN_MODE_POINTS) {
// TODO - this does not have access to allFields...
// So we can't do the same validation as for the field errors
var _hasStart = this.hasColumnValue(columns.lat0) || this.hasColumnValue(columns.lng0);
var hasEnd = this.hasColumnValue(columns.lat1) || this.hasColumnValue(columns.lng1);
return _hasStart && hasEnd;
}
var hasStart = this.hasColumnValue(columns.lat) || this.hasColumnValue(columns.lng);
var hasNeibors = this.hasColumnValue(columns.neighbors);
return hasStart && hasNeibors;
}
}, {
key: "getDefaultLayerConfig",
value: function getDefaultLayerConfig(props) {
var _props$columnMode;
var defaultLayerConfig = _superPropGet(ArcLayer, "getDefaultLayerConfig", this, 3)([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
});
}
}, {
key: "calculateDataAttributeForGeoArrow",
value: function calculateDataAttributeForGeoArrow(_ref2, getPosition) {
var dataContainer = _ref2.dataContainer,
filteredIndex = _ref2.filteredIndex;
this.filteredIndex = (0, _layerUtils.getFilteredIndex)(dataContainer.numRows(), filteredIndex, this.filteredIndex);
this.filteredIndexTrigger = filteredIndex;
if (this.config.columnMode === COLUMN_MODE_GEOARROW) {
this.geoArrowVector0 = dataContainer.getColumn(this.config.columns.geoarrow0.fieldIdx);
this.geoArrowVector1 = dataContainer.getColumn(this.config.columns.geoarrow1.fieldIdx);
} else {
// generate columns compatible with geoarrow point extension
// TODO remove excessive intermediate allocations
this.geoArrowVector0 = (0, _layerUtils.createGeoArrowPointVector)(dataContainer, function (d) {
return getPosition(d).slice(0, 3);
});
this.geoArrowVector1 = (0, _layerUtils.createGeoArrowPointVector)(dataContainer, function (d) {
return getPosition(d).slice(3, 6);
});
}
return dataContainer.getTable();
}
}, {
key: "calculateDataAttributeForPoints",
value: function calculateDataAttributeForPoints(_ref3, getPosition) {
var filteredIndex = _ref3.filteredIndex;
var data = [];
for (var i = 0; i < filteredIndex.length; i++) {
var index = filteredIndex[i];
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
if (pos.every(Number.isFinite)) {
data.push({
index: index,
sourcePosition: [pos[0], pos[1], pos[2]],
targetPosition: [pos[3], pos[4], pos[5]]
});
}
}
return data;
}
}, {
key: "calculateDataAttributeForPointNNeighbors",
value: function calculateDataAttributeForPointNNeighbors(_ref4, getPosition) {
var _this2 = this;
var dataContainer = _ref4.dataContainer,
filteredIndex = _ref4.filteredIndex;
var data = [];
var _loop = function _loop() {
var index = filteredIndex[i];
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
if (pos.every(Number.isFinite)) {
// push all neibors
var neighborIdx = _this2.config.columns.neighbors.value ? dataContainer.valueAt(index, _this2.config.columns.neighbors.fieldIdx) : [];
if (Array.isArray(neighborIdx)) {
neighborIdx.forEach(function (idx) {
// TODO prevent row materialization here
var tPos = dataContainer.rowAsArray(idx) ? getPosition({
index: idx
}) : null;
if (tPos && tPos.every(Number.isFinite)) {
data.push({
index: index,
sourcePosition: [pos[0], pos[1], pos[2]],
targetPosition: [tPos[0], tPos[1], tPos[2]]
});
}
});
}
}
};
for (var i = 0; i < filteredIndex.length; i++) {
_loop();
}
return data;
}
}, {
key: "calculateDataAttribute",
value: function calculateDataAttribute(_ref5, getPosition) {
var dataContainer = _ref5.dataContainer,
filteredIndex = _ref5.filteredIndex;
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)) {
return this.calculateDataAttributeForGeoArrow({
dataContainer: dataContainer,
filteredIndex: filteredIndex
}, getPosition);
}
// we don't need these in non-Arrow modes atm.
this.geoArrowVector0 = undefined;
this.geoArrowVector1 = undefined;
this.filteredIndex = null;
if (this.config.columnMode === COLUMN_MODE_POINTS) {
return this.calculateDataAttributeForPoints({
dataContainer: dataContainer,
filteredIndex: filteredIndex
}, getPosition);
}
return this.calculateDataAttributeForPointNNeighbors({
dataContainer: dataContainer,
filteredIndex: filteredIndex
}, getPosition);
}
}, {
key: "formatLayerData",
value: function formatLayerData(datasets, oldLayerData) {
var _this3 = this;
if (this.config.dataId === null) {
return {};
}
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;
var accessors = this.getAttributeAccessors({
dataContainer: dataContainer
});
var isFilteredAccessor = function isFilteredAccessor(data) {
// for GeoArrow data is a buffer, so use objectInfo
return _this3.filteredIndex ? _this3.filteredIndex[data.index] : 1;
};
return _objectSpread({
data: data,
getFilterValue: gpuFilter.filterValueAccessor(dataContainer)(),
getFiltered: isFilteredAccessor
}, accessors);
}
/* eslint-enable complexity */
}, {
key: "updateLayerMeta",
value: function updateLayerMeta(dataset) {
var dataContainer = dataset.dataContainer;
this.dataContainer = dataContainer;
// get bounds from arcs
var getPosition = this.getPositionAccessor(dataContainer);
var sBounds = this.getPointsBounds(dataContainer, function (d) {
var pos = getPosition(d);
return [pos[0], pos[1]];
});
var tBounds = [];
if (this.config.columnMode === COLUMN_MODE_POINTS) {
tBounds = this.getPointsBounds(dataContainer, function (d) {
var pos = getPosition(d);
return [pos[3], pos[4]];
});
} else {
// when columnMode is neighbors, it reference the same collection of points
tBounds = sBounds;
}
var bounds = tBounds && sBounds ? [Math.min(sBounds[0], tBounds[0]), Math.min(sBounds[1], tBounds[1]), Math.max(sBounds[2], tBounds[2]), Math.max(sBounds[3], tBounds[3])] : sBounds || tBounds;
this.updateMeta({
bounds: bounds
});
}
}, {
key: "renderLayer",
value: function renderLayer(opts) {
var data = opts.data,
gpuFilter = opts.gpuFilter,
objectHovered = opts.objectHovered,
interactionConfig = opts.interactionConfig,
dataset = opts.dataset;
var updateTriggers = _objectSpread({
getPosition: this.config.columns,
getFilterValue: gpuFilter.filterValueUpdateTriggers,
getFiltered: this.filteredIndexTrigger
}, this.getVisualChannelUpdateTriggers());
var widthScale = this.config.visConfig.thickness * _constants.PROJECTED_PIXEL_SIZE_MULTIPLIER;
var defaultLayerProps = this.getDefaultDeckLayerProps(opts);
var hoveredObject = this.hasHoveredObject(objectHovered);
var useArrowLayer = Boolean(this.geoArrowVector0);
var ArcLayerClass = _layers.ArcLayer;
var experimentalPropOverrides = {};
if (useArrowLayer) {
ArcLayerClass = _deckglArrowLayers.GeoArrowArcLayer;
experimentalPropOverrides = {
data: dataset.dataContainer.getTable(),
getSourcePosition: this.geoArrowVector0,
getTargetPosition: this.geoArrowVector1
};
}
return [
// @ts-expect-error
new ArcLayerClass(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, defaultLayerProps), this.getBrushingExtensionProps(interactionConfig, 'source_target')), data), experimentalPropOverrides), {}, {
widthScale: widthScale,
updateTriggers: updateTriggers,
extensions: [].concat((0, _toConsumableArray2["default"])(defaultLayerProps.extensions), [brushingExtension], (0, _toConsumableArray2["default"])(useArrowLayer ? [arrowCPUFilterExtension] : []))
}))].concat((0, _toConsumableArray2["default"])(hoveredObject ? [new _layers.ArcLayer(_objectSpread(_objectSpread({}, this.getDefaultHoverLayerProps()), {}, {
visible: defaultLayerProps.visible,
data: [hoveredObject],
widthScale: widthScale,
getSourceColor: this.config.highlightColor,
getTargetColor: this.config.highlightColor,
getWidth: data.getWidth
}))] : []));
}
}, {
key: "hasHoveredObject",
value: function hasHoveredObject(objectInfo) {
if ((0, _layerUtils.isLayerHoveredFromArrow)(objectInfo, this.id) && objectInfo.index >= 0 && this.dataContainer) {
// objectInfo.index can point to data of arcs created in neighbor mode, so get index to source data.
var hoveredObject = _superPropGet(ArcLayer, "hasHoveredObject", this, 3)([objectInfo]);
return hoveredObject ? {
index: hoveredObject.index,
position: this.getPositionAccessor(this.dataContainer)({
index: hoveredObject.index
})
} : null;
}
return _superPropGet(ArcLayer, "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.geoArrowVector0 ? hoverInfo === null || hoverInfo === void 0 ? void 0 : hoverInfo.index : object.index;
if (index >= 0) {
return dataContainer.row(index);
}
return null;
}
}, {
key: "getLegendVisualChannels",
value: function getLegendVisualChannels() {
var _channels$sourceColor;
var channels = this.visualChannels;
if ((_channels$sourceColor = channels.sourceColor) !== null && _channels$sourceColor !== void 0 && _channels$sourceColor.field && this.config[channels.sourceColor.field]) {
// Remove targetColor to avoid duplicate legend
channels = _objectSpread({}, channels);
delete channels.targetColor;
}
return channels;
}
}], [{
key: "findDefaultLayerProps",
value: function findDefaultLayerProps(_ref6) {
var _ref6$fieldPairs = _ref6.fieldPairs,
fieldPairs = _ref6$fieldPairs === void 0 ? [] : _ref6$fieldPairs;
if (fieldPairs.length < 2) {
return {
props: []
};
}
var props = {
color: (0, _utils.hexToRgb)(_constants.DEFAULT_LAYER_COLOR.tripArc),
// connect the first two point layer with arc
// @ts-expect-error separate types for point / neighbor columns
columns: {
lat0: fieldPairs[0].pair.lat,
lng0: fieldPairs[0].pair.lng,
lat1: fieldPairs[1].pair.lat,
lng1: fieldPairs[1].pair.lng
},
label: "".concat(fieldPairs[0].defaultName, " -> ").concat(fieldPairs[1].defaultName, " arc")
};
return {
props: [props]
};
}
}]);
}(_baseLayer["default"]);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmFzZUxheWVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfZXh0ZW5zaW9ucyIsIl9kZWNrZ2xBcnJvd0xheWVycyIsIl9kZWNrZ2xMYXllcnMiLCJfbGF5ZXJzIiwiX3V0aWxzIiwiX2FyY0xheWVySWNvbiIsIl9sYXllclV0aWxzIiwiX2NvbnN0YW50cyIsIm93bktleXMiLCJlIiwiciIsInQiLCJPYmplY3QiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImRlZmluZVByb3BlcnR5IiwiX2NhbGxTdXBlciIsIl9nZXRQcm90b3R5cGVPZjIiLCJfcG9zc2libGVDb25zdHJ1Y3RvclJldHVybjIiLCJfaXNOYXRpdmVSZWZsZWN0Q29uc3RydWN0IiwiUmVmbGVjdCIsImNvbnN0cnVjdCIsImNvbnN0cnVjdG9yIiwiQm9vbGVhbiIsInByb3RvdHlwZSIsInZhbHVlT2YiLCJjYWxsIiwiX3N1cGVyUHJvcEdldCIsInAiLCJfZ2V0MiIsImFyY1JlcXVpcmVkQ29sdW1ucyIsImV4cG9ydHMiLCJuZWlnaGJvclJlcXVpcmVkQ29sdW1ucyIsImdlb2Fycm93UmVxdWlyZWRDb2x1bW5zIiwiYXJjQ29sdW1uTGFiZWxzIiwibGF0MCIsImxuZzAiLCJsYXQxIiwibG5nMSIsIm5laWdoYm9ycyIsImFyY1Zpc0NvbmZpZ3MiLCJvcGFjaXR5IiwidGhpY2tuZXNzIiwiY29sb3JSYW5nZSIsInNpemVSYW5nZSIsInRhcmdldENvbG9yIiwiQ09MVU1OX01PREVfUE9JTlRTIiwiQ09MVU1OX01PREVfTkVJR0hCT1JTIiwiQ09MVU1OX01PREVfR0VPQVJST1ciLCJTVVBQT1JURURfQ09MVU1OX01PREVTIiwia2V5IiwibGFiZWwiLCJyZXF1aXJlZENvbHVtbnMiLCJERUZBVUxUX0NPTFVNTl9NT0RFIiwiYnJ1c2hpbmdFeHRlbnNpb24iLCJCcnVzaGluZ0V4dGVuc2lvbiIsImFycm93Q1BVRmlsdGVyRXh0ZW5zaW9uIiwiRmlsdGVyQXJyb3dFeHRlbnNpb24iLCJpc0gzRmllbGQiLCJjb2x1bW5zIiwiYWxsRmllbGRzIiwiZmllbGQiLCJmaWVsZElkeCIsInR5cGUiLCJBTExfRklFTERfVFlQRVMiLCJoMyIsImFyY1Bvc0FjY2Vzc29yIiwiX3JlZiIsImNvbHVtbk1vZGUiLCJsYXQiLCJsbmciLCJnZW9hcnJvdzAiLCJnZW9hcnJvdzEiLCJkYyIsImQiLCJzdGFydCIsInZhbHVlQXQiLCJpbmRleCIsImVuZCIsImdldCIsInN0YXJ0UG9zIiwibWF5YmVIZXhUb0dlbyIsImVuZFBvcyIsIkFyY0xheWVyIiwiX0xheWVyIiwicHJvcHMiLCJfdGhpcyIsIl9jbGFzc0NhbGxDaGVjazIiLCJ1bmRlZmluZWQiLCJyZWdpc3RlclZpc0NvbmZpZyIsImdldFBvc2l0aW9uQWNjZXNzb3IiLCJkYXRhQ29udGFpbmVyIiwiY29uZmlnIiwiX2luaGVyaXRzMiIsIl9jcmVhdGVDbGFzczIiLCJBcmNMYXllckljb24iLCJkZWZhdWx0TGlua0NvbHVtblBhaXJzIiwic291cmNlQ29sb3IiLCJjb2xvciIsInByb3BlcnR5IiwiYWNjZXNzb3IiLCJkZWZhdWx0VmFsdWUiLCJ2aXNDb25maWciLCJzaXplIiwiY29sdW1uIiwidmFsdWUiLCJoYXNBbGxDb2x1bW5zIiwiaGFzQ29sdW1uVmFsdWUiLCJoYXNTdGFydCIsImhhc0VuZCIsImhhc05laWJvcnMiLCJnZXREZWZhdWx0TGF5ZXJDb25maWciLCJfcHJvcHMkY29sdW1uTW9kZSIsImRlZmF1bHRMYXllckNvbmZpZyIsImNhbGN1bGF0ZURhdGFBdHRyaWJ1dGVGb3JHZW9BcnJvdyIsIl9yZWYyIiwiZ2V0UG9zaXRpb24iLCJmaWx0ZXJlZEluZGV4IiwiZ2V0RmlsdGVyZWRJbmRleCIsIm51bVJvd3MiLCJmaWx0ZXJlZEluZGV4VHJpZ2dlciIsImdlb0Fycm93VmVjdG9yMCIsImdldENvbHVtbiIsImdlb0Fycm93VmVjdG9yMSIsImNyZWF0ZUdlb0Fycm93UG9pbnRWZWN0b3IiLCJzbGljZSIsImdldFRhYmxlIiwiY2FsY3VsYXRlRGF0YUF0dHJpYnV0ZUZvclBvaW50cyIsIl9yZWYzIiwiZGF0YSIsImkiLCJwb3MiLCJldmVyeSIsIk51bWJlciIsImlzRmluaXRlIiwic291cmNlUG9zaXRpb24iLCJ0YXJnZXRQb3NpdGlvbiIsImNhbGN1bGF0ZURhdGFBdHRyaWJ1dGVGb3JQb2ludE5OZWlnaGJvcnMiLCJfcmVmNCIsIl90aGlzMiIsIl9sb29wIiwibmVpZ2hib3JJZHgiLCJBcnJheSIsImlzQXJyYXkiLCJpZHgiLCJ0UG9zIiwicm93QXNBcnJheSIsImNhbGN1bGF0ZURhdGFBdHRyaWJ1dGUiLCJfcmVmNSIsIkFycm93RGF0YUNvbnRhaW5lciIsImZvcm1hdExheWVyRGF0YSIsImRhdGFzZXRzIiwib2xkTGF5ZXJEYXRhIiwiX3RoaXMzIiwiZGF0YUlkIiwiX2RhdGFzZXRzJHRoaXMkY29uZmlnIiwiZ3B1RmlsdGVyIiwiX3RoaXMkdXBkYXRlRGF0YSIsInVwZGF0ZURhdGEiLCJhY2Nlc3NvcnMiLCJnZXRBdHRyaWJ1dGVBY2Nlc3NvcnMiLCJpc0ZpbHRlcmVkQWNjZXNzb3IiLCJnZXRGaWx0ZXJWYWx1ZSIsImZpbHRlclZhbHVlQWNjZXNzb3IiLCJnZXRGaWx0ZXJlZCIsInVwZGF0ZUxheWVyTWV0YSIsImRhdGFzZXQiLCJzQm91bmRzIiwiZ2V0UG9pbnRzQm91bmRzIiwidEJvdW5kcyIsImJvdW5kcyIsIk1hdGgiLCJtaW4iLCJtYXgiLCJ1cGRhdGVNZXRhIiwicmVuZGVyTGF5ZXIiLCJvcHRzIiwib2JqZWN0SG92ZXJlZCIsImludGVyYWN0aW9uQ29uZmlnIiwidXBkYXRlVHJpZ2dlcnMiLCJmaWx0ZXJWYWx1ZVVwZGF0ZVRyaWdnZXJzIiwiZ2V0VmlzdWFsQ2hhbm5lbFVwZGF0ZVRyaWdnZXJzIiwid2lkdGhTY2FsZSIsIlBST0pFQ1RFRF9QSVhFTF9TSVpFX01VTFRJUExJRVIiLCJkZWZhdWx0TGF5ZXJQcm9wcyIsImdldERlZmF1bHREZWNrTGF5ZXJQcm9wcyIsImhvdmVyZWRPYmplY3QiLCJoYXNIb3ZlcmVkT2JqZWN0IiwidXNlQXJyb3dMYXllciIsIkFyY0xheWVyQ2xhc3MiLCJEZWNrQXJjTGF5ZXIiLCJleHBlcmltZW50YWxQcm9wT3ZlcnJpZGVzIiwiR2VvQXJyb3dBcmNMYXllciIsImdldFNvdXJjZVBvc2l0aW9uIiwiZ2V0VGFyZ2V0UG9zaXRpb24iLCJnZXRCcnVzaGluZ0V4dGVuc2lvblByb3BzIiwiZXh0ZW5zaW9ucyIsImNvbmNhdCIsIl90b0NvbnN1bWFibGVBcnJheTIiLCJnZXREZWZhdWx0SG92ZXJMYXllclByb3BzIiwidmlzaWJsZSIsImdldFNvdXJjZUNvbG9yIiwiaGlnaGxpZ2h0Q29sb3IiLCJnZXRUYXJnZXRDb2xvciIsImdldFdpZHRoIiwib2JqZWN0SW5mbyIsImlzTGF5ZXJIb3ZlcmVkRnJvbUFycm93IiwiaWQiLCJwb3NpdGlvbiIsImdldEhvdmVyRGF0YSIsIm9iamVjdCIsImZpZWxkcyIsImFuaW1hdGlvbkNvbmZpZyIsImhvdmVySW5mbyIsInJvdyIsImdldExlZ2VuZFZpc3VhbENoYW5uZWxzIiwiX2NoYW5uZWxzJHNvdXJjZUNvbG9yIiwiY2hhbm5lbHMiLCJ2aXN1YWxDaGFubmVscyIsImZpbmREZWZhdWx0TGF5ZXJQcm9wcyIsIl9yZWY2IiwiX3JlZjYkZmllbGRQYWlycyIsImZpZWxkUGFpcnMiLCJoZXhUb1JnYiIsIkRFRkFVTFRfTEFZRVJfQ09MT1IiLCJ0cmlwQXJjIiwicGFpciIsImRlZmF1bHROYW1lIiwiTGF5ZXIiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvYXJjLWxheWVyL2FyYy1sYXllci50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQgKiBhcyBhcnJvdyBmcm9tICdhcGFjaGUtYXJyb3cnO1xuXG5pbXBvcnQgTGF5ZXIsIHtcbiAgTGF5ZXJCYXNlQ29uZmlnLFxuICBMYXllckNvbG9yQ29uZmlnLFxuICBMYXllclNpemVDb25maWcsXG4gIExheWVyQm91bmRzLFxuICBMYXllckJhc2VDb25maWdQYXJ0aWFsLFxuICBWaXN1YWxDaGFubmVsXG59IGZyb20gJy4uL2Jhc2UtbGF5ZXInO1xuaW1wb3J0IHtCcnVzaGluZ0V4dGVuc2lvbn0gZnJvbSAnQGRlY2suZ2wvZXh0ZW5zaW9ucyc7XG5pbXBvcnQge0dlb0Fycm93QXJjTGF5ZXJ9IGZyb20gJ0BrZXBsZXIuZ2wvZGVja2dsLWFycm93LWxheWVycyc7XG5pbXBvcnQge0ZpbHRlckFycm93RXh0ZW5zaW9ufSBmcm9tICdAa2VwbGVyLmdsL2RlY2tnbC1sYXllcnMnO1xuaW1wb3J0IHtBcmNMYXllciBhcyBEZWNrQXJjTGF5ZXJ9IGZyb20gJ0BkZWNrLmdsL2xheWVycyc7XG5cbmltcG9ydCB7XG4gIGhleFRvUmdiLFxuICBEYXRhQ29udGFpbmVySW50ZXJmYWNlLFxuICBtYXliZUhleFRvR2VvLFxuICBBcnJvd0RhdGFDb250YWluZXJcbn0gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5pbXBvcnQgQXJjTGF5ZXJJY29uIGZyb20gJy4vYXJjLWxheWVyLWljb24nO1xuaW1wb3J0IHtpc0xheWVySG92ZXJlZEZyb21BcnJvdywgY3JlYXRlR2VvQXJyb3dQb2ludFZlY3RvciwgZ2V0RmlsdGVyZWRJbmRleH0gZnJvbSAnLi4vbGF5ZXItdXRpbHMnO1xuaW1wb3J0IHtcbiAgREVGQVVMVF9MQVlFUl9DT0xPUixcbiAgUFJPSkVDVEVEX1BJWEVMX1NJWkVfTVVMVElQTElFUixcbiAgQUxMX0ZJRUxEX1RZUEVTXG59IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcblxuaW1wb3J0IHtcbiAgQ29sb3JSYW5nZSxcbiAgUkdCQ29sb3IsXG4gIE1lcmdlLFxuICBWaXNDb25maWdDb2xvclJhbmdlLFxuICBWaXNDb25maWdDb2xvclNlbGVjdCxcbiAgVmlzQ29uZmlnTnVtYmVyLFxuICBWaXNDb25maWdSYW5nZSxcbiAgTGF5ZXJDb2x1bW4sXG4gIEZpZWxkLFxuICBBbmltYXRpb25Db25maWdcbn0gZnJvbSAnQGtlcGxlci5nbC90eXBlcyc7XG5pbXBvcnQge0tlcGxlclRhYmxlfSBmcm9tICdAa2VwbGVyLmdsL3RhYmxlJztcblxuZXhwb3J0IHR5cGUgQXJjTGF5ZXJWaXNDb25maWdTZXR0aW5ncyA9IHtcbiAgb3BhY2l0eTogVmlzQ29uZmlnTnVtYmVyO1xuICB0aGlja25lc3M6IFZpc0NvbmZpZ051bWJlcjtcbiAgY29sb3JSYW5nZTogVmlzQ29uZmlnQ29sb3JSYW5nZTtcbiAgc2l6ZVJhbmdlOiBWaXNDb25maWdSYW5nZTtcbiAgdGFyZ2V0Q29sb3I6IFZpc0NvbmZpZ0NvbG9yU2VsZWN0O1xufTtcblxuZXhwb3J0IHR5cGUgQXJjTGF5ZXJDb2x1bW5zQ29uZmlnID0ge1xuICAvLyBDT0xVTU5fTU9ERV9QT0lOVFMgcmVxdWlyZWQgY29sdW1uc1xuICBsYXQwOiBMYXllckNvbHVtbjtcbiAgbGF0MTogTGF5ZXJDb2x1bW47XG4gIGxuZzA6IExheWVyQ29sdW1uO1xuICBsbmcxOiBMYXllckNvbHVtbjtcblxuICAvLyBDT0xVTU5fTU9ERV9ORUlHSEJPUlMgcmVxdWlyZWQgY29sdW1uc1xuICBsYXQ6IExheWVyQ29sdW1uO1xuICBsbmc6IExheWVyQ29sdW1uO1xuICBuZWlnaGJvcnM6IExheWVyQ29sdW1uO1xuXG4gIC8vIENPTFVNTl9NT0RFX0dFT0FSUk9XXG4gIGdlb2Fycm93MDogTGF5ZXJDb2x1bW47XG4gIGdlb2Fycm93MTogTGF5ZXJDb2x1bW47XG59O1xuXG5leHBvcnQgdHlwZSBBcmNMYXllclZpc0NvbmZpZyA9IHtcbiAgY29sb3JSYW5nZTogQ29sb3JSYW5nZTtcbiAgb3BhY2l0eTogbnVtYmVyO1xuICBzaXplUmFuZ2U6IFtudW1iZXIsIG51bWJlcl07XG4gIHRhcmdldENvbG9yOiBSR0JDb2xvcjtcbiAgdGhpY2tuZXNzOiBudW1iZXI7XG59O1xuXG5leHBvcnQgdHlwZSBBcmNMYXllclZpc3VhbENoYW5uZWxDb25maWcgPSBMYXllckNvbG9yQ29uZmlnICYgTGF5ZXJTaXplQ29uZmlnO1xuZXhwb3J0IHR5cGUgQXJjTGF5ZXJDb25maWcgPSBNZXJnZTxcbiAgTGF5ZXJCYXNlQ29uZmlnLFxuICB7Y29sdW1uczogQXJjTGF5ZXJDb2x1bW5zQ29uZmlnOyB2aXNDb25maWc6IEFyY0xheWVyVmlzQ29uZmlnfVxuPiAmXG4gIEFyY0xheWVyVmlzdWFsQ2hhbm5lbENvbmZpZztcblxuZXhwb3J0IHR5cGUgQXJjTGF5ZXJEYXRhID0ge1xuICBpbmRleDogbnVtYmVyO1xuICBzb3VyY2VQb3NpdGlvbjogW251bWJlciwgbnVtYmVyLCBudW1iZXJdO1xuICB0YXJnZXRQb3NpdGlvbjogW251bWJlciwgbnVtYmVyLCBudW1iZXJdO1xufTtcblxuZXhwb3J0IHR5cGUgQXJjTGF5ZXJNZXRhID0ge1xuICBib3VuZHM6IExheWVyQm91bmRzO1xufTtcblxuZXhwb3J0IGNvbnN0IGFyY1JlcXVpcmVkQ29sdW1ucyA9IFsnbGF0MCcsICdsbmcwJywgJ2xhdDEnLCAnbG5nMSddO1xuZXhwb3J0IGNvbnN0IG5laWdoYm9yUmVxdWlyZWRDb2x1bW5zID0gWydsYXQnLCAnbG5nJywgJ25laWdoYm9ycyddO1xuZXhwb3J0IGNvbnN0IGdlb2Fycm93UmVxdWlyZWRDb2x1bW5zID0gWydnZW9hcnJvdzAnLCAnZ2VvYXJyb3cxJ107XG5cbmV4cG9ydCBjb25zdCBhcmNDb2x1bW5MYWJlbHMgPSB7XG4gIGxhdDA6ICdhcmMubGF0MCcsXG4gIGxuZzA6ICdhcmMubG5nMCcsXG4gIGxhdDE6ICdhcmMubGF0MScsXG4gIGxuZzE6ICdhcmMubG5nMScsXG4gIG5laWdoYm9yczogJ25laWdoYm9ycydcbn07XG5cbmV4cG9ydCBjb25zdCBhcmNWaXNDb25maWdzOiB7XG4gIG9wYWNpdHk6ICdvcGFjaXR5JztcbiAgdGhpY2tuZXNzOiAndGhpY2tuZXNzJztcbiAgY29sb3JSYW5nZTogJ2NvbG9yUmFuZ2UnO1xuICBzaXplUmFuZ2U6ICdzdHJva2VXaWR0aFJhbmdlJztcbiAgdGFyZ2V0Q29sb3I6ICd0YXJnZXRDb2xvcic7XG59ID0ge1xuICBvcGFjaXR5OiAnb3BhY2l0eScsXG4gIHRoaWNrbmVzczogJ3RoaWNrbmVzcycsXG4gIGNvbG9yUmFuZ2U6ICdjb2xvclJhbmdlJyxcbiAgc2l6ZVJhbmdlOiAnc3Ryb2tlV2lkdGhSYW5nZScsXG4gIHRhcmdldENvbG9yOiAndGFyZ2V0Q29sb3InXG59O1xuXG5leHBvcnQgY29uc3QgQ09MVU1OX01PREVfUE9JTlRTID0gJ3BvaW50cyc7XG5leHBvcnQgY29uc3QgQ09MVU1OX01PREVfTkVJR0hCT1JTID0gJ25laWdoYm9ycyc7XG5leHBvcnQgY29uc3QgQ09MVU1OX01PREVfR0VPQVJST1cgPSAnZ2VvYXJyb3cnO1xuY29uc3QgU1VQUE9SVEVEX0NPTFVNTl9NT0RFUyA9IFtcbiAge1xuICAgIGtleTogQ09MVU1OX01PREVfUE9JTlRTLFxuICAgIGxhYmVsOiAnUG9pbnRzJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IGFyY1JlcXVpcmVkQ29sdW1uc1xuICB9LFxuICB7XG4gICAga2V5OiBDT0xVTU5fTU9ERV9ORUlHSEJPUlMsXG4gICAgbGFiZWw6ICdQb2ludCBhbmQgTmVpZ2hib3JzJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IG5laWdoYm9yUmVxdWlyZWRDb2x1bW5zXG4gIH0sXG4gIHtcbiAgICBrZXk6IENPTFVNTl9NT0RFX0dFT0FSUk9XLFxuICAgIGxhYmVsOiAnR2VvYXJyb3cgUG9pbnRzJyxcbiAgICByZXF1aXJlZENvbHVtbnM6IGdlb2Fycm93UmVxdWlyZWRDb2x1bW5zXG4gIH1cbl07XG5jb25zdCBERUZBVUxUX0NPTFVNTl9NT0RFID0gQ09MVU1OX01PREVfUE9JTlRTO1xuXG5jb25zdCBicnVzaGluZ0V4dGVuc2lvbiA9IG5ldyBCcnVzaGluZ0V4dGVuc2lvbigpO1xuY29uc3QgYXJyb3dDUFVGaWx0ZXJFeHRlbnNpb24gPSBuZXcgRmlsdGVyQXJyb3dFeHRlbnNpb24oKTtcblxuZnVuY3Rpb24gaXNIM0ZpZWxkKGNvbHVtbnMsIGFsbEZpZWxkcywga2V5KSB7XG4gIGNvbnN0IGZpZWxkID0gYWxsRmllbGRzW2NvbHVtbnNba2V5XS5maWVsZElkeF07XG4gIHJldHVybiBmaWVsZD8udHlwZSA9PT0gQUxMX0ZJRUxEX1RZUEVTLmgzO1xufVxuXG5leHBvcnQgY29uc3QgYXJjUG9zQWNjZXNzb3IgPVxuICAoe2xhdDAsIGxuZzAsIGxhdDEsIGxuZzEsIGxhdCwgbG5nLCBnZW9hcnJvdzAsIGdlb2Fycm93MX06IEFyY0xheWVyQ29sdW1uc0NvbmZpZywgY29sdW1uTW9kZSkgPT5cbiAgKGRjOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlKSA9PiB7XG4gICAgc3dpdGNoIChjb2x1bW5Nb2RlKSB7XG4gICAgICBjYXNlIENPTFVNTl9NT0RFX0dFT0FSUk9XOlxuICAgICAgICByZXR1cm4gZCA9PiB7XG4gICAgICAgICAgY29uc3Qgc3RhcnQgPSBkYy52YWx1ZUF0KGQuaW5kZXgsIGdlb2Fycm93MC5maWVsZElkeCk7XG4gICAgICAgICAgY29uc3QgZW5kID0gZGMudmFsdWVBdChkLmluZGV4LCBnZW9hcnJvdzEuZmllbGRJZHgpO1xuICAgICAgICAgIHJldHVybiBbc3RhcnQuZ2V0KDApLCBzdGFydC5nZXQoMSksIDAsIGVuZC5nZXQoMiksIGVuZC5nZXQoMyksIDBdO1xuICAgICAgICB9O1xuICAgICAgY2FzZSBDT0xVTU5fTU9ERV9ORUlHSEJPUlM6XG4gICAgICAgIHJldHVybiBkID0+IHtcbiAgICAgICAgICBjb25zdCBzdGFydFBvcyA9IG1heWJlSGV4VG9HZW8oZGMsIGQsIGxhdCwgbG5nKTtcbiAgICAgICAgICAvLyBvbmx5IHJldHVybiBzb3VyY2UgcG9pbnQgaWYgY29sdW1uTW9kZSBpcyBDT0xVTU5fTU9ERV9ORUlHSEJPUlNcblxuICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICBzdGFydFBvcyA/IHN0YXJ0UG9zWzBdIDogZGMudmFsdWVBdChkLmluZGV4LCBsbmcuZmllbGRJZHgpLFxuICAgICAgICAgICAgc3RhcnRQb3MgPyBzdGFydFBvc1sxXSA6IGRjLnZhbHVlQXQoZC5pbmRleCwgbGF0LmZpZWxkSWR4KSxcbiAgICAgICAgICAgIDBcbiAgICAgICAgICBdO1xuICAgICAgICB9O1xuICAgICAgZGVmYXVsdDpcbiAgICAgICAgLy8gQ09MVU1OX01PREVfUE9JTlRTXG4gICAgICAgIHJldHVybiBkID0+IHtcbiAgICAgICAgICAvLyBsYXQgb3IgbG5nIGNvbHVtbiBjb3VsZCBiZSBoZXggY29sdW1uXG4gICAgICAgICAgLy8gd2UgYXNzdW1lIHN0cmluZyB2YWx1ZSBpcyBoZXggYW5kIHRyeSB0byBjb252ZXJ0IGl0IHRvIGdlbyBsYXQgbG5nXG4gICAgICAgICAgY29uc3Qgc3RhcnRQb3MgPSBtYXliZUhleFRvR2VvKGRjLCBkLCBsYXQwLCBsbmcwKTtcbiAgICAgICAgICBjb25zdCBlbmRQb3MgPSBtYXliZUhleFRvR2VvKGRjLCBkLCBsYXQxLCBsbmcxKTtcbiAgICAgICAgICByZXR1cm4gW1xuICAgICAgICAgICAgc3RhcnRQb3MgPyBzdGFydFBvc1swXSA6IGRjLnZhbHVlQXQoZC5pbmRleCwgbG5nMC5maWVsZElkeCksXG4gICAgICAgICAgICBzdGFydFBvcyA/IHN0YXJ0UG9zWzFdIDogZGMudmFsdWVBdChkLmluZGV4LCBsYXQwLmZpZWxkSWR4KSxcbiAgICAgICAgICAgIDAsXG4gICAgICAgICAgICBlbmRQb3MgPyBlbmRQb3NbMF0gOiBkYy52YWx1ZUF0KGQuaW5kZXgsIGxuZzEuZmllbGRJZHgpLFxuICAgICAgICAgICAgZW5kUG9zID8gZW5kUG9zWzFdIDogZGMudmFsdWVBdChkLmluZGV4LCBsYXQxLmZpZWxkSWR4KSxcbiAgICAgICAgICAgIDBcbiAgICAgICAgICBdO1xuICAgICAgICB9O1xuICAgIH1cbiAgfTtcbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEFyY0xheWVyIGV4dGVuZHMgTGF5ZXIge1xuICBkZWNsYXJlIHZpc0NvbmZpZ1NldHRpbmdzOiBBcmNMYXllclZpc0NvbmZpZ1NldHRpbmdzO1xuICBkZWNsYXJlIGNvbmZpZzogQXJjTGF5ZXJDb25maWc7XG4gIGRlY2xhcmUgbWV0YTogQXJjTGF5ZXJNZXRhO1xuXG4gIGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2UgfCBudWxsID0gbnVsbDtcbiAgZ2VvQXJyb3dWZWN0b3IwOiBhcnJvdy5WZWN0b3IgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG4gIGdlb0Fycm93VmVjdG9yMTogYXJyb3cuVmVjdG9yIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuXG4gIC8qXG4gICAqIENQVSBmaWx0ZXJpbmcgYW4gYXJyb3cgdGFibGUgYnkgdmFsdWVzIGFuZCBhc3NlbWJsaW5nIGEgcGFydGlhbCBjb3B5IG9mIHRoZSByYXcgdGFibGUgaXMgZXhwZW5zaXZlXG4gICAqIHNvIHdlIHdpbGwgdXNlIGZpbHRlcmVkSW5kZXggdG8gY3JlYXRlIGFuIGF0dHJpYnV0ZSBlLmcuIGZpbHRlcmVkSW5kZXggWzB8MV0gZm9yIEdQVSBmaWx0ZXJpbmdcbiAgICogaW4gZGVjay5nbCBsYXllciwgc2VlOiBGaWx0ZXJBcnJvd0V4dGVuc2lvbiBpbiBAa2VwbGVyLmdsL2RlY2tnbC1sYXllcnMuXG4gICAqIE5vdGUgdGhhdCB0aGlzIGFwcHJvYWNoIGNhbiBjcmVhdGUgdmlzaWJsZSBsYWdzIGluIGNhc2Ugb2YgYSBsb3Qgb2YgZGlzY2FydGVkIGdlb21ldHJ5LlxuICAgKi9cbiAgZmlsdGVyZWRJbmRleDogVWludDhDbGFtcGVkQXJyYXkgfCBudWxsID0gbnVsbDtcbiAgZmlsdGVyZWRJbmRleFRyaWdnZXI6IG51bWJlcltdID0gW107XG5cbiAgY29uc3RydWN0b3IocHJvcHMpIHtcbiAgICBzdXBlcihwcm9wcyk7XG5cbiAgICB0aGlzLnJlZ2lzdGVyVmlzQ29uZmlnKGFyY1Zpc0NvbmZpZ3MpO1xuICAgIHRoaXMuZ2V0UG9zaXRpb25BY2Nlc3NvciA9IChkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlKSA9PlxuICAgICAgYXJjUG9zQWNjZXNzb3IodGhpcy5jb25maWcuY29sdW1ucywgdGhpcy5jb25maWcuY29sdW1uTW9kZSkoZGF0YUNvbnRhaW5lcik7XG4gIH1cblxuICBnZXQgdHlwZSgpIHtcbiAgICByZXR1cm4gJ2FyYyc7XG4gIH1cblxuICBnZXQgaXNBZ2dyZWdhdGVkKCkge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxuXG4gIGdldCBsYXllckljb24oKSB7XG4gICAgcmV0dXJuIEFyY0xheWVySWNvbjtcbiAgfVxuXG4gIGdldCBjb2x1bW5MYWJlbHMoKTogUmVjb3JkPHN0cmluZywgc3RyaW5nPiB7XG4gICAgcmV0dXJuIGFyY0NvbHVtbkxhYmVscztcbiAgfVxuXG4gIGdldCBjb2x1bW5QYWlycygpIHtcbiAgICByZXR1cm4gdGhpcy5kZWZhdWx0TGlua0NvbHVtblBhaXJzO1xuICB9XG5cbiAgZ2V0IHN1cHBvcnRlZENvbHVtbk1vZGVzKCkge1xuICAgIHJldHVybiBTVVBQT1JURURfQ09MVU1OX01PREVTO1xuICB9XG5cbiAgZ2V0IHZpc3VhbENoYW5uZWxzKCkge1xuICAgIHJldHVybiB7XG4gICAgICBzb3VyY2VDb2xvcjoge1xuICAgICAgICAuLi5zdXBlci52aXN1YWxDaGFubmVscy5jb2xvcixcbiAgICAgICAgcHJvcGVydHk6ICdjb2xvcicsXG4gICAgICAgIGtleTogJ3NvdXJjZUNvbG9yJyxcbiAgICAgICAgYWNjZXNzb3I6ICdnZXRTb3VyY2VDb2xvcicsXG4gICAgICAgIGRlZmF1bHRWYWx1ZTogY29uZmlnID0+IGNvbmZpZy5jb2xvclxuICAgICAgfSxcbiAgICAgIHRhcmdldENvbG9yOiB7XG4gICAgICAgIC4uLnN1cGVyLnZpc3VhbENoYW5uZWxzLmNvbG9yLFxuICAgICAgICBwcm9wZXJ0eTogJ3RhcmdldENvbG9yJyxcbiAgICAgICAga2V5OiAndGFyZ2V0Q29sb3InLFxuICAgICAgICBhY2Nlc3NvcjogJ2dldFRhcmdldENvbG9yJyxcbiAgICAgICAgZGVmYXVsdFZhbHVlOiBjb25maWcgPT4gY29uZmlnLnZpc0NvbmZpZy50YXJnZXRDb2xvciB8fCBjb25maWcuY29sb3JcbiAgICAgIH0sXG4gICAgICBzaXplOiB7XG4gICAgICAgIC4uLnN1cGVyLnZpc3VhbENoYW5uZWxzLnNpemUsXG4gICAgICAgIGFjY2Vzc29yOiAnZ2V0V2lkdGgnLFxuICAgICAgICBwcm9wZXJ0eTogJ3N0cm9rZSdcbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgZ2V0IGNvbHVtblZhbGlkYXRvcnMoKSB7XG4gICAgLy8gaWYgb25lIG9mIHRoZSBsYXQgb3IgbG5nIGNvbHVtbiBpcyBzdHJpbmcgdHlwZSwgd2UgYWxsb3cgaXRcbiAgICAvLyB3aWxsIHRyeSB0byBwYXNzIGl0IGFzIGhleFxuICAgIHJldHVybiB7XG4gICAgICBsYXQwOiAoY29sdW1uLCBjb2x1bW5zLCBhbGxGaWVsZHMpID0+IGlzSDNGaWVsZChjb2x1bW5zLCBhbGxGaWVsZHMsICdsbmcwJyksXG4gICAgICBsbmcwOiAoY29sdW1uLCBjb2x1bW5zLCBhbGxGaWVsZHMpID0+IGlzSDNGaWVsZChjb2x1bW5zLCBhbGxGaWVsZHMsICdsYXQwJyksXG4gICAgICBsYXQxOiAoY29sdW1uLCBjb2x1bW5zLCBhbGxGaWVsZHMpID0+IGlzSDNGaWVsZChjb2x1bW5zLCBhbGxGaWVsZHMsICdsbmcxJyksXG4gICAgICBsbmcxOiAoY29sdW1uLCBjb2x1bW5zLCBhbGxGaWVsZHMpID0+IGlzSDNGaWVsZChjb2x1bW5zLCBhbGxGaWVsZHMsICdsYXQxJyksXG4gICAgICBsYXQ6IChjb2x1bW4sIGNvbHVtbnMsIGFsbEZpZWxkcykgPT4gaXNIM0ZpZWxkKGNvbHVtbnMsIGFsbEZpZWxkcywgJ2xuZycpLFxuICAgICAgbG5nOiAoY29sdW1uLCBjb2x1bW5zLCBhbGxGaWVsZHMpID0+IGlzSDNGaWVsZChjb2x1bW5zLCBhbGxGaWVsZHMsICdsYXQnKVxuICAgIH07XG4gIH1cblxuICBoYXNBbGxDb2x1bW5zKCkge1xuICAgIGNvbnN0IHtjb2x1bW5zfSA9IHRoaXMuY29uZmlnO1xuICAgIGlmICh0aGlzLmNvbmZpZy5jb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9BUlJPVykge1xuICAgICAgcmV0dXJuIHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5nZW9hcnJvdzApICYmIHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5nZW9hcnJvdzEpO1xuICAgIH1cbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfUE9JTlRTKSB7XG4gICAgICAvLyBUT0RPIC0gdGhpcyBkb2VzIG5vdCBoYXZlIGFjY2VzcyB0byBhbGxGaWVsZHMuLi5cbiAgICAgIC8vIFNvIHdlIGNhbid0IGRvIHRoZSBzYW1lIHZhbGlkYXRpb24gYXMgZm9yIHRoZSBmaWVsZCBlcnJvcnNcbiAgICAgIGNvbnN0IGhhc1N0YXJ0ID0gdGhpcy5oYXNDb2x1bW5WYWx1ZShjb2x1bW5zLmxhdDApIHx8IHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5sbmcwKTtcbiAgICAgIGNvbnN0IGhhc0VuZCA9IHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5sYXQxKSB8fCB0aGlzLmhhc0NvbHVtblZhbHVlKGNvbHVtbnMubG5nMSk7XG4gICAgICByZXR1cm4gaGFzU3RhcnQgJiYgaGFzRW5kO1xuICAgIH1cbiAgICBjb25zdCBoYXNTdGFydCA9IHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5sYXQpIHx8IHRoaXMuaGFzQ29sdW1uVmFsdWUoY29sdW1ucy5sbmcpO1xuICAgIGNvbnN0IGhhc05laWJvcnMgPSB0aGlzLmhhc0NvbHVtblZhbHVlKGNvbHVtbnMubmVpZ2hib3JzKTtcbiAgICByZXR1cm4gaGFzU3RhcnQgJiYgaGFzTmVpYm9ycztcbiAgfVxuXG4gIHN0YXRpYyBmaW5kRGVmYXVsdExheWVyUHJvcHMoe2ZpZWxkUGFpcnMgPSBbXX06IEtlcGxlclRhYmxlKToge1xuICAgIHByb3BzOiB7Y29sb3I/OiBSR0JDb2xvcjsgY29sdW1uczogQXJjTGF5ZXJDb2x1bW5zQ29uZmlnOyBsYWJlbDogc3RyaW5nfVtdO1xuICB9IHtcbiAgICBpZiAoZmllbGRQYWlycy5sZW5ndGggPCAyKSB7XG4gICAgICByZXR1cm4ge3Byb3BzOiBbXX07XG4gICAgfVxuXG4gICAgY29uc3QgcHJvcHM6IHtcbiAgICAgIGNvbG9yOiBSR0JDb2xvcjtcbiAgICAgIGNvbHVtbnM6IEFyY0xheWVyQ29sdW1uc0NvbmZpZztcbiAgICAgIGxhYmVsOiBzdHJpbmc7XG4gICAgfSA9IHtcbiAgICAgIGNvbG9yOiBoZXhUb1JnYihERUZBVUxUX0xBWUVSX0NPTE9SLnRyaXBBcmMpLFxuICAgICAgLy8gY29ubmVjdCB0aGUgZmlyc3QgdHdvIHBvaW50IGxheWVyIHdpdGggYXJjXG4gICAgICAvLyBAdHMtZXhwZWN0LWVycm9yIHNlcGFyYXRlIHR5cGVzIGZvciBwb2ludCAvIG5laWdoYm9yIGNvbHVtbnNcbiAgICAgIGNvbHVtbnM6IHtcbiAgICAgICAgbGF0MDogZmllbGRQYWlyc1swXS5wYWlyLmxhdCxcbiAgICAgICAgbG5nMDogZmllbGRQYWlyc1swXS5wYWlyLmxuZyxcbiAgICAgICAgbGF0MTogZmllbGRQYWlyc1sxXS5wYWlyLmxhdCxcbiAgICAgICAgbG5nMTogZmllbGRQYWlyc1sxXS5wYWlyLmxuZ1xuICAgICAgfSxcbiAgICAgIGxhYmVsOiBgJHtmaWVsZFBhaXJzWzBdLmRlZmF1bHROYW1lfSAtPiAke2ZpZWxkUGFpcnNbMV0uZGVmYXVsdE5hbWV9IGFyY2BcbiAgICB9O1xuXG4gICAgcmV0dXJuIHtwcm9wczogW3Byb3BzXX07XG4gIH1cblxuICBnZXREZWZhdWx0TGF5ZXJDb25maWcocHJvcHM6IExheWVyQmFzZUNvbmZpZ1BhcnRpYWwpIHtcbiAgICBjb25zdCBkZWZhdWx0TGF5ZXJDb25maWcgPSBzdXBlci5nZXREZWZhdWx0TGF5ZXJDb25maWcocHJvcHMpO1xuXG4gICAgcmV0dXJuIHtcbiAgICAgIC4uLmRlZmF1bHRMYXllckNvbmZpZyxcbiAgICAgIGNvbHVtbk1vZGU6IHByb3BzPy5jb2x1bW5Nb2RlID8/IERFRkFVTFRfQ09MVU1OX01PREVcbiAgICB9O1xuICB9XG5cbiAgY2FsY3VsYXRlRGF0YUF0dHJpYnV0ZUZvckdlb0Fycm93KFxuICAgIHtkYXRhQ29udGFpbmVyLCBmaWx0ZXJlZEluZGV4fToge2RhdGFDb250YWluZXI6IEFycm93RGF0YUNvbnRhaW5lcjsgZmlsdGVyZWRJbmRleDogbnVtYmVyW119LFxuICAgIGdldFBvc2l0aW9uXG4gICkge1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleCA9IGdldEZpbHRlcmVkSW5kZXgoXG4gICAgICBkYXRhQ29udGFpbmVyLm51bVJvd3MoKSxcbiAgICAgIGZpbHRlcmVkSW5kZXgsXG4gICAgICB0aGlzLmZpbHRlcmVkSW5kZXhcbiAgICApO1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleFRyaWdnZXIgPSBmaWx0ZXJlZEluZGV4O1xuXG4gICAgaWYgKHRoaXMuY29uZmlnLmNvbHVtbk1vZGUgPT09IENPTFVNTl9NT0RFX0dFT0FSUk9XKSB7XG4gICAgICB0aGlzLmdlb0Fycm93VmVjdG9yMCA9IGRhdGFDb250YWluZXIuZ2V0Q29sdW1uKHRoaXMuY29uZmlnLmNvbHVtbnMuZ2VvYXJyb3cwLmZpZWxkSWR4KTtcbiAgICAgIHRoaXMuZ2VvQXJyb3dWZWN0b3IxID0gZGF0YUNvbnRhaW5lci5nZXRDb2x1bW4odGhpcy5jb25maWcuY29sdW1ucy5nZW9hcnJvdzEuZmllbGRJZHgpO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyBnZW5lcmF0ZSBjb2x1bW5zIGNvbXBhdGlibGUgd2l0aCBnZW9hcnJvdyBwb2ludCBleHRlbnNpb25cbiAgICAgIC8vIFRPRE8gcmVtb3ZlIGV4Y2Vzc2l2ZSBpbnRlcm1lZGlhdGUgYWxsb2NhdGlvbnNcbiAgICAgIHRoaXMuZ2VvQXJyb3dWZWN0b3IwID0gY3JlYXRlR2VvQXJyb3dQb2ludFZlY3RvcihkYXRhQ29udGFpbmVyLCBkID0+IHtcbiAgICAgICAgcmV0dXJuIGdldFBvc2l0aW9uKGQpLnNsaWNlKDAsIDMpO1xuICAgICAgfSk7XG4gICAgICB0aGlzLmdlb0Fycm93VmVjdG9yMSA9IGNyZWF0ZUdlb0Fycm93UG9pbnRWZWN0b3IoZGF0YUNvbnRhaW5lciwgZCA9PiB7XG4gICAgICAgIHJldHVybiBnZXRQb3NpdGlvbihkKS5zbGljZSgzLCA2KTtcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIHJldHVybiBkYXRhQ29udGFpbmVyLmdldFRhYmxlKCk7XG4gIH1cblxuICBjYWxjdWxhdGVEYXRhQXR0cmlidXRlRm9yUG9pbnRzKFxuICAgIHtmaWx0ZXJlZEluZGV4fToge2RhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2U7IGZpbHRlcmVkSW5kZXg6IG51bWJlcltdfSxcbiAgICBnZXRQb3NpdGlvblxuICApIHtcbiAgICBjb25zdCBkYXRhOiBBcmNMYXllckRhdGFbXSA9IFtdO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZmlsdGVyZWRJbmRleC5sZW5ndGg7IGkrKykge1xuICAgICAgY29uc3QgaW5kZXggPSBmaWx0ZXJlZEluZGV4W2ldO1xuICAgICAgY29uc3QgcG9zID0gZ2V0UG9zaXRpb24oe2luZGV4fSk7XG5cbiAgICAgIC8vIGlmIGRvZXNuJ3QgaGF2ZSBwb2ludCBsYXQgb3IgbG5nLCBkbyBub3QgYWRkIHRoZSBwb2ludFxuICAgICAgLy8gZGVjay5nbCBjYW4ndCBoYW5kbGUgcG9zaXRpb24gPSBudWxsXG4gICAgICBpZiAocG9zLmV2ZXJ5KE51bWJlci5pc0Zpbml0ZSkpIHtcbiAgICAgICAgZGF0YS5wdXNoKHtcbiAgICAgICAgICBpbmRleCxcbiAgICAgICAgICBzb3VyY2VQb3NpdGlvbjogW3Bvc1swXSwgcG9zWzFdLCBwb3NbMl1dLFxuICAgICAgICAgIHRhcmdldFBvc2l0aW9uOiBbcG9zWzNdLCBwb3NbNF0sIHBvc1s1XV1cbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBkYXRhO1xuICB9XG5cbiAgY2FsY3VsYXRlRGF0YUF0dHJpYnV0ZUZvclBvaW50Tk5laWdoYm9ycyhcbiAgICB7XG4gICAgICBkYXRhQ29udGFpbmVyLFxuICAgICAgZmlsdGVyZWRJbmRleFxuICAgIH06IHtkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlOyBmaWx0ZXJlZEluZGV4OiBudW1iZXJbXX0sXG4gICAgZ2V0UG9zaXRpb25cbiAgKSB7XG4gICAgY29uc3QgZGF0YToge2luZGV4OiBudW1iZXI7IHNvdXJjZVBvc2l0aW9uOiBudW1iZXJbXTsgdGFyZ2V0UG9zaXRpb246IG51bWJlcltdfVtdID0gW107XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBmaWx0ZXJlZEluZGV4Lmxlbmd0aDsgaSsrKSB7XG4gICAgICBjb25zdCBpbmRleCA9IGZpbHRlcmVkSW5kZXhbaV07XG4gICAgICBjb25zdCBwb3MgPSBnZXRQb3NpdGlvbih7aW5kZXh9KTtcbiAgICAgIC8vIGlmIGRvZXNuJ3QgaGF2ZSBwb2ludCBsYXQgb3IgbG5nLCBkbyBub3QgYWRkIHRoZSBwb2ludFxuICAgICAgLy8gZGVjay5nbCBjYW4ndCBoYW5kbGUgcG9zaXRpb24gPSBudWxsXG4gICAgICBpZiAocG9zLmV2ZXJ5KE51bWJlci5pc0Zpbml0ZSkpIHtcbiAgICAgICAgLy8gcHVzaCBhbGwgbmVpYm9yc1xuICAgICAgICBjb25zdCBuZWlnaGJvcklkeCA9IHRoaXMuY29uZmlnLmNvbHVtbnMubmVpZ2hib3JzLnZhbHVlXG4gICAgICAgICAgPyBkYXRhQ29udGFpbmVyLnZhbHVlQXQoaW5kZXgsIHRoaXMuY29uZmlnLmNvbHVtbnMubmVpZ2hib3JzLmZpZWxkSWR4KVxuICAgICAgICAgIDogW107XG4gICAgICAgIGlmIChBcnJheS5pc0FycmF5KG5laWdoYm9ySWR4KSkge1xuICAgICAgICAgIG5laWdoYm9ySWR4LmZvckVhY2goaWR4ID0+IHtcbiAgICAgICAgICAgIC8vIFRPRE8gcHJldmVudCByb3cgbWF0ZXJpYWxpemF0aW9uIGhlcmVcbiAgICAgICAgICAgIGNvbnN0IHRQb3MgPSBkYXRhQ29udGFpbmVyLnJvd0FzQXJyYXkoaWR4KSA/IGdldFBvc2l0aW9uKHtpbmRleDogaWR4fSkgOiBudWxsO1xuICAgICAgICAgICAgaWYgKHRQb3MgJiYgdFBvcy5ldmVyeShOdW1iZXIuaXNGaW5pdGUpKSB7XG4gICAgICAgICAgICAgIGRhdGEucHVzaCh7XG4gICAgICAgICAgICAgICAgaW5kZXgsXG4gICAgICAgICAgICAgICAgc291cmNlUG9zaXRpb246IFtwb3NbMF0sIHBvc1sxXSwgcG9zWzJdXSxcbiAgICAgICAgICAgICAgICB0YXJnZXRQb3NpdGlvbjogW3RQb3NbMF0sIHRQb3NbMV0sIHRQb3NbMl1dXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuXG4gICAgcmV0dXJuIGRhdGE7XG4gIH1cblxuICBjYWxjdWxhdGVEYXRhQXR0cmlidXRlKHtkYXRhQ29udGFpbmVyLCBmaWx0ZXJlZEluZGV4fTogS2VwbGVyVGFibGUsIGdldFBvc2l0aW9uKSB7XG4gICAgY29uc3Qge2NvbHVtbk1vZGV9ID0gdGhpcy5jb25maWc7XG5cbiAgICAvLyAxKSBDT0xVTU5fTU9ERV9HRU9BUlJPVyAtIHdoZW4gd2UgaGF2ZSBhIGdlb2Fycm93IHBvaW50IGNvbHVtblxuICAgIC8vIDIpIENPTFVNTl9NT0RFX1BPSU5UUyArIEFycm93RGF0YUNvbnRhaW5lciA+IGNyZWF0ZSBnZW9hcnJvdyBwb2ludCBjb2x1bW4gb24gdGhlIGZseVxuICAgIGlmIChcbiAgICAgIGRhdGFDb250YWluZXIgaW5zdGFuY2VvZiBBcnJvd0RhdGFDb250YWluZXIgJiZcbiAgICAgIChjb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9HRU9BUlJPVyB8fCBjb2x1bW5Nb2RlID09PSBDT0xVTU5fTU9ERV9QT0lOVFMpXG4gICAgKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWxjdWxhdGVEYXRhQXR0cmlidXRlRm9yR2VvQXJyb3coe2RhdGFDb250YWluZXIsIGZpbHRlcmVkSW5kZXh9LCBnZXRQb3NpdGlvbik7XG4gICAgfVxuXG4gICAgLy8gd2UgZG9uJ3QgbmVlZCB0aGVzZSBpbiBub24tQXJyb3cgbW9kZXMgYXRtLlxuICAgIHRoaXMuZ2VvQXJyb3dWZWN0b3IwID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuZ2VvQXJyb3dWZWN0b3IxID0gdW5kZWZpbmVkO1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleCA9IG51bGw7XG5cbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfUE9JTlRTKSB7XG4gICAgICByZXR1cm4gdGhpcy5jYWxjdWxhdGVEYXRhQXR0cmlidXRlRm9yUG9pbnRzKHtkYXRhQ29udGFpbmVyLCBmaWx0ZXJlZEluZGV4fSwgZ2V0UG9zaXRpb24pO1xuICAgIH1cbiAgICByZXR1cm4gdGhpcy5jYWxjdWxhdGVEYXRhQXR0cmlidXRlRm9yUG9pbnROTmVpZ2hib3JzKFxuICAgICAge2RhdGFDb250YWluZXIsIGZpbHRlcmVkSW5kZXh9LFxuICAgICAgZ2V0UG9zaXRpb25cbiAgICApO1xuICB9XG5cbiAgZm9ybWF0TGF5ZXJEYXRhKGRhdGFzZXRzLCBvbGRMYXllckRhdGEpIHtcbiAgICBpZiAodGhpcy5jb25maWcuZGF0YUlkID09PSBudWxsKSB7XG4gICAgICByZXR1cm4ge307XG4gICAgfVxuICAgIGNvbnN0IHtncHVGaWx0ZXIsIGRhdGFDb250YWluZXJ9ID0gZGF0YXNldHNbdGhpcy5jb25maWcuZGF0YUlkXTtcbiAgICBjb25zdCB7ZGF0YX0gPSB0aGlzLnVwZGF0ZURhdGEoZGF0YXNldHMsIG9sZExheWVyRGF0YSk7XG4gICAgY29uc3QgYWNjZXNzb3JzID0gdGhpcy5nZXRBdHRyaWJ1dGVBY2Nlc3NvcnMoe2RhdGFDb250YWluZXJ9KTtcbiAgICBjb25zdCBpc0ZpbHRlcmVkQWNjZXNzb3IgPSAoZGF0YToge2luZGV4OiBudW1iZXJ9KSA9PiB7XG4gICAgICAvLyBmb3IgR2VvQXJyb3cgZGF0YSBpcyBhIGJ1ZmZlciwgc28gdXNlIG9iamVjdEluZm9cbiAgICAgIHJldHVybiB0aGlzLmZpbHRlcmVkSW5kZXggPyB0aGlzLmZpbHRlcmVkSW5kZXhbZGF0YS5pbmRleF0gOiAxO1xuICAgIH07XG5cbiAgICByZXR1cm4ge1xuICAgICAgZGF0YSxcbiAgICAgIGdldEZpbHRlclZhbHVlOiBncHVGaWx0ZXIuZmlsdGVyVmFsdWVBY2Nlc3NvcihkYXRhQ29udGFpbmVyKSgpLFxuICAgICAgZ2V0RmlsdGVyZWQ6IGlzRmlsdGVyZWRBY2Nlc3NvcixcbiAgICAgIC4uLmFjY2Vzc29yc1xuICAgIH07XG4gIH1cbiAgLyogZXNsaW50LWVuYWJsZSBjb21wbGV4aXR5ICovXG5cbiAgdXBkYXRlTGF5ZXJNZXRhKGRhdGFzZXQ6IEtlcGxlclRhYmxlKSB7XG4gICAgY29uc3Qge2RhdGFDb250YWluZXJ9ID0gZGF0YXNldDtcblxuICAgIHRoaXMuZGF0YUNvbnRhaW5lciA9IGRhdGFDb250YWluZXI7XG5cbiAgICAvLyBnZXQgYm91bmRzIGZyb20gYXJjc1xuICAgIGNvbnN0IGdldFBvc2l0aW9uID0gdGhpcy5nZXRQb3NpdGlvbkFjY2Vzc29yKGRhdGFDb250YWluZXIpO1xuXG4gICAgY29uc3Qgc0JvdW5kcyA9IHRoaXMuZ2V0UG9pbnRzQm91bmRzKGRhdGFDb250YWluZXIsIGQgPT4ge1xuICAgICAgY29uc3QgcG9zID0gZ2V0UG9zaXRpb24oZCk7XG4gICAgICByZXR1cm4gW3Bvc1swXSwgcG9zWzFdXTtcbiAgICB9KTtcblxuICAgIGxldCB0Qm91bmRzOiBudW1iZXJbXSB8IG51bGwgPSBbXTtcbiAgICBpZiAodGhpcy5jb25maWcuY29sdW1uTW9kZSA9PT0gQ09MVU1OX01PREVfUE9JTlRTKSB7XG4gICAgICB0Qm91bmRzID0gdGhpcy5nZXRQb2ludHNCb3VuZHMoZGF0YUNvbnRhaW5lciwgZCA9PiB7XG4gICAgICAgIGNvbnN0IHBvcyA9IGdldFBvc2l0aW9uKGQpO1xuICAgICAgICByZXR1cm4gW3Bvc1szXSwgcG9zWzRdXTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICAvLyB3aGVuIGNvbHVtbk1vZGUgaXMgbmVpZ2hib3JzLCBpdCByZWZlcmVuY2UgdGhlIHNhbWUgY29sbGVjdGlvbiBvZiBwb2ludHNcbiAgICAgIHRCb3VuZHMgPSBzQm91bmRzO1xuICAgIH1cblxuICAgIGNvbnN0IGJvdW5kcyA9XG4gICAgICB0Qm91bmRzICYmIHNCb3VuZHNcbiAgICAgICAgPyBbXG4gICAgICAgICAgICBNYXRoLm1pbihzQm91bmRzWzBdLCB0Qm91bmRzWzBdKSxcbiAgICAgICAgICAgIE1hdGgubWluKHNCb3VuZHNbMV0sIHRCb3VuZHNbMV0pLFxuICAgICAgICAgICAgTWF0aC5tYXgoc0JvdW5kc1syXSwgdEJvdW5kc1syXSksXG4gICAgICAgICAgICBNYXRoLm1heChzQm91bmRzWzNdLCB0Qm91bmRzWzNdKVxuICAgICAgICAgIF1cbiAgICAgICAgOiBzQm91bmRzIHx8IHRCb3VuZHM7XG5cbiAgICB0aGlzLnVwZGF0ZU1ldGEoe2JvdW5kc30pO1xuICB9XG5cbiAgcmVuZGVyTGF5ZXIob3B0cykge1xuICAgIGNvbnN0IHtkYXRhLCBncHVGaWx0ZXIsIG9iamVjdEhvdmVyZWQsIGludGVyYWN0aW9uQ29uZmlnLCBkYXRhc2V0fSA9IG9wdHM7XG4gICAgY29uc3QgdXBkYXRlVHJpZ2dlcnMgPSB7XG4gICAgICBnZXRQb3NpdGlvbjogdGhpcy5jb25maWcuY29sdW1ucyxcbiAgICAgIGdldEZpbHRlclZhbHVlOiBncHVGaWx0ZXIuZmlsdGVyVmFsdWVVcGRhdGVUcmlnZ2VycyxcbiAgICAgIGdldEZpbHRlcmVkOiB0aGlzLmZpbHRlcmVkSW5kZXhUcmlnZ2VyLFxuICAgICAgLi4udGhpcy5nZXRWaXN1YWxDaGFubmVsVXBkYXRlVHJpZ2dlcnMoKVxuICAgIH07XG4gICAgY29uc3Qgd2lkdGhTY2FsZSA9IHRoaXMuY29uZmlnLnZpc0NvbmZpZy50aGlja25lc3MgKiBQUk9KRUNURURfUElYRUxfU0laRV9NVUxUSVBMSUVSO1xuICAgIGNvbnN0IGRlZmF1bHRMYXllclByb3BzID0gdGhpcy5nZXREZWZhdWx0RGVja0xheWVyUHJvcHMob3B0cyk7XG4gICAgY29uc3QgaG92ZXJlZE9iamVjdCA9IHRoaXMuaGFzSG92ZXJlZE9iamVjdChvYmplY3RIb3ZlcmVkKTtcblxuICAgIGNvbnN0IHVzZUFycm93TGF5ZXIgPSBCb29sZWFuKHRoaXMuZ2VvQXJyb3dWZWN0b3IwKT