UNPKG

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)

616 lines (615 loc) 77 kB
'use strict'; 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'; import RpcMetaApiConnectionInstance from './rpcMetaApiConnectionInstance'; import StreamingMetaApiConnectionInstance from './streamingMetaApiConnectionInstance'; import HistoryDatabase from './historyDatabase/index'; import ExpertAdvisor from './expertAdvisor'; import { ValidationError } from '../clients/errorHandler'; import MetatraderAccountReplica from './metatraderAccountReplica'; //eslint-disable-next-line max-len import { Reliability, State, Version, ConnectionStatus, CopyFactoryRoles, Type, AccountConnection, ConfigurationLink, MetatraderAccountDto, DedicatedIp } from '../clients/metaApi/metatraderAccount.client'; let MetatraderAccount = class MetatraderAccount { /** * Returns unique account id * @return {string} unique account id */ get id() { return this._data._id; } /** * Returns current account state. One of CREATED, DEPLOYING, DEPLOYED, DEPLOY_FAILED, UNDEPLOYING, * UNDEPLOYED, UNDEPLOY_FAILED, DELETING, DELETE_FAILED, REDEPLOY_FAILED, DRAFT * @return {State} current account 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 reliability value */ get reliability() { return this._data.reliability; } /** * Returns user-defined account tags * @return {Array<string>} user-defined account tags */ get tags() { return this._data.tags; } /** * Returns extra information which can be stored together with your account * @return {Object} extra information which can be stored together with your account */ get metadata() { return this._data.metadata; } /** * Returns number of resource slots to allocate to account. 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 */ get resourceSlots() { return this._data.resourceSlots; } /** * Returns the number of CopyFactory 2 resource slots to allocate to account. * 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 to CopyFactory 2 by specifying copyFactoryRoles field. * @return {number} number of CopyFactory 2 resource slots to allocate to account */ get copyFactoryResourceSlots() { return this._data.copyFactoryResourceSlots; } /** * Returns account region * @return {string} account region value */ get region() { return this._data.region; } /** * Returns the time account was created at, in ISO format * @returns {string} the time account was created at, in ISO format */ get createdAt() { return new Date(this._data.createdAt); } /** * Returns human-readable account name * @return {string} human-readable account name */ get name() { return this._data.name; } /** * Returns flag indicating if trades should be placed as manual trades on this account * @return {boolean} flag indicating if trades should be placed as manual trades on this account */ get manualTrades() { return this._data.manualTrades; } /** * Returns default trade slippage in points * @return {number} default trade slippage in points */ get slippage() { return this._data.slippage; } /** * Returns id of the account's provisioning profile * @return {string} id of the account's provisioning profile */ get provisioningProfileId() { return this._data.provisioningProfileId; } /** * Returns MetaTrader account login * @return {string} MetaTrader account number */ get login() { return this._data.login; } /** * Returns MetaTrader server name to connect to * @return {string} MetaTrader server name to connect to */ get server() { return this._data.server; } /** * Returns account type. Possible values are cloud-g1, cloud-g2 * @return {Type} account type */ get type() { return this._data.type; } /** * Returns MT version. Possible values are 4 and 5 * @return {Version} MT version */ get version() { return this._data.version; } /** * Returns hash-code of the account * @return {number} hash-code of the account */ get hash() { return this._data.hash; } /** * Returns 3-character ISO currency code of the account base currency. The setting is to be used * for copy trading accounts which use national currencies only, such as some Brazilian brokers. You should not alter * this setting unless you understand what you are doing. * @return {string} 3-character ISO currency code of the account base currency */ get baseCurrency() { return this._data.baseCurrency; } /** * Returns account roles for CopyFactory2 application. Possible values are `PROVIDER` and `SUBSCRIBER` * @return {Array<CopyFactoryRoles>} account roles for CopyFactory2 application */ get copyFactoryRoles() { return this._data.copyFactoryRoles; } /** * Returns flag indicating that risk management API is enabled on account * @return {boolean} flag indicating that risk management API is enabled on account */ get riskManagementApiEnabled() { return this._data.riskManagementApiEnabled; } /** * Returns flag indicating that MetaStats API is enabled on account * @return {boolean} flag indicating that MetaStats API is enabled on account */ get metastatsApiEnabled() { return this._data.metastatsApiEnabled; } /** * Returns configured dedicated IP protocol to connect to the trading account terminal * @return {DedicatedIp} */ get allocateDedicatedIp() { return this._data.allocateDedicatedIp; } /** * Returns active account connections * @return {Array<AccountConnection>} active account connections */ get connections() { return this._data.connections; } /** * Returns flag indicating that account is primary * @return {boolean} flag indicating that account is primary */ get primaryReplica() { return this._data.primaryReplica; } /** * Returns user id * @return {string} user id */ get userId() { return this._data.userId; } /** * Returns primary account id * @return {string} primary account id */ get primaryAccountId() { return this._data.primaryAccountId; } /** * Returns account replicas from DTO * @return {MetatraderAccountReplica[]} account replicas from DTO */ get accountReplicas() { return this._data.accountReplicas; } /** * Returns account replica instances * @return {MetatraderAccountReplica[]} account replica instances */ get replicas() { return this._replicas; } /** * Returns a dictionary with account's available regions and replicas * @returns {{[region: string]: string}} */ get accountRegions() { const regions = { [this.region]: this.id }; this.replicas.forEach((replica)=>regions[replica.region] = replica.id); return regions; } /** * Reloads MetaTrader account from API * @return {Promise} promise resolving when MetaTrader account is updated */ reload() { var _this = this; return _async_to_generator(function*() { _this._data = yield _this._metatraderAccountClient.getAccount(_this.id); const updatedReplicaData = _this._data.accountReplicas || []; const regions = updatedReplicaData.map((replica)=>replica.region); const createdReplicaRegions = _this._replicas.map((replica)=>replica.region); _this._replicas = _this._replicas.filter((replica)=>regions.includes(replica.region)); _this._replicas.forEach((replica)=>{ const updatedData = updatedReplicaData.find((replicaData)=>replicaData.region === replica.region); replica.updateData(updatedData); }); updatedReplicaData.forEach((replica)=>{ if (!createdReplicaRegions.includes(replica.region)) { _this._replicas.push(new MetatraderAccountReplica(replica, _this, _this._metatraderAccountClient)); } }); })(); } /** * Removes a trading account and stops the API server serving the account. * The account state such as downloaded market data history will be removed as well when you remove the account. * @return {Promise} promise resolving when account is scheduled for deletion */ remove() { var _this = this; return _async_to_generator(function*() { _this._connectionRegistry.remove(_this.id); yield _this._metatraderAccountClient.deleteAccount(_this.id); const fileManager = HistoryDatabase.getInstance(); yield fileManager.clear(_this.id, _this._application); if (_this.type !== 'self-hosted') { try { yield _this.reload(); } catch (err) { if (err.name !== 'NotFoundError') { throw err; } } } })(); } /** * Starts API server and trading terminal for trading account. * This request will be ignored if the account is already deployed. * @returns {Promise} promise resolving when account is scheduled for deployment */ deploy() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.deployAccount(_this.id); yield _this.reload(); })(); } /** * Stops API server and trading terminal for trading account. * This request will be ignored if trading account is already undeployed * @returns {Promise} promise resolving when account is scheduled for undeployment */ undeploy() { var _this = this; return _async_to_generator(function*() { _this._connectionRegistry.remove(_this.id); yield _this._metatraderAccountClient.undeployAccount(_this.id); yield _this.reload(); })(); } /** * Redeploys trading account. This is equivalent to undeploy immediately followed by deploy * @returns {Promise} promise resolving when account is scheduled for redeployment */ redeploy() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.redeployAccount(_this.id); yield _this.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 reliability is increased */ increaseReliability() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.increaseReliability(_this.id); yield _this.reload(); })(); } /** * Enables risk management API for trading account. * The account will be temporary stopped to perform this action. * Note that risk management API is a paid option * @returns {Promise} promise resolving when account risk management is enabled */ enableRiskManagementApi() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.enableRiskManagementApi(_this.id); yield _this.reload(); })(); } /** * Enables MetaStats API for trading account. * The account will be temporary stopped to perform this action. * Note that this is a paid option * @returns {Promise} promise resolving when account MetaStats API is enabled */ enableMetaStatsApi() { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.enableMetaStatsApi(_this.id); yield _this.reload(); })(); } /** * Waits until API server has finished deployment and account reached the DEPLOYED state * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deployed * @throws {TimeoutError} if account have 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.reload(); while(_this.state !== 'DEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (_this.state !== 'DEPLOYED') { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be deployed'); } })(); } /** * Waits until API server has finished undeployment and account reached the UNDEPLOYED state * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deployed * @throws {TimeoutError} if account have 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.reload(); while(_this.state !== 'UNDEPLOYED' && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (_this.state !== 'UNDEPLOYED') { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be undeployed'); } })(); } /** * Waits until account has been deleted * @param {number} timeoutInSeconds wait timeout in seconds, default is 5m * @param {number} intervalInMilliseconds interval between account reloads while waiting for a change, default is 1s * @return {Promise} promise which resolves when account is deleted * @throws {TimeoutError} if account was not deleted within timeout allowed */ waitRemoved(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { let startTime = Date.now(); try { yield _this.reload(); while(startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to be deleted'); } catch (err) { if (err.name === 'NotFoundError') { return; } else { throw err; } } })(); } /** * 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 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 have not connected to the broker within timeout allowed */ waitConnected(timeoutInSeconds = 300, intervalInMilliseconds = 1000) { var _this = this; return _async_to_generator(function*() { const checkConnected = ()=>{ return [ _this.connectionStatus ].concat(_this.replicas.map((replica)=>replica.connectionStatus)).includes('CONNECTED'); }; let startTime = Date.now(); yield _this.reload(); while(!checkConnected() && startTime + timeoutInSeconds * 1000 > Date.now()){ yield _this._delay(intervalInMilliseconds); yield _this.reload(); } if (!checkConnected()) { throw new TimeoutError('Timed out waiting for account ' + _this.id + ' to connect to the broker'); } })(); } /** * Connects to MetaApi. There is only one connection per account. Subsequent calls to this method will return the same connection. * @param {HistoryStorage} historyStorage optional history storage * @param {Date} [historyStartTime] history start time. Used for tests * @return {StreamingMetaApiConnectionInstance} MetaApi connection instance */ getStreamingConnection(historyStorage, historyStartTime) { if (this._metaApiWebsocketClient.region && this._metaApiWebsocketClient.region !== this.region) { throw new ValidationError(`Account ${this.id} is not on specified region ${this._metaApiWebsocketClient.region}`); } return this._connectionRegistry.connectStreaming(this, historyStorage, historyStartTime); } /** * Connects to MetaApi via RPC connection instance. * @returns {RpcMetaApiConnectionInstance} MetaApi connection instance */ getRPCConnection() { if (this._metaApiWebsocketClient.region && this._metaApiWebsocketClient.region !== this.region) { throw new ValidationError(`Account ${this.id} is not on specified region ${this._metaApiWebsocketClient.region}`); } return this._connectionRegistry.connectRpc(this); } /** * Updates trading account. * Please redeploy the trading account in order for updated settings to take effect * @param {MetatraderAccountUpdateDto} account updated account information * @return {Promise} promise resolving when account is updated */ update(account) { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.updateAccount(_this.id, account); yield _this.reload(); })(); } /** * Creates a trading account replica in a region different from trading account region and starts a cloud API server for it * @param {NewMetaTraderAccountDto} account MetaTrader account data * @return {Promise<MetatraderAccountReplica>} promise resolving with created MetaTrader account replica entity */ createReplica(account) { var _this = this; return _async_to_generator(function*() { yield _this._metatraderAccountClient.createAccountReplica(_this.id, account); yield _this.reload(); return _this._replicas.find((r)=>r.region === account.region); })(); } /** * Retrieves expert advisor of current account * @returns {Promise<ExpertAdvisor[]>} promise resolving with an array of expert advisor entities */ getExpertAdvisors() { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); let expertAdvisors = yield _this._expertAdvisorClient.getExpertAdvisors(_this.id); return expertAdvisors.map((e)=>new ExpertAdvisor(e, _this.id, _this._expertAdvisorClient)); })(); } /** * Retrieves a expert advisor of current account by id * @param {String} expertId expert advisor id * @returns {Promise<ExpertAdvisor>} promise resolving with expert advisor entity */ getExpertAdvisor(expertId) { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); let expertAdvisor = yield _this._expertAdvisorClient.getExpertAdvisor(_this.id, expertId); return new ExpertAdvisor(expertAdvisor, _this.id, _this._expertAdvisorClient); })(); } /** * Creates an expert advisor * @param {string} expertId expert advisor id * @param {NewExpertAdvisorDto} expert expert advisor data * @returns {Promise<ExpertAdvisor>} promise resolving with expert advisor entity */ createExpertAdvisor(expertId, expert) { var _this = this; return _async_to_generator(function*() { _this._checkExpertAdvisorAllowed(); yield _this._expertAdvisorClient.updateExpertAdvisor(_this.id, expertId, expert); return _this.getExpertAdvisor(expertId); })(); } /** * Returns historical candles for a specific symbol and timeframe from the MetaTrader account. * See https://metaapi.cloud/docs/client/restApi/api/retrieveMarketData/readHistoricalCandles/ * @param {string} symbol symbol to retrieve candles for (e.g. a currency pair or an index) * @param {string} timeframe defines the timeframe according to which the candles must be generated. Allowed values * for MT5 are 1m, 2m, 3m, 4m, 5m, 6m, 10m, 12m, 15m, 20m, 30m, 1h, 2h, 3h, 4h, 6h, 8h, 12h, 1d, 1w, 1mn. Allowed * values for MT4 are 1m, 5m, 15m 30m, 1h, 4h, 1d, 1w, 1mn * @param {Date} [startTime] time to start loading candles from. Note that candles are loaded in backwards direction, so * this should be the latest time. Leave empty to request latest candles. * @param {number} [limit] maximum number of candles to retrieve. Must be less or equal to 1000 * @return {Promise<Array<MetatraderCandle>>} promise resolving with historical candles downloaded */ getHistoricalCandles(symbol, timeframe, startTime, limit) { return this._historicalMarketDataClient.getHistoricalCandles(this.id, this.region, symbol, timeframe, startTime, limit); } /** * Returns historical ticks for a specific symbol from the MetaTrader account. This API is not supported by MT4 * accounts. * See https://metaapi.cloud/docs/client/restApi/api/retrieveMarketData/readHistoricalTicks/ * @param {string} symbol symbol to retrieve ticks for (e.g. a currency pair or an index) * @param {Date} [startTime] time to start loading ticks from. Note that candles are loaded in forward direction, so * this should be the earliest time. Leave empty to request latest candles. * @param {number} [offset] number of ticks to skip (you can use it to avoid requesting ticks from previous request * twice) * @param {number} [limit] maximum number of ticks to retrieve. Must be less or equal to 1000 * @return {Promise<Array<MetatraderTick>>} promise resolving with historical ticks downloaded */ getHistoricalTicks(symbol, startTime, offset, limit) { return this._historicalMarketDataClient.getHistoricalTicks(this.id, this.region, symbol, startTime, offset, limit); } /** * Generates trading account configuration link by account id. * @param {number} [ttlInDays] Lifetime of the link in days. Default is 7. * @return {Promise<ConfigurationLink>} promise resolving with configuration link */ createConfigurationLink(ttlInDays) { var _this = this; return _async_to_generator(function*() { const configurationLink = yield _this._metatraderAccountClient.createConfigurationLink(_this.id, ttlInDays); return configurationLink; })(); } _checkExpertAdvisorAllowed() { if (this.version !== 4 || this.type !== 'cloud-g1') { throw new ValidationError('Custom expert advisor is available only for MT4 G1 accounts'); } } _delay(timeoutInMilliseconds) { return new Promise((res)=>setTimeout(res, timeoutInMilliseconds)); } /** * Constructs a MetaTrader account entity * @param {MetatraderAccountDto} data MetaTrader account data * @param {MetatraderAccountClient} metatraderAccountClient MetaTrader account REST API client * @param {MetaApiWebsocketClient} metaApiWebsocketClient MetaApi websocket client * @param {ConnectionRegistry} connectionRegistry metatrader account connection registry * @param {ExpertAdvisorClient} expertAdvisorClient expert advisor REST API client * @param {HistoricalMarketDataClient} historicalMarketDataClient historical market data HTTP API client * @param {string} application application name */ constructor(data, metatraderAccountClient, metaApiWebsocketClient, connectionRegistry, expertAdvisorClient, historicalMarketDataClient, application){ this._data = data; this._metatraderAccountClient = metatraderAccountClient; this._metaApiWebsocketClient = metaApiWebsocketClient; this._connectionRegistry = connectionRegistry; this._expertAdvisorClient = expertAdvisorClient; this._historicalMarketDataClient = historicalMarketDataClient; this._application = application; this._replicas = (data.accountReplicas || []).map((replica)=>new MetatraderAccountReplica(replica, this, metatraderAccountClient)); } }; /** * Implements a MetaTrader account entity */ export { MetatraderAccount as default }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBUaW1lb3V0RXJyb3IgZnJvbSAnLi4vY2xpZW50cy90aW1lb3V0RXJyb3InO1xuaW1wb3J0IFJwY01ldGFBcGlDb25uZWN0aW9uSW5zdGFuY2UgZnJvbSAnLi9ycGNNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlJztcbmltcG9ydCBTdHJlYW1pbmdNZXRhQXBpQ29ubmVjdGlvbkluc3RhbmNlIGZyb20gJy4vc3RyZWFtaW5nTWV0YUFwaUNvbm5lY3Rpb25JbnN0YW5jZSc7XG5pbXBvcnQgSGlzdG9yeURhdGFiYXNlIGZyb20gJy4vaGlzdG9yeURhdGFiYXNlL2luZGV4JztcbmltcG9ydCBFeHBlcnRBZHZpc29yIGZyb20gJy4vZXhwZXJ0QWR2aXNvcic7XG5pbXBvcnQge1ZhbGlkYXRpb25FcnJvcn0gZnJvbSAnLi4vY2xpZW50cy9lcnJvckhhbmRsZXInO1xuaW1wb3J0IE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYSBmcm9tICcuL21ldGF0cmFkZXJBY2NvdW50UmVwbGljYSc7XG4vL2VzbGludC1kaXNhYmxlLW5leHQtbGluZSBtYXgtbGVuXG5pbXBvcnQge1xuICBSZWxpYWJpbGl0eSwgU3RhdGUsIFZlcnNpb24sIENvbm5lY3Rpb25TdGF0dXMsIENvcHlGYWN0b3J5Um9sZXMsIFR5cGUsIEFjY291bnRDb25uZWN0aW9uLCBDb25maWd1cmF0aW9uTGluayxcbiAgTWV0YXRyYWRlckFjY291bnREdG8sIERlZGljYXRlZElwXG59IGZyb20gJy4uL2NsaWVudHMvbWV0YUFwaS9tZXRhdHJhZGVyQWNjb3VudC5jbGllbnQnO1xuXG4vKipcbiAqIEltcGxlbWVudHMgYSBNZXRhVHJhZGVyIGFjY291bnQgZW50aXR5XG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIE1ldGF0cmFkZXJBY2NvdW50IHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBhIE1ldGFUcmFkZXIgYWNjb3VudCBlbnRpdHlcbiAgICogQHBhcmFtIHtNZXRhdHJhZGVyQWNjb3VudER0b30gZGF0YSBNZXRhVHJhZGVyIGFjY291bnQgZGF0YVxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJBY2NvdW50Q2xpZW50fSBtZXRhdHJhZGVyQWNjb3VudENsaWVudCBNZXRhVHJhZGVyIGFjY291bnQgUkVTVCBBUEkgY2xpZW50XG4gICAqIEBwYXJhbSB7TWV0YUFwaVdlYnNvY2tldENsaWVudH0gbWV0YUFwaVdlYnNvY2tldENsaWVudCBNZXRhQXBpIHdlYnNvY2tldCBjbGllbnRcbiAgICogQHBhcmFtIHtDb25uZWN0aW9uUmVnaXN0cnl9IGNvbm5lY3Rpb25SZWdpc3RyeSBtZXRhdHJhZGVyIGFjY291bnQgY29ubmVjdGlvbiByZWdpc3RyeVxuICAgKiBAcGFyYW0ge0V4cGVydEFkdmlzb3JDbGllbnR9IGV4cGVydEFkdmlzb3JDbGllbnQgZXhwZXJ0IGFkdmlzb3IgUkVTVCBBUEkgY2xpZW50XG4gICAqIEBwYXJhbSB7SGlzdG9yaWNhbE1hcmtldERhdGFDbGllbnR9IGhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50IGhpc3RvcmljYWwgbWFya2V0IGRhdGEgSFRUUCBBUEkgY2xpZW50XG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcHBsaWNhdGlvbiBhcHBsaWNhdGlvbiBuYW1lXG4gICAqL1xuICBjb25zdHJ1Y3RvcihkYXRhLCBtZXRhdHJhZGVyQWNjb3VudENsaWVudCwgbWV0YUFwaVdlYnNvY2tldENsaWVudCwgY29ubmVjdGlvblJlZ2lzdHJ5LCBleHBlcnRBZHZpc29yQ2xpZW50LCBcbiAgICBoaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCwgYXBwbGljYXRpb24pIHtcbiAgICB0aGlzLl9kYXRhID0gZGF0YTtcbiAgICB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudCA9IG1ldGF0cmFkZXJBY2NvdW50Q2xpZW50O1xuICAgIHRoaXMuX21ldGFBcGlXZWJzb2NrZXRDbGllbnQgPSBtZXRhQXBpV2Vic29ja2V0Q2xpZW50O1xuICAgIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeSA9IGNvbm5lY3Rpb25SZWdpc3RyeTtcbiAgICB0aGlzLl9leHBlcnRBZHZpc29yQ2xpZW50ID0gZXhwZXJ0QWR2aXNvckNsaWVudDtcbiAgICB0aGlzLl9oaXN0b3JpY2FsTWFya2V0RGF0YUNsaWVudCA9IGhpc3RvcmljYWxNYXJrZXREYXRhQ2xpZW50O1xuICAgIHRoaXMuX2FwcGxpY2F0aW9uID0gYXBwbGljYXRpb247XG4gICAgdGhpcy5fcmVwbGljYXMgPSAoZGF0YS5hY2NvdW50UmVwbGljYXMgfHwgW10pXG4gICAgICAubWFwKHJlcGxpY2EgPT4gbmV3IE1ldGF0cmFkZXJBY2NvdW50UmVwbGljYShyZXBsaWNhLCB0aGlzLCBtZXRhdHJhZGVyQWNjb3VudENsaWVudCkpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdW5pcXVlIGFjY291bnQgaWRcbiAgICogQHJldHVybiB7c3RyaW5nfSB1bmlxdWUgYWNjb3VudCBpZFxuICAgKi9cbiAgZ2V0IGlkKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLl9pZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGN1cnJlbnQgYWNjb3VudCBzdGF0ZS4gT25lIG9mIENSRUFURUQsIERFUExPWUlORywgREVQTE9ZRUQsIERFUExPWV9GQUlMRUQsIFVOREVQTE9ZSU5HLFxuICAgKiBVTkRFUExPWUVELCBVTkRFUExPWV9GQUlMRUQsIERFTEVUSU5HLCBERUxFVEVfRkFJTEVELCBSRURFUExPWV9GQUlMRUQsIERSQUZUXG4gICAqIEByZXR1cm4ge1N0YXRlfSBjdXJyZW50IGFjY291bnQgc3RhdGVcbiAgICovXG4gIGdldCBzdGF0ZSgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5zdGF0ZTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIE1ldGFUcmFkZXIgbWFnaWMgdG8gcGxhY2UgdHJhZGVzIHVzaW5nXG4gICAqIEByZXR1cm4ge251bWJlcn0gTWV0YVRyYWRlciBtYWdpYyB0byBwbGFjZSB0cmFkZXMgdXNpbmdcbiAgICovXG4gIGdldCBtYWdpYygpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5tYWdpYztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRlcm1pbmFsICYgYnJva2VyIGNvbm5lY3Rpb24gc3RhdHVzLCBvbmUgb2YgQ09OTkVDVEVELCBESVNDT05ORUNURUQsIERJU0NPTk5FQ1RFRF9GUk9NX0JST0tFUlxuICAgKiBAcmV0dXJuIHtDb25uZWN0aW9uU3RhdHVzfSB0ZXJtaW5hbCAmIGJyb2tlciBjb25uZWN0aW9uIHN0YXR1c1xuICAgKi9cbiAgZ2V0IGNvbm5lY3Rpb25TdGF0dXMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29ubmVjdGlvblN0YXR1cztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHMgXG4gICAqIEByZXR1cm4ge251bWJlcn0gcXVvdGUgc3RyZWFtaW5nIGludGVydmFsIGluIHNlY29uZHNcbiAgICovXG4gIGdldCBxdW90ZVN0cmVhbWluZ0ludGVydmFsSW5TZWNvbmRzKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnF1b3RlU3RyZWFtaW5nSW50ZXJ2YWxJblNlY29uZHM7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIHN5bWJvbCBwcm92aWRlZCBieSBicm9rZXIgXG4gICAqIEByZXR1cm4ge3N0cmluZ30gYW55IHN5bWJvbCBwcm92aWRlZCBieSBicm9rZXJcbiAgICovXG4gIGdldCBzeW1ib2woKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuc3ltYm9sO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyByZWxpYWJpbGl0eSB2YWx1ZS4gUG9zc2libGUgdmFsdWVzIGFyZSByZWd1bGFyIGFuZCBoaWdoXG4gICAqIEByZXR1cm4ge1JlbGlhYmlsaXR5fSBhY2NvdW50IHJlbGlhYmlsaXR5IHZhbHVlXG4gICAqL1xuICBnZXQgcmVsaWFiaWxpdHkoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmVsaWFiaWxpdHk7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIHVzZXItZGVmaW5lZCBhY2NvdW50IHRhZ3NcbiAgICogQHJldHVybiB7QXJyYXk8c3RyaW5nPn0gdXNlci1kZWZpbmVkIGFjY291bnQgdGFnc1xuICAgKi9cbiAgZ2V0IHRhZ3MoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEudGFncztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGV4dHJhIGluZm9ybWF0aW9uIHdoaWNoIGNhbiBiZSBzdG9yZWQgdG9nZXRoZXIgd2l0aCB5b3VyIGFjY291bnRcbiAgICogQHJldHVybiB7T2JqZWN0fSBleHRyYSBpbmZvcm1hdGlvbiB3aGljaCBjYW4gYmUgc3RvcmVkIHRvZ2V0aGVyIHdpdGggeW91ciBhY2NvdW50XG4gICAqL1xuICBnZXQgbWV0YWRhdGEoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEubWV0YWRhdGE7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBudW1iZXIgb2YgcmVzb3VyY2Ugc2xvdHMgdG8gYWxsb2NhdGUgdG8gYWNjb3VudC4gQWxsb2NhdGluZyBleHRyYSByZXNvdXJjZSBzbG90c1xuICAgKiByZXN1bHRzIGluIGJldHRlciBhY2NvdW50IHBlcmZvcm1hbmNlIHVuZGVyIGxvYWQgd2hpY2ggaXMgdXNlZnVsIGZvciBzb21lIGFwcGxpY2F0aW9ucy4gRS5nLiBpZiB5b3UgaGF2ZSBtYW55XG4gICAqIGFjY291bnRzIGNvcHlpbmcgdGhlIHNhbWUgc3RyYXRlZ3kgdmlhIENvcHlGYWN0b3J5IEFQSSwgdGhlbiB5b3UgY2FuIGluY3JlYXNlIHJlc291cmNlU2xvdHMgdG8gZ2V0IGEgbG93ZXIgdHJhZGVcbiAgICogY29weWluZyBsYXRlbmN5LiBQbGVhc2Ugbm90ZSB0aGF0IGFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2Ugc2xvdHMgaXMgYSBwYWlkIG9wdGlvbi4gUGxlYXNlIG5vdGUgdGhhdCBoaWdoXG4gICAqIHJlbGlhYmlsaXR5IGFjY291bnRzIHVzZSByZWR1bmRhbnQgaW5mcmFzdHJ1Y3R1cmUsIHNvIHRoYXQgZWFjaCByZXNvdXJjZSBzbG90IGZvciBhIGhpZ2ggcmVsaWFiaWxpdHkgYWNjb3VudFxuICAgKiBpcyBiaWxsZWQgYXMgMiBzdGFuZGFyZCByZXNvdXJjZSBzbG90cy5cbiAgICogQHJldHVybiB7bnVtYmVyfSBudW1iZXIgb2YgcmVzb3VyY2Ugc2xvdHMgdG8gYWxsb2NhdGUgdG8gYWNjb3VudFxuICAgKi9cbiAgZ2V0IHJlc291cmNlU2xvdHMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmVzb3VyY2VTbG90cztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRoZSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50LlxuICAgKiBBbGxvY2F0aW5nIGV4dHJhIHJlc291cmNlIHNsb3RzIHJlc3VsdHMgaW4gbG93ZXIgdHJhZGUgY29weWluZyBsYXRlbmN5LiBQbGVhc2Ugbm90ZSB0aGF0IGFsbG9jYXRpbmcgZXh0cmEgcmVzb3VyY2VcbiAgICogc2xvdHMgaXMgYSBwYWlkIG9wdGlvbi4gUGxlYXNlIGFsc28gbm90ZSB0aGF0IENvcHlGYWN0b3J5IDIgdXNlcyByZWR1bmRhbnQgaW5mcmFzdHJ1Y3R1cmUgc28gdGhhdFxuICAgKiBlYWNoIENvcHlGYWN0b3J5IHJlc291cmNlIHNsb3QgaXMgYmlsbGVkIGFzIDIgc3RhbmRhcmQgcmVzb3VyY2Ugc2xvdHMuIFlvdSB3aWxsIGJlIGJpbGxlZCBmb3IgQ29weUZhY3RvcnkgMlxuICAgKiByZXNvdXJjZSBzbG90cyBvbmx5IGlmIHlvdSBoYXZlIGFkZGVkIHlvdXIgYWNjb3VudCB0byBDb3B5RmFjdG9yeSAyIGJ5IHNwZWNpZnlpbmcgY29weUZhY3RvcnlSb2xlcyBmaWVsZC5cbiAgICogQHJldHVybiB7bnVtYmVyfSBudW1iZXIgb2YgQ29weUZhY3RvcnkgMiByZXNvdXJjZSBzbG90cyB0byBhbGxvY2F0ZSB0byBhY2NvdW50XG4gICAqL1xuICBnZXQgY29weUZhY3RvcnlSZXNvdXJjZVNsb3RzKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmNvcHlGYWN0b3J5UmVzb3VyY2VTbG90cztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgcmVnaW9uXG4gICAqIEByZXR1cm4ge3N0cmluZ30gYWNjb3VudCByZWdpb24gdmFsdWVcbiAgICovXG4gIGdldCByZWdpb24oKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmVnaW9uO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgdGhlIHRpbWUgYWNjb3VudCB3YXMgY3JlYXRlZCBhdCwgaW4gSVNPIGZvcm1hdFxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSB0aGUgdGltZSBhY2NvdW50IHdhcyBjcmVhdGVkIGF0LCBpbiBJU08gZm9ybWF0XG4gICAqL1xuICBnZXQgY3JlYXRlZEF0KCkge1xuICAgIHJldHVybiBuZXcgRGF0ZSh0aGlzLl9kYXRhLmNyZWF0ZWRBdCk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBodW1hbi1yZWFkYWJsZSBhY2NvdW50IG5hbWVcbiAgICogQHJldHVybiB7c3RyaW5nfSBodW1hbi1yZWFkYWJsZSBhY2NvdW50IG5hbWVcbiAgICovXG4gIGdldCBuYW1lKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm5hbWU7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyBpZiB0cmFkZXMgc2hvdWxkIGJlIHBsYWNlZCBhcyBtYW51YWwgdHJhZGVzIG9uIHRoaXMgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtib29sZWFufSBmbGFnIGluZGljYXRpbmcgaWYgdHJhZGVzIHNob3VsZCBiZSBwbGFjZWQgYXMgbWFudWFsIHRyYWRlcyBvbiB0aGlzIGFjY291bnRcbiAgICovXG4gIGdldCBtYW51YWxUcmFkZXMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEubWFudWFsVHJhZGVzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZGVmYXVsdCB0cmFkZSBzbGlwcGFnZSBpbiBwb2ludHNcbiAgICogQHJldHVybiB7bnVtYmVyfSBkZWZhdWx0IHRyYWRlIHNsaXBwYWdlIGluIHBvaW50c1xuICAgKi9cbiAgZ2V0IHNsaXBwYWdlKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnNsaXBwYWdlO1xuICB9XG4gIFxuICAvKipcbiAgICogUmV0dXJucyBpZCBvZiB0aGUgYWNjb3VudCdzIHByb3Zpc2lvbmluZyBwcm9maWxlXG4gICAqIEByZXR1cm4ge3N0cmluZ30gaWQgb2YgdGhlIGFjY291bnQncyBwcm92aXNpb25pbmcgcHJvZmlsZVxuICAgKi9cbiAgZ2V0IHByb3Zpc2lvbmluZ1Byb2ZpbGVJZCgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5wcm92aXNpb25pbmdQcm9maWxlSWQ7XG4gIH1cbiAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIE1ldGFUcmFkZXIgYWNjb3VudCBsb2dpblxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IE1ldGFUcmFkZXIgYWNjb3VudCBudW1iZXJcbiAgICovXG4gIGdldCBsb2dpbigpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5sb2dpbjtcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqIEByZXR1cm4ge3N0cmluZ30gTWV0YVRyYWRlciBzZXJ2ZXIgbmFtZSB0byBjb25uZWN0IHRvXG4gICAqL1xuICBnZXQgc2VydmVyKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnNlcnZlcjtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgdHlwZS4gUG9zc2libGUgdmFsdWVzIGFyZSBjbG91ZC1nMSwgY2xvdWQtZzJcbiAgICogQHJldHVybiB7VHlwZX0gYWNjb3VudCB0eXBlXG4gICAqL1xuICBnZXQgdHlwZSgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS50eXBlO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgTVQgdmVyc2lvbi4gUG9zc2libGUgdmFsdWVzIGFyZSA0IGFuZCA1XG4gICAqIEByZXR1cm4ge1ZlcnNpb259IE1UIHZlcnNpb25cbiAgICovXG4gIGdldCB2ZXJzaW9uKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnZlcnNpb247XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBoYXNoLWNvZGUgb2YgdGhlIGFjY291bnRcbiAgICogQHJldHVybiB7bnVtYmVyfSBoYXNoLWNvZGUgb2YgdGhlIGFjY291bnRcbiAgICovXG4gIGdldCBoYXNoKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmhhc2g7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyAzLWNoYXJhY3RlciBJU08gY3VycmVuY3kgY29kZSBvZiB0aGUgYWNjb3VudCBiYXNlIGN1cnJlbmN5LiBUaGUgc2V0dGluZyBpcyB0byBiZSB1c2VkXG4gICAqIGZvciBjb3B5IHRyYWRpbmcgYWNjb3VudHMgd2hpY2ggdXNlIG5hdGlvbmFsIGN1cnJlbmNpZXMgb25seSwgc3VjaCBhcyBzb21lIEJyYXppbGlhbiBicm9rZXJzLiBZb3Ugc2hvdWxkIG5vdCBhbHRlclxuICAgKiB0aGlzIHNldHRpbmcgdW5sZXNzIHlvdSB1bmRlcnN0YW5kIHdoYXQgeW91IGFyZSBkb2luZy5cbiAgICogQHJldHVybiB7c3RyaW5nfSAzLWNoYXJhY3RlciBJU08gY3VycmVuY3kgY29kZSBvZiB0aGUgYWNjb3VudCBiYXNlIGN1cnJlbmN5XG4gICAqL1xuICBnZXQgYmFzZUN1cnJlbmN5KCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLmJhc2VDdXJyZW5jeTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvbi4gUG9zc2libGUgdmFsdWVzIGFyZSBgUFJPVklERVJgIGFuZCBgU1VCU0NSSUJFUmBcbiAgICogQHJldHVybiB7QXJyYXk8Q29weUZhY3RvcnlSb2xlcz59IGFjY291bnQgcm9sZXMgZm9yIENvcHlGYWN0b3J5MiBhcHBsaWNhdGlvblxuICAgKi9cbiAgZ2V0IGNvcHlGYWN0b3J5Um9sZXMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuY29weUZhY3RvcnlSb2xlcztcbiAgfVxuICBcbiAgLyoqXG4gICAqIFJldHVybnMgZmxhZyBpbmRpY2F0aW5nIHRoYXQgcmlzayBtYW5hZ2VtZW50IEFQSSBpcyBlbmFibGVkIG9uIGFjY291bnRcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gZmxhZyBpbmRpY2F0aW5nIHRoYXQgcmlzayBtYW5hZ2VtZW50IEFQSSBpcyBlbmFibGVkIG9uIGFjY291bnRcbiAgICovXG4gIGdldCByaXNrTWFuYWdlbWVudEFwaUVuYWJsZWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucmlza01hbmFnZW1lbnRBcGlFbmFibGVkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgZmxhZyBpbmRpY2F0aW5nIHRoYXQgTWV0YVN0YXRzIEFQSSBpcyBlbmFibGVkIG9uIGFjY291bnRcbiAgICogQHJldHVybiB7Ym9vbGVhbn0gZmxhZyBpbmRpY2F0aW5nIHRoYXQgTWV0YVN0YXRzIEFQSSBpcyBlbmFibGVkIG9uIGFjY291bnRcbiAgICovXG4gIGdldCBtZXRhc3RhdHNBcGlFbmFibGVkKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLm1ldGFzdGF0c0FwaUVuYWJsZWQ7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBjb25maWd1cmVkIGRlZGljYXRlZCBJUCBwcm90b2NvbCB0byBjb25uZWN0IHRvIHRoZSB0cmFkaW5nIGFjY291bnQgdGVybWluYWxcbiAgICogQHJldHVybiB7RGVkaWNhdGVkSXB9XG4gICAqL1xuICBnZXQgYWxsb2NhdGVEZWRpY2F0ZWRJcCgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5hbGxvY2F0ZURlZGljYXRlZElwO1xuICB9XG4gICAgXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjdGl2ZSBhY2NvdW50IGNvbm5lY3Rpb25zXG4gICAqIEByZXR1cm4ge0FycmF5PEFjY291bnRDb25uZWN0aW9uPn0gYWN0aXZlIGFjY291bnQgY29ubmVjdGlvbnNcbiAgICovXG4gIGdldCBjb25uZWN0aW9ucygpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5jb25uZWN0aW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGZsYWcgaW5kaWNhdGluZyB0aGF0IGFjY291bnQgaXMgcHJpbWFyeVxuICAgKiBAcmV0dXJuIHtib29sZWFufSBmbGFnIGluZGljYXRpbmcgdGhhdCBhY2NvdW50IGlzIHByaW1hcnlcbiAgICovXG4gIGdldCBwcmltYXJ5UmVwbGljYSgpIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YS5wcmltYXJ5UmVwbGljYTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHVzZXIgaWRcbiAgICogQHJldHVybiB7c3RyaW5nfSB1c2VyIGlkXG4gICAqL1xuICBnZXQgdXNlcklkKCkge1xuICAgIHJldHVybiB0aGlzLl9kYXRhLnVzZXJJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHByaW1hcnkgYWNjb3VudCBpZFxuICAgKiBAcmV0dXJuIHtzdHJpbmd9IHByaW1hcnkgYWNjb3VudCBpZFxuICAgKi9cbiAgZ2V0IHByaW1hcnlBY2NvdW50SWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEucHJpbWFyeUFjY291bnRJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFjY291bnQgcmVwbGljYXMgZnJvbSBEVE9cbiAgICogQHJldHVybiB7TWV0YXRyYWRlckFjY291bnRSZXBsaWNhW119IGFjY291bnQgcmVwbGljYXMgZnJvbSBEVE9cbiAgICovXG4gIGdldCBhY2NvdW50UmVwbGljYXMoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKiBAcmV0dXJuIHtNZXRhdHJhZGVyQWNjb3VudFJlcGxpY2FbXX0gYWNjb3VudCByZXBsaWNhIGluc3RhbmNlc1xuICAgKi9cbiAgZ2V0IHJlcGxpY2FzKCkge1xuICAgIHJldHVybiB0aGlzLl9yZXBsaWNhcztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGEgZGljdGlvbmFyeSB3aXRoIGFjY291bnQncyBhdmFpbGFibGUgcmVnaW9ucyBhbmQgcmVwbGljYXNcbiAgICogQHJldHVybnMge3tbcmVnaW9uOiBzdHJpbmddOiBzdHJpbmd9fVxuICAgKi9cbiAgZ2V0IGFjY291bnRSZWdpb25zKCkge1xuICAgIGNvbnN0IHJlZ2lvbnMgPSB7W3RoaXMucmVnaW9uXTogdGhpcy5pZH07XG4gICAgdGhpcy5yZXBsaWNhcy5mb3JFYWNoKHJlcGxpY2EgPT4gcmVnaW9uc1tyZXBsaWNhLnJlZ2lvbl0gPSByZXBsaWNhLmlkKTtcbiAgICByZXR1cm4gcmVnaW9ucztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWxvYWRzIE1ldGFUcmFkZXIgYWNjb3VudCBmcm9tIEFQSVxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIE1ldGFUcmFkZXIgYWNjb3VudCBpcyB1cGRhdGVkXG4gICAqL1xuICBhc3luYyByZWxvYWQoKSB7XG4gICAgdGhpcy5fZGF0YSA9IGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LmdldEFjY291bnQodGhpcy5pZCk7XG4gICAgY29uc3QgdXBkYXRlZFJlcGxpY2FEYXRhID0gKHRoaXMuX2RhdGEuYWNjb3VudFJlcGxpY2FzIHx8IFtdKTtcbiAgICBjb25zdCByZWdpb25zID0gdXBkYXRlZFJlcGxpY2FEYXRhLm1hcChyZXBsaWNhID0+IHJlcGxpY2EucmVnaW9uKTtcbiAgICBjb25zdCBjcmVhdGVkUmVwbGljYVJlZ2lvbnMgPSB0aGlzLl9yZXBsaWNhcy5tYXAocmVwbGljYSA9PiByZXBsaWNhLnJlZ2lvbik7XG4gICAgdGhpcy5fcmVwbGljYXMgPSB0aGlzLl9yZXBsaWNhcy5maWx0ZXIocmVwbGljYSA9PiByZWdpb25zLmluY2x1ZGVzKHJlcGxpY2EucmVnaW9uKSk7XG4gICAgdGhpcy5fcmVwbGljYXMuZm9yRWFjaChyZXBsaWNhID0+IHtcbiAgICAgIGNvbnN0IHVwZGF0ZWREYXRhID0gdXBkYXRlZFJlcGxpY2FEYXRhLmZpbmQocmVwbGljYURhdGEgPT4gcmVwbGljYURhdGEucmVnaW9uID09PSByZXBsaWNhLnJlZ2lvbik7XG4gICAgICByZXBsaWNhLnVwZGF0ZURhdGEodXBkYXRlZERhdGEpO1xuICAgIH0pO1xuICAgIHVwZGF0ZWRSZXBsaWNhRGF0YS5mb3JFYWNoKHJlcGxpY2EgPT4ge1xuICAgICAgaWYoIWNyZWF0ZWRSZXBsaWNhUmVnaW9ucy5pbmNsdWRlcyhyZXBsaWNhLnJlZ2lvbikpIHtcbiAgICAgICAgdGhpcy5fcmVwbGljYXMucHVzaChuZXcgTWV0YXRyYWRlckFjY291bnRSZXBsaWNhKHJlcGxpY2EsIHRoaXMsIHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50KSk7XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHRyYWRpbmcgYWNjb3VudCBhbmQgc3RvcHMgdGhlIEFQSSBzZXJ2ZXIgc2VydmluZyB0aGUgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgc3RhdGUgc3VjaCBhcyBkb3dubG9hZGVkIG1hcmtldCBkYXRhIGhpc3Rvcnkgd2lsbCBiZSByZW1vdmVkIGFzIHdlbGwgd2hlbiB5b3UgcmVtb3ZlIHRoZSBhY2NvdW50LlxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgaXMgc2NoZWR1bGVkIGZvciBkZWxldGlvblxuICAgKi9cbiAgYXN5bmMgcmVtb3ZlKCkge1xuICAgIHRoaXMuX2Nvbm5lY3Rpb25SZWdpc3RyeS5yZW1vdmUodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQuZGVsZXRlQWNjb3VudCh0aGlzLmlkKTtcbiAgICBjb25zdCBmaWxlTWFuYWdlciA9IEhpc3RvcnlEYXRhYmFzZS5nZXRJbnN0YW5jZSgpO1xuICAgIGF3YWl0IGZpbGVNYW5hZ2VyLmNsZWFyKHRoaXMuaWQsIHRoaXMuX2FwcGxpY2F0aW9uKTtcbiAgICBpZiAodGhpcy50eXBlICE9PSAnc2VsZi1ob3N0ZWQnKSB7XG4gICAgICB0cnkge1xuICAgICAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIGlmIChlcnIubmFtZSAhPT0gJ05vdEZvdW5kRXJyb3InKSB7XG4gICAgICAgICAgdGhyb3cgZXJyO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFN0YXJ0cyBBUEkgc2VydmVyIGFuZCB0cmFkaW5nIHRlcm1pbmFsIGZvciB0cmFkaW5nIGFjY291bnQuXG4gICAqIFRoaXMgcmVxdWVzdCB3aWxsIGJlIGlnbm9yZWQgaWYgdGhlIGFjY291bnQgaXMgYWxyZWFkeSBkZXBsb3llZC5cbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCBpcyBzY2hlZHVsZWQgZm9yIGRlcGxveW1lbnRcbiAgICovXG4gIGFzeW5jIGRlcGxveSgpIHtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC5kZXBsb3lBY2NvdW50KHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogU3RvcHMgQVBJIHNlcnZlciBhbmQgdHJhZGluZyB0ZXJtaW5hbCBmb3IgdHJhZGluZyBhY2NvdW50LlxuICAgKiBUaGlzIHJlcXVlc3Qgd2lsbCBiZSBpZ25vcmVkIGlmIHRyYWRpbmcgYWNjb3VudCBpcyBhbHJlYWR5IHVuZGVwbG95ZWRcbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCBpcyBzY2hlZHVsZWQgZm9yIHVuZGVwbG95bWVudFxuICAgKi9cbiAgYXN5bmMgdW5kZXBsb3koKSB7XG4gICAgdGhpcy5fY29ubmVjdGlvblJlZ2lzdHJ5LnJlbW92ZSh0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLl9tZXRhdHJhZGVyQWNjb3VudENsaWVudC51bmRlcGxveUFjY291bnQodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWRlcGxveXMgdHJhZGluZyBhY2NvdW50LiBUaGlzIGlzIGVxdWl2YWxlbnQgdG8gdW5kZXBsb3kgaW1tZWRpYXRlbHkgZm9sbG93ZWQgYnkgZGVwbG95XG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgaXMgc2NoZWR1bGVkIGZvciByZWRlcGxveW1lbnRcbiAgICovXG4gIGFzeW5jIHJlZGVwbG95KCkge1xuICAgIGF3YWl0IHRoaXMuX21ldGF0cmFkZXJBY2NvdW50Q2xpZW50LnJlZGVwbG95QWNjb3VudCh0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEluY3JlYXNlcyB0cmFkaW5nIGFjY291bnQgcmVsaWFiaWxpdHkgaW4gb3JkZXIgdG8gaW5jcmVhc2UgdGhlIGV4cGVjdGVkIGFjY291bnQgdXB0aW1lLlxuICAgKiBUaGUgYWNjb3VudCB3aWxsIGJlIHRlbXBvcmFyeSBzdG9wcGVkIHRvIHBlcmZvcm0gdGhpcyBhY3Rpb24uXG4gICAqIE5vdGUgdGhhdCBpbmNyZWFzaW5nIHJlbGlhYmlsaXR5IGlzIGEgcGFpZCBvcHRpb25cbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCByZWxpYWJpbGl0eSBpcyBpbmNyZWFzZWRcbiAgICovXG4gIGFzeW5jIGluY3JlYXNlUmVsaWFiaWxpdHkoKSB7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQuaW5jcmVhc2VSZWxpYWJpbGl0eSh0aGlzLmlkKTtcbiAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICB9XG5cbiAgLyoqXG4gICAqIEVuYWJsZXMgcmlzayBtYW5hZ2VtZW50IEFQSSBmb3IgdHJhZGluZyBhY2NvdW50LlxuICAgKiBUaGUgYWNjb3VudCB3aWxsIGJlIHRlbXBvcmFyeSBzdG9wcGVkIHRvIHBlcmZvcm0gdGhpcyBhY3Rpb24uXG4gICAqIE5vdGUgdGhhdCByaXNrIG1hbmFnZW1lbnQgQVBJIGlzIGEgcGFpZCBvcHRpb25cbiAgICogQHJldHVybnMge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gYWNjb3VudCByaXNrIG1hbmFnZW1lbnQgaXMgZW5hYmxlZFxuICAgKi9cbiAgYXN5bmMgZW5hYmxlUmlza01hbmFnZW1lbnRBcGkoKSB7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQuZW5hYmxlUmlza01hbmFnZW1lbnRBcGkodGhpcy5pZCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFbmFibGVzIE1ldGFTdGF0cyBBUEkgZm9yIHRyYWRpbmcgYWNjb3VudC5cbiAgICogVGhlIGFjY291bnQgd2lsbCBiZSB0ZW1wb3Jhcnkgc3RvcHBlZCB0byBwZXJmb3JtIHRoaXMgYWN0aW9uLlxuICAgKiBOb3RlIHRoYXQgdGhpcyBpcyBhIHBhaWQgb3B0aW9uXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHJlc29sdmluZyB3aGVuIGFjY291bnQgTWV0YVN0YXRzIEFQSSBpcyBlbmFibGVkXG4gICAqL1xuICBhc3luYyBlbmFibGVNZXRhU3RhdHNBcGkoKSB7XG4gICAgYXdhaXQgdGhpcy5fbWV0YXRyYWRlckFjY291bnRDbGllbnQuZW5hYmxlTWV0YVN0YXRzQXBpKHRoaXMuaWQpO1xuICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gIH1cblxuICAvKipcbiAgICogV2FpdHMgdW50aWwgQVBJIHNlcnZlciBoYXMgZmluaXNoZWQgZGVwbG95bWVudCBhbmQgYWNjb3VudCByZWFjaGVkIHRoZSBERVBMT1lFRCBzdGF0ZVxuICAgKiBAcGFyYW0ge251bWJlcn0gdGltZW91dEluU2Vjb25kcyB3YWl0IHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyA1bVxuICAgKiBAcGFyYW0ge251bWJlcn0gaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyBpbnRlcnZhbCBiZXR3ZWVuIGFjY291bnQgcmVsb2FkcyB3aGlsZSB3YWl0aW5nIGZvciBhIGNoYW5nZSwgZGVmYXVsdCBpcyAxc1xuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBwcm9taXNlIHdoaWNoIHJlc29sdmVzIHdoZW4gYWNjb3VudCBpcyBkZXBsb3llZFxuICAgKiBAdGhyb3dzIHtUaW1lb3V0RXJyb3J9IGlmIGFjY291bnQgaGF2ZSBub3QgcmVhY2hlZCB0aGUgREVQTE9ZRUQgc3RhdGUgd2l0aGluIHRpbWVvdXQgYWxsb3dlZFxuICAgKi9cbiAgYXN5bmMgd2FpdERlcGxveWVkKHRpbWVvdXRJblNlY29uZHMgPSAzMDAsIGludGVydmFsSW5NaWxsaXNlY29uZHMgPSAxMDAwKSB7XG4gICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgICB3aGlsZSAodGhpcy5zdGF0ZSAhPT0gJ0RFUExPWUVEJyAmJiAoc3RhcnRUaW1lICsgdGltZW91dEluU2Vjb25kcyAqIDEwMDApID4gRGF0ZS5ub3coKSkge1xuICAgICAgYXdhaXQgdGhpcy5fZGVsYXkoaW50ZXJ2YWxJbk1pbGxpc2Vjb25kcyk7XG4gICAgICBhd2FpdCB0aGlzLnJlbG9hZCgpO1xuICAgIH1cbiAgICBpZiAodGhpcy5zdGF0ZSAhPT0gJ0RFUExPWUVEJykge1xuICAgICAgdGhyb3cgbmV3IFRpbWVvdXRFcnJvcignVGltZWQgb3V0IHdhaXRpbmcgZm9yIGFjY291bnQgJyArIHRoaXMuaWQgKyAnIHRvIGJlIGRlcGxveWVkJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdhaXRzIHVudGlsIEFQSSBzZXJ2ZXIgaGFzIGZpbmlzaGVkIHVuZGVwbG95bWVudCBhbmQgYWNjb3VudCByZWFjaGVkIHRoZSBVTkRFUExPWUVEIHN0YXRlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSB0aW1lb3V0SW5TZWNvbmRzIHdhaXQgdGltZW91dCBpbiBzZWNvbmRzLCBkZWZhdWx0IGlzIDVtXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpbnRlcnZhbEluTWlsbGlzZWNvbmRzIGludGVydmFsIGJldHdlZW4gYWNjb3VudCByZWxvYWRzIHdoaWxlIHdhaXRpbmcgZm9yIGEgY2hhbmdlLCBkZWZhdWx0IGlzIDFzXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiBhY2NvdW50IGlzIGRlcGxveWVkXG4gICAqIEB0aHJvd3Mge1RpbWVvdXRFcnJvcn0gaWYgYWNjb3VudCBoYXZlIG5vdCByZWFjaGVkIHRoZSBVTkRFUExPWUVEIHN0YXRlIHdpdGhpbiB0aW1lb3V0IGFsbG93ZWRcbiAgICovXG4gIGFzeW5jIHdhaXRVbmRlcGxveWVkKHRpbWVvdXRJblNlY29uZHMgPSAzMDAsIGludGVydmFsSW5NaWxsaXNlY29uZHMgPSAxMDAwKSB7XG4gICAgbGV0IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG4gICAgYXdhaXQgdGhpcy5yZWxvYWQoKTtcbiAgICB3aGlsZSAodGhpcy5zdGF0ZSAhPT0gJ1VOREVQTE9ZRUQnICYmIChzdGFydFRpbWUgKyB0aW1lb3V0SW5TZWNvbmRzICogMTAwMCkgPiBEYXRlLm5vdygpKSB7XG4gICAgICBhd2FpdCB0aGlzLl9kZWxheShpbnRlcnZhbEluTWlsbGlzZWNvbmRzKTtcbiAgICAgIGF3YWl0IHRoaXMucmVsb2FkKCk7XG4gICAgfVxuICAgIGlmICh0aGlzLnN0YXRlICE9PSAnVU5ERVBMT1lFRCcpIHtcbiAgICAgIHRocm93IG5ldyBUaW1lb3V0RXJyb3IoJ1RpbWVkIG91dCB3YWl0aW5nIGZvciBhY2NvdW50ICcgKyB0aGlzLmlkICsgJyB0byBiZSB1bmRlcGxveWVkJyk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFdhaXRzIHVudGlsIGFjY291bnQgaGFzIGJlZW4gZGVsZXRlZFxuICAgKiBAcGFyYW0ge251bWJlcn0gdGltZW91dEluU2Vjb25kcyB3YWl0IHRpbWVvdXQgaW4gc2Vjb25kcywgZGVmYXVsdCBpcyA1bVxuICAgKiBAcGFyYW0ge251bWJlcn0gaW50ZXJ2YWxJbk1pbGxpc2Vjb25