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)

395 lines (394 loc) 51.3 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 TrackerEventListenerManager from './trackerEventListenerManager'; import PeriodStatisticsStreamManager from './periodStatisticsStreamManager'; import EquityChartStreamManager from './equityChartStreamManager'; import EquityBalanceStreamManager from './equityBalanceStreamManager'; import moment from 'moment'; let EquityTrackingClient = class EquityTrackingClient { /** * Tracker configuration update * @typedef {Object} TrackerUpdate * @property {String} name unique tracker name */ /** * Period length to track profit and drawdown for * @typedef {'day' | 'date' | 'week' | 'week-to-date' | 'month' | 'month-to-date' | 'quarter' | 'quarter-to-date' | * 'year' | 'year-to-date' | 'lifetime'} Period */ /** * New tracker configuration * @typedef {TrackerUpdate} NewTracker * @property {String} [startBrokerTime] time to start tracking from in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format * @property {String} [endBrokerTime] time to end tracking at in broker timezone, YYYY-MM-DD HH:mm:ss.SSS format * @property {Period} period period to track profit and drawdown for * @property {Number} [relativeDrawdownThreshold] relative drawdown threshold after which tracker event is generated, * a fraction of 1 * @property {Number} [absoluteDrawdownThreshold] absolute drawdown threshold after which tracker event is generated, * should be greater than 0 * @property {Number} [relativeProfitThreshold] relative profit threshold after which tracker event is generated, * a fraction of 1 * @property {Number} [absoluteProfitThreshold] absolute profit threshold after which tracker event is generated, * should be greater than 0 */ /** * Tracker id * @typedef {Object} TrackerId * @property {String} id unique tracker id */ /** * Creates a profit/drawdown tracker. See * https://metaapi.cloud/docs/risk-management/restApi/api/createTracker/ * @param {String} accountId id of the MetaApi account * @param {NewTracker} tracker profit/drawdown tracker * @return {Promise<TrackerId>} promise resolving with profit/drawdown tracker id */ createTracker(accountId, tracker) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers`, headers: { 'auth-token': this._domainClient.token, 'api-version': '1' }, method: 'POST', data: tracker }); } /** * Tracker configuration * @typedef {NewTracker} Tracker * @property {String} _id unique tracker id */ /** * Returns trackers defined for an account. See * https://metaapi.cloud/docs/risk-management/restApi/api/getTrackers/ * @param {String} accountId id of the MetaApi account * @return {Promise<Tracker[]>} promise resolving with trackers */ getTrackers(accountId) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers`, headers: { 'auth-token': this._domainClient.token, 'api-version': '1' }, method: 'GET' }); } /** * Returns profit/drawdown tracker by account and id. See * https://metaapi.cloud/docs/risk-management/restApi/api/getTracker/ * @param {string} accountId id of the MetaApi account * @param {string} id tracker id * @returns {Promise<Tracker>} promise resolving with profit/drawdown tracker found */ getTracker(accountId, id) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers/${id}`, headers: { 'auth-token': this._domainClient.token, 'api-version': '1' }, method: 'GET' }); } /** * Returns profit/drawdown tracker by account and name * @param {string} accountId id of the MetaApi account * @param {string} name tracker name * @returns {Promise<Tracker>} promise resolving with profit/drawdown tracker found */ getTrackerByName(accountId, name) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers/name/${encodeURIComponent(name)}`, headers: { 'auth-token': this._domainClient.token, 'api-version': '1' }, method: 'GET' }); } /** * Updates profit/drawdown tracker. See * https://metaapi.cloud/docs/risk-management/restApi/api/updateTracker/ * @param {String} accountId id of the MetaApi account * @param {String} id id of the tracker * @param {TrackerUpdate} update tracker update * @return {Promise} promise resolving when profit/drawdown tracker updated */ updateTracker(accountId, id, update) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers/${id}`, method: 'PUT', data: update }); } /** * Removes profit/drawdown tracker. See * https://metaapi.cloud/docs/risk-management/restApi/api/removeTracker/ * @param {String} accountId id of the MetaApi account * @param {String} id id of the tracker * @return {Promise} promise resolving when profit/drawdown tracker removed */ deleteTracker(accountId, id) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers/${id}`, method: 'DELETE' }); } /** * Type of the exceeded threshold * @typedef {'profit' | 'drawdown'} ExceededThresholdType */ /** * Profit/drawdown threshold exceeded event model * @typedef {Object} TrackerEvent * @property {Number} sequenceNumber event unique sequence number * @property {String} accountId MetaApi account id * @property {String} trackerId profit/drawdown tracker id * @property {String} startBrokerTime profit/drawdown tracking period start time in broker timezone, * in YYYY-MM-DD HH:mm:ss.SSS format * @property {String} [endBrokerTime] profit/drawdown tracking period end time in broker timezone, * in YYYY-MM-DD HH:mm:ss.SSS format * @property {Period} period profit/drawdown tracking period * @property {String} brokerTime profit/drawdown threshold exceeded event time in broker timezone, * in YYY-MM-DD HH:mm:ss.SSS format * @property {Number} absoluteDrawdown absolute drawdown value which was observed when the profit or drawdown * threshold was exceeded * @property {Number} relativeDrawdown relative drawdown value which was observed when the profit or drawdown * threshold was exceeded * @property {Number} absoluteProfit absolute profit value which was observed when the profit or drawdown * threshold was exceeded * @property {Number} relativeProfit relative profit value which was observed when the profit or drawdown * threshold was exceeded * @property {ExceededThresholdType} exceededThresholdType type of the exceeded threshold */ /** * Returns tracker events by broker time range. See * https://metaapi.cloud/docs/risk-management/restApi/api/getTrackerEvents/ * @param {String} [startBrokerTime] value of the event time in broker timezone to start loading data from, inclusive, * in 'YYYY-MM-DD HH:mm:ss.SSS format * @param {String} [endBrokerTime] value of the event time in broker timezone to end loading data at, inclusive, * in 'YYYY-MM-DD HH:mm:ss.SSS format * @param {String} [accountId] id of the MetaApi account * @param {String} [trackerId] id of the tracker * @param {Number} [limit] pagination limit, default is 1000 * @return {Promise<TrackerEvent[]>} promise resolving with tracker events */ getTrackerEvents(startBrokerTime, endBrokerTime, accountId, trackerId, limit) { return this._domainClient.requestApi({ url: '/users/current/tracker-events/by-broker-time', params: { startBrokerTime, endBrokerTime, accountId, trackerId, limit }, method: 'GET' }); } /** * Adds a tracker event listener and creates a job to make requests * @param {TrackerEventListener} listener tracker event listener * @param {String} [accountId] account id * @param {String} [trackerId] tracker id * @param {Number} [sequenceNumber] sequence number * @return {String} listener id */ addTrackerEventListener(listener, accountId, trackerId, sequenceNumber) { return this._trackerEventListenerManager.addTrackerEventListener(listener, accountId, trackerId, sequenceNumber); } /** * Removes tracker event listener and cancels the event stream * @param {String} listenerId tracker event listener id */ removeTrackerEventListener(listenerId) { this._trackerEventListenerManager.removeTrackerEventListener(listenerId); } /** * Period statistics * @typedef {Object} PeriodStatistics * @property {String} startBrokerTime period start time in broker timezone, in YYYY-MM-DD HH:mm:ss format * @property {String} [endBrokerTime] period end time in broker timezone, in YYYY-MM-DD HH:mm:ss format * @property {Period} period period length * @property {Number} initialBalance balance at period start time * @property {String} [maxDrawdownTime] time max drawdown was observed at in broker timezone, * in YYYY-MM-DD HH:mm:ss format * @property {Number} [maxAbsoluteDrawdown] the value of maximum absolute drawdown observed * @property {Number} [maxRelativeDrawdown] the value of maximum relative drawdown observed * @property {String} [maxProfitTime] time max profit was observed at in broker timezone, * in YYYY-MM-DD HH:mm:ss format * @property {Number} [maxAbsoluteProfit] the value of maximum absolute profit observed * @property {Number} [maxRelativeProfit] the value of maximum relative profit observed * @property {Boolean} thresholdExceeded the flag indicating that max allowed total drawdown was exceeded * @property {ExceededThresholdType} exceededThresholdType type of the exceeded threshold * @property {Number} [balanceAdjustment] balance adjustment applied to equity for tracking drawdown/profit * @property {Number} [tradeDayCount] count of days when trades were performed during the period */ /** * Returns account profit and drawdown tracking statistics by tracker id. See * https://metaapi.cloud/docs/risk-management/restApi/api/getTrackingStats/ * @param {String} accountId id of MetaAPI account * @param {String} trackerId id of the tracker * @param {String} [startTime] time to start loading stats from, default is current time. Note that stats is loaded in * backwards direction * @param {Number} [limit] number of records to load, default is 1 * @param {Boolean} [realTime] if true, real-time data will be requested * @return {Promise<PeriodStatistics[]>} promise resolving with profit and drawdown statistics */ getTrackingStatistics(accountId, trackerId, startTime, limit, realTime = false) { return this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/trackers/${trackerId}/statistics`, headers: { 'auth-token': this._domainClient.token, 'api-version': '1' }, params: { startTime, limit, realTime }, method: 'GET' }); } /** * Adds a period statistics event listener * @param {PeriodStatisticsListener} listener period statistics event listener * @param {String} accountId account id * @param {String} trackerId tracker id * @returns {Promise<String>} listener id */ addPeriodStatisticsListener(listener, accountId, trackerId) { return this._periodStatisticsStreamManager.addPeriodStatisticsListener(listener, accountId, trackerId); } /** * Removes period statistics event listener by id * @param {String} listenerId listener id */ removePeriodStatisticsListener(listenerId) { this._periodStatisticsStreamManager.removePeriodStatisticsListener(listenerId); } /** * Equity chart item * @typedef {Object} EquityChartItem * @property {String} startBrokerTime start time of a chart item as per broker timezone, in YYYY-MM-DD HH:mm:ss format * @property {String} endBrokerTime end time of a chart item as per broker timezone, in YYYY-MM-DD HH:mm:ss format * @property {Number} averageBalance average balance value during the period * @property {Number} minBalance minimum balance value during the period * @property {Number} maxBalance maximum balance value during the period * @property {Number} averageEquity average equity value during the period * @property {Number} minEquity minimum equity value during the period * @property {Number} maxEquity maximum equity value during the period * @property {Number} startBalance starting balance value observed during the period * @property {Number} startEquity starting equity value observed during the period * @property {Number} lastBalance last balance value observed during the period * @property {Number} lastEquity last equity value observed during the period */ /** * Returns equity chart by account id. See * https://metaapi.cloud/docs/risk-management/restApi/api/getEquityChart/ * @param {String} accountId metaApi account id * @param {String} [startTime] starting broker time in YYYY-MM-DD HH:mm:ss format * @param {String} [endTime] ending broker time in YYYY-MM-DD HH:mm:ss format * @param {Boolean} [realTime] if true, real-time data will be requested * @param {Boolean} [fillSkips] if true, skipped records will be automatically filled based on existing ones * @return {Promise<EquityChartItem[]>} promise resolving with equity chart */ getEquityChart(accountId, startTime, endTime, realTime = false, fillSkips = false) { var _this = this; return _async_to_generator(function*() { const records = yield _this._domainClient.requestApi({ url: `/users/current/accounts/${accountId}/equity-chart`, headers: { 'auth-token': _this._domainClient.token, 'api-version': '1' }, params: { startTime, endTime, realTime }, method: 'GET' }); if (fillSkips) { let i = 0; while(i < records.length - 1){ const timeDiff = new Date(records[i + 1].startBrokerTime).getTime() - new Date(records[i].startBrokerTime).getTime(); if (timeDiff > 60 * 60 * 1000 && records[i].lastBalance !== undefined) { const recordCopy = JSON.parse(JSON.stringify(records[i])); recordCopy.minEquity = recordCopy.lastEquity; recordCopy.maxEquity = recordCopy.lastEquity; recordCopy.averageEquity = recordCopy.lastEquity; recordCopy.minBalance = recordCopy.lastBalance; recordCopy.maxBalance = recordCopy.lastBalance; recordCopy.averageBalance = recordCopy.lastBalance; const startBrokerTime = new Date(recordCopy.startBrokerTime); startBrokerTime.setUTCHours(startBrokerTime.getUTCHours() + 1); startBrokerTime.setUTCMinutes(0); startBrokerTime.setUTCSeconds(0); startBrokerTime.setUTCMilliseconds(0); recordCopy.startBrokerTime = moment(startBrokerTime).format('YYYY-MM-DD HH:mm:ss.SSS'); startBrokerTime.setUTCHours(startBrokerTime.getUTCHours() + 1); startBrokerTime.setUTCMilliseconds(-1); recordCopy.endBrokerTime = moment(startBrokerTime).format('YYYY-MM-DD HH:mm:ss.SSS'); recordCopy.brokerTime = recordCopy.endBrokerTime; records.splice(i + 1, 0, recordCopy); } i++; } } return records; })(); } /** * Adds an equity chart event listener * @param {EquityChartListener} listener equity chart event listener * @param {String} accountId account id * @param {Date} [startTime] date to start tracking from * @returns {Promise<string>} listener id */ addEquityChartListener(listener, accountId, startTime) { return this._equityChartStreamManager.addEquityChartListener(listener, accountId, startTime); } /** * Removes equity chart event listener by id * @param {String} listenerId equity chart listener id */ removeEquityChartListener(listenerId) { this._equityChartStreamManager.removeEquityChartListener(listenerId); } /** * Adds an equity balance event listener * @param {EquityBalanceListener} listener equity balance event listener * @param {string} accountId account id * @returns {Promise<string>} listener id */ addEquityBalanceListener(listener, accountId) { return this._equityBalanceStreamManager.addEquityBalanceListener(listener, accountId); } /** * Removes equity balance event listener by id * @param {string} listenerId equity balance listener id */ removeEquityBalanceListener(listenerId) { this._equityBalanceStreamManager.removeEquityBalanceListener(listenerId); } /** * Constructs RiskManagement equity tracking API client instance * @param {DomainClient} domainClient domain client * @param {MetaApi} metaApi metaApi SDK instance */ constructor(domainClient, metaApi){ this._domainClient = domainClient; this._trackerEventListenerManager = new TrackerEventListenerManager(domainClient); this._equityBalanceStreamManager = new EquityBalanceStreamManager(domainClient, metaApi); this._periodStatisticsStreamManager = new PeriodStatisticsStreamManager(domainClient, this, metaApi); this._equityChartStreamManager = new EquityChartStreamManager(domainClient, this, metaApi); } }; /** * metaapi.cloud RiskManagement equity tracking API client (see https://metaapi.cloud/docs/risk-management/) */ export { EquityTrackingClient as default }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyIndXNlIHN0cmljdCc7XG5cbmltcG9ydCBUcmFja2VyRXZlbnRMaXN0ZW5lck1hbmFnZXIgZnJvbSAnLi90cmFja2VyRXZlbnRMaXN0ZW5lck1hbmFnZXInO1xuaW1wb3J0IFBlcmlvZFN0YXRpc3RpY3NTdHJlYW1NYW5hZ2VyIGZyb20gJy4vcGVyaW9kU3RhdGlzdGljc1N0cmVhbU1hbmFnZXInO1xuaW1wb3J0IEVxdWl0eUNoYXJ0U3RyZWFtTWFuYWdlciBmcm9tICcuL2VxdWl0eUNoYXJ0U3RyZWFtTWFuYWdlcic7XG5pbXBvcnQgRXF1aXR5QmFsYW5jZVN0cmVhbU1hbmFnZXIgZnJvbSAnLi9lcXVpdHlCYWxhbmNlU3RyZWFtTWFuYWdlcic7XG5pbXBvcnQgbW9tZW50IGZyb20gJ21vbWVudCc7XG5cbi8qKlxuICogbWV0YWFwaS5jbG91ZCBSaXNrTWFuYWdlbWVudCBlcXVpdHkgdHJhY2tpbmcgQVBJIGNsaWVudCAoc2VlIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL3Jpc2stbWFuYWdlbWVudC8pXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEVxdWl0eVRyYWNraW5nQ2xpZW50IHtcblxuICAvKipcbiAgICogQ29uc3RydWN0cyBSaXNrTWFuYWdlbWVudCBlcXVpdHkgdHJhY2tpbmcgQVBJIGNsaWVudCBpbnN0YW5jZVxuICAgKiBAcGFyYW0ge0RvbWFpbkNsaWVudH0gZG9tYWluQ2xpZW50IGRvbWFpbiBjbGllbnRcbiAgICogQHBhcmFtIHtNZXRhQXBpfSBtZXRhQXBpIG1ldGFBcGkgU0RLIGluc3RhbmNlXG4gICAqL1xuICBjb25zdHJ1Y3Rvcihkb21haW5DbGllbnQsIG1ldGFBcGkpIHtcbiAgICB0aGlzLl9kb21haW5DbGllbnQgPSBkb21haW5DbGllbnQ7XG4gICAgdGhpcy5fdHJhY2tlckV2ZW50TGlzdGVuZXJNYW5hZ2VyID0gbmV3IFRyYWNrZXJFdmVudExpc3RlbmVyTWFuYWdlcihkb21haW5DbGllbnQpO1xuICAgIHRoaXMuX2VxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyID0gbmV3IEVxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyKGRvbWFpbkNsaWVudCwgbWV0YUFwaSk7XG4gICAgdGhpcy5fcGVyaW9kU3RhdGlzdGljc1N0cmVhbU1hbmFnZXIgPSBuZXcgUGVyaW9kU3RhdGlzdGljc1N0cmVhbU1hbmFnZXIoZG9tYWluQ2xpZW50LCB0aGlzLCBtZXRhQXBpKTtcbiAgICB0aGlzLl9lcXVpdHlDaGFydFN0cmVhbU1hbmFnZXIgPSBuZXcgRXF1aXR5Q2hhcnRTdHJlYW1NYW5hZ2VyKGRvbWFpbkNsaWVudCwgdGhpcywgbWV0YUFwaSk7IFxuICB9XG5cbiAgLyoqXG4gICAqIFRyYWNrZXIgY29uZmlndXJhdGlvbiB1cGRhdGVcbiAgICogQHR5cGVkZWYge09iamVjdH0gVHJhY2tlclVwZGF0ZVxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gbmFtZSB1bmlxdWUgdHJhY2tlciBuYW1lXG4gICAqL1xuXG4gIC8qKlxuICAgKiBQZXJpb2QgbGVuZ3RoIHRvIHRyYWNrIHByb2ZpdCBhbmQgZHJhd2Rvd24gZm9yXG4gICAqIEB0eXBlZGVmIHsnZGF5JyB8ICdkYXRlJyB8ICd3ZWVrJyB8ICd3ZWVrLXRvLWRhdGUnIHwgJ21vbnRoJyB8ICdtb250aC10by1kYXRlJyB8ICdxdWFydGVyJyB8ICdxdWFydGVyLXRvLWRhdGUnIHxcbiAgICogJ3llYXInIHwgJ3llYXItdG8tZGF0ZScgfCAnbGlmZXRpbWUnfSBQZXJpb2RcbiAgICovXG5cbiAgLyoqXG4gICAqIE5ldyB0cmFja2VyIGNvbmZpZ3VyYXRpb25cbiAgICogQHR5cGVkZWYge1RyYWNrZXJVcGRhdGV9IE5ld1RyYWNrZXJcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IFtzdGFydEJyb2tlclRpbWVdIHRpbWUgdG8gc3RhcnQgdHJhY2tpbmcgZnJvbSBpbiBicm9rZXIgdGltZXpvbmUsIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW2VuZEJyb2tlclRpbWVdIHRpbWUgdG8gZW5kIHRyYWNraW5nIGF0IGluIGJyb2tlciB0aW1lem9uZSwgWVlZWS1NTS1ERCBISDptbTpzcy5TU1MgZm9ybWF0XG4gICAqIEBwcm9wZXJ0eSB7UGVyaW9kfSBwZXJpb2QgcGVyaW9kIHRvIHRyYWNrIHByb2ZpdCBhbmQgZHJhd2Rvd24gZm9yXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbcmVsYXRpdmVEcmF3ZG93blRocmVzaG9sZF0gcmVsYXRpdmUgZHJhd2Rvd24gdGhyZXNob2xkIGFmdGVyIHdoaWNoIHRyYWNrZXIgZXZlbnQgaXMgZ2VuZXJhdGVkLFxuICAgKiBhIGZyYWN0aW9uIG9mIDFcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFthYnNvbHV0ZURyYXdkb3duVGhyZXNob2xkXSBhYnNvbHV0ZSBkcmF3ZG93biB0aHJlc2hvbGQgYWZ0ZXIgd2hpY2ggdHJhY2tlciBldmVudCBpcyBnZW5lcmF0ZWQsXG4gICAqIHNob3VsZCBiZSBncmVhdGVyIHRoYW4gMFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gW3JlbGF0aXZlUHJvZml0VGhyZXNob2xkXSByZWxhdGl2ZSBwcm9maXQgdGhyZXNob2xkIGFmdGVyIHdoaWNoIHRyYWNrZXIgZXZlbnQgaXMgZ2VuZXJhdGVkLFxuICAgKiBhIGZyYWN0aW9uIG9mIDFcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFthYnNvbHV0ZVByb2ZpdFRocmVzaG9sZF0gYWJzb2x1dGUgcHJvZml0IHRocmVzaG9sZCBhZnRlciB3aGljaCB0cmFja2VyIGV2ZW50IGlzIGdlbmVyYXRlZCxcbiAgICogc2hvdWxkIGJlIGdyZWF0ZXIgdGhhbiAwXG4gICAqL1xuXG4gIC8qKlxuICAgKiBUcmFja2VyIGlkXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFRyYWNrZXJJZFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gaWQgdW5pcXVlIHRyYWNrZXIgaWRcbiAgICovXG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBwcm9maXQvZHJhd2Rvd24gdHJhY2tlci4gU2VlXG4gICAqIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL3Jpc2stbWFuYWdlbWVudC9yZXN0QXBpL2FwaS9jcmVhdGVUcmFja2VyL1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhQXBpIGFjY291bnRcbiAgICogQHBhcmFtIHtOZXdUcmFja2VyfSB0cmFja2VyIHByb2ZpdC9kcmF3ZG93biB0cmFja2VyXG4gICAqIEByZXR1cm4ge1Byb21pc2U8VHJhY2tlcklkPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBwcm9maXQvZHJhd2Rvd24gdHJhY2tlciBpZFxuICAgKi9cbiAgY3JlYXRlVHJhY2tlcihhY2NvdW50SWQsIHRyYWNrZXIpIHtcbiAgICByZXR1cm4gdGhpcy5fZG9tYWluQ2xpZW50LnJlcXVlc3RBcGkoe1xuICAgICAgdXJsOiBgL3VzZXJzL2N1cnJlbnQvYWNjb3VudHMvJHthY2NvdW50SWR9L3RyYWNrZXJzYCxcbiAgICAgIGhlYWRlcnM6IHsnYXV0aC10b2tlbic6IHRoaXMuX2RvbWFpbkNsaWVudC50b2tlbiwgJ2FwaS12ZXJzaW9uJzogJzEnfSxcbiAgICAgIG1ldGhvZDogJ1BPU1QnLFxuICAgICAgZGF0YTogdHJhY2tlclxuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFRyYWNrZXIgY29uZmlndXJhdGlvblxuICAgKiBAdHlwZWRlZiB7TmV3VHJhY2tlcn0gVHJhY2tlclxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gX2lkIHVuaXF1ZSB0cmFja2VyIGlkXG4gICAqL1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHRyYWNrZXJzIGRlZmluZWQgZm9yIGFuIGFjY291bnQuIFNlZVxuICAgKiBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9yaXNrLW1hbmFnZW1lbnQvcmVzdEFwaS9hcGkvZ2V0VHJhY2tlcnMvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFBcGkgYWNjb3VudFxuICAgKiBAcmV0dXJuIHtQcm9taXNlPFRyYWNrZXJbXT59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggdHJhY2tlcnNcbiAgICovXG4gIGdldFRyYWNrZXJzKGFjY291bnRJZCkge1xuICAgIHJldHVybiB0aGlzLl9kb21haW5DbGllbnQucmVxdWVzdEFwaSh7XG4gICAgICB1cmw6IGAvdXNlcnMvY3VycmVudC9hY2NvdW50cy8ke2FjY291bnRJZH0vdHJhY2tlcnNgLFxuICAgICAgaGVhZGVyczogeydhdXRoLXRva2VuJzogdGhpcy5fZG9tYWluQ2xpZW50LnRva2VuLCAnYXBpLXZlcnNpb24nOiAnMSd9LFxuICAgICAgbWV0aG9kOiAnR0VUJ1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcHJvZml0L2RyYXdkb3duIHRyYWNrZXIgYnkgYWNjb3VudCBhbmQgaWQuIFNlZVxuICAgKiBodHRwczovL21ldGFhcGkuY2xvdWQvZG9jcy9yaXNrLW1hbmFnZW1lbnQvcmVzdEFwaS9hcGkvZ2V0VHJhY2tlci9cbiAgICogQHBhcmFtIHtzdHJpbmd9IGFjY291bnRJZCBpZCBvZiB0aGUgTWV0YUFwaSBhY2NvdW50IFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaWQgdHJhY2tlciBpZCBcbiAgICogQHJldHVybnMge1Byb21pc2U8VHJhY2tlcj59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggcHJvZml0L2RyYXdkb3duIHRyYWNrZXIgZm91bmRcbiAgICovXG4gIGdldFRyYWNrZXIoYWNjb3VudElkLCBpZCkge1xuICAgIHJldHVybiB0aGlzLl9kb21haW5DbGllbnQucmVxdWVzdEFwaSh7XG4gICAgICB1cmw6IGAvdXNlcnMvY3VycmVudC9hY2NvdW50cy8ke2FjY291bnRJZH0vdHJhY2tlcnMvJHtpZH1gLFxuICAgICAgaGVhZGVyczogeydhdXRoLXRva2VuJzogdGhpcy5fZG9tYWluQ2xpZW50LnRva2VuLCAnYXBpLXZlcnNpb24nOiAnMSd9LFxuICAgICAgbWV0aG9kOiAnR0VUJ1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgcHJvZml0L2RyYXdkb3duIHRyYWNrZXIgYnkgYWNjb3VudCBhbmQgbmFtZVxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhQXBpIGFjY291bnQgXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lIHRyYWNrZXIgbmFtZSBcbiAgICogQHJldHVybnMge1Byb21pc2U8VHJhY2tlcj59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggcHJvZml0L2RyYXdkb3duIHRyYWNrZXIgZm91bmRcbiAgICovXG4gIGdldFRyYWNrZXJCeU5hbWUoYWNjb3VudElkLCBuYW1lKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RvbWFpbkNsaWVudC5yZXF1ZXN0QXBpKHtcbiAgICAgIHVybDogYC91c2Vycy9jdXJyZW50L2FjY291bnRzLyR7YWNjb3VudElkfS90cmFja2Vycy9uYW1lLyR7ZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpfWAsXG4gICAgICBoZWFkZXJzOiB7J2F1dGgtdG9rZW4nOiB0aGlzLl9kb21haW5DbGllbnQudG9rZW4sICdhcGktdmVyc2lvbic6ICcxJ30sXG4gICAgICBtZXRob2Q6ICdHRVQnXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogVXBkYXRlcyBwcm9maXQvZHJhd2Rvd24gdHJhY2tlci4gU2VlXG4gICAqIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL3Jpc2stbWFuYWdlbWVudC9yZXN0QXBpL2FwaS91cGRhdGVUcmFja2VyL1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGlkIG9mIHRoZSBNZXRhQXBpIGFjY291bnRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGlkIGlkIG9mIHRoZSB0cmFja2VyXG4gICAqIEBwYXJhbSB7VHJhY2tlclVwZGF0ZX0gdXBkYXRlIHRyYWNrZXIgdXBkYXRlXG4gICAqIEByZXR1cm4ge1Byb21pc2V9IHByb21pc2UgcmVzb2x2aW5nIHdoZW4gcHJvZml0L2RyYXdkb3duIHRyYWNrZXIgdXBkYXRlZFxuICAgKi9cbiAgdXBkYXRlVHJhY2tlcihhY2NvdW50SWQsIGlkLCB1cGRhdGUpIHtcbiAgICByZXR1cm4gdGhpcy5fZG9tYWluQ2xpZW50LnJlcXVlc3RBcGkoe1xuICAgICAgdXJsOiBgL3VzZXJzL2N1cnJlbnQvYWNjb3VudHMvJHthY2NvdW50SWR9L3RyYWNrZXJzLyR7aWR9YCxcbiAgICAgIG1ldGhvZDogJ1BVVCcsXG4gICAgICBkYXRhOiB1cGRhdGVcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIHByb2ZpdC9kcmF3ZG93biB0cmFja2VyLiBTZWVcbiAgICogaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3Mvcmlzay1tYW5hZ2VtZW50L3Jlc3RBcGkvYXBpL3JlbW92ZVRyYWNrZXIvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgdGhlIE1ldGFBcGkgYWNjb3VudFxuICAgKiBAcGFyYW0ge1N0cmluZ30gaWQgaWQgb2YgdGhlIHRyYWNrZXJcbiAgICogQHJldHVybiB7UHJvbWlzZX0gcHJvbWlzZSByZXNvbHZpbmcgd2hlbiBwcm9maXQvZHJhd2Rvd24gdHJhY2tlciByZW1vdmVkXG4gICAqL1xuICBkZWxldGVUcmFja2VyKGFjY291bnRJZCwgaWQpIHtcbiAgICByZXR1cm4gdGhpcy5fZG9tYWluQ2xpZW50LnJlcXVlc3RBcGkoe1xuICAgICAgdXJsOiBgL3VzZXJzL2N1cnJlbnQvYWNjb3VudHMvJHthY2NvdW50SWR9L3RyYWNrZXJzLyR7aWR9YCxcbiAgICAgIG1ldGhvZDogJ0RFTEVURSdcbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBUeXBlIG9mIHRoZSBleGNlZWRlZCB0aHJlc2hvbGRcbiAgICogQHR5cGVkZWYgeydwcm9maXQnIHwgJ2RyYXdkb3duJ30gRXhjZWVkZWRUaHJlc2hvbGRUeXBlXG4gICAqL1xuXG4gIC8qKlxuICAgKiBQcm9maXQvZHJhd2Rvd24gdGhyZXNob2xkIGV4Y2VlZGVkIGV2ZW50IG1vZGVsXG4gICAqIEB0eXBlZGVmIHtPYmplY3R9IFRyYWNrZXJFdmVudFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gc2VxdWVuY2VOdW1iZXIgZXZlbnQgdW5pcXVlIHNlcXVlbmNlIG51bWJlclxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gYWNjb3VudElkIE1ldGFBcGkgYWNjb3VudCBpZFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gdHJhY2tlcklkIHByb2ZpdC9kcmF3ZG93biB0cmFja2VyIGlkXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBzdGFydEJyb2tlclRpbWUgcHJvZml0L2RyYXdkb3duIHRyYWNraW5nIHBlcmlvZCBzdGFydCB0aW1lIGluIGJyb2tlciB0aW1lem9uZSxcbiAgICogaW4gWVlZWS1NTS1ERCBISDptbTpzcy5TU1MgZm9ybWF0XG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbZW5kQnJva2VyVGltZV0gcHJvZml0L2RyYXdkb3duIHRyYWNraW5nIHBlcmlvZCBlbmQgdGltZSBpbiBicm9rZXIgdGltZXpvbmUsXG4gICAqIGluIFlZWVktTU0tREQgSEg6bW06c3MuU1NTIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge1BlcmlvZH0gcGVyaW9kIHByb2ZpdC9kcmF3ZG93biB0cmFja2luZyBwZXJpb2RcbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IGJyb2tlclRpbWUgcHJvZml0L2RyYXdkb3duIHRocmVzaG9sZCBleGNlZWRlZCBldmVudCB0aW1lIGluIGJyb2tlciB0aW1lem9uZSxcbiAgICogaW4gWVlZLU1NLUREIEhIOm1tOnNzLlNTUyBmb3JtYXRcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IGFic29sdXRlRHJhd2Rvd24gYWJzb2x1dGUgZHJhd2Rvd24gdmFsdWUgd2hpY2ggd2FzIG9ic2VydmVkIHdoZW4gdGhlIHByb2ZpdCBvciBkcmF3ZG93blxuICAgKiB0aHJlc2hvbGQgd2FzIGV4Y2VlZGVkXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSByZWxhdGl2ZURyYXdkb3duIHJlbGF0aXZlIGRyYXdkb3duIHZhbHVlIHdoaWNoIHdhcyBvYnNlcnZlZCB3aGVuIHRoZSBwcm9maXQgb3IgZHJhd2Rvd25cbiAgICogdGhyZXNob2xkIHdhcyBleGNlZWRlZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gYWJzb2x1dGVQcm9maXQgYWJzb2x1dGUgcHJvZml0IHZhbHVlIHdoaWNoIHdhcyBvYnNlcnZlZCB3aGVuIHRoZSBwcm9maXQgb3IgZHJhd2Rvd25cbiAgICogdGhyZXNob2xkIHdhcyBleGNlZWRlZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gcmVsYXRpdmVQcm9maXQgcmVsYXRpdmUgcHJvZml0IHZhbHVlIHdoaWNoIHdhcyBvYnNlcnZlZCB3aGVuIHRoZSBwcm9maXQgb3IgZHJhd2Rvd25cbiAgICogdGhyZXNob2xkIHdhcyBleGNlZWRlZFxuICAgKiBAcHJvcGVydHkge0V4Y2VlZGVkVGhyZXNob2xkVHlwZX0gZXhjZWVkZWRUaHJlc2hvbGRUeXBlIHR5cGUgb2YgdGhlIGV4Y2VlZGVkIHRocmVzaG9sZFxuICAgKi9cblxuICAvKipcbiAgICogUmV0dXJucyB0cmFja2VyIGV2ZW50cyBieSBicm9rZXIgdGltZSByYW5nZS4gU2VlXG4gICAqIGh0dHBzOi8vbWV0YWFwaS5jbG91ZC9kb2NzL3Jpc2stbWFuYWdlbWVudC9yZXN0QXBpL2FwaS9nZXRUcmFja2VyRXZlbnRzL1xuICAgKiBAcGFyYW0ge1N0cmluZ30gW3N0YXJ0QnJva2VyVGltZV0gdmFsdWUgb2YgdGhlIGV2ZW50IHRpbWUgaW4gYnJva2VyIHRpbWV6b25lIHRvIHN0YXJ0IGxvYWRpbmcgZGF0YSBmcm9tLCBpbmNsdXNpdmUsXG4gICAqIGluICdZWVlZLU1NLUREIEhIOm1tOnNzLlNTUyBmb3JtYXRcbiAgICogQHBhcmFtIHtTdHJpbmd9IFtlbmRCcm9rZXJUaW1lXSB2YWx1ZSBvZiB0aGUgZXZlbnQgdGltZSBpbiBicm9rZXIgdGltZXpvbmUgdG8gZW5kIGxvYWRpbmcgZGF0YSBhdCwgaW5jbHVzaXZlLFxuICAgKiBpbiAnWVlZWS1NTS1ERCBISDptbTpzcy5TU1MgZm9ybWF0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbYWNjb3VudElkXSBpZCBvZiB0aGUgTWV0YUFwaSBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBbdHJhY2tlcklkXSBpZCBvZiB0aGUgdHJhY2tlclxuICAgKiBAcGFyYW0ge051bWJlcn0gW2xpbWl0XSBwYWdpbmF0aW9uIGxpbWl0LCBkZWZhdWx0IGlzIDEwMDBcbiAgICogQHJldHVybiB7UHJvbWlzZTxUcmFja2VyRXZlbnRbXT59IHByb21pc2UgcmVzb2x2aW5nIHdpdGggdHJhY2tlciBldmVudHNcbiAgICovXG4gIGdldFRyYWNrZXJFdmVudHMoc3RhcnRCcm9rZXJUaW1lLCBlbmRCcm9rZXJUaW1lLCBhY2NvdW50SWQsIHRyYWNrZXJJZCwgbGltaXQpIHtcbiAgICByZXR1cm4gdGhpcy5fZG9tYWluQ2xpZW50LnJlcXVlc3RBcGkoe1xuICAgICAgdXJsOiAnL3VzZXJzL2N1cnJlbnQvdHJhY2tlci1ldmVudHMvYnktYnJva2VyLXRpbWUnLFxuICAgICAgcGFyYW1zOiB7c3RhcnRCcm9rZXJUaW1lLCBlbmRCcm9rZXJUaW1lLCBhY2NvdW50SWQsIHRyYWNrZXJJZCwgbGltaXR9LFxuICAgICAgbWV0aG9kOiAnR0VUJ1xuICAgIH0pO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYSB0cmFja2VyIGV2ZW50IGxpc3RlbmVyIGFuZCBjcmVhdGVzIGEgam9iIHRvIG1ha2UgcmVxdWVzdHNcbiAgICogQHBhcmFtIHtUcmFja2VyRXZlbnRMaXN0ZW5lcn0gbGlzdGVuZXIgdHJhY2tlciBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge1N0cmluZ30gW2FjY291bnRJZF0gYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gW3RyYWNrZXJJZF0gdHJhY2tlciBpZFxuICAgKiBAcGFyYW0ge051bWJlcn0gW3NlcXVlbmNlTnVtYmVyXSBzZXF1ZW5jZSBudW1iZXJcbiAgICogQHJldHVybiB7U3RyaW5nfSBsaXN0ZW5lciBpZFxuICAgKi9cbiAgYWRkVHJhY2tlckV2ZW50TGlzdGVuZXIobGlzdGVuZXIsIGFjY291bnRJZCwgdHJhY2tlcklkLCBzZXF1ZW5jZU51bWJlcikge1xuICAgIHJldHVybiB0aGlzLl90cmFja2VyRXZlbnRMaXN0ZW5lck1hbmFnZXIuYWRkVHJhY2tlckV2ZW50TGlzdGVuZXIobGlzdGVuZXIsIGFjY291bnRJZCwgdHJhY2tlcklkLCBzZXF1ZW5jZU51bWJlcik7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyB0cmFja2VyIGV2ZW50IGxpc3RlbmVyIGFuZCBjYW5jZWxzIHRoZSBldmVudCBzdHJlYW1cbiAgICogQHBhcmFtIHtTdHJpbmd9IGxpc3RlbmVySWQgdHJhY2tlciBldmVudCBsaXN0ZW5lciBpZFxuICAgKi9cbiAgcmVtb3ZlVHJhY2tlckV2ZW50TGlzdGVuZXIobGlzdGVuZXJJZCkge1xuICAgIHRoaXMuX3RyYWNrZXJFdmVudExpc3RlbmVyTWFuYWdlci5yZW1vdmVUcmFja2VyRXZlbnRMaXN0ZW5lcihsaXN0ZW5lcklkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQZXJpb2Qgc3RhdGlzdGljc1xuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBQZXJpb2RTdGF0aXN0aWNzXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBzdGFydEJyb2tlclRpbWUgcGVyaW9kIHN0YXJ0IHRpbWUgaW4gYnJva2VyIHRpbWV6b25lLCBpbiBZWVlZLU1NLUREIEhIOm1tOnNzIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW2VuZEJyb2tlclRpbWVdIHBlcmlvZCBlbmQgdGltZSBpbiBicm9rZXIgdGltZXpvbmUsIGluIFlZWVktTU0tREQgSEg6bW06c3MgZm9ybWF0XG4gICAqIEBwcm9wZXJ0eSB7UGVyaW9kfSBwZXJpb2QgcGVyaW9kIGxlbmd0aFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gaW5pdGlhbEJhbGFuY2UgYmFsYW5jZSBhdCBwZXJpb2Qgc3RhcnQgdGltZVxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gW21heERyYXdkb3duVGltZV0gdGltZSBtYXggZHJhd2Rvd24gd2FzIG9ic2VydmVkIGF0IGluIGJyb2tlciB0aW1lem9uZSxcbiAgICogaW4gWVlZWS1NTS1ERCBISDptbTpzcyBmb3JtYXRcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IFttYXhBYnNvbHV0ZURyYXdkb3duXSB0aGUgdmFsdWUgb2YgbWF4aW11bSBhYnNvbHV0ZSBkcmF3ZG93biBvYnNlcnZlZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gW21heFJlbGF0aXZlRHJhd2Rvd25dIHRoZSB2YWx1ZSBvZiBtYXhpbXVtIHJlbGF0aXZlIGRyYXdkb3duIG9ic2VydmVkXG4gICAqIEBwcm9wZXJ0eSB7U3RyaW5nfSBbbWF4UHJvZml0VGltZV0gdGltZSBtYXggcHJvZml0IHdhcyBvYnNlcnZlZCBhdCBpbiBicm9rZXIgdGltZXpvbmUsXG4gICAqIGluIFlZWVktTU0tREQgSEg6bW06c3MgZm9ybWF0XG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbbWF4QWJzb2x1dGVQcm9maXRdIHRoZSB2YWx1ZSBvZiBtYXhpbXVtIGFic29sdXRlIHByb2ZpdCBvYnNlcnZlZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gW21heFJlbGF0aXZlUHJvZml0XSB0aGUgdmFsdWUgb2YgbWF4aW11bSByZWxhdGl2ZSBwcm9maXQgb2JzZXJ2ZWRcbiAgICogQHByb3BlcnR5IHtCb29sZWFufSB0aHJlc2hvbGRFeGNlZWRlZCB0aGUgZmxhZyBpbmRpY2F0aW5nIHRoYXQgbWF4IGFsbG93ZWQgdG90YWwgZHJhd2Rvd24gd2FzIGV4Y2VlZGVkXG4gICAqIEBwcm9wZXJ0eSB7RXhjZWVkZWRUaHJlc2hvbGRUeXBlfSBleGNlZWRlZFRocmVzaG9sZFR5cGUgdHlwZSBvZiB0aGUgZXhjZWVkZWQgdGhyZXNob2xkXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbYmFsYW5jZUFkanVzdG1lbnRdIGJhbGFuY2UgYWRqdXN0bWVudCBhcHBsaWVkIHRvIGVxdWl0eSBmb3IgdHJhY2tpbmcgZHJhd2Rvd24vcHJvZml0XG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBbdHJhZGVEYXlDb3VudF0gY291bnQgb2YgZGF5cyB3aGVuIHRyYWRlcyB3ZXJlIHBlcmZvcm1lZCBkdXJpbmcgdGhlIHBlcmlvZFxuICAgKi9cblxuICAvKipcbiAgICogUmV0dXJucyBhY2NvdW50IHByb2ZpdCBhbmQgZHJhd2Rvd24gdHJhY2tpbmcgc3RhdGlzdGljcyBieSB0cmFja2VyIGlkLiBTZWVcbiAgICogaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3Mvcmlzay1tYW5hZ2VtZW50L3Jlc3RBcGkvYXBpL2dldFRyYWNraW5nU3RhdHMvXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgaWQgb2YgTWV0YUFQSSBhY2NvdW50XG4gICAqIEBwYXJhbSB7U3RyaW5nfSB0cmFja2VySWQgaWQgb2YgdGhlIHRyYWNrZXJcbiAgICogQHBhcmFtIHtTdHJpbmd9IFtzdGFydFRpbWVdIHRpbWUgdG8gc3RhcnQgbG9hZGluZyBzdGF0cyBmcm9tLCBkZWZhdWx0IGlzIGN1cnJlbnQgdGltZS4gTm90ZSB0aGF0IHN0YXRzIGlzIGxvYWRlZCBpblxuICAgKiBiYWNrd2FyZHMgZGlyZWN0aW9uXG4gICAqIEBwYXJhbSB7TnVtYmVyfSBbbGltaXRdIG51bWJlciBvZiByZWNvcmRzIHRvIGxvYWQsIGRlZmF1bHQgaXMgMVxuICAgKiBAcGFyYW0ge0Jvb2xlYW59IFtyZWFsVGltZV0gaWYgdHJ1ZSwgcmVhbC10aW1lIGRhdGEgd2lsbCBiZSByZXF1ZXN0ZWRcbiAgICogQHJldHVybiB7UHJvbWlzZTxQZXJpb2RTdGF0aXN0aWNzW10+fSBwcm9taXNlIHJlc29sdmluZyB3aXRoIHByb2ZpdCBhbmQgZHJhd2Rvd24gc3RhdGlzdGljc1xuICAgKi9cbiAgZ2V0VHJhY2tpbmdTdGF0aXN0aWNzKGFjY291bnRJZCwgdHJhY2tlcklkLCBzdGFydFRpbWUsIGxpbWl0LCByZWFsVGltZSA9IGZhbHNlKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RvbWFpbkNsaWVudC5yZXF1ZXN0QXBpKHtcbiAgICAgIHVybDogYC91c2Vycy9jdXJyZW50L2FjY291bnRzLyR7YWNjb3VudElkfS90cmFja2Vycy8ke3RyYWNrZXJJZH0vc3RhdGlzdGljc2AsXG4gICAgICBoZWFkZXJzOiB7J2F1dGgtdG9rZW4nOiB0aGlzLl9kb21haW5DbGllbnQudG9rZW4sICdhcGktdmVyc2lvbic6ICcxJ30sXG4gICAgICBwYXJhbXM6IHtzdGFydFRpbWUsIGxpbWl0LCByZWFsVGltZX0sXG4gICAgICBtZXRob2Q6ICdHRVQnXG4gICAgfSk7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIHBlcmlvZCBzdGF0aXN0aWNzIGV2ZW50IGxpc3RlbmVyXG4gICAqIEBwYXJhbSB7UGVyaW9kU3RhdGlzdGljc0xpc3RlbmVyfSBsaXN0ZW5lciBwZXJpb2Qgc3RhdGlzdGljcyBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IHRyYWNrZXJJZCB0cmFja2VyIGlkXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPFN0cmluZz59IGxpc3RlbmVyIGlkXG4gICAqL1xuICBhZGRQZXJpb2RTdGF0aXN0aWNzTGlzdGVuZXIobGlzdGVuZXIsIGFjY291bnRJZCwgdHJhY2tlcklkKSB7XG4gICAgcmV0dXJuIHRoaXMuX3BlcmlvZFN0YXRpc3RpY3NTdHJlYW1NYW5hZ2VyLmFkZFBlcmlvZFN0YXRpc3RpY3NMaXN0ZW5lcihsaXN0ZW5lciwgYWNjb3VudElkLCB0cmFja2VySWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlbW92ZXMgcGVyaW9kIHN0YXRpc3RpY3MgZXZlbnQgbGlzdGVuZXIgYnkgaWRcbiAgICogQHBhcmFtIHtTdHJpbmd9IGxpc3RlbmVySWQgbGlzdGVuZXIgaWQgXG4gICAqL1xuICByZW1vdmVQZXJpb2RTdGF0aXN0aWNzTGlzdGVuZXIobGlzdGVuZXJJZCkge1xuICAgIHRoaXMuX3BlcmlvZFN0YXRpc3RpY3NTdHJlYW1NYW5hZ2VyLnJlbW92ZVBlcmlvZFN0YXRpc3RpY3NMaXN0ZW5lcihsaXN0ZW5lcklkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBFcXVpdHkgY2hhcnQgaXRlbVxuICAgKiBAdHlwZWRlZiB7T2JqZWN0fSBFcXVpdHlDaGFydEl0ZW1cbiAgICogQHByb3BlcnR5IHtTdHJpbmd9IHN0YXJ0QnJva2VyVGltZSBzdGFydCB0aW1lIG9mIGEgY2hhcnQgaXRlbSBhcyBwZXIgYnJva2VyIHRpbWV6b25lLCBpbiBZWVlZLU1NLUREIEhIOm1tOnNzIGZvcm1hdFxuICAgKiBAcHJvcGVydHkge1N0cmluZ30gZW5kQnJva2VyVGltZSBlbmQgdGltZSBvZiBhIGNoYXJ0IGl0ZW0gYXMgcGVyIGJyb2tlciB0aW1lem9uZSwgaW4gWVlZWS1NTS1ERCBISDptbTpzcyBmb3JtYXRcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IGF2ZXJhZ2VCYWxhbmNlIGF2ZXJhZ2UgYmFsYW5jZSB2YWx1ZSBkdXJpbmcgdGhlIHBlcmlvZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gbWluQmFsYW5jZSBtaW5pbXVtIGJhbGFuY2UgdmFsdWUgZHVyaW5nIHRoZSBwZXJpb2RcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IG1heEJhbGFuY2UgbWF4aW11bSBiYWxhbmNlIHZhbHVlIGR1cmluZyB0aGUgcGVyaW9kXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBhdmVyYWdlRXF1aXR5IGF2ZXJhZ2UgZXF1aXR5IHZhbHVlIGR1cmluZyB0aGUgcGVyaW9kXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBtaW5FcXVpdHkgbWluaW11bSBlcXVpdHkgdmFsdWUgZHVyaW5nIHRoZSBwZXJpb2RcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IG1heEVxdWl0eSBtYXhpbXVtIGVxdWl0eSB2YWx1ZSBkdXJpbmcgdGhlIHBlcmlvZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gc3RhcnRCYWxhbmNlIHN0YXJ0aW5nIGJhbGFuY2UgdmFsdWUgb2JzZXJ2ZWQgZHVyaW5nIHRoZSBwZXJpb2RcbiAgICogQHByb3BlcnR5IHtOdW1iZXJ9IHN0YXJ0RXF1aXR5IHN0YXJ0aW5nIGVxdWl0eSB2YWx1ZSBvYnNlcnZlZCBkdXJpbmcgdGhlIHBlcmlvZFxuICAgKiBAcHJvcGVydHkge051bWJlcn0gbGFzdEJhbGFuY2UgbGFzdCBiYWxhbmNlIHZhbHVlIG9ic2VydmVkIGR1cmluZyB0aGUgcGVyaW9kXG4gICAqIEBwcm9wZXJ0eSB7TnVtYmVyfSBsYXN0RXF1aXR5IGxhc3QgZXF1aXR5IHZhbHVlIG9ic2VydmVkIGR1cmluZyB0aGUgcGVyaW9kXG4gICAqL1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGVxdWl0eSBjaGFydCBieSBhY2NvdW50IGlkLiBTZWVcbiAgICogaHR0cHM6Ly9tZXRhYXBpLmNsb3VkL2RvY3Mvcmlzay1tYW5hZ2VtZW50L3Jlc3RBcGkvYXBpL2dldEVxdWl0eUNoYXJ0L1xuICAgKiBAcGFyYW0ge1N0cmluZ30gYWNjb3VudElkIG1ldGFBcGkgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gW3N0YXJ0VGltZV0gc3RhcnRpbmcgYnJva2VyIHRpbWUgaW4gWVlZWS1NTS1ERCBISDptbTpzcyBmb3JtYXRcbiAgICogQHBhcmFtIHtTdHJpbmd9IFtlbmRUaW1lXSBlbmRpbmcgYnJva2VyIHRpbWUgaW4gWVlZWS1NTS1ERCBISDptbTpzcyBmb3JtYXRcbiAgICogQHBhcmFtIHtCb29sZWFufSBbcmVhbFRpbWVdIGlmIHRydWUsIHJlYWwtdGltZSBkYXRhIHdpbGwgYmUgcmVxdWVzdGVkXG4gICAqIEBwYXJhbSB7Qm9vbGVhbn0gW2ZpbGxTa2lwc10gaWYgdHJ1ZSwgc2tpcHBlZCByZWNvcmRzIHdpbGwgYmUgYXV0b21hdGljYWxseSBmaWxsZWQgYmFzZWQgb24gZXhpc3Rpbmcgb25lc1xuICAgKiBAcmV0dXJuIHtQcm9taXNlPEVxdWl0eUNoYXJ0SXRlbVtdPn0gcHJvbWlzZSByZXNvbHZpbmcgd2l0aCBlcXVpdHkgY2hhcnRcbiAgICovXG4gIGFzeW5jIGdldEVxdWl0eUNoYXJ0KGFjY291bnRJZCwgc3RhcnRUaW1lLCBlbmRUaW1lLCByZWFsVGltZSA9IGZhbHNlLCBmaWxsU2tpcHMgPSBmYWxzZSkge1xuICAgIGNvbnN0IHJlY29yZHMgPSBhd2FpdCB0aGlzLl9kb21haW5DbGllbnQucmVxdWVzdEFwaSh7XG4gICAgICB1cmw6IGAvdXNlcnMvY3VycmVudC9hY2NvdW50cy8ke2FjY291bnRJZH0vZXF1aXR5LWNoYXJ0YCxcbiAgICAgIGhlYWRlcnM6IHsnYXV0aC10b2tlbic6IHRoaXMuX2RvbWFpbkNsaWVudC50b2tlbiwgJ2FwaS12ZXJzaW9uJzogJzEnfSxcbiAgICAgIHBhcmFtczoge3N0YXJ0VGltZSwgZW5kVGltZSwgcmVhbFRpbWV9LFxuICAgICAgbWV0aG9kOiAnR0VUJ1xuICAgIH0pO1xuICAgIGlmKGZpbGxTa2lwcyl7XG4gICAgICBsZXQgaSA9IDA7XG4gICAgICB3aGlsZShpIDwgcmVjb3Jkcy5sZW5ndGggLSAxKSB7XG4gICAgICAgIGNvbnN0IHRpbWVEaWZmID0gbmV3IERhdGUocmVjb3Jkc1tpICsgMV0uc3RhcnRCcm9rZXJUaW1lKS5nZXRUaW1lKCkgLSBcbiAgICAgICAgbmV3IERhdGUocmVjb3Jkc1tpXS5zdGFydEJyb2tlclRpbWUpLmdldFRpbWUoKTtcbiAgICAgICAgXG4gICAgICAgIGlmKHRpbWVEaWZmID4gNjAgKiA2MCAqIDEwMDAgJiYgcmVjb3Jkc1tpXS5sYXN0QmFsYW5jZSAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgY29uc3QgcmVjb3JkQ29weSA9IEpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkocmVjb3Jkc1tpXSkpO1xuICAgICAgICAgIHJlY29yZENvcHkubWluRXF1aXR5ID0gcmVjb3JkQ29weS5sYXN0RXF1aXR5O1xuICAgICAgICAgIHJlY29yZENvcHkubWF4RXF1aXR5ID0gcmVjb3JkQ29weS5sYXN0RXF1aXR5O1xuICAgICAgICAgIHJlY29yZENvcHkuYXZlcmFnZUVxdWl0eSA9IHJlY29yZENvcHkubGFzdEVxdWl0eTtcbiAgICAgICAgICByZWNvcmRDb3B5Lm1pbkJhbGFuY2UgPSByZWNvcmRDb3B5Lmxhc3RCYWxhbmNlO1xuICAgICAgICAgIHJlY29yZENvcHkubWF4QmFsYW5jZSA9IHJlY29yZENvcHkubGFzdEJhbGFuY2U7XG4gICAgICAgICAgcmVjb3JkQ29weS5hdmVyYWdlQmFsYW5jZSA9IHJlY29yZENvcHkubGFzdEJhbGFuY2U7XG4gICAgICAgICAgY29uc3Qgc3RhcnRCcm9rZXJUaW1lID0gbmV3IERhdGUocmVjb3JkQ29weS5zdGFydEJyb2tlclRpbWUpO1xuICAgICAgICAgIHN0YXJ0QnJva2VyVGltZS5zZXRVVENIb3VycyhzdGFydEJyb2tlclRpbWUuZ2V0VVRDSG91cnMoKSArIDEpO1xuICAgICAgICAgIHN0YXJ0QnJva2VyVGltZS5zZXRVVENNaW51dGVzKDApO1xuICAgICAgICAgIHN0YXJ0QnJva2VyVGltZS5zZXRVVENTZWNvbmRzKDApO1xuICAgICAgICAgIHN0YXJ0QnJva2VyVGltZS5zZXRVVENNaWxsaXNlY29uZHMoMCk7XG4gICAgICAgICAgcmVjb3JkQ29weS5zdGFydEJyb2tlclRpbWUgPSBtb21lbnQoc3RhcnRCcm9rZXJUaW1lKS5mb3JtYXQoJ1lZWVktTU0tREQgSEg6bW06c3MuU1NTJyk7XG4gICAgICAgICAgc3RhcnRCcm9rZXJUaW1lLnNldFVUQ0hvdXJzKHN0YXJ0QnJva2VyVGltZS5nZXRVVENIb3VycygpICsgMSk7XG4gICAgICAgICAgc3RhcnRCcm9rZXJUaW1lLnNldFVUQ01pbGxpc2Vjb25kcygtMSk7XG4gICAgICAgICAgcmVjb3JkQ29weS5lbmRCcm9rZXJUaW1lID0gbW9tZW50KHN0YXJ0QnJva2VyVGltZSkuZm9ybWF0KCdZWVlZLU1NLUREIEhIOm1tOnNzLlNTUycpO1xuICAgICAgICAgIHJlY29yZENvcHkuYnJva2VyVGltZSA9IHJlY29yZENvcHkuZW5kQnJva2VyVGltZTtcbiAgICAgICAgICByZWNvcmRzLnNwbGljZShpICsgMSwgMCwgcmVjb3JkQ29weSk7XG4gICAgICAgIH1cbiAgICAgICAgaSsrO1xuICAgICAgfSAgXG4gICAgfVxuICAgIHJldHVybiByZWNvcmRzO1xuICB9XG5cbiAgLyoqXG4gICAqIEFkZHMgYW4gZXF1aXR5IGNoYXJ0IGV2ZW50IGxpc3RlbmVyXG4gICAqIEBwYXJhbSB7RXF1aXR5Q2hhcnRMaXN0ZW5lcn0gbGlzdGVuZXIgZXF1aXR5IGNoYXJ0IGV2ZW50IGxpc3RlbmVyXG4gICAqIEBwYXJhbSB7U3RyaW5nfSBhY2NvdW50SWQgYWNjb3VudCBpZFxuICAgKiBAcGFyYW0ge0RhdGV9IFtzdGFydFRpbWVdIGRhdGUgdG8gc3RhcnQgdHJhY2tpbmcgZnJvbVxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxzdHJpbmc+fSBsaXN0ZW5lciBpZFxuICAgKi9cbiAgYWRkRXF1aXR5Q2hhcnRMaXN0ZW5lcihsaXN0ZW5lciwgYWNjb3VudElkLCBzdGFydFRpbWUpIHtcbiAgICByZXR1cm4gdGhpcy5fZXF1aXR5Q2hhcnRTdHJlYW1NYW5hZ2VyLmFkZEVxdWl0eUNoYXJ0TGlzdGVuZXIobGlzdGVuZXIsIGFjY291bnRJZCwgc3RhcnRUaW1lKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGVxdWl0eSBjaGFydCBldmVudCBsaXN0ZW5lciBieSBpZFxuICAgKiBAcGFyYW0ge1N0cmluZ30gbGlzdGVuZXJJZCBlcXVpdHkgY2hhcnQgbGlzdGVuZXIgaWQgXG4gICAqL1xuICByZW1vdmVFcXVpdHlDaGFydExpc3RlbmVyKGxpc3RlbmVySWQpIHtcbiAgICB0aGlzLl9lcXVpdHlDaGFydFN0cmVhbU1hbmFnZXIucmVtb3ZlRXF1aXR5Q2hhcnRMaXN0ZW5lcihsaXN0ZW5lcklkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBBZGRzIGFuIGVxdWl0eSBiYWxhbmNlIGV2ZW50IGxpc3RlbmVyXG4gICAqIEBwYXJhbSB7RXF1aXR5QmFsYW5jZUxpc3RlbmVyfSBsaXN0ZW5lciBlcXVpdHkgYmFsYW5jZSBldmVudCBsaXN0ZW5lclxuICAgKiBAcGFyYW0ge3N0cmluZ30gYWNjb3VudElkIGFjY291bnQgaWRcbiAgICogQHJldHVybnMge1Byb21pc2U8c3RyaW5nPn0gbGlzdGVuZXIgaWRcbiAgICovXG4gIGFkZEVxdWl0eUJhbGFuY2VMaXN0ZW5lcihsaXN0ZW5lciwgYWNjb3VudElkKSB7XG4gICAgcmV0dXJuIHRoaXMuX2VxdWl0eUJhbGFuY2VTdHJlYW1NYW5hZ2VyLmFkZEVxdWl0eUJhbGFuY2VMaXN0ZW5lcihsaXN0ZW5lciwgYWNjb3VudElkKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZW1vdmVzIGVxdWl0eSBiYWxhbmNlIGV2ZW50IGxpc3RlbmVyIGJ5IGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBsaXN0ZW5lcklkIGVxdWl0eSBiYWxhbmNlIGxpc3RlbmVyIGlkIFxuICAgKi9cbiAgcmVtb3ZlRXF1aXR5QmFsYW5jZUxpc3RlbmVyKGxpc3RlbmVySWQpIHtcbiAgICB0aGlzLl9lcXVpdHlCYWxhbmNlU3RyZWFtTWFuYWdlci5yZW1vdmVFcXVpdHlCYWxhbmNlTGlzdGVuZXIobGlzdGVuZXJJZCk7XG4gIH1cblxufVxuIl0sIm5hbWVzIjpbIlRyYWNrZXJFdmVudExpc3RlbmVyTWFuYWdlciIsIlBlcmlvZFN0YXRpc3RpY3NTdHJlYW1NYW5hZ2VyIiwiRXF1aXR5Q2hhcnRTdHJlYW1NYW5hZ2VyIiwiRXF1aXR5QmFsYW5jZVN0cmVhbU1hbmFnZXIiLCJtb21lbnQiLCJFcXVpdHlUcmFja2luZ0NsaWVudCIsImNyZWF0ZVRyYWNrZXIiLCJhY2NvdW50SWQiLCJ0cmFja2VyIiwiX2RvbWFpbkNsaWVudCIsInJlcXVlc3RBcGkiLCJ1cmwiLCJoZWFkZXJzIiwidG9rZW4iLCJtZXRob2QiLCJkYXRhIiwiZ2V0VHJhY2tlcnMiLCJnZXRUcmFja2VyIiwiaWQiLCJnZXRUcmFja2VyQnlOYW1lIiwibmFtZSIsImVuY29kZVVSSUNvbXBvbmVudCIsInVwZGF0ZVRyYWNrZXIiLCJ1cGRhdGUiLCJkZWxldGVUcmFja2VyIiwiZ2V0VHJhY2tlckV2ZW50cyIsInN0YXJ0QnJva2VyVGltZSIsImVuZEJyb2tlclRpbWUiLCJ0cmFja2VySWQiLCJsaW1pdCIsInBhcmFtcyIsImFkZFRyYWNrZXJFdmVudExpc3RlbmVyIiwibGlzdGVuZXIiLCJzZXF1ZW5jZU51bWJlciIsIl90cmFja2VyRXZlbnRMaXN0ZW5lck1hbmFnZXIiLCJyZW1vdmVUcmFja2VyRXZlbnRMaXN0ZW5lciIsImxpc3RlbmVySWQiLCJnZXRUcmFja2luZ1N0YXRpc3RpY3MiLCJzdGFydFRpbWUiLCJyZWFsVGltZSIsImFkZFBlcmlvZFN0YXRpc3RpY3NMaXN0ZW5lciIsIl9wZXJpb2RTdGF0aXN0aWNzU3RyZWFtTWFuYWdlciIsInJlbW92ZVBlcmlvZFN0YXRpc3RpY3NMaXN0ZW5lciIsImdldEVxdWl0eUNoYXJ0IiwiZW5kVGltZSIsImZpbGxTa2lwcyIsInJlY29yZHMiLCJpIiwibGVuZ3RoIiwidGltZURpZmYiLCJEYXRlIiwiZ2V0VGltZSIsImxhc3RCYWxhbmNlIiwidW5kZWZpbmVkIiwicmVjb3JkQ29weSIsIkpTT04iLCJwYXJzZSIsInN0cmluZ2lmeSIsIm1pbkVxdWl0eSIsImxhc3RFcXVpdHkiLCJtYXhFcXVpdHkiLCJhdmVyYWdlRXF1aXR5IiwibWluQmFsYW5jZSIsIm1heEJhbGFuY2UiLCJhdmVyYWdlQmFsYW5jZSIsInNldFVUQ0hvdXJzIiwiZ2V0VVRDSG91cnMiLCJzZXRVVENNaW51dGVzIiwic2V0VVRDU2Vjb25kcyIsInNldFVUQ01pbGxpc2Vjb25kcyIsImZvcm1hdCIsImJyb2tlclRpbWUiLCJzcGxpY2UiLCJhZGRFcXVpdHlDaGFydExpc3RlbmVyIiwiX2VxdWl0eUNoYXJ0U3RyZWFtTWFuYWdlciIsInJlbW92ZUVxdWl0eUNoYXJ0TGlzdGVuZXIiLCJhZGRFcXVpdHlCYWxhbmNlTGlzdGVuZXIiLCJfZXF1aXR5QmFsYW5jZVN0cmVhbU1hbmFnZXIiLCJyZW1vdmVFcXVpdHlCYWxhbmNlTGlzdGVuZXIiLCJjb25zdHJ1Y3RvciIsImRvbWFpbkNsaWVudCIsIm1ldGFBcGkiXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFFQSxPQUFPQSxpQ0FBaUMsZ0NBQWdDO0FBQ3hFLE9BQU9DLG1DQUFtQyxrQ0FBa0M7QUFDNUUsT0FBT0MsOEJBQThCLDZCQUE2QjtBQUNsRSxPQUFPQyxnQ0FBZ0MsK0JBQStCO0FBQ3RFLE9BQU9DLFlBQVksU0FBUztBQUtiLElBQUEsQUFBTUMsdUJBQU4sTUFBTUE7SUFlbkI7Ozs7R0FJQyxHQUVEOzs7O0dBSUMsR0FFRDs7Ozs7Ozs7Ozs7Ozs7R0FjQyxHQUVEOzs7O0dBSUMsR0FFRDs7Ozs7O0dBTUMsR0FDREMsY0FBY0MsU0FBUyxFQUFFQyxPQUFPLEVBQUU7UUFDaEMsT0FBTyxJQUFJLENBQUNDLGFBQWEsQ0FBQ0MsVUFBVSxDQUFDO1lBQ25DQyxLQUFLLENBQUMsd0JBQXdCLEVBQUVKLFVBQVUsU0FBUyxDQUFDO1lBQ3BESyxTQUFTO2dCQUFDLGNBQWMsSUFBSSxDQUFDSCxhQUFhLENBQUNJLEtBQUs7Z0JBQUUsZUFBZTtZQUFHO1lBQ3BFQyxRQUFRO1lBQ1JDLE1BQU1QO1FBQ1I7SUFDRjtJQUVBOzs7O0dBSUMsR0FFRDs7Ozs7R0FLQyxHQUNEUSxZQUFZVCxTQUFTLEVBQUU7UUFDckIsT0FBTyxJQUFJLENBQUNFLGFBQWEsQ0FBQ0MsVUFBVSxDQUFDO1lBQ25DQyxLQUFLLENBQUMsd0JBQXdCLEVBQUVKLFVBQVUsU0FBUyxDQUFDO1lBQ3BESyxTQUFTO2dCQUFDLGNBQWMsSUFBSSxDQUFDSCxhQUFhLENBQUNJLEtBQUs7Z0JBQUUsZUFBZTtZQUFHO1lBQ3BFQyxRQUFRO1FBQ1Y7SUFDRjtJQUVBOzs7Ozs7R0FNQyxHQUNERyxXQUFXVixTQUFTLEVBQUVXLEVBQUUsRUFBRTtRQUN4QixPQUFP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