UNPKG

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