UNPKG

webdriverio-automation

Version:

WebdriverIO-Automation android ios project

395 lines (324 loc) 61.5 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.cropBase64Image = cropBase64Image; exports.base64ToImage = base64ToImage; exports.imageToBase64 = imageToBase64; exports.cropImage = cropImage; exports.getImagesMatches = getImagesMatches; exports.getImagesSimilarity = getImagesSimilarity; exports.getImageOccurrence = getImageOccurrence; exports.getJimpImage = getJimpImage; exports.MIME_BMP = exports.MIME_PNG = exports.MIME_JPEG = void 0; require("source-map-support/register"); var _lodash = _interopRequireDefault(require("lodash")); var _jimp = _interopRequireDefault(require("jimp")); var _buffer = require("buffer"); var _pngjs = require("pngjs"); var _bluebird = _interopRequireDefault(require("bluebird")); var _util = require("./util"); var _logger = _interopRequireDefault(require("./logger")); var _node = require("./node"); const { MIME_JPEG, MIME_PNG, MIME_BMP } = _jimp.default; exports.MIME_BMP = MIME_BMP; exports.MIME_PNG = MIME_PNG; exports.MIME_JPEG = MIME_JPEG; let cv = null; const BYTES_IN_PIXEL_BLOCK = 4; const SCANLINE_FILTER_METHOD = 4; const DEFAULT_MATCH_THRESHOLD = 0.5; const AVAILABLE_DETECTORS = ['AKAZE', 'AGAST', 'BRISK', 'FAST', 'GFTT', 'KAZE', 'MSER', 'SIFT', 'ORB']; const AVAILABLE_MATCHING_FUNCTIONS = ['FlannBased', 'BruteForce', 'BruteForceL1', 'BruteForceHamming', 'BruteForceHammingLut', 'BruteForceSL2']; async function getJimpImage(data) { return await new _bluebird.default((resolve, reject) => { if (!_lodash.default.isString(data) && !_lodash.default.isBuffer(data)) { return reject(new Error('Must initialize jimp object with string or buffer')); } if (_lodash.default.isString(data)) { data = _buffer.Buffer.from(data, 'base64'); } new _jimp.default(data, (err, imgObj) => { if (err) { return reject(err); } if (!imgObj) { return reject(new Error('Could not create jimp image from that data')); } imgObj._getBuffer = imgObj.getBuffer.bind(imgObj); imgObj.getBuffer = _bluebird.default.promisify(imgObj._getBuffer, { context: imgObj }); resolve(imgObj); }); }); } async function initOpenCV() { if (cv) { return; } _logger.default.debug(`Initializing opencv`); try { cv = await (0, _node.requirePackage)('opencv4nodejs'); } catch (err) { _logger.default.warn(`Unable to load 'opencv4nodejs': ${err.message}`); } if (!cv) { throw new Error(`'opencv4nodejs' module is required to use OpenCV features. ` + `Please install it first ('npm i -g opencv4nodejs') and restart Appium. ` + 'Read https://github.com/justadudewhohacks/opencv4nodejs#how-to-install for more details on this topic.'); } } async function detectAndCompute(img, detector) { const keyPoints = await detector.detectAsync(img); const descriptor = await detector.computeAsync(img, keyPoints); return { keyPoints, descriptor }; } function calculateMatchedRect(matchedPoints) { if (matchedPoints.length < 2) { return { x: 0, y: 0, width: 0, height: 0 }; } const pointsSortedByDistance = matchedPoints.map(point => [Math.sqrt(point.x * point.x + point.y * point.y), point]).sort((pair1, pair2) => pair1[0] >= pair2[0]).map(pair => pair[1]); const firstPoint = _lodash.default.head(pointsSortedByDistance); const lastPoint = _lodash.default.last(pointsSortedByDistance); const topLeftPoint = { x: firstPoint.x <= lastPoint.x ? firstPoint.x : lastPoint.x, y: firstPoint.y <= lastPoint.y ? firstPoint.y : lastPoint.y }; const bottomRightPoint = { x: firstPoint.x >= lastPoint.x ? firstPoint.x : lastPoint.x, y: firstPoint.y >= lastPoint.y ? firstPoint.y : lastPoint.y }; return { x: topLeftPoint.x, y: topLeftPoint.y, width: bottomRightPoint.x - topLeftPoint.x, height: bottomRightPoint.y - topLeftPoint.y }; } function highlightRegion(mat, region) { if (region.width <= 0 || region.height <= 0) { return; } const color = new cv.Vec(0, 0, 255); const thickness = 2; mat.drawRectangle(new cv.Rect(region.x, region.y, region.width, region.height), color, thickness, cv.LINE_8); return mat; } async function getImagesMatches(img1Data, img2Data, options = {}) { await initOpenCV(); const { detectorName = 'ORB', visualize = false, goodMatchesFactor, matchFunc = 'BruteForce' } = options; if (!_lodash.default.includes(AVAILABLE_DETECTORS, detectorName)) { throw new Error(`'${detectorName}' detector is unknown. ` + `Only ${JSON.stringify(AVAILABLE_DETECTORS)} detectors are supported.`); } if (!_lodash.default.includes(AVAILABLE_MATCHING_FUNCTIONS, matchFunc)) { throw new Error(`'${matchFunc}' matching function is unknown. ` + `Only ${JSON.stringify(AVAILABLE_MATCHING_FUNCTIONS)} matching functions are supported.`); } const detector = new cv[`${detectorName}Detector`](); const [img1, img2] = await _bluebird.default.all([cv.imdecodeAsync(img1Data), cv.imdecodeAsync(img2Data)]); const [result1, result2] = await _bluebird.default.all([detectAndCompute(img1, detector), detectAndCompute(img2, detector)]); let matches = []; try { matches = await cv[`match${matchFunc}Async`](result1.descriptor, result2.descriptor); } catch (e) { throw new Error(`Cannot find any matches between the given images. Try another detection algorithm. ` + ` Original error: ${e}`); } const totalCount = matches.length; if ((0, _util.hasValue)(goodMatchesFactor)) { if (_lodash.default.isFunction(goodMatchesFactor)) { const distances = matches.map(match => match.distance); const minDistance = _lodash.default.min(distances); const maxDistance = _lodash.default.max(distances); matches = matches.filter(match => goodMatchesFactor(match.distance, minDistance, maxDistance)); } else { if (matches.length > goodMatchesFactor) { matches = matches.sort((match1, match2) => match1.distance - match2.distance).slice(0, goodMatchesFactor); } } } const extractPoint = (keyPoints, indexPropertyName) => match => { const { pt, point } = keyPoints[match[indexPropertyName]]; return pt || point; }; const points1 = matches.map(extractPoint(result1.keyPoints, 'queryIdx')); const rect1 = calculateMatchedRect(points1); const points2 = matches.map(extractPoint(result2.keyPoints, 'trainIdx')); const rect2 = calculateMatchedRect(points2); const result = { points1, rect1, points2, rect2, totalCount, count: matches.length }; if (visualize) { const visualization = cv.drawMatches(img1, img2, result1.keyPoints, result2.keyPoints, matches); highlightRegion(visualization, rect1); highlightRegion(visualization, { x: img1.cols + rect2.x, y: rect2.y, width: rect2.width, height: rect2.height }); result.visualization = await cv.imencodeAsync('.png', visualization); } return result; } async function getImagesSimilarity(img1Data, img2Data, options = {}) { await initOpenCV(); const { visualize = false } = options; let [template, reference] = await _bluebird.default.all([cv.imdecodeAsync(img1Data), cv.imdecodeAsync(img2Data)]); if (template.rows !== reference.rows || template.cols !== reference.cols) { throw new Error('Both images are expected to have the same size in order to ' + 'calculate the similarity score.'); } [template, reference] = await _bluebird.default.all([template.convertToAsync(cv.CV_8UC3), reference.convertToAsync(cv.CV_8UC3)]); const matched = await reference.matchTemplateAsync(template, cv.TM_CCOEFF_NORMED); const minMax = await matched.minMaxLocAsync(); const result = { score: minMax.maxVal }; if (visualize) { const resultMat = new cv.Mat(template.rows, template.cols * 2, cv.CV_8UC3); await _bluebird.default.all([reference.copyToAsync(resultMat.getRegion(new cv.Rect(0, 0, reference.cols, reference.rows))), template.copyToAsync(resultMat.getRegion(new cv.Rect(reference.cols, 0, template.cols, template.rows)))]); let mask = reference.absdiff(template); mask = await mask.cvtColorAsync(cv.COLOR_BGR2GRAY); let contours = []; try { mask = await mask.thresholdAsync(128, 255, cv.THRESH_BINARY | cv.THRESH_OTSU); contours = await mask.findContoursAsync(cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE); } catch (ign) {} for (const contour of contours) { const boundingRect = contour.boundingRect(); highlightRegion(resultMat, boundingRect); highlightRegion(resultMat, { x: reference.cols + boundingRect.x, y: boundingRect.y, width: boundingRect.width, height: boundingRect.height }); } result.visualization = await cv.imencodeAsync('.png', resultMat); } return result; } async function getImageOccurrence(fullImgData, partialImgData, options = {}) { await initOpenCV(); const { visualize = false, threshold = DEFAULT_MATCH_THRESHOLD } = options; const [fullImg, partialImg] = await _bluebird.default.all([cv.imdecodeAsync(fullImgData), cv.imdecodeAsync(partialImgData)]); const result = {}; try { const matched = await fullImg.matchTemplateAsync(partialImg, cv.TM_CCOEFF_NORMED); const minMax = await matched.minMaxLocAsync(); result.score = minMax.maxVal; if (result.score < threshold) { throw new Error(`Cannot find any occurrences of the partial image in the full ` + `image above the threshold of ${threshold}. Highest match value ` + `found was ${minMax.maxVal}`); } result.rect = { x: minMax.maxLoc.x, y: minMax.maxLoc.y, width: partialImg.cols, height: partialImg.rows }; } catch (e) { throw new Error(`Cannot find any occurrences of the partial image in the full image. ` + `Original error: ${e}`); } if (visualize) { highlightRegion(fullImg, result.rect); result.visualization = await cv.imencodeAsync('.png', fullImg); } return result; } async function cropBase64Image(base64Image, rect) { const image = await base64ToImage(base64Image); cropImage(image, rect); return await imageToBase64(image); } async function base64ToImage(base64Image) { const imageBuffer = _buffer.Buffer.from(base64Image, 'base64'); return await new _bluebird.default((resolve, reject) => { const image = new _pngjs.PNG({ filterType: SCANLINE_FILTER_METHOD }); image.parse(imageBuffer, (err, image) => { if (err) { return reject(err); } resolve(image); }); }); } async function imageToBase64(image) { return await new _bluebird.default((resolve, reject) => { const chunks = []; image.pack().on('data', chunk => chunks.push(chunk)).on('end', () => { resolve(_buffer.Buffer.concat(chunks).toString('base64')); }).on('error', err => { reject(err); }); }); } function cropImage(image, rect) { const imageRect = { width: image.width, height: image.height }; const interRect = getRectIntersection(rect, imageRect); if (interRect.width < rect.width || interRect.height < rect.height) { throw new Error(`Cannot crop ${JSON.stringify(rect)} from ${JSON.stringify(imageRect)} because the intersection between them was not the size of the rect`); } const firstVerticalPixel = interRect.top; const lastVerticalPixel = interRect.top + interRect.height; const firstHorizontalPixel = interRect.left; const lastHorizontalPixel = interRect.left + interRect.width; const croppedArray = []; for (let y = firstVerticalPixel; y < lastVerticalPixel; y++) { for (let x = firstHorizontalPixel; x < lastHorizontalPixel; x++) { const firstByteIdxInPixelBlock = imageRect.width * y + x << 2; for (let byteIdx = 0; byteIdx < BYTES_IN_PIXEL_BLOCK; byteIdx++) { croppedArray.push(image.data[firstByteIdxInPixelBlock + byteIdx]); } } } image.data = _buffer.Buffer.from(croppedArray); image.width = interRect.width; image.height = interRect.height; return image; } function getRectIntersection(rect, imageSize) { const left = rect.left >= imageSize.width ? imageSize.width : rect.left; const top = rect.top >= imageSize.height ? imageSize.height : rect.top; const width = imageSize.width >= left + rect.width ? rect.width : imageSize.width - left; const height = imageSize.height >= top + rect.height ? rect.height : imageSize.height - top; return { left, top, width, height }; }require('source-map-support').install(); //# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi9pbWFnZS11dGlsLmpzIl0sIm5hbWVzIjpbIk1JTUVfSlBFRyIsIk1JTUVfUE5HIiwiTUlNRV9CTVAiLCJKaW1wIiwiY3YiLCJCWVRFU19JTl9QSVhFTF9CTE9DSyIsIlNDQU5MSU5FX0ZJTFRFUl9NRVRIT0QiLCJERUZBVUxUX01BVENIX1RIUkVTSE9MRCIsIkFWQUlMQUJMRV9ERVRFQ1RPUlMiLCJBVkFJTEFCTEVfTUFUQ0hJTkdfRlVOQ1RJT05TIiwiZ2V0SmltcEltYWdlIiwiZGF0YSIsIkIiLCJyZXNvbHZlIiwicmVqZWN0IiwiXyIsImlzU3RyaW5nIiwiaXNCdWZmZXIiLCJFcnJvciIsIkJ1ZmZlciIsImZyb20iLCJlcnIiLCJpbWdPYmoiLCJfZ2V0QnVmZmVyIiwiZ2V0QnVmZmVyIiwiYmluZCIsInByb21pc2lmeSIsImNvbnRleHQiLCJpbml0T3BlbkNWIiwibG9nIiwiZGVidWciLCJ3YXJuIiwibWVzc2FnZSIsImRldGVjdEFuZENvbXB1dGUiLCJpbWciLCJkZXRlY3RvciIsImtleVBvaW50cyIsImRldGVjdEFzeW5jIiwiZGVzY3JpcHRvciIsImNvbXB1dGVBc3luYyIsImNhbGN1bGF0ZU1hdGNoZWRSZWN0IiwibWF0Y2hlZFBvaW50cyIsImxlbmd0aCIsIngiLCJ5Iiwid2lkdGgiLCJoZWlnaHQiLCJwb2ludHNTb3J0ZWRCeURpc3RhbmNlIiwibWFwIiwicG9pbnQiLCJNYXRoIiwic3FydCIsInNvcnQiLCJwYWlyMSIsInBhaXIyIiwicGFpciIsImZpcnN0UG9pbnQiLCJoZWFkIiwibGFzdFBvaW50IiwibGFzdCIsInRvcExlZnRQb2ludCIsImJvdHRvbVJpZ2h0UG9pbnQiLCJoaWdobGlnaHRSZWdpb24iLCJtYXQiLCJyZWdpb24iLCJjb2xvciIsIlZlYyIsInRoaWNrbmVzcyIsImRyYXdSZWN0YW5nbGUiLCJSZWN0IiwiTElORV84IiwiZ2V0SW1hZ2VzTWF0Y2hlcyIsImltZzFEYXRhIiwiaW1nMkRhdGEiLCJvcHRpb25zIiwiZGV0ZWN0b3JOYW1lIiwidmlzdWFsaXplIiwiZ29vZE1hdGNoZXNGYWN0b3IiLCJtYXRjaEZ1bmMiLCJpbmNsdWRlcyIsIkpTT04iLCJzdHJpbmdpZnkiLCJpbWcxIiwiaW1nMiIsImFsbCIsImltZGVjb2RlQXN5bmMiLCJyZXN1bHQxIiwicmVzdWx0MiIsIm1hdGNoZXMiLCJlIiwidG90YWxDb3VudCIsImlzRnVuY3Rpb24iLCJkaXN0YW5jZXMiLCJtYXRjaCIsImRpc3RhbmNlIiwibWluRGlzdGFuY2UiLCJtaW4iLCJtYXhEaXN0YW5jZSIsIm1heCIsImZpbHRlciIsIm1hdGNoMSIsIm1hdGNoMiIsInNsaWNlIiwiZXh0cmFjdFBvaW50IiwiaW5kZXhQcm9wZXJ0eU5hbWUiLCJwdCIsInBvaW50czEiLCJyZWN0MSIsInBvaW50czIiLCJyZWN0MiIsInJlc3VsdCIsImNvdW50IiwidmlzdWFsaXphdGlvbiIsImRyYXdNYXRjaGVzIiwiY29scyIsImltZW5jb2RlQXN5bmMiLCJnZXRJbWFnZXNTaW1pbGFyaXR5IiwidGVtcGxhdGUiLCJyZWZlcmVuY2UiLCJyb3dzIiwiY29udmVydFRvQXN5bmMiLCJDVl84VUMzIiwibWF0Y2hlZCIsIm1hdGNoVGVtcGxhdGVBc3luYyIsIlRNX0NDT0VGRl9OT1JNRUQiLCJtaW5NYXgiLCJtaW5NYXhMb2NBc3luYyIsInNjb3JlIiwibWF4VmFsIiwicmVzdWx0TWF0IiwiTWF0IiwiY29weVRvQXN5bmMiLCJnZXRSZWdpb24iLCJtYXNrIiwiYWJzZGlmZiIsImN2dENvbG9yQXN5bmMiLCJDT0xPUl9CR1IyR1JBWSIsImNvbnRvdXJzIiwidGhyZXNob2xkQXN5bmMiLCJUSFJFU0hfQklOQVJZIiwiVEhSRVNIX09UU1UiLCJmaW5kQ29udG91cnNBc3luYyIsIlJFVFJfRVhURVJOQUwiLCJDSEFJTl9BUFBST1hfU0lNUExFIiwiaWduIiwiY29udG91ciIsImJvdW5kaW5nUmVjdCIsImdldEltYWdlT2NjdXJyZW5jZSIsImZ1bGxJbWdEYXRhIiwicGFydGlhbEltZ0RhdGEiLCJ0aHJlc2hvbGQiLCJmdWxsSW1nIiwicGFydGlhbEltZyIsInJlY3QiLCJtYXhMb2MiLCJjcm9wQmFzZTY0SW1hZ2UiLCJiYXNlNjRJbWFnZSIsImltYWdlIiwiYmFzZTY0VG9JbWFnZSIsImNyb3BJbWFnZSIsImltYWdlVG9CYXNlNjQiLCJpbWFnZUJ1ZmZlciIsIlBORyIsImZpbHRlclR5cGUiLCJwYXJzZSIsImNodW5rcyIsInBhY2siLCJvbiIsImNodW5rIiwicHVzaCIsImNvbmNhdCIsInRvU3RyaW5nIiwiaW1hZ2VSZWN0IiwiaW50ZXJSZWN0IiwiZ2V0UmVjdEludGVyc2VjdGlvbiIsImZpcnN0VmVydGljYWxQaXhlbCIsInRvcCIsImxhc3RWZXJ0aWNhbFBpeGVsIiwiZmlyc3RIb3Jpem9udGFsUGl4ZWwiLCJsZWZ0IiwibGFzdEhvcml6b250YWxQaXhlbCIsImNyb3BwZWRBcnJheSIsImZpcnN0Qnl0ZUlkeEluUGl4ZWxCbG9jayIsImJ5dGVJZHgiLCJpbWFnZVNpemUiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFDQTs7QUFHQSxNQUFNO0FBQUVBLEVBQUFBLFNBQUY7QUFBYUMsRUFBQUEsUUFBYjtBQUF1QkMsRUFBQUE7QUFBdkIsSUFBb0NDLGFBQTFDOzs7O0FBQ0EsSUFBSUMsRUFBRSxHQUFHLElBQVQ7QUF3QkEsTUFBTUMsb0JBQW9CLEdBQUcsQ0FBN0I7QUFDQSxNQUFNQyxzQkFBc0IsR0FBRyxDQUEvQjtBQUNBLE1BQU1DLHVCQUF1QixHQUFHLEdBQWhDO0FBRUEsTUFBTUMsbUJBQW1CLEdBQUcsQ0FDMUIsT0FEMEIsRUFFMUIsT0FGMEIsRUFHMUIsT0FIMEIsRUFJMUIsTUFKMEIsRUFLMUIsTUFMMEIsRUFNMUIsTUFOMEIsRUFPMUIsTUFQMEIsRUFRMUIsTUFSMEIsRUFTMUIsS0FUMEIsQ0FBNUI7QUFZQSxNQUFNQyw0QkFBNEIsR0FBRyxDQUNuQyxZQURtQyxFQUVuQyxZQUZtQyxFQUdu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TCxJQUFhSyxTQUFTLENBQUMzSSxLQUF2QixHQUErQjJJLFNBQVMsQ0FBQzNJLEtBQXpDLEdBQWlENEcsSUFBSSxDQUFDMEIsSUFBbkU7QUFDQSxRQUFNSCxHQUFHLEdBQUd2QixJQUFJLENBQUN1QixHQUFMLElBQVlRLFNBQVMsQ0FBQzFJLE1BQXRCLEdBQStCMEksU0FBUyxDQUFDMUksTUFBekMsR0FBa0QyRyxJQUFJLENBQUN1QixHQUFuRTtBQUNBLFFBQU1uSSxLQUFLLEdBQUcySSxTQUFTLENBQUMzSSxLQUFWLElBQW9Cc0ksSUFBSSxHQUFHMUIsSUFBSSxDQUFDNUcsS0FBaEMsR0FBeUM0RyxJQUFJLENBQUM1RyxLQUE5QyxHQUF1RDJJLFNBQVMsQ0FBQzNJLEtBQVYsR0FBa0JzSSxJQUF2RjtBQUNBLFFBQU1ySSxNQUFNLEdBQUcwSSxTQUFTLENBQUMxSSxNQUFWLElBQXFCa0ksR0FBRyxHQUFHdkIsSUFBSSxDQUFDM0csTUFBaEMsR0FBMEMyRyxJQUFJLENBQUMzRyxNQUEvQyxHQUF5RDBJLFNBQVMsQ0FBQzFJLE1BQVYsR0FBbUJrSSxHQUEzRjtBQUNBLFNBQU87QUFBQ0csSUFBQUEsSUFBRDtBQUFPSCxJQUFBQSxHQUFQO0FBQVluSSxJQUFBQSxLQUFaO0FBQW1CQyxJQUFBQTtBQUFuQixHQUFQO0FBQ0QiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgXyBmcm9tICdsb2Rhc2gnO1xuaW1wb3J0IEppbXAgZnJvbSAnamltcCc7XG5pbXBvcnQgeyBCdWZmZXIgfSBmcm9tICdidWZmZXInO1xuaW1wb3J0IHsgUE5HIH0gZnJvbSAncG5nanMnO1xuaW1wb3J0IEIgZnJvbSAnYmx1ZWJpcmQnO1xuaW1wb3J0IHsgaGFzVmFsdWUgfSBmcm9tICcuL3V0aWwnO1xuaW1wb3J0IGxvZyBmcm9tICcuL2xvZ2dlcic7XG5pbXBvcnQgeyByZXF1aXJlUGFja2FnZSB9IGZyb20gJy4vbm9kZSc7XG5cblxuY29uc3QgeyBNSU1FX0pQRUcsIE1JTUVfUE5HLCBNSU1FX0JNUCB9ID0gSmltcDtcbmxldCBjdiA9IG51bGw7XG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gUmVnaW9uXG4gKiBAcHJvcGVydHkge251bWJlcn0gbGVmdCAtIFRoZSBvZmZzZXQgZnJvbSB0aGUgbGVmdCBzaWRlXG4gKiBAcHJvcGVydHkge251bWJlcn0gdG9wIC0gVGhlIG9mZnNldCBmcm9tIHRoZSB0b3BcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB3aWR0aCAtIFRoZSB3aWR0aFxuICogQHByb3BlcnR5IHtudW1iZXJ9IGhlaWdodCAtIFRoZSBoZWlnaHRcbiAqL1xuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFBvaW50XG4gKiBAcHJvcGVydHkge251bWJlcn0geCAtIFRoZSB4IGNvb3JkaW5hdGVcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB5IC0gVGhlIHkgY29vcmRpbmF0ZVxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gUmVjdFxuICogQHByb3BlcnR5IHtudW1iZXJ9IHggLSBUaGUgdG9wIGxlZnQgY29vcmRpbmF0ZVxuICogQHByb3BlcnR5IHtudW1iZXJ9IHkgLSBUaGUgYm90dG9tIHJpZ2h0IGNvb3JkaW5hdGVcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSB3aWR0aCAtIFRoZSB3aWR0aFxuICogQHByb3BlcnR5IHtudW1iZXJ9IGhlaWdodCAtIFRoZSBoZWlnaHRcbiAqL1xuXG5jb25zdCBCWVRFU19JTl9QSVhFTF9CTE9DSyA9IDQ7XG5jb25zdCBTQ0FOTElORV9GSUxURVJfTUVUSE9EID0gNDtcbmNvbnN0IERFRkFVTFRfTUFUQ0hfVEhSRVNIT0xEID0gMC41O1xuXG5jb25zdCBBVkFJTEFCTEVfREVURUNUT1JTID0gW1xuICAnQUtBWkUnLFxuICAnQUdBU1QnLFxuICAnQlJJU0snLFxuICAnRkFTVCcsXG4gICdHRlRUJyxcbiAgJ0tBWkUnLFxuICAnTVNFUicsXG4gICdTSUZUJyxcbiAgJ09SQicsXG5dO1xuXG5jb25zdCBBVkFJTEFCTEVfTUFUQ0hJTkdfRlVOQ1RJT05TID0gW1xuICAnRmxhbm5CYXNlZCcsXG4gICdCcnV0ZUZvcmNlJyxcbiAgJ0JydXRlRm9yY2VMMScsXG4gICdCcnV0ZUZvcmNlSGFtbWluZycsXG4gICdCcnV0ZUZvcmNlSGFtbWluZ0x1dCcsXG4gICdCcnV0ZUZvcmNlU0wyJyxcbl07XG5cbi8qKlxuICogVXRpbGl0eSBmdW5jdGlvbiB0byBnZXQgYSBKaW1wIGltYWdlIG9iamVjdCBmcm9tIGJ1ZmZlciBvciBiYXNlNjQgZGF0YS4gSmltcFxuICogaXMgYSBncmVhdCBsaWJyYXJ5IGhvd2V2ZXIgaXQgZG9lcyBJTyBpbiB0aGUgY29uc3RydWN0b3Igc28gaXQncyBub3RcbiAqIGNvbnZlbmllbnQgZm9yIG91ciBhc3luYy9hd2FpdCBtb2RlbC5cbiAqXG4gKiBAcGFyYW0ge0J1ZmZlcnxzdHJpbmd9IGRhdGEgLSBiaW5hcnkgaW1hZ2UgYnVmZmVyIG9yIGJhc2U2NC1lbmNvZGVkIGltYWdlXG4gKiBzdHJpbmdcbiAqIEByZXR1cm5zIHtKaW1wfSAtIHRoZSBqaW1wIGltYWdlIG9iamVjdFxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRKaW1wSW1hZ2UgKGRhdGEpIHtcbiAgcmV0dXJuIGF3YWl0IG5ldyBCKChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICBpZiAoIV8uaXNTdHJpbmcoZGF0YSkgJiYgIV8uaXNCdWZmZXIoZGF0YSkpIHtcbiAgICAgIHJldHVybiByZWplY3QobmV3IEVycm9yKCdNdXN0IGluaXRpYWxpemUgamltcCBvYmplY3Qgd2l0aCBzdHJpbmcgb3IgYnVmZmVyJykpO1xuICAgIH1cbiAgICAvLyBpZiBkYXRhIGlzIGEgc3RyaW5nLCBhc3N1bWUgaXQgaXMgYSBiYXNlNjQtZW5jb2RlZCBpbWFnZVxuICAgIGlmIChfLmlzU3RyaW5nKGRhdGEpKSB7XG4gICAgICBkYXRhID0gQnVmZmVyLmZyb20oZGF0YSwgJ2Jhc2U2NCcpO1xuICAgIH1cbiAgICBuZXcgSmltcChkYXRhLCAoZXJyLCBpbWdPYmopID0+IHtcbiAgICAgIGlmIChlcnIpIHtcbiAgICAgICAgcmV0dXJuIHJlamVjdChlcnIpO1xuICAgICAgfVxuICAgICAgaWYgKCFpbWdPYmopIHtcbiAgICAgICAgcmV0dXJuIHJlamVjdChuZXcgRXJyb3IoJ0NvdWxkIG5vdCBjcmVhdGUgamltcCBpbWFnZSBmcm9tIHRoYXQgZGF0YScpKTtcbiAgICAgIH1cbiAgICAgIGltZ09iai5fZ2V0QnVmZmVyID0gaW1nT2JqLmdldEJ1ZmZlci5iaW5kKGltZ09iaik7XG4gICAgICBpbWdPYmouZ2V0QnVmZmVyID0gQi5wcm9taXNpZnkoaW1nT2JqLl9nZXRCdWZmZXIsIHtjb250ZXh0OiBpbWdPYmp9KTtcbiAgICAgIHJlc29sdmUoaW1nT2JqKTtcbiAgICB9KTtcbiAgfSk7XG59XG5cbi8qKlxuICogQHRocm93cyB7RXJyb3J9IElmIG9wZW5jdjRub2RlanMgbW9kdWxlIGlzIG5vdCBpbnN0YWxsZWQgb3IgY2Fubm90IGJlIGxvYWRlZFxuICovXG5hc3luYyBmdW5jdGlvbiBpbml0T3BlbkNWICgpIHtcbiAgaWYgKGN2KSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgbG9nLmRlYnVnKGBJbml0aWFsaXppbmcgb3BlbmN2YCk7XG4gIHRyeSB7XG4gICAgY3YgPSBhd2FpdCByZXF1aXJlUGFja2FnZSgnb3BlbmN2NG5vZGVqcycpO1xuICB9IGNhdGNoIChlcnIpIHtcbiAgICBsb2cud2FybihgVW5hYmxlIHRvIGxvYWQgJ29wZW5jdjRub2RlanMnOiAke2Vyci5tZXNzYWdlfWApO1xuICB9XG5cbiAgaWYgKCFjdikge1xuICAgIHRocm93IG5ldyBFcnJvcihgJ29wZW5jdjRub2RlanMnIG1vZHVsZSBpcyByZXF1aXJlZCB0byB1c2UgT3BlbkNWIGZlYXR1cmVzLiBgICtcbiAgICAgICAgICAgICAgICAgICAgYFBsZWFzZSBpbnN0YWxsIGl0IGZpcnN0ICgnbnBtIGkgLWcgb3BlbmN2NG5vZGVqcycpIGFuZCByZXN0YXJ0IEFwcGl1bS4gYCArXG4gICAgICAgICAgICAgICAgICAgICdSZWFkIGh0dHBzOi8vZ2l0aHViLmNvbS9qdXN0YWR1ZGV3aG9oYWNrcy9vcGVuY3Y0bm9kZWpzI2hvdy10by1pbnN0YWxsIGZvciBtb3JlIGRldGFpbHMgb24gdGhpcyB0b3BpYy4nKTtcbiAgfVxufVxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IE1hdGNoQ29tcHV0YXRpb25SZXN1bHRcbiAqIEBwcm9wZXJ0eSB7Y3YuRGVzY3JpcHRvck1hdGNofSBkZXNjaXB0b3IgLSBPcGVuQ1YgbWF0Y2ggZGVzY3JpcHRvclxuICogQHByb3BlcnR5IHtBcnJheTxjdi5LZXlQb2ludD59IGtleVBvaW50cyAtIFRoZSBhcnJheSBvZiBrZXkgcG9pbnRzXG4gKi9cblxuLyoqXG4gKiBDYWxjdWxhdGVzIGFuIE9wZW5DViBtYXRjaCBkZXNjcmlwdG9yIG9mIGFuIGltYWdlLCB3aGljaCBjYW4gYmUgdXNlZFxuICogZm9yIGJydXRlLWZvcmNlIG1hdGNoaW5nLlxuICogUmVhZCBodHRwczovL2RvY3Mub3BlbmN2Lm9yZy8zLjAtYmV0YS9kb2MvcHlfdHV0b3JpYWxzL3B5X2ZlYXR1cmUyZC9weV9tYXRjaGVyL3B5X21hdGNoZXIuaHRtbFxuICogZm9yIG1vcmUgZGV0YWlscy5cbiAqXG4gKiBAcGFyYW0ge2N2Lk1hdH0gaW1nIEltYWdlIGRhdGFcbiAqIEBwYXJhbSB7Y3YuRmVhdHVyZURldGVjdG9yfSBkZXRlY3RvciBPcGVuQ1YgZmVhdHVyZSBkZXRlY3RvciBpbnN0YW5jZVxuICpcbiAqIEByZXR1cm5zIHtNYXRjaENvbXB1dGF0aW9uUmVzdWx0fVxuICovXG5hc3luYyBmdW5jdGlvbiBkZXRlY3RBbmRDb21wdXRlIChpbWcsIGRldGVjdG9yKSB7XG4gIGNvbnN0IGtleVBvaW50cyA9IGF3YWl0IGRldGVjdG9yLmRldGVjdEFzeW5jKGltZyk7XG4gIGNvbnN0IGRlc2NyaXB0b3IgPSBhd2FpdCBkZXRlY3Rvci5jb21wdXRlQXN5bmMoaW1nLCBrZXlQb2ludHMpO1xuICByZXR1cm4ge1xuICAgIGtleVBvaW50cyxcbiAgICBkZXNjcmlwdG9yXG4gIH07XG59XG5cbi8qKlxuICogQ2FsY3VsYXRlZCB0aGUgYm91bmRpbmcgcmVjdCBjb29yZGluYXRlcyBmb3IgdGhlIGFycmF5IG9mIG1hdGNoaW5nIHBvaW50c1xuICpcbiAqIEBwYXJhbSB7QXJyYXk8UG9pbnQ+fSBtYXRjaGVkUG9pbnRzIEFycmF5IG9mIG1hdGNoaW5nIHBvaW50c1xuICogQHJldHVybnMge1JlY3R9IFRoZSBtYXRjaGluZyBib3VuZGluZyByZWN0IG9yIGEgemVybyByZWN0IGlmIG5vIG1hdGNoXG4gKiBjYW4gYmUgZm91bmQuXG4gKi9cbmZ1bmN0aW9uIGNhbGN1bGF0ZU1hdGNoZWRSZWN0IChtYXRjaGVkUG9pbnRzKSB7XG4gIGlmIChtYXRjaGVkUG9pbnRzLmxlbmd0aCA8IDIpIHtcbiAgICByZXR1cm4ge1xuICAgICAgeDogMCxcbiAgICAgIHk6IDAsXG4gICAgICB3aWR0aDogMCxcbiAgICAgIGhlaWdodDogMFxuICAgIH07XG4gIH1cblxuICBjb25zdCBwb2ludHNTb3J0ZWRCeURpc3RhbmNlID0gbWF0Y2hlZFBvaW50c1xuICAgIC5tYXAoKHBvaW50KSA9PiBbTWF0aC5zcXJ0KHBvaW50LnggKiBwb2ludC54ICsgcG9pbnQueSAqIHBvaW50LnkpLCBwb2ludF0pXG4gICAgLnNvcnQoKHBhaXIxLCBwYWlyMikgPT4gcGFpcjFbMF0gPj0gcGFpcjJbMF0pXG4gICAgLm1hcCgocGFpcikgPT4gcGFpclsxXSk7XG4gIGNvbnN0IGZpcnN0UG9pbnQgPSBfLmhlYWQocG9pbnRzU29ydGVkQnlEaXN0YW5jZSk7XG4gIGNvbnN0IGxhc3RQb2ludCA9IF8ubGFzdChwb2ludHNTb3J0ZWRCeURpc3RhbmNlKTtcbiAgY29uc3QgdG9wTGVmdFBvaW50ID0ge1xuICAgIHg6IGZpcnN0UG9pbnQueCA8PSBsYXN0UG9pbnQueCA/IGZpcnN0UG9pbnQueCA6IGxhc3RQb2ludC54LFxuICAgIHk6IGZpcnN0UG9pbnQueSA8PSBsYXN0UG9pbnQueSA/IGZpcnN0UG9pbnQueSA6IGxhc3RQb2ludC55LFxuICB9O1xuICBjb25zdCBib3R0b21SaWdodFBvaW50ID0ge1xuICAgIHg6IGZpcnN0UG9pbnQueCA+PSBsYXN0UG9pbnQueCA/IGZpcnN0UG9pbnQueCA6IGxhc3RQb2ludC54LFxuICAgIHk6IGZpcnN0UG9pbnQueSA+PSBsYXN0UG9pbnQueSA/IGZpcnN0UG9pbnQueSA6IGxhc3RQb2ludC55LFxuICB9O1xuICByZXR1cm4ge1xuICAgIHg6IHRvcExlZnRQb2ludC54LFxuICAgIHk6IHRvcExlZnRQb2ludC55LFxuICAgIHdpZHRoOiBib3R0b21SaWdodFBvaW50LnggLSB0b3BMZWZ0UG9pbnQueCxcbiAgICBoZWlnaHQ6IGJvdHRvbVJpZ2h0UG9pbnQueSAtIHRvcExlZnRQb2ludC55XG4gIH07XG59XG5cbi8qKlxuICogRHJhd3MgYSByZWN0YW5uZ2xlIG9uIHRoZSBnaXZlbiBpbWFnZSBtYXRyaXhcbiAqXG4gKiBAcGFyYW0ge2N2Lk1hdH0gbWF0IFRoZSBzb3VyY2UgaW1hZ2VcbiAqIEBwYXJhbSB7UmVjdH0gcmVnaW9uIFRoZSByZWdpb24gdG8gaGlnaGxpZ2h0XG4gKlxuICogQHJldHVybnMge2N2Lk1hdH0gVGhlIHNhbWUgaW1hZ2Ugd2l0aCB0aGUgcmVjdGFuZ2Ugb24gaXRcbiAqL1xuZnVuY3Rpb24gaGlnaGxpZ2h0UmVnaW9uIChtYXQsIHJlZ2lvbikge1xuICBpZiAocmVnaW9uLndpZHRoIDw9IDAgfHwgcmVnaW9uLmhlaWdodCA8PSAwKSB7XG4gICAgcmV0dXJuO1xuICB9XG5cbiAgLy8gaGlnaGxpZ2h0IGluIHJlZFxuICBjb25zdCBjb2xvciA9IG5ldyBjdi5WZWMoMCwgMCwgMjU1KTtcbiAgY29uc3QgdGhpY2tuZXNzID0gMjtcbiAgbWF0LmRyYXdSZWN0YW5nbGUobmV3IGN2LlJlY3QocmVnaW9uLngsIHJlZ2lvbi55LCByZWdpb24ud2lkdGgsIHJlZ2lvbi5oZWlnaHQpLCBjb2xvciwgdGhpY2tuZXNzLCBjdi5MSU5FXzgpO1xuICByZXR1cm4gbWF0O1xufVxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IE1hdGNoaW5nT3B0aW9uc1xuICogQHByb3BlcnR5IHs/c3RyaW5nfSBkZXRlY3Rvck5hbWUgWydPUkInXSBPbmUgb2YgcG9zc2libGUgT3BlbkNWIGZlYXR1cmUgZGV0ZWN0b3IgbmFtZXNcbiAqIGZyb20gYEFWQUlMQUJMRV9ERVRFQ1RPUlNgIGFycmF5LlxuICogU29tZSBvZiB0aGVzZSBtZXRob2RzIChGQVNULCBBR0FTVCwgR0ZUVCwgRkFTVCwgU0lGVCBhbmQgTVNFUikgYXJlIG5vdCBhdmFpbGFibGVcbiAqIGluIHRoZSBkZWZhdWx0IE9wZW5DViBpbnN0YWxsYXRpb24gYW5kIGhhdmUgdG8gYmUgZW5hYmxlZCBtYW51YWxseSBiZWZvcmVcbiAqIGxpYnJhcnkgY29tcGlsYXRpb24uXG4gKiBAcHJvcGVydHkgez9zdHJpbmd9IG1hdGNoRnVuYyBbJ0JydXRlRm9yY2UnXSBUaGUgbmFtZSBvZiB0aGUgbWF0Y2hpbmcgZnVuY3Rpb24uXG4gKiBTaG91bGQgYmUgb25lIG9mIGBBVkFJTEFCTEVfTUFUQ0hJTkdfRlVOQ1RJT05TYCBhcnJheS5cbiAqIEBwcm9wZXJ0eSB7P251bWJlcnxGdW5jdGlvbn0gZ29vZE1hdGNoZXNGYWN0b3IgVGhlIG1heGltdW0gY291bnQgb2YgXCJnb29kXCIgbWF0Y2hlc1xuICogKGUuIGcuIHdpdGggbWluaW1hbCBkaXN0YW5jZXMpIG9yIGEgZnVuY3Rpb24sIHdoaWNoIGFjY2VwdHMgMyBhcmd1bWVudHM6IHRoZSBjdXJyZW50IGRpc3RhbmNlLFxuICogbWluaW1hbCBkaXN0YW5jZSwgbWF4aW11bSBkaXN0YW5jZSBhbmQgcmV0dXJucyB0cnVlIG9yIGZhbHNlIHRvIGluY2x1ZGUgb3IgZXhjbHVkZSB0aGUgbWF0Y2guXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSB2aXN1YWxpemUgW2ZhbHNlXSBXaGV0aGVyIHRvIHJldHVybiB0aGUgcmVzdWx0aW5nIHZpc2FsaXphdGlvblxuICogYXMgYW4gaW1hZ2UgKHVzZWZ1bCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzKVxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gTWF0Y2hpbmdSZXN1bHRcbiAqIEBwcm9wZXJ0eSB7bnVtYmVyfSBjb3VudCBUaGUgY291bnQgb2YgbWF0Y2hlZCBlZGdlcyBvbiBib3RoIGltYWdlcy5cbiAqIFRoZSBtb3JlIG1hdGNoaW5nIGVkZ2VzIHRoZXJlIGFyZSBubyBib3RoIGltYWdlcyB0aGUgbW9yZSBzaW1pbGFyIHRoZXkgYXJlLlxuICogQHByb3BlcnR5IHtudW1iZXJ9IHRvdGFsQ291bnQgVGhlIHRvdGFsIGNvdW50IG9mIG1hdGNoZWQgZWRnZXMgb24gYm90aCBpbWFnZXMuXG4gKiBJdCBpcyBlcXVhbCB0byBgY291bnRgIGlmIGBnb29kTWF0Y2hlc0ZhY3RvcmAgZG9lcyBub3QgbGltaXQgdGhlIG1hdGNoZXMsXG4gKiBvdGhlcndpc2UgaXQgY29udGFpbnMgdGhlIHRvdGFsIGNvdW50IG9mIG1hdGNoZXMgYmVmb3JlIGBnb29kTWF0Y2hlc0ZhY3RvcmAgaXNcbiAqIGFwcGxpZWQuXG4gKiBAcHJvcGVydHkgez9CdWZmZXJ9IHZpc3VhbGl6YXRpb24gVGhlIHZpc3VhbGl6YXRpb24gb2YgdGhlIG1hdGNoaW5nIHJlc3VsdFxuICogcmVwcmVzZW50ZWQgYXMgUE5HIGltYWdlIGJ1ZmZlci4gVGhpcyB2aXN1YWxpemF0aW9uIGxvb2tzIGxpa2VcbiAqIGh0dHBzOi8vdXNlci1pbWFnZXMuZ2l0aHVidXNlcmNvbnRlbnQuY29tLzMxMTI1NTIxLzI5NzAyNzMxLWM3OWUzMTQyLTg5NzItMTFlNy05NDdlLWRiMTA5ZDQxNTQ2OS5qcGdcbiAqIEBwcm9wZXJ0eSB7QXJyYXk8UG9pbnQ+fSBwb2ludHMxIFRoZSBhcnJheSBvZiBtYXRjaGluZyBwb2ludHMgb24gdGhlIGZpcnN0IGltYWdlXG4gKiBAcHJvcGVydHkge1JlY3R9IHJlY3QxIFRoZSBib3VuZGluZyByZWN0IGZvciB0aGUgYG1hdGNoZWRQb2ludHMxYCBzZXQgb3IgYSB6ZXJvIHJlY3RcbiAqIGlmIG5vdCBlbm91Z2ggbWF0Y2hpbmcgcG9pbnRzIGFyZSBmb3VuZFxuICogQHByb3BlcnR5IHtBcnJheTxQb2ludD59IHBvaW50czIgVGhlIGFycmF5IG9mIG1hdGNoaW5nIHBvaW50cyBvbiB0aGUgc2Vjb25kIGltYWdlXG4gKiBAcHJvcGVydHkge1JlY3R9IHJlY3QyIFRoZSBib3VuZGluZyByZWN0IGZvciB0aGUgYG1hdGNoZWRQb2ludHMyYCBzZXQgb3IgYSB6ZXJvIHJlY3RcbiAqIGlmIG5vdCBlbm91Z2ggbWF0Y2hpbmcgcG9pbnRzIGFyZSBmb3VuZFxuICovXG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgY291bnQgb2YgY29tbW9uIGVkZ2VzIGJldHdlZW4gdHdvIGltYWdlcy5cbiAqIFRoZSBpbWFnZXMgbWlnaHQgYmUgcm90YXRlZCBvciByZXNpemVkIHJlbGF0aXZlbHkgdG8gZWFjaCBvdGhlci5cbiAqXG4gKiBAcGFyYW0ge0J1ZmZlcn0gaW1nMURhdGEgVGhlIGRhdGEgb2YgdGhlIGZpcnN0IGltYWdlIHBhY2tlZCBpbnRvIGEgTm9kZUpTIGJ1ZmZlclxuICogQHBhcmFtIHtCdWZmZXJ9IGltZzJEYXRhIFRoZSBkYXRhIG9mIHRoZSBzZWNvbmQgaW1hZ2UgcGFja2VkIGludG8gYSBOb2RlSlMgYnVmZmVyXG4gKiBAcGFyYW0gez9NYXRjaGluZ09wdGlvbnN9IG9wdGlvbnMgW3t9XSBTZXQgb2YgbWF0Y2hpbmcgb3B0aW9uc1xuICpcbiAqIEByZXR1cm5zIHtNYXRjaGluZ1Jlc3VsdH0gTWFjaGluZyByZXN1bHRcbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiBgZGV0ZWN0b3JOYW1lYCB2YWx1ZSBpcyB1bmtub3duLlxuICovXG5hc3luYyBmdW5jdGlvbiBnZXRJbWFnZXNNYXRjaGVzIChpbWcxRGF0YSwgaW1nMkRhdGEsIG9wdGlvbnMgPSB7fSkge1xuICBhd2FpdCBpbml0T3BlbkNWKCk7XG5cbiAgY29uc3Qge2RldGVjdG9yTmFtZSA9ICdPUkInLCB2aXN1YWxpemUgPSBmYWxzZSxcbiAgICAgICAgIGdvb2RNYXRjaGVzRmFjdG9yLCBtYXRjaEZ1bmMgPSAnQnJ1dGVGb3JjZSd9ID0gb3B0aW9ucztcbiAgaWYgKCFfLmluY2x1ZGVzKEFWQUlMQUJMRV9ERVRFQ1RPUlMsIGRldGVjdG9yTmFtZSkpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoYCcke2RldGVjdG9yTmFtZX0nIGRldGVjdG9yIGlzIHVua25vd24uIGAgK1xuICAgICAgICAgICAgICAgICAgICBgT25seSAke0pTT04uc3RyaW5naWZ5KEFWQUlMQUJMRV9ERVRFQ1RPUlMpfSBkZXRlY3RvcnMgYXJlIHN1cHBvcnRlZC5gKTtcbiAgfVxuICBpZiAoIV8uaW5jbHVkZXMoQVZBSUxBQkxFX01BVENISU5HX0ZVTkNUSU9OUywgbWF0Y2hGdW5jKSkge1xuICAgIHRocm93IG5ldyBFcnJvcihgJyR7bWF0Y2hGdW5jfScgbWF0Y2hpbmcgZnVuY3Rpb24gaXMgdW5rbm93bi4gYCArXG4gICAgICAgICAgICAgICAgICAgIGBPbmx5ICR7SlNPTi5zdHJpbmdpZnkoQVZBSUxBQkxFX01BVENISU5HX0ZVTkNUSU9OUyl9IG1hdGNoaW5nIGZ1bmN0aW9ucyBhcmUgc3VwcG9ydGVkLmApO1xuICB9XG5cbiAgY29uc3QgZGV0ZWN0b3IgPSBuZXcgY3ZbYCR7ZGV0ZWN0b3JOYW1lfURldGVjdG9yYF0oKTtcbiAgY29uc3QgW2ltZzEsIGltZzJdID0gYXdhaXQgQi5hbGwoW1xuICAgIGN2LmltZGVjb2RlQXN5bmMoaW1nMURhdGEpLFxuICAgIGN2LmltZGVjb2RlQXN5bmMoaW1nMkRhdGEpXG4gIF0pO1xuICBjb25zdCBbcmVzdWx0MSwgcmVzdWx0Ml0gPSBhd2FpdCBCLmFsbChbXG4gICAgZGV0ZWN0QW5kQ29tcHV0ZShpbWcxLCBkZXRlY3RvciksXG4gICAgZGV0ZWN0QW5kQ29tcHV0ZShpbWcyLCBkZXRlY3RvcilcbiAgXSk7XG4gIGxldCBtYXRjaGVzID0gW107XG4gIHRyeSB7XG4gICAgbWF0Y2hlcyA9IGF3YWl0IGN2W2BtYXRjaCR7bWF0Y2hGdW5jfUFzeW5jYF0ocmVzdWx0MS5kZXNjcmlwdG9yLCByZXN1bHQyLmRlc2NyaXB0b3IpO1xuICB9IGNhdGNoIChlKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKGBDYW5ub3QgZmluZCBhbnkgbWF0Y2hlcyBiZXR3ZWVuIHRoZSBnaXZlbiBpbWFnZXMuIFRyeSBhbm90aGVyIGRldGVjdGlvbiBhbGdvcml0aG0uIGAgK1xuICAgICAgICAgICAgICAgICAgICBgIE9yaWdpbmFsIGVycm9yOiAke2V9YCk7XG4gIH1cbiAgY29uc3QgdG90YWxDb3VudCA9IG1hdGNoZXMubGVuZ3RoO1xuICBpZiAoaGFzVmFsdWUoZ29vZE1hdGNoZXNGYWN0b3IpKSB7XG4gICAgaWYgKF8uaXNGdW5jdGlvbihnb29kTWF0Y2hlc0ZhY3RvcikpIHtcbiAgICAgIGNvbnN0IGRpc3RhbmNlcyA9IG1hdGNoZXMubWFwKChtYXRjaCkgPT4gbWF0Y2guZGlzdGFuY2UpO1xuICAgICAgY29uc3QgbWluRGlzdGFuY2UgPSBfLm1pbihkaXN0YW5jZXMpO1xuICAgICAgY29uc3QgbWF4RGlzdGFuY2UgPSBfLm1heChkaXN0YW5jZXMpO1xuICAgICAgbWF0Y2hlcyA9IG1hdGNoZXNcbiAgICAgICAgLmZpbHRlcigobWF0Y2gpID0+IGdvb2RNYXRjaGVzRmFjdG9yKG1hdGNoLmRpc3RhbmNlLCBtaW5EaXN0YW5jZSwgbWF4RGlzdGFuY2UpKTtcbiAgICB9IGVsc2Uge1xuICAgICAgaWYgKG1hdGNoZXMubGVuZ3RoID4gZ29vZE1hdGNoZXNGYWN0b3IpIHtcbiAgICAgICAgbWF0Y2hlcyA9IG1hdGNoZXNcbiAgICAgICAgICAuc29ydCgobWF0Y2gxLCBtYXRjaDIpID0+IG1hdGNoMS5kaXN0YW5jZSAtIG1hdGNoMi5kaXN0YW5jZSlcbiAgICAgICAgICAuc2xpY2UoMCwgZ29vZE1hdGNoZXNGYWN0b3IpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGNvbnN0IGV4dHJhY3RQb2ludCA9IChrZXlQb2ludHMsIGluZGV4UHJvcGVydHlOYW1lKSA9PiAobWF0Y2gpID0+IHtcbiAgICBjb25zdCB7cHQsIHBvaW50fSA9IGtleVBvaW50c1ttYXRjaFtpbmRleFByb3BlcnR5TmFtZV1dO1xuICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9qdXN0YWR1ZGV3aG9oYWNrcy9vcGVuY3Y0bm9kZWpzL2lzc3Vlcy81ODRcbiAgICByZXR1cm4gKHB0IHx8IHBvaW50KTtcbiAgfTtcbiAgY29uc3QgcG9pbnRzMSA9IG1hdGNoZXMubWFwKGV4dHJhY3RQb2ludChyZXN1bHQxLmtleVBvaW50cywgJ3F1ZXJ5SWR4JykpO1xuICBjb25zdCByZWN0MSA9IGNhbGN1bGF0ZU1hdGNoZWRSZWN0KHBvaW50czEpO1xuICBjb25zdCBwb2ludHMyID0gbWF0Y2hlcy5tYXAoZXh0cmFjdFBvaW50KHJlc3VsdDIua2V5UG9pbnRzLCAndHJhaW5JZHgnKSk7XG4gIGNvbnN0IHJlY3QyID0gY2FsY3VsYXRlTWF0Y2hlZFJlY3QocG9pbnRzMik7XG5cbiAgY29uc3QgcmVzdWx0ID0ge1xuICAgIHBvaW50czEsXG4gICAgcmVjdDEsXG4gICAgcG9pbnRzMixcbiAgICByZWN0MixcbiAgICB0b3RhbENvdW50LFxuICAgIGNvdW50OiBtYXRjaGVzLmxlbmd0aCxcbiAgfTtcbiAgaWYgKHZpc3VhbGl6ZSkge1xuICAgIGNvbnN0IHZpc3VhbGl6YXRpb24gPSBjdi5kcmF3TWF0Y2hlcyhpbWcxLCBpbWcyLCByZXN1bHQxLmtleVBvaW50cywgcmVzdWx0Mi5rZXlQb2ludHMsIG1hdGNoZXMpO1xuICAgIGhpZ2hsaWdodFJlZ2lvbih2aXN1YWxpemF0aW9uLCByZWN0MSk7XG4gICAgaGlnaGxpZ2h0UmVnaW9uKHZpc3VhbGl6YXRpb24sIHtcbiAgICAgIHg6IGltZzEuY29scyArIHJlY3QyLngsXG4gICAgICB5OiByZWN0Mi55LFxuICAgICAgd2lkdGg6IHJlY3QyLndpZHRoLFxuICAgICAgaGVpZ2h0OiByZWN0Mi5oZWlnaHRcbiAgICB9KTtcbiAgICByZXN1bHQudmlzdWFsaXphdGlvbiA9IGF3YWl0IGN2LmltZW5jb2RlQXN5bmMoJy5wbmcnLCB2aXN1YWxpemF0aW9uKTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufVxuXG4vKipcbiAqIEB0eXBlZGVmIHtPYmplY3R9IFNpbWlsYXJpdHlPcHRpb25zXG4gKiBAcHJvcGVydHkgez9ib29sZWFufSB2aXN1YWxpemUgW2ZhbHNlXSBXaGV0aGVyIHRvIHJldHVybiB0aGUgcmVzdWx0aW5nIHZpc2FsaXphdGlvblxuICogYXMgYW4gaW1hZ2UgKHVzZWZ1bCBmb3IgZGVidWdnaW5nIHB1cnBvc2VzKVxuICovXG5cbi8qKlxuICogQHR5cGVkZWYge09iamVjdH0gU2ltaWxhcml0eVJlc3VsdFxuICogQHByb3BlcnR5IHtudW1iZXJ9IHNjb3JlIFRoZSBzaW1pbGFyaXR5IHNjb3JlIGFzIGEgZmxvYXQgbnVtYmVyIGluIHJhbmdlIFswLjAsIDEuMF0uXG4gKiAxLjAgaXMgdGhlIGhpZ2hlc3Qgc2NvcmUgKG1lYW5zIGJvdGggaW1hZ2VzIGFyZSB0b3RhbGx5IGVxdWFsKS5cbiAqIEBwcm9wZXJ0eSB7P0J1ZmZlcn0gdmlzdWFsaXphdGlvbiBUaGUgdmlzdWFsaXphdGlvbiBvZiB0aGUgbWF0Y2hpbmcgcmVzdWx0XG4gKiByZXByZXNlbnRlZCBhcyBQTkcgaW1hZ2UgYnVmZmVyLiBUaGlzIGltYWdlIGluY2x1ZGVzIGJvdGggaW5wdXQgcGljdHVyZXMgd2hlcmVcbiAqIGRpZmZlcmVuY2UgcmVnaW9ucyBhcmUgaGlnaGxpZ2h0ZWQgd2l0aCByZWN0YW5nbGVzLlxuICovXG5cbi8qKlxuICogQ2FsY3VsYXRlcyB0aGUgc2ltaWxhcml0eSBzY29yZSBiZXR3ZWVuIHR3byBpbWFnZXMuXG4gKiBJdCBpcyBleHBlY3RlZCwgdGhhdCBib3RoIGltYWdlcyBoYXZlIHRoZSBzYW1lIHJlc29sdXRpb24uXG4gKlxuICogQHBhcmFtIHtCdWZmZXJ9IGltZzFEYXRhIFRoZSBkYXRhIG9mIHRoZSBmaXJzdCBpbWFnZSBwYWNrZWQgaW50byBhIE5vZGVKUyBidWZmZX