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)

358 lines (357 loc) 41.1 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 HistoryStorage from './historyStorage'; import HistoryDatabase from './historyDatabase/index'; import { AVLTree } from 'binary-search-tree'; import LoggerManager from '../logger'; let MemoryHistoryStorage = class MemoryHistoryStorage extends HistoryStorage { /** * Initializes the storage and loads required data from a persistent storage * @param {string} accountId account id * @param {string} [application] application. Default is MetaApi */ initialize(accountId, application = 'MetaApi') { var _this = this, _superprop_get_initialize = ()=>super.initialize; return _async_to_generator(function*() { yield _superprop_get_initialize().call(_this, accountId, application); let { deals, historyOrders } = yield _this._historyDatabase.loadHistory(accountId, application); for (let deal of deals){ yield _this._addDeal(deal, true); } for (let historyOrder of historyOrders){ yield _this._addHistoryOrder(historyOrder, true); } })(); } /** * Resets the storage. Intended for use in tests * @returns {Promise} promise when the history is removed */ clear() { var _this = this; return _async_to_generator(function*() { _this._reset(); yield _this._historyDatabase.clear(_this._accountId, _this._application); })(); } /** * Returns the time of the last history order record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history order record stored in the history storage */ lastHistoryOrderTime(instanceNumber) { return this._maxHistoryOrderTime; } /** * Returns the time of the last history deal record stored in the history storage * @param {Number} [instanceNumber] index of an account instance connected * @returns {Date} the time of the last history deal record stored in the history storage */ lastDealTime(instanceNumber) { return this._maxDealTime; } /** * Invoked when a new MetaTrader history order is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderOrder} historyOrder new MetaTrader history order */ onHistoryOrderAdded(instanceIndex, historyOrder) { var _this = this; return _async_to_generator(function*() { yield _this._addHistoryOrder(historyOrder); })(); } /** * Invoked when a new MetaTrader history deal is added * @param {String} instanceIndex index of an account instance connected * @param {MetatraderDeal} deal new MetaTrader history deal */ onDealAdded(instanceIndex, deal) { var _this = this; return _async_to_generator(function*() { yield _this._addDeal(deal); })(); } /** * Returns all deals * @returns {Array<MetatraderDeal>} all deals */ get deals() { return this.getDealsByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns deals by ticket id * @param {string} id ticket id * @returns {Array<MetatraderDeal>} deals found */ getDealsByTicket(id) { let deals = Object.values(this._dealsByTicket[id] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by position id * @param {string} positionId position id * @returns {Array<MetatraderDeal>} deals found */ getDealsByPosition(positionId) { let deals = Object.values(this._dealsByPosition[positionId] || {}); deals.sort(this._dealsComparator); return deals; } /** * Returns deals by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderDeal>} deals found */ getDealsByTimeRange(startTime, endTime) { let deals = this._dealsByTime.betweenBounds({ $gte: { time: startTime, id: 0, entryType: '' }, $lte: { time: endTime, id: Number.MAX_VALUE, entryType: '' } }); return deals; } /** * Returns all history orders * @returns {Array<MetatraderOrder>} all history orders */ get historyOrders() { return this.getHistoryOrdersByTimeRange(new Date(0), new Date(8640000000000000)); } /** * Returns history orders by ticket id * @param {string} id ticket id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByTicket(id) { let historyOrders = Object.values(this._historyOrdersByTicket[id] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by position id * @param {string} positionId position id * @returns {Array<MetatraderOrder>} history orders found */ getHistoryOrdersByPosition(positionId) { let historyOrders = Object.values(this._historyOrdersByPosition[positionId] || {}); historyOrders.sort(this._historyOrdersComparator); return historyOrders; } /** * Returns history orders by time range * @param startTime start time, inclusive * @param endTime end time, inclusive * @returns {Array<MetatraderOrder>} hisotry orders found */ getHistoryOrdersByTimeRange(startTime, endTime) { let historyOrders = this._historyOrdersByTime.betweenBounds({ $gte: { doneTime: startTime, id: 0, type: '', state: '' }, $lte: { doneTime: endTime, id: Number.MAX_VALUE, type: '', state: '' } }); return historyOrders; } /** * Invoked when a synchronization of history deals on a MetaTrader account have finished to indicate progress of an * initial terminal state synchronization * @param {String} instanceIndex index of an account instance connected * @param {String} synchronizationId synchronization request id * @return {Promise} promise which resolves when the asynchronous event is processed */ onDealsSynchronized(instanceIndex, synchronizationId) { var _this = this, _superprop_get_onDealsSynchronized = ()=>super.onDealsSynchronized; return _async_to_generator(function*() { yield _this._flushDatabase(); yield _superprop_get_onDealsSynchronized().call(_this, instanceIndex, synchronizationId); })(); } _reset() { this._orderSynchronizationFinished = {}; this._dealSynchronizationFinished = {}; this._dealsByTicket = {}; this._dealsByPosition = {}; this._historyOrdersByTicket = {}; this._historyOrdersByPosition = {}; // eslint-disable-next-line complexity this._historyOrdersComparator = (o1, o2)=>{ let timeDiff = (o1.doneTime || new Date(0)).getTime() - (o2.doneTime || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = o1.id - o2.id; if (idDiff === 0) { if (o1.type > o2.type) { return 1; } else if (o1.type < o2.type) { return -1; } else { if (o1.state > o2.state) { return 1; } else if (o1.state < o2.state) { return -1; } else { return 0; } } } else { return idDiff; } } else { return timeDiff; } }; this._historyOrdersByTime = new AVLTree({ compareKeys: this._historyOrdersComparator }); this._dealsComparator = (d1, d2)=>{ let timeDiff = (d1.time || new Date(0)).getTime() - (d2.time || new Date(0)).getTime(); if (timeDiff === 0) { let idDiff = d1.id - d2.id; if (idDiff === 0) { if (d1.entryType > d2.entryType) { return 1; } else if (d1.entryType < d2.entryType) { return -1; } else { return 0; } } else { return idDiff; } } else { return timeDiff; } }; this._dealsByTime = new AVLTree({ compareKeys: this._dealsComparator }); this._maxHistoryOrderTime = new Date(0); this._maxDealTime = new Date(0); this._newHistoryOrders = []; this._newDeals = []; clearTimeout(this._flushTimeout); delete this._flushTimeout; } // eslint-disable-next-line complexity _addDeal(deal, existing) { var _this = this; return _async_to_generator(function*() { let key = _this._getDealKey(deal); _this._dealsByTicket[deal.id] = _this._dealsByTicket[deal.id] || {}; let newDeal = !existing && !_this._dealsByTicket[deal.id][key]; _this._dealsByTicket[deal.id][key] = deal; if (deal.positionId) { _this._dealsByPosition[deal.positionId] = _this._dealsByPosition[deal.positionId] || {}; _this._dealsByPosition[deal.positionId][key] = deal; } _this._dealsByTime.delete(deal); _this._dealsByTime.insert(deal, deal); if (deal.time && (!_this._maxDealTime || _this._maxDealTime.getTime() < deal.time.getTime())) { _this._maxDealTime = deal.time; } if (newDeal) { _this._newDeals.push(deal); clearTimeout(_this._flushTimeout); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 5000); } })(); } _getDealKey(deal) { return (deal.time || new Date(0)).toISOString() + ':' + deal.id + ':' + deal.entryType; } // eslint-disable-next-line complexity _addHistoryOrder(historyOrder, existing) { var _this = this; return _async_to_generator(function*() { let key = _this._getHistoryOrderKey(historyOrder); _this._historyOrdersByTicket[historyOrder.id] = _this._historyOrdersByTicket[historyOrder.id] || {}; let newHistoryOrder = !existing && !_this._historyOrdersByTicket[historyOrder.id][key]; _this._historyOrdersByTicket[historyOrder.id][key] = historyOrder; if (historyOrder.positionId) { _this._historyOrdersByPosition[historyOrder.positionId] = _this._historyOrdersByPosition[historyOrder.positionId] || {}; _this._historyOrdersByPosition[historyOrder.positionId][key] = historyOrder; } _this._historyOrdersByTime.delete(historyOrder); _this._historyOrdersByTime.insert(historyOrder, historyOrder); if (historyOrder.doneTime && (!_this._maxHistoryOrderTime || _this._maxHistoryOrderTime.getTime() < historyOrder.doneTime.getTime())) { _this._maxHistoryOrderTime = historyOrder.doneTime; } if (newHistoryOrder) { _this._newHistoryOrders.push(historyOrder); clearTimeout(_this._flushTimeout); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 5000); } })(); } _getHistoryOrderKey(historyOrder) { return (historyOrder.doneTime || new Date(0)).toISOString() + ':' + historyOrder.id + ':' + historyOrder.type + ':' + historyOrder.state; } _flushDatabase() { var _this = this; return _async_to_generator(function*() { if (_this._flushPromise) { yield _this._flushPromise; } if (_this._flushRunning) { return; } _this._flushRunning = true; let resolve; _this._flushPromise = new Promise((res)=>resolve = res); try { yield _this._historyDatabase.flush(_this._accountId, _this._application, _this._newHistoryOrders, _this._newDeals); _this._newHistoryOrders = []; _this._newDeals = []; _this._logger.debug(`${_this._accountId}: flushed history db`); } catch (err) { _this._logger.warn(`${_this._accountId}: error flushing history db`, err); _this._flushTimeout = setTimeout(_this._flushDatabase.bind(_this), 15000); } finally{ resolve(); _this._flushRunning = false; } })(); } /** * Constructs the in-memory history store instance */ constructor(){ super(); this._historyDatabase = HistoryDatabase.getInstance(); this._reset(); this._logger = LoggerManager.getLogger('MemoryHistoryStorage'); } }; /** * History storage which stores MetaTrader history in RAM */ export { MemoryHistoryStorage as default }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBIaXN0b3J5U3RvcmFnZSBmcm9tICcuL2hpc3RvcnlTdG9yYWdlJztcbmltcG9ydCBIaXN0b3J5RGF0YWJhc2UgZnJvbSAnLi9oaXN0b3J5RGF0YWJhc2UvaW5kZXgnO1xuaW1wb3J0IHsgQVZMVHJlZSB9IGZyb20gJ2JpbmFyeS1zZWFyY2gtdHJlZSc7XG5pbXBvcnQgTG9nZ2VyTWFuYWdlciBmcm9tICcuLi9sb2dnZXInO1xuXG4vKipcbiAqIEhpc3Rvcnkgc3RvcmFnZSB3aGljaCBzdG9yZXMgTWV0YVRyYWRlciBoaXN0b3J5IGluIFJBTVxuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNZW1vcnlIaXN0b3J5U3RvcmFnZSBleHRlbmRzIEhpc3RvcnlTdG9yYWdlIHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyB0aGUgaW4tbWVtb3J5IGhpc3Rvcnkgc3RvcmUgaW5zdGFuY2VcbiAgICovXG4gIGNvbnN0cnVjdG9yKCkge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5faGlzdG9yeURhdGFiYXNlID0gSGlzdG9yeURhdGFiYXNlLmdldEluc3RhbmNlKCk7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICB0aGlzLl9sb2dnZXIgPSBMb2dnZXJNYW5hZ2VyLmdldExvZ2dlcignTWVtb3J5SGlzdG9yeVN0b3JhZ2UnKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBJbml0aWFsaXplcyB0aGUgc3RvcmFnZSBhbmQgbG9hZHMgcmVxdWlyZWQgZGF0YSBmcm9tIGEgcGVyc2lzdGVudCBzdG9yYWdlXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FwcGxpY2F0aW9uXSBhcHBsaWNhdGlvbi4gRGVmYXVsdCBpcyBNZXRhQXBpXG4gICAqL1xuICBhc3luYyBpbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24gPSAnTWV0YUFwaScpIHtcbiAgICBhd2FpdCBzdXBlci5pbml0aWFsaXplKGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGxldCB7ZGVhbHMsIGhpc3RvcnlPcmRlcnN9ID0gYXdhaXQgdGhpcy5faGlzdG9yeURhdGFiYXNlLmxvYWRIaXN0b3J5KGFjY291bnRJZCwgYXBwbGljYXRpb24pO1xuICAgIGZvciAobGV0IGRlYWwgb2YgZGVhbHMpIHtcbiAgICAgIGF3YWl0IHRoaXMuX2FkZERlYWwoZGVhbCwgdHJ1ZSk7XG4gICAgfVxuICAgIGZvciAobGV0IGhpc3RvcnlPcmRlciBvZiBoaXN0b3J5T3JkZXJzKSB7XG4gICAgICBhd2FpdCB0aGlzLl9hZGRIaXN0b3J5T3JkZXIoaGlzdG9yeU9yZGVyLCB0cnVlKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmVzZXRzIHRoZSBzdG9yYWdlLiBJbnRlbmRlZCBmb3IgdXNlIGluIHRlc3RzXG4gICAqIEByZXR1cm5zIHtQcm9taXNlfSBwcm9taXNlIHdoZW4gdGhlIGhpc3RvcnkgaXMgcmVtb3ZlZFxuICAgKi9cbiAgYXN5bmMgY2xlYXIoKSB7XG4gICAgdGhpcy5fcmVzZXQoKTtcbiAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuY2xlYXIodGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbik7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IG9yZGVyIHJlY29yZCBzdG9yZWQgaW4gdGhlIGhpc3Rvcnkgc3RvcmFnZVxuICAgKiBAcGFyYW0ge051bWJlcn0gW2luc3RhbmNlTnVtYmVyXSBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcmV0dXJucyB7RGF0ZX0gdGhlIHRpbWUgb2YgdGhlIGxhc3QgaGlzdG9yeSBvcmRlciByZWNvcmQgc3RvcmVkIGluIHRoZSBoaXN0b3J5IHN0b3JhZ2VcbiAgICovXG4gIGxhc3RIaXN0b3J5T3JkZXJUaW1lKGluc3RhbmNlTnVtYmVyKSB7XG4gICAgcmV0dXJuIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWU7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBbaW5zdGFuY2VOdW1iZXJdIGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEByZXR1cm5zIHtEYXRlfSB0aGUgdGltZSBvZiB0aGUgbGFzdCBoaXN0b3J5IGRlYWwgcmVjb3JkIHN0b3JlZCBpbiB0aGUgaGlzdG9yeSBzdG9yYWdlXG4gICAqL1xuICBsYXN0RGVhbFRpbWUoaW5zdGFuY2VOdW1iZXIpIHtcbiAgICByZXR1cm4gdGhpcy5fbWF4RGVhbFRpbWU7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBvcmRlciBpcyBhZGRlZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaW5zdGFuY2VJbmRleCBpbmRleCBvZiBhbiBhY2NvdW50IGluc3RhbmNlIGNvbm5lY3RlZFxuICAgKiBAcGFyYW0ge01ldGF0cmFkZXJPcmRlcn0gaGlzdG9yeU9yZGVyIG5ldyBNZXRhVHJhZGVyIGhpc3Rvcnkgb3JkZXJcbiAgICovXG4gIGFzeW5jIG9uSGlzdG9yeU9yZGVyQWRkZWQoaW5zdGFuY2VJbmRleCwgaGlzdG9yeU9yZGVyKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlcik7XG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGEgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsIGlzIGFkZGVkXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBpbnN0YW5jZUluZGV4IGluZGV4IG9mIGFuIGFjY291bnQgaW5zdGFuY2UgY29ubmVjdGVkXG4gICAqIEBwYXJhbSB7TWV0YXRyYWRlckRlYWx9IGRlYWwgbmV3IE1ldGFUcmFkZXIgaGlzdG9yeSBkZWFsXG4gICAqL1xuICBhc3luYyBvbkRlYWxBZGRlZChpbnN0YW5jZUluZGV4LCBkZWFsKSB7XG4gICAgYXdhaXQgdGhpcy5fYWRkRGVhbChkZWFsKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGFsbCBkZWFsc1xuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBhbGwgZGVhbHNcbiAgICovXG4gIGdldCBkZWFscygpIHtcbiAgICByZXR1cm4gdGhpcy5nZXREZWFsc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpY2tldChpZCkge1xuICAgIGxldCBkZWFscyA9IE9iamVjdC52YWx1ZXModGhpcy5fZGVhbHNCeVRpY2tldFtpZF0gfHwge30pO1xuICAgIGRlYWxzLnNvcnQodGhpcy5fZGVhbHNDb21wYXJhdG9yKTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBkZWFscyBieSBwb3NpdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gcG9zaXRpb25JZCBwb3NpdGlvbiBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVBvc2l0aW9uKHBvc2l0aW9uSWQpIHtcbiAgICBsZXQgZGVhbHMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2RlYWxzQnlQb3NpdGlvbltwb3NpdGlvbklkXSB8fCB7fSk7XG4gICAgZGVhbHMuc29ydCh0aGlzLl9kZWFsc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBkZWFscztcbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGRlYWxzIGJ5IHRpbWUgcmFuZ2VcbiAgICogQHBhcmFtIHN0YXJ0VGltZSBzdGFydCB0aW1lLCBpbmNsdXNpdmVcbiAgICogQHBhcmFtIGVuZFRpbWUgZW5kIHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlckRlYWw+fSBkZWFscyBmb3VuZFxuICAgKi9cbiAgZ2V0RGVhbHNCeVRpbWVSYW5nZShzdGFydFRpbWUsIGVuZFRpbWUpIHtcbiAgICBsZXQgZGVhbHMgPSB0aGlzLl9kZWFsc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHt0aW1lOiBzdGFydFRpbWUsIGlkOiAwLCBlbnRyeVR5cGU6ICcnfSxcbiAgICAgICRsdGU6IHt0aW1lOiBlbmRUaW1lLCBpZDogTnVtYmVyLk1BWF9WQUxVRSwgZW50cnlUeXBlOiAnJ31cbiAgICB9KTtcbiAgICByZXR1cm4gZGVhbHM7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBhbGwgaGlzdG9yeSBvcmRlcnNcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGFsbCBoaXN0b3J5IG9yZGVyc1xuICAgKi9cbiAgZ2V0IGhpc3RvcnlPcmRlcnMoKSB7XG4gICAgcmV0dXJuIHRoaXMuZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKG5ldyBEYXRlKDApLCBuZXcgRGF0ZSg4NjQwMDAwMDAwMDAwMDAwKSk7XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBoaXN0b3J5IG9yZGVycyBieSB0aWNrZXQgaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkIHRpY2tldCBpZFxuICAgKiBAcmV0dXJucyB7QXJyYXk8TWV0YXRyYWRlck9yZGVyPn0gaGlzdG9yeSBvcmRlcnMgZm91bmRcbiAgICovXG4gIGdldEhpc3RvcnlPcmRlcnNCeVRpY2tldChpZCkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gT2JqZWN0LnZhbHVlcyh0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgcG9zaXRpb24gaWRcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBvc2l0aW9uSWQgcG9zaXRpb24gaWRcbiAgICogQHJldHVybnMge0FycmF5PE1ldGF0cmFkZXJPcmRlcj59IGhpc3Rvcnkgb3JkZXJzIGZvdW5kXG4gICAqL1xuICBnZXRIaXN0b3J5T3JkZXJzQnlQb3NpdGlvbihwb3NpdGlvbklkKSB7XG4gICAgbGV0IGhpc3RvcnlPcmRlcnMgPSBPYmplY3QudmFsdWVzKHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uW3Bvc2l0aW9uSWRdIHx8IHt9KTtcbiAgICBoaXN0b3J5T3JkZXJzLnNvcnQodGhpcy5faGlzdG9yeU9yZGVyc0NvbXBhcmF0b3IpO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGlzdG9yeSBvcmRlcnMgYnkgdGltZSByYW5nZVxuICAgKiBAcGFyYW0gc3RhcnRUaW1lIHN0YXJ0IHRpbWUsIGluY2x1c2l2ZVxuICAgKiBAcGFyYW0gZW5kVGltZSBlbmQgdGltZSwgaW5jbHVzaXZlXG4gICAqIEByZXR1cm5zIHtBcnJheTxNZXRhdHJhZGVyT3JkZXI+fSBoaXNvdHJ5IG9yZGVycyBmb3VuZFxuICAgKi9cbiAgZ2V0SGlzdG9yeU9yZGVyc0J5VGltZVJhbmdlKHN0YXJ0VGltZSwgZW5kVGltZSkge1xuICAgIGxldCBoaXN0b3J5T3JkZXJzID0gdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5iZXR3ZWVuQm91bmRzKHtcbiAgICAgICRndGU6IHtkb25lVGltZTogc3RhcnRUaW1lLCBpZDogMCwgdHlwZTogJycsIHN0YXRlOiAnJ30sXG4gICAgICAkbHRlOiB7ZG9uZVRpbWU6IGVuZFRpbWUsIGlkOiBOdW1iZXIuTUFYX1ZBTFVFLCB0eXBlOiAnJywgc3RhdGU6ICcnfVxuICAgIH0pO1xuICAgIHJldHVybiBoaXN0b3J5T3JkZXJzO1xuICB9XG5cbiAgLyoqXG4gICAqIEludm9rZWQgd2hlbiBhIHN5bmNocm9uaXphdGlvbiBvZiBoaXN0b3J5IGRlYWxzIG9uIGEgTWV0YVRyYWRlciBhY2NvdW50IGhhdmUgZmluaXNoZWQgdG8gaW5kaWNhdGUgcHJvZ3Jlc3Mgb2YgYW5cbiAgICogaW5pdGlhbCB0ZXJtaW5hbCBzdGF0ZSBzeW5jaHJvbml6YXRpb25cbiAgICogQHBhcmFtIHtTdHJpbmd9IGluc3RhbmNlSW5kZXggaW5kZXggb2YgYW4gYWNjb3VudCBpbnN0YW5jZSBjb25uZWN0ZWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHN5bmNocm9uaXphdGlvbklkIHN5bmNocm9uaXphdGlvbiByZXF1ZXN0IGlkXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2Ugd2hpY2ggcmVzb2x2ZXMgd2hlbiB0aGUgYXN5bmNocm9ub3VzIGV2ZW50IGlzIHByb2Nlc3NlZFxuICAgKi9cbiAgYXN5bmMgb25EZWFsc1N5bmNocm9uaXplZChpbnN0YW5jZUluZGV4LCBzeW5jaHJvbml6YXRpb25JZCkge1xuICAgIGF3YWl0IHRoaXMuX2ZsdXNoRGF0YWJhc2UoKTtcbiAgICBhd2FpdCBzdXBlci5vbkRlYWxzU3luY2hyb25pemVkKGluc3RhbmNlSW5kZXgsIHN5bmNocm9uaXphdGlvbklkKTtcbiAgfVxuXG4gIF9yZXNldCgpIHtcbiAgICB0aGlzLl9vcmRlclN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbFN5bmNocm9uaXphdGlvbkZpbmlzaGVkID0ge307XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2RlYWxzQnlQb3NpdGlvbiA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldCA9IHt9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVBvc2l0aW9uID0ge307XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQ29tcGFyYXRvciA9IChvMSwgbzIpID0+IHtcbiAgICAgIGxldCB0aW1lRGlmZiA9IChvMS5kb25lVGltZSB8fCBuZXcgRGF0ZSgwKSkuZ2V0VGltZSgpIC0gKG8yLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IG8xLmlkIC0gbzIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAobzEudHlwZSA+IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAxO1xuICAgICAgICAgIH0gZWxzZSBpZiAobzEudHlwZSA8IG8yLnR5cGUpIHtcbiAgICAgICAgICAgIHJldHVybiAtMTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgaWYgKG8xLnN0YXRlID4gbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDE7XG4gICAgICAgICAgICB9IGVsc2UgaWYgKG8xLnN0YXRlIDwgbzIuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgcmV0dXJuIDA7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIHJldHVybiBpZERpZmY7XG4gICAgICAgIH1cbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHJldHVybiB0aW1lRGlmZjtcbiAgICAgIH1cbiAgICB9O1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yfSk7XG4gICAgdGhpcy5fZGVhbHNDb21wYXJhdG9yID0gKGQxLCBkMikgPT4ge1xuICAgICAgbGV0IHRpbWVEaWZmID0gKGQxLnRpbWUgfHwgbmV3IERhdGUoMCkpLmdldFRpbWUoKSAtIChkMi50aW1lIHx8IG5ldyBEYXRlKDApKS5nZXRUaW1lKCk7XG4gICAgICBpZiAodGltZURpZmYgPT09IDApIHtcbiAgICAgICAgbGV0IGlkRGlmZiA9IGQxLmlkIC0gZDIuaWQ7XG4gICAgICAgIGlmIChpZERpZmYgPT09IDApIHtcbiAgICAgICAgICBpZiAoZDEuZW50cnlUeXBlID4gZDIuZW50cnlUeXBlKSB7XG4gICAgICAgICAgICByZXR1cm4gMTtcbiAgICAgICAgICB9IGVsc2UgaWYgKGQxLmVudHJ5VHlwZSA8IGQyLmVudHJ5VHlwZSkge1xuICAgICAgICAgICAgcmV0dXJuIC0xO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICByZXR1cm4gMDtcbiAgICAgICAgICB9XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgcmV0dXJuIGlkRGlmZjtcbiAgICAgICAgfVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIHRpbWVEaWZmO1xuICAgICAgfVxuICAgIH07XG4gICAgdGhpcy5fZGVhbHNCeVRpbWUgPSBuZXcgQVZMVHJlZSh7Y29tcGFyZUtleXM6IHRoaXMuX2RlYWxzQ29tcGFyYXRvcn0pO1xuICAgIHRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgPSBuZXcgRGF0ZSgwKTtcbiAgICB0aGlzLl9tYXhEZWFsVGltZSA9IG5ldyBEYXRlKDApO1xuICAgIHRoaXMuX25ld0hpc3RvcnlPcmRlcnMgPSBbXTtcbiAgICB0aGlzLl9uZXdEZWFscyA9IFtdO1xuICAgIGNsZWFyVGltZW91dCh0aGlzLl9mbHVzaFRpbWVvdXQpO1xuICAgIGRlbGV0ZSB0aGlzLl9mbHVzaFRpbWVvdXQ7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkRGVhbChkZWFsLCBleGlzdGluZykge1xuICAgIGxldCBrZXkgPSB0aGlzLl9nZXREZWFsS2V5KGRlYWwpO1xuICAgIHRoaXMuX2RlYWxzQnlUaWNrZXRbZGVhbC5pZF0gPSB0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdIHx8IHt9O1xuICAgIGxldCBuZXdEZWFsID0gIWV4aXN0aW5nICYmICF0aGlzLl9kZWFsc0J5VGlja2V0W2RlYWwuaWRdW2tleV07XG4gICAgdGhpcy5fZGVhbHNCeVRpY2tldFtkZWFsLmlkXVtrZXldID0gZGVhbDtcbiAgICBpZiAoZGVhbC5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9kZWFsc0J5UG9zaXRpb25bZGVhbC5wb3NpdGlvbklkXSA9IHRoaXMuX2RlYWxzQnlQb3NpdGlvbltkZWFsLnBvc2l0aW9uSWRdIHx8IHt9O1xuICAgICAgdGhpcy5fZGVhbHNCeVBvc2l0aW9uW2RlYWwucG9zaXRpb25JZF1ba2V5XSA9IGRlYWw7XG4gICAgfVxuICAgIHRoaXMuX2RlYWxzQnlUaW1lLmRlbGV0ZShkZWFsKTtcbiAgICB0aGlzLl9kZWFsc0J5VGltZS5pbnNlcnQoZGVhbCwgZGVhbCk7XG4gICAgaWYgKGRlYWwudGltZSAmJiAoIXRoaXMuX21heERlYWxUaW1lIHx8IHRoaXMuX21heERlYWxUaW1lLmdldFRpbWUoKSA8IGRlYWwudGltZS5nZXRUaW1lKCkpKSB7XG4gICAgICB0aGlzLl9tYXhEZWFsVGltZSA9IGRlYWwudGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0RlYWwpIHtcbiAgICAgIHRoaXMuX25ld0RlYWxzLnB1c2goZGVhbCk7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0RGVhbEtleShkZWFsKSB7XG4gICAgcmV0dXJuIChkZWFsLnRpbWUgfHwgbmV3IERhdGUoMCkpLnRvSVNPU3RyaW5nKCkgKyAnOicgKyBkZWFsLmlkICsgJzonICsgZGVhbC5lbnRyeVR5cGU7XG4gIH1cblxuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgY29tcGxleGl0eVxuICBhc3luYyBfYWRkSGlzdG9yeU9yZGVyKGhpc3RvcnlPcmRlciwgZXhpc3RpbmcpIHtcbiAgICBsZXQga2V5ID0gdGhpcy5fZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcik7XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGlja2V0W2hpc3RvcnlPcmRlci5pZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXSB8fCB7fTtcbiAgICBsZXQgbmV3SGlzdG9yeU9yZGVyID0gIWV4aXN0aW5nICYmICF0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaWNrZXRbaGlzdG9yeU9yZGVyLmlkXVtrZXldO1xuICAgIHRoaXMuX2hpc3RvcnlPcmRlcnNCeVRpY2tldFtoaXN0b3J5T3JkZXIuaWRdW2tleV0gPSBoaXN0b3J5T3JkZXI7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5wb3NpdGlvbklkKSB7XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gPSB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF0gfHxcbiAgICAgICAge307XG4gICAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlQb3NpdGlvbltoaXN0b3J5T3JkZXIucG9zaXRpb25JZF1ba2V5XSA9IGhpc3RvcnlPcmRlcjtcbiAgICB9XG4gICAgdGhpcy5faGlzdG9yeU9yZGVyc0J5VGltZS5kZWxldGUoaGlzdG9yeU9yZGVyKTtcbiAgICB0aGlzLl9oaXN0b3J5T3JkZXJzQnlUaW1lLmluc2VydChoaXN0b3J5T3JkZXIsIGhpc3RvcnlPcmRlcik7XG4gICAgaWYgKGhpc3RvcnlPcmRlci5kb25lVGltZSAmJiAoIXRoaXMuX21heEhpc3RvcnlPcmRlclRpbWUgfHxcbiAgICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZS5nZXRUaW1lKCkgPCBoaXN0b3J5T3JkZXIuZG9uZVRpbWUuZ2V0VGltZSgpKSkge1xuICAgICAgdGhpcy5fbWF4SGlzdG9yeU9yZGVyVGltZSA9IGhpc3RvcnlPcmRlci5kb25lVGltZTtcbiAgICB9XG4gICAgaWYgKG5ld0hpc3RvcnlPcmRlcikge1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycy5wdXNoKGhpc3RvcnlPcmRlcik7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5fZmx1c2hUaW1lb3V0KTtcbiAgICAgIHRoaXMuX2ZsdXNoVGltZW91dCA9IHNldFRpbWVvdXQodGhpcy5fZmx1c2hEYXRhYmFzZS5iaW5kKHRoaXMpLCA1MDAwKTtcbiAgICB9XG4gIH1cblxuICBfZ2V0SGlzdG9yeU9yZGVyS2V5KGhpc3RvcnlPcmRlcikge1xuICAgIHJldHVybiAoaGlzdG9yeU9yZGVyLmRvbmVUaW1lIHx8IG5ldyBEYXRlKDApKS50b0lTT1N0cmluZygpICsgJzonICsgaGlzdG9yeU9yZGVyLmlkICsgJzonICtcbiAgICAgIGhpc3RvcnlPcmRlci50eXBlICsgJzonICsgaGlzdG9yeU9yZGVyLnN0YXRlO1xuICB9XG5cbiAgYXN5bmMgX2ZsdXNoRGF0YWJhc2UoKSB7XG4gICAgaWYgKHRoaXMuX2ZsdXNoUHJvbWlzZSkge1xuICAgICAgYXdhaXQgdGhpcy5fZmx1c2hQcm9taXNlO1xuICAgIH1cbiAgICBpZiAodGhpcy5fZmx1c2hSdW5uaW5nKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX2ZsdXNoUnVubmluZyA9IHRydWU7XG4gICAgbGV0IHJlc29sdmU7XG4gICAgdGhpcy5fZmx1c2hQcm9taXNlID0gbmV3IFByb21pc2UocmVzID0+IHJlc29sdmUgPSByZXMpO1xuICAgIHRyeSB7XG4gICAgICBhd2FpdCB0aGlzLl9oaXN0b3J5RGF0YWJhc2UuZmx1c2godGhpcy5fYWNjb3VudElkLCB0aGlzLl9hcHBsaWNhdGlvbiwgdGhpcy5fbmV3SGlzdG9yeU9yZGVycywgdGhpcy5fbmV3RGVhbHMpO1xuICAgICAgdGhpcy5fbmV3SGlzdG9yeU9yZGVycyA9IFtdO1xuICAgICAgdGhpcy5fbmV3RGVhbHMgPSBbXTtcbiAgICAgIHRoaXMuX2xvZ2dlci5kZWJ1ZyhgJHt0aGlzLl9hY2NvdW50SWR9OiBmbHVzaGVkIGhpc3RvcnkgZGJgKTtcbiAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgIHRoaXMuX2xvZ2dlci53YXJuKGAke3RoaXMuX2FjY291bnRJZH06IGVycm9yIGZsdXNoaW5nIGhpc3RvcnkgZGJgLCBlcnIpO1xuICAgICAgdGhpcy5fZmx1c2hUaW1lb3V0ID0gc2V0VGltZW91dCh0aGlzLl9mbHVzaERhdGFiYXNlLmJpbmQodGhpcyksIDE1MDAwKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgcmVzb2x2ZSgpO1xuICAgICAgdGhpcy5fZmx1c2hSdW5uaW5nID0gZmFsc2U7XG4gICAgfVxuICB9XG5cbn1cbiJdLCJuYW1lcyI6WyJIaXN0b3J5U3RvcmFnZSIsIkhpc3RvcnlEYXRhYmFzZSIsIkFWTFRyZWUiLCJMb2dnZXJNYW5hZ2VyIiwiTWVtb3J5SGlzdG9yeVN0b3JhZ2UiLCJpbml0aWFsaXplIiwiYWNjb3VudElkIiwiYXBwbGljYXRpb24iLCJkZWFscyIsImhpc3RvcnlPcmRlcnMiLCJfaGlzdG9yeURhdGFiYXNlIiwibG9hZEhpc3RvcnkiLCJkZWFsIiwiX2FkZERlYWwiLCJoaXN0b3J5T3JkZXIiLCJfYWRkSGlzdG9yeU9yZGVyIiwiY2xlYXIiLCJfcmVzZXQiLCJfYWNjb3VudElkIiwiX2FwcGxpY2F0aW9uIiwibGFzdEhpc3RvcnlPcmRlclRpbWUiLCJpbnN0YW5jZU51bWJlciIsIl9tYXhIaXN0b3J5T3JkZXJUaW1lIiwibGFzdERlYWxUaW1lIiwiX21heERlYWxUaW1lIiwib25IaXN0b3J5T3JkZXJBZGRlZCIsImluc3RhbmNlSW5kZXgiLCJvbkRlYWxBZGRlZCIsImdldERlYWxzQnlUaW1lUmFuZ2UiLCJEYXRlIiwiZ2V0RGVhbHNCeVRpY2tldCIsImlkIiwiT2JqZWN0IiwidmFsdWVzIiwiX2RlYWxzQnlUaWNrZXQiLCJzb3J0IiwiX2RlYWxzQ29tcGFyYXRvciIsImdldERlYWxzQnlQb3NpdGlvbiIsInBvc2l0aW9uSWQiLCJfZGVhbHNCeVBvc2l0aW9uIiwic3RhcnRUaW1lIiwiZW5kVGltZSIsIl9kZWFsc0J5VGltZSIsImJldHdlZW5Cb3VuZHMiLCIkZ3RlIiwidGltZSIsImVudHJ5VHlwZSIsIiRsdGUiLCJOdW1iZXIiLCJNQVhfVkFMVUUiLCJnZXRIaXN0b3J5T3JkZXJzQnlUaW1lUmFuZ2UiLCJnZXRIaXN0b3J5T3JkZXJzQnlUaWNrZXQiLCJfaGlzdG9yeU9yZGVyc0J5VGlja2V0IiwiX2hpc3RvcnlPcmRlcnNDb21wYXJhdG9yIiwiZ2V0SGlzdG9yeU9yZGVyc0J5UG9zaXRpb24iLCJfaGlzdG9yeU9yZGVyc0J5UG9zaXRpb24iLCJfaGlzdG9yeU9yZGVyc0J5VGltZSIsImRvbmVUaW1lIiwidHlwZSIsInN0YXRlIiwib25EZWFsc1N5bmNocm9uaXplZCIsInN5bmNocm9uaXphdGlvbklkIiwiX2ZsdXNoRGF0YWJhc2UiLCJfb3JkZXJTeW5jaHJvbml6YXRpb25GaW5pc2hlZCIsIl9kZWFsU3luY2hyb25pemF0aW9uRmluaXNoZWQiLCJvMSIsIm8yIiwidGltZURpZmYiLCJnZXRUaW1lIiwiaWREaWZmIiwiY29tcGFyZUtleXMiLCJkMSIsImQyIiwiX25ld0hpc3RvcnlPcmRlcnMiLCJfbmV3RGVhbHMiLCJjbGVhclRpbWVvdXQiLCJfZmx1c2hUaW1lb3V0IiwiZXhpc3RpbmciLCJrZXkiLCJfZ2V0RGVhbEtleSIsIm5ld0RlYWwiLCJkZWxldGUiLCJpbnNlcnQiLCJwdXNoIiwic2V0VGltZW91dCIsImJpbmQiLCJ0b0lTT1N0cmluZyIsIl9nZXRIaXN0b3J5T3JkZXJLZXkiLCJuZXdIaXN0b3J5T3JkZXIiLCJfZmx1c2hQcm9taXNlIiwiX2ZsdXNoUnVubmluZyIsInJlc29sdmUiLCJQcm9taXNlIiwicmVzIiwiZmx1c2giLCJfbG9nZ2VyIiwiZGVidWciLCJlcnIiLCJ3YXJuIiwiY29uc3RydWN0b3IiLCJnZXRJbnN0YW5jZSIsImdldExvZ2dlciJdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUVBLE9BQU9BLG9CQUFvQixtQkFBbUI7QUFDOUMsT0FBT0MscUJBQXFCLDBCQUEwQjtBQUN0RCxTQUFTQyxPQUFPLFFBQVEscUJBQXFCO0FBQzdDLE9BQU9DLG1CQUFtQixZQUFZO0FBS3ZCLElBQUEsQUFBTUMsdUJBQU4sTUFBTUEsNkJBQTZCSjtJQVloRDs7OztHQUlDLEdBQ0QsQUFBTUssV0FBV0MsU0FBUyxFQUFFQyxjQUFjLFNBQVM7O2VBQW5ELG9CQUFBO1lBQ0UsTUFBTSw0QkFBQSxZQUFpQkQsV0FBV0M7WUFDbEMsSUFBSSxFQUFDQyxLQUFLLEVBQUVDLGFBQWEsRUFBQyxHQUFHLE1BQU0sTUFBS0MsZ0JBQWdCLENBQUNDLFdBQVcsQ0FBQ0wsV0FBV0M7WUFDaEYsS0FBSyxJQUFJSyxRQUFRSixNQUFPO2dCQUN0QixNQUFNLE1BQUtLLFFBQVEsQ0FBQ0QsTUFBTTtZQUM1QjtZQUNBLEtBQUssSUFBSUUsZ0JBQWdCTCxjQUFlO2dCQUN0QyxNQUFNLE1BQUtNLGdCQUFnQixDQUFDRCxjQUFjO1lBQzVDO1FBQ0Y7O0lBRUE7OztHQUdDLEdBQ0QsQUFBTUU7O2VBQU4sb0JBQUE7WUFDRSxNQUFLQyxNQUFNO1lBQ1gsTUFBTSxNQUFLUCxnQkFBZ0IsQ0FBQ00sS0FBSyxDQUFDLE1BQUtFLFVBQVUsRUFBRSxNQUFLQyxZQUFZO1FBQ3RFOztJQUVBOzs7O0dBSUMsR0FDREMscUJBQXFCQyxjQUFjLEVBQUU7UUFDbkMsT0FBTyxJQUFJLENBQUNDLG9CQUFvQjtJQUNsQztJQUVBOzs7O0dBSUMsR0FDREMsYUFBYUYsY0FBYyxFQUFFO1FBQzNCLE9BQU8sSUFBSSxDQUFDRyxZQUFZO0lBQzFCO0lBRUE7Ozs7R0FJQyxHQUNELEFBQU1DLG9CQUFvQkMsYUFBYSxFQUFFWixZQUFZOztlQUFyRCxvQkFBQTtZQUNFLE1BQU0sTUFBS0MsZ0JBQWdCLENBQUNEO1FBQzlCOztJQUVBOzs7O0dBSUMsR0FDRCxBQUFNYSxZQUFZRCxhQUFhLEVBQUVkLElBQUk7O2VBQXJDLG9CQUFBO1lBQ0UsTUFBTSxNQUFLQyxRQUFRLENBQUNEO1FBQ3RCOztJQUVBOzs7R0FHQyxHQUNELElBQUlKLFFBQVE7UUFDVixPQUFPLElBQUksQ0FBQ29CLG1CQUFtQixDQUFDLElBQUlDLEtBQUssSUFBSSxJQUFJQSxLQUFLO0lBQ3hEO0lBRUE7Ozs7R0FJQyxHQUNEQyxpQkFBaUJDLEVBQUUsRUFBRTtRQUNuQixJQUFJdkIsUUFBUXdCLE9BQU9DLE1BQU0sQ0FBQyxJQUFJLENBQUNDLGNBQWMsQ0FBQ0gsR0FBRyxJQUFJLENBQUM7UUFDdER2QixNQUFNMkIsSUFBSSxDQUFDLElBQUksQ0FBQ0MsZ0JBQWdCO1FBQ2hDLE9BQU81QjtJQUNUO0lBRUE7Ozs7R0FJQyxHQUNENkIsbUJBQW1CQyxVQUFVLEVBQUU7UUFDN0IsSUFBSTlCLFFBQVF3QixPQUFPQyxNQUFNLENBQUMsSUFBSSxDQUFDTSxnQkFBZ0IsQ0FBQ0QsV0FBVyxJQUFJLENBQUM7UUFDaEU5QixNQUFNMkIsSUFBSSxDQUFDLElBQUksQ0FBQ0MsZ0JBQWdCO1FBQ2hDLE9BQU81QjtJQUNUO0lBRUE7Ozs7O0dBS0MsR0FDRG9CLG9CQUFvQlksU0FBUyxFQUFFQyxPQUFPLEVBQUU7UUFDdEMsSUFBSWpDLFFBQVEsSUFBSSxDQUFDa0MsWUFBWSxDQUFDQyxhQUFhLENBQUM7WUFDMUNDLE1BQU07Z0JBQUNDLE1BQU1MO2dCQUFXVCxJQUFJO2dCQUFHZSxXQUFXO1lBQUU7WUFDNUNDLE1BQU07Z0JBQUNGLE1BQU1KO2dCQUFTVixJQUFJaUIsT0FBT0MsU0FBUztnQkFBRUgsV0FBVztZQUFFO1FBQzNEO1FBQ0EsT0FBT3RDO0lBQ1Q7SUFFQTs7O0dBR0MsR0FDRCxJQUFJQyxnQkFBZ0I7UUFDbEIsT0FBTyxJQUFJLENBQUN5QywyQkFBMkIsQ0FBQyxJQUFJckIsS0FBSyxJQUFJLElBQUlBLEtBQUs7SUFDaEU7SUFFQTs7OztHQUlDLEdBQ0RzQix5QkFBeUJwQixFQUFFLEVBQUU7UUFDM0IsSUFBSXRCLGdCQUFnQnVCLE9BQU9DLE1BQU0sQ0FBQyxJQUFJLENBQUNtQixzQkFBc0IsQ0FBQ3JCLEdBQUcsSUFBSSxDQUFDO1FBQ3RFdEIsY0FBYzBCLElBQUksQ0FBQyxJQUFJLENBQUNrQix3QkFBd0I7UUFDaEQsT0FBTzVDO0lBQ1Q7SUFFQTs7OztHQUlDLEdBQ0Q2QywyQkFBMkJoQixVQUFVLEVBQUU7UUFDckMsSUFBSTdCLGdCQUFnQnVCLE9BQU9DLE1BQU0sQ0FBQyxJQUFJLENBQUNzQix3QkFBd0IsQ0FBQ2pCLFdBQVcsSUFBSSxDQUFDO1FBQ2hGN0IsY0FBYzBCLElBQUksQ0FBQyxJQUFJLENBQUNrQix3QkFBd0I7UUFDaEQsT0FBTzVDO0lBQ1Q7SUFFQTs7Ozs7R0FLQyxHQUNEeUMsNEJBQTRCVixTQUFTLEVBQUVDLE9BQU8sRUFBRTtRQUM5QyxJQUFJaEMsZ0JBQWdCLElBQUksQ0FBQytDLG9CQUFvQixDQUFDYixhQUFhLENBQUM7WUFDMURDLE1BQU07Z0JBQUNhLFVBQVVqQjtnQkFBV1QsSUFBSTtnQkFBRzJCLE1BQU07Z0JBQUlDLE9BQU87WUFBRTtZQUN0RFosTUFBTTtnQkFBQ1UsVUFBVWhCO2dCQUFTVixJQUFJaUIsT0FBT0MsU0FBUztnQkFBRVMsTUFBTTtnQkFBSUMsT0FBTztZQUFFO1FBQ3JFO1FBQ0EsT0FBT2xEO0lBQ1Q7SUFFQTs7Ozs7O0dBTUMsR0FDRCxBQUFNbUQsb0JBQW9CbEMsYUFBYSxFQUFFbUMsaUJBQWlCOztlQUExRCxvQkFBQTtZQUNFLE1BQU0sTUFBS0MsY0FBYztZQUN6QixNQUFNLHFDQUFBLFlBQTBCcEMsZUFBZW1DO1FBQ2pEOztJQUVBNUMsU0FBUztRQUNQLElBQUksQ0FBQzhDLDZCQUE2QixHQUFHLENBQUM7UUFDdEMsSUFBSSxDQUFDQyw0QkFBNEIsR0FBRyxDQUFDO1FBQ3JDLElBQUksQ0FBQzlCLGNBQWMsR0FBRyxDQUFDO1FBQ3ZCLElBQUksQ0FBQ0ssZ0JBQWdCLEdBQUcsQ0FBQztRQUN6QixJQUFJLENBQUNhLHNCQUFzQixHQUFHLENBQUM7UUFDL0IsSUFBSSxDQUFDRyx3QkFBd0IsR0FBRyxDQUFDO1FBQ2pDLHNDQUFzQztRQUN0QyxJQUFJLENBQUNGLHdCQUF3QixHQUFHLENBQUNZLElBQUlDO1lBQ25DLElBQUlDLFdBQVcsQUFBQ0YsQ0FBQUEsR0FBR1IsUUFBUSxJQUFJLElBQUk1QixLQUFLLEVBQUMsRUFBR3VDLE9BQU8sS0FBSyxBQUFDRixDQUFBQSxHQUFHVCxRQUFRLElBQUksSUFBSTVCLEtBQUssRUFBQyxFQUFHdUMsT0FBTztZQUM1RixJQUFJRCxhQUFhLEdBQUc7Z0JBQ2xCLElBQUlFLFNBQVNKLEdBQUdsQyxFQUFFLEdBQUdtQyxHQUFHbkMsRUFBRTtnQkFDMUIsSUFBSXNDLFdBQVcsR0FBRztvQkFDaEIsSUFBSUosR0FBR1AsSUFBSSxHQUFHUSxHQUFHUixJQUFJLEVBQUU7d0JBQ3JCLE9BQU87b0JBQ1QsT0FBTyxJQUFJTyxHQUFHUCxJQUFJLEdBQUdRLEdBQUdSLElBQUksRUFBRTt3QkFDNUIsT0FBTyxDQUFDO29CQUNWLE9BQU87d0JBQ0wsSUFBSU8sR0FBR04sS0FBSyxHQUFHTyxHQUFHUCxLQUFLLEVBQUU7NEJBQ3ZCLE9BQU87d0JBQ1QsT0FBTyxJQUFJTSxHQUFHTixLQUFLLEdBQUdPLEdBQUdQLEtBQUssRUFBRTs0QkFDOUIsT0FBTyxDQUFDO3dCQUNWLE9BQU87NEJBQ0wsT0FBTzt3QkFDVDtvQkFDRjtnQkFDRixPQUFPO29CQUNMLE9BQU9VO2dCQUNUO1lBQ0YsT0FBTztnQkFDTCxPQUFPRjtZQUNUO1FBQ0Y7UUFDQSxJQUFJLENBQUNYLG9CQUFvQixHQUFHLElBQUl0RCxRQUFRO1lBQUNvRSxhQUFhLElBQUksQ0FBQ2pCLHdCQUF3QjtRQUFBO1FBQ25GLElBQUksQ0FBQ2pCLGdCQUFnQixHQUFHLENBQUNtQyxJQUFJQztZQUMzQixJQUFJTCxXQUFXLEFBQUNJLENBQUFBLEdBQUcxQixJQUFJLElBQUksSUFBSWhCLEtBQUssRUFBQyxFQUFHdUMsT0FBTyxLQUFLLEFBQUNJLENBQUFBLEdBQUczQixJQUFJLElBQUksSUFBSWhCLEtBQUssRUFBQyxFQUFHdUMsT0FBTztZQUNwRixJQUFJRCxhQUFhLEdBQUc7Z0JBQ2xCLElBQUlFLFNBQVNFLEdBQUd4QyxFQUFFLEdBQUd5QyxHQUFHekMsRUFBRTtnQkFDMUIsSUFBSXNDLFdBQVcsR0FBRztvQkFDaEIsSUFBSUUsR0FBR3pCLFNBQVMsR0FBRzBCLEdBQUcxQixTQUFTLEVBQUU7d0JBQy9CLE9BQU87b0JBQ1QsT0FBTyxJQUFJeUIsR0FBR3pCLFNBQVMsR0FBRzBCLEdBQUcxQixTQUFTLEVBQUU7d0JBQ3RDLE9BQU8sQ0FBQztvQkFDVixPQUFPO3dCQUNMLE9BQU87b0JBQ1Q7Z0JBQ0YsT0FBTztvQkFDTCxPQUFPdUI7Z0JBQ1Q7WUFDRixPQUFPO2dCQUNMLE9BQU9GO1lBQ1Q7UUFDRjtRQUNBLElBQUksQ0FBQ3pCLFlBQVksR0FBRyxJQUFJeEMsUUFBUTtZQUFDb0UsYUFBYSxJQUFJLENBQUNsQyxnQkFBZ0I7UUFBQTtRQUNuRSxJQUFJLENBQUNkLG9CQUFvQixHQUFHLElBQUlPLEtBQUs7UUFDckMsSUFBSSxDQUFDTCxZQUFZLEdBQUcsSUFBSUssS0FBSztRQUM3QixJQUFJLENBQUM0QyxpQkFBaUIsR0FBRyxFQUFFO1FBQzNCLElBQUksQ0FBQ0MsU0FBUyxHQUFHLEVBQUU7UUFDbkJDLGFBQWEsSUFBSSxDQUFDQyxhQUFhO1FBQy9CLE9BQU8sSUFBSSxDQUFDQSxhQUFhO0lBQzNCO0lBRUEsc0NBQXNDO0lBQ2hDL0QsU0FBU0QsSUFBSSxFQUFFaUUsUUFBUTs7ZUFBN0Isb0JBQUE7WUFDRSxJQUFJQyxNQUFNLE1BQUtDLFdBQVcsQ0FBQ25FO1lBQzNCLE1BQUtzQixjQUFjLENBQUN0QixLQUFLbUIsRUFBRSxDQUFDLEdBQUcsTUFBS0csY0FBYyxDQUFDdEIsS0FBS21CLEVBQUUsQ0FBQyxJQUFJLENBQUM7WUFDaEUsSUFBSWlELFVBQVUsQ0FBQ0gsWUFBWSxDQUFDLE1BQUszQyxjQUFjLENBQUN0QixLQUFLbUIsRUFBRSxDQUFDLENBQUMrQyxJQUFJO1lBQzdELE1BQUs1QyxjQUFjLENBQUN0QixLQUFLbUIsRUFBRSxDQUFDLENBQUMrQyxJQUFJLEdBQUdsRTtZQUNwQyxJQUFJQSxLQUFLMEIsVUFBVSxFQUFFO2dCQUNuQixNQUFLQyxnQkFBZ0IsQ0FBQzNCLEtBQUswQixVQUFVLENBQUMsR0FBRyxNQUFLQyxnQkFBZ0IsQ0FBQzNCLEtBQUswQixVQUFVLENBQUMsSUFBSSxDQUFDO2dCQUNwRixNQUFLQyxnQkFBZ0IsQ0FBQzNCLEtBQUswQixVQUFVLENBQUMsQ0FBQ3dDLElBQUksR0FBR2xFO1lBQ2hEO1lBQ0EsTUFBSzhCLFlBQVksQ0FBQ3VDLE1BQU0sQ0FBQ3JFO1lBQ3pCLE1BQUs4QixZQUFZLENBQUN3QyxNQUFNLENBQUN0RSxNQUFNQTtZQUMvQixJQUFJQSxLQUFLaUMsSUFBSSxJQUFLLENBQUEsQ0FBQyxNQUFLckIsWUFBWSxJQUFJLE1BQUtBLFlBQVksQ0FBQzRDLE9BQU8sS0FBS3hELEtBQUtpQyxJQUFJLENBQUN1QixPQUFPLEVBQUMsR0FBSTtnQkFDMUYsTUFBSzVDLFlBQVksR0FBR1osS0FBS2lDLElBQUk7WUFDL0I7WUFDQSxJQUFJbUMsU0FBUztnQkFDWCxNQUFLTixTQUFTLENBQUNTLElBQUksQ0FBQ3ZFO2dCQUNwQitELGFBQWEsTUFBS0MsYUFBYTtnQkFDL0IsTUFBS0EsYUFBYSxHQUFHUSxXQUFXLE1BQUt0QixjQUFjLENBQUN1QixJQUFJLFNBQVE7WUFDbEU7UUFDRjs7SUFFQU4sWUFBWW5FLElBQUksRUFBRTtRQUNoQixPQUFPLEFBQUNBLENBQUFBLEtBQUtpQyxJQUFJLElBQUksSUFBSWhCLEtBQUssRUFBQyxFQUFHeUQsV0FBVyxLQUFLLE1BQU0xRSxLQUFLbUIsRUFBRSxHQUFHLE1BQU1uQixLQUFLa0MsU0FBUztJQUN4RjtJQUVBLHNDQUFzQztJQUNoQy9CLGlCQUFpQkQsWUFBWSxFQUFFK0QsUUFBUTs7ZUFBN0Msb0JBQUE7WUFDRSxJQUFJQyxNQUFNLE1BQUtTLG1CQUFtQixDQUFDekU7WUFDbkMsTUFBS3NDLHNCQUFzQixDQUFDdEMsYUFBYWlCLEVBQUUsQ0FBQyxHQUFHLE1BQUtxQixzQkFBc0IsQ0FBQ3RDLGFBQWFpQixFQUFFLENBQUMsSUFBSSxDQUFDO1lBQ2hHLElBQUl5RCxrQkFBa0IsQ0FBQ1gsWUFBWSxDQUFDLE1BQUt6QixzQkFBc0IsQ0FBQ3RDLGFBQWFpQixFQUFFLENBQUMsQ0FBQytDLElBQUk7WUFDckYsTUFBSzFCLHNCQUFzQixDQUFDdEMsYUFBYWlCLEVBQUUsQ0FBQyxDQUFDK0MsSUFBSSxHQUFHaEU7WUFDcEQsSUFBSUEsYUFBYXdCLFVBQVUsRUFBRTtnQkFDM0IsTUFBS2lCLHdCQUF3QixDQUFDekMsYUFBYXdCLFVBQVUsQ0FBQyxHQUFHLE1BQUtpQix3QkFBd0IsQ0FBQ3pDLGFBQWF3QixVQUFVLENBQUMsSUFDN0csQ0FBQztnQkFDSCxNQUFLaUIsd0JBQXdCLENBQUN6QyxhQUFhd0IsVUFBVSxDQUFDLENBQUN3QyxJQUFJLEdBQUdoRTtZQUNoRTtZQUNBLE1BQUswQyxvQkFBb0IsQ0FBQ3lCLE1BQU0sQ0FBQ25FO1lBQ2pDLE1BQUswQyxvQkFBb0IsQ0FBQzBCLE1BQU0sQ0FBQ3BFLGNBQWNBO1lBQy9DLElBQUlBLGFBQWEyQyxRQUFRLElBQUssQ0FBQSxDQUFDLE1BQUtuQyxvQkFBb0IsSUFDcEQsTUFBS0Esb0JBQW9CLENBQUM4QyxPQUFPLEtBQUt0RCxhQUFhMkMsUUFBUSxDQUFDVyxPQUFPLEVBQUMsR0FBSTtnQkFDMUUsTUFBSzlDLG9CQUFvQixHQUFHUixhQUFhMkMsUUFBUTtZQUNuRDtZQUNBLElBQUkrQixpQkFBaUI7Z0JBQ25CLE1BQUtmLGlCQUFpQixDQUFDVSxJQUFJLENBQUNyRTtnQkFDNUI2RCxhQUFhLE1BQUtDLGFBQWE7Z0JBQy9CLE1BQUtBLGFBQWEsR0FBR1EsV0FBVyxNQUFLdEIsY0FBYyxDQUFDdUIsSUFBSSxTQUFRO1lBQ2xFO1FBQ0Y7O0lBRUFFLG9CQUFvQnpFLFlBQVksRUFBRTtRQUNoQyxPQUFPLEFBQUNBLENBQUFBLGFBQWEyQyxRQUFRLElBQUksSUFBSTVCLEtBQUssRUFBQyxFQUFHeUQsV0FBVyxLQUFLLE1BQU14RSxhQUFhaUIsRUFBRSxHQUFHLE1BQ3BGakIsYUFBYTRDLElBQUksR0FBRyxNQUFNNUMsYUFBYTZDLEtBQUs7SUFDaEQ7SUFFTUc7O2VBQU4sb0JBQUE7WUFDRSxJQUFJLE1BQUsyQixhQUFhLEVBQUU7Z0JBQ3RCLE1BQU0sTUFBS0EsYUFBYTtZQUMxQjtZQUNBLElBQUksTUFBS0MsYUFBYSxFQUFFO2dCQUN0QjtZQUNGO1lBQ0EsTUFBS0EsYUFBYSxHQUFHO1lBQ3JCLElBQUlDO1lBQ0osTUFBS0YsYUFBYSxHQUFHLElBQUlHLFFBQVFDLENBQUFBLE1BQU9GLFVBQVVFO1lBQ2xELElBQUk7Z0JBQ0YsTUFBTSxNQUFLbkYsZ0JBQWdCLENBQUNvRixLQUFLLENBQUMsTUFBSzVFLFVBQVUsRUFBRSxNQUFLQyxZQUFZLEVBQUUsTUFBS3NELGlCQUFpQixFQUFFLE1BQUtDLFNBQVM7Z0JBQzVHLE1BQUtELGlCQUFpQixHQUFHLEVBQUU7Z0JBQzNCLE1BQUtDLFNBQVMsR0FBRyxFQUFFO2dCQUNuQixNQUFLcUIsT0FBTyxDQUFDQyxLQUFLLENBQUMsQ0FBQyxFQUFFLE1BQUs5RSxVQUFVLENBQUMsb0JBQW9CLENBQUM7WUFDN0QsRUFBRSxPQUFPK0UsS0FBSztnQkFDWixNQUFLRixPQUFPLENBQUNHLElBQUksQ0FBQyxDQUFDLEVBQUUsTUFBS2hGLFVBQVUsQ0FBQywyQkFBMkIsQ0FBQyxFQUFFK0U7Z0JBQ25FLE1BQUtyQixhQUFhLEdBQUdRLFdBQVcsTUFBS3RCLGNBQWMsQ0FBQ3VCLElBQUksU0FBUTtZQUNsRSxTQUFVO2dCQUNSTTtnQkFDQSxNQUFLRCxhQUFhLEdBQUc7WUFDdkI7UUFDRjs7SUF0VEE7O0dBRUMsR0FDRFMsYUFBYztRQUNaLEtBQUs7UUFDTCxJQUFJLENBQUN6RixnQkFBZ0IsR0FBR1QsZ0JBQWdCbUcsV0FBVztRQUNuRCxJQUFJLENBQUNuRixNQUFNO1FBQ1gsSUFBSSxDQUFDOEUsT0FBTyxHQUFHNUYsY0FBY2tHLFNBQVMsQ0FBQztJQUN6QztBQWdURjtBQTdUQTs7Q0FFQyxHQUNELFNBQXFCakcsa0NBMFRwQiJ9