UNPKG

kepler.gl

Version:

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

648 lines (621 loc) 83.6 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, arrowTable: data.arrowTable, 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 _ref3, _data$arrowTable, _this$dataContainer$u, _this$dataContainer; var dataContainerData; return _regenerator["default"].wrap(function _callee2$(_context2) { while (1) switch (_context2.prev = _context2.next) { case 0: dataContainerData = (_ref3 = (_data$arrowTable = data.arrowTable) !== null && _data$arrowTable !== void 0 ? _data$arrowTable : data.cols) !== null && _ref3 !== void 0 ? _ref3 : 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,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfY29uc29sZSIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJyZXF1aXJlIiwiX2QzQXJyYXkiLCJfY29uc3RhbnRzIiwiX2dwdUZpbHRlclV0aWxzIiwiX3V0aWxzIiwiX2NvbW1vblV0aWxzIiwiX2NyZWF0ZUZvck9mSXRlcmF0b3JIZWxwZXIiLCJyIiwiZSIsInQiLCJTeW1ib2wiLCJpdGVyYXRvciIsIkFycmF5IiwiaXNBcnJheSIsIl91bnN1cHBvcnRlZEl0ZXJhYmxlVG9BcnJheSIsImxlbmd0aCIsIl9uIiwiRiIsInMiLCJuIiwiZG9uZSIsInZhbHVlIiwiZiIsIlR5cGVFcnJvciIsIm8iLCJhIiwidSIsImNhbGwiLCJuZXh0IiwiX2FycmF5TGlrZVRvQXJyYXkiLCJ0b1N0cmluZyIsInNsaWNlIiwiY29uc3RydWN0b3IiLCJuYW1lIiwiZnJvbSIsInRlc3QiLCJvd25LZXlzIiwiT2JqZWN0Iiwia2V5cyIsImdldE93blByb3BlcnR5U3ltYm9scyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3JzIiwiZGVmaW5lUHJvcGVydGllcyIsImRlZmluZVByb3BlcnR5IiwiRklEX0tFWSIsIm1heWJlVG9EYXRlIiwiaXNUaW1lIiwiZmllbGRJZHgiLCJmb3JtYXQiLCJkYyIsImQiLCJ0aW1lVG9Vbml4TWlsbGkiLCJ2YWx1ZUF0IiwiaW5kZXgiLCJLZXBsZXJUYWJsZSIsIl9yZWYiLCJpbmZvIiwiY29sb3IiLCJtZXRhZGF0YSIsIl9yZWYkc3VwcG9ydGVkRmlsdGVyVCIsInN1cHBvcnRlZEZpbHRlclR5cGVzIiwiX3JlZiRkaXNhYmxlRGF0YU9wZXJhIiwiZGlzYWJsZURhdGFPcGVyYXRpb24iLCJfY2xhc3NDYWxsQ2hlY2syIiwiZGF0YXNldEluZm8iLCJpZCIsImdlbmVyYXRlSGFzaElkIiwibGFiZWwiLCJ0eXBlIiwiZGVmYXVsdE1ldGFkYXRhIiwiZGF0YUNvbnRhaW5lciIsImNyZWF0ZURhdGFDb250YWluZXIiLCJncHVGaWx0ZXIiLCJnZXRHcHVGaWx0ZXJQcm9wcyIsInVuZGVmaW5lZCIsIl9jcmVhdGVDbGFzczIiLCJrZXkiLCJfaW1wb3J0RGF0YSIsIl9hc3luY1RvR2VuZXJhdG9yMiIsIl9yZWdlbmVyYXRvciIsIm1hcmsiLCJfY2FsbGVlIiwiX3JlZjIiLCJkYXRhIiwiZGF0YUNvbnRhaW5lckRhdGEiLCJpbnB1dERhdGFGb3JtYXQiLCJmaWVsZHMiLCJhbGxJbmRleGVzIiwid3JhcCIsIl9jYWxsZWUkIiwiX2NvbnRleHQiLCJwcmV2IiwiY29scyIsInJvd3MiLCJEYXRhRm9ybSIsIkNPTFNfQVJSQVkiLCJST1dTX0FSUkFZIiwiYXJyb3dUYWJsZSIsIm1hcCIsImkiLCJkaXNwbGF5TmFtZSIsImFuYWx5emVyVHlwZSIsIkFMTF9GSUVMRF9UWVBFUyIsInN0cmluZyIsInZhbHVlQWNjZXNzb3IiLCJnZXRGaWVsZFZhbHVlQWNjZXNzb3IiLCJnZXRQbGFpbkluZGV4IiwiZmlsdGVyZWRJbmRleCIsImZpbHRlcmVkSW5kZXhGb3JEb21haW4iLCJmaWVsZFBhaXJzIiwiZmluZFBvaW50RmllbGRQYWlycyIsInN0b3AiLCJpbXBvcnREYXRhIiwiX3giLCJfdXBkYXRlIiwiX2NhbGxlZTIiLCJfcmVmMyIsIl9kYXRhJGFycm93VGFibGUiLCJfdGhpcyRkYXRhQ29udGFpbmVyJHUiLCJfdGhpcyRkYXRhQ29udGFpbmVyIiwiX2NhbGxlZTIkIiwiX2NvbnRleHQyIiwidXBkYXRlIiwiYWJydXB0IiwiX3gyIiwiZ2V0IiwibnVtUm93cyIsImdldENvbHVtbkZpZWxkIiwiY29sdW1uTmFtZSIsImZpZWxkIiwiZmluZCIsImZkIiwiX2Fzc2V0RmllbGQiLCJnZXRDb2x1bW5GaWVsZElkeCIsImZpbmRJbmRleCIsIkJvb2xlYW4iLCJnZXRDb2x1bW5EaXNwbGF5Rm9ybWF0IiwiZGlzcGxheUZvcm1hdCIsImdldFZhbHVlIiwicm93SWR4IiwidXBkYXRlQ29sdW1uRmllbGQiLCJuZXdGaWVsZCIsImFzc2lnbiIsIl90b0NvbnN1bWFibGVBcnJheTIiLCJ1cGRhdGVUYWJsZUNvbG9yIiwibmV3Q29sb3IiLCJnZXRDb2x1bW5GaWx0ZXJQcm9wcyIsInByb3RvdHlwZSIsImhhc093blByb3BlcnR5IiwiZmlsdGVyUHJvcHMiLCJmaWVsZERvbWFpbiIsImdldENvbHVtbkZpbHRlckRvbWFpbiIsImdldEZpbHRlclByb3BzIiwiZmlsdGVyVGFibGUiLCJmaWx0ZXJzIiwibGF5ZXJzIiwib3B0IiwiX3RoaXMiLCJkYXRhSWQiLCJvbGRGaWx0ZXJSZWNvcmQiLCJmaWx0ZXJSZWNvcmQiLCJnZXRGaWx0ZXJSZWNvcmQiLCJjaGFuZ2VkRmlsdGVycyIsImRpZmZGaWx0ZXJzIiwic2hvdWxkQ2FsRG9tYWluIiwiZHluYW1pY0RvbWFpbiIsInNob3VsZENhbEluZGV4IiwiY3B1IiwiZmlsdGVyUmVzdWx0IiwiZHluYW1pY0RvbWFpbkZpbHRlcnMiLCJjcHVGaWx0ZXJzIiwiZmlsdGVyRnVuY3MiLCJyZWR1Y2UiLCJhY2MiLCJmaWVsZEluZGV4IiwiZ2V0RGF0YXNldEZpZWxkSW5kZXhGb3JGaWx0ZXIiLCJnZXRGaWx0ZXJGdW5jdGlvbiIsImZpbHRlckRhdGFCeUZpbHRlclR5cGVzIiwiZmlsdGVyVGFibGVDUFUiLCJjcHVPbmx5IiwiaWdub3JlRG9tYWluIiwiZmlsdGVyZWRJZHhDUFUiLCJmaWx0ZXJSZWNvcmRDUFUiLCJncHUiLCJjb3BpZWQiLCJjb3B5VGFibGUiLCJmaWx0ZXJlZCIsImRvbWFpbiIsInJlYWwiLCJpbnRlZ2VyIiwiZ2V0TnVtZXJpY0ZpZWxkRG9tYWluIiwiaDMiLCJkYXRlIiwiZ2V0T3JkaW5hbERvbWFpbiIsInRpbWVzdGFtcCIsImdldFRpbWVzdGFtcEZpZWxkRG9tYWluIiwiZ2V0Q29sdW1uTGF5ZXJEb21haW4iLCJzY2FsZVR5cGUiLCJTQ0FMRV9UWVBFUyIsIkNvbnNvbGUiLCJlcnJvciIsImNvbmNhdCIsImluZGV4VmFsdWVBY2Nlc3NvciIsInNvcnRGdW5jdGlvbiIsImdldFNvcnRpbmdGdW5jdGlvbiIsIm9yZGluYWwiLCJjdXN0b21PcmRpbmFsIiwicG9pbnQiLCJxdWFudGlsZSIsImdldFF1YW50aWxlRG9tYWluIiwibG9nIiwiZ2V0TG9nRG9tYWluIiwicXVhbnRpemUiLCJsaW5lYXIiLCJzcXJ0IiwiY3VzdG9tIiwiZ2V0TGluZWFyRG9tYWluIiwiZmllbGROYW1lIiwiY29uZGl0aW9uIiwic3BlY2lhbENoYXJhY3RlclNldCIsImZvdW5kTWF0Y2hpbmdGaWVsZHMiLCJyZSIsInN1ZmZpeFBhaXIiLCJhbGxOYW1lcyIsInBhcnRuZXJJZHgiLCJyZXBsYWNlIiwibWF0Y2giLCJhbHRJZHgiLCJBTFRJVFVERV9GSUVMRFMiLCJzb21lIiwiYWx0IiwidG9Mb3dlckNhc2UiLCJjYXJyeSIsImlkeCIsIl9pdGVyYXRvciIsIlRSSVBfUE9JTlRfRklFTERTIiwiX3N0ZXAiLCJSZWdFeHAiLCJfZm91bmRNYXRjaGluZ0ZpZWxkcyIsInRyaW1OYW1lIiwidHJpbSIsImRlZmF1bHROYW1lIiwicGFpciIsImxhdCIsImxuZyIsImFsdGl0dWRlIiwic3VmZml4IiwiZXJyIiwic29ydERhdGFzZXRCeUNvbHVtbiIsImRhdGFzZXQiLCJjb2x1bW4iLCJtb2RlIiwic29ydEJ5IiwiU09SVF9PUkRFUiIsIkFTQ0VORElORyIsIlVOU09SVCIsInNvcnRDb2x1bW4iLCJzb3J0T3JkZXIiLCJhc2NlbmRpbmciLCJkZXNjZW5kaW5nIiwic29ydCIsImIiLCJ2YWx1ZTEiLCJ2YWx1ZTIiLCJub3ROdWxsb3JVbmRlZmluZWQiLCJwaW5UYWJsZUNvbHVtbnMiLCJwaW5uZWRDb2x1bW5zIiwiaW5jbHVkZXMiLCJjbyIsImNvcHlUYWJsZUFuZFVwZGF0ZSIsIm9yaWdpbmFsIiwiY3JlYXRlIiwiZ2V0UHJvdG90eXBlT2YiLCJvcHRpb25zIiwiZW50cmllcyIsImVudHJ5IiwiYmluZCIsIl9kZWZhdWx0IiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uL3NyYy9rZXBsZXItdGFibGUudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IE1JVFxuLy8gQ29weXJpZ2h0IGNvbnRyaWJ1dG9ycyB0byB0aGUga2VwbGVyLmdsIHByb2plY3RcblxuaW1wb3J0IENvbnNvbGUgZnJvbSAnZ2xvYmFsL2NvbnNvbGUnO1xuaW1wb3J0IHthc2NlbmRpbmcsIGRlc2NlbmRpbmd9IGZyb20gJ2QzLWFycmF5JztcblxuaW1wb3J0IHtcbiAgVFJJUF9QT0lOVF9GSUVMRFMsXG4gIFNPUlRfT1JERVIsXG4gIEFMTF9GSUVMRF9UWVBFUyxcbiAgQUxUSVRVREVfRklFTERTLFxuICBTQ0FMRV9UWVBFU1xufSBmcm9tICdAa2VwbGVyLmdsL2NvbnN0YW50cyc7XG5pbXBvcnQge1xuICBSR0JDb2xvcixcbiAgRmllbGQsXG4gIEZpZWxkUGFpcixcbiAgRmllbGREb21haW4sXG4gIEZpbHRlcixcbiAgUHJvdG9EYXRhc2V0LFxuICBGaWx0ZXJSZWNvcmQsXG4gIEZpbHRlckRhdGFzZXRPcHQsXG4gIFJhbmdlRmllbGREb21haW4sXG4gIFNlbGVjdEZpZWxkRG9tYWluLFxuICBNdWx0aVNlbGVjdEZpZWxkRG9tYWluLFxuICBUaW1lUmFuZ2VGaWVsZERvbWFpblxufSBmcm9tICdAa2VwbGVyLmdsL3R5cGVzJztcblxuaW1wb3J0IHtnZXRHcHVGaWx0ZXJQcm9wcywgZ2V0RGF0YXNldEZpZWxkSW5kZXhGb3JGaWx0ZXJ9IGZyb20gJy4vZ3B1LWZpbHRlci11dGlscyc7XG5cbmltcG9ydCB7XG4gIGdldFNvcnRpbmdGdW5jdGlvbixcbiAgdGltZVRvVW5peE1pbGxpLFxuICBjcmVhdGVEYXRhQ29udGFpbmVyLFxuICBEYXRhRm9ybSxcbiAgZGlmZkZpbHRlcnMsXG4gIGZpbHRlckRhdGFCeUZpbHRlclR5cGVzLFxuICBGaWx0ZXJSZXN1bHQsXG4gIGdldEZpbHRlckZ1bmN0aW9uLFxuICBnZXRGaWx0ZXJQcm9wcyxcbiAgZ2V0RmlsdGVyUmVjb3JkLFxuICBnZXROdW1lcmljRmllbGREb21haW4sXG4gIGdldFRpbWVzdGFtcEZpZWxkRG9tYWluLFxuICBnZXRMaW5lYXJEb21haW4sXG4gIGdldExvZ0RvbWFpbixcbiAgZ2V0T3JkaW5hbERvbWFpbixcbiAgZ2V0UXVhbnRpbGVEb21haW4sXG4gIERhdGFDb250YWluZXJJbnRlcmZhY2UsXG4gIEZpbHRlckNoYW5nZWRcbn0gZnJvbSAnQGtlcGxlci5nbC91dGlscyc7XG5pbXBvcnQge2dlbmVyYXRlSGFzaElkLCBub3ROdWxsb3JVbmRlZmluZWR9IGZyb20gJ0BrZXBsZXIuZ2wvY29tbW9uLXV0aWxzJztcblxuLy8gVE9ETyBpc29sYXRlIGxheWVyIHR5cGUsIGRlcGVuZHMgb24gQGtlcGxlci5nbC9sYXllcnNcbnR5cGUgTGF5ZXIgPSBhbnk7XG5cbmV4cG9ydCB0eXBlIEdwdUZpbHRlciA9IHtcbiAgZmlsdGVyUmFuZ2U6IG51bWJlcltdW107XG4gIGZpbHRlclZhbHVlVXBkYXRlVHJpZ2dlcnM6IHtcbiAgICBbaWQ6IHN0cmluZ106IHtuYW1lOiBzdHJpbmc7IGRvbWFpbjA6IG51bWJlcn0gfCBudWxsO1xuICB9O1xuICBmaWx0ZXJWYWx1ZUFjY2Vzc29yOiAoXG4gICAgZGM6IERhdGFDb250YWluZXJJbnRlcmZhY2VcbiAgKSA9PiAoXG4gICAgZ2V0SW5kZXg/OiAoYW55KSA9PiBudW1iZXIsXG4gICAgZ2V0RGF0YT86IChkY186IERhdGFDb250YWluZXJJbnRlcmZhY2UsIGQ6IGFueSwgZmllbGRJbmRleDogbnVtYmVyKSA9PiBhbnlcbiAgKSA9PiAoZDogYW55LCBvYmplY3RJbmZvPzoge2luZGV4OiBudW1iZXJ9KSA9PiAobnVtYmVyIHwgbnVtYmVyW10pW107XG59O1xuXG5leHBvcnQgdHlwZSBGaWx0ZXJQcm9wcyA9XG4gIHwgTnVtZXJpY0ZpZWxkRmlsdGVyUHJvcHNcbiAgfCBCb29sZWFuRmllbGRGaWx0ZXJQcm9wc1xuICB8IFN0cmluZ0ZpZWxkRmlsdGVyUHJvcHNcbiAgfCBUaW1lRmllbGRGaWx0ZXJQcm9wcztcblxuZXhwb3J0IHR5cGUgTnVtZXJpY0ZpZWxkRmlsdGVyUHJvcHMgPSBSYW5nZUZpZWxkRG9tYWluICYge1xuICB2YWx1ZTogW251bWJlciwgbnVtYmVyXTtcbiAgdHlwZTogc3RyaW5nO1xuICB0eXBlT3B0aW9uczogc3RyaW5nW107XG4gIGdwdTogYm9vbGVhbjtcbiAgY29sdW1uU3RhdHM/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xufTtcbmV4cG9ydCB0eXBlIEJvb2xlYW5GaWVsZEZpbHRlclByb3BzID0gU2VsZWN0RmllbGREb21haW4gJiB7XG4gIHR5cGU6IHN0cmluZztcbiAgdmFsdWU6IGJvb2xlYW47XG4gIGdwdTogYm9vbGVhbjtcbiAgY29sdW1uU3RhdHM/OiBSZWNvcmQ8c3RyaW5nLCBhbnk+O1xufTtcbmV4cG9ydCB0eXBlIFN0cmluZ0ZpZWxkRmlsdGVyUHJvcHMgPSBNdWx0aVNlbGVjdEZpZWxkRG9tYWluICYge1xuICB0eXBlOiBzdHJpbmc7XG4gIHZhbHVlOiBzdHJpbmdbXTtcbiAgZ3B1OiBib29sZWFuO1xuICBjb2x1bW5TdGF0cz86IFJlY29yZDxzdHJpbmcsIGFueT47XG59O1xuZXhwb3J0IHR5cGUgVGltZUZpZWxkRmlsdGVyUHJvcHMgPSBUaW1lUmFuZ2VGaWVsZERvbWFpbiAmIHtcbiAgdHlwZTogc3RyaW5nO1xuICB2aWV3OiBGaWx0ZXJbJ3ZpZXcnXTtcbiAgZml4ZWREb21haW46IGJvb2xlYW47XG4gIHZhbHVlOiBudW1iZXJbXTtcbiAgZ3B1OiBib29sZWFuO1xuICBjb2x1bW5TdGF0cz86IFJlY29yZDxzdHJpbmcsIGFueT47XG59O1xuXG4vLyBVbmlxdWUgaWRlbnRpZmllciBvZiBlYWNoIGZpZWxkXG5jb25zdCBGSURfS0VZID0gJ25hbWUnO1xuXG5leHBvcnQgZnVuY3Rpb24gbWF5YmVUb0RhdGUoXG4gIGlzVGltZTogYm9vbGVhbixcbiAgZmllbGRJZHg6IG51bWJlcixcbiAgZm9ybWF0OiBzdHJpbmcsXG4gIGRjOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlLFxuICAvLyBBbiBvYmplY3Qgd2l0aCByb3cgaW5kZXggb3IgYSBtYXRlcmlhbGl6ZWQgcm93IGFycmF5IChmb3IgbWF0ZXJpYWxpemVkIGhvdmVyIGluZm8gZnJvbSB0cmlwIGxheWVyKVxuICBkOiB7aW5kZXg6IG51bWJlcn0gfCBhbnlbXVxuKSB7XG4gIGlmIChpc1RpbWUpIHtcbiAgICByZXR1cm4gdGltZVRvVW5peE1pbGxpKEFycmF5LmlzQXJyYXkoZCkgPyBkW2ZpZWxkSWR4XSA6IGRjLnZhbHVlQXQoZC5pbmRleCwgZmllbGRJZHgpLCBmb3JtYXQpO1xuICB9XG5cbiAgcmV0dXJuIEFycmF5LmlzQXJyYXkoZCkgPyBkW2ZpZWxkSWR4XSA6IGRjLnZhbHVlQXQoZC5pbmRleCwgZmllbGRJZHgpO1xufVxuXG5jbGFzcyBLZXBsZXJUYWJsZTxGIGV4dGVuZHMgRmllbGQgPSBGaWVsZD4ge1xuICByZWFkb25seSBpZDogc3RyaW5nO1xuXG4gIHR5cGU/OiBzdHJpbmc7XG4gIGxhYmVsOiBzdHJpbmc7XG4gIGNvbG9yOiBSR0JDb2xvcjtcblxuICAvLyBmaWVsZHMgYW5kIGRhdGFcbiAgZmllbGRzOiBGW10gPSBbXTtcblxuICBkYXRhQ29udGFpbmVyOiBEYXRhQ29udGFpbmVySW50ZXJmYWNlO1xuXG4gIGFsbEluZGV4ZXM6IG51bWJlcltdID0gW107XG4gIGZpbHRlcmVkSW5kZXg6IG51bWJlcltdID0gW107XG4gIGZpbHRlcmVkSWR4Q1BVPzogbnVtYmVyW107XG4gIGZpbHRlcmVkSW5kZXhGb3JEb21haW46IG51bWJlcltdID0gW107XG4gIGZpZWxkUGFpcnM6IEZpZWxkUGFpcltdID0gW107XG4gIGdwdUZpbHRlcjogR3B1RmlsdGVyO1xuICBmaWx0ZXJSZWNvcmQ/OiBGaWx0ZXJSZWNvcmQ7XG4gIGZpbHRlclJlY29yZENQVT86IEZpbHRlclJlY29yZDtcbiAgY2hhbmdlZEZpbHRlcnM/OiBGaWx0ZXJDaGFuZ2VkO1xuXG4gIC8vIHRhYmxlLWluamVjdGVkIG1ldGFkYXRhXG4gIHNvcnRDb2x1bW4/OiB7XG4gICAgLy8gY29sdW1uIG5hbWU6IHNvcnRlZCBpZHhcbiAgICBba2V5OiBzdHJpbmddOiBzdHJpbmc7IC8vIEFTQ0VORElORyB8IERFU0NFTkRJTkcgfCBVTlNPUlRcbiAgfTtcbiAgc29ydE9yZGVyPzogbnVtYmVyW10gfCBudWxsO1xuXG4gIHBpbm5lZENvbHVtbnM/OiBzdHJpbmdbXTtcbiAgc3VwcG9ydGVkRmlsdGVyVHlwZXM/OiBzdHJpbmdbXSB8IG51bGw7XG4gIGRpc2FibGVEYXRhT3BlcmF0aW9uPzogYm9vbGVhbjtcblxuICAvLyB0YWJsZS1pbmplY3RlZCBtZXRhZGF0YVxuICBtZXRhZGF0YTogUmVjb3JkPHN0cmluZywgYW55PjtcblxuICBnZXRGaWxlUHJvY2Vzc29yPzogKGRhdGE6IGFueSwgaW5wdXRGb3JtYXQ/OiBzdHJpbmcpID0+IHtkYXRhOiBhbnk7IGZvcm1hdDogc3RyaW5nfTtcblxuICBjb25zdHJ1Y3Rvcih7XG4gICAgaW5mbyxcbiAgICBjb2xvcixcbiAgICBtZXRhZGF0YSxcbiAgICBzdXBwb3J0ZWRGaWx0ZXJUeXBlcyA9IG51bGwsXG4gICAgZGlzYWJsZURhdGFPcGVyYXRpb24gPSBmYWxzZVxuICB9OiB7XG4gICAgaW5mbz86IFByb3RvRGF0YXNldFsnaW5mbyddO1xuICAgIGNvbG9yOiBSR0JDb2xvcjtcbiAgICBtZXRhZGF0YT86IFByb3RvRGF0YXNldFsnbWV0YWRhdGEnXTtcbiAgICBzdXBwb3J0ZWRGaWx0ZXJUeXBlcz86IFByb3RvRGF0YXNldFsnc3VwcG9ydGVkRmlsdGVyVHlwZXMnXTtcbiAgICBkaXNhYmxlRGF0YU9wZXJhdGlvbj86IFByb3RvRGF0YXNldFsnZGlzYWJsZURhdGFPcGVyYXRpb24nXTtcbiAgfSkge1xuICAgIC8vIFRPRE8gLSB3aGF0IHRvIGRvIGlmIHZhbGlkYXRpb24gZmFpbHM/IENhbiBrZXBsZXIgaGFuZGxlIGV4Y2VwdGlvbnM/XG4gICAgLy8gY29uc3QgdmFsaWRhdGVkRGF0YSA9IHZhbGlkYXRlSW5wdXREYXRhKGRhdGEpO1xuICAgIC8vIGlmICghdmFsaWRhdGVkRGF0YSkge1xuICAgIC8vICAgcmV0dXJuIHRoaXM7XG4gICAgLy8gfVxuXG4gICAgY29uc3QgZGF0YXNldEluZm8gPSB7XG4gICAgICBpZDogZ2VuZXJhdGVIYXNoSWQoNCksXG4gICAgICBsYWJlbDogJ25ldyBkYXRhc2V0JyxcbiAgICAgIHR5cGU6ICcnLFxuICAgICAgLi4uaW5mb1xuICAgIH07XG5cbiAgICBjb25zdCBkZWZhdWx0TWV0YWRhdGEgPSB7XG4gICAgICBpZDogZGF0YXNldEluZm8uaWQsXG4gICAgICAvLyBAdHMtaWdub3JlXG4gICAgICBmb3JtYXQ6IGRhdGFzZXRJbmZvLmZvcm1hdCB8fCAnJyxcbiAgICAgIGxhYmVsOiBkYXRhc2V0SW5mby5sYWJlbCB8fCAnJ1xuICAgIH07XG5cbiAgICB0aGlzLmlkID0gZGF0YXNldEluZm8uaWQ7XG4gICAgdGhpcy50eXBlID0gZGF0YXNldEluZm8udHlwZTtcbiAgICB0aGlzLmxhYmVsID0gZGF0YXNldEluZm8ubGFiZWw7XG4gICAgdGhpcy5jb2xvciA9IGNvbG9yO1xuICAgIHRoaXMubWV0YWRhdGEgPSB7XG4gICAgICAuLi5kZWZhdWx0TWV0YWRhdGEsXG4gICAgICAuLi5tZXRhZGF0YVxuICAgIH07XG5cbiAgICB0aGlzLnN1cHBvcnRlZEZpbHRlclR5cGVzID0gc3VwcG9ydGVkRmlsdGVyVHlwZXM7XG4gICAgdGhpcy5kaXNhYmxlRGF0YU9wZXJhdGlvbiA9IGRpc2FibGVEYXRhT3BlcmF0aW9uO1xuXG4gICAgdGhpcy5kYXRhQ29udGFpbmVyID0gY3JlYXRlRGF0YUNvbnRhaW5lcihbXSk7XG4gICAgdGhpcy5ncHVGaWx0ZXIgPSBnZXRHcHVGaWx0ZXJQcm9wcyhbXSwgdGhpcy5pZCwgW10sIHVuZGVmaW5lZCk7XG4gIH1cblxuICBhc3luYyBpbXBvcnREYXRhKHtkYXRhfToge2RhdGE6IFByb3RvRGF0YXNldFsnZGF0YSddfSkge1xuICAgIGNvbnN0IGRhdGFDb250YWluZXJEYXRhID0gZGF0YS5jb2xzID8gZGF0YS5jb2xzIDogZGF0YS5yb3dzO1xuICAgIGNvbnN0IGlucHV0RGF0YUZvcm1hdCA9IGRhdGEuY29scyA/IERhdGFGb3JtLkNPTFNfQVJSQVkgOiBEYXRhRm9ybS5ST1dTX0FSUkFZO1xuXG4gICAgY29uc3QgZGF0YUNvbnRhaW5lciA9IGNyZWF0ZURhdGFDb250YWluZXIoZGF0YUNvbnRhaW5lckRhdGEsIHtcbiAgICAgIGZpZWxkczogZGF0YS5maWVsZHMsXG4gICAgICBhcnJvd1RhYmxlOiBkYXRhLmFycm93VGFibGUsXG4gICAgICBpbnB1dERhdGFGb3JtYXRcbiAgICB9KTtcblxuICAgIGNvbnN0IGZpZWxkczogRmllbGRbXSA9IGRhdGEuZmllbGRzLm1hcCgoZiwgaSkgPT4gKHtcbiAgICAgIC4uLmYsXG4gICAgICBmaWVsZElkeDogaSxcbiAgICAgIGlkOiBmLm5hbWUsXG4gICAgICBkaXNwbGF5TmFtZTogZi5kaXNwbGF5TmFtZSB8fCBmLm5hbWUsXG4gICAgICBhbmFseXplclR5cGU6IGYuYW5hbHl6ZXJUeXBlIHx8IEFMTF9GSUVMRF9UWVBFUy5zdHJpbmcsXG4gICAgICBmb3JtYXQ6IGYuZm9ybWF0IHx8ICcnLFxuICAgICAgdmFsdWVBY2Nlc3NvcjogZ2V0RmllbGRWYWx1ZUFjY2Vzc29yKGYsIGksIGRhdGFDb250YWluZXIpXG4gICAgfSkpO1xuXG4gICAgY29uc3QgYWxsSW5kZXhlcyA9IGRhdGFDb250YWluZXIuZ2V0UGxhaW5JbmRleCgpO1xuICAgIHRoaXMuZGF0YUNvbnRhaW5lciA9IGRhdGFDb250YWluZXI7XG4gICAgdGhpcy5hbGxJbmRleGVzID0gYWxsSW5kZXhlcztcbiAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSBhbGxJbmRleGVzO1xuICAgIHRoaXMuZmlsdGVyZWRJbmRleEZvckRvbWFpbiA9IGFsbEluZGV4ZXM7XG4gICAgdGhpcy5maWVsZFBhaXJzID0gZmluZFBvaW50RmllbGRQYWlycyhmaWVsZHMpO1xuICAgIC8vIEB0cy1leHBlY3QtZXJyb3IgTWFrZSBzdXJlIHRoYXQgZmllbGRzIHNhdGlzZmllcyBGIGV4dGVuZHMgRmllbGRcbiAgICB0aGlzLmZpZWxkcyA9IGZpZWxkcztcbiAgICB0aGlzLmdwdUZpbHRlciA9IGdldEdwdUZpbHRlclByb3BzKFtdLCB0aGlzLmlkLCBmaWVsZHMsIHVuZGVmaW5lZCk7XG4gIH1cblxuICAvKipcbiAgICogdXBkYXRlIHRhYmxlIHdpdGggbmV3IGRhdGFcbiAgICogQHBhcmFtIGRhdGEgLSBuZXcgZGF0YSBlLmcuIHRoZSBhcnJvdyBkYXRhIHdpdGggbmV3IGJhdGNoZXMgbG9hZGVkXG4gICAqL1xuICBhc3luYyB1cGRhdGUoZGF0YTogUHJvdG9EYXRhc2V0WydkYXRhJ10pIHtcbiAgICBjb25zdCBkYXRhQ29udGFpbmVyRGF0YSA9IGRhdGEuYXJyb3dUYWJsZSA/PyBkYXRhLmNvbHMgPz8gZGF0YS5yb3dzO1xuICAgIHRoaXMuZGF0YUNvbnRhaW5lci51cGRhdGU/LihkYXRhQ29udGFpbmVyRGF0YSk7XG4gICAgdGhpcy5hbGxJbmRleGVzID0gdGhpcy5kYXRhQ29udGFpbmVyLmdldFBsYWluSW5kZXgoKTtcbiAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSB0aGlzLmFsbEluZGV4ZXM7XG4gICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID0gdGhpcy5hbGxJbmRleGVzO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICBnZXQgbGVuZ3RoKCkge1xuICAgIHJldHVybiB0aGlzLmRhdGFDb250YWluZXIubnVtUm93cygpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdldCBmaWVsZFxuICAgKiBAcGFyYW0gY29sdW1uTmFtZVxuICAgKi9cbiAgZ2V0Q29sdW1uRmllbGQoY29sdW1uTmFtZTogc3RyaW5nKTogRiB8IHVuZGVmaW5lZCB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmZpZWxkcy5maW5kKGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIGZpZWxkKTtcbiAgICByZXR1cm4gZmllbGQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IGZpZWxkSWR4XG4gICAqIEBwYXJhbSBjb2x1bW5OYW1lXG4gICAqL1xuICBnZXRDb2x1bW5GaWVsZElkeChjb2x1bW5OYW1lOiBzdHJpbmcpOiBudW1iZXIge1xuICAgIGNvbnN0IGZpZWxkSWR4ID0gdGhpcy5maWVsZHMuZmluZEluZGV4KGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIEJvb2xlYW4oZmllbGRJZHggPiAtMSkpO1xuICAgIHJldHVybiBmaWVsZElkeDtcbiAgfVxuXG4gIC8qKlxuICAgKiBHZXQgZGlzcGxheUZvcm1hdFxuICAgKiBAcGFyYW0gY29sdW1uTmFtZVxuICAgKi9cbiAgZ2V0Q29sdW1uRGlzcGxheUZvcm1hdChjb2x1bW5OYW1lKSB7XG4gICAgY29uc3QgZmllbGQgPSB0aGlzLmZpZWxkcy5maW5kKGZkID0+IGZkW0ZJRF9LRVldID09PSBjb2x1bW5OYW1lKTtcbiAgICB0aGlzLl9hc3NldEZpZWxkKGNvbHVtbk5hbWUsIGZpZWxkKTtcbiAgICByZXR1cm4gZmllbGQ/LmRpc3BsYXlGb3JtYXQ7XG4gIH1cblxuICAvKipcbiAgICogR2V0IHRoZSB2YWx1ZSBvZiBhIGNlbGxcbiAgICovXG4gIGdldFZhbHVlKGNvbHVtbk5hbWU6IHN0cmluZywgcm93SWR4OiBudW1iZXIpOiBhbnkge1xuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5nZXRDb2x1bW5GaWVsZChjb2x1bW5OYW1lKTtcbiAgICByZXR1cm4gZmllbGQgPyBmaWVsZC52YWx1ZUFjY2Vzc29yKHtpbmRleDogcm93SWR4fSkgOiBudWxsO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgZXhpc3RpbmcgZmllbGQgd2l0aCBhIG5ldyBvYmplY3RcbiAgICogQHBhcmFtIGZpZWxkSWR4XG4gICAqIEBwYXJhbSBuZXdGaWVsZFxuICAgKi9cbiAgdXBkYXRlQ29sdW1uRmllbGQoZmllbGRJZHg6IG51bWJlciwgbmV3RmllbGQ6IEYpOiB2b2lkIHtcbiAgICB0aGlzLmZpZWxkcyA9IE9iamVjdC5hc3NpZ24oWy4uLnRoaXMuZmllbGRzXSwge1tmaWVsZElkeF06IG5ld0ZpZWxkfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlIGRhdGFzZXQgY29sb3IgYnkgY3VzdG9tIGNvbG9yXG4gICAqIEBwYXJhbSBuZXdDb2xvclxuICAgKi9cbiAgdXBkYXRlVGFibGVDb2xvcihuZXdDb2xvcjogUkdCQ29sb3IpOiB2b2lkIHtcbiAgICB0aGlzLmNvbG9yID0gbmV3Q29sb3I7XG4gIH1cblxuICAvKipcbiAgICogU2F2ZSBmaWx0ZXJQcm9wcyB0byBmaWVsZCBhbmQgcmV0cmlldmUgaXRcbiAgICogQHBhcmFtIGNvbHVtbk5hbWVcbiAgICovXG4gIGdldENvbHVtbkZpbHRlclByb3BzKGNvbHVtbk5hbWU6IHN0cmluZyk6IEZbJ2ZpbHRlclByb3BzJ10gfCBudWxsIHwgdW5kZWZpbmVkIHtcbiAgICBjb25zdCBmaWVsZElkeCA9IHRoaXMuZ2V0Q29sdW1uRmllbGRJZHgoY29sdW1uTmFtZSk7XG4gICAgaWYgKGZpZWxkSWR4IDwgMCkge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuICAgIGNvbnN0IGZpZWxkID0gdGhpcy5maWVsZHNbZmllbGRJZHhdO1xuICAgIGlmIChPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwoZmllbGQsICdmaWx0ZXJQcm9wcycpKSB7XG4gICAgICByZXR1cm4gZmllbGQuZmlsdGVyUHJvcHM7XG4gICAgfVxuXG4gICAgY29uc3QgZmllbGREb21haW4gPSB0aGlzLmdldENvbHVtbkZpbHRlckRvbWFpbihmaWVsZCk7XG4gICAgaWYgKCFmaWVsZERvbWFpbikge1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3QgZmlsdGVyUHJvcHMgPSBnZXRGaWx0ZXJQcm9wcyhmaWVsZCwgZmllbGREb21haW4pO1xuICAgIGNvbnN0IG5ld0ZpZWxkID0ge1xuICAgICAgLi4uZmllbGQsXG4gICAgICBmaWx0ZXJQcm9wc1xuICAgIH07XG5cbiAgICB0aGlzLnVwZGF0ZUNvbHVtbkZpZWxkKGZpZWxkSWR4LCBuZXdGaWVsZCk7XG5cbiAgICByZXR1cm4gZmlsdGVyUHJvcHM7XG4gIH1cblxuICAvKipcbiAgICogQXBwbHkgZmlsdGVycyB0byBkYXRhc2V0LCByZXR1cm4gdGhlIGZpbHRlcmVkIGRhdGFzZXQgd2l0aCB1cGRhdGVkIGBncHVGaWx0ZXJgLCBgZmlsdGVyUmVjb3JkYCwgYGZpbHRlcmVkSW5kZXhgLCBgZmlsdGVyZWRJbmRleEZvckRvbWFpbmBcbiAgICogQHBhcmFtIGZpbHRlcnNcbiAgICogQHBhcmFtIGxheWVyc1xuICAgKiBAcGFyYW0gb3B0XG4gICAqL1xuICBmaWx0ZXJUYWJsZShmaWx0ZXJzOiBGaWx0ZXJbXSwgbGF5ZXJzOiBMYXllcltdLCBvcHQ/OiBGaWx0ZXJEYXRhc2V0T3B0KTogS2VwbGVyVGFibGU8RmllbGQ+IHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lciwgaWQ6IGRhdGFJZCwgZmlsdGVyUmVjb3JkOiBvbGRGaWx0ZXJSZWNvcmQsIGZpZWxkc30gPSB0aGlzO1xuXG4gICAgLy8gaWYgdGhlcmUgaXMgbm8gZmlsdGVyc1xuICAgIGNvbnN0IGZpbHRlclJlY29yZCA9IGdldEZpbHRlclJlY29yZChkYXRhSWQsIGZpbHRlcnMsIG9wdCB8fCB7fSk7XG5cbiAgICB0aGlzLmZpbHRlclJlY29yZCA9IGZpbHRlclJlY29yZDtcbiAgICB0aGlzLmdwdUZpbHRlciA9IGdldEdwdUZpbHRlclByb3BzKGZpbHRlcnMsIGRhdGFJZCwgZmllbGRzLCB0aGlzLmdwdUZpbHRlcik7XG5cbiAgICB0aGlzLmNoYW5nZWRGaWx0ZXJzID0gZGlmZkZpbHRlcnMoZmlsdGVyUmVjb3JkLCBvbGRGaWx0ZXJSZWNvcmQpO1xuXG4gICAgaWYgKCFmaWx0ZXJzLmxlbmd0aCkge1xuICAgICAgdGhpcy5maWx0ZXJlZEluZGV4ID0gdGhpcy5hbGxJbmRleGVzO1xuICAgICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID0gdGhpcy5hbGxJbmRleGVzO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLy8gZ2VuZXJhdGUgMiBzZXRzIG9mIGZpbHRlciByZXN1bHRcbiAgICAvLyBmaWx0ZXJlZEluZGV4IHVzZWQgdG8gY2FsY3VsYXRlIGxheWVyIGRhdGFcbiAgICAvLyBmaWx0ZXJlZEluZGV4Rm9yRG9tYWluIHVzZWQgdG8gY2FsY3VsYXRlIGxheWVyIERvbWFpblxuICAgIGNvbnN0IHNob3VsZENhbERvbWFpbiA9IEJvb2xlYW4odGhpcy5jaGFuZ2VkRmlsdGVycy5keW5hbWljRG9tYWluKTtcbiAgICBjb25zdCBzaG91bGRDYWxJbmRleCA9IEJvb2xlYW4odGhpcy5jaGFuZ2VkRmlsdGVycy5jcHUpO1xuXG4gICAgbGV0IGZpbHRlclJlc3VsdDogRmlsdGVyUmVzdWx0ID0ge307XG4gICAgaWYgKHNob3VsZENhbERvbWFpbiB8fCBzaG91bGRDYWxJbmRleCkge1xuICAgICAgY29uc3QgZHluYW1pY0RvbWFpbkZpbHRlcnMgPSBzaG91bGRDYWxEb21haW4gPyBmaWx0ZXJSZWNvcmQuZHluYW1pY0RvbWFpbiA6IG51bGw7XG4gICAgICBjb25zdCBjcHVGaWx0ZXJzID0gc2hvdWxkQ2FsSW5kZXggPyBmaWx0ZXJSZWNvcmQuY3B1IDogbnVsbDtcblxuICAgICAgY29uc3QgZmlsdGVyRnVuY3MgPSBmaWx0ZXJzLnJlZHVjZSgoYWNjLCBmaWx0ZXIpID0+IHtcbiAgICAgICAgY29uc3QgZmllbGRJbmRleCA9IGdldERhdGFzZXRGaWVsZEluZGV4Rm9yRmlsdGVyKHRoaXMuaWQsIGZpbHRlcik7XG4gICAgICAgIGNvbnN0IGZpZWxkID0gZmllbGRJbmRleCAhPT0gLTEgPyBmaWVsZHNbZmllbGRJbmRleF0gOiBudWxsO1xuXG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgLi4uYWNjLFxuICAgICAgICAgIFtmaWx0ZXIuaWRdOiBnZXRGaWx0ZXJGdW5jdGlvbihmaWVsZCwgdGhpcy5pZCwgZmlsdGVyLCBsYXllcnMsIGRhdGFDb250YWluZXIpXG4gICAgICAgIH07XG4gICAgICB9LCB7fSk7XG5cbiAgICAgIGZpbHRlclJlc3VsdCA9IGZpbHRlckRhdGFCeUZpbHRlclR5cGVzKFxuICAgICAgICB7ZHluYW1pY0RvbWFpbkZpbHRlcnMsIGNwdUZpbHRlcnMsIGZpbHRlckZ1bmNzfSxcbiAgICAgICAgZGF0YUNvbnRhaW5lclxuICAgICAgKTtcbiAgICB9XG5cbiAgICB0aGlzLmZpbHRlcmVkSW5kZXggPSBmaWx0ZXJSZXN1bHQuZmlsdGVyZWRJbmRleCB8fCB0aGlzLmZpbHRlcmVkSW5kZXg7XG4gICAgdGhpcy5maWx0ZXJlZEluZGV4Rm9yRG9tYWluID1cbiAgICAgIGZpbHRlclJlc3VsdC5maWx0ZXJlZEluZGV4Rm9yRG9tYWluIHx8IHRoaXMuZmlsdGVyZWRJbmRleEZvckRvbWFpbjtcblxuICAgIHJldHVybiB0aGlzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFwcGx5IGZpbHRlcnMgdG8gYSBkYXRhc2V0IGFsbCBvbiBDUFUsIGFzc2lnbiB0byBgZmlsdGVyZWRJZHhDUFVgLCBgZmlsdGVyUmVjb3JkQ1BVYFxuICAgKiBAcGFyYW0gZmlsdGVyc1xuICAgKiBAcGFyYW0gbGF5ZXJzXG4gICAqL1xuICBmaWx0ZXJUYWJsZUNQVShmaWx0ZXJzOiBGaWx0ZXJbXSwgbGF5ZXJzOiBMYXllcltdKTogS2VwbGVyVGFibGU8RmllbGQ+IHtcbiAgICBjb25zdCBvcHQgPSB7XG4gICAgICBjcHVPbmx5OiB0cnVlLFxuICAgICAgaWdub3JlRG9tYWluOiB0cnVlXG4gICAgfTtcblxuICAgIC8vIG5vIGZpbHRlclxuICAgIGlmICghZmlsdGVycy5sZW5ndGgpIHtcbiAgICAgIHRoaXMuZmlsdGVyZWRJZHhDUFUgPSB0aGlzLmFsbEluZGV4ZXM7XG4gICAgICB0aGlzLmZpbHRlclJlY29yZENQVSA9IGdldEZpbHRlclJlY29yZCh0aGlzLmlkLCBmaWx0ZXJzLCBvcHQpO1xuICAgICAgcmV0dXJuIHRoaXM7XG4gICAgfVxuXG4gICAgLy8gbm8gZ3B1IGZpbHRlclxuICAgIGlmICghZmlsdGVycy5maW5kKGYgPT4gZi5ncHUpKSB7XG4gICAgICB0aGlzLmZpbHRlcmVkSWR4Q1BVID0gdGhpcy5maWx0ZXJlZEluZGV4O1xuICAgICAgdGhpcy5maWx0ZXJSZWNvcmRDUFUgPSBnZXRGaWx0ZXJSZWNvcmQodGhpcy5pZCwgZmlsdGVycywgb3B0KTtcbiAgICAgIHJldHVybiB0aGlzO1xuICAgIH1cblxuICAgIC8vIG1ha2UgYSBjb3B5IGZvciBjcHUgZmlsdGVyaW5nXG4gICAgY29uc3QgY29waWVkID0gY29weVRhYmxlKHRoaXMpO1xuXG4gICAgY29waWVkLmZpbHRlclJlY29yZCA9IHRoaXMuZmlsdGVyUmVjb3JkQ1BVO1xuICAgIGNvcGllZC5maWx0ZXJlZEluZGV4ID0gdGhpcy5maWx0ZXJlZElkeENQVSB8fCBbXTtcblxuICAgIGNvbnN0IGZpbHRlcmVkID0gY29waWVkLmZpbHRlclRhYmxlKGZpbHRlcnMsIGxheWVycywgb3B0KTtcblxuICAgIHRoaXMuZmlsdGVyZWRJZHhDUFUgPSBmaWx0ZXJlZC5maWx0ZXJlZEluZGV4O1xuICAgIHRoaXMuZmlsdGVyUmVjb3JkQ1BVID0gZmlsdGVyZWQuZmlsdGVyUmVjb3JkO1xuXG4gICAgcmV0dXJuIHRoaXM7XG4gIH1cblxuICAvKipcbiAgICogQ2FsY3VsYXRlIGZpZWxkIGRvbWFpbiBiYXNlZCBvbiBmaWVsZCB0eXBlIGFuZCBkYXRhXG4gICAqIGZvciBGaWx0ZXJcbiAgICovXG4gIGdldENvbHVtbkZpbHRlckRvbWFpbihmaWVsZDogRik6IEZpZWxkRG9tYWluIHtcbiAgICBjb25zdCB7ZGF0YUNvbnRhaW5lcn0gPSB0aGlzO1xuICAgIGNvbnN0IHt2YWx1ZUFjY2Vzc29yfSA9IGZpZWxkO1xuXG4gICAgbGV0IGRvbWFpbjtcblxuICAgIHN3aXRjaCAoZmllbGQudHlwZSkge1xuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMucmVhbDpcbiAgICAgIGNhc2UgQUxMX0ZJRUxEX1RZUEVTLmludGVnZXI6XG4gICAgICAgIC8vIGNhbGN1bGF0ZSBkb21haW4gYW5kIHN0ZXBcbiAgICAgICAgcmV0dXJuIGdldE51bWVyaWNGaWVsZERvbWFpbihkYXRhQ29udGFpbmVyLCB2YWx1ZUFjY2Vzc29yKTtcblxuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMuYm9vbGVhbjpcbiAgICAgICAgcmV0dXJuIHtkb21haW46IFt0cnVlLCBmYWxzZV19O1xuXG4gICAgICBjYXNlIEFMTF9GSUVMRF9UWVBFUy5zdHJpbmc6XG4gICAgICBjYXNlIEFMTF9GSUVMRF9UWVBFUy5oMzpcbiAgICAgIGNhc2UgQUxMX0ZJRUxEX1RZUEVTLmRhdGU6XG4gICAgICAgIGRvbWFpbiA9IGdldE9yZGluYWxEb21haW4oZGF0YUNvbnRhaW5lciwgdmFsdWVBY2Nlc3Nvcik7XG4gICAgICAgIHJldHVybiB7ZG9tYWlufTtcblxuICAgICAgY2FzZSBBTExfRklFTERfVFlQRVMudGltZXN0YW1wOlxuICAgICAgICByZXR1cm4gZ2V0VGltZXN0YW1wRmllbGREb21haW4oZGF0YUNvbnRhaW5lciwgdmFsdWVBY2Nlc3Nvcik7XG5cbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIHJldHVybiB7ZG9tYWluOiBnZXRPcmRpbmFsRG9tYWluKGRhdGFDb250YWluZXIsIHZhbHVlQWNjZXNzb3IpfTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogIEdldCB0aGUgZG9tYWluIG9mIHRoaXMgY29sdW1uIGJhc2VkIG9uIHNjYWxlIHR5cGVcbiAgICovXG4gIGdldENvbHVtbkxheWVyRG9tYWluKGZpZWxkOiBGLCBzY2FsZVR5cGU6IHN0cmluZyk6IG51bWJlcltdIHwgc3RyaW5nW10gfCBbbnVtYmVyLCBudW1iZXJdIHwgbnVsbCB7XG4gICAgY29uc3Qge2RhdGFDb250YWluZXIsIGZpbHRlcmVkSW5kZXhGb3JEb21haW59ID0gdGhpcztcblxuICAgIGlmICghU0NBTEVfVFlQRVNbc2NhbGVUeXBlXSkge1xuICAgICAgQ29uc29sZS5lcnJvcihgc2NhbGUgdHlwZSAke3NjYWxlVHlwZX0gbm90IHN1cHBvcnRlZGApO1xuICAgICAgcmV0dXJuIG51bGw7XG4gICAgfVxuXG4gICAgY29uc3Qge3ZhbHVlQWNjZXNzb3J9ID0gZmllbGQ7XG4gICAgY29uc3QgaW5kZXhWYWx1ZUFjY2Vzc29yID0gaSA9PiB2YWx1ZUFjY2Vzc29yKHtpbmRleDogaX0pO1xuICAgIGNvbnN0IHNvcnRGdW5jdGlvbiA9IGdldFNvcnRpbmdGdW5jdGlvbihmaWVsZC50eXBlKTtcblxuICAgIHN3aXRjaCAoc2NhbGVUeXBlKSB7XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLm9yZGluYWw6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLmN1c3RvbU9yZGluYWw6XG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnBvaW50OlxuICAgICAgICAvLyBkbyBub3QgcmVjYWxjdWxhdGUgb3JkaW5hbCBkb21haW4gYmFzZWQgb24gZmlsdGVyZWQgZGF0YVxuICAgICAgICAvLyBkb24ndCBuZWVkIHRvIHVwZGF0ZSBvcmRpbmFsIGRvbWFpbiBldmVyeSB0aW1lXG4gICAgICAgIHJldHVybiBnZXRPcmRpbmFsRG9tYWluKGRhdGFDb250YWluZXIsIHZhbHVlQWNjZXNzb3IpO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnF1YW50aWxlOlxuICAgICAgICByZXR1cm4gZ2V0UXVhbnRpbGVEb21haW4oZmlsdGVyZWRJbmRleEZvckRvbWFpbiwgaW5kZXhWYWx1ZUFjY2Vzc29yLCBzb3J0RnVuY3Rpb24pO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLmxvZzpcbiAgICAgICAgcmV0dXJuIGdldExvZ0RvbWFpbihmaWx0ZXJlZEluZGV4Rm9yRG9tYWluLCBpbmRleFZhbHVlQWNjZXNzb3IpO1xuXG4gICAgICBjYXNlIFNDQUxFX1RZUEVTLnF1YW50aXplOlxuICAgICAgY2FzZSBTQ0FMRV9UWVBFUy5saW5