metaapi.cloud-sdk
Version:
SDK for MetaApi, a professional cloud forex API which includes MetaTrader REST API and MetaTrader websocket API. Supports both MetaTrader 5 (MT5) and MetaTrader 4 (MT4). CopyFactory copy trading API included. (https://metaapi.cloud)
331 lines (330 loc) • 43 kB
JavaScript
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
Promise.resolve(value).then(_next, _throw);
}
}
function _async_to_generator(fn) {
return function() {
var self = this, args = arguments;
return new Promise(function(resolve, reject) {
var gen = fn.apply(self, args);
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
}
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
}
_next(undefined);
});
};
}
import crypto from 'crypto-js';
import ReferenceTree from './referenceTree';
let TerminalHashManager = class TerminalHashManager {
/**
* Refreshes hashing ignored field lists
* @param {String} region account region
* @returns {Promise} promise resolving when the hashing field lists are updated.
*/ refreshIgnoredFieldLists(region) {
var _this = this;
return _async_to_generator(function*() {
yield _this._clientApiClient.refreshIgnoredFieldLists(region);
})();
}
/**
* Returns specifications data by hash
* @param {string} specificationsHash specifications hash
* @returns {[id: string]: MetatraderSymbolSpecification}
*/ getSpecificationsByHash(specificationsHash) {
return this._specificationsTree.getItemsByHash(specificationsHash);
}
/**
* Returns specifications hash data by hash
* @param {string} specificationsHash specifications hash
* @returns {[id: string]: string}
*/ getSpecificationsHashesByHash(specificationsHash) {
return this._specificationsTree.getHashesByHash(specificationsHash);
}
/**
* Returns positions data by hash
* @param {string} positionsHash positions hash
* @returns {[id: string]: MetatraderPosition}
*/ getPositionsByHash(positionsHash) {
return this._positionsTree.getItemsByHash(positionsHash);
}
/**
* Returns positions hash data by hash
* @param {string} positionsHash positions hash
* @returns {[id: string]: string} dictionary of position hashes
*/ getPositionsHashesByHash(positionsHash) {
return this._positionsTree.getHashesByHash(positionsHash);
}
/**
* Returns orders data by hash
* @param {string} ordersHash orders hash
* @returns {[id: string]: MetatraderOrder} removed position ids
*/ getOrdersByHash(ordersHash) {
return this._ordersTree.getItemsByHash(ordersHash);
}
/**
* Returns orders hash data by hash
* @param {string} ordersHash orders hash
* @returns {[id: string]: string} dictionary of order hashes
*/ getOrdersHashesByHash(ordersHash) {
return this._ordersTree.getHashesByHash(ordersHash);
}
/**
* Creates an entry for specification data and returns hash
* @param {string} serverName broker server name
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {MetatraderSymbolSpecification[]} specifications specifications array
* @returns {string} dictionary hash
*/ recordSpecifications(serverName, accountType, connectionId, instanceIndex, specifications) {
return this._specificationsTree.recordItems(serverName, accountType, connectionId, instanceIndex, specifications);
}
/**
* Updates specification data
* @param {string} serverName broker server name
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {MetatraderSymbolSpecification[]} specifications specifications array
* @param {string[]} removedSymbols removed specification symbols
* @param {string} parentHash parent hash
* @returns {string} updated dictionary hash
*/ // eslint-disable-next-line complexity
updateSpecifications(serverName, accountType, connectionId, instanceIndex, specifications, removedSymbols, parentHash) {
return this._specificationsTree.updateItems(serverName, accountType, connectionId, instanceIndex, specifications, removedSymbols, parentHash);
}
/**
* Creates an entry for positions data and returns hash
* @param {string} accountId account id
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {MetatraderPosition[]} positions positions array
* @returns {string} dictionary hash
*/ recordPositions(accountId, accountType, connectionId, instanceIndex, positions) {
return this._positionsTree.recordItems(accountId, accountType, connectionId, instanceIndex, positions);
}
/**
* Updates positions data
* @param {string} accountId account id
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {MetatraderPosition[]} positions positions
* @param {string[]} removedPositions removed position ids
* @param {string} parentHash parent hash
* @returns {string} updated dictionary hash
*/ updatePositions(accountId, accountType, connectionId, instanceIndex, positions, removedPositions, parentHash) {
return this._positionsTree.updateItems(accountId, accountType, connectionId, instanceIndex, positions, removedPositions, parentHash);
}
/**
* Creates an entry for orders data and returns hash
* @param {string} accountId account id
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {Array<MetatraderOrder>} orders orders array
* @returns {string} dictionary hash
*/ recordOrders(accountId, accountType, connectionId, instanceIndex, orders) {
return this._ordersTree.recordItems(accountId, accountType, connectionId, instanceIndex, orders);
}
/**
* Updates orders data
* @param {string} accountId account id
* @param {string} accountType account type
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
* @param {MetatraderOrder[]} orders orders array
* @param {string[]} completedOrders completed order ids
* @param {string} parentHash parent hash
* @returns {string} updated dictionary hash
*/ updateOrders(accountId, accountType, connectionId, instanceIndex, orders, completedOrders, parentHash) {
return this._ordersTree.updateItems(accountId, accountType, connectionId, instanceIndex, orders, completedOrders, parentHash);
}
/**
* Returns the list of last used specification hashes, with specified server hashes prioritized
* @param {string} serverName server name
* @returns {string[]} last used specification hashes
*/ getLastUsedSpecificationHashes(serverName) {
return this._specificationsTree.getLastUsedHashes(serverName);
}
/**
* Returns the list of last used position hashes
* @param {string} accountId account id
* @returns {string[]} last used position hashes
*/ getLastUsedPositionHashes(accountId) {
return this._positionsTree.getLastUsedHashes(accountId);
}
/**
* Returns the list of last used order hashes
* @param {string} accountId account id
* @returns {string[]} last used order hashes
*/ getLastUsedOrderHashes(accountId) {
return this._ordersTree.getLastUsedHashes(accountId);
}
/**
* Removes all references for a connection
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ removeConnectionReferences(connectionId, instanceIndex) {
this.removeSpecificationReference(connectionId, instanceIndex);
this.removePositionReference(connectionId, instanceIndex);
this.removeOrderReference(connectionId, instanceIndex);
}
/**
* Adds a reference from a terminal state instance index to a specifications hash
* @param {string} hash specifications hash
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ addSpecificationReference(hash, connectionId, instanceIndex) {
this._specificationsTree.addReference(hash, connectionId, instanceIndex);
}
/**
* Removes a reference from a terminal state instance index to a specifications hash
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ removeSpecificationReference(connectionId, instanceIndex) {
this._specificationsTree.removeReference(connectionId, instanceIndex);
}
/**
* Adds a reference from a terminal state instance index to a positions hash
* @param {string} hash positions hash
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ addPositionReference(hash, connectionId, instanceIndex) {
this._positionsTree.addReference(hash, connectionId, instanceIndex);
}
/**
* Removes a reference from a terminal state instance index to a positions hash
* @param {string} accountId account id
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ removePositionReference(connectionId, instanceIndex) {
this._positionsTree.removeReference(connectionId, instanceIndex);
}
/**
* Adds a reference from a terminal state instance index to a orders hash
* @param {string} hash positions hash
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ addOrderReference(hash, connectionId, instanceIndex) {
this._ordersTree.addReference(hash, connectionId, instanceIndex);
}
/**
* Removes a reference from a terminal state instance index to a orders hash
* @param {string} connectionId connection id
* @param {string} instanceIndex instance index
*/ removeOrderReference(connectionId, instanceIndex) {
this._ordersTree.removeReference(connectionId, instanceIndex);
}
// eslint-disable-next-line complexity
getItemHash(item, type, accountType, region) {
const hashFields = this._clientApiClient.getHashingIgnoredFieldLists(region);
item = Object.assign({}, item);
switch(type){
case 'specifications':
if (accountType === 'cloud-g1') {
hashFields.g1.specification.forEach((field)=>delete item[field]);
} else if (accountType === 'cloud-g2') {
hashFields.g2.specification.forEach((field)=>delete item[field]);
}
return this._getHash(item, accountType, [
'digits'
]);
case 'positions':
if (accountType === 'cloud-g1') {
hashFields.g1.position.forEach((field)=>delete item[field]);
} else if (accountType === 'cloud-g2') {
hashFields.g2.position.forEach((field)=>delete item[field]);
}
return this._getHash(item, accountType, [
'magic'
]);
case 'orders':
if (accountType === 'cloud-g1') {
hashFields.g1.order.forEach((field)=>delete item[field]);
} else if (accountType === 'cloud-g2') {
hashFields.g2.order.forEach((field)=>delete item[field]);
}
return this._getHash(item, accountType, [
'magic'
]);
}
}
_getHash(obj, accountType, integerKeys) {
let jsonItem = '';
if (accountType === 'cloud-g1') {
const stringify = (objFromJson, key)=>{
if (typeof objFromJson === 'number') {
if (integerKeys.includes(key)) {
return objFromJson;
} else {
return objFromJson.toFixed(8);
}
} else if (Array.isArray(objFromJson)) {
return `[${objFromJson.map((item)=>stringify(item)).join(',')}]`;
} else if (objFromJson === null) {
return objFromJson;
} else if (typeof objFromJson !== 'object' || objFromJson.getTime) {
return JSON.stringify(objFromJson);
}
let props = Object.keys(objFromJson).map((keyItem)=>`"${keyItem}":${stringify(objFromJson[keyItem], keyItem)}`).join(',');
return `{${props}}`;
};
jsonItem = stringify(obj);
} else if (accountType === 'cloud-g2') {
const stringify = (objFromJson, key)=>{
if (typeof objFromJson === 'number') {
if (integerKeys.includes(key)) {
return objFromJson;
} else {
return parseFloat(objFromJson.toFixed(8));
}
} else if (Array.isArray(objFromJson)) {
return `[${objFromJson.map((item)=>stringify(item)).join(',')}]`;
} else if (objFromJson === null) {
return objFromJson;
} else if (typeof objFromJson !== 'object' || objFromJson.getTime) {
return JSON.stringify(objFromJson);
}
let props = Object.keys(objFromJson).map((keyItem)=>`"${keyItem}":${stringify(objFromJson[keyItem], keyItem)}`).join(',');
return `{${props}}`;
};
jsonItem = stringify(obj);
}
return crypto.MD5(jsonItem).toString();
}
_stop() {
this._specificationsTree.stop();
this._positionsTree.stop();
this._ordersTree.stop();
}
/**
* Constructs the instance of terminal hash manager class
* @param {ClientApiClient} clientApiClient client api client
* @param {boolean} [keepHashTrees] if set to true, unused data will not be cleared (for use in debugging)
*/ constructor(clientApiClient, keepHashTrees = false){
this._clientApiClient = clientApiClient;
this._specificationsTree = new ReferenceTree(this, 'symbol', 'specifications', true, keepHashTrees);
this._positionsTree = new ReferenceTree(this, 'id', 'positions', false, keepHashTrees);
this._ordersTree = new ReferenceTree(this, 'id', 'orders', false, keepHashTrees);
}
};
/**
* Responsible for handling positions, orders, and specifications hash data
*/ export { TerminalHashManager as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgY3J5cHRvIGZyb20gJ2NyeXB0by1qcyc7XG5pbXBvcnQgUmVmZXJlbmNlVHJlZSBmcm9tICcuL3JlZmVyZW5jZVRyZWUnO1xuXG4vKipcbiAqIFJlc3BvbnNpYmxlIGZvciBoYW5kbGluZyBwb3NpdGlvbnMsIG9yZGVycywgYW5kIHNwZWNpZmljYXRpb25zIGhhc2ggZGF0YVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUZXJtaW5hbEhhc2hNYW5hZ2VyIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0aGUgaW5zdGFuY2Ugb2YgdGVybWluYWwgaGFzaCBtYW5hZ2VyIGNsYXNzXG4gICAqIEBwYXJhbSB7Q2xpZW50QXBpQ2xpZW50fSBjbGllbnRBcGlDbGllbnQgY2xpZW50IGFwaSBjbGllbnRcbiAgICogQHBhcmFtIHtib29sZWFufSBba2VlcEhhc2hUcmVlc10gaWYgc2V0IHRvIHRydWUsIHVudXNlZCBkYXRhIHdpbGwgbm90IGJlIGNsZWFyZWQgKGZvciB1c2UgaW4gZGVidWdnaW5nKVxuICAgKi9cbiAgY29uc3RydWN0b3IoY2xpZW50QXBpQ2xpZW50LCBrZWVwSGFzaFRyZWVzID0gZmFsc2UpIHtcbiAgICB0aGlzLl9jbGllbnRBcGlDbGllbnQgPSBjbGllbnRBcGlDbGllbnQ7XG4gICAgdGhpcy5fc3BlY2lmaWNhdGlvbnNUcmVlID0gbmV3IFJlZmVyZW5jZVRyZWUodGhpcywgJ3N5bWJvbCcsICdzcGVjaWZpY2F0aW9ucycsIHRydWUsIGtlZXBIYXNoVHJlZXMpO1xuICAgIHRoaXMuX3Bvc2l0aW9uc1RyZWUgPSBuZXcgUmVmZXJlbmNlVHJlZSh0aGlzLCAnaWQnLCAncG9zaXRpb25zJywgZmFsc2UsIGtlZXBIYXNoVHJlZXMpO1xuICAgIHRoaXMuX29yZGVyc1RyZWUgPSBuZXcgUmVmZXJlbmNlVHJlZSh0aGlzLCAnaWQnLCAnb3JkZXJzJywgZmFsc2UsIGtlZXBIYXNoVHJlZXMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlZnJlc2hlcyBoYXNoaW5nIGlnbm9yZWQgZmllbGQgbGlzdHNcbiAgICogQHBhcmFtIHtTdHJpbmd9IHJlZ2lvbiBhY2NvdW50IHJlZ2lvblxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiB0aGUgaGFzaGluZyBmaWVsZCBsaXN0cyBhcmUgdXBkYXRlZC5cbiAgICovXG4gIGFzeW5jIHJlZnJlc2hJZ25vcmVkRmllbGRMaXN0cyhyZWdpb24pIHtcbiAgICBhd2FpdCB0aGlzLl9jbGllbnRBcGlDbGllbnQucmVmcmVzaElnbm9yZWRGaWVsZExpc3RzKHJlZ2lvbik7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBzcGVjaWZpY2F0aW9ucyBkYXRhIGJ5IGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IHNwZWNpZmljYXRpb25zSGFzaCBzcGVjaWZpY2F0aW9ucyBoYXNoXG4gICAqIEByZXR1cm5zIHtbaWQ6IHN0cmluZ106IE1ldGF0cmFkZXJTeW1ib2xTcGVjaWZpY2F0aW9ufVxuICAgKi9cbiAgZ2V0U3BlY2lmaWNhdGlvbnNCeUhhc2goc3BlY2lmaWNhdGlvbnNIYXNoKXtcbiAgICByZXR1cm4gdGhpcy5fc3BlY2lmaWNhdGlvbnNUcmVlLmdldEl0ZW1zQnlIYXNoKHNwZWNpZmljYXRpb25zSGFzaCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBzcGVjaWZpY2F0aW9ucyBoYXNoIGRhdGEgYnkgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gc3BlY2lmaWNhdGlvbnNIYXNoIHNwZWNpZmljYXRpb25zIGhhc2hcbiAgICogQHJldHVybnMge1tpZDogc3RyaW5nXTogc3RyaW5nfVxuICAgKi9cbiAgZ2V0U3BlY2lmaWNhdGlvbnNIYXNoZXNCeUhhc2goc3BlY2lmaWNhdGlvbnNIYXNoKXtcbiAgICByZXR1cm4gdGhpcy5fc3BlY2lmaWNhdGlvbnNUcmVlLmdldEhhc2hlc0J5SGFzaChzcGVjaWZpY2F0aW9uc0hhc2gpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcG9zaXRpb25zIGRhdGEgYnkgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcG9zaXRpb25zSGFzaCBwb3NpdGlvbnMgaGFzaFxuICAgKiBAcmV0dXJucyB7W2lkOiBzdHJpbmddOiBNZXRhdHJhZGVyUG9zaXRpb259XG4gICAqL1xuICBnZXRQb3NpdGlvbnNCeUhhc2gocG9zaXRpb25zSGFzaCkge1xuICAgIHJldHVybiB0aGlzLl9wb3NpdGlvbnNUcmVlLmdldEl0ZW1zQnlIYXNoKHBvc2l0aW9uc0hhc2gpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcG9zaXRpb25zIGhhc2ggZGF0YSBieSBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwb3NpdGlvbnNIYXNoIHBvc2l0aW9ucyBoYXNoXG4gICAqIEByZXR1cm5zIHtbaWQ6IHN0cmluZ106IHN0cmluZ30gZGljdGlvbmFyeSBvZiBwb3NpdGlvbiBoYXNoZXNcbiAgICovXG4gIGdldFBvc2l0aW9uc0hhc2hlc0J5SGFzaChwb3NpdGlvbnNIYXNoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3Bvc2l0aW9uc1RyZWUuZ2V0SGFzaGVzQnlIYXNoKHBvc2l0aW9uc0hhc2gpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgb3JkZXJzIGRhdGEgYnkgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gb3JkZXJzSGFzaCBvcmRlcnMgaGFzaFxuICAgKiBAcmV0dXJucyB7W2lkOiBzdHJpbmddOiBNZXRhdHJhZGVyT3JkZXJ9IHJlbW92ZWQgcG9zaXRpb24gaWRzXG4gICAqL1xuICBnZXRPcmRlcnNCeUhhc2gob3JkZXJzSGFzaCl7XG4gICAgcmV0dXJuIHRoaXMuX29yZGVyc1RyZWUuZ2V0SXRlbXNCeUhhc2gob3JkZXJzSGFzaCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBvcmRlcnMgaGFzaCBkYXRhIGJ5IGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IG9yZGVyc0hhc2ggb3JkZXJzIGhhc2hcbiAgICogQHJldHVybnMge1tpZDogc3RyaW5nXTogc3RyaW5nfSBkaWN0aW9uYXJ5IG9mIG9yZGVyIGhhc2hlc1xuICAgKi9cbiAgZ2V0T3JkZXJzSGFzaGVzQnlIYXNoKG9yZGVyc0hhc2gpIHtcbiAgICByZXR1cm4gdGhpcy5fb3JkZXJzVHJlZS5nZXRIYXNoZXNCeUhhc2gob3JkZXJzSGFzaCk7XG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhbiBlbnRyeSBmb3Igc3BlY2lmaWNhdGlvbiBkYXRhIGFuZCByZXR1cm5zIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IHNlcnZlck5hbWUgYnJva2VyIHNlcnZlciBuYW1lIFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudFR5cGUgYWNjb3VudCB0eXBlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJTeW1ib2xTcGVjaWZpY2F0aW9uW119IHNwZWNpZmljYXRpb25zIHNwZWNpZmljYXRpb25zIGFycmF5XG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IGRpY3Rpb25hcnkgaGFzaFxuICAgKi9cbiAgcmVjb3JkU3BlY2lmaWNhdGlvbnMoc2VydmVyTmFtZSwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCxcbiAgICBpbnN0YW5jZUluZGV4LCBzcGVjaWZpY2F0aW9ucykge1xuICAgIHJldHVybiB0aGlzLl9zcGVjaWZpY2F0aW9uc1RyZWUucmVjb3JkSXRlbXMoc2VydmVyTmFtZSwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCxcbiAgICAgIGluc3RhbmNlSW5kZXgsIHNwZWNpZmljYXRpb25zKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBVcGRhdGVzIHNwZWNpZmljYXRpb24gZGF0YVxuICAgKiBAcGFyYW0ge3N0cmluZ30gc2VydmVyTmFtZSBicm9rZXIgc2VydmVyIG5hbWUgXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50VHlwZSBhY2NvdW50IHR5cGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlclN5bWJvbFNwZWNpZmljYXRpb25bXX0gc3BlY2lmaWNhdGlvbnMgc3BlY2lmaWNhdGlvbnMgYXJyYXlcbiAgICogQHBhcmFtIHtzdHJpbmdbXX0gcmVtb3ZlZFN5bWJvbHMgcmVtb3ZlZCBzcGVjaWZpY2F0aW9uIHN5bWJvbHNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmVudEhhc2ggcGFyZW50IGhhc2hcbiAgICogQHJldHVybnMge3N0cmluZ30gdXBkYXRlZCBkaWN0aW9uYXJ5IGhhc2hcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gIHVwZGF0ZVNwZWNpZmljYXRpb25zKHNlcnZlck5hbWUsIGFjY291bnRUeXBlLCBjb25uZWN0aW9uSWQsXG4gICAgaW5zdGFuY2VJbmRleCwgc3BlY2lmaWNhdGlvbnMsIHJlbW92ZWRTeW1ib2xzLCBwYXJlbnRIYXNoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3NwZWNpZmljYXRpb25zVHJlZS51cGRhdGVJdGVtcyhzZXJ2ZXJOYW1lLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLFxuICAgICAgaW5zdGFuY2VJbmRleCwgc3BlY2lmaWNhdGlvbnMsIHJlbW92ZWRTeW1ib2xzLCBwYXJlbnRIYXNoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGVudHJ5IGZvciBwb3NpdGlvbnMgZGF0YSBhbmQgcmV0dXJucyBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudFR5cGUgYWNjb3VudCB0eXBlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJQb3NpdGlvbltdfSBwb3NpdGlvbnMgcG9zaXRpb25zIGFycmF5XG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IGRpY3Rpb25hcnkgaGFzaFxuICAgKi9cbiAgcmVjb3JkUG9zaXRpb25zKGFjY291bnRJZCwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCwgcG9zaXRpb25zKSB7XG4gICAgcmV0dXJuIHRoaXMuX3Bvc2l0aW9uc1RyZWUucmVjb3JkSXRlbXMoYWNjb3VudElkLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4LCBwb3NpdGlvbnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgcG9zaXRpb25zIGRhdGFcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkIFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudFR5cGUgYWNjb3VudCB0eXBlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJQb3NpdGlvbltdfSBwb3NpdGlvbnMgcG9zaXRpb25zXG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHJlbW92ZWRQb3NpdGlvbnMgcmVtb3ZlZCBwb3NpdGlvbiBpZHNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmVudEhhc2ggcGFyZW50IGhhc2hcbiAgICogQHJldHVybnMge3N0cmluZ30gdXBkYXRlZCBkaWN0aW9uYXJ5IGhhc2hcbiAgICovXG4gIHVwZGF0ZVBvc2l0aW9ucyhhY2NvdW50SWQsIGFjY291bnRUeXBlLCBjb25uZWN0aW9uSWQsXG4gICAgaW5zdGFuY2VJbmRleCwgcG9zaXRpb25zLCByZW1vdmVkUG9zaXRpb25zLCBwYXJlbnRIYXNoKSB7XG4gICAgcmV0dXJuIHRoaXMuX3Bvc2l0aW9uc1RyZWUudXBkYXRlSXRlbXMoYWNjb3VudElkLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLFxuICAgICAgaW5zdGFuY2VJbmRleCwgcG9zaXRpb25zLCByZW1vdmVkUG9zaXRpb25zLCBwYXJlbnRIYXNoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGVudHJ5IGZvciBvcmRlcnMgZGF0YSBhbmQgcmV0dXJucyBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZCBcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRUeXBlIGFjY291bnQgdHlwZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gY29ubmVjdGlvbklkIGNvbm5lY3Rpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGluc3RhbmNlSW5kZXggaW5zdGFuY2UgaW5kZXhcbiAgICogQHBhcmFtIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBvcmRlcnMgb3JkZXJzIGFycmF5XG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IGRpY3Rpb25hcnkgaGFzaFxuICAgKi9cbiAgcmVjb3JkT3JkZXJzKGFjY291bnRJZCwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCwgb3JkZXJzKSB7XG4gICAgcmV0dXJuIHRoaXMuX29yZGVyc1RyZWUucmVjb3JkSXRlbXMoYWNjb3VudElkLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4LCBvcmRlcnMpO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgb3JkZXJzIGRhdGFcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkIFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudFR5cGUgYWNjb3VudCB0eXBlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJPcmRlcltdfSBvcmRlcnMgb3JkZXJzIGFycmF5XG4gICAqIEBwYXJhbSB7c3RyaW5nW119IGNvbXBsZXRlZE9yZGVycyBjb21wbGV0ZWQgb3JkZXIgaWRzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBwYXJlbnRIYXNoIHBhcmVudCBoYXNoXG4gICAqIEByZXR1cm5zIHtzdHJpbmd9IHVwZGF0ZWQgZGljdGlvbmFyeSBoYXNoXG4gICAqL1xuICB1cGRhdGVPcmRlcnMoYWNjb3VudElkLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLFxuICAgIGluc3RhbmNlSW5kZXgsIG9yZGVycywgY29tcGxldGVkT3JkZXJzLCBwYXJlbnRIYXNoKSB7XG4gICAgcmV0dXJuIHRoaXMuX29yZGVyc1RyZWUudXBkYXRlSXRlbXMoYWNjb3VudElkLCBhY2NvdW50VHlwZSwgY29ubmVjdGlvbklkLFxuICAgICAgaW5zdGFuY2VJbmRleCwgb3JkZXJzLCBjb21wbGV0ZWRPcmRlcnMsIHBhcmVudEhhc2gpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIGxpc3Qgb2YgbGFzdCB1c2VkIHNwZWNpZmljYXRpb24gaGFzaGVzLCB3aXRoIHNwZWNpZmllZCBzZXJ2ZXIgaGFzaGVzIHByaW9yaXRpemVkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzZXJ2ZXJOYW1lIHNlcnZlciBuYW1lXG4gICAqIEByZXR1cm5zIHtzdHJpbmdbXX0gbGFzdCB1c2VkIHNwZWNpZmljYXRpb24gaGFzaGVzXG4gICAqL1xuICBnZXRMYXN0VXNlZFNwZWNpZmljYXRpb25IYXNoZXMoc2VydmVyTmFtZSkge1xuICAgIHJldHVybiB0aGlzLl9zcGVjaWZpY2F0aW9uc1RyZWUuZ2V0TGFzdFVzZWRIYXNoZXMoc2VydmVyTmFtZSk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBsaXN0IG9mIGxhc3QgdXNlZCBwb3NpdGlvbiBoYXNoZXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEByZXR1cm5zIHtzdHJpbmdbXX0gbGFzdCB1c2VkIHBvc2l0aW9uIGhhc2hlc1xuICAgKi9cbiAgZ2V0TGFzdFVzZWRQb3NpdGlvbkhhc2hlcyhhY2NvdW50SWQpIHtcbiAgICByZXR1cm4gdGhpcy5fcG9zaXRpb25zVHJlZS5nZXRMYXN0VXNlZEhhc2hlcyhhY2NvdW50SWQpO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyB0aGUgbGlzdCBvZiBsYXN0IHVzZWQgb3JkZXIgaGFzaGVzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcmV0dXJucyB7c3RyaW5nW119IGxhc3QgdXNlZCBvcmRlciBoYXNoZXNcbiAgICovXG4gIGdldExhc3RVc2VkT3JkZXJIYXNoZXMoYWNjb3VudElkKSB7XG4gICAgcmV0dXJuIHRoaXMuX29yZGVyc1RyZWUuZ2V0TGFzdFVzZWRIYXNoZXMoYWNjb3VudElkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGFsbCByZWZlcmVuY2VzIGZvciBhIGNvbm5lY3Rpb25cbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqL1xuICByZW1vdmVDb25uZWN0aW9uUmVmZXJlbmNlcyhjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLnJlbW92ZVNwZWNpZmljYXRpb25SZWZlcmVuY2UoY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4KTtcbiAgICB0aGlzLnJlbW92ZVBvc2l0aW9uUmVmZXJlbmNlKGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCk7XG4gICAgdGhpcy5yZW1vdmVPcmRlclJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSByZWZlcmVuY2UgZnJvbSBhIHRlcm1pbmFsIHN0YXRlIGluc3RhbmNlIGluZGV4IHRvIGEgc3BlY2lmaWNhdGlvbnMgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaGFzaCBzcGVjaWZpY2F0aW9ucyBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKi9cbiAgYWRkU3BlY2lmaWNhdGlvblJlZmVyZW5jZShoYXNoLCBjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLl9zcGVjaWZpY2F0aW9uc1RyZWUuYWRkUmVmZXJlbmNlKGhhc2gsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlZmVyZW5jZSBmcm9tIGEgdGVybWluYWwgc3RhdGUgaW5zdGFuY2UgaW5kZXggdG8gYSBzcGVjaWZpY2F0aW9ucyBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKi9cbiAgcmVtb3ZlU3BlY2lmaWNhdGlvblJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLl9zcGVjaWZpY2F0aW9uc1RyZWUucmVtb3ZlUmVmZXJlbmNlKGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIHJlZmVyZW5jZSBmcm9tIGEgdGVybWluYWwgc3RhdGUgaW5zdGFuY2UgaW5kZXggdG8gYSBwb3NpdGlvbnMgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaGFzaCBwb3NpdGlvbnMgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gY29ubmVjdGlvbklkIGNvbm5lY3Rpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGluc3RhbmNlSW5kZXggaW5zdGFuY2UgaW5kZXhcbiAgICovXG4gIGFkZFBvc2l0aW9uUmVmZXJlbmNlKGhhc2gsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCkge1xuICAgIHRoaXMuX3Bvc2l0aW9uc1RyZWUuYWRkUmVmZXJlbmNlKGhhc2gsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlZmVyZW5jZSBmcm9tIGEgdGVybWluYWwgc3RhdGUgaW5zdGFuY2UgaW5kZXggdG8gYSBwb3NpdGlvbnMgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqL1xuICByZW1vdmVQb3NpdGlvblJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLl9wb3NpdGlvbnNUcmVlLnJlbW92ZVJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSByZWZlcmVuY2UgZnJvbSBhIHRlcm1pbmFsIHN0YXRlIGluc3RhbmNlIGluZGV4IHRvIGEgb3JkZXJzIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGhhc2ggcG9zaXRpb25zIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqL1xuICBhZGRPcmRlclJlZmVyZW5jZShoYXNoLCBjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLl9vcmRlcnNUcmVlLmFkZFJlZmVyZW5jZShoYXNoLCBjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgYSByZWZlcmVuY2UgZnJvbSBhIHRlcm1pbmFsIHN0YXRlIGluc3RhbmNlIGluZGV4IHRvIGEgb3JkZXJzIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqL1xuICByZW1vdmVPcmRlclJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpIHtcbiAgICB0aGlzLl9vcmRlcnNUcmVlLnJlbW92ZVJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICB9XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgZ2V0SXRlbUhhc2goaXRlbSwgdHlwZSwgYWNjb3VudFR5cGUsIHJlZ2lvbikge1xuICAgIGNvbnN0IGhhc2hGaWVsZHMgPSB0aGlzLl9jbGllbnRBcGlDbGllbnQuZ2V0SGFzaGluZ0lnbm9yZWRGaWVsZExpc3RzKHJlZ2lvbik7XG4gICAgaXRlbSA9IE9iamVjdC5hc3NpZ24oe30sIGl0ZW0pO1xuICAgIHN3aXRjaCh0eXBlKSB7XG4gICAgY2FzZSAnc3BlY2lmaWNhdGlvbnMnOlxuICAgICAgaWYoYWNjb3VudFR5cGUgPT09ICdjbG91ZC1nMScpIHtcbiAgICAgICAgaGFzaEZpZWxkcy5nMS5zcGVjaWZpY2F0aW9uLmZvckVhY2goZmllbGQgPT4gZGVsZXRlIGl0ZW1bZmllbGRdKTtcbiAgICAgIH0gZWxzZSBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcyJykge1xuICAgICAgICBoYXNoRmllbGRzLmcyLnNwZWNpZmljYXRpb24uZm9yRWFjaChmaWVsZCA9PiBkZWxldGUgaXRlbVtmaWVsZF0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHRoaXMuX2dldEhhc2goaXRlbSwgYWNjb3VudFR5cGUsIFsnZGlnaXRzJ10pO1xuICAgIGNhc2UgJ3Bvc2l0aW9ucyc6XG4gICAgICBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcxJykge1xuICAgICAgICBoYXNoRmllbGRzLmcxLnBvc2l0aW9uLmZvckVhY2goZmllbGQgPT4gZGVsZXRlIGl0ZW1bZmllbGRdKTtcbiAgICAgIH0gZWxzZSBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcyJykge1xuICAgICAgICBoYXNoRmllbGRzLmcyLnBvc2l0aW9uLmZvckVhY2goZmllbGQgPT4gZGVsZXRlIGl0ZW1bZmllbGRdKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLl9nZXRIYXNoKGl0ZW0sIGFjY291bnRUeXBlLCBbJ21hZ2ljJ10pO1xuICAgIGNhc2UgJ29yZGVycyc6XG4gICAgICBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcxJykge1xuICAgICAgICBoYXNoRmllbGRzLmcxLm9yZGVyLmZvckVhY2goZmllbGQgPT4gZGVsZXRlIGl0ZW1bZmllbGRdKTtcbiAgICAgIH0gZWxzZSBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcyJykge1xuICAgICAgICBoYXNoRmllbGRzLmcyLm9yZGVyLmZvckVhY2goZmllbGQgPT4gZGVsZXRlIGl0ZW1bZmllbGRdKTtcbiAgICAgIH1cbiAgICAgIHJldHVybiB0aGlzLl9nZXRIYXNoKGl0ZW0sIGFjY291bnRUeXBlLCBbJ21hZ2ljJ10pO1xuICAgIH1cbiAgfVxuXG4gIF9nZXRIYXNoKG9iaiwgYWNjb3VudFR5cGUsIGludGVnZXJLZXlzKSB7XG4gICAgbGV0IGpzb25JdGVtID0gJyc7XG4gICAgaWYoYWNjb3VudFR5cGUgPT09ICdjbG91ZC1nMScpIHtcbiAgICAgIGNvbnN0IHN0cmluZ2lmeSA9IChvYmpGcm9tSnNvbiwga2V5KSA9PiB7XG4gICAgICAgIGlmKHR5cGVvZiBvYmpGcm9tSnNvbiA9PT0gJ251bWJlcicpIHtcbiAgICAgICAgICBpZihpbnRlZ2VyS2V5cy5pbmNsdWRlcyhrZXkpKSB7XG4gICAgICAgICAgICByZXR1cm4gb2JqRnJvbUpzb247XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBvYmpGcm9tSnNvbi50b0ZpeGVkKDgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIGlmKEFycmF5LmlzQXJyYXkob2JqRnJvbUpzb24pKSB7XG4gICAgICAgICAgcmV0dXJuIGBbJHtvYmpGcm9tSnNvbi5tYXAoaXRlbSA9PiBzdHJpbmdpZnkoaXRlbSkpLmpvaW4oJywnKX1dYDsgXG4gICAgICAgIH0gZWxzZSBpZiAob2JqRnJvbUpzb24gPT09IG51bGwpIHtcbiAgICAgICAgICByZXR1cm4gb2JqRnJvbUpzb247XG4gICAgICAgIH0gZWxzZSBpZiAodHlwZW9mIG9iakZyb21Kc29uICE9PSAnb2JqZWN0JyB8fCBvYmpGcm9tSnNvbi5nZXRUaW1lKXtcbiAgICAgICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkob2JqRnJvbUpzb24pO1xuICAgICAgICB9XG4gICAgXG4gICAgICAgIGxldCBwcm9wcyA9IE9iamVjdFxuICAgICAgICAgIC5rZXlzKG9iakZyb21Kc29uKVxuICAgICAgICAgIC5tYXAoa2V5SXRlbSA9PiBgXCIke2tleUl0ZW19XCI6JHtzdHJpbmdpZnkob2JqRnJvbUpzb25ba2V5SXRlbV0sIGtleUl0ZW0pfWApXG4gICAgICAgICAgLmpvaW4oJywnKTtcbiAgICAgICAgcmV0dXJuIGB7JHtwcm9wc319YDtcbiAgICAgIH07XG4gICAgXG4gICAgICBqc29uSXRlbSA9IHN0cmluZ2lmeShvYmopO1xuICAgIH0gZWxzZSBpZihhY2NvdW50VHlwZSA9PT0gJ2Nsb3VkLWcyJykge1xuICAgICAgY29uc3Qgc3RyaW5naWZ5ID0gKG9iakZyb21Kc29uLCBrZXkpID0+IHtcbiAgICAgICAgaWYodHlwZW9mIG9iakZyb21Kc29uID09PSAnbnVtYmVyJykge1xuICAgICAgICAgIGlmKGludGVnZXJLZXlzLmluY2x1ZGVzKGtleSkpIHtcbiAgICAgICAgICAgIHJldHVybiBvYmpGcm9tSnNvbjtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHBhcnNlRmxvYXQob2JqRnJvbUpzb24udG9GaXhlZCg4KSk7XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2UgaWYoQXJyYXkuaXNBcnJheShvYmpGcm9tSnNvbikpIHtcbiAgICAgICAgICByZXR1cm4gYFske29iakZyb21Kc29uLm1hcChpdGVtID0+IHN0cmluZ2lmeShpdGVtKSkuam9pbignLCcpfV1gOyBcbiAgICAgICAgfSBlbHNlIGlmIChvYmpGcm9tSnNvbiA9PT0gbnVsbCkge1xuICAgICAgICAgIHJldHVybiBvYmpGcm9tSnNvbjtcbiAgICAgICAgfSBlbHNlIGlmICh0eXBlb2Ygb2JqRnJvbUpzb24gIT09ICdvYmplY3QnIHx8IG9iakZyb21Kc29uLmdldFRpbWUpe1xuICAgICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeShvYmpGcm9tSnNvbik7XG4gICAgICAgIH1cbiAgICBcbiAgICAgICAgbGV0IHByb3BzID0gT2JqZWN0XG4gICAgICAgICAgLmtleXMob2JqRnJvbUpzb24pXG4gICAgICAgICAgLm1hcChrZXlJdGVtID0+IGBcIiR7a2V5SXRlbX1cIjoke3N0cmluZ2lmeShvYmpGcm9tSnNvbltrZXlJdGVtXSwga2V5SXRlbSl9YClcbiAgICAgICAgICAuam9pbignLCcpO1xuICAgICAgICByZXR1cm4gYHske3Byb3BzfX1gO1xuICAgICAgfTtcblxuICAgICAganNvbkl0ZW0gPSBzdHJpbmdpZnkob2JqKTtcbiAgICB9XG4gICAgcmV0dXJuIGNyeXB0by5NRDUoanNvbkl0ZW0pLnRvU3RyaW5nKCk7XG4gIH1cblxuICBfc3RvcCgpIHtcbiAgICB0aGlzLl9zcGVjaWZpY2F0aW9uc1RyZWUuc3RvcCgpO1xuICAgIHRoaXMuX3Bvc2l0aW9uc1RyZWUuc3RvcCgpO1xuICAgIHRoaXMuX29yZGVyc1RyZWUuc3RvcCgpO1xuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJjcnlwdG8iLCJSZWZlcmVuY2VUcmVlIiwiVGVybWluYWxIYXNoTWFuYWdlciIsInJlZnJlc2hJZ25vcmVkRmllbGRMaXN0cyIsInJlZ2lvbiIsIl9jbGllbnRBcGlDbGllbnQiLCJnZXRTcGVjaWZpY2F0aW9uc0J5SGFzaCIsInNwZWNpZmljYXRpb25zSGFzaCIsIl9zcGVjaWZpY2F0aW9uc1RyZWUiLCJnZXRJdGVtc0J5SGFzaCIsImdldFNwZWNpZmljYXRpb25zSGFzaGVzQnlIYXNoIiwiZ2V0SGFzaGVzQnlIYXNoIiwiZ2V0UG9zaXRpb25zQnlIYXNoIiwicG9zaXRpb25zSGFzaCIsIl9wb3NpdGlvbnNUcmVlIiwiZ2V0UG9zaXRpb25zSGFzaGVzQnlIYXNoIiwiZ2V0T3JkZXJzQnlIYXNoIiwib3JkZXJzSGFzaCIsIl9vcmRlcnNUcmVlIiwiZ2V0T3JkZXJzSGFzaGVzQnlIYXNoIiwicmVjb3JkU3BlY2lmaWNhdGlvbnMiLCJzZXJ2ZXJOYW1lIiwiYWNjb3VudFR5cGUiLCJjb25uZWN0aW9uSWQiLCJpbnN0YW5jZUluZGV4Iiwic3BlY2lmaWNhdGlvbnMiLCJyZWNvcmRJdGVtcyIsInVwZGF0ZVNwZWNpZmljYXRpb25zIiwicmVtb3ZlZFN5bWJvbHMiLCJwYXJlbnRIYXNoIiwidXBkYXRlSXRlbXMiLCJyZWNvcmRQb3NpdGlvbnMiLCJhY2NvdW50SWQiLCJwb3NpdGlvbnMiLCJ1cGRhdGVQb3NpdGlvbnMiLCJyZW1vdmVkUG9zaXRpb25zIiwicmVjb3JkT3JkZXJzIiwib3JkZXJzIiwidXBkYXRlT3JkZXJzIiwiY29tcGxldGVkT3JkZXJzIiwiZ2V0TGFzdFVzZWRTcGVjaWZpY2F0aW9uSGFzaGVzIiwiZ2V0TGFzdFVzZWRIYXNoZXMiLCJnZXRMYXN0VXNlZFBvc2l0aW9uSGFzaGVzIiwiZ2V0TGFzdFVzZWRPcmRlckhhc2hlcyIsInJlbW92ZUNvbm5lY3Rpb25SZWZlcmVuY2VzIiwicmVtb3ZlU3BlY2lmaWNhdGlvblJlZmVyZW5jZSIsInJlbW92ZVBvc2l0aW9uUmVmZXJlbmNlIiwicmVtb3ZlT3JkZXJSZWZlcmVuY2UiLCJhZGRTcGVjaWZpY2F0aW9uUmVmZXJlbmNlIiwiaGFzaCIsImFkZFJlZmVyZW5jZSIsInJlbW92ZVJlZmVyZW5jZSIsImFkZFBvc2l0aW9uUmVmZXJlbmNlIiwiYWRkT3JkZXJSZWZlcmVuY2UiLCJnZXRJdGVtSGFzaCIsIml0ZW0iLCJ0eXBlIiwiaGFzaEZpZWxkcyIsImdldEhhc2hpbmdJZ25vcmVkRmllbGRMaXN0cyIsIk9iamVjdCIsImFzc2lnbiIsImcxIiwic3BlY2lmaWNhdGlvbiIsImZvckVhY2giLCJmaWVsZCIsImcyIiwiX2dldEhhc2giLCJwb3NpdGlvbiIsIm9yZGVyIiwib2JqIiwiaW50ZWdlcktleXMiLCJqc29uSXRlbSIsInN0cmluZ2lmeSIsIm9iakZyb21Kc29uIiwia2V5IiwiaW5jbHVkZXMiLCJ0b0ZpeGVkIiwiQXJyYXkiLCJpc0FycmF5IiwibWFwIiwiam9pbiIsImdldFRpbWUiLCJKU09OIiwicHJvcHMiLCJrZXlzIiwia2V5SXRlbSIsInBhcnNlRmxvYXQiLCJNRDUiLCJ0b1N0cmluZyIsIl9zdG9wIiwic3RvcCIsImNvbnN0cnVjdG9yIiwiY2xpZW50QXBpQ2xpZW50Iiwia2VlcEhhc2hUcmVlcyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxPQUFPQSxZQUFZLFlBQVk7QUFDL0IsT0FBT0MsbUJBQW1CLGtCQUFrQjtBQUs3QixJQUFBLEFBQU1DLHNCQUFOLE1BQU1BO0lBY25COzs7O0dBSUMsR0FDRCxBQUFNQyx5QkFBeUJDLE1BQU07O2VBQXJDLG9CQUFBO1lBQ0UsTUFBTSxNQUFLQyxnQkFBZ0IsQ0FBQ0Ysd0JBQXdCLENBQUNDO1FBQ3ZEOztJQUVBOzs7O0dBSUMsR0FDREUsd0JBQXdCQyxrQkFBa0IsRUFBQztRQUN6QyxPQUFPLElBQUksQ0FBQ0MsbUJBQW1CLENBQUNDLGNBQWMsQ0FBQ0Y7SUFDakQ7SUFFQTs7OztHQUlDLEdBQ0RHLDhCQUE4Qkgsa0JBQWtCLEVBQUM7UUFDL0MsT0FBTyxJQUFJLENBQUNDLG1CQUFtQixDQUFDRyxlQUFlLENBQUNKO0lBQ2xEO0lBRUE7Ozs7R0FJQyxHQUNESyxtQkFBbUJDLGFBQWEsRUFBRTtRQUNoQyxPQUFPLElBQUksQ0FBQ0MsY0FBYyxDQUFDTCxjQUFjLENBQUNJO0lBQzVDO0lBRUE7Ozs7R0FJQyxHQUNERSx5QkFBeUJGLGFBQWEsRUFBRTtRQUN0QyxPQUFPLElBQUksQ0FBQ0MsY0FBYyxDQUFDSCxlQUFlLENBQUNFO0lBQzdDO0lBRUE7Ozs7R0FJQyxHQUNERyxnQkFBZ0JDLFVBQVUsRUFBQztRQUN6QixPQUFPLElBQUksQ0FBQ0MsV0FBVyxDQUFDVCxjQUFjLENBQUNRO0lBQ3pDO0lBRUE7Ozs7R0FJQyxHQUNERSxzQkFBc0JGLFVBQVUsRUFBRTtRQUNoQyxPQUFPLElBQUksQ0FBQ0MsV0FBVyxDQUFDUCxlQUFlLENBQUNNO0lBQzFDO0lBRUE7Ozs7Ozs7O0dBUUMsR0FDREcscUJBQXFCQyxVQUFVLEVBQUVDLFdBQVcsRUFBRUMsWUFBWSxFQUN4REMsYUFBYSxFQUFFQyxjQUFjLEVBQUU7UUFDL0IsT0FBTyxJQUFJLENBQUNqQixtQkFBbUIsQ0FBQ2tCLFdBQVcsQ0FBQ0wsWUFBWUMsYUFBYUMsY0FDbkVDLGVBQWVDO0lBQ25CO0lBRUE7Ozs7Ozs7Ozs7R0FVQyxHQUNELHNDQUFzQztJQUN0Q0UscUJBQXFCTixVQUFVLEVBQUVDLFdBQVcsRUFBRUMsWUFBWSxFQUN4REMsYUFBYSxFQUFFQyxjQUFjLEVBQUVHLGNBQWMsRUFBRUMsVUFBVSxFQUFFO1FBQzNELE9BQU8sSUFBSSxDQUFDckIsbUJBQW1CLENBQUNzQixXQUFXLENBQUNULFlBQVlDLGFBQWFDLGNBQ25FQyxlQUFlQyxnQkFBZ0JHLGdCQUFnQkM7SUFDbkQ7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNERSxnQkFBZ0JDLFNBQVMsRUFBRVYsV0FBVyxFQUFFQyxZQUFZLEVBQUVDLGFBQWEsRUFBRVMsU0FBUyxFQUFFO1FBQzlFLE9BQU8sSUFBSSxDQUFDbkIsY0FBYyxDQUFDWSxXQUFXLENBQUNNLFdBQVdWLGFBQWFDLGNBQWNDLGVBQWVTO0lBQzlGO0lBRUE7Ozs7Ozs7Ozs7R0FVQyxHQUNEQyxnQkFBZ0JGLFNBQVMsRUFBRVYsV0FBVyxFQUFFQyxZQUFZLEVBQ2xEQyxhQUFhLEVBQUVTLFNBQVMsRUFBRUUsZ0JBQWdCLEVBQUVOLFVBQVUsRUFBRTtRQUN4RCxPQUFPLElBQUksQ0FBQ2YsY0FBYyxDQUFDZ0IsV0FBVyxDQUFDRSxXQUFXVixhQUFhQyxjQUM3REMsZUFBZVMsV0FBV0Usa0JBQWtCTjtJQUNoRDtJQUVBOzs7Ozs7OztHQVFDLEdBQ0RPLGFBQWFKLFNBQVMsRUFBRVYsV0FBVyxFQUFFQyxZQUFZLEVBQUVDLGFBQWEsRUFBRWEsTUFBTSxFQUFFO1FBQ3hFLE9BQU8sSUFBSSxDQUFDbkIsV0FBVyxDQUFDUSxXQUFXLENBQUNNLFdBQVdWLGFBQWFDLGNBQWNDLGVBQWVhO0lBQzNGO0lBRUE7Ozs7Ozs7Ozs7R0FVQyxHQUNEQyxhQUFhTixTQUFTLEVBQUVWLFdBQVcsRUFBRUMsWUFBWSxFQUMvQ0MsYUFBYSxFQUFFYSxNQUFNLEVBQUVFLGVBQWUsRUFBRVYsVUFBVSxFQUFFO1FBQ3BELE9BQU8sSUFBSSxDQUFDWCxXQUFXLENBQUNZLFdBQVcsQ0FBQ0UsV0FBV1YsYUFBYUMsY0FDMURDLGVBQWVhLFFBQVFFLGlCQUFpQlY7SUFDNUM7SUFFQTs7OztHQUlDLEdBQ0RXLCtCQUErQm5CLFVBQVUsRUFBRTtRQUN6QyxPQUFPLElBQUksQ0FBQ2IsbUJBQW1CLENBQUNpQyxpQkFBaUIsQ0FBQ3BCO0lBQ3BEO0lBRUE7Ozs7R0FJQyxHQUNEcUIsMEJBQTBCVixTQUFTLEVBQUU7UUFDbkMsT0FBTyxJQUFJLENBQUNsQixjQUFjLENBQUMyQixpQkFBaUIsQ0FBQ1Q7SUFDL0M7SUFFQTs7OztHQUlDLEdBQ0RXLHVCQUF1QlgsU0FBUyxFQUFFO1FBQ2hDLE9BQU8sSUFBSSxDQUFDZCxXQUFXLENBQUN1QixpQkFBaUIsQ0FBQ1Q7SUFDNUM7SUFFQTs7OztHQUlDLEdBQ0RZLDJCQUEyQnJCLFlBQVksRUFBRUMsYUFBYSxFQUFFO1FBQ3RELElBQUksQ0FBQ3FCLDRCQUE0QixDQUFDdEIsY0FBY0M7UUFDaEQsSUFBSSxDQUFDc0IsdUJBQXVCLENBQUN2QixjQUFjQztRQUMzQyxJQUFJLENBQUN1QixvQkFBb0IsQ0FBQ3hCLGNBQWNDO0lBQzFDO0lBRUE7Ozs7O0dBS0MsR0FDRHdCLDBCQUEwQkMsSUFBSSxFQUFFMUIsWUFBWSxFQUFFQyxhQUFhLEVBQUU7UUFDM0QsSUFBSSxDQUFDaEIsbUJBQW1CLENBQUMwQyxZQUFZLENBQUNELE1BQU0xQixjQUFjQztJQUM1RDtJQUVBOzs7O0dBSUMsR0FDRHFCLDZCQUE2QnRCLFlBQVksRUFBRUMsYUFBYSxFQUFFO1FBQ3hELElBQUksQ0FBQ2hCLG1CQUFtQixDQUFDMkMsZUFBZSxDQUFDNUIsY0FBY0M7SUFDekQ7SUFFQTs7Ozs7R0FLQyxHQUNENEIscUJBQXFCSCxJQUFJLEVBQUUxQixZQUFZLEVBQUVDLGFBQWEsRUFBRTtRQUN0RCxJQUFJLENBQUNWLGNBQWMsQ0FBQ29DLFlBQVksQ0FBQ0QsTUFBTTFCLGNBQWNDO0lBQ3ZEO0lBRUE7Ozs7O0dBS0MsR0FDRHNCLHdCQUF3QnZCLFlBQVksRUFBRUMsYUFBYSxFQUFFO1FBQ25ELElBQUksQ0FBQ1YsY0FBYyxDQUFDcUMsZUFBZSxDQUFDNUIsY0FBY0M7SUFDcEQ7SUFFQTs7Ozs7R0FLQyxHQUNENkIsa0JBQWtCSixJQUFJLEVBQUUxQixZQUFZLEVBQUVDLGFBQWEsRUFBRTtRQUNuRCxJQUFJLENBQUNOLFdBQVcsQ0FBQ2dDLFlBQVksQ0FBQ0QsTUFBTTFCLGNBQWNDO0lBQ3BEO0lBRUE7Ozs7R0FJQyxHQUNEdUIscUJBQXFCeEIsWUFBWSxFQUFFQyxhQUFhLEVBQUU7UUFDaEQsSUFBSSxDQUFDTixXQUFXLENBQUNpQyxlQUFlLENBQUM1QixjQUFjQztJQUNqRDtJQUVBLHNDQUFzQztJQUN0QzhCLFlBQVlDLElBQUksRUFBRUMsSUFBSSxFQUFFbEMsV0FBVyxFQUFFbEIsTUFBTSxFQUFFO1FBQzNDLE1BQU1xRCxhQUFhLElBQUksQ0FBQ3BELGdCQUFnQixDQUFDcUQsMkJBQTJCLENBQUN0RDtRQUNyRW1ELE9BQU9JLE9BQU9DLE1BQU0sQ0FBQyxDQUFDLEdBQUdMO1FBQ3pCLE9BQU9DO1lBQ1AsS0FBSztnQkFDSCxJQUFHbEMsZ0JBQWdCLFlBQVk7b0JBQzdCbUMsV0FBV0ksRUFBRSxDQUFDQyxhQUFhLENBQUNDLE9BQU8sQ0FBQ0MsQ0FBQUEsUUFBUyxPQUFPVCxJQUFJLENBQUNTLE1BQU07Z0JBQ2pFLE9BQU8sSUFBRzFDLGdCQUFnQixZQUFZO29CQUNwQ21DLFdBQVdRLEVBQUUsQ0FBQ0gsYUFBYSxDQUFDQyxPQUFPLENBQUNDLENBQUFBLFFBQVMsT0FBT1QsSUFBSSxDQUFDUyxNQUFNO2dCQUNqRTtnQkFDQSxPQUFPLElBQUksQ0FBQ0UsUUFBUSxDQUFDWCxNQUFNakMsYUFBYTtvQkFBQztpQkFBUztZQUNwRCxLQUFLO2dCQUNILElBQUdBLGdCQUFnQixZQUFZO29CQUM3Qm1DLFdBQVdJLEVBQUUsQ0FBQ00sUUFBUSxDQUFDSixPQUFPLENBQUNDLENBQUFBLFFBQVMsT0FBT1QsSUFBSSxDQUFDUyxNQUFNO2dCQUM1RCxPQUFPLElBQUcxQyxnQkFBZ0IsWUFBWTtvQkFDcENtQyxXQUFXUSxFQUFFLENBQUNFLFFBQVEsQ0FBQ0osT0FBTyxDQUFDQyxDQUFBQSxRQUFTLE9BQU9ULElBQUksQ0FBQ1MsTUFBTTtnQkFDNUQ7Z0JBQ0EsT0FBTyxJQUFJLENBQUNFLFFBQVEsQ0FBQ1gsTUFBTWpDLGFBQWE7b0JBQUM7aUJBQVE7WUFDbkQsS0FBSztnQkFDSCxJQUFHQSxnQkFBZ0IsWUFBWTtvQkFDN0JtQyxXQUFXSSxFQUFFLENBQUNPLEtBQUssQ0FBQ0wsT0FBTyxDQUFDQyxDQUFBQSxRQUFTLE9BQU9ULElBQUksQ0FBQ1MsTUFBTTtnQkFDekQsT0FBTyxJQUFHMUMsZ0JBQWdCLFlBQVk7b0JBQ3BDbUMsV0FBV1EsRUFBRSxDQUFDRyxLQUFLLENBQUNMLE9BQU8sQ0FBQ0MsQ0FBQUEsUUFBUyxPQUFPVCxJQUFJLENBQUNTLE1BQU07Z0JBQ3pEO2dCQUNBLE9BQU8sSUFBSSxDQUFDRSxRQUFRLENBQUNYLE1BQU1qQyxhQUFhO29CQUFDO2lCQUFRO1FBQ25EO0lBQ0Y7SUFFQTRDLFNBQVNHLEdBQUcsRUFBRS9DLFdBQVcsRUFBRWdELFdBQVcsRUFBRTtRQUN0QyxJQUFJQyxXQUFXO1FBQ2YsSUFBR2pELGdCQUFnQixZQUFZO1lBQzdCLE1BQU1rRCxZQUFZLENBQUNDLGFBQWFDO2dCQUM5QixJQUFHLE9BQU9ELGdCQUFnQixVQUFVO29CQUNsQyxJQUFHSCxZQUFZSyxRQUFRLENBQUNELE1BQU07d0JBQzVCLE9BQU9EO29CQUNULE9BQU87d0JBQ0wsT0FBT0EsWUFBWUcsT0FBTyxDQUFDO29CQUM3QjtnQkFDRixPQUFPLElBQUdDLE1BQU1DLE9BQU8sQ0FBQ0wsY0FBYztvQkFDcEMsT0FBTyxDQUFDLENBQUMsRUFBRUEsWUFBWU0sR0FBRyxDQUFDeEIsQ0FBQUEsT0FBUWlCLFVBQVVqQixPQUFPeUIsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUNsRSxPQUFPLElBQUlQLGdCQUFnQixNQUFNO29CQUMvQixPQUFPQTtnQkFDVCxPQUFPLElBQUksT0FBT0EsZ0JBQWdCLFlBQVlBLFlBQVlRLE9BQU8sRUFBQztvQkFDaEUsT0FBT0MsS0FBS1YsU0FBUyxDQUFDQztnQkFDeEI7Z0JBRUEsSUFBSVUsUUFBUXhCLE9BQ1R5QixJQUFJLENBQUNYLGFBQ0xNLEdBQUcsQ0FBQ00sQ0FBQUEsVUFBVyxDQUFDLENBQUMsRUFBRUEsUUFBUSxFQUFFLEVBQUViLFVBQVVDLFdBQVcsQ0FBQ1ksUUFBUSxFQUFFQSxTQUFTLENBQUMsRUFDekVMLElBQUksQ0FBQztnQkFDUixPQUFPLENBQUMsQ0FBQyxFQUFFRyxNQUFNLENBQUMsQ0FBQztZQUNyQjtZQUVBWixXQUFXQyxVQUFVSDtRQUN2QixPQUFPLElBQUcvQyxnQkFBZ0IsWUFBWTtZQUNwQyxNQUFNa0QsWUFBWSxDQUFDQyxhQUFhQztnQkFDOUIsSUFBRyxPQUFPRCxnQkFBZ0IsVUFBVTtvQkFDbEMsSUFBR0gsWUFBWUssUUFBUSxDQUFDRCxNQUFNO3dCQUM1QixPQUFPRDtvQkFDVCxPQUFPO3dCQUNMLE9BQU9hLFdBQVdiLFlBQVlHLE9BQU8sQ0FBQztvQkFDeEM7Z0JBQ0YsT0FBTyxJQUFHQyxNQUFNQyxPQUFPLENBQUNMLGNBQWM7b0JBQ3BDLE9BQU8sQ0FBQyxDQUFDLEVBQUVBLFlBQVlNLEdBQUcsQ0FBQ3hCLENBQUFBLE9BQVFpQixVQUFVakIsT0FBT3lCLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztnQkFDbEUsT0FBTyxJQUFJUCxnQkFBZ0IsTUFBTTtvQkFDL0IsT0FBT0E7Z0JBQ1QsT0FBTyxJQUFJLE9BQU9BLGdCQUFnQixZQUFZQSxZQUFZUSxPQUFPLEVBQUM7b0JBQ2hFLE9BQU9DLEtBQUtWLFNBQVMsQ0FBQ0M7Z0JBQ3hCO2dCQUVBLElBQUlVLFFBQVF4QixPQUNUeUIsSUFBSSxDQUFDWCxhQUNMTSxHQUFHLENBQUNNLENBQUFBLFVBQVcsQ0FBQyxDQUFDLEVBQUVBLFFBQVEsRUFBRSxFQUFFYixVQUFVQyxXQUFXLENBQUNZLFFBQVEsRUFBRUEsU0FBUyxDQUFDLEVBQ3pFTCxJQUFJLENBQUM7Z0JBQ1IsT0FBTyxDQUFDLENBQUMsRUFBRUcsTUFBTSxDQUFDLENBQUM7WUFDckI7WUFFQVosV0FBV0MsVUFBVUg7UUFDdkI7UUFDQSxPQUFPckUsT0FBT3VGLEdBQUcsQ0FBQ2hCLFVBQVVpQixRQUFRO0lBQ3RDO0lBRUFDLFFBQVE7UUFDTixJQUFJLENBQUNqRixtQkFBbUIsQ0FBQ2tGLElBQUk7UUFDN0IsSUFBSSxDQUFDNUUsY0FBYyxDQUFDNEUsSUFBSTtRQUN4QixJQUFJLENBQUN4RSxXQUFXLENBQUN3RSxJQUFJO0lBQ3ZCO0lBL1ZBOzs7O0dBSUMsR0FDREMsWUFBWUMsZUFBZSxFQUFFQyxnQkFBZ0IsS0FBSyxDQUFFO1FBQ2xELElBQUksQ0FBQ3hGLGdCQUFnQixHQUFHdUY7UUFDeEIsSUFBSSxDQUFDcEYsbUJBQW1CLEdBQUcsSUFBSVAsY0FBYyxJQUFJLEVBQUUsVUFBVSxrQkFBa0IsTUFBTTRGO1FBQ3JGLElBQUksQ0FBQy9FLGNBQWMsR0FBRyxJQUFJYixjQUFjLElBQUksRUFBRSxNQUFNLGFBQWEsT0FBTzRGO1FBQ3hFLElBQUksQ0FBQzNFLFdBQVcsR0FBRyxJQUFJakIsY0FBYyxJQUFJLEVBQUUsTUFBTSxVQUFVLE9BQU80RjtJQUNwRTtBQXVWRjtBQXRXQTs7Q0FFQyxHQUNELFNBQXFCM0YsaUNBbVdwQiJ9