kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
434 lines (350 loc) • 33.7 kB
JavaScript
;
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isGeoJson = isGeoJson;
exports.isFeature = isFeature;
exports.isFeatureCollection = isFeatureCollection;
exports.isRowObject = isRowObject;
exports.isKeplerGlMap = isKeplerGlMap;
exports.makeProgressIterator = makeProgressIterator;
exports.readBatch = readBatch;
exports.readFileInBatches = readFileInBatches;
exports.processFileData = processFileData;
exports.filesToDataPayload = filesToDataPayload;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _awaitAsyncGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/awaitAsyncGenerator"));
var _wrapAsyncGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/wrapAsyncGenerator"));
var _asyncIterator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncIterator"));
require("@loaders.gl/polyfills");
var _core = require("@loaders.gl/core");
var _json = require("@loaders.gl/json");
var _csv = require("@loaders.gl/csv");
var _dataProcessor = require("./data-processor");
var _utils = require("../utils/utils");
var _defaultSettings = require("../constants/default-settings");
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; }
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { (0, _defineProperty2["default"])(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
var BATCH_TYPE = {
METADATA: 'metadata',
PARTIAL_RESULT: 'partial-result',
FINAL_RESULT: 'final-result'
};
var CSV_LOADER_OPTIONS = {
batchSize: 4000,
// Auto de tect number of rows per batch (network batch size)
rowFormat: 'object',
dynamicTyping: false // not working for now
};
var JSON_LOADER_OPTIONS = {
// instruct loaders.gl on what json paths to stream
jsonpaths: ['$', // JSON Row array
'$.features', // GeoJSON
'$.datasets' // KeplerGL JSON
]
};
function isGeoJson(json) {
// json can be feature collection
// or single feature
return (0, _utils.isPlainObject)(json) && (isFeature(json) || isFeatureCollection(json));
}
function isFeature(json) {
return json.type === 'Feature' && json.geometry;
}
function isFeatureCollection(json) {
return json.type === 'FeatureCollection' && json.features;
}
function isRowObject(json) {
return Array.isArray(json) && (0, _utils.isPlainObject)(json[0]);
}
function isKeplerGlMap(json) {
return Boolean((0, _utils.isPlainObject)(json) && json.datasets && json.config && json.info && json.info.app === 'kepler.gl');
}
function makeProgressIterator(_x, _x2) {
return _makeProgressIterator.apply(this, arguments);
} // eslint-disable-next-line complexity
function _makeProgressIterator() {
_makeProgressIterator = (0, _wrapAsyncGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee(asyncIterator, info) {
var rowCount, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, _value, batch, rowCountInBatch, percent, progress;
return _regenerator["default"].wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
rowCount = 0;
_iteratorNormalCompletion = true;
_didIteratorError = false;
_context.prev = 3;
_iterator = (0, _asyncIterator2["default"])(asyncIterator);
case 5:
_context.next = 7;
return (0, _awaitAsyncGenerator2["default"])(_iterator.next());
case 7:
_step = _context.sent;
_iteratorNormalCompletion = _step.done;
_context.next = 11;
return (0, _awaitAsyncGenerator2["default"])(_step.value);
case 11:
_value = _context.sent;
if (_iteratorNormalCompletion) {
_context.next = 23;
break;
}
batch = _value;
rowCountInBatch = batch.data && batch.data.length || 0;
rowCount += rowCountInBatch;
percent = Number.isFinite(batch.bytesUsed) ? batch.bytesUsed / info.size : null; // Update progress object
progress = _objectSpread({
rowCount: rowCount,
rowCountInBatch: rowCountInBatch
}, Number.isFinite(percent) ? {
percent: percent
} : {});
_context.next = 20;
return _objectSpread(_objectSpread({}, batch), {}, {
progress: progress
});
case 20:
_iteratorNormalCompletion = true;
_context.next = 5;
break;
case 23:
_context.next = 29;
break;
case 25:
_context.prev = 25;
_context.t0 = _context["catch"](3);
_didIteratorError = true;
_iteratorError = _context.t0;
case 29:
_context.prev = 29;
_context.prev = 30;
if (!(!_iteratorNormalCompletion && _iterator["return"] != null)) {
_context.next = 34;
break;
}
_context.next = 34;
return (0, _awaitAsyncGenerator2["default"])(_iterator["return"]());
case 34:
_context.prev = 34;
if (!_didIteratorError) {
_context.next = 37;
break;
}
throw _iteratorError;
case 37:
return _context.finish(34);
case 38:
return _context.finish(29);
case 39:
case "end":
return _context.stop();
}
}
}, _callee, null, [[3, 25, 29, 39], [30,, 34, 38]]);
}));
return _makeProgressIterator.apply(this, arguments);
}
function readBatch(_x3, _x4) {
return _readBatch.apply(this, arguments);
}
function _readBatch() {
_readBatch = (0, _wrapAsyncGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee2(asyncIterator, fileName) {
var result, batches, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, _value2, batch, streamingPath, i;
return _regenerator["default"].wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
result = null;
batches = [];
_iteratorNormalCompletion2 = true;
_didIteratorError2 = false;
_context2.prev = 4;
_iterator2 = (0, _asyncIterator2["default"])(asyncIterator);
case 6:
_context2.next = 8;
return (0, _awaitAsyncGenerator2["default"])(_iterator2.next());
case 8:
_step2 = _context2.sent;
_iteratorNormalCompletion2 = _step2.done;
_context2.next = 12;
return (0, _awaitAsyncGenerator2["default"])(_step2.value);
case 12:
_value2 = _context2.sent;
if (_iteratorNormalCompletion2) {
_context2.next = 21;
break;
}
batch = _value2;
// Last batch will have this special type and will provide all the root
// properties of the parsed document.
// Only json parse will have `FINAL_RESULT`
if (batch.batchType === BATCH_TYPE.FINAL_RESULT) {
if (batch.container) {
result = _objectSpread({}, batch.container);
} // Set the streamed data correctly is Batch json path is set
// and the path streamed is not the top level object (jsonpath = '$')
if (batch.jsonpath && batch.jsonpath.length > 1) {
streamingPath = new _json._JSONPath(batch.jsonpath);
streamingPath.setFieldAtPath(result, batches);
} else if (batch.jsonpath && batch.jsonpath.length === 1) {
// The streamed object is a ROW JSON-batch (jsonpath = '$')
// row objects
result = batches;
}
} else {
for (i = 0; i < batch.data.length; i++) {
batches.push(batch.data[i]);
}
}
_context2.next = 18;
return _objectSpread(_objectSpread(_objectSpread({}, batch), batch.schema ? {
headers: Object.keys(batch.schema)
} : {}), {}, {
fileName: fileName,
// if dataset is CSV, data is set to the raw batches
data: result ? result : batches
});
case 18:
_iteratorNormalCompletion2 = true;
_context2.next = 6;
break;
case 21:
_context2.next = 27;
break;
case 23:
_context2.prev = 23;
_context2.t0 = _context2["catch"](4);
_didIteratorError2 = true;
_iteratorError2 = _context2.t0;
case 27:
_context2.prev = 27;
_context2.prev = 28;
if (!(!_iteratorNormalCompletion2 && _iterator2["return"] != null)) {
_context2.next = 32;
break;
}
_context2.next = 32;
return (0, _awaitAsyncGenerator2["default"])(_iterator2["return"]());
case 32:
_context2.prev = 32;
if (!_didIteratorError2) {
_context2.next = 35;
break;
}
throw _iteratorError2;
case 35:
return _context2.finish(32);
case 36:
return _context2.finish(27);
case 37:
case "end":
return _context2.stop();
}
}
}, _callee2, null, [[4, 23, 27, 37], [28,, 32, 36]]);
}));
return _readBatch.apply(this, arguments);
}
function readFileInBatches(_x5) {
return _readFileInBatches.apply(this, arguments);
}
function _readFileInBatches() {
_readFileInBatches = (0, _asyncToGenerator2["default"])( /*#__PURE__*/_regenerator["default"].mark(function _callee3(_ref) {
var file, _ref$fileCache, fileCache, _ref$loaders, loaders, _ref$loadOptions, loadOptions, batchIterator, progressIterator;
return _regenerator["default"].wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
file = _ref.file, _ref$fileCache = _ref.fileCache, fileCache = _ref$fileCache === void 0 ? [] : _ref$fileCache, _ref$loaders = _ref.loaders, loaders = _ref$loaders === void 0 ? [] : _ref$loaders, _ref$loadOptions = _ref.loadOptions, loadOptions = _ref$loadOptions === void 0 ? {} : _ref$loadOptions;
loaders = [_json.JSONLoader, _csv.CSVLoader].concat((0, _toConsumableArray2["default"])(loaders));
loadOptions = _objectSpread({
csv: CSV_LOADER_OPTIONS,
json: JSON_LOADER_OPTIONS,
metadata: true
}, loadOptions);
_context3.next = 5;
return (0, _core.parseInBatches)(file, loaders, loadOptions);
case 5:
batchIterator = _context3.sent;
progressIterator = makeProgressIterator(batchIterator, {
size: file.size
});
return _context3.abrupt("return", readBatch(progressIterator, file.name));
case 8:
case "end":
return _context3.stop();
}
}
}, _callee3);
}));
return _readFileInBatches.apply(this, arguments);
}
function processFileData(_ref2) {
var content = _ref2.content,
fileCache = _ref2.fileCache;
return new Promise(function (resolve, reject) {
var data = content.data;
var format;
var processor;
if (isKeplerGlMap(data)) {
format = _defaultSettings.DATASET_FORMATS.keplergl;
processor = _dataProcessor.processKeplerglJSON;
} else if (isRowObject(data)) {
format = _defaultSettings.DATASET_FORMATS.row;
processor = _dataProcessor.processRowObject;
} else if (isGeoJson(data)) {
format = _defaultSettings.DATASET_FORMATS.geojson;
processor = _dataProcessor.processGeojson;
}
if (format && processor) {
var result = processor(data);
resolve([].concat((0, _toConsumableArray2["default"])(fileCache), [{
data: result,
info: {
label: content.fileName,
format: format
}
}]));
}
reject('Unknow File Format');
});
}
function filesToDataPayload(fileCache) {
// seperate out files which could be a single datasets. or a keplergl map json
var collection = fileCache.reduce(function (accu, file) {
var data = file.data,
_file$info = file.info,
info = _file$info === void 0 ? {} : _file$info;
var format = info.format;
if (format === _defaultSettings.DATASET_FORMATS.keplergl) {
// if file contains a single kepler map dataset & config
accu.keplerMaps.push(_objectSpread(_objectSpread({}, data), {}, {
options: {
centerMap: !(data.config && data.config.mapState)
}
}));
} else if (_defaultSettings.DATASET_FORMATS[format]) {
// if file contains only data
var newDataset = {
data: data,
info: _objectSpread({
id: info.id || (0, _utils.generateHashId)(4)
}, info)
};
accu.datasets.push(newDataset);
}
return accu;
}, {
datasets: [],
keplerMaps: []
}); // add kepler map first with config
// add datasets later in one add data call
return collection.keplerMaps.concat({
datasets: collection.datasets
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9wcm9jZXNzb3JzL2ZpbGUtaGFuZGxlci5qcyJdLCJuYW1lcyI6WyJCQVRDSF9UWVBFIiwiTUVUQURBVEEiLCJQQVJUSUFMX1JFU1VMVCIsIkZJTkFMX1JFU1VMVCIsIkNTVl9MT0FERVJfT1BUSU9OUyIsImJhdGNoU2l6ZSIsInJvd0Zvcm1hdCIsImR5bmFtaWNUeXBpbmciLCJKU09OX0xPQURFUl9PUFRJT05TIiwianNvbnBhdGhzIiwiaXNHZW9Kc29uIiwianNvbiIsImlzRmVhdHVyZSIsImlzRmVhdHVyZUNvbGxlY3Rpb24iLCJ0eXBlIiwiZ2VvbWV0cnkiLCJmZWF0dXJlcyIsImlzUm93T2JqZWN0IiwiQXJyYXkiLCJpc0FycmF5IiwiaXNLZXBsZXJHbE1hcCIsIkJvb2xlYW4iLCJkYXRhc2V0cyIsImNvbmZpZyIsImluZm8iLCJhcHAiLCJtYWtlUHJvZ3Jlc3NJdGVyYXRvciIsImFzeW5jSXRlcmF0b3IiLCJyb3dDb3VudCIsImJhdGNoIiwicm93Q291bnRJbkJhdGNoIiwiZGF0YSIsImxlbmd0aCIsInBlcmNlbnQiLCJOdW1iZXIiLCJpc0Zpbml0ZSIsImJ5dGVzVXNlZCIsInNpemUiLCJwcm9ncmVzcyIsInJlYWRCYXRjaCIsImZpbGVOYW1lIiwicmVzdWx0IiwiYmF0Y2hlcyIsImJhdGNoVHlwZSIsImNvbnRhaW5lciIsImpzb25wYXRoIiwic3RyZWFtaW5nUGF0aCIsIl9KU09OUGF0aCIsInNldEZpZWxkQXRQYXRoIiwiaSIsInB1c2giLCJzY2hlbWEiLCJoZWFkZXJzIiwiT2JqZWN0Iiwia2V5cyIsInJlYWRGaWxlSW5CYXRjaGVzIiwiZmlsZSIsImZpbGVDYWNoZSIsImxvYWRlcnMiLCJsb2FkT3B0aW9ucyIsIkpTT05Mb2FkZXIiLCJDU1ZMb2FkZXIiLCJjc3YiLCJtZXRhZGF0YSIsImJhdGNoSXRlcmF0b3IiLCJwcm9ncmVzc0l0ZXJhdG9yIiwibmFtZSIsInByb2Nlc3NGaWxlRGF0YSIsImNvbnRlbnQiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImZvcm1hdCIsInByb2Nlc3NvciIsIkRBVEFTRVRfRk9STUFUUyIsImtlcGxlcmdsIiwicHJvY2Vzc0tlcGxlcmdsSlNPTiIsInJvdyIsInByb2Nlc3NSb3dPYmplY3QiLCJnZW9qc29uIiwicHJvY2Vzc0dlb2pzb24iLCJsYWJlbCIsImZpbGVzVG9EYXRhUGF5bG9hZCIsImNvbGxlY3Rpb24iLCJyZWR1Y2UiLCJhY2N1Iiwia2VwbGVyTWFwcyIsIm9wdGlvbnMiLCJjZW50ZXJNYXAiLCJtYXBTdGF0ZSIsIm5ld0RhdGFzZXQiLCJpZCIsImNvbmNhdCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFvQkE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7Ozs7OztBQUVBLElBQU1BLFVBQVUsR0FBRztBQUNqQkMsRUFBQUEsUUFBUSxFQUFFLFVBRE87QUFFakJDLEVBQUFBLGNBQWMsRUFBRSxnQkFGQztBQUdqQkMsRUFBQUEsWUFBWSxFQUFFO0FBSEcsQ0FBbkI7QUFNQSxJQUFNQyxrQkFBa0IsR0FBRztBQUN6QkMsRUFBQUEsU0FBUyxFQUFFLElBRGM7QUFDUjtBQUNqQkMsRUFBQUEsU0FBUyxFQUFFLFFBRmM7QUFHekJDLEVBQUFBLGFBQWEsRUFBRSxLQUhVLENBR0o7O0FBSEksQ0FBM0I7QUFNQSxJQUFNQyxtQkFBbUIsR0FBRztBQUMxQjtBQUNBQyxFQUFBQSxTQUFTLEVBQUUsQ0FDVCxHQURTLEVBQ0o7QUFDTCxjQUZTLEVBRUs7QUFDZCxjQUhTLENBR0k7QUFISjtBQUZlLENBQTVCOztBQVNPLFNBQVNDLFNBQVQsQ0FBbUJDLElBQW5CLEVBQXlCO0FBQzlCO0FBQ0E7QUFDQSxTQUFPLDBCQUFjQSxJQUFkLE1BQXdCQyxTQUFTLENBQUNELElBQUQsQ0FBVCxJQUFtQkUsbUJBQW1CLENBQUNGLElBQUQsQ0FBOUQsQ0FBUDtBQUNEOztBQUVNLFNBQVNDLFNBQVQsQ0FBbUJELElBQW5CLEVBQXlCO0FBQzlC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QUFBQSxxQkFDWXlCLElBRFosQ0FDRGhDLElBREM7QUFBQSxRQUNEQSxJQURDLDJCQUNNLEVBRE47QUFBQSxRQUVQZ0QsTUFGTyxHQUVHaEQsSUFGSCxDQUVQZ0QsTUFGTzs7QUFHZCxRQUFJQSxNQUFNLEtBQUtFLGlDQUFnQkMsUUFBL0IsRUFBeUM7QUFDdkM7QUFDQVUsTUFBQUEsSUFBSSxDQUFDQyxVQUFMLENBQWdCcEMsSUFBaEIsaUNBQ0tuQixJQURMO0FBRUV3RCxRQUFBQSxPQUFPLEVBQUU7QUFDUEMsVUFBQUEsU0FBUyxFQUFFLEVBQUV6RCxJQUFJLENBQUNSLE1BQUwsSUFBZVEsSUFBSSxDQUFDUixNQUFMLENBQVlrRSxRQUE3QjtBQURKO0FBRlg7QUFNRCxLQVJELE1BUU8sSUFBSWYsaUNBQWdCRixNQUFoQixDQUFKLEVBQTZCO0FBQ2xDO0FBQ0EsVUFBTWtCLFVBQVUsR0FBRztBQUNqQjNELFFBQUFBLElBQUksRUFBSkEsSUFEaUI7QUFFakJQLFFBQUFBLElBQUk7QUFDRm1FLFVBQUFBLEVBQUUsRUFBRW5FLElBQUksQ0FBQ21FLEVBQUwsSUFBVywyQkFBZSxDQUFmO0FBRGIsV0FFQ25FLElBRkQ7QUFGYSxPQUFuQjtBQU9BNkQsTUFBQUEsSUFBSSxDQUFDL0QsUUFBTCxDQUFjNEIsSUFBZCxDQUFtQndDLFVBQW5CO0FBQ0Q7O0FBQ0QsV0FBT0wsSUFBUDtBQUNELEdBeEJnQixFQXlCakI7QUFBQy9ELElBQUFBLFFBQVEsRUFBRSxFQUFYO0FBQWVnRSxJQUFBQSxVQUFVLEVBQUU7QUFBM0IsR0F6QmlCLENBQW5CLENBRjRDLENBOEI1QztBQUNBOztBQUNBLFNBQU9ILFVBQVUsQ0FBQ0csVUFBWCxDQUFzQk0sTUFBdEIsQ0FBNkI7QUFBQ3RFLElBQUFBLFFBQVEsRUFBRTZELFVBQVUsQ0FBQzdEO0FBQXRCLEdBQTdCLENBQVA7QUFDRCIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAoYykgMjAyMSBVYmVyIFRlY2hub2xvZ2llcywgSW5jLlxuLy9cbi8vIFBlcm1pc3Npb24gaXMgaGVyZWJ5IGdyYW50ZWQsIGZyZWUgb2YgY2hhcmdlLCB0byBhbnkgcGVyc29uIG9idGFpbmluZyBhIGNvcHlcbi8vIG9mIHRoaXMgc29mdHdhcmUgYW5kIGFzc29jaWF0ZWQgZG9jdW1lbnRhdGlvbiBmaWxlcyAodGhlIFwiU29mdHdhcmVcIiksIHRvIGRlYWxcbi8vIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHNcbi8vIHRvIHVzZSwgY29weSwgbW9kaWZ5LCBtZXJnZSwgcHVibGlzaCwgZGlzdHJpYnV0ZSwgc3VibGljZW5zZSwgYW5kL29yIHNlbGxcbi8vIGNvcGllcyBvZiB0aGUgU29mdHdhcmUsIGFuZCB0byBwZXJtaXQgcGVyc29ucyB0byB3aG9tIHRoZSBTb2Z0d2FyZSBpc1xuLy8gZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gYWxsIGNvcGllcyBvciBzdWJzdGFudGlhbCBwb3J0aW9ucyBvZiB0aGUgU29mdHdhcmUuXG4vL1xuLy8gVEhFIFNPRlRXQVJFIElTIFBST1ZJREVEIFwiQVMgSVNcIiwgV0lUSE9VVCBXQVJSQU5UWSBPRiBBTlkgS0lORCwgRVhQUkVTUyBPUlxuLy8gSU1QTElFRCwgSU5DTFVESU5HIEJVVCBOT1QgTElNSVRFRCBUTyBUSEUgV0FSUkFOVElFUyBPRiBNRVJDSEFOVEFCSUxJVFksXG4vLyBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vIEFVVEhPUlMgT1IgQ09QWVJJR0hUIEhPTERFUlMgQkUgTElBQkxFIEZPUiBBTlkgQ0xBSU0sIERBTUFHRVMgT1IgT1RIRVJcbi8vIExJQUJJTElUWSwgV0hFVEhFUiBJTiBBTiBBQ1RJT04gT0YgQ09OVFJBQ1QsIFRPUlQgT1IgT1RIRVJXSVNFLCBBUklTSU5HIEZST00sXG4vLyBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyBUSEUgU09GVFdBUkUuXG5cbmltcG9ydCAnQGxvYWRlcnMuZ2wvcG9seWZpbGxzJztcbmltcG9ydCB7cGFyc2VJbkJhdGNoZXN9IGZyb20gJ0Bsb2FkZXJzLmdsL2NvcmUnO1xuaW1wb3J0IHtKU09OTG9hZGVyLCBfSlNPTlBhdGh9IGZyb20gJ0Bsb2FkZXJzLmdsL2pzb24nO1xuaW1wb3J0IHtDU1ZMb2FkZXJ9IGZyb20gJ0Bsb2FkZXJzLmdsL2Nzdic7XG5pbXBvcnQge3Byb2Nlc3NHZW9qc29uLCBwcm9jZXNzS2VwbGVyZ2xKU09OLCBwcm9jZXNzUm93T2JqZWN0fSBmcm9tICcuL2RhdGEtcHJvY2Vzc29yJztcbmltcG9ydCB7aXNQbGFpbk9iamVjdCwgZ2VuZXJhdGVIYXNoSWR9IGZyb20gJ3V0aWxzL3V0aWxzJztcbmltcG9ydCB7REFUQVNFVF9GT1JNQVRTfSBmcm9tICdjb25zdGFudHMvZGVmYXVsdC1zZXR0aW5ncyc7XG5cbmNvbnN0IEJBVENIX1RZUEUgPSB7XG4gIE1FVEFEQVRBOiAnbWV0YWRhdGEnLFxuICBQQVJUSUFMX1JFU1VMVDogJ3BhcnRpYWwtcmVzdWx0JyxcbiAgRklOQUxfUkVTVUxUOiAnZmluYWwtcmVzdWx0J1xufTtcblxuY29uc3QgQ1NWX0xPQURFUl9PUFRJT05TID0ge1xuICBiYXRjaFNpemU6IDQwMDAsIC8vIEF1dG8gZGUgdGVjdCBudW1iZXIgb2Ygcm93cyBwZXIgYmF0Y2ggKG5ldHdvcmsgYmF0Y2ggc2l6ZSlcbiAgcm93Rm9ybWF0OiAnb2JqZWN0JyxcbiAgZHluYW1pY1R5cGluZzogZmFsc2UgLy8gbm90IHdvcmtpbmcgZm9yIG5vd1xufTtcblxuY29uc3QgSlNPTl9MT0FERVJfT1BUSU9OUyA9IHtcbiAgLy8gaW5zdHJ1Y3QgbG9hZGVycy5nbCBvbiB3aGF0IGpzb24gcGF0aHMgdG8gc3RyZWFtXG4gIGpzb25wYXRoczogW1xuICAgICckJywgLy8gSlNPTiBSb3cgYXJyYXlcbiAgICAnJC5mZWF0dXJlcycsIC8vIEdlb0pTT05cbiAgICAnJC5kYXRhc2V0cycgLy8gS2VwbGVyR0wgSlNPTlxuICBdXG59O1xuXG5leHBvcnQgZnVuY3Rpb24gaXNHZW9Kc29uKGpzb24pIHtcbiAgLy8ganNvbiBjYW4gYmUgZmVhdHVyZSBjb2xsZWN0aW9uXG4gIC8vIG9yIHNpbmdsZSBmZWF0dXJlXG4gIHJldHVybiBpc1BsYWluT2JqZWN0KGpzb24pICYmIChpc0ZlYXR1cmUoanNvbikgfHwgaXNGZWF0dXJlQ29sbGVjdGlvbihqc29uKSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0ZlYXR1cmUoanNvbikge1xuICByZXR1cm4ganNvbi50eXBlID09PSAnRmVhdHVyZScgJiYganNvbi5nZW9tZXRyeTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGlzRmVhdHVyZUNvbGxlY3Rpb24oanNvbikge1xuICByZXR1cm4ganNvbi50eXBlID09PSAnRmVhdHVyZUNvbGxlY3Rpb24nICYmIGpzb24uZmVhdHVyZXM7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc1Jvd09iamVjdChqc29uKSB7XG4gIHJldHVybiBBcnJheS5pc0FycmF5KGpzb24pICYmIGlzUGxhaW5PYmplY3QoanNvblswXSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBpc0tlcGxlckdsTWFwKGpzb24pIHtcbiAgcmV0dXJuIEJvb2xlYW4oXG4gICAgaXNQbGFpbk9iamVjdChqc29uKSAmJlxuICAgICAganNvbi5kYXRhc2V0cyAmJlxuICAgICAganNvbi5jb25maWcgJiZcbiAgICAgIGpzb24uaW5mbyAmJlxuICAgICAganNvbi5pbmZvLmFwcCA9PT0gJ2tlcGxlci5nbCdcbiAgKTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uKiBtYWtlUHJvZ3Jlc3NJdGVyYXRvcihhc3luY0l0ZXJhdG9yLCBpbmZvKSB7XG4gIGxldCByb3dDb3VudCA9IDA7XG5cbiAgZm9yIGF3YWl0IChjb25zdCBiYXRjaCBvZiBhc3luY0l0ZXJhdG9yKSB7XG4gICAgY29uc3Qgcm93Q291bnRJbkJhdGNoID0gKGJhdGNoLmRhdGEgJiYgYmF0Y2guZGF0YS5sZW5ndGgpIHx8IDA7XG4gICAgcm93Q291bnQgKz0gcm93Q291bnRJbkJhdGNoO1xuICAgIGNvbnN0IHBlcmNlbnQgPSBOdW1iZXIuaXNGaW5pdGUoYmF0Y2guYnl0ZXNVc2VkKSA/IGJhdGNoLmJ5dGVzVXNlZCAvIGluZm8uc2l6ZSA6IG51bGw7XG5cbiAgICAvLyBVcGRhdGUgcHJvZ3Jlc3Mgb2JqZWN0XG4gICAgY29uc3QgcHJvZ3Jlc3MgPSB7XG4gICAgICByb3dDb3VudCxcbiAgICAgIHJvd0NvdW50SW5CYXRjaCxcbiAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgIC4uLihOdW1iZXIuaXNGaW5pdGUocGVyY2VudCkgPyB7cGVyY2VudH0gOiB7fSlcbiAgICB9O1xuXG4gICAgeWllbGQgey4uLmJhdGNoLCBwcm9ncmVzc307XG4gIH1cbn1cblxuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbmV4cG9ydCBhc3luYyBmdW5jdGlvbiogcmVhZEJhdGNoKGFzeW5jSXRlcmF0b3IsIGZpbGVOYW1lKSB7XG4gIGxldCByZXN1bHQgPSBudWxsO1xuICBjb25zdCBiYXRjaGVzID0gW107XG5cbiAgZm9yIGF3YWl0IChjb25zdCBiYXRjaCBvZiBhc3luY0l0ZXJhdG9yKSB7XG4gICAgLy8gTGFzdCBiYXRjaCB3aWxsIGhhdmUgdGhpcyBzcGVjaWFsIHR5cGUgYW5kIHdpbGwgcHJvdmlkZSBhbGwgdGhlIHJvb3RcbiAgICAvLyBwcm9wZXJ0aWVzIG9mIHRoZSBwYXJzZWQgZG9jdW1lbnQuXG4gICAgLy8gT25seSBqc29uIHBhcnNlIHdpbGwgaGF2ZSBgRklOQUxfUkVTVUxUYFxuICAgIGlmIChiYXRjaC5iYXRjaFR5cGUgPT09IEJBVENIX1RZUEUuRklOQUxfUkVTVUxUKSB7XG4gICAgICBpZiAoYmF0Y2guY29udGFpbmVyKSB7XG4gICAgICAgIHJlc3VsdCA9IHsuLi5iYXRjaC5jb250YWluZXJ9O1xuICAgICAgfVxuICAgICAgLy8gU2V0IHRoZSBzdHJlYW1lZCBkYXRhIGNvcnJlY3RseSBpcyBCYXRjaCBqc29uIHBhdGggaXMgc2V0XG4gICAgICAvLyBhbmQgdGhlIHBhdGggc3RyZWFtZWQgaXMgbm90IHRoZSB0b3AgbGV2ZWwgb2JqZWN0IChqc29ucGF0aCA9ICckJylcbiAgICAgIGlmIChiYXRjaC5qc29ucGF0aCAmJiBiYXRjaC5qc29ucGF0aC5sZW5ndGggPiAxKSB7XG4gICAgICAgIGNvbnN0IHN0cmVhbWluZ1BhdGggPSBuZXcgX0pTT05QYXRoKGJhdGNoLmpzb25wYXRoKTtcbiAgICAgICAgc3RyZWFtaW5nUGF0aC5zZXRGaWVsZEF0UGF0aChyZXN1bHQsIGJhdGNoZXMpO1xuICAgICAgfSBlbHNlIGlmIChiYXRjaC5qc29ucGF0aCAmJiBiYXRjaC5qc29ucGF0aC5sZW5ndGggPT09IDEpIHtcbiAgICAgICAgLy8gVGhlIHN0cmVhbWVkIG9iamVjdCBpcyBhIFJPVyBKU09OLWJhdGNoIChqc29ucGF0aCA9ICckJylcbiAgICAgICAgLy8gcm93IG9iamVjdHNcbiAgICAgICAgcmVzdWx0ID0gYmF0Y2hlcztcbiAgICAgIH1cbiAgICB9IGVsc2Uge1xuICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBiYXRjaC5kYXRhLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIGJhdGNoZXMucHVzaChiYXRjaC5kYXRhW2ldKTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICB5aWVsZCB7XG4gICAgICAuLi5iYXRjaCxcbiAgICAgIC4uLihiYXRjaC5zY2hlbWEgPyB7aGVhZGVyczogT2JqZWN0LmtleXMoYmF0Y2guc2NoZW1hKX0gOiB7fSksXG4gICAgICBmaWxlTmFtZSxcbiAgICAgIC8vIGlmIGRhdGFzZXQgaXMgQ1NWLCBkYXRhIGlzIHNldCB0byB0aGUgcmF3IGJhdGNoZXNcbiAgICAgIGRhdGE6IHJlc3VsdCA/IHJlc3VsdCA6IGJhdGNoZXNcbiAgICB9O1xuICB9XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiByZWFkRmlsZUluQmF0Y2hlcyh7ZmlsZSwgZmlsZUNhY2hlID0gW10sIGxvYWRlcnMgPSBbXSwgbG9hZE9wdGlvbnMgPSB7fX0pIHtcbiAgbG9hZGVycyA9IFtKU09OTG9hZGVyLCBDU1ZMb2FkZXIsIC4uLmxvYWRlcnNdO1xuICBsb2FkT3B0aW9ucyA9IHtcbiAgICBjc3Y6IENTVl9MT0FERVJfT1BUSU9OUyxcbiAgICBqc29uOiBKU09OX0xPQURFUl9PUFRJT05TLFxuICAgIG1ldGFkYXRhOiB0cnVlLFxuICAgIC4uLmxvYWRPcHRpb25zXG4gIH07XG5cbiAgY29uc3QgYmF0Y2hJdGVyYXRvciA9IGF3YWl0IHBhcnNlSW5CYXRjaGVzKGZpbGUsIGxvYWRlcnMsIGxvYWRPcHRpb25zKTtcbiAgY29uc3QgcHJvZ3Jlc3NJdGVyYXRvciA9IG1ha2VQcm9ncmVzc0l0ZXJhdG9yKGJhdGNoSXRlcmF0b3IsIHtzaXplOiBmaWxlLnNpemV9KTtcblxuICByZXR1cm4gcmVhZEJhdGNoKHByb2dyZXNzSXRlcmF0b3IsIGZpbGUubmFtZSk7XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBwcm9jZXNzRmlsZURhdGEoe2NvbnRlbnQsIGZpbGVDYWNoZX0pIHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBjb25zdCB7ZGF0YX0gPSBjb250ZW50O1xuXG4gICAgbGV0IGZvcm1hdDtcbiAgICBsZXQgcHJvY2Vzc29yO1xuICAgIGlmIChpc0tlcGxlckdsTWFwKGRhdGEpKSB7XG4gICAgICBmb3JtYXQgPSBEQVRBU0VUX0ZPUk1BVFMua2VwbGVyZ2w7XG4gICAgICBwcm9jZXNzb3IgPSBwcm9jZXNzS2VwbGVyZ2xKU09OO1xuICAgIH0gZWxzZSBpZiAoaXNSb3dPYmplY3QoZGF0YSkpIHtcbiAgICAgIGZvcm1hdCA9IERBVEFTRVRfRk9STUFUUy5yb3c7XG4gICAgICBwcm9jZXNzb3IgPSBwcm9jZXNzUm93T2JqZWN0O1xuICAgIH0gZWxzZSBpZiAoaXNHZW9Kc29uKGRhdGEpKSB7XG4gICAgICBmb3JtYXQgPSBEQVRBU0VUX0ZPUk1BVFMuZ2VvanNvbjtcbiAgICAgIHByb2Nlc3NvciA9IHByb2Nlc3NHZW9qc29uO1xuICAgIH1cblxuICAgIGlmIChmb3JtYXQgJiYgcHJvY2Vzc29yKSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBwcm9jZXNzb3IoZGF0YSk7XG5cbiAgICAgIHJlc29sdmUoW1xuICAgICAgICAuLi5maWxlQ2FjaGUsXG4gICAgICAgIHtcbiAgICAgICAgICBkYXRhOiByZXN1bHQsXG4gICAgICAgICAgaW5mbzoge1xuICAgICAgICAgICAgbGFiZWw6IGNvbnRlbnQuZmlsZU5hbWUsXG4gICAgICAgICAgICBmb3JtYXRcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIF0pO1xuICAgIH1cblxuICAgIHJlamVjdCgnVW5rbm93IEZpbGUgRm9ybWF0Jyk7XG4gIH0pO1xufVxuXG5leHBvcnQgZnVuY3Rpb24gZmlsZXNUb0RhdGFQYXlsb2FkKGZpbGVDYWNoZSkge1xuICAvLyBzZXBlcmF0ZSBvdXQgZmlsZXMgd2hpY2ggY291bGQgYmUgYSBzaW5nbGUgZGF0YXNldHMuIG9yIGEga2VwbGVyZ2wgbWFwIGpzb25cbiAgY29uc3QgY29sbGVjdGlvbiA9IGZpbGVDYWNoZS5yZWR1Y2UoXG4gICAgKGFjY3UsIGZpbGUpID0+IHtcbiAgICAgIGNvbnN0IHtkYXRhLCBpbmZvID0ge319ID0gZmlsZTtcbiAgICAgIGNvbnN0IHtmb3JtYXR9ID0gaW5mbztcbiAgICAgIGlmIChmb3JtYXQgPT09IERBVEFTRVRfRk9STUFUUy5rZXBsZXJnbCkge1xuICAgICAgICAvLyBpZiBmaWxlIGNvbnRhaW5zIGEgc2luZ2xlIGtlcGxlciBtYXAgZGF0YXNldCAmIGNvbmZpZ1xuICAgICAgICBhY2N1LmtlcGxlck1hcHMucHVzaCh7XG4gICAgICAgICAgLi4uZGF0YSxcbiAgICAgICAgICBvcHRpb25zOiB7XG4gICAgICAgICAgICBjZW50ZXJNYXA6ICEoZGF0YS5jb25maWcgJiYgZGF0YS5jb25maWcubWFwU3RhdGUpXG4gICAgICAgICAgfVxuICAgICAgICB9KTtcbiAgICAgIH0gZWxzZSBpZiAoREFUQVNFVF9GT1JNQVRTW2Zvcm1hdF0pIHtcbiAgICAgICAgLy8gaWYgZmlsZSBjb250YWlucyBvbmx5IGRhdGFcbiAgICAgICAgY29uc3QgbmV3RGF0YXNldCA9IHtcbiAgICAgICAgICBkYXRhLFxuICAgICAgICAgIGluZm86IHtcbiAgICAgICAgICAgIGlkOiBpbmZvLmlkIHx8IGdlbmVyYXRlSGFzaElkKDQpLFxuICAgICAgICAgICAgLi4uaW5mb1xuICAgICAgICAgIH1cbiAgICAgICAgfTtcbiAgICAgICAgYWNjdS5kYXRhc2V0cy5wdXNoKG5ld0RhdGFzZXQpO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGFjY3U7XG4gICAgfSxcbiAgICB7ZGF0YXNldHM6IFtdLCBrZXBsZXJNYXBzOiBbXX1cbiAgKTtcblxuICAvLyBhZGQga2VwbGVyIG1hcCBmaXJzdCB3aXRoIGNvbmZpZ1xuICAvLyBhZGQgZGF0YXNldHMgbGF0ZXIgaW4gb25lIGFkZCBkYXRhIGNhbGxcbiAgcmV0dXJuIGNvbGxlY3Rpb24ua2VwbGVyTWFwcy5jb25jYXQoe2RhdGFzZXRzOiBjb2xsZWN0aW9uLmRhdGFzZXRzfSk7XG59XG4iXX0=