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)

399 lines (398 loc) 53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "default", { enumerable: true, get: function() { return ReferenceTree; } }); const _fuse = /*#__PURE__*/ _interop_require_default(require("fuse.js")); const _buffer = require("buffer"); function _interop_require_default(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined"; const isNodeServer = typeof process !== "undefined" && process.release && process.release.name === "node"; const isSSR = !isBrowser && isNodeServer; let ReferenceTree = class ReferenceTree { /** * Returns data by hash * @param {string} hash records hash * @returns {[id: string]: Object} */ // eslint-disable-next-line complexity getItemsByHash(hash) { const data = this._dataByHash[hash]; if (!data) { return null; } else if (!data.parentHash) { return data.data; } else { /** * If specified hash is not a base hash, build a chain of hashes * to the base one, apply all changes and return */ let hashChain = [ hash ]; hashChain.unshift(data.parentHash); let parentData = this._dataByHash[data.parentHash]; while(parentData.parentHash){ hashChain.unshift(parentData.parentHash); parentData = this._dataByHash[parentData.parentHash]; } const state = Object.assign({}, this._dataByHash[hashChain.shift()].data); for (let chainHash of hashChain){ const chainData = this._dataByHash[chainHash]; Object.keys(chainData.data).forEach((id)=>{ state[id] = chainData.data[id]; }); chainData.removedItemIds.forEach((id)=>{ delete state[id]; }); } return state; } } /** * Returns hash data by hash * @param {string} hash records hash * @returns {[id: string]: string} */ getHashesByHash(hash) { const data = this._dataByHash[hash]; if (!data) { return null; } else if (!data.parentHash) { return data.hashes; } else { let hashChain = [ hash ]; hashChain.unshift(data.parentHash); let parentData = this._dataByHash[data.parentHash]; while(parentData.parentHash){ hashChain.unshift(parentData.parentHash); parentData = this._dataByHash[parentData.parentHash]; } const state = Object.assign({}, this._dataByHash[hashChain.shift()].hashes); for (let chainHash of hashChain){ const chainData = this._dataByHash[chainHash]; Object.keys(chainData.hashes).forEach((id)=>{ state[id] = chainData.hashes[id]; }); chainData.removedItemIds.forEach((id)=>{ delete state[id]; }); } return state; } } /** * Creates an entry for data and returns hash * @param {string} categoryName category name * @param {string} accountType account type * @param {string} connectionId connection id * @param {string} instanceIndex instance index * @param {Object[]} items items to record * @returns {string} data hash */ recordItems(categoryName, accountType, connectionId, instanceIndex, items) { const region = instanceIndex.split(":")[0]; const hashDictionary = {}; const dataDictionary = {}; if (!items.length) { return null; } for (let item of items){ const hash = this._terminalHashManager.getItemHash(item, this._dataType, accountType, region); dataDictionary[item[this._idKey]] = item; hashDictionary[item[this._idKey]] = hash; } const dictionaryHash = this._getArrayXor(Object.values(hashDictionary)); this._updateCategoryRecord(categoryName, dictionaryHash); this.removeReference(connectionId, instanceIndex); if (this._dataByHash[dictionaryHash]) { this.addReference(dictionaryHash, connectionId, instanceIndex); } else { this._dataByHash[dictionaryHash] = { hashes: hashDictionary, data: dataDictionary, removedItemIds: [], parentHash: null, childHashes: [], lastUpdated: Date.now(), references: { [connectionId]: [ instanceIndex ] } }; } return dictionaryHash; } /** * Updates data and returns new hash * @param {string} categoryName category name * @param {string} accountType account type * @param {string} connectionId connection id * @param {string} instanceIndex instance index * @param {Object[]} items items array * @param {string[]} removedItemIds removed item ids * @param {string} parentHash parent hash * @returns {string} updated dictionary hash */ // eslint-disable-next-line complexity updateItems(categoryName, accountType, connectionId, instanceIndex, items, removedItemIds, parentHash) { if (!parentHash) { return this.recordItems(categoryName, accountType, connectionId, instanceIndex, items); } const region = instanceIndex.split(":")[0]; const hashDictionary = {}; const dataDictionary = {}; let parentData = this.getHashesByHash(parentHash); if (!parentData) { throw Error("Parent data doesn't exist"); } else { const parentHashDictionary = Object.assign({}, parentData); for (let item of items){ const hash = this._terminalHashManager.getItemHash(item, this._dataType, accountType, region); dataDictionary[item[this._idKey]] = item; hashDictionary[item[this._idKey]] = hash; parentHashDictionary[item[this._idKey]] = hash; } for (let removedId of removedItemIds){ delete parentHashDictionary[removedId]; } const dictionaryHash = this._getArrayXor(Object.values(parentHashDictionary)); this._updateCategoryRecord(categoryName, dictionaryHash); if (dictionaryHash !== parentHash) { this.removeReference(connectionId, instanceIndex); if (this._dataByHash[dictionaryHash]) { this.addReference(dictionaryHash, connectionId, instanceIndex); } else if (dictionaryHash) { this._dataByHash[dictionaryHash] = { hashes: hashDictionary, data: dataDictionary, parentHash, removedItemIds, childHashes: [], lastUpdated: Date.now(), references: { [connectionId]: [ instanceIndex ] } }; this._dataByHash[parentHash].childHashes.push(dictionaryHash); } } else { this.removeReference(connectionId, instanceIndex); this.addReference(dictionaryHash, connectionId, instanceIndex); } return dictionaryHash; } } /** * Returns the list of last used records hashes * @param {string} categoryName category name * @returns {string[]} last used records hashes */ getLastUsedHashes(categoryName) { let searchHashes = []; const getTopHashes = (category, hashAmount)=>{ const categoryData = this._hashesByCategory[category]; if (!categoryData) { return []; } else { let hashesArray = []; if (!hashAmount) { hashAmount = Infinity; } const keys = Object.keys(categoryData); keys.sort((a, b)=>b - a); for (let key of keys){ hashesArray = hashesArray.concat(categoryData[key]); if (hashesArray.length > hashAmount) { hashesArray = hashesArray.slice(0, hashAmount); break; } } return hashesArray; } }; if (this._useFuzzySearch) { let results = this._getSimilarCategoryNames(categoryName); // include all results from exact match if (results[0] === categoryName) { searchHashes = getTopHashes(categoryName); results = results.slice(1); } // include 3 latest updated hashes from close matches results.forEach((category)=>{ searchHashes = searchHashes.concat(getTopHashes(category, 3)); }); } else { searchHashes = getTopHashes(categoryName, 20); } searchHashes = searchHashes.slice(0, 20); return searchHashes; } /** * Adds a reference from a terminal state instance index to a records hash * @param {string} hash records hash * @param {string} connectionId connection id * @param {string} instanceIndex instance index */ // eslint-disable-next-line complexity addReference(hash, connectionId, instanceIndex) { if (!this._dataByHash[hash]) { throw Error(`Can't add reference - ${this._dataType} data for hash ${hash} doesn't exist`); } const references = this._dataByHash[hash].references; if (!references[connectionId]) { references[connectionId] = [ instanceIndex ]; } else { if (!references[connectionId].includes(instanceIndex)) { references[connectionId].push(instanceIndex); } } this._dataByHash[hash].lastUpdated = Date.now(); } /** * Removes a reference from a terminal state instance index to a records hash * @param {string} connectionId connection id * @param {string} instanceIndex instance index */ removeReference(connectionId, instanceIndex) { Object.keys(this._dataByHash).forEach((hash)=>{ const references = this._dataByHash[hash].references; if (references[connectionId]) { const index = references[connectionId].findIndex((instance)=>instanceIndex === instance); if (index !== -1) { references[connectionId].splice(index, 1); } if (!references[connectionId].length) { delete references[connectionId]; } } }); } _getSimilarCategoryNames(categoryName) { const categoryNameList = Object.keys(this._hashesByCategory); const fuse = new _fuse.default(categoryNameList, { threshold: 0.3 }); return fuse.search(categoryName).map((result)=>result.item); } /** * Calculates hash from array of hashes * @param {String[]} hexArray array of hashes * @returns {string} resulting hash */ _getArrayXor(hexArray) { return hexArray.length ? hexArray.reduce((a, b)=>this._getHexXor(a, b)) : null; } _getHexXor(hex1, hex2) { const buf1 = _buffer.Buffer.from(hex1, "hex"); const buf2 = _buffer.Buffer.from(hex2, "hex"); // eslint-disable-next-line no-bitwise const bufResult = buf1.map((b, i)=>b ^ buf2[i]); return isBrowser || isSSR ? Array.prototype.map.call(bufResult, (byte)=>("0" + (byte & 0xFF).toString(16)).slice(-2)).join("") : bufResult.toString("hex"); } _updateCategoryRecord(categoryName, hash) { if (!hash) { return; } const date = Date.now(); this._removeCategoryRecord(categoryName, hash); if (!this._hashesByCategory[categoryName]) { this._hashesByCategory[categoryName] = {}; } if (!this._hashesByCategory[categoryName][date]) { this._hashesByCategory[categoryName][date] = []; } this._hashesByCategory[categoryName][date].push(hash); } _removeCategoryRecord(categoryName, hash) { if (this._hashesByCategory[categoryName]) { const dates = Object.keys(this._hashesByCategory[categoryName]); dates.forEach((date)=>{ if (this._hashesByCategory[categoryName][date].includes(hash)) { this._hashesByCategory[categoryName][date] = this._hashesByCategory[categoryName][date].filter((item)=>item !== hash); if (this._hashesByCategory[categoryName][date].length === 0) { delete this._hashesByCategory[categoryName][date]; } } }); if (Object.keys(this._hashesByCategory[categoryName]).length === 0) { delete this._hashesByCategory[categoryName]; } } } _optimizeTreesJob() { const now = Date.now(); // eslint-disable-next-line complexity Object.keys(this._dataByHash).forEach((hash)=>{ const data = this._dataByHash[hash]; if (data.lastUpdated <= now - this._recordExpirationTime && !Object.keys(data.references).length && data.childHashes.length < 2) { if (data.childHashes.length === 1) { const childHash = data.childHashes[0]; const childData = this._dataByHash[childHash]; if (data.parentHash) { const combinedChanges = Object.assign({}, data.data, childData.data); const combinedHashes = Object.assign({}, data.hashes, childData.hashes); const childDataIds = Object.keys(childData.data); let combinedRemovedIds = data.removedItemIds.filter((id)=>!childDataIds.includes(id)).concat(childData.removedItemIds); childData.data = combinedChanges; childData.hashes = combinedHashes; childData.removedItemIds = combinedRemovedIds; childData.parentHash = data.parentHash; this._dataByHash[data.parentHash].childHashes.push(childHash); } else { const childItems = this.getItemsByHash(childHash); const childHashes = this.getHashesByHash(childHash); childData.data = childItems; childData.hashes = childHashes; childData.removedItemIds = []; childData.parentHash = null; } } if (data.parentHash) { const parentData = this._dataByHash[data.parentHash]; if (parentData) { parentData.childHashes = parentData.childHashes.filter((itemHash)=>hash !== itemHash); } } delete this._dataByHash[hash]; const categories = Object.keys(this._hashesByCategory); categories.forEach((category)=>{ this._removeCategoryRecord(category, hash); }); } }); } /** * Stops reference tree optimize job & clears interval */ stop() { clearInterval(this._interval); } /** * Constructs the instance of reference tree * @param {TerminalHashManager} terminalHashManager terminal hash manager * @param {string} idKey field name that contains the item id * @param {string} dataType data type * @param {boolean} [useFuzzySearch] whether to use fuzzy search on nearby categories * @param {boolean} [keepHashTrees] if set to true, unused data will not be cleared (for use in debugging) */ constructor(terminalHashManager, idKey, dataType, useFuzzySearch = false, keepHashTrees = false){ this._terminalHashManager = terminalHashManager; this._idKey = idKey; this._dataByHash = {}; this._hashesByCategory = {}; this._dataType = dataType; this._useFuzzySearch = useFuzzySearch; this._recordExpirationTime = 10 * 60 * 1000; if (!keepHashTrees) { this._optimizeTreesJob = this._optimizeTreesJob.bind(this); this._interval = setInterval(this._optimizeTreesJob, 5 * 60 * 1000); } } }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjxhbm9uPiJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgRnVzZSBmcm9tICdmdXNlLmpzJztcbmltcG9ydCB7IEJ1ZmZlciB9IGZyb20gJ2J1ZmZlcic7XG5cbmNvbnN0IGlzQnJvd3NlciA9IHR5cGVvZiB3aW5kb3cgIT09ICd1bmRlZmluZWQnICYmIHR5cGVvZiB3aW5kb3cuZG9jdW1lbnQgIT09ICd1bmRlZmluZWQnO1xuY29uc3QgaXNOb2RlU2VydmVyID0gdHlwZW9mIHByb2Nlc3MgIT09ICd1bmRlZmluZWQnICYmIHByb2Nlc3MucmVsZWFzZSAmJiBwcm9jZXNzLnJlbGVhc2UubmFtZSA9PT0gJ25vZGUnO1xuY29uc3QgaXNTU1IgPSAhaXNCcm93c2VyICYmIGlzTm9kZVNlcnZlcjtcblxuLyoqXG4gKiBDbGFzcyBmb3IgbWFuYWdpbmcgYSBkYXRhIHRyZWUgd2l0aCBoYXNoIHJlZmVyZW5jZXNcbiAqL1xuZXhwb3J0IGRlZmF1bHQgY2xhc3MgUmVmZXJlbmNlVHJlZSB7XG5cbiAgLyoqXG4gICAqIENvbnN0cnVjdHMgdGhlIGluc3RhbmNlIG9mIHJlZmVyZW5jZSB0cmVlXG4gICAqIEBwYXJhbSB7VGVybWluYWxIYXNoTWFuYWdlcn0gdGVybWluYWxIYXNoTWFuYWdlciB0ZXJtaW5hbCBoYXNoIG1hbmFnZXJcbiAgICogQHBhcmFtIHtzdHJpbmd9IGlkS2V5IGZpZWxkIG5hbWUgdGhhdCBjb250YWlucyB0aGUgaXRlbSBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gZGF0YVR5cGUgZGF0YSB0eXBlXG4gICAqIEBwYXJhbSB7Ym9vbGVhbn0gW3VzZUZ1enp5U2VhcmNoXSB3aGV0aGVyIHRvIHVzZSBmdXp6eSBzZWFyY2ggb24gbmVhcmJ5IGNhdGVnb3JpZXNcbiAgICogQHBhcmFtIHtib29sZWFufSBba2VlcEhhc2hUcmVlc10gaWYgc2V0IHRvIHRydWUsIHVudXNlZCBkYXRhIHdpbGwgbm90IGJlIGNsZWFyZWQgKGZvciB1c2UgaW4gZGVidWdnaW5nKVxuICAgKi9cbiAgY29uc3RydWN0b3IodGVybWluYWxIYXNoTWFuYWdlciwgaWRLZXksIGRhdGFUeXBlLCB1c2VGdXp6eVNlYXJjaCA9IGZhbHNlLCBrZWVwSGFzaFRyZWVzID0gZmFsc2UpIHtcbiAgICB0aGlzLl90ZXJtaW5hbEhhc2hNYW5hZ2VyID0gdGVybWluYWxIYXNoTWFuYWdlcjtcbiAgICB0aGlzLl9pZEtleSA9IGlkS2V5O1xuICAgIHRoaXMuX2RhdGFCeUhhc2ggPSB7fTtcbiAgICB0aGlzLl9oYXNoZXNCeUNhdGVnb3J5ID0ge307XG4gICAgdGhpcy5fZGF0YVR5cGUgPSBkYXRhVHlwZTtcbiAgICB0aGlzLl91c2VGdXp6eVNlYXJjaCA9IHVzZUZ1enp5U2VhcmNoO1xuICAgIHRoaXMuX3JlY29yZEV4cGlyYXRpb25UaW1lID0gMTAgKiA2MCAqIDEwMDA7XG4gICAgaWYoIWtlZXBIYXNoVHJlZXMpIHtcbiAgICAgIHRoaXMuX29wdGltaXplVHJlZXNKb2IgPSB0aGlzLl9vcHRpbWl6ZVRyZWVzSm9iLmJpbmQodGhpcyk7XG4gICAgICB0aGlzLl9pbnRlcnZhbCA9IHNldEludGVydmFsKHRoaXMuX29wdGltaXplVHJlZXNKb2IsIDUgKiA2MCAqIDEwMDApO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGRhdGEgYnkgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaGFzaCByZWNvcmRzIGhhc2hcbiAgICogQHJldHVybnMge1tpZDogc3RyaW5nXTogT2JqZWN0fVxuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgZ2V0SXRlbXNCeUhhc2goaGFzaCkge1xuICAgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2hhc2hdO1xuICAgIGlmKCFkYXRhKSB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9IGVsc2UgaWYoIWRhdGEucGFyZW50SGFzaCkge1xuICAgICAgcmV0dXJuIGRhdGEuZGF0YTtcbiAgICB9IGVsc2Uge1xuICAgICAgLyoqXG4gICAgICAgKiBJZiBzcGVjaWZpZWQgaGFzaCBpcyBub3QgYSBiYXNlIGhhc2gsIGJ1aWxkIGEgY2hhaW4gb2YgaGFzaGVzXG4gICAgICAgKiB0byB0aGUgYmFzZSBvbmUsIGFwcGx5IGFsbCBjaGFuZ2VzIGFuZCByZXR1cm5cbiAgICAgICAqL1xuICAgICAgbGV0IGhhc2hDaGFpbiA9IFtoYXNoXTtcbiAgICAgIGhhc2hDaGFpbi51bnNoaWZ0KGRhdGEucGFyZW50SGFzaCk7XG4gICAgICBsZXQgcGFyZW50RGF0YSA9IHRoaXMuX2RhdGFCeUhhc2hbZGF0YS5wYXJlbnRIYXNoXTtcbiAgICAgIHdoaWxlKHBhcmVudERhdGEucGFyZW50SGFzaCkge1xuICAgICAgICBoYXNoQ2hhaW4udW5zaGlmdChwYXJlbnREYXRhLnBhcmVudEhhc2gpO1xuICAgICAgICBwYXJlbnREYXRhID0gdGhpcy5fZGF0YUJ5SGFzaFtwYXJlbnREYXRhLnBhcmVudEhhc2hdO1xuICAgICAgfVxuICAgICAgY29uc3Qgc3RhdGUgPSBPYmplY3QuYXNzaWduKHt9LCB0aGlzLl9kYXRhQnlIYXNoW2hhc2hDaGFpbi5zaGlmdCgpXS5kYXRhKTtcbiAgICAgIGZvcihsZXQgY2hhaW5IYXNoIG9mIGhhc2hDaGFpbikge1xuICAgICAgICBjb25zdCBjaGFpbkRhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2NoYWluSGFzaF07XG4gICAgICAgIE9iamVjdC5rZXlzKGNoYWluRGF0YS5kYXRhKS5mb3JFYWNoKGlkID0+IHtcbiAgICAgICAgICBzdGF0ZVtpZF0gPSBjaGFpbkRhdGEuZGF0YVtpZF07XG4gICAgICAgIH0pO1xuICAgICAgICBjaGFpbkRhdGEucmVtb3ZlZEl0ZW1JZHMuZm9yRWFjaChpZCA9PiB7XG4gICAgICAgICAgZGVsZXRlIHN0YXRlW2lkXTtcbiAgICAgICAgfSk7XG4gICAgICB9XG4gICAgICByZXR1cm4gc3RhdGU7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFJldHVybnMgaGFzaCBkYXRhIGJ5IGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGhhc2ggcmVjb3JkcyBoYXNoXG4gICAqIEByZXR1cm5zIHtbaWQ6IHN0cmluZ106IHN0cmluZ31cbiAgICovXG4gIGdldEhhc2hlc0J5SGFzaChoYXNoKSB7XG4gICAgY29uc3QgZGF0YSA9IHRoaXMuX2RhdGFCeUhhc2hbaGFzaF07XG4gICAgaWYoIWRhdGEpIHtcbiAgICAgIHJldHVybiBudWxsO1xuICAgIH0gZWxzZSBpZighZGF0YS5wYXJlbnRIYXNoKSB7XG4gICAgICByZXR1cm4gZGF0YS5oYXNoZXM7XG4gICAgfSBlbHNlIHtcbiAgICAgIGxldCBoYXNoQ2hhaW4gPSBbaGFzaF07XG4gICAgICBoYXNoQ2hhaW4udW5zaGlmdChkYXRhLnBhcmVudEhhc2gpO1xuICAgICAgbGV0IHBhcmVudERhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2RhdGEucGFyZW50SGFzaF07XG4gICAgICB3aGlsZShwYXJlbnREYXRhLnBhcmVudEhhc2gpIHtcbiAgICAgICAgaGFzaENoYWluLnVuc2hpZnQocGFyZW50RGF0YS5wYXJlbnRIYXNoKTtcbiAgICAgICAgcGFyZW50RGF0YSA9IHRoaXMuX2RhdGFCeUhhc2hbcGFyZW50RGF0YS5wYXJlbnRIYXNoXTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IHN0YXRlID0gT2JqZWN0LmFzc2lnbih7fSwgdGhpcy5fZGF0YUJ5SGFzaFtoYXNoQ2hhaW4uc2hpZnQoKV0uaGFzaGVzKTtcbiAgICAgIGZvcihsZXQgY2hhaW5IYXNoIG9mIGhhc2hDaGFpbikge1xuICAgICAgICBjb25zdCBjaGFpbkRhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2NoYWluSGFzaF07XG4gICAgICAgIE9iamVjdC5rZXlzKGNoYWluRGF0YS5oYXNoZXMpLmZvckVhY2goaWQgPT4ge1xuICAgICAgICAgIHN0YXRlW2lkXSA9IGNoYWluRGF0YS5oYXNoZXNbaWRdO1xuICAgICAgICB9KTtcbiAgICAgICAgY2hhaW5EYXRhLnJlbW92ZWRJdGVtSWRzLmZvckVhY2goaWQgPT4ge1xuICAgICAgICAgIGRlbGV0ZSBzdGF0ZVtpZF07XG4gICAgICAgIH0pO1xuICAgICAgfVxuICAgICAgcmV0dXJuIHN0YXRlO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFuIGVudHJ5IGZvciBkYXRhIGFuZCByZXR1cm5zIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNhdGVnb3J5TmFtZSBjYXRlZ29yeSBuYW1lXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50VHlwZSBhY2NvdW50IHR5cGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7T2JqZWN0W119IGl0ZW1zIGl0ZW1zIHRvIHJlY29yZFxuICAgKiBAcmV0dXJucyB7c3RyaW5nfSBkYXRhIGhhc2hcbiAgICovXG4gIHJlY29yZEl0ZW1zKGNhdGVnb3J5TmFtZSwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCwgaXRlbXMpIHtcbiAgICBjb25zdCByZWdpb24gPSBpbnN0YW5jZUluZGV4LnNwbGl0KCc6JylbMF07XG4gICAgY29uc3QgaGFzaERpY3Rpb25hcnkgPSB7fTtcbiAgICBjb25zdCBkYXRhRGljdGlvbmFyeSA9IHt9O1xuICAgIGlmKCFpdGVtcy5sZW5ndGgpICB7XG4gICAgICByZXR1cm4gbnVsbDtcbiAgICB9XG5cbiAgICBmb3IobGV0IGl0ZW0gb2YgaXRlbXMpIHtcbiAgICAgIGNvbnN0IGhhc2ggPSB0aGlzLl90ZXJtaW5hbEhhc2hNYW5hZ2VyLmdldEl0ZW1IYXNoKGl0ZW0sIHRoaXMuX2RhdGFUeXBlLCBhY2NvdW50VHlwZSwgcmVnaW9uKTtcbiAgICAgIGRhdGFEaWN0aW9uYXJ5W2l0ZW1bdGhpcy5faWRLZXldXSA9IGl0ZW07XG4gICAgICBoYXNoRGljdGlvbmFyeVtpdGVtW3RoaXMuX2lkS2V5XV0gPSBoYXNoO1xuICAgIH1cbiAgICBjb25zdCBkaWN0aW9uYXJ5SGFzaCA9IHRoaXMuX2dldEFycmF5WG9yKE9iamVjdC52YWx1ZXMoaGFzaERpY3Rpb25hcnkpKTtcbiAgICB0aGlzLl91cGRhdGVDYXRlZ29yeVJlY29yZChjYXRlZ29yeU5hbWUsIGRpY3Rpb25hcnlIYXNoKTtcbiAgICB0aGlzLnJlbW92ZVJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICAgIGlmKHRoaXMuX2RhdGFCeUhhc2hbZGljdGlvbmFyeUhhc2hdKSB7XG4gICAgICB0aGlzLmFkZFJlZmVyZW5jZShkaWN0aW9uYXJ5SGFzaCwgY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4KTtcbiAgICB9IGVsc2Uge1xuICAgICAgdGhpcy5fZGF0YUJ5SGFzaFtkaWN0aW9uYXJ5SGFzaF0gPSB7XG4gICAgICAgIGhhc2hlczogaGFzaERpY3Rpb25hcnksXG4gICAgICAgIGRhdGE6IGRhdGFEaWN0aW9uYXJ5LFxuICAgICAgICByZW1vdmVkSXRlbUlkczogW10sXG4gICAgICAgIHBhcmVudEhhc2g6IG51bGwsXG4gICAgICAgIGNoaWxkSGFzaGVzOiBbXSxcbiAgICAgICAgbGFzdFVwZGF0ZWQ6IERhdGUubm93KCksXG4gICAgICAgIHJlZmVyZW5jZXM6IHtbY29ubmVjdGlvbklkXTogW2luc3RhbmNlSW5kZXhdfVxuICAgICAgfTtcbiAgICB9XG4gICAgcmV0dXJuIGRpY3Rpb25hcnlIYXNoO1xuICB9XG5cbiAgLyoqXG4gICAqIFVwZGF0ZXMgZGF0YSBhbmQgcmV0dXJucyBuZXcgaGFzaFxuICAgKiBAcGFyYW0ge3N0cmluZ30gY2F0ZWdvcnlOYW1lIGNhdGVnb3J5IG5hbWUgXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhY2NvdW50VHlwZSBhY2NvdW50IHR5cGVcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqIEBwYXJhbSB7T2JqZWN0W119IGl0ZW1zIGl0ZW1zIGFycmF5XG4gICAqIEBwYXJhbSB7c3RyaW5nW119IHJlbW92ZWRJdGVtSWRzIHJlbW92ZWQgaXRlbSBpZHNcbiAgICogQHBhcmFtIHtzdHJpbmd9IHBhcmVudEhhc2ggcGFyZW50IGhhc2hcbiAgICogQHJldHVybnMge3N0cmluZ30gdXBkYXRlZCBkaWN0aW9uYXJ5IGhhc2hcbiAgICovXG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gIHVwZGF0ZUl0ZW1zKGNhdGVnb3J5TmFtZSwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCwgaXRlbXMsIHJlbW92ZWRJdGVtSWRzLCBwYXJlbnRIYXNoKSB7XG4gICAgaWYoIXBhcmVudEhhc2gpIHtcbiAgICAgIHJldHVybiB0aGlzLnJlY29yZEl0ZW1zKGNhdGVnb3J5TmFtZSwgYWNjb3VudFR5cGUsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCwgaXRlbXMpO1xuICAgIH1cbiAgICBjb25zdCByZWdpb24gPSBpbnN0YW5jZUluZGV4LnNwbGl0KCc6JylbMF07XG4gICAgY29uc3QgaGFzaERpY3Rpb25hcnkgPSB7fTtcbiAgICBjb25zdCBkYXRhRGljdGlvbmFyeSA9IHt9O1xuICAgIGxldCBwYXJlbnREYXRhID0gdGhpcy5nZXRIYXNoZXNCeUhhc2gocGFyZW50SGFzaCk7XG4gICAgaWYoIXBhcmVudERhdGEpIHtcbiAgICAgIHRocm93IEVycm9yKCdQYXJlbnQgZGF0YSBkb2VzblxcJ3QgZXhpc3QnKTtcbiAgICB9IGVsc2Uge1xuICAgICAgY29uc3QgcGFyZW50SGFzaERpY3Rpb25hcnkgPSBPYmplY3QuYXNzaWduKHt9LCBwYXJlbnREYXRhKTtcbiAgICAgIGZvcihsZXQgaXRlbSBvZiBpdGVtcykge1xuICAgICAgICBjb25zdCBoYXNoID0gdGhpcy5fdGVybWluYWxIYXNoTWFuYWdlci5nZXRJdGVtSGFzaChpdGVtLCB0aGlzLl9kYXRhVHlwZSwgYWNjb3VudFR5cGUsIHJlZ2lvbik7XG4gICAgICAgIGRhdGFEaWN0aW9uYXJ5W2l0ZW1bdGhpcy5faWRLZXldXSA9IGl0ZW07XG4gICAgICAgIGhhc2hEaWN0aW9uYXJ5W2l0ZW1bdGhpcy5faWRLZXldXSA9IGhhc2g7XG4gICAgICAgIHBhcmVudEhhc2hEaWN0aW9uYXJ5W2l0ZW1bdGhpcy5faWRLZXldXSA9IGhhc2g7XG4gICAgICB9XG4gICAgICBmb3IobGV0IHJlbW92ZWRJZCBvZiByZW1vdmVkSXRlbUlkcykge1xuICAgICAgICBkZWxldGUgcGFyZW50SGFzaERpY3Rpb25hcnlbcmVtb3ZlZElkXTtcbiAgICAgIH1cbiAgICAgIGNvbnN0IGRpY3Rpb25hcnlIYXNoID0gdGhpcy5fZ2V0QXJyYXlYb3IoT2JqZWN0LnZhbHVlcyhwYXJlbnRIYXNoRGljdGlvbmFyeSkpO1xuICAgICAgdGhpcy5fdXBkYXRlQ2F0ZWdvcnlSZWNvcmQoY2F0ZWdvcnlOYW1lLCBkaWN0aW9uYXJ5SGFzaCk7XG4gICAgICBpZihkaWN0aW9uYXJ5SGFzaCAhPT0gcGFyZW50SGFzaCkge1xuICAgICAgICB0aGlzLnJlbW92ZVJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICAgICAgICBpZih0aGlzLl9kYXRhQnlIYXNoW2RpY3Rpb25hcnlIYXNoXSkge1xuICAgICAgICAgIHRoaXMuYWRkUmVmZXJlbmNlKGRpY3Rpb25hcnlIYXNoLCBjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICAgICAgICB9IGVsc2UgaWYoZGljdGlvbmFyeUhhc2gpIHtcbiAgICAgICAgICB0aGlzLl9kYXRhQnlIYXNoW2RpY3Rpb25hcnlIYXNoXSA9IHtcbiAgICAgICAgICAgIGhhc2hlczogaGFzaERpY3Rpb25hcnksXG4gICAgICAgICAgICBkYXRhOiBkYXRhRGljdGlvbmFyeSxcbiAgICAgICAgICAgIHBhcmVudEhhc2gsXG4gICAgICAgICAgICByZW1vdmVkSXRlbUlkcyxcbiAgICAgICAgICAgIGNoaWxkSGFzaGVzOiBbXSxcbiAgICAgICAgICAgIGxhc3RVcGRhdGVkOiBEYXRlLm5vdygpLFxuICAgICAgICAgICAgcmVmZXJlbmNlczoge1tjb25uZWN0aW9uSWRdOiBbaW5zdGFuY2VJbmRleF19XG4gICAgICAgICAgfTtcbiAgICAgICAgICB0aGlzLl9kYXRhQnlIYXNoW3BhcmVudEhhc2hdLmNoaWxkSGFzaGVzLnB1c2goZGljdGlvbmFyeUhhc2gpO1xuICAgICAgICB9XG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLnJlbW92ZVJlZmVyZW5jZShjb25uZWN0aW9uSWQsIGluc3RhbmNlSW5kZXgpO1xuICAgICAgICB0aGlzLmFkZFJlZmVyZW5jZShkaWN0aW9uYXJ5SGFzaCwgY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4KTtcbiAgICAgIH1cbiAgICAgIHJldHVybiBkaWN0aW9uYXJ5SGFzaDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyB0aGUgbGlzdCBvZiBsYXN0IHVzZWQgcmVjb3JkcyBoYXNoZXNcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNhdGVnb3J5TmFtZSBjYXRlZ29yeSBuYW1lXG4gICAqIEByZXR1cm5zIHtzdHJpbmdbXX0gbGFzdCB1c2VkIHJlY29yZHMgaGFzaGVzXG4gICAqL1xuICBnZXRMYXN0VXNlZEhhc2hlcyhjYXRlZ29yeU5hbWUpIHtcbiAgICBsZXQgc2VhcmNoSGFzaGVzID0gW107XG4gICAgY29uc3QgZ2V0VG9wSGFzaGVzID0gKGNhdGVnb3J5LCBoYXNoQW1vdW50KSA9PiB7XG4gICAgICBjb25zdCBjYXRlZ29yeURhdGEgPSB0aGlzLl9oYXNoZXNCeUNhdGVnb3J5W2NhdGVnb3J5XTtcbiAgICAgIGlmKCFjYXRlZ29yeURhdGEpIHtcbiAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbGV0IGhhc2hlc0FycmF5ID0gW107XG4gICAgICAgIGlmKCFoYXNoQW1vdW50KSB7XG4gICAgICAgICAgaGFzaEFtb3VudCA9IEluZmluaXR5O1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGtleXMgPSBPYmplY3Qua2V5cyhjYXRlZ29yeURhdGEpO1xuICAgICAgICBrZXlzLnNvcnQoKGEsIGIpID0+IGIgLSBhKTtcbiAgICAgICAgZm9yKGxldCBrZXkgb2Yga2V5cykge1xuICAgICAgICAgIGhhc2hlc0FycmF5ID0gaGFzaGVzQXJyYXkuY29uY2F0KGNhdGVnb3J5RGF0YVtrZXldKTtcbiAgICAgICAgICBpZihoYXNoZXNBcnJheS5sZW5ndGggPiBoYXNoQW1vdW50KSB7XG4gICAgICAgICAgICBoYXNoZXNBcnJheSA9IGhhc2hlc0FycmF5LnNsaWNlKDAsIGhhc2hBbW91bnQpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHJldHVybiBoYXNoZXNBcnJheTtcbiAgICAgIH1cbiAgICB9O1xuXG4gICAgaWYodGhpcy5fdXNlRnV6enlTZWFyY2gpIHtcbiAgICAgIGxldCByZXN1bHRzID0gdGhpcy5fZ2V0U2ltaWxhckNhdGVnb3J5TmFtZXMoY2F0ZWdvcnlOYW1lKTtcbiAgICAgIC8vIGluY2x1ZGUgYWxsIHJlc3VsdHMgZnJvbSBleGFjdCBtYXRjaFxuICAgICAgaWYocmVzdWx0c1swXSA9PT0gY2F0ZWdvcnlOYW1lKSB7XG4gICAgICAgIHNlYXJjaEhhc2hlcyA9IGdldFRvcEhhc2hlcyhjYXRlZ29yeU5hbWUpO1xuICAgICAgICByZXN1bHRzID0gcmVzdWx0cy5zbGljZSgxKTtcbiAgICAgIH1cbiAgICAgIC8vIGluY2x1ZGUgMyBsYXRlc3QgdXBkYXRlZCBoYXNoZXMgZnJvbSBjbG9zZSBtYXRjaGVzXG4gICAgICByZXN1bHRzLmZvckVhY2goY2F0ZWdvcnkgPT4ge1xuICAgICAgICBzZWFyY2hIYXNoZXMgPSBzZWFyY2hIYXNoZXMuY29uY2F0KGdldFRvcEhhc2hlcyhjYXRlZ29yeSwgMykpO1xuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIHNlYXJjaEhhc2hlcyA9IGdldFRvcEhhc2hlcyhjYXRlZ29yeU5hbWUsIDIwKTtcbiAgICB9XG4gICAgICBcbiAgICBzZWFyY2hIYXNoZXMgPSBzZWFyY2hIYXNoZXMuc2xpY2UoMCwgMjApO1xuICAgIHJldHVybiBzZWFyY2hIYXNoZXM7XG4gIH1cblxuICAvKipcbiAgICogQWRkcyBhIHJlZmVyZW5jZSBmcm9tIGEgdGVybWluYWwgc3RhdGUgaW5zdGFuY2UgaW5kZXggdG8gYSByZWNvcmRzIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGhhc2ggcmVjb3JkcyBoYXNoXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBjb25uZWN0aW9uSWQgY29ubmVjdGlvbiBpZFxuICAgKiBAcGFyYW0ge3N0cmluZ30gaW5zdGFuY2VJbmRleCBpbnN0YW5jZSBpbmRleFxuICAgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGNvbXBsZXhpdHlcbiAgYWRkUmVmZXJlbmNlKGhhc2gsIGNvbm5lY3Rpb25JZCwgaW5zdGFuY2VJbmRleCkge1xuICAgIGlmICghdGhpcy5fZGF0YUJ5SGFzaFtoYXNoXSkge1xuICAgICAgdGhyb3cgRXJyb3IoYENhbid0IGFkZCByZWZlcmVuY2UgLSAke3RoaXMuX2RhdGFUeXBlfSBkYXRhIGZvciBoYXNoICR7aGFzaH0gZG9lc24ndCBleGlzdGApO1xuICAgIH1cbiAgICBjb25zdCByZWZlcmVuY2VzID0gdGhpcy5fZGF0YUJ5SGFzaFtoYXNoXS5yZWZlcmVuY2VzO1xuICAgIGlmKCFyZWZlcmVuY2VzW2Nvbm5lY3Rpb25JZF0pIHtcbiAgICAgIHJlZmVyZW5jZXNbY29ubmVjdGlvbklkXSA9IFtpbnN0YW5jZUluZGV4XTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYoIXJlZmVyZW5jZXNbY29ubmVjdGlvbklkXS5pbmNsdWRlcyhpbnN0YW5jZUluZGV4KSkge1xuICAgICAgICByZWZlcmVuY2VzW2Nvbm5lY3Rpb25JZF0ucHVzaChpbnN0YW5jZUluZGV4KTtcbiAgICAgIH1cbiAgICB9XG4gICAgdGhpcy5fZGF0YUJ5SGFzaFtoYXNoXS5sYXN0VXBkYXRlZCA9IERhdGUubm93KCk7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIHJlZmVyZW5jZSBmcm9tIGEgdGVybWluYWwgc3RhdGUgaW5zdGFuY2UgaW5kZXggdG8gYSByZWNvcmRzIGhhc2hcbiAgICogQHBhcmFtIHtzdHJpbmd9IGNvbm5lY3Rpb25JZCBjb25uZWN0aW9uIGlkXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBpbnN0YW5jZUluZGV4IGluc3RhbmNlIGluZGV4XG4gICAqL1xuICByZW1vdmVSZWZlcmVuY2UoY29ubmVjdGlvbklkLCBpbnN0YW5jZUluZGV4KSB7XG4gICAgT2JqZWN0LmtleXModGhpcy5fZGF0YUJ5SGFzaCkuZm9yRWFjaChoYXNoID0+IHtcbiAgICAgIGNvbnN0IHJlZmVyZW5jZXMgPSB0aGlzLl9kYXRhQnlIYXNoW2hhc2hdLnJlZmVyZW5jZXM7XG4gICAgICBpZihyZWZlcmVuY2VzW2Nvbm5lY3Rpb25JZF0pIHtcbiAgICAgICAgY29uc3QgaW5kZXggPSByZWZlcmVuY2VzW2Nvbm5lY3Rpb25JZF0uZmluZEluZGV4KGluc3RhbmNlID0+IGluc3RhbmNlSW5kZXggPT09IGluc3RhbmNlKTtcbiAgICAgICAgaWYoaW5kZXggIT09IC0xKSB7XG4gICAgICAgICAgcmVmZXJlbmNlc1tjb25uZWN0aW9uSWRdLnNwbGljZShpbmRleCwgMSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYoIXJlZmVyZW5jZXNbY29ubmVjdGlvbklkXS5sZW5ndGgpIHtcbiAgICAgICAgICBkZWxldGUgcmVmZXJlbmNlc1tjb25uZWN0aW9uSWRdO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfSk7XG4gIH1cblxuICBfZ2V0U2ltaWxhckNhdGVnb3J5TmFtZXMoY2F0ZWdvcnlOYW1lKSB7XG4gICAgY29uc3QgY2F0ZWdvcnlOYW1lTGlzdCA9IE9iamVjdC5rZXlzKHRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnkpO1xuICAgIGNvbnN0IGZ1c2UgPSBuZXcgRnVzZShjYXRlZ29yeU5hbWVMaXN0LCB7XG4gICAgICB0aHJlc2hvbGQ6IDAuM1xuICAgIH0pO1xuICAgIHJldHVybiBmdXNlLnNlYXJjaChjYXRlZ29yeU5hbWUpLm1hcChyZXN1bHQgPT4gcmVzdWx0Lml0ZW0pO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgaGFzaCBmcm9tIGFycmF5IG9mIGhhc2hlc1xuICAgKiBAcGFyYW0ge1N0cmluZ1tdfSBoZXhBcnJheSBhcnJheSBvZiBoYXNoZXNcbiAgICogQHJldHVybnMge3N0cmluZ30gcmVzdWx0aW5nIGhhc2hcbiAgICovXG4gIF9nZXRBcnJheVhvcihoZXhBcnJheSkge1xuICAgIHJldHVybiBoZXhBcnJheS5sZW5ndGggPyBoZXhBcnJheS5yZWR1Y2UoKGEsIGIpID0+IHRoaXMuX2dldEhleFhvcihhLCBiKSkgOiBudWxsO1xuICB9XG5cbiAgX2dldEhleFhvcihoZXgxLCBoZXgyKSB7XG4gICAgY29uc3QgYnVmMSA9IEJ1ZmZlci5mcm9tKGhleDEsICdoZXgnKTtcbiAgICBjb25zdCBidWYyID0gQnVmZmVyLmZyb20oaGV4MiwgJ2hleCcpO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1iaXR3aXNlXG4gICAgY29uc3QgYnVmUmVzdWx0ID0gYnVmMS5tYXAoKGIsIGkpID0+IGIgXiBidWYyW2ldKTtcbiAgICByZXR1cm4gKGlzQnJvd3NlciB8fCBpc1NTUilcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1iaXR3aXNlXG4gICAgICA/IEFycmF5LnByb3RvdHlwZS5tYXAuY2FsbChidWZSZXN1bHQsIGJ5dGUgPT4gKCcwJyArIChieXRlICYgMHhGRikudG9TdHJpbmcoMTYpKS5zbGljZSgtMikpLmpvaW4oJycpXG4gICAgICA6IGJ1ZlJlc3VsdC50b1N0cmluZygnaGV4Jyk7XG4gIH1cblxuICBfdXBkYXRlQ2F0ZWdvcnlSZWNvcmQoY2F0ZWdvcnlOYW1lLCBoYXNoKSB7XG4gICAgaWYoIWhhc2gpIHtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgZGF0ZSA9IERhdGUubm93KCk7XG4gICAgdGhpcy5fcmVtb3ZlQ2F0ZWdvcnlSZWNvcmQoY2F0ZWdvcnlOYW1lLCBoYXNoKTtcbiAgICBpZighdGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdKSB7XG4gICAgICB0aGlzLl9oYXNoZXNCeUNhdGVnb3J5W2NhdGVnb3J5TmFtZV0gPSB7fTtcbiAgICB9XG4gICAgaWYoIXRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnlbY2F0ZWdvcnlOYW1lXVtkYXRlXSkge1xuICAgICAgdGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdW2RhdGVdID0gW107XG4gICAgfVxuICAgIHRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnlbY2F0ZWdvcnlOYW1lXVtkYXRlXS5wdXNoKGhhc2gpO1xuICB9XG5cbiAgX3JlbW92ZUNhdGVnb3J5UmVjb3JkKGNhdGVnb3J5TmFtZSwgaGFzaCkge1xuICAgIGlmKHRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnlbY2F0ZWdvcnlOYW1lXSkge1xuICAgICAgY29uc3QgZGF0ZXMgPSBPYmplY3Qua2V5cyh0aGlzLl9oYXNoZXNCeUNhdGVnb3J5W2NhdGVnb3J5TmFtZV0pO1xuICAgICAgZGF0ZXMuZm9yRWFjaChkYXRlID0+IHtcbiAgICAgICAgaWYodGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdW2RhdGVdLmluY2x1ZGVzKGhhc2gpKSB7XG4gICAgICAgICAgdGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdW2RhdGVdID0gXG4gICAgICAgICAgICB0aGlzLl9oYXNoZXNCeUNhdGVnb3J5W2NhdGVnb3J5TmFtZV1bZGF0ZV0uZmlsdGVyKGl0ZW0gPT4gaXRlbSAhPT0gaGFzaCk7XG4gICAgICAgICAgaWYodGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdW2RhdGVdLmxlbmd0aCA9PT0gMCkge1xuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnlbY2F0ZWdvcnlOYW1lXVtkYXRlXTtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH0pO1xuICAgICAgaWYoT2JqZWN0LmtleXModGhpcy5faGFzaGVzQnlDYXRlZ29yeVtjYXRlZ29yeU5hbWVdKS5sZW5ndGggPT09IDApIHtcbiAgICAgICAgZGVsZXRlIHRoaXMuX2hhc2hlc0J5Q2F0ZWdvcnlbY2F0ZWdvcnlOYW1lXTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBfb3B0aW1pemVUcmVlc0pvYigpIHtcbiAgICBjb25zdCBub3cgPSBEYXRlLm5vdygpO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBjb21wbGV4aXR5XG4gICAgT2JqZWN0LmtleXModGhpcy5fZGF0YUJ5SGFzaCkuZm9yRWFjaChoYXNoID0+IHtcbiAgICAgIGNvbnN0IGRhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2hhc2hdO1xuICAgICAgaWYoZGF0YS5sYXN0VXBkYXRlZCA8PSBub3cgLSB0aGlzLl9yZWNvcmRFeHBpcmF0aW9uVGltZSAmJiAhT2JqZWN0LmtleXMoZGF0YS5yZWZlcmVuY2VzKS5sZW5ndGggJiZcbiAgICAgICAgICBkYXRhLmNoaWxkSGFzaGVzLmxlbmd0aCA8IDIpIHtcbiAgICAgICAgaWYgKGRhdGEuY2hpbGRIYXNoZXMubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgY29uc3QgY2hpbGRIYXNoID0gZGF0YS5jaGlsZEhhc2hlc1swXTtcbiAgICAgICAgICBjb25zdCBjaGlsZERhdGEgPSB0aGlzLl9kYXRhQnlIYXNoW2NoaWxkSGFzaF07XG4gICAgICAgICAgaWYoZGF0YS5wYXJlbnRIYXNoKSB7XG4gICAgICAgICAgICBjb25zdCBjb21iaW5lZENoYW5nZXMgPSBPYmplY3QuYXNzaWduKHt9LCBkYXRhLmRhdGEsIGNoaWxkRGF0YS5kYXRhKTtcbiAgICAgICAgICAgIGNvbnN0IGNvbWJpbmVkSGFzaGVzID0gT2JqZWN0LmFzc2lnbih7fSwgZGF0YS5oYXNoZXMsIGNoaWxkRGF0YS5oYXNoZXMpO1xuICAgICAgICAgICAgY29uc3QgY2hpbGREYXRhSWRzID0gT2JqZWN0LmtleXMoY2hpbGREYXRhLmRhdGEpO1xuICAgICAgICAgICAgbGV0IGNvbWJpbmVkUmVtb3ZlZElkcyA9IGRhdGEucmVtb3ZlZEl0ZW1JZHMuZmlsdGVyKGlkID0+ICFjaGlsZERhdGFJZHMuaW5jbHVkZXMoaWQpKVxuICAgICAgICAgICAgICAuY29uY2F0KGNoaWxkRGF0YS5yZW1vdmVkSXRlbUlkcyk7XG4gICAgICAgICAgICBjaGlsZERhdGEuZGF0YSA9IGNvbWJpbmVkQ2hhbmdlcztcbiAgICAgICAgICAgIGNoaWxkRGF0YS5oYXNoZXMgPSBjb21iaW5lZEhhc2hlcztcbiAgICAgICAgICAgIGNoaWxkRGF0YS5yZW1vdmVkSXRlbUlkcyA9IGNvbWJpbmVkUmVtb3ZlZElkcztcbiAgICAgICAgICAgIGNoaWxkRGF0YS5wYXJlbnRIYXNoID0gZGF0YS5wYXJlbnRIYXNoO1xuICAgICAgICAgICAgdGhpcy5fZGF0YUJ5SGFzaFtkYXRhLnBhcmVudEhhc2hdLmNoaWxkSGFzaGVzLnB1c2goY2hpbGRIYXNoKTtcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgY2hpbGRJdGVtcyA9IHRoaXMuZ2V0SXRlbXNCeUhhc2goY2hpbGRIYXNoKTtcbiAgICAgICAgICAgIGNvbnN0IGNoaWxkSGFzaGVzID0gdGhpcy5nZXRIYXNoZXNCeUhhc2goY2hpbGRIYXNoKTtcbiAgICAgICAgICAgIGNoaWxkRGF0YS5kYXRhID0gY2hpbGRJdGVtcztcbiAgICAgICAgICAgIGNoaWxkRGF0YS5oYXNoZXMgPSBjaGlsZEhhc2hlcztcbiAgICAgICAgICAgIGNoaWxkRGF0YS5yZW1vdmVkSXRlbUlkcyA9IFtdO1xuICAgICAgICAgICAgY2hpbGREYXRhLnBhcmVudEhhc2ggPSBudWxsO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZihkYXRhLnBhcmVudEhhc2gpIHtcbiAgICAgICAgICBjb25zdCBwYXJlbnREYXRhID0gdGhpcy5fZGF0YUJ5SGFzaFtkYXRhLnBhcmVudEhhc2hdO1xuICAgICAgICAgIGlmKHBhcmVudERhdGEpIHtcbiAgICAgICAgICAgIHBhcmVudERhdGEuY2hpbGRIYXNoZXMgPSBwYXJlbnREYXRhLmNoaWxkSGFzaGVzLmZpbHRlcihpdGVtSGFzaCA9PiBoYXNoICE9PSBpdGVtSGFzaCk7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGRlbGV0ZSB0aGlzLl9kYXRhQnlIYXNoW2hhc2hdO1xuICAgICAgICBjb25zdCBjYXRlZ29yaWVzID0gT2JqZWN0LmtleXModGhpcy5faGFzaGVzQnlDYXRlZ29yeSk7XG4gICAgICAgIGNhdGVnb3JpZXMuZm9yRWFjaChjYXRlZ29yeSA9PiB7XG4gICAgICAgICAgdGhpcy5fcmVtb3ZlQ2F0ZWdvcnlSZWNvcmQoY2F0ZWdvcnksIGhhc2gpO1xuICAgICAgICB9KTtcbiAgICAgIH1cbiAgICB9KTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTdG9wcyByZWZlcmVuY2UgdHJlZSBvcHRpbWl6ZSBqb2IgJiBjbGVhcnMgaW50ZXJ2YWxcbiAgICovXG4gIHN0b3AoKSB7XG4gICAgY2xlYXJJbnRlcnZhbCh0aGlzLl9pbnRlcnZhbCk7XG4gIH1cbn1cbiJdLCJuYW1lcyI6WyJSZWZlcmVuY2VUcmVlIiwiaXNCcm93c2VyIiwid2luZG93IiwiZG9jdW1lbnQiLCJpc05vZGVTZXJ2ZXIiLCJwcm9jZXNzIiwicmVsZWFzZSIsIm5hbWUiLCJpc1NTUiIsImdldEl0ZW1zQnlIYXNoIiwiaGFzaCIsImRhdGEiLCJfZGF0YUJ5SGFzaCIsInBhcmVudEhhc2giLCJoYXNoQ2hhaW4iLCJ1bnNoaWZ0IiwicGFyZW50RGF0YSIsInN0YXRlIiwiT2JqZWN0IiwiYXNzaWduIiwic2hpZnQiLCJjaGFpbkhhc2giLCJjaGFpbkRhdGEiLCJrZXlzIiwiZm9yRWFjaCIsImlkIiwicmVtb3ZlZEl0ZW1JZHMiLCJnZXRIYXNoZXNCeUhhc2giLCJoYXNoZXMiLCJyZWNvcmRJdGVtcyIsImNhdGVnb3J5TmFtZSIsImFjY291bnRUeXBlIiwiY29ubmVjdGlvbklkIiwiaW5zdGFuY2VJbmRleCIsIml0ZW1zIiwicmVnaW9uIiwic3BsaXQiLCJoYXNoRGljdGlvbmFyeSIsImRhdGFEaWN0aW9uYXJ5IiwibGVuZ3RoIiwiaXRlbSIsIl90ZXJtaW5hbEhhc2hNYW5hZ2VyIiwiZ2V0SXRlbUhhc2giLCJfZGF0YVR5cGUiLCJfaWRLZXkiLCJkaWN0aW9uYXJ5SGFzaCIsIl9nZXRBcnJheVhvciIsInZhbHVlcyIsIl91cGRhdGVDYXRlZ29yeVJlY29yZCIsInJlbW92ZVJlZmVyZW5jZSIsImFkZFJlZmVyZW5jZSIsImNoaWxkSGFzaGVzIiwibGFzdFVwZGF0ZWQiLCJEYXRlIiwibm93IiwicmVmZXJlbmNlcyIsInVwZGF0ZUl0ZW1zIiwiRXJyb3IiLCJwYXJlbnRIYXNoRGljdGlvbmFyeSIsInJlbW92ZWRJZCIsInB1c2giLCJnZXRMYXN0VXNlZEhhc2hlcyIsInNlYXJjaEhhc2hlcyIsImdldFRvcEhhc2hlcyIsImNhdGVnb3J5IiwiaGFzaEFtb3VudCIsImNhdGVnb3J5RGF0YSIsIl9oYXNoZXNCeUNhdGVnb3J5IiwiaGFzaGVzQXJyYXkiLCJJbmZpbml0eSIsInNvcnQiLCJhIiwiYiIsImtleSIsImNvbmNhdCIsInNsaWNlIiwiX3VzZUZ1enp5U2VhcmNoIiwicmVzdWx0cyIsIl9nZXRTaW1pbGFyQ2F0ZWdvcnlOYW1lcyIsImluY2x1ZGVzIiwiaW5kZXgiLCJmaW5kSW5kZXgiLCJpbnN0YW5jZSIsInNwbGljZSIsImNhdGVnb3J5TmFtZUxpc3QiLCJmdXNlIiwiRnVzZSIsInRocmVzaG9sZCIsInNlYXJjaCIsIm1hcCIsInJlc3VsdCIsImhleEFycmF5IiwicmVkdWNlIiwiX2dldEhleFhvciIsImhleDEiLCJoZXgyIiwiYnVmMSIsIkJ1ZmZlciIsImZyb20iLCJidWYyIiwiYnVmUmVzdWx0IiwiaSIsIkFycmF5IiwicHJvdG90eXBlIiwiY2FsbCIsImJ5dGUiLCJ0b1N0cmluZyIsImpvaW4iLCJkYXRlIiwiX3JlbW92ZUNhdGVnb3J5UmVjb3JkIiwiZGF0ZXMiLCJmaWx0ZXIiLCJfb3B0aW1pemVUcmVlc0pvYiIsIl9yZWNvcmRFeHBpcmF0aW9uVGltZSIsImNoaWxkSGFzaCIsImNoaWxkRGF0YSIsImNvbWJpbmVkQ2hhbmdlcyIsImNvbWJpbmVkSGFzaGVzIiwiY2hpbGREYXRhSWRzIiwiY29tYmluZWRSZW1vdmVkSWRzIiwiY2hpbGRJdGVtcyIsIml0ZW1IYXNoIiwiY2F0ZWdvcmllcyIsInN0b3AiLCJjbGVhckludGVydmFsIiwiX2ludGVydmFsIiwiY29uc3RydWN0b3IiLCJ0ZXJtaW5hbEhhc2hNYW5hZ2VyIiwiaWRLZXkiLCJkYXRhVHlwZSIsInVzZUZ1enp5U2VhcmNoIiwia2VlcEhhc2hUcmVlcyIsImJpbmQiLCJzZXRJbnRlcnZhbCJdLCJtYXBwaW5ncyI6Ijs7Ozs7OztlQVVxQkE7Ozs2REFWSjt3QkFDTTs7Ozs7O0FBRXZCLE1BQU1DLFlBQVksT0FBT0MsV0FBVyxlQUFlLE9BQU9BLE9BQU9DLFFBQVEsS0FBSztBQUM5RSxNQUFNQyxlQUFlLE9BQU9DLFlBQVksZUFBZUEsUUFBUUMsT0FBTyxJQUFJRCxRQUFRQyxPQUFPLENBQUNDLElBQUksS0FBSztBQUNuRyxNQUFNQyxRQUFRLENBQUNQLGFBQWFHO0FBS2IsSUFBQSxBQUFNSixnQkFBTixNQUFNQTtJQXdCbkI7Ozs7R0FJQyxHQUNELHNDQUFzQztJQUN0Q1MsZUFBZUMsSUFBSSxFQUFFO1FBQ25CLE1BQU1DLE9BQU8sSUFBSSxDQUFDQyxXQUFXLENBQUNGLEtBQUs7UUFDbkMsSUFBRyxDQUFDQyxNQUFNO1lBQ1IsT0FBTztRQUNULE9BQU8sSUFBRyxDQUFDQSxLQUFLRSxVQUFVLEVBQUU7WUFDMUIsT0FBT0YsS0FBS0EsSUFBSTtRQUNsQixPQUFPO1lBQ0w7OztPQUdDLEdBQ0QsSUFBSUcsWUFBWTtnQkFBQ0o7YUFBSztZQUN0QkksVUFBVUMsT0FBTyxDQUFDSixLQUFLRSxVQUFVO1lBQ2pDLElBQUlHLGFBQWEsSUFBSSxDQUFDSixXQUFXLENBQUNELEtBQUtFLFVBQVUsQ0FBQztZQUNsRCxNQUFNRyxXQUFXSCxVQUFVLENBQUU7Z0JBQzNCQyxVQUFVQyxPQUFPLENBQUNDLFdBQVdILFVBQVU7Z0JBQ3ZDRyxhQUFhLElBQUksQ0FBQ0osV0FBVyxDQUFDSSxXQUFXSCxVQUFVLENBQUM7WUFDdEQ7WUFDQSxNQUFNSSxRQUFRQyxPQUFPQyxNQUFNLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQ1AsV0FBVyxDQUFDRSxVQUFVTSxLQUFLLEdBQUcsQ0FBQ1QsSUFBSTtZQUN4RSxLQUFJLElBQUlVLGFBQWFQLFVBQVc7Z0JBQzlCLE1BQU1RLFlBQVksSUFBSSxDQUFDVixXQUFXLENBQUNTLFVBQVU7Z0JBQzdDSCxPQUFPSyxJQUFJLENBQUNELFVBQVVYLElBQUksRUFBRWEsT0FBTyxDQUFDQyxDQUFBQTtvQkFDbENSLEtBQUssQ0FBQ1EsR0FBRyxHQUFHSCxVQUFVWCxJQUFJLENBQUNjLEdBQUc7Z0JBQ2hDO2dCQUNBSCxVQUFVSSxjQUFjLENBQUNGLE9BQU8sQ0FBQ0MsQ0FBQUE7b0JBQy9CLE9BQU9SLEtBQUssQ0FBQ1EsR0FBRztnQkFDbEI7WUFDRjtZQUNBLE9BQU9SO1FBQ1Q7SUFDRjtJQUVBOzs7O0dBSUMsR0FDRFUsZ0JBQWdCakIsSUFBSSxFQUFFO1FBQ3BCLE1BQU1DLE9BQU8sSUFBSSxDQUFDQyxXQUFXLENBQUNGLEtBQUs7UUFDbkMsSUFBRyxDQUFDQyxNQUFNO1lBQ1IsT0FBTztRQUNULE9BQU8sSUFBRyxDQUFDQSxLQUFLRSxVQUFVLEVBQUU7WUFDMUIsT0FBT0YsS0FBS2lCLE1BQU07UUFDcEIsT0FBTztZQUNMLElBQUlkLFlBQVk7Z0JBQUNKO2FBQUs7WUFDdEJJLFVBQVVDLE9BQU8sQ0FBQ0osS0FBS0UsVUFBVTtZQUNqQyxJQUFJRyxhQUFhLElBQUksQ0FBQ0osV0FBVyxDQUFDRCxLQUFLRSxVQUFVLENBQUM7WUFDbEQsTUFBTUcsV0FBV0gsVUFBVSxDQUFFO2dCQUMzQkMsVUFBVUMsT0FBTyxDQUFDQyxXQUFXSCxVQUFVO2dCQUN2Q0csYUFBYSxJQUFJLENBQUNKLFdBQVcsQ0FBQ0ksV0FBV0gsVUFBVSxDQUFDO1lBQ3REO1lBQ0EsTUFBTUksUUFBUUMsT0FBT0MsTUFBTSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUNQLFdBQVcsQ0FBQ0UsVUFBVU0sS0FBSyxHQUFHLENBQUNRLE1BQU07WUFDMUUsS0FBSSxJQUFJUCxhQUFhUCxVQUFXO2dCQUM5QixNQUFNUSxZQUFZLElBQUksQ0FBQ1YsV0FBVyxDQUFDUyxVQUFVO2dCQUM3Q0gsT0FBT0ssSUFBSSxDQUFDRCxVQUFVTSxNQUFNLEVBQUVKLE9BQU8sQ0FBQ0MsQ0FBQUE7b0JBQ3BDUixLQUFLLENBQUNRLEdBQUcsR0FBR0gsVUFBVU0sTUFBTSxDQUFDSCxHQUFHO2dCQUNsQztnQkFDQUgsVUFBVUksY0FBYyxDQUFDRixPQUFPLENBQUNDLENBQUFBO29CQUMvQixPQUFPUixLQUFLLENBQUNRLEdBQUc7Z0JBQ2xCO1lBQ0Y7WUFDQSxPQUFPUjtRQUNUO0lBQ0Y7SUFFQTs7Ozs7Ozs7R0FRQyxHQUNEWSxZQUFZQyxZQUFZLEVBQUVDLFdBQVcsRUFBRUMsWUFBWSxFQUFFQyxhQUFhLEVBQUVDLEtBQUssRUFBRTtRQUN6RSxNQUFNQyxTQUFTRixjQUFjRyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7UUFDMUMsTUFBTUMsaUJBQWlCLENBQUM7UUFDeEIsTUFBTUMsaUJBQWlCLENBQUM7UUFDeEIsSUFBRyxDQUFDSixNQUFNSyxNQUFNLEVBQUc7WUFDakIsT0FBTztRQUNUO1FBRUEsS0FBSSxJQUFJQyxRQUFRTixNQUFPO1lBQ3JCLE1BQU14QixPQUFPLElBQUksQ0FBQytCLG9CQUFvQixDQUFDQyxXQUFXLENBQUNGLE1BQU0sSUFBSSxDQUFDRyxTQUFTLEVBQUVaLGFBQWFJO1lBQ3RGRyxjQUFjLENBQUNFLElBQUksQ0FBQyxJQUFJLENBQUNJLE1BQU0sQ0FBQyxDQUFDLEdBQUdKO1lBQ3BDSCxjQUFjLENBQUNHLElBQUksQ0FBQyxJQUFJLENBQUNJLE1BQU0sQ0FBQyxDQUFDLEdBQUdsQztRQUN0QztRQUNBLE1BQU1tQyxpQkFBaUIsSUFBSSxDQUFDQyxZQUFZLENBQUM1QixPQUFPNkIsTUFBTSxDQUFDVjtRQUN2RCxJQUFJLENBQUNXLHFCQUFxQixDQUFDbEIsY0FBY2U7UUFDekMsSUFBSSxDQUFDSSxlQUFlLENBQUNqQixjQUFjQztRQUNuQyxJQUFHLElBQUksQ0FBQ3JCLFdBQVcsQ0FBQ2lDLGVBQWUsRUFBRTtZQUNuQyxJQUFJLENBQUNLLFlBQVksQ0FBQ0wsZ0JBQWdCYixjQUFjQztRQUNsRCxPQUFPO1lBQ0wsSUFBSSxDQUFDckIsV0FBVyxDQUFDaUMsZUFBZSxHQUFHO2dCQUNqQ2pCLFFBQVFTO2dCQUNSMUIsTUFBTTJCO2dCQUNOWixnQkFBZ0IsRUFBRTtnQkFDbEJiLFlBQVk7Z0JBQ1pzQyxhQUFhLEVBQUU7Z0JBQ2ZDLGFBQWFDLEtBQUtDLEdBQUc7Z0JBQ3JCQyxZQUFZO29CQUFDLENBQUN2QixhQUFhLEVBQUU7d0JBQUNDO3FCQUFjO2dCQUFBO1lBQzlDO1FBQ0Y7UUFDQSxPQUFPWTtJQUNUO0lBRUE7Ozs7Ozs7Ozs7R0FVQyxHQUNELHNDQUFzQztJQUN0Q1csWUFBWTFCLFlBQVksRUFBRUMsV0FBVyxFQUFFQyxZQUFZLEVBQUVDLGFBQWEsRUFBRUMsS0FBSyxFQUFFUixjQUFjLEVBQUViLFVBQVUsRUFBRTtRQUNyRyxJQUFHLENBQUNBLFlBQVk7WUFDZCxPQUFPLElBQUksQ0FBQ2dCLFdBQVcsQ0FBQ0MsY0FBY0MsYUFBYUMsY0FBY0MsZUFBZUM7UUFDbEY7UUFDQSxNQUFNQyxTQUFTRixjQUFjRyxLQUFLLENBQUMsSUFBSSxDQUFDLEVBQUU7UUFDMUMsTUFBTUMsaUJBQWlCLENBQUM7UUFDeEIsTUFBTUMsaUJBQWlCLENBQUM7UUFDeEIsSUFBSXRCLGFBQWEsSUFBSSxDQUFDVyxlQUFlLENBQUNkO1FBQ3RDLElBQUcsQ0FBQ0csWUFBWTtZQUNkLE1BQU15QyxNQUFNO1FBQ2QsT0FBTztZQUNMLE1BQU1DLHVCQUF1QnhDLE9BQU9DLE1BQU0sQ0FBQyxDQUFDLEdBQUdIO1lBQy9DLEtBQUksSUFBSXdCLFFBQVFOLE1BQU87Z0JBQ3JCLE1BQU14QixPQUFPLElBQUksQ0FBQytCLG9CQUFvQixDQUFDQyxXQUFXLENBQUNGLE1BQU0sSUFBSSxDQUFDRyxTQUFTLEVBQUVaLGFBQWFJO2dCQUN0RkcsY0FBYyxDQUFDRSxJQUFJLENBQUMsSUFBSSxDQUFDSSxNQUFNLENBQUMsQ0FBQyxHQUFHSjtnQkFDcENILGNBQWMsQ0FBQ0csSUFBSSxDQUFDLElBQUksQ0FBQ0ksTUFBTSxDQUFDLENBQUMsR0FBR2xDO2dCQUNwQ2dELG9CQUFvQixDQUFDbEIsSUFBSSxDQUFDLElBQUksQ0FBQ0ksTUFBTSxDQUFDLENBQUMsR0FBR2xDO1lBQzVDO1lBQ0EsS0FBSSxJQUFJaUQsYUFBYWpDLGVBQWdCO2dCQUNuQyxPQUFPZ0Msb0JBQW9CLENBQUNDLFVBQVU7WUFDeEM7WUFDQSxNQUFNZCxpQkFBaUIsSUFBSSxDQUFDQyxZQUFZLENBQUM1QixPQUFPNkIsTUFBTSxDQUFDVztZQUN2RCxJQUFJLENBQUNWLHFCQUFxQixDQUFDbEIsY0FBY2U7WUFDekMsSUFBR0EsbUJBQW1CaEMsWUFBWTtnQkFDaEMsSUFBSSxDQUFDb0MsZUFBZSxDQUFDakIsY0FBY0M7Z0JBQ25DLElBQUcsSUFBSSxDQUFDckIsV0FBVyxDQUFDaUMsZUFBZSxFQUFFO29CQUNuQyxJQUFJLENBQUNLLFlBQVksQ0FBQ0wsZ0JBQWdCYixjQUFjQztnQkFDbEQsT0FBTyxJQUFHWSxnQkFBZ0I7b0JBQ3hCLElBQUksQ0FBQ2pDLFdBQVcsQ0FBQ2lDLGVBQWUsR0FBRzt3QkFDakNqQixRQUFRUzt3QkFDUjFCLE1BQU0yQjt3QkFDTnpCO3dCQUNBYTt3QkFDQXlCLGFBQWEsRUFBRTt3QkFDZkMsYUFBYUMsS0FBS0MsR0FBRzt3QkFDckJDLFlBQVk7NEJBQUMsQ0FBQ3ZCLGFBQWEsRUFBRTtnQ0FBQ0M7NkJBQWM7d0JBQUE7b0JBQzlDO29CQUNBLElBQUksQ0FBQ3JCLFdBQVcsQ0FBQ0MsV0FBVyxDQUFDc0MsV0FBVyxDQUFDUyxJQUFJLENBQUNmO2dCQUNoRDtZQUNGLE9BQU87Z0JBQ0wsSUFBSSxDQUFDSSxlQUFlLENBQUNqQixjQUFjQztnQkFDbkMsSUFBSSxDQUFDaUIsWUFBWSxDQUFDTCxnQkFBZ0JiLGNBQWNDO1lBQ2xEO1lBQ0EsT0FBT1k7UUFDVDtJQUNGO0lBRUE7Ozs7R0FJQyxHQUNEZ0Isa0JBQWtCL0IsWUFBWSxFQUFFO1FBQzlCLElBQUlnQyxlQUFlLEVBQUU7UUFDckIsTUFBTUMsZUFBZSxDQUFDQyxVQUFVQztZQUM5QixNQUFNQyxlQUFlLElBQUksQ0FBQ0MsaUJBQWlCLENBQUNILFNBQVM7WUFDckQsSUFBRyxDQUFDRSxjQUFjO2dCQUNoQixPQUFPLEVBQUU7WUFDWCxPQUFPO2dCQUNMLElBQUlFLGNBQWMsRUFBRTtnQkFDcEIsSUFBRyxDQUFDSCxZQUFZO29CQUNkQSxhQUFhSTtnQkFDZjtnQkFDQSxNQUFNOUMsT0FBT0wsT0FBT0ssSUFBSSxDQUFDMkM7Z0JBQ3pCM0MsS0FBSytDLElBQUksQ0FBQyxDQUFDQyxHQUFHQyxJQUFNQSxJQUFJRDtnQkFDeEIsS0FBSSxJQUFJRSxPQUFPbEQsS0FBTTtvQkFDbkI2QyxjQUFjQSxZQUFZTSxNQUFNLENBQUNSLFlBQVksQ0FBQ08sSUFBSTtvQkFDbEQsSUFBR0wsWUFBWTdCLE1BQU0sR0FBRzBCLFlBQVk7d0JBQ2xDRyxjQUFjQSxZQUFZTyxLQUFLLENBQUMsR0FBR1Y7d0JBQ25DO29CQUNGO2dCQUNGO2dCQUNBLE9BQU9HO1lBQ1Q7UUFDRjtRQUVBLElBQUcsSUFBSSxDQUFDUSxlQUFlLEVBQUU7WUFDdkIsSUFBSUMsVUFBVSxJQUFJLENBQUNDLHdCQUF3QixDQUFDaEQ7WUFDNUMsdUNBQXVDO1lBQ3ZDLElBQUcrQyxPQUFPLENBQUMsRUFBRSxLQUFLL0MsY0FBYztnQkFDOUJnQyxlQUFlQyxhQUFhakM7Z0JBQzVCK0MsVUFBVUEsUUFBUUYsS0FBSyxDQUFDO1lBQzFCO1lBQ0EscURBQXFEO1lBQ3JERSxRQUFRckQsT0FBTyxDQUFDd0MsQ0FBQUE7Z0JBQ2RGLGVBQWVBLGFBQWFZLE1BQU0sQ0FBQ1gsYUFBYUMsVUFBVTtZQUM1RDtRQUNGLE9BQU87WUFDTEYsZUFBZUMsYUFBYWpDLGNBQWM7UUFDNUM7UUFFQWdDLGVBQWVBLGFBQWFhLEtBQUssQ0FBQyxHQUFHO1FBQ3JDLE9BQU9iO0lBQ1Q7SUFFQTs7Ozs7R0FLQyxHQUNELHNDQUFzQztJQUN0Q1osYUFBYXhDLElBQUksRUFBRXNCLFlBQVksRUFBRUMsYUFBYSxFQUFFO1FBQzlDLElBQUksQ0FBQyxJQUFJLENBQUNyQixXQUFXLENBQUNGLEtBQUssRUFBRTtZQUMzQixNQUFNK0MsTUFBTSxDQUFDLHNCQUFzQixFQUFFLElBQUksQ0FBQ2QsU0FBUyxDQUFDLGVBQWUsRUFBRWpDLEtBQUssY0FBYyxDQUFDO1FBQzNGO1FBQ0EsTUFBTTZDLGFBQWEsSUFBSSxDQUFDM0MsV0FBVyxDQUFDRixLQUFLLENBQUM2QyxVQUFVO1FBQ3BELElBQUcsQ0FBQ0EsVUFBVSxDQUFDdkIsYUFBYSxFQUFFO1lBQzVCdUIsVUFBVSxDQUFDdkIsYUFBYSxHQUFHO2dCQUFDQzthQUFjO1FBQzVDLE9BQU87WUFDTCxJQUFHLENBQUNzQixVQUFVLENBQUN2QixhQUFhLENBQUMrQyxRQUFRLENBQUM5QyxnQkFBZ0I7Z0JBQ3BEc0IsVUFBVSxDQUFDdkIsYUFBYSxDQUFDNEIsSUFBSSxDQUFDM0I7WUFDaEM7UUFDRjtRQUNBLElBQUksQ0FBQ3JCLFdBQVcsQ0FBQ0YsS0FBSyxDQUFDMEMsV0FBVyxHQUFHQyxLQUFLQyxHQUFHO0lBQy9DO0lBRUE7Ozs7R0FJQyxHQUNETCxnQkFBZ0JqQixZQUFZLEVBQUVDLGFBQWEsRUFBRTtRQUMzQ2YsT0FBT0ssSUFBSSxDQUFDLElBQUksQ0FBQ1gsV0FBVyxFQUFFWSxPQUFPLENBQUNkLENBQUFBO1lBQ3BDLE1BQU02QyxhQUFhLElBQUksQ0FBQzNDLFdBQVcsQ0FBQ0YsS0FBSyxDQUFDNkMsVUFBVTtZQUNwRCxJQUFHQSxVQUFVLENBQUN2QixhQUFhLEVBQUU7Z0JBQzNCLE1BQU1nRCxRQUFRekIsVUFBVSxDQUFDdkIsYUFBYSxDQUFDaUQsU0FBUyxDQUFDQyxDQUFBQSxXQUFZakQsa0JBQWtCaUQ7Z0JBQy9FLElBQUdGLFVBQVUsQ0FBQyxHQUFHO29CQUNmekIsVUFBVSxDQUFDdkIsYUFBYSxDQUFDbUQsTUFBTSxDQUFDSCxPQUFPO2dCQUN6QztnQkFDQSxJQUFHLENBQUN6QixVQUFVLENBQUN2QixhQUFhLENBQUNPLE1BQU0sRUFBRTtvQkFDbkMsT0FBT2dCLFVBQVUsQ0FBQ3ZCLGFBQWE7Z0JBQ2pDO1lBQ0Y7UUFDRjtJQUNGO0lBRUE4Qyx5QkFBeUJoRCxZQUFZLEVBQUU7UUFDckMsTUFBTXNELG1CQUFtQmxFLE9BQU9LLElBQUksQ0FBQyxJQUFJLENBQUM0QyxpQkFBaUI7UUFDM0QsTUFBTWtCLE9BQU8sSUFBSUMsYUFBSSxDQUFDRixrQkFBa0I7WUFDdENHLFdBQVc7UUFDYjtRQUNBLE9BQU9GLEtBQUtHLE1BQU0sQ0FBQzFELGNBQWMyRCxHQUFHLENBQUNDLENBQUFBLFNBQVVBLE9BQU9sRCxJQUFJO0lBQzVEO0lBRUE7Ozs7R0FJQyxHQUNETSxhQUFhNkMsUUFBUSxFQUFFO1FBQ3JCLE9BQU9BLFNBQVNwRCxNQUFNLEdBQUdvRCxTQUFTQyxNQUFNLENBQUMsQ0FBQ3JCLEdBQUdDLElBQU0sSUFBSSxDQUFDcUIsVUFBVSxDQUFDdEIsR0FBR0MsTUFBTTtJQUM5RTtJQUVBcUIsV0FBV0MsSUFBSSxFQUFFQyxJQUFJLEVBQUU7UUFDckIsTUFBTUMsT0FBT0MsY0FBTSxDQUFDQyxJQUFJLENBQUNKLE1BQU07UUFDL0IsTUFBTUssT0FBT0YsY0FBTSxDQUFDQyxJQUFJLENBQUNILE1BQU07UUFDL0Isc0NBQXNDO1FBQ3RDLE1BQU1LLFlBQVlKLEtBQUtQLEdBQUcsQ0FBQyxDQUFDakIsR0FBRzZCLElBQU03QixJQUFJMkIsSUFBSSxDQUFDRSxFQUFFO1FBQ2hELE9BQU8sQUFBQ3BHLGFBQWFPLFFBRWpCOEYsTUFBTUMsU0FBUyxDQUFDZCxHQUFHLENBQUNlLElBQUksQ0FBQ0osV0FBV0ssQ0FBQUEsT0FBUSxBQUFDLENBQUEsTUFBTSxBQUFDQSxDQUFBQSxPQUFPLElBQUcsRUFBR0MsUUFBUSxDQUFDLEdBQUUsRUFBRy9CLEtBQUssQ0FBQyxDQUFDLElBQUlnQyxJQUFJLENBQUMsTUFDL0ZQLFVBQVVNLFFBQVEsQ0FBQztJQUN6QjtJQUVBMUQsc0JBQXNCbEIsWUFBWSxFQUFFcEIsSUFBSSxFQUFFO1FBQ3hDLElBQUcsQ0FBQ0EsTUFBTTtZQUNSO1FBQ0Y7UUFDQSxNQUFNa0csT0FBT3ZELEtBQUtDLEdBQUc7UUFDckIsSUFBSSxDQUFDdUQscUJBQXFCLENBQUMvRSxjQUFjcEI7UUFDekMsSUFBRyxDQUFDLElBQUksQ0FBQ3lELGlCQUFpQixDQUFDckMsYUFBYSxFQUFFO1lBQ3hDLElBQUksQ0FBQ3FDLGlCQUFpQixDQUFDckMsYUFBYSxHQUFHLENBQUM7UUFDMUM7UUFDQSxJQUFHLENBQUMsSUFBSSxDQUFDcUMsaUJBQWlCLENBQUNyQyxhQUFhLENBQUM4RSxLQUFLLEVBQUU7WUFDOUMsSUFBSSxDQUFDekMsaUJBQWlCLENBQUNyQyxhQUFhLENBQUM4RSxLQUFLLEdBQUcsRUFBRTtRQUNqRDtRQUNBLElBQUksQ0FBQ3pDLGlCQUFpQixDQUFDckMsYUFBYSxDQUFDOEUsS0FBSyxDQUFDaEQsSUFBSSxDQUFDbEQ7SUFDbEQ7SUFFQW1HLHNCQUFzQi9FLFlBQVksRUFBRXBCLElBQUksRUFBRTtRQUN4QyxJQUFHLElBQUksQ0FBQ3lELGlCQUFpQixDQUFDckMsYUFBYSxFQUFFO1lBQ3ZDLE1BQU1nRixRQUFRNUYsT0FBT0ssSUFBSSxDQUFDLElBQUksQ0FBQzRDLGlCQUFpQixDQUFDckMsYUFBYTtZQUM5RGdGLE1BQU10RixPQUFPLENBQUNvRixDQUFBQTtnQkFDWixJQUFHLElBQUksQ0FBQ3pDLGlCQUFpQixDQUFDckMsYUFBYSxDQUFDOEUsS0FBSyxDQUFDN0IsUUFBUSxDQUFDckUsT0FBTztvQkFDNUQsSUFBSSxDQUFDeUQsaUJBQWlCLENBQUNyQyxhQUFhLENBQUM4RSxLQUFLLEdBQ3hDLElBQUksQ0FBQ3pDLGlCQUFpQixDQUFDckMsYUFBYSxDQUFDOEUsS0FBSyxDQUFDRyxNQUFNLENBQUN2RSxDQUFBQSxPQUFRQSxTQUFTOUI7b0JBQ3JFLElBQUcsSUFBSSxDQUFDeUQsaUJBQWlCLENBQUNyQyxhQUFhLENBQUM4RSxLQUFLLENBQUNyRSxNQUFNLEtBQUssR0FBRzt3QkFDMUQsT0FBTyxJQUFJLENBQUM0QixpQkFBaUIsQ0FBQ3JDLGFBQWEsQ0FBQzhFLEtBQUs7b0JBQ25EO2dCQUNGO1lBQ0Y7WUFDQSxJQUFHMUYsT0FBT0ssSUFBSSxDQUFDLElBQUksQ0FBQzRDLGlCQUFpQixDQUFDckMsYUFBYSxFQUFFUyxNQUFNLEtBQUssR0FBRztnQkFDak