kepler.gl
Version:
kepler.gl is a webgl based application to visualize large scale location data in the browser
302 lines (288 loc) • 33.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ACCEPTED_ANALYZER_TYPES = void 0;
exports.analyzerTypeToFieldType = analyzerTypeToFieldType;
exports.getFieldsFromData = getFieldsFromData;
exports.getSampleForTypeAnalyze = getSampleForTypeAnalyze;
exports.getSampleForTypeAnalyzeArrow = getSampleForTypeAnalyzeArrow;
exports.renameDuplicateFields = renameDuplicateFields;
var _typeAnalyzer = require("type-analyzer");
var _constants = require("@kepler.gl/constants");
var _window = require("global/window");
var _d3Array = require("d3-array");
var _data = require("./data");
var _h3Utils = require("./h3-utils");
// SPDX-License-Identifier: MIT
// Copyright contributors to the kepler.gl project
var H3_ANALYZER_TYPE = 'H3';
var ACCEPTED_ANALYZER_TYPES = exports.ACCEPTED_ANALYZER_TYPES = [_typeAnalyzer.DATA_TYPES.DATE, _typeAnalyzer.DATA_TYPES.TIME, _typeAnalyzer.DATA_TYPES.DATETIME, _typeAnalyzer.DATA_TYPES.NUMBER, _typeAnalyzer.DATA_TYPES.INT, _typeAnalyzer.DATA_TYPES.FLOAT, _typeAnalyzer.DATA_TYPES.BOOLEAN, _typeAnalyzer.DATA_TYPES.STRING, _typeAnalyzer.DATA_TYPES.GEOMETRY, _typeAnalyzer.DATA_TYPES.GEOMETRY_FROM_STRING, _typeAnalyzer.DATA_TYPES.PAIR_GEOMETRY_FROM_STRING, _typeAnalyzer.DATA_TYPES.ZIPCODE, _typeAnalyzer.DATA_TYPES.ARRAY, _typeAnalyzer.DATA_TYPES.OBJECT, H3_ANALYZER_TYPE];
var IGNORE_DATA_TYPES = Object.keys(_typeAnalyzer.DATA_TYPES).filter(function (type) {
return !ACCEPTED_ANALYZER_TYPES.includes(type);
});
/**
* Getting sample data for analyzing field type.
*/
function getSampleForTypeAnalyze(_ref) {
var fields = _ref.fields,
rows = _ref.rows,
_ref$sampleCount = _ref.sampleCount,
sampleCount = _ref$sampleCount === void 0 ? 50 : _ref$sampleCount;
var total = Math.min(sampleCount, rows.length);
// const fieldOrder = fields.map(f => f.name);
var sample = (0, _d3Array.range)(0, total, 1).map(function () {
return {};
});
if (rows.length < 1) {
return [];
}
var isRowObject = !Array.isArray(rows[0]);
// collect sample data for each field
fields.forEach(function (field, fieldIdx) {
// row counter
var i = 0;
// sample counter
var j = 0;
while (j < total) {
if (i >= rows.length) {
// if depleted data pool
sample[j][field] = null;
j++;
} else if ((0, _data.notNullorUndefined)(rows[i][isRowObject ? field : fieldIdx])) {
var value = rows[i][isRowObject ? field : fieldIdx];
sample[j][field] = typeof value === 'string' ? value.trim() : value;
j++;
i++;
} else {
i++;
}
}
});
return sample;
}
/**
* Getting sample data for analyzing field type for Arrow tables.
* @param table Arrow table or an array of vectors.
* @param fields Field names.
* @param sampleCount Number of sample rows to get.
* @returns Sample rows.
*/
function getSampleForTypeAnalyzeArrow(table, fields) {
var sampleCount = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 50;
var isTable = !Array.isArray(table);
var numRows = isTable ? table.numRows : table[0].length;
var getVector = isTable ? function (index) {
return table.getChildAt(index);
} : function (index) {
return table[index];
};
var total = Math.min(sampleCount, numRows);
var sample = (0, _d3Array.range)(0, total, 1).map(function () {
return {};
});
if (numRows < 1) {
return [];
}
// collect sample data for each field
fields.forEach(function (field, fieldIdx) {
var rowIndex = 0;
var sampleIndex = 0;
while (sampleIndex < total) {
var _getVector;
if (rowIndex >= numRows) {
// if depleted data pool
sample[sampleIndex][field] = null;
sampleIndex++;
} else if ((0, _data.notNullorUndefined)((_getVector = getVector(fieldIdx)) === null || _getVector === void 0 ? void 0 : _getVector.get(rowIndex))) {
var _getVector2;
var value = (_getVector2 = getVector(fieldIdx)) === null || _getVector2 === void 0 ? void 0 : _getVector2.get(rowIndex);
sample[sampleIndex][field] = typeof value === 'string' ? value.trim() : value;
sampleIndex++;
rowIndex++;
} else {
rowIndex++;
}
}
});
return sample;
}
/**
* Convert type-analyzer output to kepler.gl field types
*
* @param aType
* @returns corresponding type in `ALL_FIELD_TYPES`
*/
/* eslint-disable complexity */
function analyzerTypeToFieldType(aType) {
var DATE = _typeAnalyzer.DATA_TYPES.DATE,
TIME = _typeAnalyzer.DATA_TYPES.TIME,
DATETIME = _typeAnalyzer.DATA_TYPES.DATETIME,
NUMBER = _typeAnalyzer.DATA_TYPES.NUMBER,
INT = _typeAnalyzer.DATA_TYPES.INT,
FLOAT = _typeAnalyzer.DATA_TYPES.FLOAT,
BOOLEAN = _typeAnalyzer.DATA_TYPES.BOOLEAN,
STRING = _typeAnalyzer.DATA_TYPES.STRING,
GEOMETRY = _typeAnalyzer.DATA_TYPES.GEOMETRY,
GEOMETRY_FROM_STRING = _typeAnalyzer.DATA_TYPES.GEOMETRY_FROM_STRING,
PAIR_GEOMETRY_FROM_STRING = _typeAnalyzer.DATA_TYPES.PAIR_GEOMETRY_FROM_STRING,
ZIPCODE = _typeAnalyzer.DATA_TYPES.ZIPCODE,
ARRAY = _typeAnalyzer.DATA_TYPES.ARRAY,
OBJECT = _typeAnalyzer.DATA_TYPES.OBJECT;
// TODO: un recognized types
// CURRENCY PERCENT NONE
switch (aType) {
case DATE:
return _constants.ALL_FIELD_TYPES.date;
case TIME:
case DATETIME:
return _constants.ALL_FIELD_TYPES.timestamp;
case FLOAT:
return _constants.ALL_FIELD_TYPES.real;
case INT:
return _constants.ALL_FIELD_TYPES.integer;
case BOOLEAN:
return _constants.ALL_FIELD_TYPES["boolean"];
case GEOMETRY:
case GEOMETRY_FROM_STRING:
case PAIR_GEOMETRY_FROM_STRING:
return _constants.ALL_FIELD_TYPES.geojson;
case ARRAY:
return _constants.ALL_FIELD_TYPES.array;
case OBJECT:
return _constants.ALL_FIELD_TYPES.object;
case NUMBER:
case STRING:
case ZIPCODE:
return _constants.ALL_FIELD_TYPES.string;
case H3_ANALYZER_TYPE:
return _constants.ALL_FIELD_TYPES.h3;
default:
_window.console.warn("Unsupported analyzer type: ".concat(aType));
return _constants.ALL_FIELD_TYPES.string;
}
}
/**
* Analyze field types from data in `string` format, e.g. uploaded csv.
* Assign `type`, `fieldIdx` and `format` (timestamp only) to each field
*
* @param data array of row object
* @param fieldOrder array of field names as string
* @returns formatted fields
* @public
* @example
*
* import {getFieldsFromData} from '@kepler.gl/common-utils';
* const data = [{
* time: '2016-09-17 00:09:55',
* value: '4',
* surge: '1.2',
* isTrip: 'true',
* zeroOnes: '0'
* }, {
* time: '2016-09-17 00:30:08',
* value: '3',
* surge: null,
* isTrip: 'false',
* zeroOnes: '1'
* }, {
* time: null,
* value: '2',
* surge: '1.3',
* isTrip: null,
* zeroOnes: '1'
* }];
*
* const fieldOrder = ['time', 'value', 'surge', 'isTrip', 'zeroOnes'];
* const fields = getFieldsFromData(data, fieldOrder);
* // fields = [
* // {name: 'time', format: 'YYYY-M-D H:m:s', fieldIdx: 1, type: 'timestamp'},
* // {name: 'value', format: '', fieldIdx: 4, type: 'integer'},
* // {name: 'surge', format: '', fieldIdx: 5, type: 'real'},
* // {name: 'isTrip', format: '', fieldIdx: 6, type: 'boolean'},
* // {name: 'zeroOnes', format: '', fieldIdx: 7, type: 'integer'}];
*
*/
function getFieldsFromData(data, fieldOrder) {
// add a check for epoch timestamp
var metadata = _typeAnalyzer.Analyzer.computeColMeta(data, [{
regex: /.*geojson|all_points/g,
dataType: 'GEOMETRY'
}, {
regex: /.*census/g,
dataType: 'STRING'
}], {
ignoredDataTypes: IGNORE_DATA_TYPES
});
var _renameDuplicateField = renameDuplicateFields(fieldOrder),
fieldByIndex = _renameDuplicateField.fieldByIndex;
var result = fieldOrder.map(function (field, index) {
var name = fieldByIndex[index];
var fieldMeta = metadata.find(function (m) {
return m.key === field;
});
// fieldMeta could be undefined if the field has no data and Analyzer.computeColMeta
// will ignore the field. In this case, we will simply assign the field type to STRING
// since dropping the column in the RowData could be expensive
var type = (fieldMeta === null || fieldMeta === void 0 ? void 0 : fieldMeta.type) || 'STRING';
var format = (fieldMeta === null || fieldMeta === void 0 ? void 0 : fieldMeta.format) || '';
// quick check if first valid string in column is H3
if (type === _typeAnalyzer.DATA_TYPES.STRING) {
for (var i = 0, n = data.length; i < n; ++i) {
if ((0, _data.notNullorUndefined)(data[i][name])) {
type = (0, _h3Utils.h3IsValid)(data[i][name] || '') ? H3_ANALYZER_TYPE : type;
break;
}
}
}
// quick check if string is hex wkb
if (type === _typeAnalyzer.DATA_TYPES.STRING) {
type = data.some(function (d) {
return (0, _data.isHexWkb)(d[name]);
}) ? _typeAnalyzer.DATA_TYPES.GEOMETRY : type;
}
return {
name: name,
id: name,
displayName: name,
format: format,
fieldIdx: index,
type: analyzerTypeToFieldType(type),
analyzerType: type,
valueAccessor: function valueAccessor(dc) {
return function (d) {
return dc.valueAt(d.index, index);
};
}
};
});
return result;
}
/**
* pass in an array of field names, rename duplicated one
* and return a map from old field index to new name
*
* @param fieldOrder
* @returns new field name by index
*/
function renameDuplicateFields(fieldOrder) {
return fieldOrder.reduce(function (accu, field, i) {
var allNames = accu.allNames;
var fieldName = field;
// add a counter to duplicated names
if (allNames.includes(field)) {
var counter = 0;
while (allNames.includes("".concat(field, "-").concat(counter))) {
counter++;
}
fieldName = "".concat(field, "-").concat(counter);
}
accu.fieldByIndex[i] = fieldName;
accu.allNames.push(fieldName);
return accu;
}, {
allNames: [],
fieldByIndex: []
});
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfdHlwZUFuYWx5emVyIiwicmVxdWlyZSIsIl9jb25zdGFudHMiLCJfd2luZG93IiwiX2QzQXJyYXkiLCJfZGF0YSIsIl9oM1V0aWxzIiwiSDNfQU5BTFlaRVJfVFlQRSIsIkFDQ0VQVEVEX0FOQUxZWkVSX1RZUEVTIiwiZXhwb3J0cyIsIkFuYWx5emVyREFUQV9UWVBFUyIsIkRBVEUiLCJUSU1FIiwiREFURVRJTUUiLCJOVU1CRVIiLCJJTlQiLCJGTE9BVCIsIkJPT0xFQU4iLCJTVFJJTkciLCJHRU9NRVRSWSIsIkdFT01FVFJZX0ZST01fU1RSSU5HIiwiUEFJUl9HRU9NRVRSWV9GUk9NX1NUUklORyIsIlpJUENPREUiLCJBUlJBWSIsIk9CSkVDVCIsIklHTk9SRV9EQVRBX1RZUEVTIiwiT2JqZWN0Iiwia2V5cyIsImZpbHRlciIsInR5cGUiLCJpbmNsdWRlcyIsImdldFNhbXBsZUZvclR5cGVBbmFseXplIiwiX3JlZiIsImZpZWxkcyIsInJvd3MiLCJfcmVmJHNhbXBsZUNvdW50Iiwic2FtcGxlQ291bnQiLCJ0b3RhbCIsIk1hdGgiLCJtaW4iLCJsZW5ndGgiLCJzYW1wbGUiLCJyYW5nZSIsIm1hcCIsImlzUm93T2JqZWN0IiwiQXJyYXkiLCJpc0FycmF5IiwiZm9yRWFjaCIsImZpZWxkIiwiZmllbGRJZHgiLCJpIiwiaiIsIm5vdE51bGxvclVuZGVmaW5lZCIsInZhbHVlIiwidHJpbSIsImdldFNhbXBsZUZvclR5cGVBbmFseXplQXJyb3ciLCJ0YWJsZSIsImFyZ3VtZW50cyIsInVuZGVmaW5lZCIsImlzVGFibGUiLCJudW1Sb3dzIiwiZ2V0VmVjdG9yIiwiaW5kZXgiLCJnZXRDaGlsZEF0Iiwicm93SW5kZXgiLCJzYW1wbGVJbmRleCIsIl9nZXRWZWN0b3IiLCJnZXQiLCJfZ2V0VmVjdG9yMiIsImFuYWx5emVyVHlwZVRvRmllbGRUeXBlIiwiYVR5cGUiLCJBTExfRklFTERfVFlQRVMiLCJkYXRlIiwidGltZXN0YW1wIiwicmVhbCIsImludGVnZXIiLCJnZW9qc29uIiwiYXJyYXkiLCJvYmplY3QiLCJzdHJpbmciLCJoMyIsImdsb2JhbENvbnNvbGUiLCJ3YXJuIiwiY29uY2F0IiwiZ2V0RmllbGRzRnJvbURhdGEiLCJkYXRhIiwiZmllbGRPcmRlciIsIm1ldGFkYXRhIiwiQW5hbHl6ZXIiLCJjb21wdXRlQ29sTWV0YSIsInJlZ2V4IiwiZGF0YVR5cGUiLCJpZ25vcmVkRGF0YVR5cGVzIiwiX3JlbmFtZUR1cGxpY2F0ZUZpZWxkIiwicmVuYW1lRHVwbGljYXRlRmllbGRzIiwiZmllbGRCeUluZGV4IiwicmVzdWx0IiwibmFtZSIsImZpZWxkTWV0YSIsImZpbmQiLCJtIiwia2V5IiwiZm9ybWF0IiwibiIsImgzSXNWYWxpZCIsInNvbWUiLCJkIiwiaXNIZXhXa2IiLCJpZCIsImRpc3BsYXlOYW1lIiwiYW5hbHl6ZXJUeXBlIiwidmFsdWVBY2Nlc3NvciIsImRjIiwidmFsdWVBdCIsInJlZHVjZSIsImFjY3UiLCJhbGxOYW1lcyIsImZpZWxkTmFtZSIsImNvdW50ZXIiLCJwdXNoIl0sInNvdXJjZXMiOlsiLi4vc3JjL2RhdGEtdHlwZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogTUlUXG4vLyBDb3B5cmlnaHQgY29udHJpYnV0b3JzIHRvIHRoZSBrZXBsZXIuZ2wgcHJvamVjdFxuXG5pbXBvcnQge0FuYWx5emVyLCBEQVRBX1RZUEVTIGFzIEFuYWx5emVyREFUQV9UWVBFU30gZnJvbSAndHlwZS1hbmFseXplcic7XG5pbXBvcnQge0Fycm93VGFibGVJbnRlcmZhY2UsIEFwYWNoZVZlY3RvckludGVyZmFjZSwgUm93RGF0YSwgRmllbGR9IGZyb20gJ0BrZXBsZXIuZ2wvdHlwZXMnO1xuaW1wb3J0IHtBTExfRklFTERfVFlQRVN9IGZyb20gJ0BrZXBsZXIuZ2wvY29uc3RhbnRzJztcbmltcG9ydCB7Y29uc29sZSBhcyBnbG9iYWxDb25zb2xlfSBmcm9tICdnbG9iYWwvd2luZG93JztcbmltcG9ydCB7cmFuZ2V9IGZyb20gJ2QzLWFycmF5JztcbmltcG9ydCB7aXNIZXhXa2IsIG5vdE51bGxvclVuZGVmaW5lZH0gZnJvbSAnLi9kYXRhJztcbmltcG9ydCB7aDNJc1ZhbGlkfSBmcm9tICcuL2gzLXV0aWxzJztcblxuY29uc3QgSDNfQU5BTFlaRVJfVFlQRSA9ICdIMyc7XG5cbmV4cG9ydCBjb25zdCBBQ0NFUFRFRF9BTkFMWVpFUl9UWVBFUyA9IFtcbiAgQW5hbHl6ZXJEQVRBX1RZUEVTLkRBVEUsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5USU1FLFxuICBBbmFseXplckRBVEFfVFlQRVMuREFURVRJTUUsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5OVU1CRVIsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5JTlQsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5GTE9BVCxcbiAgQW5hbHl6ZXJEQVRBX1RZUEVTLkJPT0xFQU4sXG4gIEFuYWx5emVyREFUQV9UWVBFUy5TVFJJTkcsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5HRU9NRVRSWSxcbiAgQW5hbHl6ZXJEQVRBX1RZUEVTLkdFT01FVFJZX0ZST01fU1RSSU5HLFxuICBBbmFseXplckRBVEFfVFlQRVMuUEFJUl9HRU9NRVRSWV9GUk9NX1NUUklORyxcbiAgQW5hbHl6ZXJEQVRBX1RZUEVTLlpJUENPREUsXG4gIEFuYWx5emVyREFUQV9UWVBFUy5BUlJBWSxcbiAgQW5hbHl6ZXJEQVRBX1RZUEVTLk9CSkVDVCxcbiAgSDNfQU5BTFlaRVJfVFlQRVxuXTtcblxuY29uc3QgSUdOT1JFX0RBVEFfVFlQRVMgPSBPYmplY3Qua2V5cyhBbmFseXplckRBVEFfVFlQRVMpLmZpbHRlcihcbiAgdHlwZSA9PiAhQUNDRVBURURfQU5BTFlaRVJfVFlQRVMuaW5jbHVkZXModHlwZSlcbik7XG5cbi8qKlxuICogR2V0dGluZyBzYW1wbGUgZGF0YSBmb3IgYW5hbHl6aW5nIGZpZWxkIHR5cGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRTYW1wbGVGb3JUeXBlQW5hbHl6ZSh7XG4gIGZpZWxkcyxcbiAgcm93cyxcbiAgc2FtcGxlQ291bnQgPSA1MFxufToge1xuICBmaWVsZHM6IHN0cmluZ1tdO1xuICByb3dzOiB1bmtub3duW11bXSB8IFJvd0RhdGE7XG4gIHNhbXBsZUNvdW50PzogbnVtYmVyO1xufSk6IFJvd0RhdGEge1xuICBjb25zdCB0b3RhbCA9IE1hdGgubWluKHNhbXBsZUNvdW50LCByb3dzLmxlbmd0aCk7XG4gIC8vIGNvbnN0IGZpZWxkT3JkZXIgPSBmaWVsZHMubWFwKGYgPT4gZi5uYW1lKTtcbiAgY29uc3Qgc2FtcGxlID0gcmFuZ2UoMCwgdG90YWwsIDEpLm1hcCgoKSA9PiAoe30pKTtcblxuICBpZiAocm93cy5sZW5ndGggPCAxKSB7XG4gICAgcmV0dXJuIFtdO1xuICB9XG4gIGNvbnN0IGlzUm93T2JqZWN0ID0gIUFycmF5LmlzQXJyYXkocm93c1swXSk7XG5cbiAgLy8gY29sbGVjdCBzYW1wbGUgZGF0YSBmb3IgZWFjaCBmaWVsZFxuICBmaWVsZHMuZm9yRWFjaCgoZmllbGQsIGZpZWxkSWR4KSA9PiB7XG4gICAgLy8gcm93IGNvdW50ZXJcbiAgICBsZXQgaSA9IDA7XG4gICAgLy8gc2FtcGxlIGNvdW50ZXJcbiAgICBsZXQgaiA9IDA7XG5cbiAgICB3aGlsZSAoaiA8IHRvdGFsKSB7XG4gICAgICBpZiAoaSA+PSByb3dzLmxlbmd0aCkge1xuICAgICAgICAvLyBpZiBkZXBsZXRlZCBkYXRhIHBvb2xcbiAgICAgICAgc2FtcGxlW2pdW2ZpZWxkXSA9IG51bGw7XG4gICAgICAgIGorKztcbiAgICAgIH0gZWxzZSBpZiAobm90TnVsbG9yVW5kZWZpbmVkKHJvd3NbaV1baXNSb3dPYmplY3QgPyBmaWVsZCA6IGZpZWxkSWR4XSkpIHtcbiAgICAgICAgY29uc3QgdmFsdWUgPSByb3dzW2ldW2lzUm93T2JqZWN0ID8gZmllbGQgOiBmaWVsZElkeF07XG4gICAgICAgIHNhbXBsZVtqXVtmaWVsZF0gPSB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgIGorKztcbiAgICAgICAgaSsrO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaSsrO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHNhbXBsZTtcbn1cblxuLyoqXG4gKiBHZXR0aW5nIHNhbXBsZSBkYXRhIGZvciBhbmFseXppbmcgZmllbGQgdHlwZSBmb3IgQXJyb3cgdGFibGVzLlxuICogQHBhcmFtIHRhYmxlIEFycm93IHRhYmxlIG9yIGFuIGFycmF5IG9mIHZlY3RvcnMuXG4gKiBAcGFyYW0gZmllbGRzIEZpZWxkIG5hbWVzLlxuICogQHBhcmFtIHNhbXBsZUNvdW50IE51bWJlciBvZiBzYW1wbGUgcm93cyB0byBnZXQuXG4gKiBAcmV0dXJucyBTYW1wbGUgcm93cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGdldFNhbXBsZUZvclR5cGVBbmFseXplQXJyb3coXG4gIHRhYmxlOiBBcnJvd1RhYmxlSW50ZXJmYWNlIHwgQXBhY2hlVmVjdG9ySW50ZXJmYWNlW10sXG4gIGZpZWxkczogc3RyaW5nW10sXG4gIHNhbXBsZUNvdW50ID0gNTBcbik6IGFueVtdIHtcbiAgY29uc3QgaXNUYWJsZSA9ICFBcnJheS5pc0FycmF5KHRhYmxlKTtcblxuICBjb25zdCBudW1Sb3dzID0gaXNUYWJsZSA/IHRhYmxlLm51bVJvd3MgOiB0YWJsZVswXS5sZW5ndGg7XG4gIGNvbnN0IGdldFZlY3RvciA9IGlzVGFibGUgPyBpbmRleCA9PiB0YWJsZS5nZXRDaGlsZEF0KGluZGV4KSA6IGluZGV4ID0+IHRhYmxlW2luZGV4XTtcblxuICBjb25zdCB0b3RhbCA9IE1hdGgubWluKHNhbXBsZUNvdW50LCBudW1Sb3dzKTtcbiAgY29uc3Qgc2FtcGxlID0gcmFuZ2UoMCwgdG90YWwsIDEpLm1hcCgoKSA9PiAoe30pKTtcblxuICBpZiAobnVtUm93cyA8IDEpIHtcbiAgICByZXR1cm4gW107XG4gIH1cblxuICAvLyBjb2xsZWN0IHNhbXBsZSBkYXRhIGZvciBlYWNoIGZpZWxkXG4gIGZpZWxkcy5mb3JFYWNoKChmaWVsZCwgZmllbGRJZHgpID0+IHtcbiAgICBsZXQgcm93SW5kZXggPSAwO1xuICAgIGxldCBzYW1wbGVJbmRleCA9IDA7XG5cbiAgICB3aGlsZSAoc2FtcGxlSW5kZXggPCB0b3RhbCkge1xuICAgICAgaWYgKHJvd0luZGV4ID49IG51bVJvd3MpIHtcbiAgICAgICAgLy8gaWYgZGVwbGV0ZWQgZGF0YSBwb29sXG4gICAgICAgIHNhbXBsZVtzYW1wbGVJbmRleF1bZmllbGRdID0gbnVsbDtcbiAgICAgICAgc2FtcGxlSW5kZXgrKztcbiAgICAgIH0gZWxzZSBpZiAobm90TnVsbG9yVW5kZWZpbmVkKGdldFZlY3RvcihmaWVsZElkeCk/LmdldChyb3dJbmRleCkpKSB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gZ2V0VmVjdG9yKGZpZWxkSWR4KT8uZ2V0KHJvd0luZGV4KTtcbiAgICAgICAgc2FtcGxlW3NhbXBsZUluZGV4XVtmaWVsZF0gPSB0eXBlb2YgdmFsdWUgPT09ICdzdHJpbmcnID8gdmFsdWUudHJpbSgpIDogdmFsdWU7XG4gICAgICAgIHNhbXBsZUluZGV4Kys7XG4gICAgICAgIHJvd0luZGV4Kys7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICByb3dJbmRleCsrO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgcmV0dXJuIHNhbXBsZTtcbn1cblxuLyoqXG4gKiBDb252ZXJ0IHR5cGUtYW5hbHl6ZXIgb3V0cHV0IHRvIGtlcGxlci5nbCBmaWVsZCB0eXBlc1xuICpcbiAqIEBwYXJhbSBhVHlwZVxuICogQHJldHVybnMgY29ycmVzcG9uZGluZyB0eXBlIGluIGBBTExfRklFTERfVFlQRVNgXG4gKi9cbi8qIGVzbGludC1kaXNhYmxlIGNvbXBsZXhpdHkgKi9cbmV4cG9ydCBmdW5jdGlvbiBhbmFseXplclR5cGVUb0ZpZWxkVHlwZShhVHlwZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3Qge1xuICAgIERBVEUsXG4gICAgVElNRSxcbiAgICBEQVRFVElNRSxcbiAgICBOVU1CRVIsXG4gICAgSU5ULFxuICAgIEZMT0FULFxuICAgIEJPT0xFQU4sXG4gICAgU1RSSU5HLFxuICAgIEdFT01FVFJZLFxuICAgIEdFT01FVFJZX0ZST01fU1RSSU5HLFxuICAgIFBBSVJfR0VPTUVUUllfRlJPTV9TVFJJTkcsXG4gICAgWklQQ09ERSxcbiAgICBBUlJBWSxcbiAgICBPQkpFQ1RcbiAgfSA9IEFuYWx5emVyREFUQV9UWVBFUztcblxuICAvLyBUT0RPOiB1biByZWNvZ25pemVkIHR5cGVzXG4gIC8vIENVUlJFTkNZIFBFUkNFTlQgTk9ORVxuICBzd2l0Y2ggKGFUeXBlKSB7XG4gICAgY2FzZSBEQVRFOlxuICAgICAgcmV0dXJuIEFMTF9GSUVMRF9UWVBFUy5kYXRlO1xuICAgIGNhc2UgVElNRTpcbiAgICBjYXNlIERBVEVUSU1FOlxuICAgICAgcmV0dXJuIEFMTF9GSUVMRF9UWVBFUy50aW1lc3RhbXA7XG4gICAgY2FzZSBGTE9BVDpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMucmVhbDtcbiAgICBjYXNlIElOVDpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMuaW50ZWdlcjtcbiAgICBjYXNlIEJPT0xFQU46XG4gICAgICByZXR1cm4gQUxMX0ZJRUxEX1RZUEVTLmJvb2xlYW47XG4gICAgY2FzZSBHRU9NRVRSWTpcbiAgICBjYXNlIEdFT01FVFJZX0ZST01fU1RSSU5HOlxuICAgIGNhc2UgUEFJUl9HRU9NRVRSWV9GUk9NX1NUUklORzpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMuZ2VvanNvbjtcbiAgICBjYXNlIEFSUkFZOlxuICAgICAgcmV0dXJuIEFMTF9GSUVMRF9UWVBFUy5hcnJheTtcbiAgICBjYXNlIE9CSkVDVDpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMub2JqZWN0O1xuICAgIGNhc2UgTlVNQkVSOlxuICAgIGNhc2UgU1RSSU5HOlxuICAgIGNhc2UgWklQQ09ERTpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMuc3RyaW5nO1xuICAgIGNhc2UgSDNfQU5BTFlaRVJfVFlQRTpcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMuaDM7XG4gICAgZGVmYXVsdDpcbiAgICAgIGdsb2JhbENvbnNvbGUud2FybihgVW5zdXBwb3J0ZWQgYW5hbHl6ZXIgdHlwZTogJHthVHlwZX1gKTtcbiAgICAgIHJldHVybiBBTExfRklFTERfVFlQRVMuc3RyaW5nO1xuICB9XG59XG5cbi8qKlxuICogQW5hbHl6ZSBmaWVsZCB0eXBlcyBmcm9tIGRhdGEgaW4gYHN0cmluZ2AgZm9ybWF0LCBlLmcuIHVwbG9hZGVkIGNzdi5cbiAqIEFzc2lnbiBgdHlwZWAsIGBmaWVsZElkeGAgYW5kIGBmb3JtYXRgICh0aW1lc3RhbXAgb25seSkgdG8gZWFjaCBmaWVsZFxuICpcbiAqIEBwYXJhbSBkYXRhIGFycmF5IG9mIHJvdyBvYmplY3RcbiAqIEBwYXJhbSBmaWVsZE9yZGVyIGFycmF5IG9mIGZpZWxkIG5hbWVzIGFzIHN0cmluZ1xuICogQHJldHVybnMgZm9ybWF0dGVkIGZpZWxkc1xuICogQHB1YmxpY1xuICogQGV4YW1wbGVcbiAqXG4gKiBpbXBvcnQge2dldEZpZWxkc0Zyb21EYXRhfSBmcm9tICdAa2VwbGVyLmdsL2NvbW1vbi11dGlscyc7XG4gKiBjb25zdCBkYXRhID0gW3tcbiAqICAgdGltZTogJzIwMTYtMDktMTcgMDA6MDk6NTUnLFxuICogICB2YWx1ZTogJzQnLFxuICogICBzdXJnZTogJzEuMicsXG4gKiAgIGlzVHJpcDogJ3RydWUnLFxuICogICB6ZXJvT25lczogJzAnXG4gKiB9LCB7XG4gKiAgIHRpbWU6ICcyMDE2LTA5LTE3IDAwOjMwOjA4JyxcbiAqICAgdmFsdWU6ICczJyxcbiAqICAgc3VyZ2U6IG51bGwsXG4gKiAgIGlzVHJpcDogJ2ZhbHNlJyxcbiAqICAgemVyb09uZXM6ICcxJ1xuICogfSwge1xuICogICB0aW1lOiBudWxsLFxuICogICB2YWx1ZTogJzInLFxuICogICBzdXJnZTogJzEuMycsXG4gKiAgIGlzVHJpcDogbnVsbCxcbiAqICAgemVyb09uZXM6ICcxJ1xuICogfV07XG4gKlxuICogY29uc3QgZmllbGRPcmRlciA9IFsndGltZScsICd2YWx1ZScsICdzdXJnZScsICdpc1RyaXAnLCAnemVyb09uZXMnXTtcbiAqIGNvbnN0IGZpZWxkcyA9IGdldEZpZWxkc0Zyb21EYXRhKGRhdGEsIGZpZWxkT3JkZXIpO1xuICogLy8gZmllbGRzID0gW1xuICogLy8ge25hbWU6ICd0aW1lJywgZm9ybWF0OiAnWVlZWS1NLUQgSDptOnMnLCBmaWVsZElkeDogMSwgdHlwZTogJ3RpbWVzdGFtcCd9LFxuICogLy8ge25hbWU6ICd2YWx1ZScsIGZvcm1hdDogJycsIGZpZWxkSWR4OiA0LCB0eXBlOiAnaW50ZWdlcid9LFxuICogLy8ge25hbWU6ICdzdXJnZScsIGZvcm1hdDogJycsIGZpZWxkSWR4OiA1LCB0eXBlOiAncmVhbCd9LFxuICogLy8ge25hbWU6ICdpc1RyaXAnLCBmb3JtYXQ6ICcnLCBmaWVsZElkeDogNiwgdHlwZTogJ2Jvb2xlYW4nfSxcbiAqIC8vIHtuYW1lOiAnemVyb09uZXMnLCBmb3JtYXQ6ICcnLCBmaWVsZElkeDogNywgdHlwZTogJ2ludGVnZXInfV07XG4gKlxuICovXG5leHBvcnQgZnVuY3Rpb24gZ2V0RmllbGRzRnJvbURhdGEoZGF0YTogUm93RGF0YSwgZmllbGRPcmRlcjogc3RyaW5nW10pOiBGaWVsZFtdIHtcbiAgLy8gYWRkIGEgY2hlY2sgZm9yIGVwb2NoIHRpbWVzdGFtcFxuICBjb25zdCBtZXRhZGF0YSA9IEFuYWx5emVyLmNvbXB1dGVDb2xNZXRhKFxuICAgIGRhdGEsXG4gICAgW1xuICAgICAge3JlZ2V4OiAvLipnZW9qc29ufGFsbF9wb2ludHMvZywgZGF0YVR5cGU6ICdHRU9NRVRSWSd9LFxuICAgICAge3JlZ2V4OiAvLipjZW5zdXMvZywgZGF0YVR5cGU6ICdTVFJJTkcnfVxuICAgIF0sXG4gICAge2lnbm9yZWREYXRhVHlwZXM6IElHTk9SRV9EQVRBX1RZUEVTfVxuICApO1xuXG4gIGNvbnN0IHtmaWVsZEJ5SW5kZXh9ID0gcmVuYW1lRHVwbGljYXRlRmllbGRzKGZpZWxkT3JkZXIpO1xuXG4gIGNvbnN0IHJlc3VsdCA9IGZpZWxkT3JkZXIubWFwKChmaWVsZCwgaW5kZXgpID0+IHtcbiAgICBjb25zdCBuYW1lID0gZmllbGRCeUluZGV4W2luZGV4XTtcblxuICAgIGNvbnN0IGZpZWxkTWV0YSA9IG1ldGFkYXRhLmZpbmQobSA9PiBtLmtleSA9PT0gZmllbGQpO1xuXG4gICAgLy8gZmllbGRNZXRhIGNvdWxkIGJlIHVuZGVmaW5lZCBpZiB0aGUgZmllbGQgaGFzIG5vIGRhdGEgYW5kIEFuYWx5emVyLmNvbXB1dGVDb2xNZXRhXG4gICAgLy8gd2lsbCBpZ25vcmUgdGhlIGZpZWxkLiBJbiB0aGlzIGNhc2UsIHdlIHdpbGwgc2ltcGx5IGFzc2lnbiB0aGUgZmllbGQgdHlwZSB0byBTVFJJTkdcbiAgICAvLyBzaW5jZSBkcm9wcGluZyB0aGUgY29sdW1uIGluIHRoZSBSb3dEYXRhIGNvdWxkIGJlIGV4cGVuc2l2ZVxuICAgIGxldCB0eXBlID0gZmllbGRNZXRhPy50eXBlIHx8ICdTVFJJTkcnO1xuICAgIGNvbnN0IGZvcm1hdCA9IGZpZWxkTWV0YT8uZm9ybWF0IHx8ICcnO1xuXG4gICAgLy8gcXVpY2sgY2hlY2sgaWYgZmlyc3QgdmFsaWQgc3RyaW5nIGluIGNvbHVtbiBpcyBIM1xuICAgIGlmICh0eXBlID09PSBBbmFseXplckRBVEFfVFlQRVMuU1RSSU5HKSB7XG4gICAgICBmb3IgKGxldCBpID0gMCwgbiA9IGRhdGEubGVuZ3RoOyBpIDwgbjsgKytpKSB7XG4gICAgICAgIGlmIChub3ROdWxsb3JVbmRlZmluZWQoZGF0YVtpXVtuYW1lXSkpIHtcbiAgICAgICAgICB0eXBlID0gaDNJc1ZhbGlkKGRhdGFbaV1bbmFtZV0gfHwgJycpID8gSDNfQU5BTFlaRVJfVFlQRSA6IHR5cGU7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICAvLyBxdWljayBjaGVjayBpZiBzdHJpbmcgaXMgaGV4IHdrYlxuICAgIGlmICh0eXBlID09PSBBbmFseXplckRBVEFfVFlQRVMuU1RSSU5HKSB7XG4gICAgICB0eXBlID0gZGF0YS5zb21lKGQgPT4gaXNIZXhXa2IoZFtuYW1lXSkpID8gQW5hbHl6ZXJEQVRBX1RZUEVTLkdFT01FVFJZIDogdHlwZTtcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgbmFtZSxcbiAgICAgIGlkOiBuYW1lLFxuICAgICAgZGlzcGxheU5hbWU6IG5hbWUsXG4gICAgICBmb3JtYXQsXG4gICAgICBmaWVsZElkeDogaW5kZXgsXG4gICAgICB0eXBlOiBhbmFseXplclR5cGVUb0ZpZWxkVHlwZSh0eXBlKSxcbiAgICAgIGFuYWx5emVyVHlwZTogdHlwZSxcbiAgICAgIHZhbHVlQWNjZXNzb3I6IGRjID0+IGQgPT4ge1xuICAgICAgICByZXR1cm4gZGMudmFsdWVBdChkLmluZGV4LCBpbmRleCk7XG4gICAgICB9XG4gICAgfTtcbiAgfSk7XG5cbiAgcmV0dXJuIHJlc3VsdDtcbn1cblxuLyoqXG4gKiBwYXNzIGluIGFuIGFycmF5IG9mIGZpZWxkIG5hbWVzLCByZW5hbWUgZHVwbGljYXRlZCBvbmVcbiAqIGFuZCByZXR1cm4gYSBtYXAgZnJvbSBvbGQgZmllbGQgaW5kZXggdG8gbmV3IG5hbWVcbiAqXG4gKiBAcGFyYW0gZmllbGRPcmRlclxuICogQHJldHVybnMgbmV3IGZpZWxkIG5hbWUgYnkgaW5kZXhcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJlbmFtZUR1cGxpY2F0ZUZpZWxkcyhmaWVsZE9yZGVyOiBzdHJpbmdbXSk6IHtcbiAgYWxsTmFtZXM6IHN0cmluZ1tdO1xuICBmaWVsZEJ5SW5kZXg6IHN0cmluZ1tdO1xufSB7XG4gIHJldHVybiBmaWVsZE9yZGVyLnJlZHVjZTx7YWxsTmFtZXM6IHN0cmluZ1tdOyBmaWVsZEJ5SW5kZXg6IHN0cmluZ1tdfT4oXG4gICAgKGFjY3UsIGZpZWxkLCBpKSA9PiB7XG4gICAgICBjb25zdCB7YWxsTmFtZXN9ID0gYWNjdTtcbiAgICAgIGxldCBmaWVsZE5hbWUgPSBmaWVsZDtcblxuICAgICAgLy8gYWRkIGEgY291bnRlciB0byBkdXBsaWNhdGVkIG5hbWVzXG4gICAgICBpZiAoYWxsTmFtZXMuaW5jbHVkZXMoZmllbGQpKSB7XG4gICAgICAgIGxldCBjb3VudGVyID0gMDtcbiAgICAgICAgd2hpbGUgKGFsbE5hbWVzLmluY2x1ZGVzKGAke2ZpZWxkfS0ke2NvdW50ZXJ9YCkpIHtcbiAgICAgICAgICBjb3VudGVyKys7XG4gICAgICAgIH1cbiAgICAgICAgZmllbGROYW1lID0gYCR7ZmllbGR9LSR7Y291bnRlcn1gO1xuICAgICAgfVxuXG4gICAgICBhY2N1LmZpZWxkQnlJbmRleFtpXSA9IGZpZWxkTmFtZTtcbiAgICAgIGFjY3UuYWxsTmFtZXMucHVzaChmaWVsZE5hbWUpO1xuXG4gICAgICByZXR1cm4gYWNjdTtcbiAgICB9LFxuICAgIHthbGxOYW1lczogW10sIGZpZWxkQnlJbmRleDogW119XG4gICk7XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O0FBR0EsSUFBQUEsYUFBQSxHQUFBQyxPQUFBO0FBRUEsSUFBQUMsVUFBQSxHQUFBRCxPQUFBO0FBQ0EsSUFBQUUsT0FBQSxHQUFBRixPQUFBO0FBQ0EsSUFBQUcsUUFBQSxHQUFBSCxPQUFBO0FBQ0EsSUFBQUksS0FBQSxHQUFBSixPQUFBO0FBQ0EsSUFBQUssUUFBQSxHQUFBTCxPQUFBO0FBVEE7QUFDQTs7QUFVQSxJQUFNTSxnQkFBZ0IsR0FBRyxJQUFJO0FBRXRCLElBQU1DLHVCQUF1QixHQUFBQyxPQUFBLENBQUFELHVCQUFBLEdBQUcsQ0FDckNFLHdCQUFrQixDQUFDQyxJQUFJLEVBQ3ZCRCx3QkFBa0IsQ0FBQ0UsSUFBSSxFQUN2QkYsd0JBQWtCLENBQUNHLFFBQVEsRUFDM0JILHdCQUFrQixDQUFDSSxNQUFNLEVBQ3pCSix3QkFBa0IsQ0FBQ0ssR0FBRyxFQUN0Qkwsd0JBQWtCLENBQUNNLEtBQUssRUFDeEJO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
;