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)
308 lines (307 loc) • 37.1 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 TimeoutError from '../clients/timeoutError';
let MetatraderAccountReplica = class MetatraderAccountReplica {
/**
* Returns account replica id
* @return {string} unique account replica id
*/ get id() {
return this._data._id;
}
/**
* Returns current account replica state. One of CREATED, DEPLOYING, DEPLOYED, DEPLOY_FAILED, UNDEPLOYING,
* UNDEPLOYED, UNDEPLOY_FAILED, DELETING, DELETE_FAILED, REDEPLOY_FAILED, DRAFT
* @return {State} current account replica state
*/ get state() {
return this._data.state;
}
/**
* Returns MetaTrader magic to place trades using
* @return {number} MetaTrader magic to place trades using
*/ get magic() {
return this._data.magic;
}
/**
* Returns terminal & broker connection status, one of CONNECTED, DISCONNECTED, DISCONNECTED_FROM_BROKER
* @return {ConnectionStatus} terminal & broker connection status
*/ get connectionStatus() {
return this._data.connectionStatus;
}
/**
* Returns quote streaming interval in seconds
* @return {number} quote streaming interval in seconds
*/ get quoteStreamingIntervalInSeconds() {
return this._data.quoteStreamingIntervalInSeconds;
}
/**
* Returns symbol provided by broker
* @return {string} any symbol provided by broker
*/ get symbol() {
return this._data.symbol;
}
/**
* Returns reliability value. Possible values are regular and high
* @return {Reliability} account replica reliability value
*/ get reliability() {
return this._data.reliability;
}
/**
* Returns user-defined account replica tags
* @return {Array<string>} user-defined account replica tags
*/ get tags() {
return this._data.tags;
}
/**
* Returns extra information which can be stored together with your account replica
* @return {Object} extra information which can be stored together with your account replica
*/ get metadata() {
return this._data.metadata;
}
/**
* Returns number of resource slots to allocate to account replica. Allocating extra resource slots
* results in better account performance under load which is useful for some applications. E.g. if you have many
* accounts copying the same strategy via CopyFactory API, then you can increase resourceSlots to get a lower trade
* copying latency. Please note that allocating extra resource slots is a paid option. Please note that high
* reliability accounts use redundant infrastructure, so that each resource slot for a high reliability account
* is billed as 2 standard resource slots.
* @return {number} number of resource slots to allocate to account replica
*/ get resourceSlots() {
return this._data.resourceSlots;
}
/**
* Returns the number of CopyFactory 2 resource slots to allocate to account replica.
* Allocating extra resource slots results in lower trade copying latency. Please note that allocating extra resource
* slots is a paid option. Please also note that CopyFactory 2 uses redundant infrastructure so that
* each CopyFactory resource slot is billed as 2 standard resource slots. You will be billed for CopyFactory 2
* resource slots only if you have added your account replica to CopyFactory 2 by specifying copyFactoryRoles field.
* @return {number} number of CopyFactory 2 resource slots to allocate to account replica
*/ get copyFactoryResourceSlots() {
return this._data.copyFactoryResourceSlots;
}
/**
* Returns account replica region
* @return {string} account replica region value
*/ get region() {
return this._data.region;
}
/**
* Returns the time account replica was created at, in ISO format
* @returns {string} the time account replica was created at, in ISO format
*/ get createdAt() {
return new Date(this._data.createdAt);
}
/**
* Returns primary MetaTrader account of the replica from DTO
* @return {MetatraderAccount} primary MetaTrader account of the replica from DTO
*/ get primaryAccountFromDto() {
return this._data.primaryAccount;
}
/**
* Returns primary MetaTrader account of the replica
* @return {MetatraderAccount} primary MetaTrader account of the replica
*/ get primaryAccount() {
return this._primaryAccount;
}
/**
* Updates account replica data
* @param {MetatraderAccountReplicaDto} data MetaTrader account replica data
*/ updateData(data) {
this._data = data;
}
/**
* Removes a trading account replica and stops the API server serving the replica
* @return {Promise} promise resolving when account replica is scheduled for deletion
*/ remove() {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.deleteAccountReplica(_this.primaryAccount.id, _this.id);
try {
yield _this._primaryAccount.reload();
} catch (err) {
if (err.name !== 'NotFoundError') {
throw err;
}
}
})();
}
/**
* Starts API server and trading terminal for trading account replica.
* This request will be ignored if the replica is already deployed
* @returns {Promise} promise resolving when account replica is scheduled for deployment
*/ deploy() {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.deployAccountReplica(_this.primaryAccount.id, _this.id);
yield _this._primaryAccount.reload();
})();
}
/**
* Stops API server and trading terminal for trading account replica.
* The request will be ignored if trading account replica is already undeployed
* @returns {Promise} promise resolving when account replica is scheduled for undeployment
*/ undeploy() {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.undeployAccountReplica(_this.primaryAccount.id, _this.id);
yield _this._primaryAccount.reload();
})();
}
/**
* Redeploys trading account replica. This is equivalent to undeploy immediately followed by deploy.
* @returns {Promise} promise resolving when account replica is scheduled for redeployment
*/ redeploy() {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.redeployAccountReplica(_this.primaryAccount.id, _this.id);
yield _this._primaryAccount.reload();
})();
}
/**
* Increases trading account reliability in order to increase the expected account uptime.
* The account will be temporary stopped to perform this action.
* Note that increasing reliability is a paid option
* @returns {Promise} promise resolving when account replica reliability is increased
*/ increaseReliability() {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.increaseReliability(_this.id);
yield _this._primaryAccount.reload();
})();
}
/**
* Waits until API server has finished deployment and account replica reached the DEPLOYED state
* @param {number} timeoutInSeconds wait timeout in seconds, default is 5m
* @param {number} intervalInMilliseconds interval between account replica reloads while waiting for a change, default is 1s
* @return {Promise} promise which resolves when account replica is deployed
* @throws {TimeoutError} if account replica has not reached the DEPLOYED state within timeout allowed
*/ waitDeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
var _this = this;
return _async_to_generator(function*() {
let startTime = Date.now();
yield _this._primaryAccount.reload();
while(_this.state !== 'DEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){
yield _this._delay(intervalInMilliseconds);
yield _this._primaryAccount.reload();
}
if (_this.state !== 'DEPLOYED') {
throw new TimeoutError('Timed out waiting for account replica ' + _this.id + ' to be deployed');
}
})();
}
/**
* Waits until API server has finished undeployment and account replica reached the UNDEPLOYED state
* @param {number} timeoutInSeconds wait timeout in seconds, default is 5m
* @param {number} intervalInMilliseconds interval between account replica reloads while waiting for a change, default is 1s
* @return {Promise} promise which resolves when account replica is deployed
* @throws {TimeoutError} if account replica has not reached the UNDEPLOYED state within timeout allowed
*/ waitUndeployed(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
var _this = this;
return _async_to_generator(function*() {
let startTime = Date.now();
yield _this._primaryAccount.reload();
while(_this.state !== 'UNDEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){
yield _this._delay(intervalInMilliseconds);
yield _this._primaryAccount.reload();
}
if (_this.state !== 'UNDEPLOYED') {
throw new TimeoutError('Timed out waiting for account replica ' + _this.id + ' to be undeployed');
}
})();
}
/**
* Waits until account replica has been deleted
* @param {number} timeoutInSeconds wait timeout in seconds, default is 5m
* @param {number} intervalInMilliseconds interval between account replica reloads while waiting for a change, default is 1s
* @return {Promise} promise which resolves when account replica is deleted
* @throws {TimeoutError} if account replica was not deleted within timeout allowed
*/ waitRemoved(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
var _this = this;
return _async_to_generator(function*() {
let startTime = Date.now();
yield _this._primaryAccount.reload();
while(startTime + timeoutInSeconds * 1000 > Date.now() && _this._primaryAccount.accountRegions[_this.region] === _this.id){
yield _this._delay(intervalInMilliseconds);
yield _this._primaryAccount.reload();
}
if (_this._primaryAccount.accountRegions[_this.region] === _this.id) {
throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be deleted');
}
})();
}
/**
* Waits until API server has connected to the terminal and terminal has connected to the broker
* @param {number} timeoutInSeconds wait timeout in seconds, default is 5m
* @param {number} intervalInMilliseconds interval between account replica reloads while waiting for a change, default is 1s
* @return {Promise} promise which resolves when API server is connected to the broker
* @throws {TimeoutError} if account replica has not connected to the broker within timeout allowed
*/ waitConnected(timeoutInSeconds = 300, intervalInMilliseconds = 1000) {
var _this = this;
return _async_to_generator(function*() {
let startTime = Date.now();
yield _this._primaryAccount.reload();
while(_this.connectionStatus !== 'CONNECTED' && startTime + timeoutInSeconds * 1000 > Date.now()){
yield _this._delay(intervalInMilliseconds);
yield _this._primaryAccount.reload();
}
if (_this.connectionStatus !== 'CONNECTED') {
throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to connect to the broker');
}
})();
}
/**
* Updates trading account replica
* @param {UpdatedMetatraderAccountReplicaDto} metatraderAccount updated account replica information
* @return {Promise} promise resolving when account replica is updated
*/ update(metatraderAccount) {
var _this = this;
return _async_to_generator(function*() {
yield _this._metatraderAccountClient.updateAccountReplica(_this._primaryAccount.id, _this.id, metatraderAccount);
yield _this._primaryAccount.reload();
})();
}
_delay(timeoutInMilliseconds) {
return new Promise((res)=>setTimeout(res, timeoutInMilliseconds));
}
/**
* Constructs a MetaTrader account replica entity
* @param {MetatraderAccountReplicaDto} data MetaTrader account replica data
* @param {MetatraderAccount} primaryAccount primary MetaTrader account
* @param {MetatraderAccountClient} metatraderAccountClient MetaTrader account REST API client
*/ constructor(data, primaryAccount, metatraderAccountClient){
this._data = data;
this._primaryAccount = primaryAccount;
this._metatraderAccountClient = metatraderAccountClient;
}
};
// import {Reliability, State, ConnectionStatus} from '../clients/metaApi/metatraderAccount.client';
/**
* Implements a MetaTrader account replica entity
*/ export { MetatraderAccountReplica as default };
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgVGltZW91dEVycm9yIGZyb20gJy4uL2NsaWVudHMvdGltZW91dEVycm9yJztcbi8vIGltcG9ydCB7UmVsaWFiaWxpdHksIFN0YXRlLCBDb25uZWN0aW9uU3RhdHVzfSBmcm9tICcuLi9jbGllbnRzL21ldGFBcGkvbWV0YXRyYWRlckFjY291bnQuY2xpZW50JztcblxuLyoqXG4gKiBJbXBsZW1lbnRzIGEgTWV0YVRyYWRlciBhY2NvdW50IHJlcGxpY2EgZW50aXR5XG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYSB7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgYSBNZXRhVHJhZGVyIGFjY291bnQgcmVwbGljYSBlbnRpdHlcbiAgICogQHBhcmFtIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FEdG99IGRhdGEgTWV0YVRyYWRlciBhY2NvdW50IHJlcGxpY2EgZGF0YVxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50fSBwcmltYXJ5QWNjb3VudCBwcmltYXJ5IE1ldGFUcmFkZXIgYWNjb3VudFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50Q2xpZW50fSBtZXRhdHJhZGVyQWNjb3VudENsaWVudCBNZXRhVHJhZGVyIGFjY291bnQgUkVTVCBBUEkgY2xpZW50XG4gICAqL1xuICBjb25zdHJ1Y3RvcihkYXRhLCBwcmltYXJ5QWNjb3VudCwgbWV0YXRyYWRlckFjY291bnRDbGllbnQpIHtcbiAgICB0aGlzLl9kYXRhID0gZGF0YTtcbiAgICB0aGlzLl9wcmltYXJ5QWNjb3VudCA9IHByaW1hcnlBY2NvdW50O1xuICAgIHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50ID0gbWV0YXRyYWRlckFjY291bnRDbGllbnQ7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhY2NvdW50IHJlcGxpY2EgaWRcbiAgICogQHJldHVybiB7c3RyaW5nfSB1bmlxdWUgYWNjb3VudCByZXBsaWNhIGlkXG4gICAqL1xuICBnZXQgaWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuX2lkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgY3VycmVudCBhY2NvdW50IHJlcGxpY2Egc3RhdGUuIE9uZSBvZiBDUkVBVEVELCBERVBMT1lJTkcsIERFUExPWUVELCBERVBMT1lfRkFJTEVELCBVTkRFUExPWUlORyxcbiAgICogVU5ERVBMT1lFRCwgVU5ERVBMT1lfRkFJTEVELCBERUxFVElORywgREVMRVRFX0ZBSUxFRCwgUkVERVBMT1lfRkFJTEVELCBEUkFGVFxuICAgKiBAcmV0dXJuIHtTdGF0ZX0gY3VycmVudCBhY2NvdW50IHJlcGxpY2Egc3RhdGVcbiAgICovXG4gIGdldCBzdGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5zdGF0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIE1ldGFUcmFkZXIgbWFnaWMgdG8gcGxhY2UgdHJhZGVzIHVzaW5nXG4gICAqIEByZXR1cm4ge251bWJlcn0gTWV0YVRyYWRlciBtYWdpYyB0byBwbGFjZSB0cmFkZXMgdXNpbmdcbiAgICovXG4gIGdldCBtYWdpYygpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tYWdpYztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgdGVybWluYWwgJiBicm9rZXIgY29ubmVjdGlvbiBzdGF0dXMsIG9uZSBvZiBDT05ORUNURUQsIERJU0NPTk5FQ1RFRCwgRElTQ09OTkVDVEVEX0ZST01fQlJPS0VSXG4gICAqIEByZXR1cm4ge0Nvbm5lY3Rpb25TdGF0dXN9IHRlcm1pbmFsICYgYnJva2VyIGNvbm5lY3Rpb24gc3RhdHVzXG4gICAqL1xuICBnZXQgY29ubmVjdGlvblN0YXR1cygpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5jb25uZWN0aW9uU3RhdHVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHMgXG4gICAqIEByZXR1cm4ge251bWJlcn0gcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHNcbiAgICovXG4gIGdldCBxdW90ZVN0cmVhbWluZ0ludGVydmFsSW5TZWNvbmRzKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnF1b3RlU3RyZWFtaW5nSW50ZXJ2YWxJblNlY29uZHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBzeW1ib2wgcHJvdmlkZWQgYnkgYnJva2VyIFxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IGFueSBzeW1ib2wgcHJvdmlkZWQgYnkgYnJva2VyXG4gICAqL1xuICBnZXQgc3ltYm9sKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnN5bWJvbDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHJlbGlhYmlsaXR5IHZhbHVlLiBQb3NzaWJsZSB2YWx1ZXMgYXJlIHJlZ3VsYXIgYW5kIGhpZ2hcbiAgICogQHJldHVybiB7UmVsaWFiaWxpdHl9IGFjY291bnQgcmVwbGljYSByZWxpYWJpbGl0eSB2YWx1ZVxuICAgKi9cbiAgZ2V0IHJlbGlhYmlsaXR5KCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJlbGlhYmlsaXR5O1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdXNlci1kZWZpbmVkIGFjY291bnQgcmVwbGljYSB0YWdzXG4gICAqIEByZXR1cm4ge0FycmF5PHN0cmluZz59IHVzZXItZGVmaW5lZCBhY2NvdW50IHJlcGxpY2EgdGFnc1xuICAgKi9cbiAgZ2V0IHRhZ3MoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEudGFncztcbiAgfSAgXG5cbiAgLyoqXG4gICAqIFJldHVybnMgZXh0cmEgaW5mb3JtYXRpb24gd2hpY2ggY2FuIGJlIHN0b3JlZCB0b2dldGhlciB3aXRoIHlvdXIgYWNjb3VudCByZXBsaWNhXG4gICAqIEByZXR1cm4ge09iamVjdH0gZXh0cmEgaW5mb3JtYXRpb24gd2hpY2ggY2FuIGJlIHN0b3JlZCB0b2dldGhlciB3aXRoIHlvdXIgYWNjb3VudCByZXBsaWNhXG4gICAqL1xuICBnZXQgbWV0YWRhdGEoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEubWV0YWRhdGE7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBudW1iZXIgb2YgcmVzb3VyY2Ugc2xvdHMgdG8gYWxsb2NhdGUgdG8gYWNjb3VudCByZXBsaWNhLiBBbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzXG4gICAqIHJlc3VsdHMgaW4gYmV0dGVyIGFjY291bnQgcGVyZm9ybWFuY2UgdW5kZXIgbG9hZCB3aGljaCBpcyB1c2VmdWwgZm9yIHNvbWUgYXBwbGljYXRpb25zLiBFLmcuIGlmIHlvdSBoYXZlIG1hbnlcbiAgICogYWNjb3VudHMgY29weWluZyB0aGUgc2FtZSBzdHJhdGVneSB2aWEgQ29weUZhY3RvcnkgQVBJLCB0aGVuIHlvdSBjYW4gaW5jcmVhc2UgcmVzb3VyY2VTbG90cyB0byBnZXQgYSBsb3dlciB0cmFkZVxuICAgKiBjb3B5aW5nIGxhdGVuY3kuIFBsZWFzZSBub3RlIHRoYXQgYWxsb2NhdGluZyBleHRyYSByZXNvdXJjZSBzbG90cyBpcyBhIHBhaWQgb3B0aW9uLiBQbGVhc2Ugbm90ZSB0aGF0IGhpZ2hcbiAgICogcmVsaWFiaWxpdHkgYWNjb3VudHMgdXNlIHJlZHVuZGFudCBpbmZyYXN0cnVjdHVyZSwgc28gdGhhdCBlYWNoIHJlc291cmNlIHNsb3QgZm9yIGEgaGlnaCByZWxpYWJpbGl0eSBhY2NvdW50XG4gICAqIGlzIGJpbGxlZCBhcyAyIHN0YW5kYXJkIHJlc291cmNlIHNsb3RzLlxuICAgKiBAcmV0dXJuIHtudW1iZXJ9IG51bWJlciBvZiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50IHJlcGxpY2FcbiAgICovXG4gIGdldCByZXNvdXJjZVNsb3RzKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnJlc291cmNlU2xvdHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgbnVtYmVyIG9mIENvcHlGYWN0b3J5IDIgcmVzb3VyY2Ugc2xvdHMgdG8gYWxsb2NhdGUgdG8gYWNjb3VudCByZXBsaWNhLlxuICAgKiBBbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIHJlc3VsdHMgaW4gbG93ZXIgdHJhZGUgY29weWluZyBsYXRlbmN5LiBQbGVhc2Ugbm90ZSB0aGF0IGFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2VcbiAgICogc2xvdHMgaXMgYSBwYWlkIG9wdGlvbi4gUGxlYXNlIGFsc28gbm90ZSB0aGF0IENvcHlGYWN0b3J5IDIgdXNlcyByZWR1bmRhbnQgaW5mcmFzdHJ1Y3R1cmUgc28gdGhhdFxuICAgKiBlYWNoIENvcHlGYWN0b3J5IHJlc291cmNlIHNsb3QgaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuIFlvdSB3aWxsIGJlIGJpbGxlZCBmb3IgQ29weUZhY3RvcnkgMlxuICAgKiByZXNvdXJjZSBzbG90cyBvbmx5IGlmIHlvdSBoYXZlIGFkZGVkIHlvdXIgYWNjb3VudCByZXBsaWNhIHRvIENvcHlGYWN0b3J5IDIgYnkgc3BlY2lmeWluZyBjb3B5RmFjdG9yeVJvbGVzIGZpZWxkLlxuICAgKiBAcmV0dXJuIHtudW1iZXJ9IG51bWJlciBvZiBDb3B5RmFjdG9yeSAyIHJlc291cmNlIHNsb3RzIHRvIGFsbG9jYXRlIHRvIGFjY291bnQgcmVwbGljYVxuICAgKi9cbiAgZ2V0IGNvcHlGYWN0b3J5UmVzb3VyY2VTbG90cygpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5jb3B5RmFjdG9yeVJlc291cmNlU2xvdHM7XG4gIH1cblxuICAvKipcbiAgICAgKiBSZXR1cm5zIGFjY291bnQgcmVwbGljYSByZWdpb25cbiAgICAgKiBAcmV0dXJuIHtzdHJpbmd9IGFjY291bnQgcmVwbGljYSByZWdpb24gdmFsdWVcbiAgICAgKi9cbiAgZ2V0IHJlZ2lvbigpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5yZWdpb247XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBhY2NvdW50IHJlcGxpY2Egd2FzIGNyZWF0ZWQgYXQsIGluIElTTyBmb3JtYXRcbiAgICogQHJldHVybnMge3N0cmluZ30gdGhlIHRpbWUgYWNjb3VudCByZXBsaWNhIHdhcyBjcmVhdGVkIGF0LCBpbiBJU08gZm9ybWF0XG4gICAqL1xuICBnZXQgY3JlYXRlZEF0KCkge1xuICAgIHJldHVybiBuZXcgRGF0ZSh0aGlzLl9kYXRhLmNyZWF0ZWRBdCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBwcmltYXJ5IE1ldGFUcmFkZXIgYWNjb3VudCBvZiB0aGUgcmVwbGljYSBmcm9tIERUT1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudH0gcHJpbWFyeSBNZXRhVHJhZGVyIGFjY291bnQgb2YgdGhlIHJlcGxpY2EgZnJvbSBEVE9cbiAgICovXG4gIGdldCBwcmltYXJ5QWNjb3VudEZyb21EdG8oKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJpbWFyeUFjY291bnQ7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBwcmltYXJ5IE1ldGFUcmFkZXIgYWNjb3VudCBvZiB0aGUgcmVwbGljYVxuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudH0gcHJpbWFyeSBNZXRhVHJhZGVyIGFjY291bnQgb2YgdGhlIHJlcGxpY2FcbiAgICovXG4gIGdldCBwcmltYXJ5QWNjb3VudCgpIHtcbiAgICByZXR1cm4gdGhpcy5fcHJpbWFyeUFjY291bnQ7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyBhY2NvdW50IHJlcGxpY2EgZGF0YVxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50UmVwbGljYUR0b30gZGF0YSBNZXRhVHJhZGVyIGFjY291bnQgcmVwbGljYSBkYXRhIFxuICAgKi9cbiAgdXBkYXRlRGF0YShkYXRhKSB7XG4gICAgdGhpcy5fZGF0YSA9IGRhdGE7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRyYWRpbmcgYWNjb3VudCByZXBsaWNhIGFuZCBzdG9wcyB0aGUgQVBJIHNlcnZlciBzZXJ2aW5nIHRoZSByZXBsaWNhXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCByZXBsaWNhIGlzIHNjaGVkdWxlZCBmb3IgZGVsZXRpb25cbiAgICovXG4gIGFzeW5jIHJlbW92ZSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5kZWxldGVBY2NvdW50UmVwbGljYSh0aGlzLnByaW1hcnlBY2NvdW50LmlkLCB0aGlzLmlkKTtcbiAgICB0cnkge1xuICAgICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBpZiAoZXJyLm5hbWUgIT09ICdOb3RGb3VuZEVycm9yJykge1xuICAgICAgICB0aHJvdyBlcnI7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0cyBBUEkgc2VydmVyIGFuZCB0cmFkaW5nIHRlcm1pbmFsIGZvciB0cmFkaW5nIGFjY291bnQgcmVwbGljYS5cbiAgICogVGhpcyByZXF1ZXN0IHdpbGwgYmUgaWdub3JlZCBpZiB0aGUgcmVwbGljYSBpcyBhbHJlYWR5IGRlcGxveWVkXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmVwbGljYSBpcyBzY2hlZHVsZWQgZm9yIGRlcGxveW1lbnRcbiAgICovXG4gIGFzeW5jIGRlcGxveSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5kZXBsb3lBY2NvdW50UmVwbGljYSh0aGlzLnByaW1hcnlBY2NvdW50LmlkLCB0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLl9wcmltYXJ5QWNjb3VudC5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyBBUEkgc2VydmVyIGFuZCB0cmFkaW5nIHRlcm1pbmFsIGZvciB0cmFkaW5nIGFjY291bnQgcmVwbGljYS5cbiAgICogVGhlIHJlcXVlc3Qgd2lsbCBiZSBpZ25vcmVkIGlmIHRyYWRpbmcgYWNjb3VudCByZXBsaWNhIGlzIGFscmVhZHkgdW5kZXBsb3llZFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IHJlcGxpY2EgaXMgc2NoZWR1bGVkIGZvciB1bmRlcGxveW1lbnRcbiAgICovXG4gIGFzeW5jIHVuZGVwbG95KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LnVuZGVwbG95QWNjb3VudFJlcGxpY2EodGhpcy5wcmltYXJ5QWNjb3VudC5pZCwgdGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogUmVkZXBsb3lzIHRyYWRpbmcgYWNjb3VudCByZXBsaWNhLiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gdW5kZXBsb3kgaW1tZWRpYXRlbHkgZm9sbG93ZWQgYnkgZGVwbG95LlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IHJlcGxpY2EgaXMgc2NoZWR1bGVkIGZvciByZWRlcGxveW1lbnRcbiAgICovXG4gIGFzeW5jIHJlZGVwbG95KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LnJlZGVwbG95QWNjb3VudFJlcGxpY2EodGhpcy5wcmltYXJ5QWNjb3VudC5pZCwgdGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogSW5jcmVhc2VzIHRyYWRpbmcgYWNjb3VudCByZWxpYWJpbGl0eSBpbiBvcmRlciB0byBpbmNyZWFzZSB0aGUgZXhwZWN0ZWQgYWNjb3VudCB1cHRpbWUuXG4gICAqIFRoZSBhY2NvdW50IHdpbGwgYmUgdGVtcG9yYXJ5IHN0b3BwZWQgdG8gcGVyZm9ybSB0aGlzIGFjdGlvbi5cbiAgICogTm90ZSB0aGF0IGluY3JlYXNpbmcgcmVsaWFiaWxpdHkgaXMgYSBwYWlkIG9wdGlvblxuICAgKiBAcmV0dXJucyB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBhY2NvdW50IHJlcGxpY2EgcmVsaWFiaWxpdHkgaXMgaW5jcmVhc2VkXG4gICAqL1xuICBhc3luYyBpbmNyZWFzZVJlbGlhYmlsaXR5KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmluY3JlYXNlUmVsaWFiaWxpdHkodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgQVBJIHNlcnZlciBoYXMgZmluaXNoZWQgZGVwbG95bWVudCBhbmQgYWNjb3VudCByZXBsaWNhIHJlYWNoZWQgdGhlIERFUExPWUVEIHN0YXRlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHdhaXQgdGltZW91dCBpbiBzZWNvbmRzLCBkZWZhdWx0IGlzIDVtXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbnRlcnZhbEluTWlsbGlzZWNvbmRzIGludGVydmFsIGJldHdlZW4gYWNjb3VudCByZXBsaWNhIHJlbG9hZHMgd2hpbGUgd2FpdGluZyBmb3IgYSBjaGFuZ2UsIGRlZmF1bHQgaXMgMXNcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIGFjY291bnQgcmVwbGljYSBpcyBkZXBsb3llZFxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFjY291bnQgcmVwbGljYSBoYXMgbm90IHJlYWNoZWQgdGhlIERFUExPWUVEIHN0YXRlIHdpdGhpbiB0aW1lb3V0IGFsbG93ZWRcbiAgICovXG4gIGFzeW5jIHdhaXREZXBsb3llZCh0aW1lb3V0SW5TZWNvbmRzID0gMzAwLCBpbnRlcnZhbEluTWlsbGlzZWNvbmRzID0gMTAwMCkge1xuICAgIGxldCBzdGFydFRpbWUgPSBEYXRlLm5vdygpO1xuICAgIGF3YWl0IHRoaXMuX3ByaW1hcnlBY2NvdW50LnJlbG9hZCgpO1xuICAgIHdoaWxlICh0aGlzLnN0YXRlICE9PSAnREVQTE9ZRUQnICYmIChzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkgPiBEYXRlLm5vdygpKSB7XG4gICAgICBhd2FpdCB0aGlzLl9kZWxheShpbnRlcnZhbEluTWlsbGlzZWNvbmRzKTtcbiAgICAgIGF3YWl0IHRoaXMuX3ByaW1hcnlBY2NvdW50LnJlbG9hZCgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5zdGF0ZSAhPT0gJ0RFUExPWUVEJykge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgcmVwbGljYSAnICsgdGhpcy5pZCArICcgdG8gYmUgZGVwbG95ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgQVBJIHNlcnZlciBoYXMgZmluaXNoZWQgdW5kZXBsb3ltZW50IGFuZCBhY2NvdW50IHJlcGxpY2EgcmVhY2hlZCB0aGUgVU5ERVBMT1lFRCBzdGF0ZVxuICAgKiBAcGFyYW0ge251bWJlcn0gdGltZW91dEluU2Vjb25kcyB3YWl0IHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyA1bVxuICAgKiBAcGFyYW0ge251bWJlcn0gaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyBpbnRlcnZhbCBiZXR3ZWVuIGFjY291bnQgcmVwbGljYSByZWxvYWRzIHdoaWxlIHdhaXRpbmcgZm9yIGEgY2hhbmdlLCBkZWZhdWx0IGlzIDFzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBhY2NvdW50IHJlcGxpY2EgaXMgZGVwbG95ZWRcbiAgICogQHRocm93cyB7VGltZW91dEVycm9yfSBpZiBhY2NvdW50IHJlcGxpY2EgaGFzIG5vdCByZWFjaGVkIHRoZSBVTkRFUExPWUVEIHN0YXRlIHdpdGhpbiB0aW1lb3V0IGFsbG93ZWRcbiAgICovXG4gIGFzeW5jIHdhaXRVbmRlcGxveWVkKHRpbWVvdXRJblNlY29uZHMgPSAzMDAsIGludGVydmFsSW5NaWxsaXNlY29uZHMgPSAxMDAwKSB7XG4gICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gICAgd2hpbGUgKHRoaXMuc3RhdGUgIT09ICdVTkRFUExPWUVEJyAmJiAoc3RhcnRUaW1lICsgdGltZW91dEluU2Vjb25kcyAqIDEwMDApID4gRGF0ZS5ub3coKSkge1xuICAgICAgYXdhaXQgdGhpcy5fZGVsYXkoaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyk7XG4gICAgICBhd2FpdCB0aGlzLl9wcmltYXJ5QWNjb3VudC5yZWxvYWQoKTtcbiAgICB9XG4gICAgaWYgKHRoaXMuc3RhdGUgIT09ICdVTkRFUExPWUVEJykge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgcmVwbGljYSAnICsgdGhpcy5pZCArICcgdG8gYmUgdW5kZXBsb3llZCcpO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBXYWl0cyB1bnRpbCBhY2NvdW50IHJlcGxpY2EgaGFzIGJlZW4gZGVsZXRlZFxuICAgKiBAcGFyYW0ge251bWJlcn0gdGltZW91dEluU2Vjb25kcyB3YWl0IHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyA1bVxuICAgKiBAcGFyYW0ge251bWJlcn0gaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyBpbnRlcnZhbCBiZXR3ZWVuIGFjY291bnQgcmVwbGljYSByZWxvYWRzIHdoaWxlIHdhaXRpbmcgZm9yIGEgY2hhbmdlLCBkZWZhdWx0IGlzIDFzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBhY2NvdW50IHJlcGxpY2EgaXMgZGVsZXRlZFxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFjY291bnQgcmVwbGljYSB3YXMgbm90IGRlbGV0ZWQgd2l0aGluIHRpbWVvdXQgYWxsb3dlZFxuICAgKi9cbiAgYXN5bmMgd2FpdFJlbW92ZWQodGltZW91dEluU2Vjb25kcyA9IDMwMCwgaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyA9IDEwMDApIHtcbiAgICBsZXQgc3RhcnRUaW1lID0gRGF0ZS5ub3coKTtcbiAgICBhd2FpdCB0aGlzLl9wcmltYXJ5QWNjb3VudC5yZWxvYWQoKTtcbiAgICB3aGlsZSAoc3RhcnRUaW1lICsgdGltZW91dEluU2Vjb25kcyAqIDEwMDAgPiBEYXRlLm5vdygpICYmIFxuICAgICAgICB0aGlzLl9wcmltYXJ5QWNjb3VudC5hY2NvdW50UmVnaW9uc1t0aGlzLnJlZ2lvbl0gPT09IHRoaXMuaWQpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2RlbGF5KGludGVydmFsSW5NaWxsaXNlY29uZHMpO1xuICAgICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gICAgfVxuICAgIGlmKHRoaXMuX3ByaW1hcnlBY2NvdW50LmFjY291bnRSZWdpb25zW3RoaXMucmVnaW9uXSA9PT0gdGhpcy5pZCkge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgJyArIHRoaXMuaWQgKyAnIHRvIGJlIGRlbGV0ZWQnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgQVBJIHNlcnZlciBoYXMgY29ubmVjdGVkIHRvIHRoZSB0ZXJtaW5hbCBhbmQgdGVybWluYWwgaGFzIGNvbm5lY3RlZCB0byB0aGUgYnJva2VyXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHdhaXQgdGltZW91dCBpbiBzZWNvbmRzLCBkZWZhdWx0IGlzIDVtXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbnRlcnZhbEluTWlsbGlzZWNvbmRzIGludGVydmFsIGJldHdlZW4gYWNjb3VudCByZXBsaWNhIHJlbG9hZHMgd2hpbGUgd2FpdGluZyBmb3IgYSBjaGFuZ2UsIGRlZmF1bHQgaXMgMXNcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSB3aGljaCByZXNvbHZlcyB3aGVuIEFQSSBzZXJ2ZXIgaXMgY29ubmVjdGVkIHRvIHRoZSBicm9rZXJcbiAgICogQHRocm93cyB7VGltZW91dEVycm9yfSBpZiBhY2NvdW50IHJlcGxpY2EgaGFzIG5vdCBjb25uZWN0ZWQgdG8gdGhlIGJyb2tlciB3aXRoaW4gdGltZW91dCBhbGxvd2VkXG4gICAqL1xuICBhc3luYyB3YWl0Q29ubmVjdGVkKHRpbWVvdXRJblNlY29uZHMgPSAzMDAsIGludGVydmFsSW5NaWxsaXNlY29uZHMgPSAxMDAwKSB7XG4gICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gICAgd2hpbGUgKHRoaXMuY29ubmVjdGlvblN0YXR1cyAhPT0gJ0NPTk5FQ1RFRCcgJiYgKHN0YXJ0VGltZSArIHRpbWVvdXRJblNlY29uZHMgKiAxMDAwKSA+IERhdGUubm93KCkpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2RlbGF5KGludGVydmFsSW5NaWxsaXNlY29uZHMpO1xuICAgICAgYXdhaXQgdGhpcy5fcHJpbWFyeUFjY291bnQucmVsb2FkKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLmNvbm5lY3Rpb25TdGF0dXMgIT09ICdDT05ORUNURUQnKSB7XG4gICAgICB0aHJvdyBuZXcgVGltZW91dEVycm9yKCdUaW1lZCBvdXQgd2FpdGluZyBmb3IgYWNjb3VudCAnICsgdGhpcy5pZCArICcgdG8gY29ubmVjdCB0byB0aGUgYnJva2VyJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgdHJhZGluZyBhY2NvdW50IHJlcGxpY2FcbiAgICogQHBhcmFtIHtVcGRhdGVkTWV0YXRyYWRlckFjY291bnRSZXBsaWNhRHRvfSBtZXRhdHJhZGVyQWNjb3VudCB1cGRhdGVkIGFjY291bnQgcmVwbGljYSBpbmZvcm1hdGlvblxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgcmVwbGljYSBpcyB1cGRhdGVkXG4gICAqL1xuICBhc3luYyB1cGRhdGUobWV0YXRyYWRlckFjY291bnQpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC51cGRhdGVBY2NvdW50UmVwbGljYSh0aGlzLl9wcmltYXJ5QWNjb3VudC5pZCwgdGhpcy5pZCwgbWV0YXRyYWRlckFjY291bnQpO1xuICAgIGF3YWl0IHRoaXMuX3ByaW1hcnlBY2NvdW50LnJlbG9hZCgpO1xuICB9XG5cbiAgX2RlbGF5KHRpbWVvdXRJbk1pbGxpc2Vjb25kcykge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZShyZXMgPT4gc2V0VGltZW91dChyZXMsIHRpbWVvdXRJbk1pbGxpc2Vjb25kcykpO1xuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJUaW1lb3V0RXJyb3IiLCJNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2EiLCJpZCIsIl9kYXRhIiwiX2lkIiwic3RhdGUiLCJtYWdpYyIsImNvbm5lY3Rpb25TdGF0dXMiLCJxdW90ZVN0cmVhbWluZ0ludGVydmFsSW5TZWNvbmRzIiwic3ltYm9sIiwicmVsaWFiaWxpdHkiLCJ0YWdzIiwibWV0YWRhdGEiLCJyZXNvdXJjZVNsb3RzIiwiY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzIiwicmVnaW9uIiwiY3JlYXRlZEF0IiwiRGF0ZSIsInByaW1hcnlBY2NvdW50RnJvbUR0byIsInByaW1hcnlBY2NvdW50IiwiX3ByaW1hcnlBY2NvdW50IiwidXBkYXRlRGF0YSIsImRhdGEiLCJyZW1vdmUiLCJfbWV0YXRyYWRlckFjY291bnRDbGllbnQiLCJkZWxldGVBY2NvdW50UmVwbGljYSIsInJlbG9hZCIsImVyciIsIm5hbWUiLCJkZXBsb3kiLCJkZXBsb3lBY2NvdW50UmVwbGljYSIsInVuZGVwbG95IiwidW5kZXBsb3lBY2NvdW50UmVwbGljYSIsInJlZGVwbG95IiwicmVkZXBsb3lBY2NvdW50UmVwbGljYSIsImluY3JlYXNlUmVsaWFiaWxpdHkiLCJ3YWl0RGVwbG95ZWQiLCJ0aW1lb3V0SW5TZWNvbmRzIiwiaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyIsInN0YXJ0VGltZSIsIm5vdyIsIl9kZWxheSIsIndhaXRVbmRlcGxveWVkIiwid2FpdFJlbW92ZWQiLCJhY2NvdW50UmVnaW9ucyIsIndhaXRDb25uZWN0ZWQiLCJ1cGRhdGUiLCJtZXRhdHJhZGVyQWNjb3VudCIsInVwZGF0ZUFjY291bnRSZXBsaWNhIiwidGltZW91dEluTWlsbGlzZWNvbmRzIiwiUHJvbWlzZSIsInJlcyIsInNldFRpbWVvdXQiLCJjb25zdHJ1Y3RvciIsIm1ldGF0cmFkZXJBY2NvdW50Q2xpZW50Il0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLE9BQU9BLGtCQUFrQiwwQkFBMEI7QUFNcEMsSUFBQSxBQUFNQywyQkFBTixNQUFNQTtJQWNuQjs7O0dBR0MsR0FDRCxJQUFJQyxLQUFLO1FBQ1AsT0FBTyxJQUFJLENBQUNDLEtBQUssQ0FBQ0MsR0FBRztJQUN2QjtJQUVBOzs7O0dBSUMsR0FDRCxJQUFJQyxRQUFRO1FBQ1YsT0FBTyxJQUFJLENBQUNGLEtBQUssQ0FBQ0UsS0FBSztJQUN6QjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQ0gsS0FBSyxDQUFDRyxLQUFLO0lBQ3pCO0lBRUE7OztHQUdDLEdBQ0QsSUFBSUMsbUJBQW1CO1FBQ3JCLE9BQU8sSUFBSSxDQUFDSixLQUFLLENBQUNJLGdCQUFnQjtJQUNwQztJQUVBOzs7R0FHQyxHQUNELElBQUlDLGtDQUFrQztRQUNwQyxPQUFPLElBQUksQ0FBQ0wsS0FBSyxDQUFDSywrQkFBK0I7SUFDbkQ7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQyxTQUFTO1FBQ1gsT0FBTyxJQUFJLENBQUNOLEtBQUssQ0FBQ00sTUFBTTtJQUMxQjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLGNBQWM7UUFDaEIsT0FBTyxJQUFJLENBQUNQLEtBQUssQ0FBQ08sV0FBVztJQUMvQjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLE9BQU87UUFDVCxPQUFPLElBQUksQ0FBQ1IsS0FBSyxDQUFDUSxJQUFJO0lBQ3hCO0lBRUE7OztHQUdDLEdBQ0QsSUFBSUMsV0FBVztRQUNiLE9BQU8sSUFBSSxDQUFDVCxLQUFLLENBQUNTLFFBQVE7SUFDNUI7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNELElBQUlDLGdCQUFnQjtRQUNsQixPQUFPLElBQUksQ0FBQ1YsS0FBSyxDQUFDVSxhQUFhO0lBQ2pDO0lBRUE7Ozs7Ozs7R0FPQyxHQUNELElBQUlDLDJCQUEyQjtRQUM3QixPQUFPLElBQUksQ0FBQ1gsS0FBSyxDQUFDVyx3QkFBd0I7SUFDNUM7SUFFQTs7O0tBR0csR0FDSCxJQUFJQyxTQUFTO1FBQ1gsT0FBTyxJQUFJLENBQUNaLEtBQUssQ0FBQ1ksTUFBTTtJQUMxQjtJQUVBOzs7R0FHQyxHQUNELElBQUlDLFlBQVk7UUFDZCxPQUFPLElBQUlDLEtBQUssSUFBSSxDQUFDZCxLQUFLLENBQUNhLFNBQVM7SUFDdEM7SUFFQTs7O0dBR0MsR0FDRCxJQUFJRSx3QkFBd0I7UUFDMUIsT0FBTyxJQUFJLENBQUNmLEtBQUssQ0FBQ2dCLGNBQWM7SUFDbEM7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQSxpQkFBaUI7UUFDbkIsT0FBTyxJQUFJLENBQUNDLGVBQWU7SUFDN0I7SUFFQTs7O0dBR0MsR0FDREMsV0FBV0MsSUFBSSxFQUFFO1FBQ2YsSUFBSSxDQUFDbkIsS0FBSyxHQUFHbUI7SUFDZjtJQUVBOzs7R0FHQyxHQUNELEFBQU1DOztlQUFOLG9CQUFBO1lBQ0UsTUFBTSxNQUFLQyx3QkFBd0IsQ0FBQ0Msb0JBQW9CLENBQUMsTUFBS04sY0FBYyxDQUFDakIsRUFBRSxFQUFFLE1BQUtBLEVBQUU7WUFDeEYsSUFBSTtnQkFDRixNQUFNLE1BQUtrQixlQUFlLENBQUNNLE1BQU07WUFDbkMsRUFBRSxPQUFPQyxLQUFLO2dCQUNaLElBQUlBLElBQUlDLElBQUksS0FBSyxpQkFBaUI7b0JBQ2hDLE1BQU1EO2dCQUNSO1lBQ0Y7UUFDRjs7SUFFQTs7OztHQUlDLEdBQ0QsQUFBTUU7O2VBQU4sb0JBQUE7WUFDRSxNQUFNLE1BQUtMLHdCQUF3QixDQUFDTSxvQkFBb0IsQ0FBQyxNQUFLWCxjQUFjLENBQUNqQixFQUFFLEVBQUUsTUFBS0EsRUFBRTtZQUN4RixNQUFNLE1BQUtrQixlQUFlLENBQUNNLE1BQU07UUFDbkM7O0lBRUE7Ozs7R0FJQyxHQUNELEFBQU1LOztlQUFOLG9CQUFBO1lBQ0UsTUFBTSxNQUFLUCx3QkFBd0IsQ0FBQ1Esc0JBQXNCLENBQUMsTUFBS2IsY0FBYyxDQUFDakIsRUFBRSxFQUFFLE1BQUtBLEVBQUU7WUFDMUYsTUFBTSxNQUFLa0IsZUFBZSxDQUFDTSxNQUFNO1FBQ25DOztJQUVBOzs7R0FHQyxHQUNELEFBQU1POztlQUFOLG9CQUFBO1lBQ0UsTUFBTSxNQUFLVCx3QkFBd0IsQ0FBQ1Usc0JBQXNCLENBQUMsTUFBS2YsY0FBYyxDQUFDakIsRUFBRSxFQUFFLE1BQUtBLEVBQUU7WUFDMUYsTUFBTSxNQUFLa0IsZUFBZSxDQUFDTSxNQUFNO1FBQ25DOztJQUVBOzs7OztHQUtDLEdBQ0QsQUFBTVM7O2VBQU4sb0JBQUE7WUFDRSxNQUFNLE1BQUtYLHdCQUF3QixDQUFDVyxtQkFBbUIsQ0FBQyxNQUFLakMsRUFBRTtZQUMvRCxNQUFNLE1BQUtrQixlQUFlLENBQUNNLE1BQU07UUFDbkM7O0lBRUE7Ozs7OztHQU1DLEdBQ0QsQUFBTVUsYUFBYUMsbUJBQW1CLEdBQUcsRUFBRUMseUJBQXlCLElBQUk7O2VBQXhFLG9CQUFBO1lBQ0UsSUFBSUMsWUFBWXRCLEtBQUt1QixHQUFHO1lBQ3hCLE1BQU0sTUFBS3BCLGVBQWUsQ0FBQ00sTUFBTTtZQUNqQyxNQUFPLE1BQUtyQixLQUFLLEtBQUssY0FBYyxBQUFDa0MsWUFBWUYsbUJBQW1CLE9BQVFwQixLQUFLdUIsR0FBRyxHQUFJO2dCQUN0RixNQUFNLE1BQUtDLE1BQU0sQ0FBQ0g7Z0JBQ2xCLE1BQU0sTUFBS2xCLGVBQWUsQ0FBQ00sTUFBTTtZQUNuQztZQUNBLElBQUksTUFBS3JCLEtBQUssS0FBSyxZQUFZO2dCQUM3QixNQUFNLElBQUlMLGFBQWEsMkNBQTJDLE1BQUtFLEVBQUUsR0FBRztZQUM5RTtRQUNGOztJQUVBOzs7Ozs7R0FNQyxHQUNELEFBQU13QyxlQUFlTCxtQkFBbUIsR0FBRyxFQUFFQyx5QkFBeUIsSUFBSTs7ZUFBMUUsb0JBQUE7WUFDRSxJQUFJQyxZQUFZdEIsS0FBS3VCLEdBQUc7WUFDeEIsTUFBTSxNQUFLcEIsZUFBZSxDQUFDTSxNQUFNO1lBQ2pDLE1BQU8sTUFBS3JCLEtBQUssS0FBSyxnQkFBZ0IsQUFBQ2tDLFlBQVlGLG1CQUFtQixPQUFRcEIsS0FBS3VCLEdBQUcsR0FBSTtnQkFDeEYsTUFBTSxNQUFLQyxNQUFNLENBQUNIO2dCQUNsQixNQUFNLE1BQUtsQixlQUFlLENBQUNNLE1BQU07WUFDbkM7WUFDQSxJQUFJLE1BQUtyQixLQUFLLEtBQUssY0FBYztnQkFDL0IsTUFBTSxJQUFJTCxhQUFhLDJDQUEyQyxNQUFLRSxFQUFFLEdBQUc7WUFDOUU7UUFDRjs7SUFFQTs7Ozs7O0dBTUMsR0FDRCxBQUFNeUMsWUFBWU4sbUJBQW1CLEdBQUcsRUFBRUMseUJBQXlCLElBQUk7O2VBQXZFLG9CQUFBO1lBQ0UsSUFBSUMsWUFBWXRCLEtBQUt1QixHQUFHO1lBQ3hCLE1BQU0sTUFBS3BCLGVBQWUsQ0FBQ00sTUFBTTtZQUNqQyxNQUFPYSxZQUFZRixtQkFBbUIsT0FBT3BCLEtBQUt1QixHQUFHLE1BQ2pELE1BQUtwQixlQUFlLENBQUN3QixjQUFjLENBQUMsTUFBSzdCLE1BQU0sQ0FBQyxLQUFLLE1BQUtiLEVBQUUsQ0FBRTtnQkFDaEUsTUFBTSxNQUFLdUMsTUFBTSxDQUFDSDtnQkFDbEIsTUFBTSxNQUFLbEIsZUFBZSxDQUFDTSxNQUFNO1lBQ25DO1lBQ0EsSUFBRyxNQUFLTixlQUFlLENBQUN3QixjQUFjLENBQUMsTUFBSzdCLE1BQU0sQ0FBQyxLQUFLLE1BQUtiLEVBQUUsRUFBRTtnQkFDL0QsTUFBTSxJQUFJRixhQUFhLG1DQUFtQyxNQUFLRSxFQUFFLEdBQUc7WUFDdEU7UUFDRjs7SUFFQTs7Ozs7O0dBTUMsR0FDRCxBQUFNMkMsY0FBY1IsbUJBQW1CLEdBQUcsRUFBRUMseUJBQXlCLElBQUk7O2VBQXpFLG9CQUFBO1lBQ0UsSUFBSUMsWUFBWXRCLEtBQUt1QixHQUFHO1lBQ3hCLE1BQU0sTUFBS3BCLGVBQWUsQ0FBQ00sTUFBTTtZQUNqQyxNQUFPLE1BQUtuQixnQkFBZ0IsS0FBSyxlQUFlLEFBQUNnQyxZQUFZRixtQkFBbUIsT0FBUXBCLEtBQUt1QixHQUFHLEdBQUk7Z0JBQ2xHLE1BQU0sTUFBS0MsTUFBTSxDQUFDSDtnQkFDbEIsTUFBTSxNQUFLbEIsZUFBZSxDQUFDTSxNQUFNO1lBQ25DO1lBQ0EsSUFBSSxNQUFLbkIsZ0JBQWdCLEtBQUssYUFBYTtnQkFDekMsTUFBTSxJQUFJUCxhQUFhLG1DQUFtQyxNQUFLRSxFQUFFLEdBQUc7WUFDdEU7UUFDRjs7SUFFQTs7OztHQUlDLEdBQ0QsQUFBTTRDLE9BQU9DLGlCQUFpQjs7ZUFBOUIsb0JBQUE7WUFDRSxNQUFNLE1BQUt2Qix3QkFBd0IsQ0FBQ3dCLG9CQUFvQixDQUFDLE1BQUs1QixlQUFlLENBQUNsQixFQUFFLEVBQUUsTUFBS0EsRUFBRSxFQUFFNkM7WUFDM0YsTUFBTSxNQUFLM0IsZUFBZSxDQUFDTSxNQUFNO1FBQ25DOztJQUVBZSxPQUFPUSxxQkFBcUIsRUFBRTtRQUM1QixPQUFPLElBQUlDLFFBQVFDLENBQUFBLE1BQU9DLFdBQVdELEtBQUtGO0lBQzVDO0lBdFNBOzs7OztHQUtDLEdBQ0RJLFlBQVkvQixJQUFJLEVBQUVILGNBQWMsRUFBRW1DLHVCQUF1QixDQUFFO1FBQ3pELElBQUksQ0FBQ25ELEtBQUssR0FBR21CO1FBQ2IsSUFBSSxDQUFDRixlQUFlLEdBQUdEO1FBQ3ZCLElBQUksQ0FBQ0ssd0JBQXdCLEdBQUc4QjtJQUNsQztBQThSRjtBQS9TQSxvR0FBb0c7QUFFcEc7O0NBRUMsR0FDRCxTQUFxQnJELHNDQTBTcEIifQ==