UNPKG

kepler.gl

Version:

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

647 lines (620 loc) 83.2 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.copyTable = copyTable; exports.copyTableAndUpdate = copyTableAndUpdate; exports["default"] = void 0; exports.findPointFieldPairs = findPointFieldPairs; exports.getFieldValueAccessor = getFieldValueAccessor; exports.maybeToDate = maybeToDate; exports.pinTableColumns = pinTableColumns; exports.sortDatasetByColumn = sortDatasetByColumn; var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck")); var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _console = _interopRequireDefault(require("global/console")); var _d3Array = require("d3-array"); var _constants = require("@kepler.gl/constants"); var _gpuFilterUtils = require("./gpu-filter-utils"); var _utils = require("@kepler.gl/utils"); var _commonUtils = require("@kepler.gl/common-utils"); function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } 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 // TODO isolate layer type, depends on @kepler.gl/layers // Unique identifier of each field var FID_KEY = 'name'; function maybeToDate(isTime, fieldIdx, format, dc, // An object with row index or a materialized row array (for materialized hover info from trip layer) d) { if (isTime) { return (0, _utils.timeToUnixMilli)(Array.isArray(d) ? d[fieldIdx] : dc.valueAt(d.index, fieldIdx), format); } return Array.isArray(d) ? d[fieldIdx] : dc.valueAt(d.index, fieldIdx); } var KeplerTable = /*#__PURE__*/function () { function KeplerTable(_ref) { var info = _ref.info, color = _ref.color, metadata = _ref.metadata, _ref$supportedFilterT = _ref.supportedFilterTypes, supportedFilterTypes = _ref$supportedFilterT === void 0 ? null : _ref$supportedFilterT, _ref$disableDataOpera = _ref.disableDataOperation, disableDataOperation = _ref$disableDataOpera === void 0 ? false : _ref$disableDataOpera; (0, _classCallCheck2["default"])(this, KeplerTable); (0, _defineProperty2["default"])(this, "id", void 0); (0, _defineProperty2["default"])(this, "type", void 0); (0, _defineProperty2["default"])(this, "label", void 0); (0, _defineProperty2["default"])(this, "color", void 0); // fields and data (0, _defineProperty2["default"])(this, "fields", []); (0, _defineProperty2["default"])(this, "dataContainer", void 0); (0, _defineProperty2["default"])(this, "allIndexes", []); (0, _defineProperty2["default"])(this, "filteredIndex", []); (0, _defineProperty2["default"])(this, "filteredIdxCPU", void 0); (0, _defineProperty2["default"])(this, "filteredIndexForDomain", []); (0, _defineProperty2["default"])(this, "fieldPairs", []); (0, _defineProperty2["default"])(this, "gpuFilter", void 0); (0, _defineProperty2["default"])(this, "filterRecord", void 0); (0, _defineProperty2["default"])(this, "filterRecordCPU", void 0); (0, _defineProperty2["default"])(this, "changedFilters", void 0); // table-injected metadata (0, _defineProperty2["default"])(this, "sortColumn", void 0); (0, _defineProperty2["default"])(this, "sortOrder", void 0); (0, _defineProperty2["default"])(this, "pinnedColumns", void 0); (0, _defineProperty2["default"])(this, "supportedFilterTypes", void 0); (0, _defineProperty2["default"])(this, "disableDataOperation", void 0); // table-injected metadata (0, _defineProperty2["default"])(this, "metadata", void 0); (0, _defineProperty2["default"])(this, "getFileProcessor", void 0); // TODO - what to do if validation fails? Can kepler handle exceptions? // const validatedData = validateInputData(data); // if (!validatedData) { // return this; // } var datasetInfo = _objectSpread({ id: (0, _commonUtils.generateHashId)(4), label: 'new dataset', type: '' }, info); var defaultMetadata = { id: datasetInfo.id, // @ts-ignore format: datasetInfo.format || '', label: datasetInfo.label || '' }; this.id = datasetInfo.id; this.type = datasetInfo.type; this.label = datasetInfo.label; this.color = color; this.metadata = _objectSpread(_objectSpread({}, defaultMetadata), metadata); this.supportedFilterTypes = supportedFilterTypes; this.disableDataOperation = disableDataOperation; this.dataContainer = (0, _utils.createDataContainer)([]); this.gpuFilter = (0, _gpuFilterUtils.getGpuFilterProps)([], this.id, [], undefined); } return (0, _createClass2["default"])(KeplerTable, [{ key: "importData", value: function () { var _importData = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(_ref2) { var data, dataContainerData, inputDataFormat, dataContainer, fields, allIndexes; return _regenerator["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: data = _ref2.data; dataContainerData = data.cols ? data.cols : data.rows; inputDataFormat = data.cols ? _utils.DataForm.COLS_ARRAY : _utils.DataForm.ROWS_ARRAY; dataContainer = (0, _utils.createDataContainer)(dataContainerData, { fields: data.fields, inputDataFormat: inputDataFormat }); fields = data.fields.map(function (f, i) { return _objectSpread(_objectSpread({}, f), {}, { fieldIdx: i, id: f.name, displayName: f.displayName || f.name, analyzerType: f.analyzerType || _constants.ALL_FIELD_TYPES.string, format: f.format || '', valueAccessor: getFieldValueAccessor(f, i, dataContainer) }); }); allIndexes = dataContainer.getPlainIndex(); this.dataContainer = dataContainer; this.allIndexes = allIndexes; this.filteredIndex = allIndexes; this.filteredIndexForDomain = allIndexes; this.fieldPairs = findPointFieldPairs(fields); // @ts-expect-error Make sure that fields satisfies F extends Field this.fields = fields; this.gpuFilter = (0, _gpuFilterUtils.getGpuFilterProps)([], this.id, fields, undefined); case 13: case "end": return _context.stop(); } }, _callee, this); })); function importData(_x) { return _importData.apply(this, arguments); } return importData; }() /** * update table with new data * @param data - new data e.g. the arrow data with new batches loaded */ }, { key: "update", value: (function () { var _update = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(data) { var _this$dataContainer$u, _this$dataContainer; var dataContainerData; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: dataContainerData = data.cols ? data.cols : data.rows; (_this$dataContainer$u = (_this$dataContainer = this.dataContainer).update) === null || _this$dataContainer$u === void 0 || _this$dataContainer$u.call(_this$dataContainer, dataContainerData); this.allIndexes = this.dataContainer.getPlainIndex(); this.filteredIndex = this.allIndexes; this.filteredIndexForDomain = this.allIndexes; return _context2.abrupt("return", this); case 6: case "end": return _context2.stop(); } }, _callee2, this); })); function update(_x2) { return _update.apply(this, arguments); } return update; }()) }, { key: "length", get: function get() { return this.dataContainer.numRows(); } /** * Get field * @param columnName */ }, { key: "getColumnField", value: function getColumnField(columnName) { var field = this.fields.find(function (fd) { return fd[FID_KEY] === columnName; }); this._assetField(columnName, field); return field; } /** * Get fieldIdx * @param columnName */ }, { key: "getColumnFieldIdx", value: function getColumnFieldIdx(columnName) { var fieldIdx = this.fields.findIndex(function (fd) { return fd[FID_KEY] === columnName; }); this._assetField(columnName, Boolean(fieldIdx > -1)); return fieldIdx; } /** * Get displayFormat * @param columnName */ }, { key: "getColumnDisplayFormat", value: function getColumnDisplayFormat(columnName) { var field = this.fields.find(function (fd) { return fd[FID_KEY] === columnName; }); this._assetField(columnName, field); return field === null || field === void 0 ? void 0 : field.displayFormat; } /** * Get the value of a cell */ }, { key: "getValue", value: function getValue(columnName, rowIdx) { var field = this.getColumnField(columnName); return field ? field.valueAccessor({ index: rowIdx }) : null; } /** * Updates existing field with a new object * @param fieldIdx * @param newField */ }, { key: "updateColumnField", value: function updateColumnField(fieldIdx, newField) { this.fields = Object.assign((0, _toConsumableArray2["default"])(this.fields), (0, _defineProperty2["default"])({}, fieldIdx, newField)); } /** * Update dataset color by custom color * @param newColor */ }, { key: "updateTableColor", value: function updateTableColor(newColor) { this.color = newColor; } /** * Save filterProps to field and retrieve it * @param columnName */ }, { key: "getColumnFilterProps", value: function getColumnFilterProps(columnName) { var fieldIdx = this.getColumnFieldIdx(columnName); if (fieldIdx < 0) { return null; } var field = this.fields[fieldIdx]; if (Object.prototype.hasOwnProperty.call(field, 'filterProps')) { return field.filterProps; } var fieldDomain = this.getColumnFilterDomain(field); if (!fieldDomain) { return null; } var filterProps = (0, _utils.getFilterProps)(field, fieldDomain); var newField = _objectSpread(_objectSpread({}, field), {}, { filterProps: filterProps }); this.updateColumnField(fieldIdx, newField); return filterProps; } /** * Apply filters to dataset, return the filtered dataset with updated `gpuFilter`, `filterRecord`, `filteredIndex`, `filteredIndexForDomain` * @param filters * @param layers * @param opt */ }, { key: "filterTable", value: function filterTable(filters, layers, opt) { var _this = this; var dataContainer = this.dataContainer, dataId = this.id, oldFilterRecord = this.filterRecord, fields = this.fields; // if there is no filters var filterRecord = (0, _utils.getFilterRecord)(dataId, filters, opt || {}); this.filterRecord = filterRecord; this.gpuFilter = (0, _gpuFilterUtils.getGpuFilterProps)(filters, dataId, fields, this.gpuFilter); this.changedFilters = (0, _utils.diffFilters)(filterRecord, oldFilterRecord); if (!filters.length) { this.filteredIndex = this.allIndexes; this.filteredIndexForDomain = this.allIndexes; return this; } // generate 2 sets of filter result // filteredIndex used to calculate layer data // filteredIndexForDomain used to calculate layer Domain var shouldCalDomain = Boolean(this.changedFilters.dynamicDomain); var shouldCalIndex = Boolean(this.changedFilters.cpu); var filterResult = {}; if (shouldCalDomain || shouldCalIndex) { var dynamicDomainFilters = shouldCalDomain ? filterRecord.dynamicDomain : null; var cpuFilters = shouldCalIndex ? filterRecord.cpu : null; var filterFuncs = filters.reduce(function (acc, filter) { var fieldIndex = (0, _gpuFilterUtils.getDatasetFieldIndexForFilter)(_this.id, filter); var field = fieldIndex !== -1 ? fields[fieldIndex] : null; return _objectSpread(_objectSpread({}, acc), {}, (0, _defineProperty2["default"])({}, filter.id, (0, _utils.getFilterFunction)(field, _this.id, filter, layers, dataContainer))); }, {}); filterResult = (0, _utils.filterDataByFilterTypes)({ dynamicDomainFilters: dynamicDomainFilters, cpuFilters: cpuFilters, filterFuncs: filterFuncs }, dataContainer); } this.filteredIndex = filterResult.filteredIndex || this.filteredIndex; this.filteredIndexForDomain = filterResult.filteredIndexForDomain || this.filteredIndexForDomain; return this; } /** * Apply filters to a dataset all on CPU, assign to `filteredIdxCPU`, `filterRecordCPU` * @param filters * @param layers */ }, { key: "filterTableCPU", value: function filterTableCPU(filters, layers) { var opt = { cpuOnly: true, ignoreDomain: true }; // no filter if (!filters.length) { this.filteredIdxCPU = this.allIndexes; this.filterRecordCPU = (0, _utils.getFilterRecord)(this.id, filters, opt); return this; } // no gpu filter if (!filters.find(function (f) { return f.gpu; })) { this.filteredIdxCPU = this.filteredIndex; this.filterRecordCPU = (0, _utils.getFilterRecord)(this.id, filters, opt); return this; } // make a copy for cpu filtering var copied = copyTable(this); copied.filterRecord = this.filterRecordCPU; copied.filteredIndex = this.filteredIdxCPU || []; var filtered = copied.filterTable(filters, layers, opt); this.filteredIdxCPU = filtered.filteredIndex; this.filterRecordCPU = filtered.filterRecord; return this; } /** * Calculate field domain based on field type and data * for Filter */ }, { key: "getColumnFilterDomain", value: function getColumnFilterDomain(field) { var dataContainer = this.dataContainer; var valueAccessor = field.valueAccessor; var domain; switch (field.type) { case _constants.ALL_FIELD_TYPES.real: case _constants.ALL_FIELD_TYPES.integer: // calculate domain and step return (0, _utils.getNumericFieldDomain)(dataContainer, valueAccessor); case _constants.ALL_FIELD_TYPES["boolean"]: return { domain: [true, false] }; case _constants.ALL_FIELD_TYPES.string: case _constants.ALL_FIELD_TYPES.h3: case _constants.ALL_FIELD_TYPES.date: domain = (0, _utils.getOrdinalDomain)(dataContainer, valueAccessor); return { domain: domain }; case _constants.ALL_FIELD_TYPES.timestamp: return (0, _utils.getTimestampFieldDomain)(dataContainer, valueAccessor); default: return { domain: (0, _utils.getOrdinalDomain)(dataContainer, valueAccessor) }; } } /** * Get the domain of this column based on scale type */ }, { key: "getColumnLayerDomain", value: function getColumnLayerDomain(field, scaleType) { var dataContainer = this.dataContainer, filteredIndexForDomain = this.filteredIndexForDomain; if (!_constants.SCALE_TYPES[scaleType]) { _console["default"].error("scale type ".concat(scaleType, " not supported")); return null; } var valueAccessor = field.valueAccessor; var indexValueAccessor = function indexValueAccessor(i) { return valueAccessor({ index: i }); }; var sortFunction = (0, _utils.getSortingFunction)(field.type); switch (scaleType) { case _constants.SCALE_TYPES.ordinal: case _constants.SCALE_TYPES.customOrdinal: case _constants.SCALE_TYPES.point: // do not recalculate ordinal domain based on filtered data // don't need to update ordinal domain every time return (0, _utils.getOrdinalDomain)(dataContainer, valueAccessor); case _constants.SCALE_TYPES.quantile: return (0, _utils.getQuantileDomain)(filteredIndexForDomain, indexValueAccessor, sortFunction); case _constants.SCALE_TYPES.log: return (0, _utils.getLogDomain)(filteredIndexForDomain, indexValueAccessor); case _constants.SCALE_TYPES.quantize: case _constants.SCALE_TYPES.linear: case _constants.SCALE_TYPES.sqrt: case _constants.SCALE_TYPES.custom: default: return (0, _utils.getLinearDomain)(filteredIndexForDomain, indexValueAccessor); } } /** * Get a sample of rows to calculate layer boundaries */ // getSampleData(rows) /** * Parse cell value based on column type and return a string representation * Value the field value, type the field type */ // parseFieldValue(value, type) // sortDatasetByColumn() /** * Assert whether field exist * @param fieldName * @param condition */ }, { key: "_assetField", value: function _assetField(fieldName, condition) { if (!condition) { _console["default"].error("".concat(fieldName, " doesnt exist in dataset ").concat(this.id)); } } }]); }(); // HELPER FUNCTIONS (MAINLY EXPORTED FOR TEST...) // have to double excape var specialCharacterSet = "[#_&@\\.\\-\\ ]"; function foundMatchingFields(re, suffixPair, allNames, fieldName) { var partnerIdx = allNames.findIndex(function (d) { return d === fieldName.replace(re, function (match) { return match.replace(suffixPair[0], suffixPair[1]); }); }); var altIdx = -1; if (partnerIdx > -1) { // if found partner, go on and look for altitude _constants.ALTITUDE_FIELDS.some(function (alt) { altIdx = allNames.findIndex(function (d) { return d === fieldName.replace(re, function (match) { return match.replace(suffixPair[0], alt); }); }); return altIdx > -1; }); } return { partnerIdx: partnerIdx, altIdx: altIdx }; } /** * Find point fields pairs from fields * * @param fields * @returns found point fields */ function findPointFieldPairs(fields) { var allNames = fields.map(function (f) { return f.name.toLowerCase(); }); // get list of all fields with matching suffixes var acc = []; return allNames.reduce(function (carry, fieldName, idx) { // This search for pairs will early exit if found. var _iterator = _createForOfIteratorHelper(_constants.TRIP_POINT_FIELDS), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var suffixPair = _step.value; // match first suffix // (^|[#_&@\.\-\ ])lat([#_&@\.\-\ ]|$) var re = new RegExp("(^|".concat(specialCharacterSet, ")").concat(suffixPair[0], "(").concat(specialCharacterSet, "|$)")); if (re.test(fieldName)) { var _foundMatchingFields = foundMatchingFields(re, suffixPair, allNames, fieldName), partnerIdx = _foundMatchingFields.partnerIdx, altIdx = _foundMatchingFields.altIdx; if (partnerIdx > -1) { var trimName = fieldName.replace(re, '').trim(); carry.push({ defaultName: trimName || 'point', pair: _objectSpread({ lat: { fieldIdx: idx, value: fields[idx].name }, lng: { fieldIdx: partnerIdx, value: fields[partnerIdx].name } }, altIdx > -1 ? { altitude: { fieldIdx: altIdx, value: fields[altIdx].name } } : {}), suffix: suffixPair }); return carry; } } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } return carry; }, acc); } /** * * @param dataset * @param column * @param mode * @type */ function sortDatasetByColumn(dataset, column, mode) { var allIndexes = dataset.allIndexes, fields = dataset.fields, dataContainer = dataset.dataContainer; var fieldIndex = fields.findIndex(function (f) { return f.name === column; }); if (fieldIndex < 0) { return dataset; } var sortBy = _constants.SORT_ORDER[mode || ''] || _constants.SORT_ORDER.ASCENDING; if (sortBy === _constants.SORT_ORDER.UNSORT) { dataset.sortColumn = {}; dataset.sortOrder = null; return dataset; } var sortFunction = sortBy === _constants.SORT_ORDER.ASCENDING ? _d3Array.ascending : _d3Array.descending; var sortOrder = allIndexes.slice().sort(function (a, b) { var value1 = dataContainer.valueAt(a, fieldIndex); var value2 = dataContainer.valueAt(b, fieldIndex); if (!(0, _commonUtils.notNullorUndefined)(value1) && (0, _commonUtils.notNullorUndefined)(value2)) { return 1; } else if ((0, _commonUtils.notNullorUndefined)(value1) && !(0, _commonUtils.notNullorUndefined)(value2)) { return -1; } return sortFunction(value1, value2); }); dataset.sortColumn = (0, _defineProperty2["default"])({}, column, sortBy); dataset.sortOrder = sortOrder; return dataset; } function pinTableColumns(dataset, column) { var field = dataset.getColumnField(column); if (!field) { return dataset; } var pinnedColumns; if (Array.isArray(dataset.pinnedColumns) && dataset.pinnedColumns.includes(field.name)) { // unpin it pinnedColumns = dataset.pinnedColumns.filter(function (co) { return co !== field.name; }); } else { pinnedColumns = (dataset.pinnedColumns || []).concat(field.name); } // @ts-ignore return copyTableAndUpdate(dataset, { pinnedColumns: pinnedColumns }); } function copyTable(original) { return Object.assign(Object.create(Object.getPrototypeOf(original)), original); } /** * @type * @returns */ function copyTableAndUpdate(original) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; return Object.entries(options).reduce(function (acc, entry) { acc[entry[0]] = entry[1]; return acc; }, copyTable(original)); } function getFieldValueAccessor(f, i, dc) { return maybeToDate.bind(null, // is time f.type === _constants.ALL_FIELD_TYPES.timestamp, i, f.format || '', dc); } var _default = exports["default"] = KeplerTable; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29uc29sZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2QzQXJyYXkiLCJfY29uc3RhbnRzIiwiX2dwdUZpbHRlclV0aWxzIiwiX3V0aWxzIiwiX2NvbW1vblV0aWxzIiwiX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIiLCJyIiwiZSIsInQiLCJTeW1ib2wiLCJpdGVyYXRvciIsIkFycmF5IiwiaXNBcnJheSIsIl91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSIsImxlbmd0aCIsIl9uIiwiRiIsInMiLCJuIiwiZG9uZSIsInZhbHVlIiwiZiIsIlR5cGVFcnJvciIsIm8iLCJhIiwidSIsImNhbGwiLCJuZXh0IiwiX2FycmF5TGlrZVRvQXJyYXkiLCJ0b1N0cmluZyIsInNsaWNlIiwiY29uc3RydWN0b3IiLCJuYW1lIiwiZnJvbSIsInRlc3QiLCJvd25LZXlzIiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImRlZmluZVByb3BlcnR5IiwiRklEX0tFWSIsIm1heWJlVG9EYXRlIiwiaXNUaW1lIiwiZmllbGRJZHgiLCJmb3JtYXQiLCJkYyIsImQiLCJ0aW1lVG9Vbml4TWlsbGkiLCJ2YWx1ZUF0IiwiaW5kZXgiLCJLZXBsZXJUYWJsZSIsIl9yZWYiLCJpbmZvIiwiY29sb3IiLCJtZXRhZGF0YSIsIl9yZWYkc3VwcG9ydGVkRmlsdGVyVCIsInN1cHBvcnRlZEZpbHRlclR5cGVzIiwiX3JlZiRkaXNhYmxlRGF0YU9wZXJhIiwiZGlzYWJsZURhdGFPcGVyYXRpb24iLCJfY2xhc3NDYWxsQ2hlY2syIiwiZGF0YXNldEluZm8iLCJpZCIsImdlbmVyYXRlSGFzaElkIiwibGFiZWwiLCJ0eXBlIiwiZGVmYXVsdE1ldGFkYXRhIiwiZGF0YUNvbnRhaW5lciIsImNyZWF0ZURhdGFDb250YWluZXIiLCJncHVGaWx0ZXIiLCJnZXRHcHVGaWx0ZXJQcm9wcyIsInVuZGVmaW5lZCIsIl9jcmVhdGVDbGFzczIiLCJrZXkiLCJfaW1wb3J0RGF0YSIsIl9hc3luY1RvR2VuZXJhdG9yMiIsIl9yZWdlbmVyYXRvciIsIm1hcmsiLCJfY2FsbGVlIiwiX3JlZjIiLCJkYXRhIiwiZGF0YUNvbnRhaW5lckRhdGEiLCJpbnB1dERhdGFGb3JtYXQiLCJmaWVsZHMiLCJhbGxJbmRleGVzIiwid3JhcCIsIl9jYWxsZWUkIiwiX2NvbnRleHQiLCJwcmV2IiwiY29scyIsInJvd3MiLCJEYXRhRm9ybSIsIkNPTFNfQVJSQVkiLCJST1dTX0FSUkFZIiwibWFwIiwiaSIsImRpc3BsYXlOYW1lIiwiYW5hbHl6ZXJUeXBlIiwiQUxMX0ZJRUxEX1RZUEVTIiwic3RyaW5nIiwidmFsdWVBY2Nlc3NvciIsImdldEZpZWxkVmFsdWVBY2Nlc3NvciIsImdldFBsYWluSW5kZXgiLCJmaWx0ZXJlZEluZGV4IiwiZmlsdGVyZWRJbmRleEZvckRvbWFpbiIsImZpZWxkUGFpcnMiLCJmaW5kUG9pbnRGaWVsZFBhaXJzIiwic3RvcCIsImltcG9ydERhdGEiLCJfeCIsIl91cGRhdGUiLCJfY2FsbGVlMiIsIl90aGlzJGRhdGFDb250YWluZXIkdSIsIl90aGlzJGRhdGFDb250YWluZXIiLCJfY2FsbGVlMiQiLCJfY29udGV4dDIiLCJ1cGRhdGUiLCJhYnJ1cHQiLCJfeDIiLCJnZXQiLCJudW1Sb3dzIiwiZ2V0Q29sdW1uRmllbGQiLCJjb2x1bW5OYW1lIiwiZmllbGQiLCJmaW5kIiwiZmQiLCJfYXNzZXRGaWVsZCIsImdldENvbHVtbkZpZWxkSWR4IiwiZmluZEluZGV4IiwiQm9vbGVhbiIsImdldENvbHVtbkRpc3BsYXlGb3JtYXQiLCJkaXNwbGF5Rm9ybWF0IiwiZ2V0VmFsdWUiLCJyb3dJZHgiLCJ1cGRhdGVDb2x1bW5GaWVsZCIsIm5ld0ZpZWxkIiwiYXNzaWduIiwiX3RvQ29uc3VtYWJsZUFycmF5MiIsInVwZGF0ZVRhYmxlQ29sb3IiLCJuZXdDb2xvciIsImdldENvbHVtbkZpbHRlclByb3BzIiwicHJvdG90eXBlIiwiaGFzT3duUHJvcGVydHkiLCJmaWx0ZXJQcm9wcyIsImZpZWxkRG9tYWluIiwiZ2V0Q29sdW1uRmlsdGVyRG9tYWluIiwiZ2V0RmlsdGVyUHJvcHMiLCJmaWx0ZXJUYWJsZSIsImZpbHRlcnMiLCJsYXllcnMiLCJvcHQiLCJfdGhpcyIsImRhdGFJZCIsIm9sZEZpbHRlclJlY29yZCIsImZpbHRlclJlY29yZCIsImdldEZpbHRlclJlY29yZCIsImNoYW5nZWRGaWx0ZXJzIiwiZGlmZkZpbHRlcnMiLCJzaG91bGRDYWxEb21haW4iLCJkeW5hbWljRG9tYWluIiwic2hvdWxkQ2FsSW5kZXgiLCJjcHUiLCJmaWx0ZXJSZXN1bHQiLCJkeW5hbWljRG9tYWluRmlsdGVycyIsImNwdUZpbHRlcnMiLCJmaWx0ZXJGdW5jcyIsInJlZHVjZSIsImFjYyIsImZpZWxkSW5kZXgiLCJnZXREYXRhc2V0RmllbGRJbmRleEZvckZpbHRlciIsImdldEZpbHRlckZ1bmN0aW9uIiwiZmlsdGVyRGF0YUJ5RmlsdGVyVHlwZXMiLCJmaWx0ZXJUYWJsZUNQVSIsImNwdU9ubHkiLCJpZ25vcmVEb21haW4iLCJmaWx0ZXJlZElkeENQVSIsImZpbHRlclJlY29yZENQVSIsImdwdSIsImNvcGllZCIsImNvcHlUYWJsZSIsImZpbHRlcmVkIiwiZG9tYWluIiwicmVhbCIsImludGVnZXIiLCJnZXROdW1lcmljRmllbGREb21haW4iLCJoMyIsImRhdGUiLCJnZXRPcmRpbmFsRG9tYWluIiwidGltZXN0YW1wIiwiZ2V0VGltZXN0YW1wRmllbGREb21haW4iLCJnZXRDb2x1bW5MYXllckRvbWFpbiIsInNjYWxlVHlwZSIsIlNDQUxFX1RZUEVTIiwiQ29uc29sZSIsImVycm9yIiwiY29uY2F0IiwiaW5kZXhWYWx1ZUFjY2Vzc29yIiwic29ydEZ1bmN0aW9uIiwiZ2V0U29ydGluZ0Z1bmN0aW9uIiwib3JkaW5hbCIsImN1c3RvbU9yZGluYWwiLCJwb2ludCIsInF1YW50aWxlIiwiZ2V0UXVhbnRpbGVEb21haW4iLCJsb2ciLCJnZXRMb2dEb21haW4iLCJxdWFudGl6ZSIsImxpbmVhciIsInNxcnQiLCJjdXN0b20iLCJnZXRMaW5lYXJEb21haW4iLCJmaWVsZE5hbWUiLCJjb25kaXRpb24iLCJzcGVjaWFsQ2hhcmFjdGVyU2V0IiwiZm91bmRNYXRjaGluZ0ZpZWxkcyIsInJlIiwic3VmZml4UGFpciIsImFsbE5hbWVzIiwicGFydG5lcklkeCIsInJlcGxhY2UiLCJtYXRjaCIsImFsdElkeCIsIkFMVElUVURFX0ZJRUxEUyIsInNvbWUiLCJhbHQiLCJ0b0xvd2VyQ2FzZSIsImNhcnJ5IiwiaWR4IiwiX2l0ZXJhdG9yIiwiVFJJUF9QT0lOVF9GSUVMRFMiLCJfc3RlcCIsIlJlZ0V4cCIsIl9mb3VuZE1hdGNoaW5nRmllbGRzIiwidHJpbU5hbWUiLCJ0cmltIiwiZGVmYXVsdE5hbWUiLCJwYWlyIiwibGF0IiwibG5nIiwiYWx0aXR1ZGUiLCJzdWZmaXgiLCJlcnIiLCJzb3J0RGF0YXNldEJ5Q29sdW1uIiwiZGF0YXNldCIsImNvbHVtbiIsIm1vZGUiLCJzb3J0QnkiLCJTT1JUX09SREVSIiwiQVNDRU5ESU5HIiwiVU5TT1JUIiwic29ydENvbHVtbiIsInNvcnRPcmRlciIsImFzY2VuZGluZyIsImRlc2NlbmRpbmciLCJzb3J0IiwiYiIsInZhbHVlMSIsInZhbHVlMiIsIm5vdE51bGxvclVuZGVmaW5lZCIsInBpblRhYmxlQ29sdW1ucyIsInBpbm5lZENvbHVtbnMiLCJpbmNsdWRlcyIsImNvIiwiY29weVRhYmxlQW5kVXBkYXRlIiwib3JpZ2luYWwiLCJjcmVhdGUiLCJnZXRQcm90b3R5cGVPZiIsIm9wdGlvbnMiLCJlbnRyaWVzIiwiZW50cnkiLCJiaW5kIiwiX2RlZmF1bHQiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vc3JjL2tlcGxlci10YWJsZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQgQ29uc29sZSBmcm9tICdnbG9iYWwvY29uc29sZSc7XG5pbXBvcnQge2FzY2VuZGluZywgZGVzY2VuZGluZ30gZnJvbSAnZDMtYXJyYXknO1xuXG5pbXBvcnQge1xuICBUUklQX1BPSU5UX0ZJRUxEUyxcbiAgU09SVF9PUkRFUixcbiAgQUxMX0ZJRUxEX1RZUEVTLFxuICBBTFRJVFVERV9GSUVMRFMsXG4gIFNDQUxFX1RZUEVTXG59IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcbmltcG9ydCB7XG4gIFJHQkNvbG9yLFxuICBGaWVsZCxcbiAgRmllbGRQYWlyLFxuICBGaWVsZERvbWFpbixcbiAgRmlsdGVyLFxuICBQcm90b0RhdGFzZXQsXG4gIEZpbHRlclJlY29yZCxcbiAgRmlsdGVyRGF0YXNldE9wdCxcbiAgUmFuZ2VGaWVsZERvbWFpbixcbiAgU2VsZWN0RmllbGREb21haW4sXG4gIE11bHRpU2VsZWN0RmllbGREb21haW4sXG4gIFRpbWVSYW5nZUZpZWxkRG9tYWluXG59IGZyb20gJ0BrZXBsZXIuZ2wvdHlwZXMnO1xuXG5pbXBvcnQge2dldEdwdUZpbHRlclByb3BzLCBnZXREYXRhc2V0RmllbGRJbmRleEZvckZpbHRlcn0gZnJvbSAnLi9ncHUtZmlsdGVyLXV0aWxzJztcblxuaW1wb3J0IHtcbiAgZ2V0U29ydGluZ0Z1bmN0aW9uLFxuICB0aW1lVG9Vbml4TWlsbGksXG4gIGNyZWF0ZURhdGFDb250YWluZXIsXG4gIERhdGFGb3JtLFxuICBkaWZmRmlsdGVycyxcbiAgZmlsdGVyRGF0YUJ5RmlsdGVyVHlwZXMsXG4gIEZpbHRlclJlc3VsdCxcbiAgZ2V0RmlsdGVyRnVuY3Rpb24sXG4gIGdldEZpbHRlclByb3BzLFxuICBnZXRGaWx0ZXJSZWNvcmQsXG4gIGdldE51bWVyaWNGaWVsZERvbWFpbixcbiAgZ2V0VGltZXN0YW1wRmllbGREb21haW4sXG4gIGdldExpbmVhckRvbWFpbixcbiAgZ2V0TG9nRG9tYWluLFxuICBnZXRPcmRpbmFsRG9tYWluLFxuICBnZXRRdWFudGlsZURvbWFpbixcbiAgRGF0YUNvbnRhaW5lckludGVyZmFjZSxcbiAgRmlsdGVyQ2hhbmdlZFxufSBmcm9tICdAa2VwbGVyLmdsL3V0aWxzJztcbmltcG9ydCB7Z2VuZXJhdGVIYXNoSWQsIG5vdE51bGxvclVuZGVmaW5lZH0gZnJvbSAnQGtlcGxlci5nbC9jb21tb24tdXRpbHMnO1xuXG4vLyBUT0RPIGlzb2xhdGUgbGF5ZXIgdHlwZSwgZGVwZW5kcyBvbiBAa2VwbGVyLmdsL2xheWVyc1xudHlwZSBMYXllciA9IGFueTtcblxuZXhwb3J0IHR5cGUgR3B1RmlsdGVyID0ge1xuICBmaWx0ZXJSYW5nZTogbnVtYmVyW11bXTtcbiAgZmlsdGVyVmFsdWVVcGRhdGVUcmlnZ2Vyczoge1xuICAgIFtpZDogc3RyaW5nXToge25hbWU6IHN0cmluZzsgZG9tYWluMDogbnVtYmVyfSB8IG51bGw7XG4gIH07XG4gIGZpbHRlclZhbHVlQWNjZXNzb3I6IChcbiAgICBkYzogRGF0YUNvbnRhaW5lckludGVyZmFjZVxuICApID0+IChcbiAgICBnZXRJbmRleD86IChhbnkpID0+IG51bWJlcixcbiAgICBnZXREYXRhPzogKGRjXzogRGF0YUNvbnRhaW5lckludGVyZmFjZSwgZDogYW55LCBmaWVsZEluZGV4OiBudW1iZXIpID0+IGFueVxuICApID0+IChkOiBhbnksIG9iamVjdEluZm8/OiB7aW5kZXg6IG51bWJlcn0pID0+IChudW1iZXIgfCBudW1iZXJbXSlbXTtcbn07XG5cbmV4cG9ydCB0eXBlIEZpbHRlclByb3BzID1cbiAgfCBOdW1lcmljRmllbGRGaWx0ZXJQcm9wc1xuICB8IEJvb2xlYW5GaWVsZEZpbHRlclByb3BzXG4gIHwgU3RyaW5nRmllbGRGaWx0ZXJQcm9wc1xuICB8IFRpbWVGaWVsZEZpbHRlclByb3BzO1xuXG5leHBvcnQgdHlwZSBOdW1lcmljRmllbGRGaWx0ZXJQcm9wcyA9IFJhbmdlRmllbGREb21haW4gJiB7XG4gIHZhbHVlOiBbbnVtYmVyLCBudW1iZXJdO1xuICB0eXBlOiBzdHJpbmc7XG4gIHR5cGVPcHRpb25zOiBzdHJpbmdbXTtcbiAgZ3B1OiBib29sZWFuO1xuICBjb2x1bW5TdGF0cz86IFJlY29yZDxzdHJpbmcsIGFueT47XG59O1xuZXhwb3J0IHR5cGUgQm9vbGVhbkZpZWxkRmlsdGVyUHJvcHMgPSBTZWxlY3RGaWVsZERvbWFpbiAmIHtcbiAgdHlwZTogc3RyaW5nO1xuICB2YWx1ZTogYm9vbGVhbjtcbiAgZ3B1OiBib29sZWFuO1xuICBjb2x1bW5TdGF0cz86IFJlY29yZDxzdHJpbmcsIGFueT47XG59O1xuZXhwb3J0IHR5cGUgU3RyaW5nRmllbGRGaWx0ZXJQcm9wcyA9IE11bHRpU2VsZWN0RmllbGREb21haW4gJiB7XG4gIHR5cGU6IHN0cmluZztcbiAgdmFsdWU6IHN0cmluZ1tdO1xuICBncHU6IGJvb2xlYW47XG4gIGNvbHVtblN0YXRzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn07XG5leHBvcnQgdHlwZSBUaW1lRmllbGRGaWx0ZXJQcm9wcyA9IFRpbWVSYW5nZUZpZWxkRG9tYWluICYge1xuICB0eXBlOiBzdHJpbmc7XG4gIHZpZXc6IEZpbHRlclsndmlldyddO1xuICBmaXhlZERvbWFpbjogYm9vbGVhbjtcbiAgdmFsdWU6IG51bWJlcltdO1xuICBncHU6IGJvb2xlYW47XG4gIGNvbHVtblN0YXRzPzogUmVjb3JkPHN0cmluZywgYW55Pjtcbn07XG5cbi8vIFVuaXF1ZSBpZGVudGlmaWVyIG9mIGVhY2ggZmllbGRcbmNvbnN0IEZJRF9LRVkgPSAnbmFtZSc7XG5cbmV4cG9ydCBmdW5jdGlvbiBtYXliZVRvRGF0ZShcbiAgaXNUaW1lOiBib29sZWFuLFxuICBmaWVsZElkeDogbnVtYmVyLFxuICBmb3JtYXQ6IHN0cmluZyxcbiAgZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2UsXG4gIC8vIEFuIG9iamVjdCB3aXRoIHJvdyBpbmRleCBvciBhIG1hdGVyaWFsaXplZCByb3cgYXJyYXkgKGZvciBtYXRlcmlhbGl6ZWQgaG92ZXIgaW5mbyBmcm9tIHRyaXAgbGF5ZXIpXG4gIGQ6IHtpbmRleDogbnVtYmVyfSB8IGFueVtdXG4pIHtcbiAgaWYgKGlzVGltZSkge1xuICAgIHJldHVybiB0aW1lVG9Vbml4TWlsbGkoQXJyYXkuaXNBcnJheShkKSA/IGRbZmllbGRJZHhdIDogZGMudmFsdWVBdChkLmluZGV4LCBmaWVsZElkeCksIGZvcm1hdCk7XG4gIH1cblxuICByZXR1cm4gQXJyYXkuaXNBcnJheShkKSA/IGRbZmllbGRJZHhdIDogZGMudmFsdWVBdChkLmluZGV4LCBmaWVsZElkeCk7XG59XG5cbmNsYXNzIEtlcGxlclRhYmxlPEYgZXh0ZW5kcyBGaWVsZCA9IEZpZWxkPiB7XG4gIHJlYWRvbmx5IGlkOiBzdHJpbmc7XG5cbiAgdHlwZT86IHN0cmluZztcbiAgbGFiZWw6IHN0cmluZztcbiAgY29sb3I6IFJHQkNvbG9yO1xuXG4gIC8vIGZpZWxkcyBhbmQgZGF0YVxuICBmaWVsZHM6IEZbXSA9IFtdO1xuXG4gIGRhdGFDb250YWluZXI6IERhdGFDb250YWluZXJJbnRlcmZhY2U7XG5cbiAgYWxsSW5kZXhlczogbnVtYmVyW10gPSBbXTtcbiAgZmlsdGVyZWRJbmRleDogbnVtYmVyW10gPSBbXTtcbiAgZmlsdGVyZWRJZHhDUFU/OiBudW1iZXJbXTtcbiAgZmlsdGVyZWRJbmRleEZvckRvbWFpbjogbnVtYmVyW10gPSBbXTtcbiAgZmllbGRQYWlyczogRmllbGRQYWlyW10gPSBbXTtcbiAgZ3B1RmlsdGVyOiBHcHVGaWx0ZXI7XG4gIGZpbHRlclJlY29yZD86IEZpbHRlclJlY29yZDtcbiAgZmlsdGVyUmVjb3JkQ1BVPzogRmlsdGVyUmVjb3JkO1xuICBjaGFuZ2VkRmlsdGVycz86IEZpbHRlckNoYW5nZWQ7XG5cbiAgLy8gdGFibGUtaW5qZWN0ZWQgbWV0YWRhdGFcbiAgc29ydENvbHVtbj86IHtcbiAgICAvLyBjb2x1bW4gbmFtZTogc29ydGVkIGlkeFxuICAgIFtrZXk6IHN0cmluZ106IHN0cmluZzsgLy8gQVNDRU5ESU5HIHwgREVTQ0VORElORyB8IFVOU09SVFxuICB9O1xuICBzb3J0T3JkZXI/OiBudW1iZXJbXSB8IG51bGw7XG5cbiAgcGlubmVkQ29sdW1ucz86IHN0cmluZ1tdO1xuICBzdXBwb3J0ZWRGaWx0ZXJUeXBlcz86IHN0cmluZ1tdIHwgbnVsbDtcbiAgZGlzYWJsZURhdGFPcGVyYXRpb24/OiBib29sZWFuO1xuXG4gIC8vIHRhYmxlLWluamVjdGVkIG1ldGFkYXRhXG4gIG1ldGFkYXRhOiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xuXG4gIGdldEZpbGVQcm9jZXNzb3I/OiAoZGF0YTogYW55LCBpbnB1dEZvcm1hdD86IHN0cmluZykgPT4ge2RhdGE6IGFueTsgZm9ybWF0OiBzdHJpbmd9O1xuXG4gIGNvbnN0cnVjdG9yKHtcbiAgICBpbmZvLFxuICAgIGNvbG9yLFxuICAgIG1ldGFkYXRhLFxuICAgIHN1cHBvcnRlZEZpbHRlclR5cGVzID0gbnVsbCxcbiAgICBkaXNhYmxlRGF0YU9wZXJhdGlvbiA9IGZhbHNlXG4gIH06IHtcbiAgICBpbmZvPzogUHJvdG9EYXRhc2V0WydpbmZvJ107XG4gICAgY29sb3I6IFJHQkNvbG9yO1xuICAgIG1ldGFkYXRhPzogUHJvdG9EYXRhc2V0WydtZXRhZGF0YSddO1xuICAgIHN1cHBvcnRlZEZpbHRlclR5cGVzPzogUHJvdG9EYXRhc2V0WydzdXBwb3J0ZWRGaWx0ZXJUeXBlcyddO1xuICAgIGRpc2FibGVEYXRhT3BlcmF0aW9uPzogUHJvdG9EYXRhc2V0WydkaXNhYmxlRGF0YU9wZXJhdGlvbiddO1xuICB9KSB7XG4gICAgLy8gVE9ETyAtIHdoYXQgdG8gZG8gaWYgdmFsaWRhdGlvbiBmYWlscz8gQ2FuIGtlcGxlciBoYW5kbGUgZXhjZXB0aW9ucz9cbiAgICAvLyBjb25zdCB2YWxpZGF0ZWREYXRhID0gdmFsaWRhdGVJbnB1dERhdGEoZGF0YSk7XG4gICAgLy8gaWYgKCF2YWxpZGF0ZWREYXRhKSB7XG4gICAgLy8gICByZXR1cm4gdGhpcztcbiAgICAvLyB9XG5cbiAgICBjb25zdCBkYXRhc2V0SW5mbyA9IHtcbiAgICAgIGlkOiBnZW5lcmF0ZUhhc2hJZCg0KSxcbiAgICAgIGxhYmVsOiAnbmV3IGRhdGFzZXQnLFxuICAgICAgdHlwZTogJycsXG4gICAgICAuLi5pbmZvXG4gICAgfTtcblxuICAgIGNvbnN0IGRlZmF1bHRNZXRhZGF0YSA9IHtcbiAgICAgIGlkOiBkYXRhc2V0SW5mby5pZCxcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIGZvcm1hdDogZGF0YXNldEluZm8uZm9ybWF0IHx8ICcnLFxuICAgICAgbGFiZWw6IGRhdGFzZXRJbmZvLmxhYmVsIHx8ICcnXG4gICAgfTtcblxuICAgIHRoaXMuaWQgPSBkYXRhc2V0SW5mby5pZDtcbiAgICB0aGlzLnR5cGUgPSBkYXRhc2V0SW5mby50eXBlO1xuICAgIHRoaXMubGFiZWwgPSBkYXRhc2V0SW5mby5sYWJlbDtcbiAgICB0aGlzLmNvbG9yID0gY29sb3I7XG4gICAgdGhpcy5tZXRhZGF0YSA9IHtcbiAgICAgIC4uLmRlZmF1bHRNZXRhZGF0YSxcbiAgICAgIC4uLm1ldGFkYXRhXG4gICAgfTtcblxuICAgIHRoaXMuc3VwcG9ydGVkRmlsdGVyVHlwZXMgPSBzdXBwb3J0ZWRGaWx0ZXJUeXBlcztcbiAgICB0aGlzLmRpc2FibGVEYXRhT3BlcmF0aW9uID0gZGlzYWJsZURhdGFPcGVyYXRpb247XG5cbiAgICB0aGlzLmRhdGFDb250YWluZXIgPSBjcmVhdGVEYXRhQ29udGFpbmVyKFtdKTtcbiAgICB0aGlzLmdwdUZpbHRlciA9IGdldEdwdUZpbHRlclByb3BzKFtdLCB0aGlzLmlkLCBbXSwgdW5kZWZpbmVkKTtcbiAgfVxuXG4gIGFzeW5jIGltcG9ydERhdGEoe2RhdGF9OiB7ZGF0YTogUHJvdG9EYXRhc2V0WydkYXRhJ119KSB7XG4gICAgY29uc3QgZGF0YUNvbnRhaW5lckRhdGEgPSBkYXRhLmNvbHMgPyBkYXRhLmNvbHMgOiBkYXRhLnJvd3M7XG4gICAgY29uc3QgaW5wdXREYXRhRm9ybWF0ID0gZGF0YS5jb2xzID8gRGF0YUZvcm0uQ09MU19BUlJBWSA6IERhdGFGb3JtLlJPV1NfQVJSQVk7XG5cbiAgICBjb25zdCBkYXRhQ29udGFpbmVyID0gY3JlYXRlRGF0YUNvbnRhaW5lcihkYXRhQ29udGFpbmVyRGF0YSwge1xuICAgICAgZmllbGRzOiBkYXRhLmZpZWxkcyxcbiAgICAgIGlucHV0RGF0YUZvcm1hdFxuICAgIH0pO1xuXG4gICAgY29uc3QgZmllbGRzOiBGaWVsZFtdID0gZGF0YS5maWVsZHMubWFwKChmLCBpKSA9PiAoe1xuICAgICAgLi4uZixcbiAgICAgIGZpZWxkSWR4OiBpLFxuICAgICAgaWQ6IGYubmFtZSxcbiAgICAgIGRpc3BsYXlOYW1lOiBmLmRpc3BsYXlOYW1lIHx8IGYubmFtZSxcbiAgICAgIGFuYWx5emVyVHlwZTogZi5hbmFseXplclR5cGUgfHwgQUxMX0ZJRUxEX1RZUEVTLnN0cmluZyxcbiAgICAgIGZvcm1hdDogZi5mb3JtYXQgfHwgJycsXG4gICAgICB2YWx1ZUFjY2Vzc29yOiBnZXRGaWVsZFZhbHVlQWNjZXNzb3IoZiwgaSwgZGF0YUNvbnRhaW5lcilcbiAgICB9KSk7XG5cbiAgICBjb25zdCBhbGxJbmRleGVzID0gZGF0YUNvbnRhaW5lci5nZXRQbGFpbkluZGV4KCk7XG4gICAgdGhpcy5kYXRhQ29udGFpbmVyID0gZGF0YUNvbnRhaW5lcjtcbiAgICB0aGlzLmFsbEluZGV4ZXMgPSBhbGxJbmRleGVzO1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleCA9IGFsbEluZGV4ZXM7XG4gICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID0gYWxsSW5kZXhlcztcbiAgICB0aGlzLmZpZWxkUGFpcnMgPSBmaW5kUG9pbnRGaWVsZFBhaXJzKGZpZWxkcyk7XG4gICAgLy8gQHRzLWV4cGVjdC1lcnJvciBNYWtlIHN1cmUgdGhhdCBmaWVsZHMgc2F0aXNmaWVzIEYgZXh0ZW5kcyBGaWVsZFxuICAgIHRoaXMuZmllbGRzID0gZmllbGRzO1xuICAgIHRoaXMuZ3B1RmlsdGVyID0gZ2V0R3B1RmlsdGVyUHJvcHMoW10sIHRoaXMuaWQsIGZpZWxkcywgdW5kZWZpbmVkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiB1cGRhdGUgdGFibGUgd2l0aCBuZXcgZGF0YVxuICAgKiBAcGFyYW0gZGF0YSAtIG5ldyBkYXRhIGUuZy4gdGhlIGFycm93IGRhdGEgd2l0aCBuZXcgYmF0Y2hlcyBsb2FkZWRcbiAgICovXG4gIGFzeW5jIHVwZGF0ZShkYXRhOiBQcm90b0RhdGFzZXRbJ2RhdGEnXSkge1xuICAgIGNvbnN0IGRhdGFDb250YWluZXJEYXRhID0gZGF0YS5jb2xzID8gZGF0YS5jb2xzIDogZGF0YS5yb3dzO1xuICAgIHRoaXMuZGF0YUNvbnRhaW5lci51cGRhdGU/LihkYXRhQ29udGFpbmVyRGF0YSk7XG4gICAgdGhpcy5hbGxJbmRleGVzID0gdGhpcy5kYXRhQ29udGFpbmVyLmdldFBsYWluSW5kZXgoKTtcbiAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSB0aGlzLmFsbEluZGV4ZXM7XG4gICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID0gdGhpcy5hbGxJbmRleGVzO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBnZXQgbGVuZ3RoKCkge1xuICAgIHJldHVybiB0aGlzLmRhdGFDb250YWluZXIubnVtUm93cygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBmaWVsZFxuICAgKiBAcGFyYW0gY29sdW1uTmFtZVxuICAgKi9cbiAgZ2V0Q29sdW1uRmllbGQoY29sdW1uTmFtZTogc3RyaW5nKTogRiB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmZpZWxkcy5maW5kKGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIGZpZWxkKTtcbiAgICByZXR1cm4gZmllbGQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGZpZWxkSWR4XG4gICAqIEBwYXJhbSBjb2x1bW5OYW1lXG4gICAqL1xuICBnZXRDb2x1bW5GaWVsZElkeChjb2x1bW5OYW1lOiBzdHJpbmcpOiBudW1iZXIge1xuICAgIGNvbnN0IGZpZWxkSWR4ID0gdGhpcy5maWVsZHMuZmluZEluZGV4KGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIEJvb2xlYW4oZmllbGRJZHggPiAtMSkpO1xuICAgIHJldHVybiBmaWVsZElkeDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgZGlzcGxheUZvcm1hdFxuICAgKiBAcGFyYW0gY29sdW1uTmFtZVxuICAgKi9cbiAgZ2V0Q29sdW1uRGlzcGxheUZvcm1hdChjb2x1bW5OYW1lKSB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmZpZWxkcy5maW5kKGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIGZpZWxkKTtcbiAgICByZXR1cm4gZmllbGQ/LmRpc3BsYXlGb3JtYXQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB2YWx1ZSBvZiBhIGNlbGxcbiAgICovXG4gIGdldFZhbHVlKGNvbHVtbk5hbWU6IHN0cmluZywgcm93SWR4OiBudW1iZXIpOiBhbnkge1xuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5nZXRDb2x1bW5GaWVsZChjb2x1bW5OYW1lKTtcbiAgICByZXR1cm4gZmllbGQgPyBmaWVsZC52YWx1ZUFjY2Vzc29yKHtpbmRleDogcm93SWR4fSkgOiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgZXhpc3RpbmcgZmllbGQgd2l0aCBhIG5ldyBvYmplY3RcbiAgICogQHBhcmFtIGZpZWxkSWR4XG4gICAqIEBwYXJhbSBuZXdGaWVsZFxuICAgKi9cbiAgdXBkYXRlQ29sdW1uRmllbGQoZmllbGRJZHg6IG51bWJlciwgbmV3RmllbGQ6IEYpOiB2b2lkIHtcbiAgICB0aGlzLmZpZWxkcyA9IE9iamVjdC5hc3NpZ24oWy4uLnRoaXMuZmllbGRzXSwge1tmaWVsZElkeF06IG5ld0ZpZWxkfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIGRhdGFzZXQgY29sb3IgYnkgY3VzdG9tIGNvbG9yXG4gICAqIEBwYXJhbSBuZXdDb2xvclxuICAgKi9cbiAgdXBkYXRlVGFibGVDb2xvcihuZXdDb2xvcjogUkdCQ29sb3IpOiB2b2lkIHtcbiAgICB0aGlzLmNvbG9yID0gbmV3Q29sb3I7XG4gIH1cblxuICAvKipcbiAgICogU2F2ZSBmaWx0ZXJQcm9wcyB0byBmaWVsZCBhbmQgcmV0cmlldmUgaXRcbiAgICogQHBhcmFtIGNvbHVtbk5hbWVcbiAgICovXG4gIGdldENvbHVtbkZpbHRlclByb3BzKGNvbHVtbk5hbWU6IHN0cmluZyk6IEZbJ2ZpbHRlclByb3BzJ10gfCBudWxsIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBmaWVsZElkeCA9IHRoaXMuZ2V0Q29sdW1uRmllbGRJZHgoY29sdW1uTmFtZSk7XG4gICAgaWYgKGZpZWxkSWR4IDwgMCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5maWVsZHNbZmllbGRJZHhdO1xuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoZmllbGQsICdmaWx0ZXJQcm9wcycpKSB7XG4gICAgICByZXR1cm4gZmllbGQuZmlsdGVyUHJvcHM7XG4gICAgfVxuXG4gICAgY29uc3QgZmllbGREb21haW4gPSB0aGlzLmdldENvbHVtbkZpbHRlckRvbWFpbihmaWVsZCk7XG4gICAgaWYgKCFmaWVsZERvbWFpbikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgZmlsdGVyUHJvcHMgPSBnZXRGaWx0ZXJQcm9wcyhmaWVsZCwgZmllbGREb21haW4pO1xuICAgIGNvbnN0IG5ld0ZpZWxkID0ge1xuICAgICAgLi4uZmllbGQsXG4gICAgICBmaWx0ZXJQcm9wc1xuICAgIH07XG5cbiAgICB0aGlzLnVwZGF0ZUNvbHVtbkZpZWxkKGZpZWxkSWR4LCBuZXdGaWVsZCk7XG5cbiAgICByZXR1cm4gZmlsdGVyUHJvcHM7XG4gIH1cblxuICAvKipcbiAgICogQXBwbHkgZmlsdGVycyB0byBkYXRhc2V0LCByZXR1cm4gdGhlIGZpbHRlcmVkIGRhdGFzZXQgd2l0aCB1cGRhdGVkIGBncHVGaWx0ZXJgLCBgZmlsdGVyUmVjb3JkYCwgYGZpbHRlcmVkSW5kZXhgLCBgZmlsdGVyZWRJbmRleEZvckRvbWFpbmBcbiAgICogQHBhcmFtIGZpbHRlcnNcbiAgICogQHBhcmFtIGxheWVyc1xuICAgKiBAcGFyYW0gb3B0XG4gICAqL1xuICBmaWx0ZXJUYWJsZShmaWx0ZXJzOiBGaWx0ZXJbXSwgbGF5ZXJzOiBMYXllcltdLCBvcHQ/OiBGaWx0ZXJEYXRhc2V0T3B0KTogS2VwbGVyVGFibGU8RmllbGQ+IHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lciwgaWQ6IGRhdGFJZCwgZmlsdGVyUmVjb3JkOiBvbGRGaWx0ZXJSZWNvcmQsIGZpZWxkc30gPSB0aGlzO1xuXG4gICAgLy8gaWYgdGhlcmUgaXMgbm8gZmlsdGVyc1xuICAgIGNvbnN0IGZpbHRlclJlY29yZCA9IGdldEZpbHRlclJlY29yZChkYXRhSWQsIGZpbHRlcnMsIG9wdCB8fCB7fSk7XG5cbiAgICB0aGlzLmZpbHRlclJlY29yZCA9IGZpbHRlclJlY29yZDtcbiAgICB0aGlzLmdwdUZpbHRlciA9IGdldEdwdUZpbHRlclByb3BzKGZpbHRlcnMsIGRhdGFJZCwgZmllbGRzLCB0aGlzLmdwdUZpbHRlcik7XG5cbiAgICB0aGlzLmNoYW5nZWRGaWx0ZXJzID0gZGlmZkZpbHRlcnMoZmlsdGVyUmVjb3JkLCBvbGRGaWx0ZXJSZWNvcmQpO1xuXG4gICAgaWYgKCFmaWx0ZXJzLmxlbmd0aCkge1xuICAgICAgdGhpcy5maWx0ZXJlZEluZGV4ID0gdGhpcy5hbGxJbmRleGVzO1xuICAgICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID0gdGhpcy5hbGxJbmRleGVzO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLy8gZ2VuZXJhdGUgMiBzZXRzIG9mIGZpbHRlciByZXN1bHRcbiAgICAvLyBmaWx0ZXJlZEluZGV4IHVzZWQgdG8gY2FsY3VsYXRlIGxheWVyIGRhdGFcbiAgICAvLyBmaWx0ZXJlZEluZGV4Rm9yRG9tYWluIHVzZWQgdG8gY2FsY3VsYXRlIGxheWVyIERvbWFpblxuICAgIGNvbnN0IHNob3VsZENhbERvbWFpbiA9IEJvb2xlYW4odGhpcy5jaGFuZ2VkRmlsdGVycy5keW5hbWljRG9tYWluKTtcbiAgICBjb25zdCBzaG91bGRDYWxJbmRleCA9IEJvb2xlYW4odGhpcy5jaGFuZ2VkRmlsdGVycy5jcHUpO1xuXG4gICAgbGV0IGZpbHRlclJlc3VsdDogRmlsdGVyUmVzdWx0ID0ge307XG4gICAgaWYgKHNob3VsZENhbERvbWFpbiB8fCBzaG91bGRDYWxJbmRleCkge1xuICAgICAgY29uc3QgZHluYW1pY0RvbWFpbkZpbHRlcnMgPSBzaG91bGRDYWxEb21haW4gPyBmaWx0ZXJSZWNvcmQuZHluYW1pY0RvbWFpbiA6IG51bGw7XG4gICAgICBjb25zdCBjcHVGaWx0ZXJzID0gc2hvdWxkQ2FsSW5kZXggPyBmaWx0ZXJSZWNvcmQuY3B1IDogbnVsbDtcblxuICAgICAgY29uc3QgZmlsdGVyRnVuY3MgPSBmaWx0ZXJzLnJlZHVjZSgoYWNjLCBmaWx0ZXIpID0+IHtcbiAgICAgICAgY29uc3QgZmllbGRJbmRleCA9IGdldERhdGFzZXRGaWVsZEluZGV4Rm9yRmlsdGVyKHRoaXMuaWQsIGZpbHRlcik7XG4gICAgICAgIGNvbnN0IGZpZWxkID0gZmllbGRJbmRleCAhPT0gLTEgPyBmaWVsZHNbZmllbGRJbmRleF0gOiBudWxsO1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgLi4uYWNjLFxuICAgICAgICAgIFtmaWx0ZXIuaWRdOiBnZXRGaWx0ZXJGdW5jdGlvbihmaWVsZCwgdGhpcy5pZCwgZmlsdGVyLCBsYXllcnMsIGRhdGFDb250YWluZXIpXG4gICAgICAgIH07XG4gICAgICB9LCB7fSk7XG5cbiAgICAgIGZpbHRlclJlc3VsdCA9IGZpbHRlckRhdGFCeUZpbHRlclR5cGVzKFxuICAgICAgICB7ZHluYW1pY0RvbWFpbkZpbHRlcnMsIGNwdUZpbHRlcnMsIGZpbHRlckZ1bmNzfSxcbiAgICAgICAgZGF0YUNvbnRhaW5lclxuICAgICAgKTtcbiAgICB9XG5cbiAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSBmaWx0ZXJSZXN1bHQuZmlsdGVyZWRJbmRleCB8fCB0aGlzLmZpbHRlcmVkSW5kZXg7XG4gICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID1cbiAgICAgIGZpbHRlclJlc3VsdC5maWx0ZXJlZEluZGV4Rm9yRG9tYWluIHx8IHRoaXMuZmlsdGVyZWRJbmRleEZvckRvbWFpbjtcblxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcGx5IGZpbHRlcnMgdG8gYSBkYXRhc2V0IGFsbCBvbiBDUFUsIGFzc2lnbiB0byBgZmlsdGVyZWRJZHhDUFVgLCBgZmlsdGVyUmVjb3JkQ1BVYFxuICAgKiBAcGFyYW0gZmlsdGVyc1xuICAgKiBAcGFyYW0gbGF5ZXJzXG4gICAqL1xuICBmaWx0ZXJUYWJsZUNQVShmaWx0ZXJzOiBGaWx0ZXJbXSwgbGF5ZXJzOiBMYXllcltdKTogS2VwbGVyVGFibGU8RmllbGQ+IHtcbiAgICBjb25zdCBvcHQgPSB7XG4gICAgICBjcHVPbmx5OiB0cnVlLFxuICAgICAgaWdub3JlRG9tYWluOiB0cnVlXG4gICAgfTtcblxuICAgIC8vIG5vIGZpbHRlclxuICAgIGlmICghZmlsdGVycy5sZW5ndGgpIHtcbiAgICAgIHRoaXMuZmlsdGVyZWRJZHhDUFUgPSB0aGlzLmFsbEluZGV4ZXM7XG4gICAgICB0aGlzLmZpbHRlclJlY29yZENQVSA9IGdldEZpbHRlclJlY29yZCh0aGlzLmlkLCBmaWx0ZXJzLCBvcHQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLy8gbm8gZ3B1IGZpbHRlclxuICAgIGlmICghZmlsdGVycy5maW5kKGYgPT4gZi5ncHUpKSB7XG4gICAgICB0aGlzLmZpbHRlcmVkSWR4Q1BVID0gdGhpcy5maWx0ZXJlZEluZGV4O1xuICAgICAgdGhpcy5maWx0ZXJSZWNvcmRDUFUgPSBnZXRGaWx0ZXJSZWNvcmQodGhpcy5pZCwgZmlsdGVycywgb3B0KTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8vIG1ha2UgYSBjb3B5IGZvciBjcHUgZmlsdGVyaW5nXG4gICAgY29uc3QgY29waWVkID0gY29weVRhYmxlKHRoaXMpO1xuXG4gICAgY29waWVkLmZpbHRlclJlY29yZCA9IHRoaXMuZmlsdGVyUmVjb3JkQ1BVO1xuICAgIGNvcGllZC5maWx0ZXJlZEluZGV4ID0gdGhpcy5maWx0ZXJlZElkeENQVSB8fCBbXTtcblxuICAgIGNvbnN0IGZpbHRlcmVkID0gY29waWVkLmZpbHRlclRhYmxlKGZpbHRlcnMsIGxheWVycywgb3B0KTtcblxuICAgIHRoaXMuZmlsdGVyZWRJZHhDUFUgPSBmaWx0ZXJlZC5maWx0ZXJlZEluZGV4O1xuICAgIHRoaXMuZmlsdGVyUmVjb3JkQ1BVID0gZmlsdGVyZWQuZmlsdGVyUmVjb3JkO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogQ2FsY3VsYXRlIGZpZWxkIGRvbWFpbiBiYXNlZCBvbiBmaWVsZCB0eXBlIGFuZCBkYXRhXG4gICAqIGZvciBGaWx0ZXJcbiAgICovXG4gIGdldENvbHVtbkZpbHRlckRvbWFpbihmaWVsZDogRik6IEZpZWxkRG9tYWluIHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lcn0gPSB0aGlzO1xuICAgIGNvbnN0IHt2YWx1ZUFjY2Vzc29yfSA9IGZpZWxkO1xuXG4gICAgbGV0IGRvbWFpbjtcblxuICAgIHN3aXRjaCAoZmllbGQudHlwZSkge1xuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMucmVhbDpcbiAgICAgIGNhc2UgQUxMX0ZJRUxEX1RZUEVTLmludGVnZXI6XG4gICAgICAgIC8vIGNhbGN1bGF0ZSBkb21haW4gYW5kIHN0ZXBcbiAgICAgICAgcmV0dXJuIGdldE51bWVyaWNGaWVsZERvbWFpbihkYXRhQ29udGFpbmVyLCB2YWx1ZUFjY2Vzc29yKTtcblxuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMuYm9vbGVhbjpcbiAgICAgICAgcmV0dXJuIHtkb21haW46IFt0cnVlLCBmYWxzZV19O1xuXG4gICAgICBjYXNlIEFMTF9GSUVMRF9UWVBFUy5zdHJpbmc6XG4gICAgICBjYXNlIEFMTF9GSUVMRF9UWVBFUy5oMzpcbiAgICAgIGNhc2UgQUxMX0ZJRUxEX1RZUEVTLmRhdGU6XG4gICAgICAgIGRvbWFpbiA9IGdldE9yZGluYWxEb21haW4oZGF0YUNvbnRhaW5lciwgdmFsdWVBY2Nlc3Nvcik7XG4gICAgICAgIHJldHVybiB7ZG9tYWlufTtcblxuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMudGltZXN0YW1wOlxuICAgICAgICByZXR1cm4gZ2V0VGltZXN0YW1wRmllbGREb21haW4oZGF0YUNvbnRhaW5lciwgdmFsdWVBY2Nlc3Nvcik7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiB7ZG9tYWluOiBnZXRPcmRpbmFsRG9tYWluKGRhdGFDb250YWluZXIsIHZhbHVlQWNjZXNzb3IpfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogIEdldCB0aGUgZG9tYWluIG9mIHRoaXMgY29sdW1uIGJhc2VkIG9uIHNjYWxlIHR5cGVcbiAgICovXG4gIGdldENvbHVtbkxheWVyRG9tYWluKGZpZWxkOiBGLCBzY2FsZVR5cGU6IHN0cmluZyk6IG51bWJlcltdIHwgc3RyaW5nW10gfCBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCB7XG4gICAgY29uc3Qge2RhdGFDb250YWluZXIsIGZpbHRlcmVkSW5kZXhGb3JEb21haW59ID0gdGhpcztcblxuICAgIGlmICghU0NBTEVfVFlQRVNbc2NhbGVUeXBlXSkge1xuICAgICAgQ29uc29sZS5lcnJvcihgc2NhbGUgdHlwZSAke3NjYWxlVHlwZX0gbm90IHN1cHBvcnRlZGApO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3Qge3ZhbHVlQWNjZXNzb3J9ID0gZmllbGQ7XG4gICAgY29uc3QgaW5kZXhWYWx1ZUFjY2Vzc29yID0gaSA9PiB2YWx1ZUFjY2Vzc29yKHtpbmRleDogaX0pO1xuICAgIGNvbnN0IHNvcnRGdW5jdGlvbiA9IGdldFNvcnRpbmdGdW5jdGlvbihmaWVsZC50eXBlKTtcblxuICAgIHN3aXRjaCAoc2NhbGVUeXBlKSB7XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLm9yZGluYWw6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLmN1c3RvbU9yZGluYWw6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnBvaW50OlxuICAgICAgICAvLyBkbyBub3QgcmVjYWxjdWxhdGUgb3JkaW5hbCBkb21haW4gYmFzZWQgb24gZmlsdGVyZWQgZGF0YVxuICAgICAgICAvLyBkb24ndCBuZWVkIHRvIHVwZGF0ZSBvcmRpbmFsIGRvbWFpbiBldmVyeSB0aW1lXG4gICAgICAgIHJldHVybiBnZXRPcmRpbmFsRG9tYWluKGRhdGFDb250YWluZXIsIHZhbHVlQWNjZXNzb3IpO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnF1YW50aWxlOlxuICAgICAgICByZXR1cm4gZ2V0UXVhbnRpbGVEb21haW4oZmlsdGVyZWRJbmRleEZvckRvbWFpbiwgaW5kZXhWYWx1ZUFjY2Vzc29yLCBzb3J0RnVuY3Rpb24pO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLmxvZzpcbiAgICAgICAgcmV0dXJuIGdldExvZ0RvbWFpbihmaWx0ZXJlZEluZGV4Rm9yRG9tYWluLCBpbmRleFZhbHVlQWNjZXNzb3IpO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnF1YW50aXplOlxuICAgICAgY2FzZSBTQ0FMRV9UWVBFUy5saW5lYXI6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnNxcnQ6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLmN1c3RvbTpcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiBnZXRMaW5lYXJEb21haW4oZmlsdGVyZWRJbmRleEZvckRvbWFpbiwgaW5kZXhWYWx1ZUFjY2Vzc29yKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2V0IGEgc2FtcGxlIG9mIHJvd3MgdG8gY2FsY3VsYXRlIGxheWVyIGJvdW5kYXJpZX