UNPKG

electron-compile

Version:

Electron supporting package to compile JS and CSS in Electron applications

584 lines (466 loc) 43.9 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _stringify = require('babel-runtime/core-js/json/stringify'); var _stringify2 = _interopRequireDefault(_stringify); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs); var _zlib = require('zlib'); var _zlib2 = _interopRequireDefault(_zlib); var _crypto = require('crypto'); var _crypto2 = _interopRequireDefault(_crypto); var _promise = require('./promise'); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _sanitizePaths = require('./sanitize-paths'); var _sanitizePaths2 = _interopRequireDefault(_sanitizePaths); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var d = require('debug')('electron-compile:file-change-cache'); /** * This class caches information about files and determines whether they have * changed contents or not. Most importantly, this class caches the hash of seen * files so that at development time, we don't have to recalculate them constantly. * * This class is also the core of how electron-compile runs quickly in production * mode - after precompilation, the cache is serialized along with the rest of the * data in {@link CompilerHost}, so that when we load the app in production mode, * we don't end up calculating hashes of file content at all, only using the contents * of this cache. */ var FileChangedCache = function () { function FileChangedCache(appRoot) { var failOnCacheMiss = arguments.length <= 1 || arguments[1] === undefined ? false : arguments[1]; (0, _classCallCheck3.default)(this, FileChangedCache); this.appRoot = (0, _sanitizePaths2.default)(appRoot); this.failOnCacheMiss = failOnCacheMiss; this.changeCache = {}; } /** * Allows you to create a FileChangedCache from serialized data saved from * {@link getSavedData}. * * @param {Object} data Saved data from getSavedData. * * @param {string} appRoot The top-level directory for your application (i.e. * the one which has your package.json). * * @param {boolean} failOnCacheMiss (optional) If True, cache misses will throw. * * @return {FileChangedCache} */ (0, _createClass3.default)(FileChangedCache, [{ key: 'getHashForPath', /** * Returns information about a given file, including its hash. This method is * the main method for this cache. * * @param {string} absoluteFilePath The path to a file to retrieve info on. * * @return {Promise<Object>} * * @property {string} hash The SHA1 hash of the file * @property {boolean} isMinified True if the file is minified * @property {boolean} isInNodeModules True if the file is in a library directory * @property {boolean} hasSourceMap True if the file has a source map * @property {boolean} isFileBinary True if the file is not a text file * @property {Buffer} binaryData (optional) The buffer that was read if the file * was binary and there was a cache miss. * @property {string} code (optional) The string that was read if the file * was text and there was a cache miss */ value: function () { var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(absoluteFilePath) { var cacheKey, cacheEntry, stat, ctime, size, _ref, digest, sourceCode, binaryData, info; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: cacheKey = (0, _sanitizePaths2.default)(absoluteFilePath); if (this.appRoot) { cacheKey = cacheKey.replace(this.appRoot, ''); } // NB: We do this because x-require will include an absolute path from the // original built app and we need to still grok it if (this.originalAppRoot) { cacheKey = cacheKey.replace(this.originalAppRoot, ''); } cacheEntry = this.changeCache[cacheKey]; if (!this.failOnCacheMiss) { _context.next = 10; break; } if (cacheEntry) { _context.next = 9; break; } d('Tried to read file cache entry for ' + absoluteFilePath); d('cacheKey: ' + cacheKey + ', appRoot: ' + this.appRoot + ', originalAppRoot: ' + this.originalAppRoot); throw new Error('Asked for ' + absoluteFilePath + ' but it was not precompiled!'); case 9: return _context.abrupt('return', cacheEntry.info); case 10: _context.next = 12; return _promise.pfs.stat(absoluteFilePath); case 12: stat = _context.sent; ctime = stat.ctime.getTime(); size = stat.size; if (!(!stat || !stat.isFile())) { _context.next = 17; break; } throw new Error('Can\'t stat ' + absoluteFilePath); case 17: if (!cacheEntry) { _context.next = 22; break; } if (!(cacheEntry.ctime >= ctime && cacheEntry.size === size)) { _context.next = 20; break; } return _context.abrupt('return', cacheEntry.info); case 20: d('Invalidating cache entry: ' + cacheEntry.ctime + ' === ' + ctime + ' && ' + cacheEntry.size + ' === ' + size); delete this.changeCache.cacheEntry; case 22: _context.next = 24; return this.calculateHashForFile(absoluteFilePath); case 24: _ref = _context.sent; digest = _ref.digest; sourceCode = _ref.sourceCode; binaryData = _ref.binaryData; info = { hash: digest, isMinified: FileChangedCache.contentsAreMinified(sourceCode || ''), isInNodeModules: FileChangedCache.isInNodeModules(absoluteFilePath), hasSourceMap: FileChangedCache.hasSourceMap(sourceCode || ''), isFileBinary: !!binaryData }; this.changeCache[cacheKey] = { ctime: ctime, size: size, info: info }; d('Cache entry for ' + cacheKey + ': ' + (0, _stringify2.default)(this.changeCache[cacheKey])); if (!binaryData) { _context.next = 35; break; } return _context.abrupt('return', _lodash2.default.extend({ binaryData: binaryData }, info)); case 35: return _context.abrupt('return', _lodash2.default.extend({ sourceCode: sourceCode }, info)); case 36: case 'end': return _context.stop(); } } }, _callee, this); })); return function getHashForPath(_x2) { return ref.apply(this, arguments); }; }() /** * Returns data that can passed to {@link loadFromData} to rehydrate this cache. * * @return {Object} */ }, { key: 'getSavedData', value: function getSavedData() { return { changeCache: this.changeCache, appRoot: this.appRoot }; } /** * Serializes this object's data to a file. * * @param {string} filePath The path to save data to. * * @return {Promise} Completion. */ }, { key: 'save', value: function () { var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee2(filePath) { var toSave, buf; return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: toSave = this.getSavedData(); _context2.next = 3; return _promise.pzlib.gzip(new Buffer((0, _stringify2.default)(toSave))); case 3: buf = _context2.sent; _context2.next = 6; return _promise.pfs.writeFile(filePath, buf); case 6: case 'end': return _context2.stop(); } } }, _callee2, this); })); return function save(_x3) { return ref.apply(this, arguments); }; }() }, { key: 'calculateHashForFile', value: function () { var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee3(absoluteFilePath) { var buf, encoding, _digest, sourceCode, digest; return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: _context3.next = 2; return _promise.pfs.readFile(absoluteFilePath); case 2: buf = _context3.sent; encoding = FileChangedCache.detectFileEncoding(buf); if (encoding) { _context3.next = 7; break; } _digest = _crypto2.default.createHash('sha1').update(buf).digest('hex'); return _context3.abrupt('return', { sourceCode: null, digest: _digest, binaryData: buf }); case 7: _context3.next = 9; return _promise.pfs.readFile(absoluteFilePath, encoding); case 9: sourceCode = _context3.sent; digest = _crypto2.default.createHash('sha1').update(sourceCode, 'utf8').digest('hex'); return _context3.abrupt('return', { sourceCode: sourceCode, digest: digest, binaryData: null }); case 12: case 'end': return _context3.stop(); } } }, _callee3, this); })); return function calculateHashForFile(_x4) { return ref.apply(this, arguments); }; }() }, { key: 'getHashForPathSync', value: function getHashForPathSync(absoluteFilePath) { var cacheKey = (0, _sanitizePaths2.default)(absoluteFilePath); if (this.appRoot) { cacheKey = cacheKey.replace(this.appRoot, ''); } // NB: We do this because x-require will include an absolute path from the // original built app and we need to still grok it if (this.originalAppRoot) { cacheKey = cacheKey.replace(this.originalAppRoot, ''); } if (this.realAppRoot) { cacheKey = cacheKey.replace(this.realAppRoot, ''); } var cacheEntry = this.changeCache[cacheKey]; if (this.failOnCacheMiss) { if (!cacheEntry) { d('Tried to read file cache entry for ' + absoluteFilePath); d('cacheKey: ' + cacheKey + ', appRoot: ' + this.appRoot + ', originalAppRoot: ' + this.originalAppRoot); throw new Error('Asked for ' + absoluteFilePath + ' but it was not precompiled!'); } return cacheEntry.info; } var stat = _fs2.default.statSync(absoluteFilePath); var ctime = stat.ctime.getTime(); var size = stat.size; if (!stat || !stat.isFile()) throw new Error('Can\'t stat ' + absoluteFilePath); if (cacheEntry) { if (cacheEntry.ctime >= ctime && cacheEntry.size === size) { return cacheEntry.info; } d('Invalidating cache entry: ' + cacheEntry.ctime + ' === ' + ctime + ' && ' + cacheEntry.size + ' === ' + size); delete this.changeCache.cacheEntry; } var _calculateHashForFile = this.calculateHashForFileSync(absoluteFilePath); var digest = _calculateHashForFile.digest; var sourceCode = _calculateHashForFile.sourceCode; var binaryData = _calculateHashForFile.binaryData; var info = { hash: digest, isMinified: FileChangedCache.contentsAreMinified(sourceCode || ''), isInNodeModules: FileChangedCache.isInNodeModules(absoluteFilePath), hasSourceMap: FileChangedCache.hasSourceMap(sourceCode || ''), isFileBinary: !!binaryData }; this.changeCache[cacheKey] = { ctime: ctime, size: size, info: info }; d('Cache entry for ' + cacheKey + ': ' + (0, _stringify2.default)(this.changeCache[cacheKey])); if (binaryData) { return _lodash2.default.extend({ binaryData: binaryData }, info); } else { return _lodash2.default.extend({ sourceCode: sourceCode }, info); } } }, { key: 'saveSync', value: function saveSync(filePath) { var toSave = this.getSavedData(); var buf = _zlib2.default.gzipSync(new Buffer((0, _stringify2.default)(toSave))); _fs2.default.writeFileSync(filePath, buf); } }, { key: 'calculateHashForFileSync', value: function calculateHashForFileSync(absoluteFilePath) { var buf = _fs2.default.readFileSync(absoluteFilePath); var encoding = FileChangedCache.detectFileEncoding(buf); if (!encoding) { var _digest2 = _crypto2.default.createHash('sha1').update(buf).digest('hex'); return { sourceCode: null, digest: _digest2, binaryData: buf }; } var sourceCode = _fs2.default.readFileSync(absoluteFilePath, encoding); var digest = _crypto2.default.createHash('sha1').update(sourceCode, 'utf8').digest('hex'); return { sourceCode: sourceCode, digest: digest, binaryData: null }; } /** * Determines via some statistics whether a file is likely to be minified. * * @private */ }], [{ key: 'loadFromData', value: function loadFromData(data, appRoot) { var failOnCacheMiss = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; var ret = new FileChangedCache(appRoot, failOnCacheMiss); ret.changeCache = data.changeCache; ret.originalAppRoot = data.appRoot; return ret; } /** * Allows you to create a FileChangedCache from serialized data saved from * {@link save}. * * @param {string} file Saved data from save. * * @param {string} appRoot The top-level directory for your application (i.e. * the one which has your package.json). * * @param {boolean} failOnCacheMiss (optional) If True, cache misses will throw. * * @return {Promise<FileChangedCache>} */ }, { key: 'loadFromFile', value: function () { var ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee4(file, appRoot) { var failOnCacheMiss = arguments.length <= 2 || arguments[2] === undefined ? true : arguments[2]; var buf; return _regenerator2.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: d('Loading canned FileChangedCache from ' + file); _context4.next = 3; return _promise.pfs.readFile(file); case 3: buf = _context4.sent; _context4.t0 = FileChangedCache; _context4.t1 = JSON; _context4.next = 8; return _promise.pzlib.gunzip(buf); case 8: _context4.t2 = _context4.sent; _context4.t3 = _context4.t1.parse.call(_context4.t1, _context4.t2); _context4.t4 = appRoot; _context4.t5 = failOnCacheMiss; return _context4.abrupt('return', _context4.t0.loadFromData.call(_context4.t0, _context4.t3, _context4.t4, _context4.t5)); case 13: case 'end': return _context4.stop(); } } }, _callee4, this); })); return function loadFromFile(_x7, _x8) { return ref.apply(this, arguments); }; }() }, { key: 'contentsAreMinified', value: function contentsAreMinified(source) { var length = source.length; if (length > 1024) length = 1024; var newlineCount = 0; // Roll through the characters and determine the average line length for (var i = 0; i < source.length; i++) { if (source[i] === '\n') newlineCount++; } // No Newlines? Any file other than a super small one is minified if (newlineCount === 0) { return length > 80; } var avgLineLength = length / newlineCount; return avgLineLength > 80; } /** * Determines whether a path is in node_modules or the Electron init code * * @private */ }, { key: 'isInNodeModules', value: function isInNodeModules(filePath) { return !!(filePath.match(/node_modules[\\\/]/i) || filePath.match(/atom\.asar/)); } /** * Returns whether a file has an inline source map * * @private */ }, { key: 'hasSourceMap', value: function hasSourceMap(sourceCode) { return sourceCode.lastIndexOf('//# sourceMap') > sourceCode.lastIndexOf('\n'); } /** * Determines the encoding of a file from the two most common encodings by trying * to decode it then looking for encoding errors * * @private */ }, { key: 'detectFileEncoding', value: function detectFileEncoding(buffer) { if (buffer.length < 1) return false; var buf = buffer.length < 4096 ? buffer : buffer.slice(0, 4096); var encodings = ['utf8', 'utf16le']; var encoding = _lodash2.default.find(encodings, function (x) { return !FileChangedCache.containsControlCharacters(buf.toString(x)); }); return encoding; } /** * Determines whether a string is likely to be poorly encoded by looking for * control characters above a certain threshold * * @private */ }, { key: 'containsControlCharacters', value: function containsControlCharacters(str) { var controlCount = 0; var threshold = str.length < 64 ? 2 : 16; for (var i = 0; i < str.length; i++) { var c = str.charCodeAt(i); if (c === 65536 || c < 8) controlCount++; if (controlCount > threshold) return true; } if (controlCount === 0) return false; return controlCount / str.length < 0.02; } }]); return FileChangedCache; }(); exports.default = FileChangedCache; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9maWxlLWNoYW5nZS1jYWNoZS5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQU9BLElBQU0sSUFBSSxRQUFRLE9BQVIsRUFBaUIsb0NBQWpCLENBQUo7Ozs7Ozs7Ozs7Ozs7O0lBYWU7QUFDbkIsV0FEbUIsZ0JBQ25CLENBQVksT0FBWixFQUE0QztRQUF2Qix3RUFBZ0IscUJBQU87d0NBRHpCLGtCQUN5Qjs7QUFDMUMsU0FBSyxPQUFMLEdBQWUsNkJBQWlCLE9BQWpCLENBQWYsQ0FEMEM7O0FBRzFDLFNBQUssZUFBTCxHQUF1QixlQUF2QixDQUgwQztBQUkxQyxTQUFLLFdBQUwsR0FBbUIsRUFBbkIsQ0FKMEM7R0FBNUM7Ozs7Ozs7Ozs7Ozs7Ozs7OzZCQURtQjs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7NEZBcUVFO1lBQ2YsVUFXQSxZQVlBLE1BQ0EsT0FDQSxZQVlDLFFBQVEsWUFBWSxZQUVyQjs7Ozs7O0FBdkNBLDJCQUFXLDZCQUFpQixnQkFBakI7O0FBQ2Ysb0JBQUksS0FBSyxPQUFMLEVBQWM7QUFDaEIsNkJBQVcsU0FBUyxPQUFULENBQWlCLEtBQUssT0FBTCxFQUFjLEVBQS9CLENBQVgsQ0FEZ0I7aUJBQWxCOzs7O0FBTUEsb0JBQUksS0FBSyxlQUFMLEVBQXNCO0FBQ3hCLDZCQUFXLFNBQVMsT0FBVCxDQUFpQixLQUFLLGVBQUwsRUFBc0IsRUFBdkMsQ0FBWCxDQUR3QjtpQkFBMUI7O0FBSUksNkJBQWEsS0FBSyxXQUFMLENBQWlCLFFBQWpCOztxQkFFYixLQUFLLGVBQUw7Ozs7O29CQUNHOzs7OztBQUNILDBEQUF3QyxnQkFBeEM7QUFDQSxpQ0FBZSwyQkFBc0IsS0FBSyxPQUFMLDJCQUFrQyxLQUFLLGVBQUwsQ0FBdkU7c0JBQ00sSUFBSSxLQUFKLGdCQUF1QixpREFBdkI7OztpREFHRCxXQUFXLElBQVg7Ozs7dUJBR1EsYUFBSSxJQUFK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LElBQUksTUFBSixFQUFZLEdBQTlCLEVBQW1DO0FBQ2pDLFlBQUksSUFBSSxJQUFJLFVBQUosQ0FBZSxDQUFmLENBQUosQ0FENkI7QUFFakMsWUFBSSxNQUFNLEtBQU4sSUFBZSxJQUFJLENBQUosRUFBTyxlQUExQjs7QUFFQSxZQUFJLGVBQWUsU0FBZixFQUEwQixPQUFPLElBQVAsQ0FBOUI7T0FKRjs7QUFPQSxVQUFJLGlCQUFpQixDQUFqQixFQUFvQixPQUFPLEtBQVAsQ0FBeEI7QUFDQSxhQUFPLFlBQUMsR0FBZSxJQUFJLE1BQUosR0FBYyxJQUE5QixDQVo2Qjs7O1NBalVuQiIsImZpbGUiOiJmaWxlLWNoYW5nZS1jYWNoZS5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBmcyBmcm9tICdmcyc7XG5pbXBvcnQgemxpYiBmcm9tICd6bGliJztcbmltcG9ydCBjcnlwdG8gZnJvbSAnY3J5cHRvJztcbmltcG9ydCB7cGZzLCBwemxpYn0gZnJvbSAnLi9wcm9taXNlJztcbmltcG9ydCBfIGZyb20gJ2xvZGFzaCc7XG5pbXBvcnQgc2FuaXRpemVGaWxlUGF0aCBmcm9tICcuL3Nhbml0aXplLXBhdGhzJztcblxuY29uc3QgZCA9IHJlcXVpcmUoJ2RlYnVnJykoJ2VsZWN0cm9uLWNvbXBpbGU6ZmlsZS1jaGFuZ2UtY2FjaGUnKTtcblxuLyoqXG4gKiBUaGlzIGNsYXNzIGNhY2hlcyBpbmZvcm1hdGlvbiBhYm91dCBmaWxlcyBhbmQgZGV0ZXJtaW5lcyB3aGV0aGVyIHRoZXkgaGF2ZVxuICogY2hhbmdlZCBjb250ZW50cyBvciBub3QuIE1vc3QgaW1wb3J0YW50bHksIHRoaXMgY2xhc3MgY2FjaGVzIHRoZSBoYXNoIG9mIHNlZW5cbiAqIGZpbGVzIHNvIHRoYXQgYXQgZGV2ZWxvcG1lbnQgdGltZSwgd2UgZG9uJ3QgaGF2ZSB0byByZWNhbGN1bGF0ZSB0aGVtIGNvbnN0YW50bHkuXG4gKlxuICogVGhpcyBjbGFzcyBpcyBhbHNvIHRoZSBjb3JlIG9mIGhvdyBlbGVjdHJvbi1jb21waWxlIHJ1bnMgcXVpY2tseSBpbiBwcm9kdWN0aW9uXG4gKiBtb2RlIC0gYWZ0ZXIgcHJlY29tcGlsYXRpb24sIHRoZSBjYWNoZSBpcyBzZXJpYWxpemVkIGFsb25nIHdpdGggdGhlIHJlc3Qgb2YgdGhlXG4gKiBkYXRhIGluIHtAbGluayBDb21waWxlckhvc3R9LCBzbyB0aGF0IHdoZW4gd2UgbG9hZCB0aGUgYXBwIGluIHByb2R1Y3Rpb24gbW9kZSxcbiAqIHdlIGRvbid0IGVuZCB1cCBjYWxjdWxhdGluZyBoYXNoZXMgb2YgZmlsZSBjb250ZW50IGF0IGFsbCwgb25seSB1c2luZyB0aGUgY29udGVudHNcbiAqIG9mIHRoaXMgY2FjaGUuXG4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEZpbGVDaGFuZ2VkQ2FjaGUge1xuICBjb25zdHJ1Y3RvcihhcHBSb290LCBmYWlsT25DYWNoZU1pc3M9ZmFsc2UpIHtcbiAgICB0aGlzLmFwcFJvb3QgPSBzYW5pdGl6ZUZpbGVQYXRoKGFwcFJvb3QpO1xuXG4gICAgdGhpcy5mYWlsT25DYWNoZU1pc3MgPSBmYWlsT25DYWNoZU1pc3M7XG4gICAgdGhpcy5jaGFuZ2VDYWNoZSA9IHt9O1xuICB9XG5cbiAgLyoqXG4gICAqIEFsbG93cyB5b3UgdG8gY3JlYXRlIGEgRmlsZUNoYW5nZWRDYWNoZSBmcm9tIHNlcmlhbGl6ZWQgZGF0YSBzYXZlZCBmcm9tXG4gICAqIHtAbGluayBnZXRTYXZlZERhdGF9LlxuICAgKlxuICAgKiBAcGFyYW0gIHtPYmplY3R9IGRhdGEgIFNhdmVkIGRhdGEgZnJvbSBnZXRTYXZlZERhdGEuXG4gICAqXG4gICAqIEBwYXJhbSAge3N0cmluZ30gYXBwUm9vdCAgVGhlIHRvcC1sZXZlbCBkaXJlY3RvcnkgZm9yIHlvdXIgYXBwbGljYXRpb24gKGkuZS5cbiAgICogICAgICAgICAgICAgICAgICAgICAgICAgICB0aGUgb25lIHdoaWNoIGhhcyB5b3VyIHBhY2thZ2UuanNvbikuXG4gICAqXG4gICAqIEBwYXJhbSAge2Jvb2xlYW59IGZhaWxPbkNhY2hlTWlzcyAob3B0aW9uYWwpICBJZiBUcnVlLCBjYWNoZSBtaXNzZXMgd2lsbCB0aHJvdy5cbiAgICpcbiAgICogQHJldHVybiB7RmlsZUNoYW5nZWRDYWNoZX1cbiAgICovXG4gIHN0YXRpYyBsb2FkRnJvbURhdGEoZGF0YSwgYXBwUm9vdCwgZmFpbE9uQ2FjaGVNaXNzPXRydWUpIHtcbiAgICBsZXQgcmV0ID0gbmV3IEZpbGVDaGFuZ2VkQ2FjaGUoYXBwUm9vdCwgZmFpbE9uQ2FjaGVNaXNzKTtcbiAgICByZXQuY2hhbmdlQ2FjaGUgPSBkYXRhLmNoYW5nZUNhY2hlO1xuICAgIHJldC5vcmlnaW5hbEFwcFJvb3QgPSBkYXRhLmFwcFJvb3Q7XG5cbiAgICByZXR1cm4gcmV0O1xuICB9XG5cblxuICAvKipcbiAgICogQWxsb3dzIHlvdSB0byBjcmVhdGUgYSBGaWxlQ2hhbmdlZENhY2hlIGZyb20gc2VyaWFsaXplZCBkYXRhIHNhdmVkIGZyb21cbiAgICoge0BsaW5rIHNhdmV9LlxuICAgKlxuICAgKiBAcGFyYW0gIHtzdHJpbmd9IGZpbGUgIFNhdmVkIGRhdGEgZnJvbSBzYXZlLlxuICAgKlxuICAgKiBAcGFyYW0gIHtzdHJpbmd9IGFwcFJvb3QgIFRoZSB0b3AtbGV2ZWwgZGlyZWN0b3J5IGZvciB5b3VyIGFwcGxpY2F0aW9uIChpLmUuXG4gICAqICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhlIG9uZSB3aGljaCBoYXMgeW91ciBwYWNrYWdlLmpzb24pLlxuICAgKlxuICAgKiBAcGFyYW0gIHtib29sZWFufSBmYWlsT25DYWNoZU1pc3MgKG9wdGlvbmFsKSAgSWYgVHJ1ZSwgY2FjaGUgbWlzc2VzIHdpbGwgdGhyb3cuXG4gICAqXG4gICAqIEByZXR1cm4ge1Byb21pc2U8RmlsZUNoYW5nZWRDYWNoZT59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgbG9hZEZyb21GaWxlKGZpbGUsIGFwcFJvb3QsIGZhaWxPbkNhY2hlTWlzcz10cnVlKSB7XG4gICAgZChgTG9hZGluZyBjYW5uZWQgRmlsZUNoYW5nZWRDYWNoZSBmcm9tICR7ZmlsZX1gKTtcblxuICAgIGxldCBidWYgPSBhd2FpdCBwZnMucmVhZEZpbGUoZmlsZSk7XG4gICAgcmV0dXJuIEZpbGVDaGFuZ2VkQ2FjaGUubG9hZEZyb21EYXRhKEpTT04ucGFyc2UoYXdhaXQgcHpsaWIuZ3VuemlwKGJ1ZikpLCBhcHBSb290LCBmYWlsT25DYWNoZU1pc3MpO1xuICB9XG5cblxuICAvKipcbiAgICogUmV0dXJucyBpbmZvcm1hdGlvbiBhYm91dCBhIGdpdmVuIGZpbGUsIGluY2x1ZGluZyBpdHMgaGFzaC4gVGhpcyBtZXRob2QgaXNcbiAgICogdGhlIG1haW4gbWV0aG9kIGZvciB0aGlzIGNhY2hlLlxuICAgKlxuICAgKiBAcGFyYW0gIHtzdHJpbmd9IGFic29sdXRlRmlsZVBhdGggIFRoZSBwYXRoIHRvIGEgZmlsZSB0byByZXRyaWV2ZSBpbmZvIG9uLlxuICAgKlxuICAgKiBAcmV0dXJuIHtQcm9taXNlPE9iamVjdD59XG4gICAqXG4gICAqIEBwcm9wZXJ0eSB7c3RyaW5nfSBoYXNoICBUaGUgU0hBMSBoYXNoIG9mIHRoZSBmaWxlXG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gaXNNaW5pZmllZCAgVHJ1ZSBpZiB0aGUgZmlsZSBpcyBtaW5pZmllZFxuICAgKiBAcHJvcGVydHkge2Jvb2xlYW59IGlzSW5Ob2RlTW9kdWxlcyAgVHJ1ZSBpZiB0aGUgZmlsZSBpcyBpbiBhIGxpYnJhcnkgZGlyZWN0b3J5XG4gICAqIEBwcm9wZXJ0eSB7Ym9vbGVhbn0gaGFzU291cmNlTWFwICBUcnVlIGlmIHRoZSBmaWxlIGhhcyBhIHNvdXJjZSBtYXBcbiAgICogQHByb3BlcnR5IHtib29sZWFufSBpc0ZpbGVCaW5hcnkgIFRydWUgaWYgdGhlIGZpbGUgaXMgbm90IGEgdGV4dCBmaWxlXG4gICAqIEBwcm9wZXJ0eSB7QnVmZmVyfSBiaW5hcnlEYXRhIChvcHRpb25hbCkgIFRoZSBidWZmZXIgdGhhdCB3YXMgcmVhZCBpZiB0aGUgZmlsZVxuICAgKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3YXMgYmluYXJ5IGFuZCB0aGVyZSB3YXMgYSBjYWNoZSBtaXNzLlxuICAgKiBAcHJvcGVydHkge3N0cmluZ30gY29kZSAob3B0aW9uYWwpICBUaGUgc3RyaW5nIHRoYXQgd2FzIHJlYWQgaWYgdGhlIGZpbGVcbiAgICogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2FzIHRleHQgYW5kIHRoZXJlIHdhcyBhIGNhY2hlIG1pc3NcbiAgICovXG4gIGFzeW5jIGdldEhhc2hGb3JQYXRoKGFic29sdXRlRmlsZVBhdGgpIHtcbiAgICBsZXQgY2FjaGVLZXkgPSBzYW5pdGl6ZUZpbGVQYXRoKGFic29sdXRlRmlsZVBhdGgpO1xuICAgIGlmICh0aGlzLmFwcFJvb3QpIHtcbiAgICAgIGNhY2hlS2V5ID0gY2FjaGVLZXkucmVwbGFjZSh0aGlzLmFwcFJvb3QsICcnKTtcbiAgICB9XG5cbiAgICAvLyBOQjogV2UgZG8gdGhpcyBiZWNhdXNlIHgtcmVxdWlyZSB3aWxsIGluY2x1ZGUgYW4gYWJzb2x1dGUgcGF0aCBmcm9tIHRoZVxuICAgIC8vIG9yaWdpbmFsIGJ1aWx0IGFwcCBhbmQgd2UgbmVlZCB0byBzdGlsbCBncm9rIGl0XG4gICAgaWYgKHRoaXMub3JpZ2luYWxBcHBSb290KSB7XG4gICAgICBjYWNoZUtleSA9IGNhY2hlS2V5LnJlcGxhY2UodGhpcy5vcmlnaW5hbEFwcFJvb3QsICcnKTtcbiAgICB9XG5cbiAgICBsZXQgY2FjaGVFbnRyeSA9IHRoaXMuY2hhbmdlQ2FjaGVbY2FjaGVLZXldO1xuXG4gICAgaWYgKHRoaXMuZmFpbE9uQ2FjaGVNaXNzKSB7XG4gICAgICBpZiAoIWNhY2hlRW50cnkpIHtcbiAgICAgICAgZChgVHJpZWQgdG8gcmVhZCBmaWxlIGNhY2hlIGVudHJ5IGZvciAke2Fic29sdXRlRmlsZVBhdGh9YCk7XG4gICAgICAgIGQoYGNhY2hlS2V5OiAke2NhY2hlS2V5fSwgYXBwUm9vdDogJHt0aGlzLmFwcFJvb3R9LCBvcmlnaW5hbEFwcFJvb3Q6ICR7dGhpcy5vcmlnaW5hbEFwcFJvb3R9YCk7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihgQXNrZWQgZm9yICR7YWJzb2x1dGVGaWxlUGF0aH0gYnV0IGl0IHdhcyBub3QgcHJlY29tcGlsZWQhYCk7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBjYWNoZUVudHJ5LmluZm87XG4gICAgfVxuXG4gICAgbGV0IHN0YXQgPSBhd2FpdCBwZnMuc3RhdChhYnNvbHV0ZUZpbGVQYXRoKTtcbiAgICBsZXQgY3RpbWUgPSBzdGF0LmN0aW1lLmdldFRpbWUoKTtcbiAgICBsZXQgc2l6ZSA9IHN0YXQuc2l6ZTtcbiAgICBpZiAoIXN0YXQgfHwgIXN0YXQuaXNGaWxlKCkpIHRocm93IG5ldyBFcnJvcihgQ2FuJ3Qgc3RhdCAke2Fic29sdXRlRmlsZVBhdGh9YCk7XG5cbiAgICBpZiAoY2FjaGVFbnRyeSkge1xuICAgICAgaWYgKGNhY2hlRW50cnkuY3RpbWUgPj0gY3RpbWUgJiYgY2FjaGVFbnRyeS5zaXplID09PSBzaXplKSB7XG4gICAgICAgIHJldHVybiBjYWNoZUVudHJ5LmluZm87XG4gICAgICB9XG5cbiAgICAgIGQoYEludmFsaWRhdGluZyBjYWNoZSBlbnRyeTogJHtjYWNoZUVudHJ5LmN0aW1lfSA9PT0gJHtjdGltZX0gJiYgJHtjYWNoZUVudHJ5LnNpemV9ID09PSAke3NpemV9YCk7XG4gICAgICBkZWxldGUgdGhpcy5jaGFuZ2VDYWNoZS5jYWNoZUVudHJ5O1xuICAgIH1cblxuICAgIGxldCB7ZGlnZXN0LCBzb3VyY2VDb2RlLCBiaW5hcnlEYXRhfSA9IGF3YWl0IHRoaXMuY2FsY3VsYXRlSGFzaEZvckZpbGUoYWJzb2x1dGVGaWxlUGF0aCk7XG5cbiAgICBsZXQgaW5mbyA9IHtcbiAgICAgIGhhc2g6IGRpZ2VzdCxcbiAgICAgIGlzTWluaWZpZWQ6IEZpbGVDaGFuZ2VkQ2FjaGUuY29udGVudHNBcmVNaW5pZmllZChzb3VyY2VDb2RlIHx8ICcnKSxcbiAgICAgIGlzSW5Ob2RlTW9kdWxlczogRmlsZUNoYW5nZWRDYWNoZS5pc0luTm9kZU1vZHVsZXMoYWJzb2x1dGVGaWxlUGF0aCksXG4gICAgICBoYXNTb3VyY2VNYXA6IEZpbGVDaGFuZ2VkQ2FjaGUuaGFzU291cmNlTWFwKHNvdXJjZUNvZGUgfHwgJycpLFxuICAgICAgaXNGaWxlQmluYXJ5OiAhIWJpbmFyeURhdGFcbiAgICB9O1xuXG4gICAgdGhpcy5jaGFuZ2VDYWNoZVtjYWNoZUtleV0gPSB7IGN0aW1lLCBzaXplLCBpbmZvIH07XG4gICAgZChgQ2FjaGUgZW50cnkgZm9yICR7Y2FjaGVLZXl9OiAke0pTT04uc3RyaW5naWZ5KHRoaXMuY2hhbmdlQ2FjaGVbY2FjaGVLZXldKX1gKTtcblxuICAgIGlmIChiaW5hcnlEYXRhKSB7XG4gICAgICByZXR1cm4gXy5leHRlbmQoe2JpbmFyeURhdGF9LCBpbmZvKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIF8uZXh0ZW5kKHtzb3VyY2VDb2RlfSwgaW5mbyk7XG4gICAgfVxuICB9XG5cblxuICAvKipcbiAgICogUmV0dXJucyBkYXRhIHRoYXQgY2FuIHBhc3NlZCB0byB7QGxpbmsgbG9hZEZyb21EYXRhfSB0byByZWh5ZHJhdGUgdGhpcyBjYWNoZS5cbiAgICpcbiAgICogQHJldHVybiB7T2JqZWN0fVxuICAgKi9cbiAgZ2V0U2F2ZWREYXRhKCkge1xuICAgIHJldHVybiB7IGNoYW5nZUNhY2hlOiB0aGlzLmNoYW5nZUNhY2hlLCBhcHBSb290OiB0aGlzLmFwcFJvb3QgfTtcbiAgfVxuXG4gIC8qKlxuICAgKiBTZXJpYWxpemVzIHRoaXMgb2JqZWN0J3MgZGF0YSB0byBhIGZpbGUuXG4gICAqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBmaWxlUGF0aCAgVGhlIHBhdGggdG8gc2F2ZSBkYXRhIHRvLlxuICAgKlxuICAgKiBAcmV0dXJuIHtQcm9taXNlfSBDb21wbGV0aW9uLlxuICAgKi9cbiAgYXN5bmMgc2F2ZShmaWxlUGF0aCkge1xuICAgIGxldCB0b1NhdmUgPSB0aGlzLmdldFNhdmVkRGF0YSgpO1xuXG4gICAgbGV0IGJ1ZiA9IGF3YWl0IHB6bGliLmd6aXAobmV3IEJ1ZmZlcihKU09OLnN0cmluZ2lmeSh0b1NhdmUpKSk7XG4gICAgYXdhaXQgcGZzLndyaXRlRmlsZShmaWxlUGF0aCwgYnVmKTtcbiAgfVxuXG4gIGFzeW5jIGNhbGN1bGF0ZUhhc2hGb3JGaWxlKGFic29sdXRlRmlsZVBhdGgpIHtcbiAgICBsZXQgYnVmID0gYXdhaXQgcGZzLnJlYWRGaWxlKGFic29sdXRlRmlsZVBhdGgpO1xuICAgIGxldCBlbmNvZGluZyA9IEZpbGVDaGFuZ2VkQ2FjaGUuZGV0ZWN0RmlsZUVuY29kaW5nKGJ1Zik7XG5cbiAgICBpZiAoIWVuY29kaW5nKSB7XG4gICAgICBsZXQgZGlnZXN0ID0gY3J5cHRvLmNyZWF0ZUhhc2goJ3NoYTEnKS51cGRhdGUoYnVmKS5kaWdlc3QoJ2hleCcpO1xuICAgICAgcmV0dXJuIHsgc291cmNlQ29kZTogbnVsbCwgZGlnZXN0LCBiaW5hcnlEYXRhOiBidWYgfTtcbiAgICB9XG5cbiAgICBsZXQgc291cmNlQ29kZSA9IGF3YWl0IHBmcy5yZWFkRmlsZShhYnNvbHV0ZUZpbGVQYXRoLCBlbmNvZGluZyk7XG4gICAgbGV0IGRpZ2VzdCA9IGNyeXB0by5jcmVhdGVIYXNoKCdzaGExJykudXBkYXRlKHNvdXJjZUNvZGUsICd1dGY4JykuZGlnZXN0KCdoZXgnKTtcblxuICAgIHJldHVybiB7c291cmNlQ29kZSwgZGlnZXN0LCBiaW5hcnlEYXRhOiBudWxsIH07XG4gIH1cblxuICBnZXRIYXNoRm9yUGF0aFN5bmMoYWJzb2x1dGVGaWxlUGF0aCkge1xuICAgIGxldCBjYWNoZUtleSA9IHNhbml0aXplRmlsZVBhdGgoYWJzb2x1dGVGaWxlUGF0aCk7XG4gICAgaWYgKHRoaXMuYXBwUm9vdCkge1xuICAgICAgY2FjaGVLZXkgPSBjYWNoZUtleS5yZXBsYWNlKHRoaXMuYXBwUm9vdCwgJycpO1xuICAgIH1cblxuICAgIC8vIE5COiBXZSBkbyB0aGlzIGJlY2F1c2UgeC1yZXF1aXJlIHdpbGwgaW5jbHVkZSBhbiBhYnNvbHV0ZSBwYXRoIGZyb20gdGhlXG4gICAgLy8gb3JpZ2luYWwgYnVpbHQgYXBwIGFuZCB3ZSBuZWVkIHRvIHN0aWxsIGdyb2sgaXRcbiAgICBpZiAodGhpcy5vcmlnaW5hbEFwcFJvb3QpIHtcbiAgICAgIGNhY2hlS2V5ID0gY2FjaGVLZXkucmVwbGFjZSh0aGlzLm9yaWdpbmFsQXBwUm9vdCwgJycpO1xuICAgIH1cblxuICAgIGlmICh0aGlzLnJlYWxBcHBSb290KSB7XG4gICAgICBjYWNoZUtleSA9IGNhY2hlS2V5LnJlcGxhY2UodGhpcy5yZWFsQXBwUm9vdCwgJycpO1xuICAgIH1cblxuICAgIGxldCBjYWNoZUVudHJ5ID0gdGhpcy5jaGFuZ2VDYWNoZVtjYWNoZUtleV07XG5cbiAgICBpZiAodGhpcy5mYWlsT25DYWNoZU1pc3MpIHtcbiAgICAgIGlmICghY2FjaGVFbnRyeSkge1xuICAgICAgICBkKGBUcmllZCB0byByZWFkIGZpbGUgY2FjaGUgZW50cnkgZm9yICR7YWJzb2x1dGVGaWxlUGF0aH1gKTtcbiAgICAgICAgZChgY2FjaGVLZXk6ICR7Y2FjaGVLZXl9LCBhcHBSb290OiAke3RoaXMuYXBwUm9vdH0sIG9yaWdpbmFsQXBwUm9vdDogJHt0aGlzLm9yaWdpbmFsQXBwUm9vdH1gKTtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBBc2tlZCBmb3IgJHthYnNvbHV0ZUZpbGVQYXRofSBidXQgaXQgd2FzIG5vdCBwcmVjb21waWxlZCFgKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIGNhY2hlRW50cnkuaW5mbztcbiAgICB9XG5cbiAgICBsZXQgc3RhdCA9IGZzLnN0YXRTeW5jKGFic29sdXRlRmlsZVBhdGgpO1xuICAgIGxldCBjdGltZSA9IHN0YXQuY3RpbWUuZ2V0VGltZSgpO1xuICAgIGxldCBzaXplID0gc3RhdC5zaXplO1xuICAgIGlmICghc3RhdCB8fCAhc3RhdC5pc0ZpbGUoKSkgdGhyb3cgbmV3IEVycm9yKGBDYW4ndCBzdGF0ICR7YWJzb2x1dGVGaWxlUGF0aH1gKTtcblxuICAgIGlmIChjYWNoZUVudHJ5KSB7XG4gICAgICBpZiAoY2FjaGVFbnRyeS5jdGltZSA+PSBjdGltZSAmJiBjYWNoZUVudHJ5LnNpemUgPT09IHNpemUpIHtcbiAgICAgICAgcmV0dXJuIGNhY2hlRW50cnkuaW5mbztcbiAgICAgIH1cblxuICAgICAgZChgSW52YWxpZGF0aW5nIGNhY2hlIGVudHJ5OiAke2NhY2hlRW50cnkuY3RpbWV9ID09PSAke2N0aW1lfSAmJiAke2NhY2hlRW50cnkuc2l6ZX0gPT09ICR7c2l6ZX1gKTtcbiAgICAgIGRlbGV0ZSB0aGlzLmNoYW5nZUNhY2hlLmNhY2hlRW50cnk7XG4gICAgfVxuXG4gICAgbGV0IHtkaWdlc3QsIHNvdXJjZUNvZGUsIGJpbmFyeURhdGF9ID0gdGhpcy5jYWxjdWxhdGVIYXNoRm9yRmlsZVN5bmMoYWJzb2x1dGVGaWxlUGF0aCk7XG5cbiAgICBsZXQgaW5mbyA9IHtcbiAgICAgIGhhc2g6IGRpZ2VzdCxcbiAgICAgIGlzTWluaWZpZWQ6IEZpbGVDaGFuZ2VkQ2FjaGUuY29udGVudHNBcmVNaW5pZmllZChzb3VyY2VDb2RlIHx8ICcnKSxcbiAgICAgIGlzSW5Ob2RlTW9kdWxlczogRmlsZUNoYW5nZWRDYWNoZS5pc0luTm9kZU1vZHVsZXMoYWJzb2x1dGVGaWxlUGF0aCksXG4gICAgICBoYXNTb3VyY2VNYXA6IEZpbGVDaGFuZ2VkQ2FjaGUuaGFzU291cmNlTWFwKHNvdXJjZUNvZGUgfHwgJycpLFxuICAgICAgaXNGaWxlQmluYXJ5OiAhIWJpbmFyeURhdGFcbiAgICB9O1xuXG4gICAgdGhpcy5jaGFuZ2VDYWNoZVtjYWNoZUtleV0gPSB7IGN0aW1lLCBzaXplLCBpbmZvIH07XG4gICAgZChgQ2FjaGUgZW50cnkgZm9yICR7Y2FjaGVLZXl9OiAke0pTT04uc3RyaW5naWZ5KHRoaXMuY2hhbmdlQ2FjaGVbY2FjaGVLZXldKX1gKTtcblxuICAgIGlmIChiaW5hcnlEYXRhKSB7XG4gICAgICByZXR1cm4gXy5leHRlbmQoe2JpbmFyeURhdGF9LCBpbmZvKTtcbiAgICB9IGVsc2Uge1xuICAgICAgcmV0dXJuIF8uZXh0ZW5kKHtzb3VyY2VDb2RlfSwgaW5mbyk7XG4gICAgfVxuICB9XG5cbiAgc2F2ZVN5bmMoZmlsZVBhdGgpIHtcbiAgICBsZXQgdG9TYXZlID0gdGhpcy5nZXRTYXZlZERhdGEoKTtcblxuICAgIGxldCBidWYgPSB6bGliLmd6aXBTeW5jKG5ldyBCdWZmZXIoSlNPTi5zdHJpbmdpZnkodG9TYXZlKSkpO1xuICAgIGZzLndyaXRlRmlsZVN5bmMoZmlsZVBhdGgsIGJ1Zik7XG4gIH1cblxuICBjYWxjdWxhdGVIYXNoRm9yRmlsZVN5bmMoYWJzb2x1dGVGaWxlUGF0aCkge1xuICAgIGxldCBidWYgPSBmcy5yZWFkRmlsZVN5bmMoYWJzb2x1dGVGaWxlUGF0aCk7XG4gICAgbGV0IGVuY29kaW5nID0gRmlsZUNoYW5nZWRDYWNoZS5kZXRlY3RGaWxlRW5jb2RpbmcoYnVmKTtcblxuICAgIGlmICghZW5jb2RpbmcpIHtcbiAgICAgIGxldCBkaWdlc3QgPSBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMScpLnVwZGF0ZShidWYpLmRpZ2VzdCgnaGV4Jyk7XG4gICAgICByZXR1cm4geyBzb3VyY2VDb2RlOiBudWxsLCBkaWdlc3QsIGJpbmFyeURhdGE6IGJ1Zn07XG4gICAgfVxuXG4gICAgbGV0IHNvdXJjZUNvZGUgPSBmcy5yZWFkRmlsZVN5bmMoYWJzb2x1dGVGaWxlUGF0aCwgZW5jb2RpbmcpO1xuICAgIGxldCBkaWdlc3QgPSBjcnlwdG8uY3JlYXRlSGFzaCgnc2hhMScpLnVwZGF0ZShzb3VyY2VDb2RlLCAndXRmOCcpLmRpZ2VzdCgnaGV4Jyk7XG5cbiAgICByZXR1cm4ge3NvdXJjZUNvZGUsIGRpZ2VzdCwgYmluYXJ5RGF0YTogbnVsbH07XG4gIH1cblxuXG4gIC8qKlxuICAgKiBEZXRlcm1pbmVzIHZpYSBzb21lIHN0YXRpc3RpY3Mgd2hldGhlciBhIGZpbGUgaXMgbGlrZWx5IHRvIGJlIG1pbmlmaWVkLlxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgc3RhdGljIGNvbnRlbnRzQXJlTWluaWZpZWQoc291cmNlKSB7XG4gICAgbGV0IGxlbmd0aCA9IHNvdXJjZS5sZW5ndGg7XG4gICAgaWYgKGxlbmd0aCA+IDEwMjQpIGxlbmd0aCA9IDEwMjQ7XG5cbiAgICBsZXQgbmV3bGluZUNvdW50ID0gMDtcblxuICAgIC8vIFJvbGwgdGhyb3VnaCB0aGUgY2hhcmFjdGVycyBhbmQgZGV0ZXJtaW5lIHRoZSBhdmVyYWdlIGxpbmUgbGVuZ3RoXG4gICAgZm9yKGxldCBpPTA7IGkgPCBzb3VyY2UubGVuZ3RoOyBpKyspIHtcbiAgICAgIGlmIChzb3VyY2VbaV0gPT09ICdcXG4nKSBuZXdsaW5lQ291bnQrKztcbiAgICB9XG5cbiAgICAvLyBObyBOZXdsaW5lcz8gQW55IGZpbGUgb3RoZXIgdGhhbiBhIHN1cGVyIHNtYWxsIG9uZSBpcyBtaW5pZmllZFxuICAgIGlmIChuZXdsaW5lQ291bnQgPT09IDApIHtcbiAgICAgIHJldHVybiAobGVuZ3RoID4gODApO1xuICAgIH1cblxuICAgIGxldCBhdmdMaW5lTGVuZ3RoID0gbGVuZ3RoIC8gbmV3bGluZUNvdW50O1xuICAgIHJldHVybiAoYXZnTGluZUxlbmd0aCA+IDgwKTtcbiAgfVxuXG5cbiAgLyoqXG4gICAqIERldGVybWluZXMgd2hldGhlciBhIHBhdGggaXMgaW4gbm9kZV9tb2R1bGVzIG9yIHRoZSBFbGVjdHJvbiBpbml0IGNvZGVcbiAgICpcbiAgICogQHByaXZhdGVcbiAgICovXG4gIHN0YXRpYyBpc0luTm9kZU1vZHVsZXMoZmlsZVBhdGgpIHtcbiAgICByZXR1cm4gISEoZmlsZVBhdGgubWF0Y2goL25vZGVfbW9kdWxlc1tcXFxcXFwvXS9pKSB8fCBmaWxlUGF0aC5tYXRjaCgvYXRvbVxcLmFzYXIvKSk7XG4gIH1cblxuXG4gIC8qKlxuICAgKiBSZXR1cm5zIHdoZXRoZXIgYSBmaWxlIGhhcyBhbiBpbmxpbmUgc291cmNlIG1hcFxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgc3RhdGljIGhhc1NvdXJjZU1hcChzb3VyY2VDb2RlKSB7XG4gICAgcmV0dXJuIHNvdXJjZUNvZGUubGFzdEluZGV4T2YoJy8vIyBzb3VyY2VNYXAnKSA+IHNvdXJjZUNvZGUubGFzdEluZGV4T2YoJ1xcbicpO1xuICB9XG5cbiAgLyoqXG4gICAqIERldGVybWluZXMgdGhlIGVuY29kaW5nIG9mIGEgZmlsZSBmcm9tIHRoZSB0d28gbW9zdCBjb21tb24gZW5jb2RpbmdzIGJ5IHRyeWluZ1xuICAgKiB0byBkZWNvZGUgaXQgdGhlbiBsb29raW5nIGZvciBlbmNvZGluZyBlcnJvcnNcbiAgICpcbiAgICogQHByaXZhdGVcbiAgICovXG4gIHN0YXRpYyBkZXRlY3RGaWxlRW5jb2RpbmcoYnVmZmVyKSB7XG4gICAgaWYgKGJ1ZmZlci5sZW5ndGggPCAxKSByZXR1cm4gZmFsc2U7XG4gICAgbGV0IGJ1ZiA9IChidWZmZXIubGVuZ3RoIDwgNDA5NiA/IGJ1ZmZlciA6IGJ1ZmZlci5zbGljZSgwLCA0MDk2KSk7XG5cbiAgICBjb25zdCBlbmNvZGluZ3MgPSBbJ3V0ZjgnLCAndXRmMTZsZSddO1xuXG4gICAgbGV0IGVuY29kaW5nID0gXy5maW5kKFxuICAgICAgZW5jb2RpbmdzLFxuICAgICAgKHgpID0+ICFGaWxlQ2hhbmdlZENhY2hlLmNvbnRhaW5zQ29udHJvbENoYXJhY3RlcnMoYnVmLnRvU3RyaW5nKHgpKSk7XG5cbiAgICByZXR1cm4gZW5jb2Rpbmc7XG4gIH1cblxuICAvKipcbiAgICogRGV0ZXJtaW5lcyB3aGV0aGVyIGEgc3RyaW5nIGlzIGxpa2VseSB0byBiZSBwb29ybHkgZW5jb2RlZCBieSBsb29raW5nIGZvclxuICAgKiBjb250cm9sIGNoYXJhY3RlcnMgYWJvdmUgYSBjZXJ0YWluIHRocmVzaG9sZFxuICAgKlxuICAgKiBAcHJpdmF0ZVxuICAgKi9cbiAgc3RhdGljIGNvbnRhaW5zQ29udHJvbENoYXJhY3RlcnMoc3RyKSB7XG4gICAgbGV0IGNvbnRyb2xDb3VudCA9IDA7XG4gICAgbGV0IHRocmVzaG9sZCA9IChzdHIubGVuZ3RoIDwgNjQgPyAyIDogMTYpO1xuXG4gICAgZm9yIChsZXQgaT0wOyBpIDwgc3RyLmxlbmd0aDsgaSsrKSB7XG4gICAgICBsZXQgYyA9IHN0ci5jaGFyQ29kZUF0KGkpO1xuICAgICAgaWYgKGMgPT09IDY1NTM2IHx8IGMgPCA4KSBjb250cm9sQ291bnQrKztcblxuICAgICAgaWYgKGNvbnRyb2xDb3VudCA+IHRocmVzaG9sZCkgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgaWYgKGNvbnRyb2xDb3VudCA9PT0gMCkgcmV0dXJuIGZhbHNlO1xuICAgIHJldHVybiAoY29udHJvbENvdW50IC8gc3RyLmxlbmd0aCkgPCAwLjAyO1xuICB9XG59XG4iXX0=