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)
360 lines (359 loc) • 48.5 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);
});
};
}
function _define_property(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
import LoggerManager from '../../logger';
let SubscriptionManager = class SubscriptionManager {
/**
* Returns whether an account is currently subscribing
* @param {String} accountId account id
* @param {Number} instanceNumber instance index number
* @returns {Boolean} whether an account is currently subscribing
*/ isAccountSubscribing(accountId, instanceNumber) {
if (instanceNumber !== undefined) {
return Object.keys(this._subscriptions).includes(accountId + ':' + instanceNumber);
} else {
for (let key of Object.keys(this._subscriptions)){
if (key.startsWith(accountId)) {
return true;
}
}
return false;
}
}
/**
* Returns whether an instance is in disconnected retry mode
* @param {String} accountId account id
* @param {Number} instanceNumber instance index number
* @returns {Boolean} whether an account is currently subscribing
*/ isDisconnectedRetryMode(accountId, instanceNumber) {
let instanceId = accountId + ':' + (instanceNumber || 0);
return this._subscriptions[instanceId] ? this._subscriptions[instanceId].isDisconnectedRetryMode : false;
}
/**
* Returns whether an account subscription is active
* @param {String} accountId account id
* @returns {Boolean} instance actual subscribe state
*/ isSubscriptionActive(accountId) {
return !!this._subscriptionState[accountId];
}
/**
* Subscribes to the Metatrader terminal events
* @param {String} accountId id of the MetaTrader account to subscribe to
* @param {Number} instanceNumber instance index number
* @returns {Promise} promise which resolves when subscription started
*/ subscribe(accountId, instanceNumber) {
this._subscriptionState[accountId] = true;
return this._websocketClient.rpcRequest(accountId, {
type: 'subscribe',
instanceIndex: instanceNumber
});
}
/**
* Schedules to send subscribe requests to an account until cancelled
* @param {String} accountId id of the MetaTrader account
* @param {Number} instanceNumber instance index number
* @param {Boolean} isDisconnectedRetryMode whether to start subscription in disconnected retry
* mode. Subscription task in disconnected mode will be immediately replaced when the status packet is received
*/ scheduleSubscribe(accountId, instanceNumber, isDisconnectedRetryMode = false) {
var _this = this;
return _async_to_generator(function*() {
const client = _this._websocketClient;
let instanceId = accountId + ':' + (instanceNumber || 0);
if (!_this._subscriptions[instanceId]) {
_this._subscriptions[instanceId] = {
shouldRetry: true,
task: null,
waitTask: null,
future: null,
isDisconnectedRetryMode
};
let subscribeRetryIntervalInSeconds = 3;
while(_this._subscriptions[instanceId].shouldRetry){
let resolveSubscribe;
_this._subscriptions[instanceId].task = {
promise: new Promise((res)=>{
resolveSubscribe = res;
})
};
_this._subscriptions[instanceId].task.resolve = resolveSubscribe;
// eslint-disable-next-line no-inner-declarations, complexity
let subscribeTask = function() {
var _ref = _async_to_generator(function*() {
try {
_this._logger.debug(`${accountId}:${instanceNumber}: running subscribe task`);
yield _this.subscribe(accountId, instanceNumber);
} catch (err) {
if (err.name === 'TooManyRequestsError') {
const socketInstanceIndex = client.socketInstancesByAccounts[instanceNumber][accountId];
if (err.metadata.type === 'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_USER') {
_this._logSubscriptionError(accountId, `${instanceId}: Failed to subscribe`, err);
}
if ([
'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_USER',
'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_SERVER',
'LIMIT_ACCOUNT_SUBSCRIPTIONS_PER_USER_PER_SERVER'
].includes(err.metadata.type)) {
delete client.socketInstancesByAccounts[instanceNumber][accountId];
client.lockSocketInstance(instanceNumber, socketInstanceIndex, _this._websocketClient.getAccountRegion(accountId), err.metadata);
} else {
const retryTime = new Date(err.metadata.recommendedRetryTime).getTime();
if (Date.now() + subscribeRetryIntervalInSeconds * 1000 < retryTime) {
yield new Promise((res)=>setTimeout(res, retryTime - Date.now() - subscribeRetryIntervalInSeconds * 1000));
}
}
} else {
_this._logSubscriptionError(accountId, `${instanceId}: Failed to subscribe`, err);
if (err.name === 'NotFoundError') {
_this.refreshAccount(accountId);
}
if (err.name === 'TimeoutError') {
const mainAccountId = _this._websocketClient.accountsByReplicaId[accountId];
if (mainAccountId) {
const region = _this._websocketClient.getAccountRegion(accountId);
const connectedInstances = _this._latencyService.getActiveAccountInstances(mainAccountId);
// eslint-disable-next-line max-depth
if (!connectedInstances.some((instance)=>instance.startsWith(`${mainAccountId}:${region}`))) {
_this._timeoutErrorCounter[accountId] = _this._timeoutErrorCounter[accountId] || 0;
_this._timeoutErrorCounter[accountId]++;
// eslint-disable-next-line max-depth
if (_this._timeoutErrorCounter[accountId] > 4) {
_this._timeoutErrorCounter[accountId] = 0;
_this.refreshAccount(accountId);
}
}
}
}
}
}
resolveSubscribe();
});
return function subscribeTask() {
return _ref.apply(this, arguments);
};
}();
subscribeTask();
yield _this._subscriptions[instanceId].task.promise;
if (!_this._subscriptions[instanceId].shouldRetry) {
break;
}
const retryInterval = subscribeRetryIntervalInSeconds;
subscribeRetryIntervalInSeconds = Math.min(subscribeRetryIntervalInSeconds * 2, 300);
let resolve;
let subscribePromise = new Promise((res)=>{
resolve = res;
});
_this._subscriptions[instanceId].waitTask = setTimeout(()=>{
resolve(true);
}, retryInterval * 1000);
_this._subscriptions[instanceId].future = {
resolve,
promise: subscribePromise
};
const result = yield _this._subscriptions[instanceId].future.promise;
_this._subscriptions[instanceId].future = null;
if (!result) {
break;
}
}
delete _this._subscriptions[instanceId];
}
})();
}
/**
* Unsubscribe from account
* @param {String} accountId id of the MetaTrader account to unsubscribe
* @param {Number} instanceNumber instance index number
* @returns {Promise} promise which resolves when socket unsubscribed
*/ unsubscribe(accountId, instanceNumber) {
var _this = this;
return _async_to_generator(function*() {
_this.cancelAccount(accountId);
delete _this._subscriptionState[accountId];
return _this._websocketClient.rpcRequest(accountId, {
type: 'unsubscribe',
instanceIndex: instanceNumber
});
})();
}
/**
* Cancels active subscription tasks for an instance id
* @param {String} instanceId instance id to cancel subscription task for
*/ cancelSubscribe(instanceId) {
if (this._subscriptions[instanceId]) {
const subscription = this._subscriptions[instanceId];
if (subscription.future) {
subscription.future.resolve(false);
clearTimeout(subscription.waitTask);
}
if (subscription.task) {
subscription.task.resolve(false);
}
subscription.shouldRetry = false;
}
}
/**
* Cancels active subscription tasks for an account
* @param {String} accountId account id to cancel subscription tasks for
*/ cancelAccount(accountId) {
for (let instanceId of Object.keys(this._subscriptions).filter((key)=>key.startsWith(accountId))){
this.cancelSubscribe(instanceId);
}
Object.keys(this._awaitingResubscribe).forEach((instanceNumber)=>delete this._awaitingResubscribe[instanceNumber][accountId]);
delete this._timeoutErrorCounter[accountId];
}
/**
* Invoked on account timeout.
* @param {String} accountId id of the MetaTrader account
* @param {Number} instanceNumber instance index number
*/ onTimeout(accountId, instanceNumber) {
const region = this._websocketClient.getAccountRegion(accountId);
if (this._websocketClient.socketInstancesByAccounts[instanceNumber][accountId] !== undefined && this._websocketClient.connected(instanceNumber, this._websocketClient.socketInstancesByAccounts[instanceNumber][accountId], region)) {
this._logger.debug(`${accountId}:${instanceNumber}: scheduling subscribe because of account timeout`);
this.scheduleSubscribe(accountId, instanceNumber, true);
}
}
/**
* Invoked when connection to MetaTrader terminal terminated
* @param {String} accountId id of the MetaTrader account
* @param {Number} instanceNumber instance index number
*/ onDisconnected(accountId, instanceNumber) {
var _this = this;
return _async_to_generator(function*() {
yield new Promise((res)=>setTimeout(res, Math.max(Math.random() * 5, 1) * 1000));
if (_this._websocketClient.socketInstancesByAccounts[instanceNumber][accountId] !== undefined) {
_this._logger.debug(`${accountId}:${instanceNumber}: scheduling subscribe because account disconnected`);
_this.scheduleSubscribe(accountId, instanceNumber, true);
}
})();
}
/**
* Invoked when connection to MetaApi websocket API restored after a disconnect.
* @param {Number} instanceNumber instance index number
* @param {Number} socketInstanceIndex socket instance index
* @param {String[]} reconnectAccountIds account ids to reconnect
*/ onReconnected(instanceNumber, socketInstanceIndex, reconnectAccountIds) {
if (!this._awaitingResubscribe[instanceNumber]) {
this._awaitingResubscribe[instanceNumber] = {};
}
const socketInstancesByAccounts = this._websocketClient.socketInstancesByAccounts[instanceNumber];
for (let instanceId of Object.keys(this._subscriptions)){
const accountId = instanceId.split(':')[0];
if (socketInstancesByAccounts[accountId] === socketInstanceIndex) {
this.cancelSubscribe(instanceId);
}
}
var _this = this;
reconnectAccountIds.forEach(function() {
var _ref = _async_to_generator(function*(accountId) {
if (!_this._awaitingResubscribe[instanceNumber][accountId]) {
_this._awaitingResubscribe[instanceNumber][accountId] = true;
while(_this.isAccountSubscribing(accountId, instanceNumber)){
yield new Promise((res)=>setTimeout(res, 1000));
}
yield new Promise((res)=>setTimeout(res, Math.random() * 5000));
if (_this._awaitingResubscribe[instanceNumber][accountId]) {
delete _this._awaitingResubscribe[instanceNumber][accountId];
_this._logger.debug(`${accountId}:${instanceNumber}: scheduling subscribe because account reconnected`);
_this.scheduleSubscribe(accountId, instanceNumber);
}
}
});
return function(accountId) {
return _ref.apply(this, arguments);
};
}());
}
/**
* Schedules a task to refresh the account data
* @param {string} accountId account id
*/ refreshAccount(accountId) {
const mainAccountId = this._websocketClient.accountsByReplicaId[accountId];
if (mainAccountId) {
const registry = this._metaApi._connectionRegistry;
const rpcConnection = registry.rpcConnections[mainAccountId];
const region = this._websocketClient.getAccountRegion(accountId);
if (region) {
if (rpcConnection) {
rpcConnection.scheduleRefresh(region);
}
const streamingConnection = registry.streamingConnections[mainAccountId];
if (streamingConnection) {
streamingConnection.scheduleRefresh(region);
}
}
}
}
_logSubscriptionError(accountId, message, error) {
const primaryAccountId = this._websocketClient.accountsByReplicaId[accountId];
const method = this._latencyService.getSynchronizedAccountInstances(primaryAccountId).length ? 'debug' : 'error';
this._logger[method](message, error);
}
/**
* Constructs the subscription manager
* @param {MetaApiWebsocketClient} websocketClient websocket client to use for sending requests
* @param {MetaApi} metaApi metaApi instance
*/ constructor(websocketClient, metaApi){
_define_property(this, "_websocketClient", void 0);
_define_property(this, "_latencyService", void 0);
_define_property(this, "_metaApi", void 0);
_define_property(this, "_subscriptions", void 0);
_define_property(this, "_awaitingResubscribe", void 0);
_define_property(this, "_subscriptionState", void 0);
_define_property(this, "_logger", void 0);
_define_property(this, "_timeoutErrorCounter", void 0);
_define_property(this, "_recentlyDeletedAccounts", void 0);
this._websocketClient = websocketClient;
this._latencyService = websocketClient.latencyService;
this._metaApi = metaApi;
this._subscriptions = {};
this._awaitingResubscribe = {};
this._subscriptionState = {};
this._logger = LoggerManager.getLogger('SubscriptionManager');
this._timeoutErrorCounter = {};
this._recentlyDeletedAccounts = {};
}
};
/**
* Subscription manager to handle account subscription logic
*/ export { SubscriptionManager as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBMb2dnZXJNYW5hZ2VyIGZyb20gJy4uLy4uL2xvZ2dlcic7XG5pbXBvcnQgTWV0YUFwaVdlYnNvY2tldENsaWVudCBmcm9tICcuL21ldGFBcGlXZWJzb2NrZXQuY2xpZW50JztcblxuLyoqXG4gKiBTdWJzY3JpcHRpb24gbWFuYWdlciB0byBoYW5kbGUgYWNjb3VudCBzdWJzY3JpcHRpb24gbG9naWNcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU3Vic2NyaXB0aW9uTWFuYWdlciB7XG4gIFxuICBwcml2YXRlIF93ZWJzb2NrZXRDbGllbnQ6IE1ldGFBcGlXZWJzb2NrZXRDbGllbnQ7XG4gIHByaXZhdGUgX2xhdGVuY3lTZXJ2aWNlOiBhbnk7XG4gIHByaXZhdGUgX21ldGFBcGk6IGFueTtcbiAgcHJpdmF0ZSBfc3Vic2NyaXB0aW9uczoge307XG4gIHByaXZhdGUgX2F3YWl0aW5nUmVzdWJzY3JpYmU6IHt9O1xuICBwcml2YXRlIF9zdWJzY3JpcHRpb25TdGF0ZToge307XG4gIHByaXZhdGUgX2xvZ2dlcjogYW55O1xuICBwcml2YXRlIF90aW1lb3V0RXJyb3JDb3VudGVyOiB7fTtcbiAgcHJpdmF0ZSBfcmVjZW50bHlEZWxldGVkQWNjb3VudHM6IHt9O1xuXG4gIC8qKlxuICAgKiBDb25zdHJ1Y3RzIHRoZSBzdWJzY3JpcHRpb24gbWFuYWdlclxuICAgKiBAcGFyYW0ge01ldGFBcGlXZWJzb2NrZXRDbGllbnR9IHdlYnNvY2tldENsaWVudCB3ZWJzb2NrZXQgY2xpZW50IHRvIHVzZSBmb3Igc2VuZGluZyByZXF1ZXN0c1xuICAgKiBAcGFyYW0ge01ldGFBcGl9IG1ldGFBcGkgbWV0YUFwaSBpbnN0YW5jZVxuICAgKi9cbiAgY29uc3RydWN0b3Iod2Vic29ja2V0Q2xpZW50LCBtZXRhQXBpKSB7XG4gICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50ID0gd2Vic29ja2V0Q2xpZW50O1xuICAgIHRoaXMuX2xhdGVuY3lTZXJ2aWNlID0gd2Vic29ja2V0Q2xpZW50LmxhdGVuY3lTZXJ2aWNlO1xuICAgIHRoaXMuX21ldGFBcGkgPSBtZXRhQXBpO1xuICAgIHRoaXMuX3N1YnNjcmlwdGlvbnMgPSB7fTtcbiAgICB0aGlzLl9hd2FpdGluZ1Jlc3Vic2NyaWJlID0ge307XG4gICAgdGhpcy5fc3Vic2NyaXB0aW9uU3RhdGUgPSB7fTtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignU3Vic2NyaXB0aW9uTWFuYWdlcicpO1xuICAgIHRoaXMuX3RpbWVvdXRFcnJvckNvdW50ZXIgPSB7fTtcbiAgICB0aGlzLl9yZWNlbnRseURlbGV0ZWRBY2NvdW50cyA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgd2hldGhlciBhbiBhY2NvdW50IGlzIGN1cnJlbnRseSBzdWJzY3JpYmluZ1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGluc3RhbmNlTnVtYmVyIGluc3RhbmNlIGluZGV4IG51bWJlclxuICAgKiBAcmV0dXJucyB7Qm9vbGVhbn0gd2hldGhlciBhbiBhY2NvdW50IGlzIGN1cnJlbnRseSBzdWJzY3JpYmluZ1xuICAgKi9cbiAgaXNBY2NvdW50U3Vic2NyaWJpbmcoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcikge1xuICAgIGlmIChpbnN0YW5jZU51bWJlciAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICByZXR1cm4gT2JqZWN0LmtleXModGhpcy5fc3Vic2NyaXB0aW9ucykuaW5jbHVkZXMoYWNjb3VudElkICsgJzonICsgaW5zdGFuY2VOdW1iZXIpO1xuICAgIH0gZWxzZSB7XG4gICAgICBmb3IgKGxldCBrZXkgb2YgT2JqZWN0LmtleXModGhpcy5fc3Vic2NyaXB0aW9ucykpIHtcbiAgICAgICAgaWYgKGtleS5zdGFydHNXaXRoKGFjY291bnRJZCkpIHtcbiAgICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHdoZXRoZXIgYW4gaW5zdGFuY2UgaXMgaW4gZGlzY29ubmVjdGVkIHJldHJ5IG1vZGVcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHJldHVybnMge0Jvb2xlYW59IHdoZXRoZXIgYW4gYWNjb3VudCBpcyBjdXJyZW50bHkgc3Vic2NyaWJpbmdcbiAgICovXG4gIGlzRGlzY29ubmVjdGVkUmV0cnlNb2RlKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICBsZXQgaW5zdGFuY2VJZCA9IGFjY291bnRJZCArICc6JyArIChpbnN0YW5jZU51bWJlciB8fCAwKTtcbiAgICByZXR1cm4gdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXSA/IHRoaXMuX3N1YnNjcmlwdGlvbnNbaW5zdGFuY2VJZF0uaXNEaXNjb25uZWN0ZWRSZXRyeU1vZGUgOiBmYWxzZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHdoZXRoZXIgYW4gYWNjb3VudCBzdWJzY3JpcHRpb24gaXMgYWN0aXZlXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcmV0dXJucyB7Qm9vbGVhbn0gaW5zdGFuY2UgYWN0dWFsIHN1YnNjcmliZSBzdGF0ZVxuICAgKi9cbiAgaXNTdWJzY3JpcHRpb25BY3RpdmUoYWNjb3VudElkKSB7XG4gICAgcmV0dXJuICEhdGhpcy5fc3Vic2NyaXB0aW9uU3RhdGVbYWNjb3VudElkXTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdWJzY3JpYmVzIHRvIHRoZSBNZXRhdHJhZGVyIHRlcm1pbmFsIGV2ZW50c1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnQgdG8gc3Vic2NyaWJlIHRvXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBzdWJzY3JpcHRpb24gc3RhcnRlZFxuICAgKi9cbiAgc3Vic2NyaWJlKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICB0aGlzLl9zdWJzY3JpcHRpb25TdGF0ZVthY2NvdW50SWRdID0gdHJ1ZTtcbiAgICByZXR1cm4gdGhpcy5fd2Vic29ja2V0Q2xpZW50LnJwY1JlcXVlc3QoYWNjb3VudElkLCB7dHlwZTogJ3N1YnNjcmliZScsIGluc3RhbmNlSW5kZXg6IGluc3RhbmNlTnVtYmVyfSk7XG4gIH1cblxuICAvKipcbiAgICogU2NoZWR1bGVzIHRvIHNlbmQgc3Vic2NyaWJlIHJlcXVlc3RzIHRvIGFuIGFjY291bnQgdW50aWwgY2FuY2VsbGVkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudFxuICAgKiBAcGFyYW0ge051bWJlcn0gaW5zdGFuY2VOdW1iZXIgaW5zdGFuY2UgaW5kZXggbnVtYmVyXG4gICAqIEBwYXJhbSB7Qm9vbGVhbn0gaXNEaXNjb25uZWN0ZWRSZXRyeU1vZGUgd2hldGhlciB0byBzdGFydCBzdWJzY3JpcHRpb24gaW4gZGlzY29ubmVjdGVkIHJldHJ5XG4gICAqIG1vZGUuIFN1YnNjcmlwdGlvbiB0YXNrIGluIGRpc2Nvbm5lY3RlZCBtb2RlIHdpbGwgYmUgaW1tZWRpYXRlbHkgcmVwbGFjZWQgd2hlbiB0aGUgc3RhdHVzIHBhY2tldCBpcyByZWNlaXZlZFxuICAgKi9cbiAgYXN5bmMgc2NoZWR1bGVTdWJzY3JpYmUoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlciwgaXNEaXNjb25uZWN0ZWRSZXRyeU1vZGUgPSBmYWxzZSkge1xuICAgIGNvbnN0IGNsaWVudCA9IHRoaXMuX3dlYnNvY2tldENsaWVudDtcbiAgICBsZXQgaW5zdGFuY2VJZCA9IGFjY291bnRJZCArICc6JyArIChpbnN0YW5jZU51bWJlciB8fCAwKTtcbiAgICBpZiAoIXRoaXMuX3N1YnNjcmlwdGlvbnNbaW5zdGFuY2VJZF0pIHtcbiAgICAgIHRoaXMuX3N1YnNjcmlwdGlvbnNbaW5zdGFuY2VJZF0gPSB7XG4gICAgICAgIHNob3VsZFJldHJ5OiB0cnVlLFxuICAgICAgICB0YXNrOiBudWxsLFxuICAgICAgICB3YWl0VGFzazogbnVsbCxcbiAgICAgICAgZnV0dXJlOiBudWxsLFxuICAgICAgICBpc0Rpc2Nvbm5lY3RlZFJldHJ5TW9kZVxuICAgICAgfTtcbiAgICAgIGxldCBzdWJzY3JpYmVSZXRyeUludGVydmFsSW5TZWNvbmRzID0gMztcbiAgICAgIHdoaWxlICh0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLnNob3VsZFJldHJ5KSB7XG4gICAgICAgIGxldCByZXNvbHZlU3Vic2NyaWJlO1xuICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLnRhc2sgPSB7cHJvbWlzZTogbmV3IFByb21pc2UoKHJlcykgPT4ge1xuICAgICAgICAgIHJlc29sdmVTdWJzY3JpYmUgPSByZXM7XG4gICAgICAgIH0pfTtcbiAgICAgICAgdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXS50YXNrLnJlc29sdmUgPSByZXNvbHZlU3Vic2NyaWJlO1xuICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8taW5uZXItZGVjbGFyYXRpb25zLCBjb21wbGV4aXR5XG4gICAgICAgIGxldCBzdWJzY3JpYmVUYXNrID0gYXN5bmMgKCkgPT4ge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLl9sb2dnZXIuZGVidWcoYCR7YWNjb3VudElkfToke2luc3RhbmNlTnVtYmVyfTogcnVubmluZyBzdWJzY3JpYmUgdGFza2ApO1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5zdWJzY3JpYmUoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcik7XG4gICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICBpZiAoZXJyLm5hbWUgPT09ICdUb29NYW55UmVxdWVzdHNFcnJvcicpIHtcbiAgICAgICAgICAgICAgY29uc3Qgc29ja2V0SW5zdGFuY2VJbmRleCA9IGNsaWVudC5zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdO1xuICAgICAgICAgICAgICBpZiAoZXJyLm1ldGFkYXRhLnR5cGUgPT09ICdMSU1JVF9BQ0NPVU5UX1NVQlNDUklQVElPTlNfUEVSX1VTRVInKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fbG9nU3Vic2NyaXB0aW9uRXJyb3IoYWNjb3VudElkLCBgJHtpbnN0YW5jZUlkfTogRmFpbGVkIHRvIHN1YnNjcmliZWAsIGVycik7XG4gICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgaWYgKFsnTElNSVRfQUNDT1VOVF9TVUJTQ1JJUFRJT05TX1BFUl9VU0VSJywgJ0xJTUlUX0FDQ09VTlRfU1VCU0NSSVBUSU9OU19QRVJfU0VSVkVSJywgXG4gICAgICAgICAgICAgICAgJ0xJTUlUX0FDQ09VTlRfU1VCU0NSSVBUSU9OU19QRVJfVVNFUl9QRVJfU0VSVkVSJ10uaW5jbHVkZXMoZXJyLm1ldGFkYXRhLnR5cGUpKSB7XG4gICAgICAgICAgICAgICAgZGVsZXRlIGNsaWVudC5zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdO1xuICAgICAgICAgICAgICAgIGNsaWVudC5sb2NrU29ja2V0SW5zdGFuY2UoaW5zdGFuY2VOdW1iZXIsIHNvY2tldEluc3RhbmNlSW5kZXgsIFxuICAgICAgICAgICAgICAgICAgdGhpcy5fd2Vic29ja2V0Q2xpZW50LmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKSwgZXJyLm1ldGFkYXRhKTtcbiAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBjb25zdCByZXRyeVRpbWUgPSBuZXcgRGF0ZShlcnIubWV0YWRhdGEucmVjb21tZW5kZWRSZXRyeVRpbWUpLmdldFRpbWUoKTtcbiAgICAgICAgICAgICAgICBpZiAoRGF0ZS5ub3coKSArIHN1YnNjcmliZVJldHJ5SW50ZXJ2YWxJblNlY29uZHMgKiAxMDAwIDwgcmV0cnlUaW1lKSB7XG4gICAgICAgICAgICAgICAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHJldHJ5VGltZSAtIERhdGUubm93KCkgLVxuICAgICAgICAgICAgICAgICAgICBzdWJzY3JpYmVSZXRyeUludGVydmFsSW5TZWNvbmRzICogMTAwMCkpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgdGhpcy5fbG9nU3Vic2NyaXB0aW9uRXJyb3IoYWNjb3VudElkLCBgJHtpbnN0YW5jZUlkfTogRmFpbGVkIHRvIHN1YnNjcmliZWAsIGVycik7XG4gICAgICAgICAgICAgIGlmIChlcnIubmFtZSA9PT0gJ05vdEZvdW5kRXJyb3InKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWZyZXNoQWNjb3VudChhY2NvdW50SWQpO1xuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIGlmIChlcnIubmFtZSA9PT0gJ1RpbWVvdXRFcnJvcicpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtYWluQWNjb3VudElkID0gdGhpcy5fd2Vic29ja2V0Q2xpZW50LmFjY291bnRzQnlSZXBsaWNhSWRbYWNjb3VudElkXTtcbiAgICAgICAgICAgICAgICBpZiAobWFpbkFjY291bnRJZCkge1xuICAgICAgICAgICAgICAgICAgY29uc3QgcmVnaW9uID0gdGhpcy5fd2Vic29ja2V0Q2xpZW50LmdldEFjY291bnRSZWdpb24oYWNjb3VudElkKTtcbiAgICAgICAgICAgICAgICAgIGNvbnN0IGNvbm5lY3RlZEluc3RhbmNlcyA9IHRoaXMuX2xhdGVuY3lTZXJ2aWNlLmdldEFjdGl2ZUFjY291bnRJbnN0YW5jZXMobWFpbkFjY291bnRJZCk7XG4gICAgICAgICAgICAgICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbWF4LWRlcHRoXG4gICAgICAgICAgICAgICAgICBpZiAoIWNvbm5lY3RlZEluc3RhbmNlcy5zb21lKGluc3RhbmNlID0+IGluc3RhbmNlLnN0YXJ0c1dpdGgoYCR7bWFpbkFjY291bnRJZH06JHtyZWdpb259YCkpKSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3RpbWVvdXRFcnJvckNvdW50ZXJbYWNjb3VudElkXSA9IHRoaXMuX3RpbWVvdXRFcnJvckNvdW50ZXJbYWNjb3VudElkXSB8fCAwO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl90aW1lb3V0RXJyb3JDb3VudGVyW2FjY291bnRJZF0rKztcbiAgICAgICAgICAgICAgICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1kZXB0aFxuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy5fdGltZW91dEVycm9yQ291bnRlclthY2NvdW50SWRdID4gNCkge1xuICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX3RpbWVvdXRFcnJvckNvdW50ZXJbYWNjb3VudElkXSA9IDA7XG4gICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWZyZXNoQWNjb3VudChhY2NvdW50SWQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICAgIHJlc29sdmVTdWJzY3JpYmUoKTtcbiAgICAgICAgfTtcbiAgICAgICAgc3Vic2NyaWJlVGFzaygpO1xuICAgICAgICBhd2FpdCB0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLnRhc2sucHJvbWlzZTtcbiAgICAgICAgaWYgKCF0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLnNob3VsZFJldHJ5KSB7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgcmV0cnlJbnRlcnZhbCA9IHN1YnNjcmliZVJldHJ5SW50ZXJ2YWxJblNlY29uZHM7XG4gICAgICAgIHN1YnNjcmliZVJldHJ5SW50ZXJ2YWxJblNlY29uZHMgPSBNYXRoLm1pbihzdWJzY3JpYmVSZXRyeUludGVydmFsSW5TZWNvbmRzICogMiwgMzAwKTtcbiAgICAgICAgbGV0IHJlc29sdmU7XG4gICAgICAgIGxldCBzdWJzY3JpYmVQcm9taXNlID0gbmV3IFByb21pc2UoKHJlcykgPT4ge1xuICAgICAgICAgIHJlc29sdmUgPSByZXM7XG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLndhaXRUYXNrID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZSh0cnVlKTtcbiAgICAgICAgfSwgcmV0cnlJbnRlcnZhbCAqIDEwMDApO1xuICAgICAgICB0aGlzLl9zdWJzY3JpcHRpb25zW2luc3RhbmNlSWRdLmZ1dHVyZSA9IHtyZXNvbHZlLCBwcm9taXNlOiBzdWJzY3JpYmVQcm9taXNlfTtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXS5mdXR1cmUucHJvbWlzZTtcbiAgICAgICAgdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXS5mdXR1cmUgPSBudWxsO1xuICAgICAgICBpZiAoIXJlc3VsdCkge1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBkZWxldGUgdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVW5zdWJzY3JpYmUgZnJvbSBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFUcmFkZXIgYWNjb3VudCB0byB1bnN1YnNjcmliZVxuICAgKiBAcGFyYW0ge051bWJlcn0gaW5zdGFuY2VOdW1iZXIgaW5zdGFuY2UgaW5kZXggbnVtYmVyXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gc29ja2V0IHVuc3Vic2NyaWJlZFxuICAgKi9cbiAgYXN5bmMgdW5zdWJzY3JpYmUoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlcikge1xuICAgIHRoaXMuY2FuY2VsQWNjb3VudChhY2NvdW50SWQpO1xuICAgIGRlbGV0ZSB0aGlzLl9zdWJzY3JpcHRpb25TdGF0ZVthY2NvdW50SWRdO1xuICAgIHJldHVybiB0aGlzLl93ZWJzb2NrZXRDbGllbnQucnBjUmVxdWVzdChhY2NvdW50SWQsIHt0eXBlOiAndW5zdWJzY3JpYmUnLCBpbnN0YW5jZUluZGV4OiBpbnN0YW5jZU51bWJlcn0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbmNlbHMgYWN0aXZlIHN1YnNjcmlwdGlvbiB0YXNrcyBmb3IgYW4gaW5zdGFuY2UgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSWQgaW5zdGFuY2UgaWQgdG8gY2FuY2VsIHN1YnNjcmlwdGlvbiB0YXNrIGZvclxuICAgKi9cbiAgY2FuY2VsU3Vic2NyaWJlKGluc3RhbmNlSWQpIHtcbiAgICBpZiAodGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXSkge1xuICAgICAgY29uc3Qgc3Vic2NyaXB0aW9uID0gdGhpcy5fc3Vic2NyaXB0aW9uc1tpbnN0YW5jZUlkXTtcbiAgICAgIGlmIChzdWJzY3JpcHRpb24uZnV0dXJlKSB7XG4gICAgICAgIHN1YnNjcmlwdGlvbi5mdXR1cmUucmVzb2x2ZShmYWxzZSk7XG4gICAgICAgIGNsZWFyVGltZW91dChzdWJzY3JpcHRpb24ud2FpdFRhc2spO1xuICAgICAgfVxuICAgICAgaWYgKHN1YnNjcmlwdGlvbi50YXNrKSB7XG4gICAgICAgIHN1YnNjcmlwdGlvbi50YXNrLnJlc29sdmUoZmFsc2UpO1xuICAgICAgfVxuICAgICAgc3Vic2NyaXB0aW9uLnNob3VsZFJldHJ5ID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENhbmNlbHMgYWN0aXZlIHN1YnNjcmlwdGlvbiB0YXNrcyBmb3IgYW4gYWNjb3VudFxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWQgdG8gY2FuY2VsIHN1YnNjcmlwdGlvbiB0YXNrcyBmb3JcbiAgICovXG4gIGNhbmNlbEFjY291bnQoYWNjb3VudElkKSB7XG4gICAgZm9yIChsZXQgaW5zdGFuY2VJZCBvZiBPYmplY3Qua2V5cyh0aGlzLl9zdWJzY3JpcHRpb25zKS5maWx0ZXIoa2V5ID0+IGtleS5zdGFydHNXaXRoKGFjY291bnRJZCkpKSB7XG4gICAgICB0aGlzLmNhbmNlbFN1YnNjcmliZShpbnN0YW5jZUlkKTtcbiAgICB9XG4gICAgT2JqZWN0LmtleXModGhpcy5fYXdhaXRpbmdSZXN1YnNjcmliZSkuZm9yRWFjaChpbnN0YW5jZU51bWJlciA9PiBcbiAgICAgIGRlbGV0ZSB0aGlzLl9hd2FpdGluZ1Jlc3Vic2NyaWJlW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdKTtcbiAgICBkZWxldGUgdGhpcy5fdGltZW91dEVycm9yQ291bnRlclthY2NvdW50SWRdO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgb24gYWNjb3VudCB0aW1lb3V0LlxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhVHJhZGVyIGFjY291bnRcbiAgICogQHBhcmFtIHtOdW1iZXJ9IGluc3RhbmNlTnVtYmVyIGluc3RhbmNlIGluZGV4IG51bWJlclxuICAgKi9cbiAgb25UaW1lb3V0KGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICBjb25zdCByZWdpb24gPSB0aGlzLl93ZWJzb2NrZXRDbGllbnQuZ2V0QWNjb3VudFJlZ2lvbihhY2NvdW50SWQpO1xuICAgIGlmIChcbiAgICAgIHRoaXMuX3dlYnNvY2tldENsaWVudC5zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdICE9PSB1bmRlZmluZWQgJiYgXG4gICAgICB0aGlzLl93ZWJzb2NrZXRDbGllbnQuY29ubmVjdGVkKFxuICAgICAgICBpbnN0YW5jZU51bWJlciwgdGhpcy5fd2Vic29ja2V0Q2xpZW50LnNvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbaW5zdGFuY2VOdW1iZXJdW2FjY291bnRJZF0sIHJlZ2lvblxuICAgICAgKVxuICAgICkge1xuICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke2FjY291bnRJZH06JHtpbnN0YW5jZU51bWJlcn06IHNjaGVkdWxpbmcgc3Vic2NyaWJlIGJlY2F1c2Ugb2YgYWNjb3VudCB0aW1lb3V0YCk7XG4gICAgICB0aGlzLnNjaGVkdWxlU3Vic2NyaWJlKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIsIHRydWUpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIHdoZW4gY29ubmVjdGlvbiB0byBNZXRhVHJhZGVyIHRlcm1pbmFsIHRlcm1pbmF0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YVRyYWRlciBhY2NvdW50XG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICovXG4gIGFzeW5jIG9uRGlzY29ubmVjdGVkKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpIHtcbiAgICBhd2FpdCBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIE1hdGgubWF4KE1hdGgucmFuZG9tKCkgKiA1LCAxKSAqIDEwMDApKTtcbiAgICBpZiAodGhpcy5fd2Vic29ja2V0Q2xpZW50LnNvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbaW5zdGFuY2VOdW1iZXJdW2FjY291bnRJZF0gIT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhpcy5fbG9nZ2VyLmRlYnVnKGAke2FjY291bnRJZH06JHtpbnN0YW5jZU51bWJlcn06IHNjaGVkdWxpbmcgc3Vic2NyaWJlIGJlY2F1c2UgYWNjb3VudCBkaXNjb25uZWN0ZWRgKTtcbiAgICAgIHRoaXMuc2NoZWR1bGVTdWJzY3JpYmUoYWNjb3VudElkLCBpbnN0YW5jZU51bWJlciwgdHJ1ZSk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBjb25uZWN0aW9uIHRvIE1ldGFBcGkgd2Vic29ja2V0IEFQSSByZXN0b3JlZCBhZnRlciBhIGRpc2Nvbm5lY3QuXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBpbnN0YW5jZU51bWJlciBpbnN0YW5jZSBpbmRleCBudW1iZXJcbiAgICogQHBhcmFtIHtOdW1iZXJ9IHNvY2tldEluc3RhbmNlSW5kZXggc29ja2V0IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7U3RyaW5nW119IHJlY29ubmVjdEFjY291bnRJZHMgYWNjb3VudCBpZHMgdG8gcmVjb25uZWN0XG4gICAqL1xuICBvblJlY29ubmVjdGVkKGluc3RhbmNlTnVtYmVyLCBzb2NrZXRJbnN0YW5jZUluZGV4LCByZWNvbm5lY3RBY2NvdW50SWRzKSB7XG4gICAgaWYgKCF0aGlzLl9hd2FpdGluZ1Jlc3Vic2NyaWJlW2luc3RhbmNlTnVtYmVyXSkge1xuICAgICAgdGhpcy5fYXdhaXRpbmdSZXN1YnNjcmliZVtpbnN0YW5jZU51bWJlcl0gPSB7fTtcbiAgICB9XG4gICAgY29uc3Qgc29ja2V0SW5zdGFuY2VzQnlBY2NvdW50cyA9IHRoaXMuX3dlYnNvY2tldENsaWVudC5zb2NrZXRJbnN0YW5jZXNCeUFjY291bnRzW2luc3RhbmNlTnVtYmVyXTtcbiAgICBmb3IobGV0IGluc3RhbmNlSWQgb2YgT2JqZWN0LmtleXModGhpcy5fc3Vic2NyaXB0aW9ucykpe1xuICAgICAgY29uc3QgYWNjb3VudElkID0gaW5zdGFuY2VJZC5zcGxpdCgnOicpWzBdO1xuICAgICAgaWYgKHNvY2tldEluc3RhbmNlc0J5QWNjb3VudHNbYWNjb3VudElkXSA9PT0gc29ja2V0SW5zdGFuY2VJbmRleCkge1xuICAgICAgICB0aGlzLmNhbmNlbFN1YnNjcmliZShpbnN0YW5jZUlkKTtcbiAgICAgIH1cbiAgICB9XG4gICAgcmVjb25uZWN0QWNjb3VudElkcy5mb3JFYWNoKGFzeW5jIGFjY291bnRJZCA9PiB7XG4gICAgICBpZiAoIXRoaXMuX2F3YWl0aW5nUmVzdWJzY3JpYmVbaW5zdGFuY2VOdW1iZXJdW2FjY291bnRJZF0pIHtcbiAgICAgICAgdGhpcy5fYXdhaXRpbmdSZXN1YnNjcmliZVtpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXSA9IHRydWU7XG4gICAgICAgIHdoaWxlICh0aGlzLmlzQWNjb3VudFN1YnNjcmliaW5nKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpKSB7XG4gICAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCAxMDAwKSk7XG4gICAgICAgIH1cbiAgICAgICAgYXdhaXQgbmV3IFByb21pc2UocmVzID0+IHNldFRpbWVvdXQocmVzLCBNYXRoLnJhbmRvbSgpICogNTAwMCkpO1xuICAgICAgICBpZiAodGhpcy5fYXdhaXRpbmdSZXN1YnNjcmliZVtpbnN0YW5jZU51bWJlcl1bYWNjb3VudElkXSkge1xuICAgICAgICAgIGRlbGV0ZSB0aGlzLl9hd2FpdGluZ1Jlc3Vic2NyaWJlW2luc3RhbmNlTnVtYmVyXVthY2NvdW50SWRdO1xuICAgICAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHthY2NvdW50SWR9OiR7aW5zdGFuY2VOdW1iZXJ9OiBzY2hlZHVsaW5nIHN1YnNjcmliZSBiZWNhdXNlIGFjY291bnQgcmVjb25uZWN0ZWRgKTtcbiAgICAgICAgICB0aGlzLnNjaGVkdWxlU3Vic2NyaWJlKGFjY291bnRJZCwgaW5zdGFuY2VOdW1iZXIpO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogU2NoZWR1bGVzIGEgdGFzayB0byByZWZyZXNoIHRoZSBhY2NvdW50IGRhdGFcbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBhY2NvdW50IGlkXG4gICAqL1xuICByZWZyZXNoQWNjb3VudChhY2NvdW50SWQpIHtcbiAgICBjb25zdCBtYWluQWNjb3VudElkID0gdGhpcy5fd2Vic29ja2V0Q2xpZW50LmFjY291bnRzQnlSZXBsaWNhSWRbYWNjb3VudElkXTtcbiAgICBpZiAobWFpbkFjY291bnRJZCkge1xuICAgICAgY29uc3QgcmVnaXN0cnkgPSB0aGlzLl9tZXRhQXBpLl9jb25uZWN0aW9uUmVnaXN0cnk7XG4gICAgICBjb25zdCBycGNDb25uZWN0aW9uID0gcmVnaXN0cnkucnBjQ29ubmVjdGlvbnNbbWFpbkFjY291bnRJZF07XG4gICAgICBjb25zdCByZWdpb24gPSB0aGlzLl93ZWJzb2NrZXRDbGllbnQuZ2V0QWNjb3VudFJlZ2lvbihhY2NvdW50SWQpO1xuICAgICAgaWYgKHJlZ2lvbikge1xuICAgICAgICBpZiAocnBjQ29ubmVjdGlvbikge1xuICAgICAgICAgIHJwY0Nvbm5lY3Rpb24uc2NoZWR1bGVSZWZyZXNoKHJlZ2lvbik7XG4gICAgICAgIH1cbiAgICAgICAgY29uc3Qgc3RyZWFtaW5nQ29ubmVjdGlvbiA9IHJlZ2lzdHJ5LnN0cmVhbWluZ0Nvbm5lY3Rpb25zW21haW5BY2NvdW50SWRdO1xuICAgICAgICBpZiAoc3RyZWFtaW5nQ29ubmVjdGlvbikge1xuICAgICAgICAgIHN0cmVhbWluZ0Nvbm5lY3Rpb24uc2NoZWR1bGVSZWZyZXNoKHJlZ2lvbik7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBfbG9nU3Vic2NyaXB0aW9uRXJyb3IoYWNjb3VudElkLCBtZXNzYWdlLCBlcnJvcikge1xuICAgIGNvbnN0IHByaW1hcnlBY2NvdW50SWQgPSB0aGlzLl93ZWJzb2NrZXRDbGllbnQuYWNjb3VudHNCeVJlcGxpY2FJZFthY2NvdW50SWRdO1xuICAgIGNvbnN0IG1ldGhvZCA9IHRoaXMuX2xhdGVuY3lTZXJ2aWNlLmdldFN5bmNocm9uaXplZEFjY291bnRJbnN0YW5jZXMocHJpbWFyeUFjY291bnRJZCkubGVuZ3RoID8gJ2RlYnVnJyA6ICdlcnJvcic7XG4gICAgdGhpcy5fbG9nZ2VyW21ldGhvZF0obWVzc2FnZSwgZXJyb3IpO1xuICB9XG59Il0sIm5hbWVzIjpbIkxvZ2dlck1hbmFnZXIiLCJTdWJzY3JpcHRpb25NYW5hZ2VyIiwiaXNBY2NvdW50U3Vic2NyaWJpbmciLCJhY2NvdW50SWQiLCJpbnN0YW5jZU51bWJlciIsInVuZGVmaW5lZCIsIk9iamVjdCIsImtleXMiLCJfc3Vic2NyaXB0aW9ucyIsImluY2x1ZGVzIiwia2V5Iiwic3RhcnRzV2l0aCIsImlzRGlzY29ubmVjdGVkUmV0cnlNb2RlIiwiaW5zdGFuY2VJZCIsImlzU3Vic2NyaXB0aW9uQWN0aXZlIiwiX3N1YnNjcmlwdGlvblN0YXRlIiwic3Vic2NyaWJlIiwiX3dlYnNvY2tldENsaWVudCIsInJwY1JlcXVlc3QiLCJ0eXBlIiwiaW5zdGFuY2VJbmRleCIsInNjaGVkdWxlU3Vic2NyaWJlIiwiY2xpZW50Iiwic2hvdWxkUmV0cnkiLCJ0YXNrIiwid2FpdFRhc2siLCJmdXR1cmUiLCJzdWJzY3JpYmVSZXRyeUludGVydmFsSW5TZWNvbmRzIiwicmVzb2x2ZVN1YnNjcmliZSIsInByb21pc2UiLCJQcm9taXNlIiwicmVzIiwicmVzb2x2ZSIsInN1YnNjcmliZVRhc2siLCJfbG9nZ2VyIiwiZGVidWciLCJlcnIiLCJuYW1lIiwic29ja2V0SW5zdGFuY2VJbmRleCIsInNvY2tldEluc3RhbmNlc0J5QWNjb3VudHMiLCJtZXRhZGF0YSIsIl9sb2dTdWJzY3JpcHRpb25FcnJvciIsImxvY2tTb2NrZXRJbnN0YW5jZSIsImdldEFjY291bnRSZWdpb24iLCJyZXRyeVRpbWUiLCJEYXRlIiwicmVjb21tZW5kZWRSZXRyeVRpbWUiLCJnZXRUaW1lIiwibm93Iiwic2V0VGltZW91dCIsInJlZnJlc2hBY2NvdW50IiwibWFpbkFjY291bnRJZCIsImFjY291bnRzQnlSZXBsaWNhSWQiLCJyZWdpb24iLCJjb25uZWN0ZWRJbnN0YW5jZXMiLCJfbGF0ZW5jeVNlcnZpY2UiLCJnZXRBY3RpdmVBY2NvdW50SW5zdGFuY2VzIiwic29tZSIsImluc3RhbmNlIiwiX3RpbWVvdXRFcnJvckNvdW50ZXIiLCJyZXRyeUludGVydmFsIiwiTWF0aCIsIm1pbiIsInN1YnNjcmliZVByb21pc2UiLCJyZXN1bHQiLCJ1bnN1YnNjcmliZSIsImNhbmNlbEFjY291bnQiLCJjYW5jZWxTdWJzY3JpYmUiLCJzdWJzY3JpcHRpb24iLCJjbGVhclRpbWVvdXQiLCJmaWx0ZXIiLCJfYXdhaXRpbmdSZXN1YnNjcmliZSIsImZvckVhY2giLCJvblRpbWVvdXQiLCJjb25uZWN0ZWQiLCJvbkRpc2Nvbm5lY3RlZCIsIm1heCIsInJhbmRvbSIsIm9uUmVjb25uZWN0ZWQiLCJyZWNvbm5lY3RBY2NvdW50SWRzIiwic3BsaXQiLCJyZWdpc3RyeSIsIl9tZXRhQXBpIiwiX2Nvbm5lY3Rpb25SZWdpc3RyeSIsInJwY0Nvbm5lY3Rpb24iLCJycGNDb25uZWN0aW9ucyIsInNjaGVkdWxlUmVmcmVzaCIsInN0cmVhbWluZ0Nvbm5lY3Rpb24iLCJzdHJlYW1pbmdDb25uZWN0aW9ucyIsIm1lc3NhZ2UiLCJlcnJvciIsInByaW1hcnlBY2NvdW50SWQiLCJtZXRob2QiLCJnZXRTeW5jaHJvbml6ZWRBY2NvdW50SW5zdGFuY2VzIiwibGVuZ3RoIiwiY29uc3RydWN0b3IiLCJ3ZWJzb2NrZXRDbGllbnQiLCJtZXRhQXBpIiwiX3JlY2VudGx5RGVsZXRlZEFjY291bnRzIiwibGF0ZW5jeVNlcnZpY2UiLCJnZXRMb2dnZXIiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FBRUEsT0FBT0EsbUJBQW1CLGVBQWU7QUFNMUIsSUFBQSxBQUFNQyxzQkFBTixNQUFNQTtJQTZCbkI7Ozs7O0dBS0MsR0FDREMscUJBQXFCQyxTQUFTLEVBQUVDLGNBQWMsRUFBRTtRQUM5QyxJQUFJQSxtQkFBbUJDLFdBQVc7WUFDaEMsT0FBT0MsT0FBT0MsSUFBSSxDQUFDLElBQUksQ0FBQ0MsY0FBYyxFQUFFQyxRQUFRLENBQUNOLFlBQVksTUFBTUM7UUFDckUsT0FBTztZQUNMLEtBQUssSUFBSU0sT0FBT0osT0FBT0MsSUFBSSxDQUFDLElBQUksQ0FBQ0MsY0FBYyxFQUFHO2dCQUNoRCxJQUFJRSxJQUFJQyxVQUFVLENBQUNSLFlBQVk7b0JBQzdCLE9BQU87Z0JBQ1Q7WUFDRjtZQUNBLE9BQU87UUFDVDtJQUNGO0lBRUE7Ozs7O0dBS0MsR0FDRFMsd0JBQXdCVCxTQUFTLEVBQUVDLGNBQWMsRUFBRTtRQUNqRCxJQUFJUyxhQUFhVixZQUFZLE1BQU9DLENBQUFBLGtCQUFrQixDQUFBO1FBQ3RELE9BQU8sSUFBSSxDQUFDSSxjQUFjLENBQUNLLFdBQVcsR0FBRyxJQUFJLENBQUNMLGNBQWMsQ0FBQ0ssV0FBVyxDQUFDRCx1QkFBdUIsR0FBRztJQUNyRztJQUVBOzs7O0dBSUMsR0FDREUscUJBQXFCWCxTQUFTLEVBQUU7UUFDOUIsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDWSxrQkFBa0IsQ0FBQ1osVUFBVTtJQUM3QztJQUVBOzs7OztHQUtDLEdBQ0RhLFVBQVViLFNBQVMsRUFBRUMsY0FBYyxFQUFFO1FBQ25DLElBQUksQ0FBQ1csa0JBQWtCLENBQUNaLFVBQVUsR0FBRztRQUNyQyxPQUFPLElBQUksQ0FBQ2MsZ0JBQWdCLENBQUNDLFVBQVUsQ0FBQ2YsV0FBVztZQUFDZ0IsTUFBTTtZQUFhQyxlQUFlaEI7UUFBYztJQUN0RztJQUVBOzs7Ozs7R0FNQyxHQUNELEFBQU1pQixrQkFBa0JsQixTQUFTLEVBQUVDLGNBQWMsRUFBRVEsMEJBQTBCLEtBQUs7O2VBQWxGLG9CQUFBO1lBQ0UsTUFBTVUsU0FBUyxNQUFLTCxnQkFBZ0I7WUFDcEMsSUFBSUosYUFBYVYsWUFBWSxNQUFPQyxDQUFBQSxrQkFBa0IsQ0FBQTtZQUN0RCxJQUFJLENBQUMsTUFBS0ksY0FBYyxDQUFDSyxXQUFXLEVBQUU7Z0JBQ3BDLE1BQUtMLGNBQWMsQ0FBQ0ssV0FBVyxHQUFHO29CQUNoQ1UsYUFBYTtvQkFDYkMsTUFBTTtvQkFDTkMsVUFBVTtvQkFDVkMsUUFBUTtvQkFDUmQ7Z0JBQ0Y7Z0JBQ0EsSUFBSWUsa0NBQWtDO2dCQUN0QyxNQUFPLE1BQUtuQixjQUFjLENBQUNLLFdBQVcsQ0FBQ1UsV0FBVyxDQUFFO29CQUNsRCxJQUFJSztvQkFDSixNQUFLcEIsY0FBYyxDQUFDSyxXQUFXLENBQUNXLElBQUksR0FBRzt3QkFBQ0ssU0FBUyxJQUFJQyxRQUFRLENBQUNDOzRCQUM1REgsbUJBQW1CRzt3QkFDckI7b0JBQUU7b0JBQ0YsTUFBS3ZCLGNBQWMsQ0FBQ0ssV0FBVyxDQUFDVyxJQUFJLENBQUNRLE9BQU8sR0FBR0o7b0JBQy9DLDZEQUE2RDtvQkFDN0QsSUFBSUs7bUNBQWdCLG9CQUFBOzRCQUNsQixJQUFJO2dDQUNGLE1BQUtDLE9BQU8sQ0FBQ0MsS0FBSyxDQUFDLENBQUMsRUFBRWhDLFVBQVUsQ0FBQyxFQUFFQyxlQUFlLHdCQUF3QixDQUFDO2dDQUMzRSxNQUFNLE1BQUtZLFNBQVMsQ0FBQ2IsV0FBV0M7NEJBQ2xDLEVBQUUsT0FBT2dDLEtBQUs7Z0NBQ1osSUFBSUEsSUFBSUMsSUFBSSxLQUFLLHdCQUF3QjtvQ0FDdkMsTUFBTUMsc0JBQXNCaEIsT0FBT2lCLHlCQUF5QixDQUFDbkMsZUFBZSxDQUFDRCxVQUFVO29DQUN2RixJQUFJaUMsSUFBSUksUUFBUSxDQUFDckIsSUFBSSxLQUFLLHdDQUF3Qzt3Q0FDaEUsTUFBS3NCLHFCQUFxQixDQUFDdEMsV0FBVyxDQUFDLEVBQUVVLFdBQVcscUJBQXFCLENBQUMsRUFBRXVCO29DQUM5RTtvQ0FDQSxJQUFJO3dDQUFDO3dDQUF3Qzt3Q0FDM0M7cUNBQWtELENBQUMzQixRQUFRLENBQUMyQixJQUFJSSxRQUFRLENBQUNyQixJQUFJLEdBQUc7d0NBQ2hGLE9BQU9HLE9BQU9pQix5QkFBeUIsQ0FBQ25DLGVBQWUsQ0FBQ0QsVUFBVTt3Q0FDbEVtQixPQUFPb0Isa0JBQWtCLENBQUN0QyxnQkFBZ0JrQyxxQkFDeEMsTUFBS3JCLGdCQUFnQixDQUFDMEIsZ0JBQWdCLENBQUN4QyxZQUFZaUMsSUFBSUksUUFBUTtvQ0FDbkUsT0FBTzt3Q0FDTCxNQUFNSSxZQUFZLElBQUlDLEtBQUtULElBQUlJLFFBQVEsQ0FBQ00sb0JBQW9CLEVBQUVDLE9BQU87d0NBQ3JFLElBQUlGLEtBQUtHLEdBQUcsS0FBS3JCLGtDQUFrQyxPQUFPaUIsV0FBVzs0Q0FDbkUsTUFBTSxJQUFJZCxRQUFRQyxDQUFBQSxNQUFPa0IsV0FBV2xCLEtBQUthLFlBQVlDLEtBQUtHLEdBQUcsS0FDM0RyQixrQ0FBa0M7d0NBQ3RDO29DQUNGO2dDQUNGLE9BQU87b0NBQ0wsTUFBS2MscUJBQXFCLENBQUN0QyxXQUFXLENBQUMsRUFBRVUsV0FBVyxxQkFBcUIsQ0FBQyxFQUFFdUI7b0NBQzVFLElBQUlBLElBQUlDLElBQUksS0FBSyxpQkFBaUI7d0NBQ2hDLE1BQUthLGNBQWMsQ0FBQy9DO29DQUN0QjtvQ0FDQSxJQUFJaUMsSUFBSUMsSUFBSSxLQUFLLGdCQUFnQjt3Q0FDL0IsTUFBTWMsZ0JBQWdCLE1BQUtsQyxnQkFBZ0IsQ0FBQ21DLG1CQUFtQixDQUFDakQsVUFBVTt3Q0FDMUUsSUFBSWdELGVBQWU7NENBQ2pCLE1BQU1FLFNBQVMsTUFBS3BDLGdCQUFnQixDQUFDMEIsZ0JBQWdCLENBQUN4Qzs0Q0FDdEQsTUFBTW1ELHFCQUFxQixNQUFLQyxlQUFlLENBQUNDLHlCQUF5QixDQUFDTDs0Q0FDMUUscUNBQXFDOzRDQUNyQyxJQUFJLENBQUNHLG1CQUFtQkcsSUFBSSxDQUFDQyxDQUFBQSxXQUFZQSxTQUFTL0MsVUFBVSxDQUFDLENBQUMsRUFBRXdDLGNBQWMsQ0FBQyxFQUFFRSxPQUFPLENBQUMsSUFBSTtnREFDM0YsTUFBS00sb0JBQW9CLENBQUN4RCxVQUFVLEdBQUcsTUFBS3dELG9CQUFvQixDQUFDeEQsVUFBVSxJQUFJO2dEQUMvRSxNQUFLd0Qsb0JBQW9CLENBQUN4RCxVQUFVO2dEQUNwQyxxQ0FBcUM7Z0RBQ3JDLElBQUksTUFBS3dELG9CQUFvQixDQUFDeEQsVUFBVSxHQUFHLEdBQUc7b0RBQzVDLE1BQUt3RCxvQkFBb0IsQ0FBQ3hELFVBQVUsR0FBRztvREFDdkMsTUFBSytDLGNBQWMsQ0FBQy9DO2dEQUN0Qjs0Q0FDRjt3Q0FDRjtvQ0FDRjtnQ0FDRjs0QkFDRjs0QkFDQXlCO3dCQUNGO3dDQS9DSUs7Ozs7b0JBZ0RKQTtvQkFDQSxNQUFNLE1BQUt6QixjQUFjLENBQUNLLFdBQVcsQ0FBQ1csSUFBSSxDQUFDSyxPQUFPO29CQUNsRCxJQUFJLENBQUMsTUFBS3JCLGNBQWMsQ0FBQ0ssV0FBVyxDQUFDVSxXQUFXLEVBQUU7d0JBQ2hEO29CQUNGO29CQUNBLE1BQU1xQyxnQkFBZ0JqQztvQkFDdEJBLGtDQUFrQ2tDLEtBQUtDLEdBQUcsQ0FBQ25DLGtDQUFrQyxHQUFHO29CQUNoRixJQUFJSztvQkFDSixJQUFJK0IsbUJBQW1CLElBQUlqQyxRQUFRLENBQUNDO3dCQUNsQ0MsVUFBVUQ7b0JBQ1o7b0JBQ0EsTUFBS3ZCLGNBQWMsQ0FBQ0ssV0FBVyxDQUFDWSxRQUFRLEdBQUd3QixXQUFXO3dCQUNwRGpCLFFBQVE7b0JBQ1YsR0FBRzRCLGdCQUFnQjtvQkFDbkIsTUFBS3BELGNBQWMsQ0FBQ0ssV0FBVyxDQUFDYSxNQUFNLEdBQUc7d0JBQUNNO3dCQUFTSCxTQUFTa0M7b0JBQWdCO29CQUM1RSxNQUFNQyxTQUFTLE1BQU0sTUFBS3hELGNBQWMsQ0FBQ0ssV0FBVyxDQUFDYSxNQUFNLENBQUNHLE9BQU87b0JBQ25FLE1BQUtyQixjQUFjLENBQUNLLFdBQVcsQ0FBQ2EsTUFBTSxHQUFHO29CQUN6QyxJQUFJLENBQUNzQyxRQUFRO3dCQUNYO29CQUNGO2dCQUNGO2dCQUNBLE9BQU8sTUFBS3hELGNBQWMsQ0FBQ0ssV0FBVztZQUN4QztRQUNGOztJQUVBOzs7OztHQUtDLEdBQ0QsQUFBTW9ELFlBQVk5RCxTQUFTLEVBQUVDLGNBQWM7O2VBQTNDLG9CQUFBO1lBQ0UsTUFBSzhELGFBQWEsQ0FBQy9EO1lBQ25CLE9BQU8sTUFBS1ksa0JBQWtCLENBQUNaLFVBQVU7WUFDekMsT0FBTyxNQUFLYyxnQkFBZ0IsQ0FBQ0MsVUFBVSxDQUFDZixXQUFXO2dCQUFDZ0IsTUFBTTtnQkFBZUMsZUFBZWhCO1lBQWM7UUFDeEc7O0lBRUE7OztHQUdDLEdBQ0QrRCxnQkFBZ0J0RCxVQUFVLEVBQUU7UUFDMUIsSUFBSSxJQUFJLENBQUNMLGNBQWMsQ0FBQ0ssV0FBVyxFQUFFO1lBQ25DLE1BQU11RCxlQUFlLElBQUksQ0FBQzVELGNBQWMsQ0FBQ0ssV0FBVztZQUNwRCxJQUFJdUQsYUFBYTFDLE1BQU0sRUFBRTtnQkFDdkIwQyxhQUFhMUMsTUFBTSxDQUFDTSxPQUFPLENBQUM7Z0JBQzVCcUMsYUFBYUQsYUFBYTNDLFFBQVE7WUFDcEM7WUFDQSxJQUFJMkMsYUFBYTVDLElBQUksRUFBRTtnQkFDckI0QyxhQUFhNUMsSUFBSSxDQUFDUSxPQUFPLENBQUM7WUFDNUI7WUFDQW9DLGFBQWE3QyxXQUFXLEdBQUc7UUFDN0I7SUFDRjtJQUVBOzs7R0FHQyxHQUNEMkMsY0FBYy9ELFNBQVMsRUFBRTtRQUN2QixLQUFLLElBQUlVLGNBQWNQLE9BQU9DLElBQUksQ0FBQyxJQUFJLENBQUNDLGNBQWMsRUFBRThELE1BQU0sQ0FBQzVELENBQUFBLE1BQU9BLElBQUlDLFVBQVUsQ0FBQ1IsWUFBYTtZQUNoRyxJQUFJLENBQUNnRSxlQUFlLENBQUN0RDtRQUN2QjtRQUNBUCxPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDZ0Usb0JBQW9CLEVBQUVDLE9BQU8sQ0FBQ3BFLENBQUFBLGlCQUM3QyxPQUFPLElBQUksQ0FBQ21FLG9CQUFvQixDQUFDbkUsZUFBZSxDQUFDRCxVQUFVO1FBQzdELE9BQU8sSUFBSSxDQUFDd0Qsb0JBQW9CLENBQUN4RCxVQUFVO0lBQzdDO0lBRUE7Ozs7R0FJQyxHQUNEc0UsVUFBVXRFLFNBQVMsRUFBRUMsY0FBYyxFQUFFO1FBQ25DLE1BQU1pRCxTQUFTLElBQUksQ0FBQ3BDLGdCQUFnQixDQUFDMEIsZ0JBQWdCLENBQUN4QztRQUN0RCxJQUNFLElBQUksQ0FBQ2MsZ0JBQWdCLENBQUNzQix5QkFBeUIsQ0FBQ25DLGVBQWUsQ0FBQ0QsVUFBVSxLQUFLRSxhQUMvRSxJQUFJLENBQUNZLGdCQUFnQixDQUFDeUQsU0FBUyxDQUM3QnRFLGdCQUFnQixJQUFJLENBQUNhLGdCQUFnQixDQUFDc0IseUJBQXlCLENBQUNuQyxlQUFlLENBQUNELFVBQVUsRUFBRWtELFNBRTlGO1lBQ0EsSUFBSSxDQUFDbkIsT0FBTyxDQUFDQyxLQUFLLENBQUMsQ0FBQyxFQUFFaEMsVUFBVSxDQUFDLEVBQUVDLGVBQWUsaURBQWlELENBQUM7WUFDcEcsSUFBSSxDQUFDaUIsaUJBQWlCLENBQUNsQixXQUFXQyxnQkFBZ0I7UUFDcEQ7SUFDRjtJQUVBOzs7O0dBSUMsR0FDRCxBQUFNdUUsZUFBZXhFLFNBQVMsRUFBRUMsY0FBYzs7ZUFBOUMsb0JBQUE7WUFDRSxNQUFNLElBQUkwQixRQUFRQyxDQUFBQSxNQUFPa0IsV0FBV2xCLEtBQUs4QixLQUFLZSxHQUFHLENBQUNmLEtBQUtnQixNQUFNLEtBQUssR0FBRyxLQUFLO1lBQzFFLElBQUksTUFBSzVELGdCQUFnQixDQUFDc0IseUJBQXlCLENBQUNuQyxlQUFlLENBQUNELFVBQVUsS0FBS0UsV0FBVztnQkFDNUYsTUFBSzZCLE9BQU8sQ0FBQ0MsS0FBSyxDQUFDLENBQUMsRUFBRWhDLFVBQVUsQ0FBQyxFQUFFQyxlQUFlLG1EQUFtRCxDQUFDO2dCQUN0RyxNQUFLaUIsaUJBQWlCLENBQUNsQixXQUFXQyxnQkFBZ0I7WUFDcEQ7UUFDRjs7SUFFQTs7Ozs7R0FLQyxHQUNEMEUsY0FBYzFFLGNBQWMsRUFBRWtDLG1CQUFtQixFQUFFeUMsbUJBQW1CLEVBQUU7UUFDdEUsSUFBSSxDQUFDLElBQUksQ0FBQ1Isb0JBQW9CLENBQUNuRSxlQUFlLEVBQUU7WUFDOUMsSUFBSSxDQUFDbUUsb0JBQW9CLENBQUNuRSxlQUFlLEdBQUcsQ0FBQztRQUMvQztRQUNBLE1BQU1tQyw0QkFBNEIsSUFBSSxDQUFDdEIsZ0JBQWdCLENBQUNzQix5QkFBeUIsQ0FBQ25DLGVBQWU7UUFDakcsS0FBSSxJQUFJUyxjQUFjUCxPQUFPQyxJQUFJLENBQUMsSUFBSSxDQUFDQyxjQUFjLEVBQUU7WUFDckQsTUFBTUwsWUFBWVUsV0FBV21FLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRTtZQUMxQyxJQUFJekMseUJBQXlCLENBQUNwQyxVQUFVLEtBQUttQyxxQkFBcUI7Z0JBQ2hFLElBQUksQ0FBQzZCLGVBQWUsQ0FBQ3REO1lBQ3ZCO1FBQ0Y7O1FBQ0FrRSxvQkFBb0JQLE9BQU87dUJBQUMsb0JBQUEsVUFBTXJFO2dCQUNoQyxJQUFJLENBQUMsTUFBS29FLG9CQUFvQixDQUFDbkUsZUFBZSxDQUFDRCxVQUFVLEVBQUU7b0JBQ3pELE1BQUtvRSxvQkFBb0IsQ0FBQ25FLGVBQWUsQ0FBQ0QsVUFBVSxHQUFHO29CQUN2RCxNQUFPLE1BQUtELG9CQUFvQixDQUFDQyxXQUFXQyxnQkFBaUI7d0JBQzNELE1BQU0sSUFBSTBCLFFBQVFDLENBQUFBLE1BQU9rQixXQUFXbEIsS0FBSztvQkFDM0M7b0JBQ0EsTUFBTSxJQUFJRCxRQUFRQyxDQUFBQSxNQUFPa0IsV0FBV2xCLEtBQUs4QixLQUFLZ0IsTUFBTSxLQUFLO29CQUN6RCxJQUFJLE1BQUtOLG9CQUFvQixDQUFDbkUsZUFBZSxDQUFDRCxVQUFVLEVBQUU7d0JBQ3hELE9BQU8sTUFBS29FLG9CQUFvQixDQUFDbkUsZUFBZSxDQUFDRCxVQUFVO3dCQUMzRCxNQUFLK0IsT0FBTyxDQUFDQyxLQUFLLENBQUMsQ0FBQyxFQUFFaEMsVUFBVSxDQUFDLEVBQUVDLGVBQWUsa0RBQWtELENBQUM7d0JBQ3JHLE1BQUtpQixpQkFBaUIsQ0FBQ2xCLFdBQVdDO29CQUNwQztnQkFDRjtZQUNGOzRCQWJrQ0Q7Ozs7SUFjcEM7SUFFQTs7O0dBR0MsR0FDRCtDLGVBQWUvQyxTQUFTLEVBQUU7UUFDeEIsTUFBTWdELGdCQUFnQixJQUFJLENBQUNsQyxnQkFBZ0IsQ0FBQ21DLG1CQUFtQixDQUFDakQsVUFBVTtRQUMxRSxJQUFJZ0QsZUFBZTtZQUNqQixNQUFNOEIsV0FBVyxJQUFJLENBQUNDLFFBQVEsQ0FBQ0MsbUJBQW1CO1lBQ2xELE1BQU1DLGdCQUFnQkgsU0FBU0ksY0FBYyxDQUFDbEMsY0FBYztZQUM1RCxNQUFNRSxTQUFTLElBQUksQ0FBQ3BDLGdCQUFnQixDQUFDMEIsZ0JBQWdCLENBQUN4QztZQUN0RCxJQUFJa0QsUUFBUTtnQkFDVixJQUFJK0IsZUFBZTtvQkFDakJBLGNBQWNFLGVBQWUsQ0FBQ2pDO2dCQUNoQztnQkFDQSxNQUFNa0Msc0JBQXNCTixTQUFTTyxvQkFBb0IsQ0FBQ3JDLGNBQWM7Z0JBQ3hFLElBQUlvQyxxQkFBcUI7b0JBQ3ZCQSxvQkFBb0JELGVBQWUsQ0FBQ2pDO2dCQUN0QztZQUNGO1FBQ0Y7SUFDRjtJQUVBWixzQkFBc0J0QyxTQUFTLEVBQUVzRixPQUFPLEVBQUVDLEtBQUssRUFBRTtRQUMvQyxNQUFNQyxtQkFBbUIsSUFBSSxDQUFDMUUsZ0JBQWdCLENBQUNtQyxtQkFBbUIsQ0FBQ2pELFVBQVU7UUFDN0UsTUFBTXlGLFNBQVMsSUFBSSxDQUFDckMsZUFBZSxDQUFDc0MsK0JBQStCLENBQUNGLGtCQUFrQkcsTUFBTSxHQUFHLFVBQVU7UUFDekcsSUFBSSxDQUFDNUQsT0FBTyxDQUFDMEQsT0FBTyxDQUFDSCxTQUFTQztJQUNoQztJQTNTQTs7OztHQUlDLEdBQ0RLLFlBQVlDLGVBQWUsRUFBRUMsT0FBTyxDQUFFO1FBZnRDLHVCQUFRaEYsb0JBQVIsS0FBQTtRQUNBLHVCQUFRc0MsbUJBQVIsS0FBQTtRQUNBLHVCQUFRMkIsWUFBUixLQUFBO1FBQ0EsdUJBQVExRSxrQkFBUixLQUFBO1FBQ0EsdUJBQVErRCx3QkFBUixLQUFBO1FBQ0EsdUJBQVF4RCxzQkFBUixLQUFBO1FBQ0EsdUJBQVFtQixXQUFSLEtBQUE7UUFDQSx1QkFBUXlCLHdCQUFSLEtBQUE7UUFDQSx1QkFBUXVDLDRCQUFSLEtBQUE7UUFRRSxJQUFJLENBQUNqRixnQkFBZ0IsR0FBRytFO1FBQ3hCLElBQUksQ0FBQ3pDLGVBQWUsR0FBR3lDLGdCQUFnQkcsY0FBYztRQUNyRCxJQUFJLENBQUNqQixRQUFRLEdBQUdlO1FBQ2hCLElBQUksQ0FBQ3pGLGNBQWMsR0FBRyxDQUFDO1FBQ3ZCLElBQUksQ0FBQytELG9CQUFvQixHQUFHLENBQUM7UUFDN0IsSUFBSSxDQUFDeEQsa0JBQWtCLEdBQUcsQ0FBQztRQUMzQixJQUFJLENBQUNtQixPQUFPLEdBQUdsQyxjQUFjb0csU0FBUyxDQUFDO1FBQ3ZDLElBQUksQ0FBQ3pDLG9CQUFvQixHQUFHLENBQUM7UUFDN0IsSUFBSSxDQUFDdUMsd0JBQXdCLEdBQUcsQ0FBQztJQUNuQztBQTZSRjtBQTNUQTs7Q0FFQyxHQUNELFNBQXFCakcsaUNBd1RwQiJ9