UNPKG

appium-adb

Version:

Android Debug Bridge interface

486 lines (394 loc) 32.5 kB
'use strict'; var _regeneratorRuntime = require('babel-runtime/regenerator')['default']; var _getIterator = require('babel-runtime/core-js/get-iterator')['default']; var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default']; Object.defineProperty(exports, '__esModule', { value: true }); var _path = require('path'); var _path2 = _interopRequireDefault(_path); var _appiumSupport = require('appium-support'); var _loggerJs = require('./logger.js'); var _loggerJs2 = _interopRequireDefault(_loggerJs); var _admZip = require('adm-zip'); var _admZip2 = _interopRequireDefault(_admZip); var _teen_process = require('teen_process'); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var rootDir = _path2['default'].resolve(__dirname, process.env.NO_PRECOMPILE ? '..' : '../..'); var androidPlatforms = ['android-4.2', 'android-17', 'android-4.3', 'android-18', 'android-4.4', 'android-19', 'android-L', 'android-20', 'android-5.0', 'android-21', 'android-22', 'android-MNC', 'android-23', 'android-6.0']; function getDirectories(rootPath) { var files, dirs, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, file, pathString; return _regeneratorRuntime.async(function getDirectories$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: context$1$0.next = 2; return _regeneratorRuntime.awrap(_appiumSupport.fs.readdir(rootPath)); case 2: files = context$1$0.sent; dirs = []; _iteratorNormalCompletion = true; _didIteratorError = false; _iteratorError = undefined; context$1$0.prev = 7; _iterator = _getIterator(files); case 9: if (_iteratorNormalCompletion = (_step = _iterator.next()).done) { context$1$0.next = 19; break; } file = _step.value; pathString = _path2['default'].resolve(rootPath, file); context$1$0.next = 14; return _regeneratorRuntime.awrap(_appiumSupport.fs.lstat(pathString)); case 14: if (!context$1$0.sent.isDirectory()) { context$1$0.next = 16; break; } dirs.push(file); case 16: _iteratorNormalCompletion = true; context$1$0.next = 9; break; case 19: context$1$0.next = 25; break; case 21: context$1$0.prev = 21; context$1$0.t0 = context$1$0['catch'](7); _didIteratorError = true; _iteratorError = context$1$0.t0; case 25: context$1$0.prev = 25; context$1$0.prev = 26; if (!_iteratorNormalCompletion && _iterator['return']) { _iterator['return'](); } case 28: context$1$0.prev = 28; if (!_didIteratorError) { context$1$0.next = 31; break; } throw _iteratorError; case 31: return context$1$0.finish(28); case 32: return context$1$0.finish(25); case 33: return context$1$0.abrupt('return', dirs.sort()); case 34: case 'end': return context$1$0.stop(); } }, null, this, [[7, 21, 25, 33], [26,, 28, 32]]); } function getAndroidPlatformAndPath() { var androidHome, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, platform, platforms, platformPath; return _regeneratorRuntime.async(function getAndroidPlatformAndPath$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: androidHome = process.env.ANDROID_HOME; if (_lodash2['default'].isString(androidHome)) { context$1$0.next = 4; break; } _loggerJs2['default'].error("ANDROID_HOME was not exported!"); return context$1$0.abrupt('return', null); case 4: _iteratorNormalCompletion2 = true; _didIteratorError2 = false; _iteratorError2 = undefined; context$1$0.prev = 7; _iterator2 = _getIterator(_lodash2['default'].clone(androidPlatforms).reverse()); case 9: if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { context$1$0.next = 20; break; } platform = _step2.value; platforms = _path2['default'].resolve(androidHome, 'platforms'); platformPath = _path2['default'].resolve(platforms, platform); context$1$0.next = 15; return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(platformPath)); case 15: if (!context$1$0.sent) { context$1$0.next = 17; break; } return context$1$0.abrupt('return', { platform: platform, platformPath: platformPath }); case 17: _iteratorNormalCompletion2 = true; context$1$0.next = 9; break; case 20: context$1$0.next = 26; break; case 22: context$1$0.prev = 22; context$1$0.t0 = context$1$0['catch'](7); _didIteratorError2 = true; _iteratorError2 = context$1$0.t0; case 26: context$1$0.prev = 26; context$1$0.prev = 27; if (!_iteratorNormalCompletion2 && _iterator2['return']) { _iterator2['return'](); } case 29: context$1$0.prev = 29; if (!_didIteratorError2) { context$1$0.next = 32; break; } throw _iteratorError2; case 32: return context$1$0.finish(29); case 33: return context$1$0.finish(26); case 34: return context$1$0.abrupt('return', null); case 35: case 'end': return context$1$0.stop(); } }, null, this, [[7, 22, 26, 34], [27,, 29, 33]]); } function unzipFile(zipPath) { var zip; return _regeneratorRuntime.async(function unzipFile$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: _loggerJs2['default'].debug('Unzipping ' + zipPath); context$1$0.prev = 1; context$1$0.next = 4; return _regeneratorRuntime.awrap(assertZipArchive(zipPath)); case 4: if (!_appiumSupport.system.isWindows()) { context$1$0.next = 10; break; } zip = new _admZip2['default'](zipPath); zip.extractAllTo(_path2['default'].dirname(zipPath), true); _loggerJs2['default'].debug("Unzip successful"); context$1$0.next = 13; break; case 10: context$1$0.next = 12; return _regeneratorRuntime.awrap((0, _teen_process.exec)('unzip', ['-o', zipPath], { cwd: _path2['default'].dirname(zipPath) })); case 12: _loggerJs2['default'].debug("Unzip successful"); case 13: context$1$0.next = 18; break; case 15: context$1$0.prev = 15; context$1$0.t0 = context$1$0['catch'](1); throw new Error('Error occurred while unzipping. Original error: ' + context$1$0.t0.message); case 18: case 'end': return context$1$0.stop(); } }, null, this, [[1, 15]]); } function assertZipArchive(zipPath) { var execOpts; return _regeneratorRuntime.async(function assertZipArchive$(context$1$0) { while (1) switch (context$1$0.prev = context$1$0.next) { case 0: _loggerJs2['default'].debug('Testing zip archive: ' + zipPath); if (!_appiumSupport.system.isWindows()) { context$1$0.next = 11; break; } context$1$0.next = 4; return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(zipPath)); case 4: if (!context$1$0.sent) { context$1$0.next = 8; break; } _loggerJs2['default'].debug("Zip archive tested clean"); context$1$0.next = 9; break; case 8: throw new Error('Zip archive not present at ' + zipPath); case 9: context$1$0.next = 14; break; case 11: execOpts = { cwd: _path2['default'].dirname(zipPath) }; context$1$0.next = 14; return _regeneratorRuntime.awrap((0, _teen_process.exec)('unzip', ['-tq', zipPath], execOpts)); case 14: case 'end': return context$1$0.stop(); } }, null, this); } function getIMEListFromOutput(stdout) { var engines = []; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = _getIterator(stdout.split('\n')), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var line = _step3.value; if (line.length > 0 && line[0] !== ' ') { // remove newline and trailing colon, and add to the list engines.push(line.trim().replace(/:$/, '')); } } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3['return']) { _iterator3['return'](); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return engines; } function getJavaForOs() { var sep = _path2['default'].sep; var java = '' + getJavaHome() + sep + 'bin' + sep + 'java'; if (_appiumSupport.system.isWindows()) { java = java + '.exe'; } return java; } function getJavaHome() { if (process.env.JAVA_HOME) { return process.env.JAVA_HOME; } throw new Error("JAVA_HOME is not set currently. Please set JAVA_HOME."); } /* * Checks mShowingLockscreen in dumpsys output to determine if lock screen is showing */ function isShowingLockscreen(dumpsys) { var m = /mShowingLockscreen=\w+/gi.exec(dumpsys); var ret = m && m.length && m[0].split('=')[1] === 'true' || false; return ret; } /* * Checks mCurrentFocus in dumpsys output to determine if Keyguard is activated */ function isCurrentFocusOnKeyguard(dumpsys) { var m = /mCurrentFocus.+Keyguard/gi.exec(dumpsys); return m && m.length && m[0] ? true : false; } /* * Checks mScreenOnFully in dumpsys output to determine if screen is showing * Default is true */ function isScreenOnFully(dumpsys) { var m = /mScreenOnFully=\w+/gi.exec(dumpsys); return !m || // if information is missing we assume screen is fully on m && m.length > 0 && m[0].split('=')[1] === 'true' || false; } function buildStartCmd(startAppOptions, apiLevel) { var cmd = ['am', 'start', '-W', '-n', startAppOptions.pkg + '/' + startAppOptions.activity]; if (startAppOptions.stopApp && apiLevel >= 15) { cmd.push('-S'); } if (startAppOptions.action) { cmd.push('-a', startAppOptions.action); } if (startAppOptions.category) { cmd.push('-c', startAppOptions.category); } if (startAppOptions.flags) { cmd.push('-f', startAppOptions.flags); } if (startAppOptions.optionalIntentArguments) { // expect optionalIntentArguments to be a single string of the form: // "-flag key" // "-flag key value" // or a combination of these (e.g., "-flag1 key1 -flag2 key2 value2") // take a string and parse out the part before any spaces, and anything after // the first space var parseKeyValue = function parseKeyValue(str) { str = str.trim(); var space = str.indexOf(' '); if (space === -1) { return !!str.length ? [str] : []; } else { return [str.substring(0, space).trim(), str.substring(space + 1).trim()]; } }; // cycle through the optionalIntentArguments and pull out the arguments // add a space initially so flags can be distinguished from arguments that // have internal hyphens var optionalIntentArguments = ' ' + startAppOptions.optionalIntentArguments; var re = / (-[^\s]+) (.+)/; while (true) { var args = re.exec(optionalIntentArguments); if (!args) { if (optionalIntentArguments.length) { // no more flags, so the remainder can be treated as 'key' or 'key value' cmd.push.apply(cmd, parseKeyValue(optionalIntentArguments)); } // we are done break; } // take the flag and see if it is at the beginning of the string // if it is not, then it means we have been through already, and // what is before the flag is the argument for the previous flag var flag = args[1]; var flagPos = optionalIntentArguments.indexOf(flag); if (flagPos !== 0) { var prevArgs = optionalIntentArguments.substring(0, flagPos); cmd.push.apply(cmd, parseKeyValue(prevArgs)); } // add the flag, as there are no more earlier arguments cmd.push(flag); // make optionalIntentArguments hold the remainder optionalIntentArguments = args[2]; } } return cmd; } // turns pkg.activity.name to .activity.name // also turns activity.name to .activity.name function getPossibleActivityNames(pkgName, activityName) { var names = [activityName]; // need to beware of namespaces with overlapping chars: // com.foo.bar // com.foo.barx if (activityName.indexOf(pkgName + '.') === 0) { names.push(activityName.substring(pkgName.length)); } if (activityName[0] !== '.') { names.push('.' + activityName); } return names; } exports.getDirectories = getDirectories; exports.getAndroidPlatformAndPath = getAndroidPlatformAndPath; exports.unzipFile = unzipFile; exports.assertZipArchive = assertZipArchive; exports.getIMEListFromOutput = getIMEListFromOutput; exports.getJavaForOs = getJavaForOs; exports.isShowingLockscreen = isShowingLockscreen; exports.isCurrentFocusOnKeyguard = isCurrentFocusOnKeyguard; exports.isScreenOnFully = isScreenOnFully; exports.buildStartCmd = buildStartCmd; exports.getPossibleActivityNames = getPossibleActivityNames; exports.getJavaHome = getJavaHome; exports.rootDir = rootDir; exports.androidPlatforms = androidPlatforms; // It is not a clean way to sort it, but in this case would work fine because // we have numerics and alphanumeric // will return some thing like this // ["17.0.0", "18.0.1", "19.0.0", "19.0.1", "19.1.0", "20.0.0", // "android-4.2.2", "android-4.3", "android-4.4"] // get the latest platform and path //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9oZWxwZXJzLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7OztvQkFBaUIsTUFBTTs7Ozs2QkFDSSxnQkFBZ0I7O3dCQUMzQixhQUFhOzs7O3NCQUNWLFNBQVM7Ozs7NEJBQ1AsY0FBYzs7c0JBQ3JCLFFBQVE7Ozs7QUFHdEIsSUFBTSxPQUFPLEdBQUcsa0JBQUssT0FBTyxDQUFDLFNBQVMsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGFBQWEsR0FBRyxJQUFJLEdBQUcsT0FBTyxDQUFDLENBQUM7QUFDcEYsSUFBTSxnQkFBZ0IsR0FBRyxDQUFDLGFBQWEsRUFBRSxZQUFZLEVBQUUsYUFBYSxFQUFFLFlBQVksRUFDeEQsYUFBYSxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUN0RCxhQUFhLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQ3hELFlBQVksRUFBRSxhQUFhLENBQUMsQ0FBQzs7QUFFdkQsU0FBZSxjQUFjLENBQUUsUUFBUTtNQUNqQyxLQUFLLEVBQ0wsSUFBSSxrRkFDQyxJQUFJLEVBQ1AsVUFBVTs7Ozs7O3lDQUhFLGtCQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUM7OztBQUFsQyxhQUFLO0FBQ0wsWUFBSSxHQUFHLEVBQUU7Ozs7O2lDQUNJLEtBQUs7Ozs7Ozs7O0FBQWIsWUFBSTtBQUNQLGtCQUFVLEdBQUcsa0JBQUssT0FBTyxDQUFDLFFBQVEsRUFBRSxJQUFJLENBQUM7O3lDQUNsQyxrQkFBRyxLQUFLLENBQUMsVUFBVSxDQUFDOzs7OEJBQUUsV0FBVzs7Ozs7QUFDMUMsWUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzRDQVFiLElBQUksQ0FBQyxJQUFJLEVBQUU7Ozs7Ozs7Q0FDbkI7O0FBRUQsU0FBZSx5QkFBeUI7TUFDaEMsV0FBVyx1RkFPUixRQUFRLEVBQ1gsU0FBUyxFQUNULFlBQVk7Ozs7O0FBVFosbUJBQVcsR0FBRyxPQUFPLENBQUMsR0FBRyxDQUFDLFlBQVk7O1lBQ3ZDLG9CQUFFLFFBQVEsQ0FBQyxXQUFXLENBQUM7Ozs7O0FBQzFCLDhCQUFJLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFDOzRDQUNyQyxJQUFJOzs7Ozs7O2tDQUlRLG9CQUFFLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDLE9BQU8sRUFBRTs7Ozs7Ozs7QUFBL0MsZ0JBQVE7QUFDWCxpQkFBUyxHQUFHLGtCQUFLLE9BQU8sQ0FBQyxXQUFXLEVBQUUsV0FBVyxDQUFDO0FBQ2xELG9CQUFZLEdBQUcsa0JBQUssT0FBTyxDQUFDLFNBQVMsRUFBRSxRQUFRLENBQUM7O3lDQUMxQyxrQkFBRyxNQUFNLENBQUMsWUFBWSxDQUFDOzs7Ozs7Ozs0Q0FDeEIsRUFBQyxRQUFRLEVBQVIsUUFBUSxFQUFFLFlBQVksRUFBWixZQUFZLEVBQUM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs0Q0FHNUIsSUFBSTs7Ozs7OztDQUNaOztBQUVELFNBQWUsU0FBUyxDQUFFLE9BQU87TUFLdkIsR0FBRzs7OztBQUpYLDhCQUFJLEtBQUssZ0JBQWMsT0FBTyxDQUFHLENBQUM7Ozt5Q0FFMUIsZ0JBQWdCLENBQUMsT0FBTyxDQUFDOzs7YUFDM0Isc0JBQU8sU0FBUyxFQUFFOzs7OztBQUNoQixXQUFHLEdBQUcsd0JBQVcsT0FBTyxDQUFDOztBQUM3QixXQUFHLENBQUMsWUFBWSxDQUFDLGtCQUFLLE9BQU8sQ0FBQyxPQUFPLENBQUMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUM5Qyw4QkFBSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQzs7Ozs7O3lDQUV4Qix3QkFBSyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxDQUFDLEVBQUUsRUFBQyxHQUFHLEVBQUUsa0JBQUssT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFDLENBQUM7OztBQUNsRSw4QkFBSSxLQUFLLENBQUMsa0JBQWtCLENBQUMsQ0FBQzs7Ozs7Ozs7O2NBRzFCLElBQUksS0FBSyxzREFBb0QsZUFBRSxPQUFPLENBQUc7Ozs7Ozs7Q0FFbEY7O0FBRUQsU0FBZSxnQkFBZ0IsQ0FBRSxPQUFPO01BU2hDLFFBQVE7Ozs7QUFSZCw4QkFBSSxLQUFLLDJCQUF5QixPQUFPLENBQUcsQ0FBQzs7YUFDekMsc0JBQU8sU0FBUyxFQUFFOzs7Ozs7eUNBQ1Ysa0JBQUcsTUFBTSxDQUFDLE9BQU8sQ0FBQzs7Ozs7Ozs7QUFDMUIsOEJBQUksS0FBSyxDQUFDLDBCQUEwQixDQUFDLENBQUM7Ozs7O2NBRWhDLElBQUksS0FBSyxpQ0FBK0IsT0FBTyxDQUFHOzs7Ozs7O0FBR3RELGdCQUFRLEdBQUcsRUFBQyxHQUFHLEVBQUUsa0JBQUssT0FBTyxDQUFDLE9BQU8sQ0FBQyxFQUFDOzt5Q0FDckMsd0JBQUssT0FBTyxFQUFFLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxFQUFFLFFBQVEsQ0FBQzs7Ozs7OztDQUVsRDs7QUFFRCxTQUFTLG9CQUFvQixDQUFFLE1BQU0sRUFBRTtBQUNyQyxNQUFJLE9BQU8sR0FBRyxFQUFFLENBQUM7Ozs7OztBQUNqQix1Q0FBaUIsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsaUhBQUU7VUFBNUIsSUFBSTs7QUFDWCxVQUFJLElBQUksQ0FBQyxNQUFNLEdBQUcsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyxHQUFHLEVBQUU7O0FBRXRDLGVBQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQztPQUM3QztLQUNGOzs7Ozs7Ozs7Ozs7Ozs7O0FBQ0QsU0FBTyxPQUFPLENBQUM7Q0FDaEI7O0FBRUQsU0FBUyxZQUFZLEdBQUk7QUFDdkIsTUFBTSxHQUFHLEdBQUcsa0JBQUssR0FBRyxDQUFDO0FBQ3JCLE1BQUksSUFBSSxRQUFNLFdBQVcsRUFBRSxHQUFHLEdBQUcsV0FBTSxHQUFHLFNBQU0sQ0FBQztBQUNqRCxNQUFJLHNCQUFPLFNBQVMsRUFBRSxFQUFFO0FBQ3RCLFFBQUksR0FBRyxJQUFJLEdBQUcsTUFBTSxDQUFDO0dBQ3RCO0FBQ0QsU0FBTyxJQUFJLENBQUM7Q0FDYjs7QUFFRCxTQUFTLFdBQVcsR0FBSTtBQUN0QixNQUFJLE9BQU8sQ0FBQyxHQUFHLENBQUMsU0FBUyxFQUFFO0FBQ3pCLFdBQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUM7R0FDOUI7QUFDRCxRQUFNLElBQUksS0FBSyxDQUFDLHVEQUF1RCxDQUFDLENBQUM7Q0FDMUU7Ozs7O0FBS0QsU0FBUyxtQkFBbUIsQ0FBRSxPQUFPLEVBQUU7QUFDckMsTUFBSSxDQUFDLEdBQUcsMEJBQTBCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ2pELE1BQUksR0FBRyxHQUFHLEFBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxNQUFNLElBQUssS0FBSyxDQUFDO0FBQ3BFLFNBQU8sR0FBRyxDQUFDO0NBQ1o7Ozs7O0FBS0QsU0FBUyx3QkFBd0IsQ0FBRSxPQUFPLEVBQUU7QUFDMUMsTUFBSSxDQUFDLEdBQUcsMkJBQTJCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQ2xELFNBQU8sQUFBQyxDQUFDLElBQUksQ0FBQyxDQUFDLE1BQU0sSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUksSUFBSSxHQUFHLEtBQUssQ0FBQztDQUMvQzs7Ozs7O0FBTUQsU0FBUyxlQUFlLENBQUUsT0FBTyxFQUFFO0FBQ2pDLE1BQUksQ0FBQyxHQUFHLHNCQUFzQixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztBQUM3QyxTQUFPLENBQUMsQ0FBQztBQUNELEdBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLE1BQU0sQUFBQyxJQUFJLEtBQUssQ0FBQztDQUN0RTs7QUFFRCxTQUFTLGFBQWEsQ0FBRSxlQUFlLEVBQUUsUUFBUSxFQUFFO0FBQ2pELE1BQUksR0FBRyxHQUFHLENBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFLLGVBQWUsQ0FBQyxHQUFHLFNBQUksZUFBZSxDQUFDLFFBQVEsQ0FBRyxDQUFDO0FBQzVGLE1BQUksZUFBZSxDQUFDLE9BQU8sSUFBSSxRQUFRLElBQUksRUFBRSxFQUFFO0FBQzdDLE9BQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7R0FDaEI7QUFDRCxNQUFJLGVBQWUsQ0FBQyxNQUFNLEVBQUU7QUFDMUIsT0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsZUFBZSxDQUFDLE1BQU0sQ0FBQyxDQUFDO0dBQ3hDO0FBQ0QsTUFBSSxlQUFlLENBQUMsUUFBUSxFQUFFO0FBQzVCLE9BQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGVBQWUsQ0FBQyxRQUFRLENBQUMsQ0FBQztHQUMxQztBQUNELE1BQUksZUFBZSxDQUFDLEtBQUssRUFBRTtBQUN6QixPQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUM7R0FDdkM7QUFDRCxNQUFJLGVBQWUsQ0FBQyx1QkFBdUIsRUFBRTs7Ozs7Ozs7QUFRM0MsUUFBSSxhQUFhLEdBQUcsU0FBaEIsYUFBYSxDQUFhLEdBQUcsRUFBRTtBQUNqQyxTQUFHLEdBQUcsR0FBRyxDQUFDLElBQUksRUFBRSxDQUFDO0FBQ2pCLFVBQUksS0FBSyxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7QUFDN0IsVUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLEVBQUU7QUFDaEIsZUFBTyxDQUFDLENBQUMsR0FBRyxDQUFDLE1BQU0sR0FBRyxDQUFDLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztPQUNsQyxNQUFNO0FBQ0wsZUFBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsQ0FBQyxFQUFFLEtBQUssQ0FBQyxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsQ0FBQyxTQUFTLENBQUMsS0FBSyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLENBQUM7T0FDMUU7S0FDRixDQUFDOzs7OztBQUtGLFFBQUksdUJBQXVCLFNBQU8sZUFBZSxDQUFDLHVCQUF1QixBQUFFLENBQUM7QUFDNUUsUUFBSSxFQUFFLEdBQUcsaUJBQWlCLENBQUM7QUFDM0IsV0FBTyxJQUFJLEVBQUU7QUFDWCxVQUFJLElBQUksR0FBRyxFQUFFLENBQUMsSUFBSSxDQUFDLHVCQUF1QixDQUFDLENBQUM7QUFDNUMsVUFBSSxDQUFDLElBQUksRUFBRTtBQUNULFlBQUksdUJBQXVCLENBQUMsTUFBTSxFQUFFOztBQUVsQyxhQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsYUFBYSxDQUFDLHVCQUF1QixDQUFDLENBQUMsQ0FBQztTQUM3RDs7QUFFRCxjQUFNO09BQ1A7Ozs7O0FBS0QsVUFBSSxJQUFJLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ25CLFVBQUksT0FBTyxHQUFHLHVCQUF1QixDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUNwRCxVQUFJLE9BQU8sS0FBSyxDQUFDLEVBQUU7QUFDakIsWUFBSSxRQUFRLEdBQUcsdUJBQXVCLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUM3RCxXQUFHLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUUsYUFBYSxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUM7T0FDOUM7OztBQUdELFNBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7OztBQUdmLDZCQUF1QixHQUFHLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUNuQztHQUNGO0FBQ0QsU0FBTyxHQUFHLENBQUM7Q0FDWjs7OztBQUlELFNBQVMsd0JBQXdCLENBQUUsT0FBTyxFQUFFLFlBQVksRUFBRTtBQUN4RCxNQUFJLEtBQUssR0FBRyxDQUFDLFlBQVksQ0FBQyxDQUFDOzs7O0FBSTNCLE1BQUksWUFBWSxDQUFDLE9BQU8sQ0FBSSxPQUFPLE9BQUksS0FBSyxDQUFDLEVBQUU7QUFDN0MsU0FBSyxDQUFDLElBQUksQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0dBQ3BEO0FBQ0QsTUFBSSxZQUFZLENBQUMsQ0FBQyxDQUFDLEtBQUssR0FBRyxFQUFFO0FBQzNCLFNBQUssQ0FBQyxJQUFJLE9BQUssWUFBWSxDQUFHLENBQUM7R0FDaEM7QUFDRCxTQUFPLEtBQUssQ0FBQztDQUNkOztRQUVRLGNBQWMsR0FBZCxjQUFjO1FBQUUseUJBQXlCLEdBQXpCLHlCQUF5QjtRQUFFLFNBQVMsR0FBVCxTQUFTO1FBQUUsZ0JBQWdCLEdBQWhCLGdCQUFnQjtRQUN0RSxvQkFBb0IsR0FBcEIsb0JBQW9CO1FBQUUsWUFBWSxHQUFaLFlBQVk7UUFBRSxtQkFBbUIsR0FBbkIsbUJBQW1CO1FBQUUsd0JBQXdCLEdBQXhCLHdCQUF3QjtRQUNqRixlQUFlLEdBQWYsZUFBZTtRQUFFLGFBQWEsR0FBYixhQUFhO1FBQUUsd0JBQXdCLEdBQXhCLHdCQUF3QjtRQUFFLFdBQVcsR0FBWCxXQUFXO1FBQ3JFLE9BQU8sR0FBUCxPQUFPO1FBQUUsZ0JBQWdCLEdBQWhCLGdCQUFnQiIsImZpbGUiOiJsaWIvaGVscGVycy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBwYXRoIGZyb20gJ3BhdGgnO1xuaW1wb3J0IHsgc3lzdGVtLCBmcyB9IGZyb20gJ2FwcGl1bS1zdXBwb3J0JztcbmltcG9ydCBsb2cgZnJvbSAnLi9sb2dnZXIuanMnO1xuaW1wb3J0IEFkbVppcCBmcm9tICdhZG0temlwJztcbmltcG9ydCB7IGV4ZWMgfSBmcm9tICd0ZWVuX3Byb2Nlc3MnO1xuaW1wb3J0IF8gZnJvbSAnbG9kYXNoJztcblxuXG5jb25zdCByb290RGlyID0gcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgcHJvY2Vzcy5lbnYuTk9fUFJFQ09NUElMRSA/ICcuLicgOiAnLi4vLi4nKTtcbmNvbnN0IGFuZHJvaWRQbGF0Zm9ybXMgPSBbJ2FuZHJvaWQtNC4yJywgJ2FuZHJvaWQtMTcnLCAnYW5kcm9pZC00LjMnLCAnYW5kcm9pZC0xOCcsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICdhbmRyb2lkLTQuNCcsICdhbmRyb2lkLTE5JywgJ2FuZHJvaWQtTCcsICdhbmRyb2lkLTIwJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgJ2FuZHJvaWQtNS4wJywgJ2FuZHJvaWQtMjEnLCAnYW5kcm9pZC0yMicsICdhbmRyb2lkLU1OQycsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICdhbmRyb2lkLTIzJywgJ2FuZHJvaWQtNi4wJ107XG5cbmFzeW5jIGZ1bmN0aW9uIGdldERpcmVjdG9yaWVzIChyb290UGF0aCkge1xuICBsZXQgZmlsZXMgPSBhd2FpdCBmcy5yZWFkZGlyKHJvb3RQYXRoKTtcbiAgbGV0IGRpcnMgPSBbXTtcbiAgZm9yIChsZXQgZmlsZSBvZiBmaWxlcykge1xuICAgIGxldCBwYXRoU3RyaW5nID0gcGF0aC5yZXNvbHZlKHJvb3RQYXRoLCBmaWxlKTtcbiAgICBpZiAoKGF3YWl0IGZzLmxzdGF0KHBhdGhTdHJpbmcpKS5pc0RpcmVjdG9yeSgpKSB7XG4gICAgICBkaXJzLnB1c2goZmlsZSk7XG4gICAgfVxuICB9XG4gIC8vIEl0IGlzIG5vdCBhIGNsZWFuIHdheSB0byBzb3J0IGl0LCBidXQgaW4gdGhpcyBjYXNlIHdvdWxkIHdvcmsgZmluZSBiZWNhdXNlXG4gIC8vIHdlIGhhdmUgbnVtZXJpY3MgYW5kIGFscGhhbnVtZXJpY1xuICAvLyB3aWxsIHJldHVybiBzb21lIHRoaW5nIGxpa2UgdGhpc1xuICAvLyBbXCIxNy4wLjBcIiwgXCIxOC4wLjFcIiwgXCIxOS4wLjBcIiwgXCIxOS4wLjFcIiwgXCIxOS4xLjBcIiwgXCIyMC4wLjBcIixcbiAgLy8gIFwiYW5kcm9pZC00LjIuMlwiLCBcImFuZHJvaWQtNC4zXCIsIFwiYW5kcm9pZC00LjRcIl1cbiAgcmV0dXJuIGRpcnMuc29ydCgpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRBbmRyb2lkUGxhdGZvcm1BbmRQYXRoICgpIHtcbiAgY29uc3QgYW5kcm9pZEhvbWUgPSBwcm9jZXNzLmVudi5BTkRST0lEX0hPTUU7XG4gIGlmICghXy5pc1N0cmluZyhhbmRyb2lkSG9tZSkpIHtcbiAgICBsb2cuZXJyb3IoXCJBTkRST0lEX0hPTUUgd2FzIG5vdCBleHBvcnRlZCFcIik7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cblxuICAvLyBnZXQgdGhlIGxhdGVzdCBwbGF0Zm9ybSBhbmQgcGF0aFxuICBmb3IgKGxldCBwbGF0Zm9ybSBvZiBfLmNsb25lKGFuZHJvaWRQbGF0Zm9ybXMpLnJldmVyc2UoKSkge1xuICAgIGxldCBwbGF0Zm9ybXMgPSBwYXRoLnJlc29sdmUoYW5kcm9pZEhvbWUsICdwbGF0Zm9ybXMnKTtcbiAgICBsZXQgcGxhdGZvcm1QYXRoID0gcGF0aC5yZXNvbHZlKHBsYXRmb3JtcywgcGxhdGZvcm0pO1xuICAgIGlmIChhd2FpdCBmcy5leGlzdHMocGxhdGZvcm1QYXRoKSkge1xuICAgICAgcmV0dXJuIHtwbGF0Zm9ybSwgcGxhdGZvcm1QYXRofTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIG51bGw7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHVuemlwRmlsZSAoemlwUGF0aCkge1xuICBsb2cuZGVidWcoYFVuemlwcGluZyAke3ppcFBhdGh9YCk7XG4gIHRyeSB7XG4gICAgYXdhaXQgYXNzZXJ0WmlwQXJjaGl2ZSh6aXBQYXRoKTtcbiAgICBpZiAoc3lzdGVtLmlzV2luZG93cygpKSB7XG4gICAgICBsZXQgemlwID0gbmV3IEFkbVppcCh6aXBQYXRoKTtcbiAgICAgIHppcC5leHRyYWN0QWxsVG8ocGF0aC5kaXJuYW1lKHppcFBhdGgpLCB0cnVlKTtcbiAgICAgIGxvZy5kZWJ1ZyhcIlVuemlwIHN1Y2Nlc3NmdWxcIik7XG4gICAgfSBlbHNlIHtcbiAgICAgIGF3YWl0IGV4ZWMoJ3VuemlwJywgWyctbycsIHppcFBhdGhdLCB7Y3dkOiBwYXRoLmRpcm5hbWUoemlwUGF0aCl9KTtcbiAgICAgIGxvZy5kZWJ1ZyhcIlVuemlwIHN1Y2Nlc3NmdWxcIik7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBFcnJvciBvY2N1cnJlZCB3aGlsZSB1bnppcHBpbmcuIE9yaWdpbmFsIGVycm9yOiAke2UubWVzc2FnZX1gKTtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBhc3NlcnRaaXBBcmNoaXZlICh6aXBQYXRoKSB7XG4gIGxvZy5kZWJ1ZyhgVGVzdGluZyB6aXAgYXJjaGl2ZTogJHt6aXBQYXRofWApO1xuICBpZiAoc3lzdGVtLmlzV2luZG93cygpKSB7XG4gICAgaWYgKGF3YWl0IGZzLmV4aXN0cyh6aXBQYXRoKSkge1xuICAgICAgbG9nLmRlYnVnKFwiWmlwIGFyY2hpdmUgdGVzdGVkIGNsZWFuXCIpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFppcCBhcmNoaXZlIG5vdCBwcmVzZW50IGF0ICR7emlwUGF0aH1gKTtcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgbGV0IGV4ZWNPcHRzID0ge2N3ZDogcGF0aC5kaXJuYW1lKHppcFBhdGgpfTtcbiAgICBhd2FpdCBleGVjKCd1bnppcCcsIFsnLXRxJywgemlwUGF0aF0sIGV4ZWNPcHRzKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBnZXRJTUVMaXN0RnJvbU91dHB1dCAoc3Rkb3V0KSB7XG4gIGxldCBlbmdpbmVzID0gW107XG4gIGZvciAobGV0IGxpbmUgb2Ygc3Rkb3V0LnNwbGl0KCdcXG4nKSkge1xuICAgIGlmIChsaW5lLmxlbmd0aCA+IDAgJiYgbGluZVswXSAhPT0gJyAnKSB7XG4gICAgICAvLyByZW1vdmUgbmV3bGluZSBhbmQgdHJhaWxpbmcgY29sb24sIGFuZCBhZGQgdG8gdGhlIGxpc3RcbiAgICAgIGVuZ2luZXMucHVzaChsaW5lLnRyaW0oKS5yZXBsYWNlKC86JC8sICcnKSk7XG4gICAgfVxuICB9XG4gIHJldHVybiBlbmdpbmVzO1xufVxuXG5mdW5jdGlvbiBnZXRKYXZhRm9yT3MgKCkge1xuICBjb25zdCBzZXAgPSBwYXRoLnNlcDtcbiAgbGV0IGphdmEgPSBgJHtnZXRKYXZhSG9tZSgpfSR7c2VwfWJpbiR7c2VwfWphdmFgO1xuICBpZiAoc3lzdGVtLmlzV2luZG93cygpKSB7XG4gICAgamF2YSA9IGphdmEgKyAnLmV4ZSc7XG4gIH1cbiAgcmV0dXJuIGphdmE7XG59XG5cbmZ1bmN0aW9uIGdldEphdmFIb21lICgpIHtcbiAgaWYgKHByb2Nlc3MuZW52LkpBVkFfSE9NRSkge1xuICAgIHJldHVybiBwcm9jZXNzLmVudi5KQVZBX0hPTUU7XG4gIH1cbiAgdGhyb3cgbmV3IEVycm9yKFwiSkFWQV9IT01FIGlzIG5vdCBzZXQgY3VycmVudGx5LiBQbGVhc2Ugc2V0IEpBVkFfSE9NRS5cIik7XG59XG5cbi8qXG4gKiBDaGVja3MgbVNob3dpbmdMb2Nrc2NyZWVuIGluIGR1bXBzeXMgb3V0cHV0IHRvIGRldGVybWluZSBpZiBsb2NrIHNjcmVlbiBpcyBzaG93aW5nXG4gKi9cbmZ1bmN0aW9uIGlzU2hvd2luZ0xvY2tzY3JlZW4gKGR1bXBzeXMpIHtcbiAgbGV0IG0gPSAvbVNob3dpbmdMb2Nrc2NyZWVuPVxcdysvZ2kuZXhlYyhkdW1wc3lzKTtcbiAgbGV0IHJldCA9IChtICYmIG0ubGVuZ3RoICYmIG1bMF0uc3BsaXQoJz0nKVsxXSA9PT0gJ3RydWUnKSB8fCBmYWxzZTtcbiAgcmV0dXJuIHJldDtcbn1cblxuLypcbiAqIENoZWNrcyBtQ3VycmVudEZvY3VzIGluIGR1bXBzeXMgb3V0cHV0IHRvIGRldGVybWluZSBpZiBLZXlndWFyZCBpcyBhY3RpdmF0ZWRcbiAqL1xuZnVuY3Rpb24gaXNDdXJyZW50Rm9jdXNPbktleWd1YXJkIChkdW1wc3lzKSB7XG4gIGxldCBtID0gL21DdXJyZW50Rm9jdXMuK0tleWd1YXJkL2dpLmV4ZWMoZHVtcHN5cyk7XG4gIHJldHVybiAobSAmJiBtLmxlbmd0aCAmJiBtWzBdKSA/IHRydWUgOiBmYWxzZTtcbn1cblxuLypcbiAqIENoZWNrcyBtU2NyZWVuT25GdWxseSBpbiBkdW1wc3lzIG91dHB1dCB0byBkZXRlcm1pbmUgaWYgc2NyZWVuIGlzIHNob3dpbmdcbiAqIERlZmF1bHQgaXMgdHJ1ZVxuICovXG5mdW5jdGlvbiBpc1NjcmVlbk9uRnVsbHkgKGR1bXBzeXMpIHtcbiAgbGV0IG0gPSAvbVNjcmVlbk9uRnVsbHk9XFx3Ky9naS5leGVjKGR1bXBzeXMpO1xuICByZXR1cm4gIW0gfHwgLy8gaWYgaW5mb3JtYXRpb24gaXMgbWlzc2luZyB3ZSBhc3N1bWUgc2NyZWVuIGlzIGZ1bGx5IG9uXG4gICAgICAgICAobSAmJiBtLmxlbmd0aCA+IDAgJiYgbVswXS5zcGxpdCgnPScpWzFdID09PSAndHJ1ZScpIHx8IGZhbHNlO1xufVxuXG5mdW5jdGlvbiBidWlsZFN0YXJ0Q21kIChzdGFydEFwcE9wdGlvbnMsIGFwaUxldmVsKSB7XG4gIGxldCBjbWQgPSBbJ2FtJywgJ3N0YXJ0JywgJy1XJywgJy1uJywgYCR7c3RhcnRBcHBPcHRpb25zLnBrZ30vJHtzdGFydEFwcE9wdGlvbnMuYWN0aXZpdHl9YF07XG4gIGlmIChzdGFydEFwcE9wdGlvbnMuc3RvcEFwcCAmJiBhcGlMZXZlbCA+PSAxNSkge1xuICAgIGNtZC5wdXNoKCctUycpO1xuICB9XG4gIGlmIChzdGFydEFwcE9wdGlvbnMuYWN0aW9uKSB7XG4gICAgY21kLnB1c2goJy1hJywgc3RhcnRBcHBPcHRpb25zLmFjdGlvbik7XG4gIH1cbiAgaWYgKHN0YXJ0QXBwT3B0aW9ucy5jYXRlZ29yeSkge1xuICAgIGNtZC5wdXNoKCctYycsIHN0YXJ0QXBwT3B0aW9ucy5jYXRlZ29yeSk7XG4gIH1cbiAgaWYgKHN0YXJ0QXBwT3B0aW9ucy5mbGFncykge1xuICAgIGNtZC5wdXNoKCctZicsIHN0YXJ0QXBwT3B0aW9ucy5mbGFncyk7XG4gIH1cbiAgaWYgKHN0YXJ0QXBwT3B0aW9ucy5vcHRpb25hbEludGVudEFyZ3VtZW50cykge1xuICAgIC8vIGV4cGVjdCBvcHRpb25hbEludGVudEFyZ3VtZW50cyB0byBiZSBhIHNpbmdsZSBzdHJpbmcgb2YgdGhlIGZvcm06XG4gICAgLy8gICAgIFwiLWZsYWcga2V5XCJcbiAgICAvLyAgICAgXCItZmxhZyBrZXkgdmFsdWVcIlxuICAgIC8vIG9yIGEgY29tYmluYXRpb24gb2YgdGhlc2UgKGUuZy4sIFwiLWZsYWcxIGtleTEgLWZsYWcyIGtleTIgdmFsdWUyXCIpXG5cbiAgICAvLyB0YWtlIGEgc3RyaW5nIGFuZCBwYXJzZSBvdXQgdGhlIHBhcnQgYmVmb3JlIGFueSBzcGFjZXMsIGFuZCBhbnl0aGluZyBhZnRlclxuICAgIC8vIHRoZSBmaXJzdCBzcGFjZVxuICAgIGxldCBwYXJzZUtleVZhbHVlID0gZnVuY3Rpb24gKHN0cikge1xuICAgICAgc3RyID0gc3RyLnRyaW0oKTtcbiAgICAgIGxldCBzcGFjZSA9IHN0ci5pbmRleE9mKCcgJyk7XG4gICAgICBpZiAoc3BhY2UgPT09IC0xKSB7XG4gICAgICAgIHJldHVybiAhIXN0ci5sZW5ndGggPyBbc3RyXSA6IFtdO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgcmV0dXJuIFtzdHIuc3Vic3RyaW5nKDAsIHNwYWNlKS50cmltKCksIHN0ci5zdWJzdHJpbmcoc3BhY2UgKyAxKS50cmltKCldO1xuICAgICAgfVxuICAgIH07XG5cbiAgICAvLyBjeWNsZSB0aHJvdWdoIHRoZSBvcHRpb25hbEludGVudEFyZ3VtZW50cyBhbmQgcHVsbCBvdXQgdGhlIGFyZ3VtZW50c1xuICAgIC8vIGFkZCBhIHNwYWNlIGluaXRpYWxseSBzbyBmbGFncyBjYW4gYmUgZGlzdGluZ3Vpc2hlZCBmcm9tIGFyZ3VtZW50cyB0aGF0XG4gICAgLy8gaGF2ZSBpbnRlcm5hbCBoeXBoZW5zXG4gICAgbGV0IG9wdGlvbmFsSW50ZW50QXJndW1lbnRzID0gYCAke3N0YXJ0QXBwT3B0aW9ucy5vcHRpb25hbEludGVudEFyZ3VtZW50c31gO1xuICAgIGxldCByZSA9IC8gKC1bXlxcc10rKSAoLispLztcbiAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgbGV0IGFyZ3MgPSByZS5leGVjKG9wdGlvbmFsSW50ZW50QXJndW1lbnRzKTtcbiAgICAgIGlmICghYXJncykge1xuICAgICAgICBpZiAob3B0aW9uYWxJbnRlbnRBcmd1bWVudHMubGVuZ3RoKSB7XG4gICAgICAgICAgLy8gbm8gbW9yZSBmbGFncywgc28gdGhlIHJlbWFpbmRlciBjYW4gYmUgdHJlYXRlZCBhcyAna2V5JyBvciAna2V5IHZhbHVlJ1xuICAgICAgICAgIGNtZC5wdXNoLmFwcGx5KGNtZCwgcGFyc2VLZXlWYWx1ZShvcHRpb25hbEludGVudEFyZ3VtZW50cykpO1xuICAgICAgICB9XG4gICAgICAgIC8vIHdlIGFyZSBkb25lXG4gICAgICAgIGJyZWFrO1xuICAgICAgfVxuXG4gICAgICAvLyB0YWtlIHRoZSBmbGFnIGFuZCBzZWUgaWYgaXQgaXMgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc3RyaW5nXG4gICAgICAvLyBpZiBpdCBpcyBub3QsIHRoZW4gaXQgbWVhbnMgd2UgaGF2ZSBiZWVuIHRocm91Z2ggYWxyZWFkeSwgYW5kXG4gICAgICAvLyB3aGF0IGlzIGJlZm9yZSB0aGUgZmxhZyBpcyB0aGUgYXJndW1lbnQgZm9yIHRoZSBwcmV2aW91cyBmbGFnXG4gICAgICBsZXQgZmxhZyA9IGFyZ3NbMV07XG4gICAgICBsZXQgZmxhZ1BvcyA9IG9wdGlvbmFsSW50ZW50QXJndW1lbnRzLmluZGV4T2YoZmxhZyk7XG4gICAgICBpZiAoZmxhZ1BvcyAhPT0gMCkge1xuICAgICAgICBsZXQgcHJldkFyZ3MgPSBvcHRpb25hbEludGVudEFyZ3VtZW50cy5zdWJzdHJpbmcoMCwgZmxhZ1Bvcyk7XG4gICAgICAgIGNtZC5wdXNoLmFwcGx5KGNtZCwgcGFyc2VLZXlWYWx1ZShwcmV2QXJncykpO1xuICAgICAgfVxuXG4gICAgICAvLyBhZGQgdGhlIGZsYWcsIGFzIHRoZXJlIGFyZSBubyBtb3JlIGVhcmxpZXIgYXJndW1lbnRzXG4gICAgICBjbWQucHVzaChmbGFnKTtcblxuICAgICAgLy8gbWFrZSBvcHRpb25hbEludGVudEFyZ3VtZW50cyBob2xkIHRoZSByZW1haW5kZXJcbiAgICAgIG9wdGlvbmFsSW50ZW50QXJndW1lbnRzID0gYXJnc1syXTtcbiAgICB9XG4gIH1cbiAgcmV0dXJuIGNtZDtcbn1cblxuLy8gdHVybnMgcGtnLmFjdGl2aXR5Lm5hbWUgdG8gLmFjdGl2aXR5Lm5hbWVcbi8vIGFsc28gdHVybnMgYWN0aXZpdHkubmFtZSB0byAuYWN0aXZpdHkubmFtZVxuZnVuY3Rpb24gZ2V0UG9zc2libGVBY3Rpdml0eU5hbWVzIChwa2dOYW1lLCBhY3Rpdml0eU5hbWUpIHtcbiAgbGV0IG5hbWVzID0gW2FjdGl2aXR5TmFtZV07XG4gIC8vIG5lZWQgdG8gYmV3YXJlIG9mIG5hbWVzcGFjZXMgd2l0aCBvdmVybGFwcGluZyBjaGFyczpcbiAgLy8gICBjb20uZm9vLmJhclxuICAvLyAgIGNvbS5mb28uYmFyeFxuICBpZiAoYWN0aXZpdHlOYW1lLmluZGV4T2YoYCR7cGtnTmFtZX0uYCkgPT09IDApIHtcbiAgICBuYW1lcy5wdXNoKGFjdGl2aXR5TmFtZS5zdWJzdHJpbmcocGtnTmFtZS5sZW5ndGgpKTtcbiAgfVxuICBpZiAoYWN0aXZpdHlOYW1lWzBdICE9PSAnLicpIHtcbiAgICBuYW1lcy5wdXNoKGAuJHthY3Rpdml0eU5hbWV9YCk7XG4gIH1cbiAgcmV0dXJuIG5hbWVzO1xufVxuXG5leHBvcnQgeyBnZXREaXJlY3RvcmllcywgZ2V0QW5kcm9pZFBsYXRmb3JtQW5kUGF0aCwgdW56aXBGaWxlLCBhc3NlcnRaaXBBcmNoaXZlLFxuICAgICAgICAgZ2V0SU1FTGlzdEZyb21PdXRwdXQsIGdldEphdmFGb3JPcywgaXNTaG93aW5nTG9ja3NjcmVlbiwgaXNDdXJyZW50Rm9jdXNPbktleWd1YXJkLFxuICAgICAgICAgaXNTY3JlZW5PbkZ1bGx5LCBidWlsZFN0YXJ0Q21kLCBnZXRQb3NzaWJsZUFjdGl2aXR5TmFtZXMsIGdldEphdmFIb21lLFxuICAgICAgICAgcm9vdERpciwgYW5kcm9pZFBsYXRmb3JtcyB9O1xuIl19