UNPKG

kepler.gl

Version:

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

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