UNPKG

kepler.gl

Version:

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

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