UNPKG

matrix-react-sdk

Version:
342 lines (279 loc) 39.8 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.editBodyDiffToHtml = editBodyDiffToHtml; var _react = _interopRequireDefault(require("react")); var _classnames = _interopRequireDefault(require("classnames")); var _diffMatchPatch = _interopRequireDefault(require("diff-match-patch")); var _diffDom = require("diff-dom"); var _HtmlUtils = require("../HtmlUtils"); /* Copyright 2019 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ const decodeEntities = function () { let textarea = null; return function (string) { if (!textarea) { textarea = document.createElement("textarea"); } textarea.innerHTML = string; return textarea.value; }; }(); function textToHtml(text) { const container = document.createElement("div"); container.textContent = text; return container.innerHTML; } function getSanitizedHtmlBody(content) { const opts = { stripReplyFallback: true, returnString: true }; if (content.format === "org.matrix.custom.html") { return (0, _HtmlUtils.bodyToHtml)(content, null, opts); } else { // convert the string to something that can be safely // embedded in an html document, e.g. use html entities where needed // This is also needed so that DiffDOM wouldn't interpret something // as a tag when somebody types e.g. "</sarcasm>" // as opposed to bodyToHtml, here we also render // text messages with dangerouslySetInnerHTML, to unify // the code paths and because we need html to show differences return textToHtml((0, _HtmlUtils.bodyToHtml)(content, null, opts)); } } function wrapInsertion(child) { const wrapper = document.createElement((0, _HtmlUtils.checkBlockNode)(child) ? "div" : "span"); wrapper.className = "mx_EditHistoryMessage_insertion"; wrapper.appendChild(child); return wrapper; } function wrapDeletion(child) { const wrapper = document.createElement((0, _HtmlUtils.checkBlockNode)(child) ? "div" : "span"); wrapper.className = "mx_EditHistoryMessage_deletion"; wrapper.appendChild(child); return wrapper; } function findRefNodes(root, route, isAddition) { let refNode = root; let refParentNode; const end = isAddition ? route.length - 1 : route.length; for (let i = 0; i < end; ++i) { refParentNode = refNode; refNode = refNode.childNodes[route[i]]; } return { refNode, refParentNode }; } function diffTreeToDOM(desc) { if (desc.nodeName === "#text") { return stringAsTextNode(desc.data); } else { const node = document.createElement(desc.nodeName); if (desc.attributes) { for (const [key, value] of Object.entries(desc.attributes)) { node.setAttribute(key, value); } } if (desc.childNodes) { for (const childDesc of desc.childNodes) { node.appendChild(diffTreeToDOM(childDesc)); } } return node; } } function insertBefore(parent, nextSibling, child) { if (nextSibling) { parent.insertBefore(child, nextSibling); } else { parent.appendChild(child); } } function isRouteOfNextSibling(route1, route2) { // routes are arrays with indices, // to be interpreted as a path in the dom tree // ensure same parent for (let i = 0; i < route1.length - 1; ++i) { if (route1[i] !== route2[i]) { return false; } } // the route2 is only affected by the diff of route1 // inserting an element if the index at the level of the // last element of route1 being larger // (e.g. coming behind route1 at that level) const lastD1Idx = route1.length - 1; return route2[lastD1Idx] >= route1[lastD1Idx]; } function adjustRoutes(diff, remainingDiffs) { if (diff.action === "removeTextElement" || diff.action === "removeElement") { // as removed text is not removed from the html, but marked as deleted, // we need to readjust indices that assume the current node has been removed. const advance = 1; for (const rd of remainingDiffs) { if (isRouteOfNextSibling(diff.route, rd.route)) { rd.route[diff.route.length - 1] += advance; } } } } function stringAsTextNode(string) { return document.createTextNode(decodeEntities(string)); } function renderDifferenceInDOM(originalRootNode, diff, diffMathPatch) { const { refNode, refParentNode } = findRefNodes(originalRootNode, diff.route); switch (diff.action) { case "replaceElement": { const container = document.createElement("span"); const delNode = wrapDeletion(diffTreeToDOM(diff.oldValue)); const insNode = wrapInsertion(diffTreeToDOM(diff.newValue)); container.appendChild(delNode); container.appendChild(insNode); refNode.parentNode.replaceChild(container, refNode); break; } case "removeTextElement": { const delNode = wrapDeletion(stringAsTextNode(diff.value)); refNode.parentNode.replaceChild(delNode, refNode); break; } case "removeElement": { const delNode = wrapDeletion(diffTreeToDOM(diff.element)); refNode.parentNode.replaceChild(delNode, refNode); break; } case "modifyTextElement": { const textDiffs = diffMathPatch.diff_main(diff.oldValue, diff.newValue); diffMathPatch.diff_cleanupSemantic(textDiffs); const container = document.createElement("span"); for (const [modifier, text] of textDiffs) { let textDiffNode = stringAsTextNode(text); if (modifier < 0) { textDiffNode = wrapDeletion(textDiffNode); } else if (modifier > 0) { textDiffNode = wrapInsertion(textDiffNode); } container.appendChild(textDiffNode); } refNode.parentNode.replaceChild(container, refNode); break; } case "addElement": { const insNode = wrapInsertion(diffTreeToDOM(diff.element)); insertBefore(refParentNode, refNode, insNode); break; } case "addTextElement": { // XXX: sometimes diffDOM says insert a newline when there shouldn't be one // but we must insert the node anyway so that we don't break the route child IDs. // See https://github.com/fiduswriter/diffDOM/issues/100 const insNode = wrapInsertion(stringAsTextNode(diff.value !== "\n" ? diff.value : "")); insertBefore(refParentNode, refNode, insNode); break; } // e.g. when changing a the href of a link, // show the link with old href as removed and with the new href as added case "removeAttribute": case "addAttribute": case "modifyAttribute": { const delNode = wrapDeletion(refNode.cloneNode(true)); const updatedNode = refNode.cloneNode(true); if (diff.action === "addAttribute" || diff.action === "modifyAttribute") { updatedNode.setAttribute(diff.name, diff.newValue); } else { updatedNode.removeAttribute(diff.name); } const insNode = wrapInsertion(updatedNode); const container = document.createElement((0, _HtmlUtils.checkBlockNode)(refNode) ? "div" : "span"); container.appendChild(delNode); container.appendChild(insNode); refNode.parentNode.replaceChild(container, refNode); break; } default: // Should not happen (modifyComment, ???) console.warn("MessageDiffUtils::editBodyDiffToHtml: diff action not supported atm", diff); } } function routeIsEqual(r1, r2) { return r1.length === r2.length && !r1.some((e, i) => e !== r2[i]); } // workaround for https://github.com/fiduswriter/diffDOM/issues/90 function filterCancelingOutDiffs(originalDiffActions) { const diffActions = originalDiffActions.slice(); for (let i = 0; i < diffActions.length; ++i) { const diff = diffActions[i]; if (diff.action === "removeTextElement") { const nextDiff = diffActions[i + 1]; const cancelsOut = nextDiff && nextDiff.action === "addTextElement" && nextDiff.text === diff.text && routeIsEqual(nextDiff.route, diff.route); if (cancelsOut) { diffActions.splice(i, 2); } } } return diffActions; } /** * Renders a message with the changes made in an edit shown visually. * @param {object} originalContent the content for the base message * @param {object} editContent the content for the edit message * @return {object} a react element similar to what `bodyToHtml` returns */ function editBodyDiffToHtml(originalContent, editContent) { // wrap the body in a div, DiffDOM needs a root element const originalBody = `<div>${getSanitizedHtmlBody(originalContent)}</div>`; const editBody = `<div>${getSanitizedHtmlBody(editContent)}</div>`; const dd = new _diffDom.DiffDOM(); // diffActions is an array of objects with at least a `action` and `route` // property. `action` tells us what the diff object changes, and `route` where. // `route` is a path on the DOM tree expressed as an array of indices. const originaldiffActions = dd.diff(originalBody, editBody); // work around https://github.com/fiduswriter/diffDOM/issues/90 const diffActions = filterCancelingOutDiffs(originaldiffActions); // for diffing text fragments const diffMathPatch = new _diffMatchPatch.default(); // parse the base html message as a DOM tree, to which we'll apply the differences found. // fish out the div in which we wrapped the messages above with children[0]. const originalRootNode = new DOMParser().parseFromString(originalBody, "text/html").body.children[0]; for (let i = 0; i < diffActions.length; ++i) { const diff = diffActions[i]; renderDifferenceInDOM(originalRootNode, diff, diffMathPatch); // DiffDOM assumes in subsequent diffs route path that // the action was applied (e.g. that a removeElement action removed the element). // This is not the case for us. We render differences in the DOM tree, and don't apply them. // So we need to adjust the routes of the remaining diffs to account for this. adjustRoutes(diff, diffActions.slice(i + 1)); } // take the html out of the modified DOM tree again const safeBody = originalRootNode.innerHTML; const className = (0, _classnames.default)({ 'mx_EventTile_body': true, 'markdown-body': true }); return /*#__PURE__*/_react.default.createElement("span", { key: "body", className: className, dangerouslySetInnerHTML: { __html: safeBody }, dir: "auto" }); } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy91dGlscy9NZXNzYWdlRGlmZlV0aWxzLmpzIl0sIm5hbWVzIjpbImRlY29kZUVudGl0aWVzIiwidGV4dGFyZWEiLCJzdHJpbmciLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJpbm5lckhUTUwiLCJ2YWx1ZSIsInRleHRUb0h0bWwiLCJ0ZXh0IiwiY29udGFpbmVyIiwidGV4dENvbnRlbnQiLCJnZXRTYW5pdGl6ZWRIdG1sQm9keSIsImNvbnRlbnQiLCJvcHRzIiwic3RyaXBSZXBseUZhbGxiYWNrIiwicmV0dXJuU3RyaW5nIiwiZm9ybWF0Iiwid3JhcEluc2VydGlvbiIsImNoaWxkIiwid3JhcHBlciIsImNsYXNzTmFtZSIsImFwcGVuZENoaWxkIiwid3JhcERlbGV0aW9uIiwiZmluZFJlZk5vZGVzIiwicm9vdCIsInJvdXRlIiwiaXNBZGRpdGlvbiIsInJlZk5vZGUiLCJyZWZQYXJlbnROb2RlIiwiZW5kIiwibGVuZ3RoIiwiaSIsImNoaWxkTm9kZXMiLCJkaWZmVHJlZVRvRE9NIiwiZGVzYyIsIm5vZGVOYW1lIiwic3RyaW5nQXNUZXh0Tm9kZSIsImRhdGEiLCJub2RlIiwiYXR0cmlidXRlcyIsImtleSIsIk9iamVjdCIsImVudHJpZXMiLCJzZXRBdHRyaWJ1dGUiLCJjaGlsZERlc2MiLCJpbnNlcnRCZWZvcmUiLCJwYXJlbnQiLCJuZXh0U2libGluZyIsImlzUm91dGVPZk5leHRTaWJsaW5nIiwicm91dGUxIiwicm91dGUyIiwibGFzdEQxSWR4IiwiYWRqdXN0Um91dGVzIiwiZGlmZiIsInJlbWFpbmluZ0RpZmZzIiwiYWN0aW9uIiwiYWR2YW5jZSIsInJkIiwiY3JlYXRlVGV4dE5vZGUiLCJyZW5kZXJEaWZmZXJlbmNlSW5ET00iLCJvcmlnaW5hbFJvb3ROb2RlIiwiZGlmZk1hdGhQYXRjaCIsImRlbE5vZGUiLCJvbGRWYWx1ZSIsImluc05vZGUiLCJuZXdWYWx1ZSIsInBhcmVudE5vZGUiLCJyZXBsYWNlQ2hpbGQiLCJlbGVtZW50IiwidGV4dERpZmZzIiwiZGlmZl9tYWluIiwiZGlmZl9jbGVhbnVwU2VtYW50aWMiLCJtb2RpZmllciIsInRleHREaWZmTm9kZSIsImNsb25lTm9kZSIsInVwZGF0ZWROb2RlIiwibmFtZSIsInJlbW92ZUF0dHJpYnV0ZSIsImNvbnNvbGUiLCJ3YXJuIiwicm91dGVJc0VxdWFsIiwicjEiLCJyMiIsInNvbWUiLCJlIiwiZmlsdGVyQ2FuY2VsaW5nT3V0RGlmZnMiLCJvcmlnaW5hbERpZmZBY3Rpb25zIiwiZGlmZkFjdGlvbnMiLCJzbGljZSIsIm5leHREaWZmIiwiY2FuY2Vsc091dCIsInNwbGljZSIsImVkaXRCb2R5RGlmZlRvSHRtbCIsIm9yaWdpbmFsQ29udGVudCIsImVkaXRDb250ZW50Iiwib3JpZ2luYWxCb2R5IiwiZWRpdEJvZHkiLCJkZCIsIkRpZmZET00iLCJvcmlnaW5hbGRpZmZBY3Rpb25zIiwiRGlmZk1hdGNoUGF0Y2giLCJET01QYXJzZXIiLCJwYXJzZUZyb21TdHJpbmciLCJib2R5IiwiY2hpbGRyZW4iLCJzYWZlQm9keSIsIl9faHRtbCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBZ0JBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQXBCQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFRQSxNQUFNQSxjQUFjLEdBQUksWUFBVztBQUMvQixNQUFJQyxRQUFRLEdBQUcsSUFBZjtBQUNBLFNBQU8sVUFBU0MsTUFBVCxFQUFpQjtBQUNwQixRQUFJLENBQUNELFFBQUwsRUFBZTtBQUNYQSxNQUFBQSxRQUFRLEdBQUdFLFFBQVEsQ0FBQ0MsYUFBVCxDQUF1QixVQUF2QixDQUFYO0FBQ0g7O0FBQ0RILElBQUFBLFFBQVEsQ0FBQ0ksU0FBVCxHQUFxQkgsTUFBckI7QUFDQSxXQUFPRCxRQUFRLENBQUNLLEtBQWhCO0FBQ0gsR0FORDtBQU9ILENBVHNCLEVBQXZCOztBQVdBLFNBQVNDLFVBQVQsQ0FB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QWEsR0FBRyxJQUFJdUMsdUJBQUosRUFBdEIsQ0FaNkQsQ0FhN0Q7QUFDQTs7QUFDQSxRQUFNeEMsZ0JBQWdCLEdBQUcsSUFBSXlDLFNBQUosR0FBZ0JDLGVBQWhCLENBQWdDUCxZQUFoQyxFQUE4QyxXQUE5QyxFQUEyRFEsSUFBM0QsQ0FBZ0VDLFFBQWhFLENBQXlFLENBQXpFLENBQXpCOztBQUNBLE9BQUssSUFBSXpFLENBQUMsR0FBRyxDQUFiLEVBQWdCQSxDQUFDLEdBQUd3RCxXQUFXLENBQUN6RCxNQUFoQyxFQUF3QyxFQUFFQyxDQUExQyxFQUE2QztBQUN6QyxVQUFNc0IsSUFBSSxHQUFHa0MsV0FBVyxDQUFDeEQsQ0FBRCxDQUF4QjtBQUNBNEIsSUFBQUEscUJBQXFCLENBQUNDLGdCQUFELEVBQW1CUCxJQUFuQixFQUF5QlEsYUFBekIsQ0FBckIsQ0FGeUMsQ0FHekM7QUFDQTtBQUNBO0FBQ0E7O0FBQ0FULElBQUFBLFlBQVksQ0FBQ0MsSUFBRCxFQUFPa0MsV0FBVyxDQUFDQyxLQUFaLENBQWtCekQsQ0FBQyxHQUFHLENBQXRCLENBQVAsQ0FBWjtBQUNILEdBeEI0RCxDQXlCN0Q7OztBQUNBLFFBQU0wRSxRQUFRLEdBQUc3QyxnQkFBZ0IsQ0FBQ3ZELFNBQWxDO0FBQ0EsUUFBTWUsU0FBUyxHQUFHLHlCQUFXO0FBQ3pCLHlCQUFxQixJQURJO0FBRXpCLHFCQUFpQjtBQUZRLEdBQVgsQ0FBbEI7QUFJQSxzQkFBTztBQUFNLElBQUEsR0FBRyxFQUFDLE1BQVY7QUFBaUIsSUFBQSxTQUFTLEVBQUVBLFNBQTVCO0FBQXVDLElBQUEsdUJBQXVCLEVBQUU7QUFBRXNGLE1BQUFBLE1BQU0sRUFBRUQ7QUFBVixLQUFoRTtBQUFzRixJQUFBLEdBQUcsRUFBQztBQUExRixJQUFQO0FBQ0giLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMTkgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4qL1xuXG5pbXBvcnQgUmVhY3QgZnJvbSAncmVhY3QnO1xuaW1wb3J0IGNsYXNzTmFtZXMgZnJvbSAnY2xhc3NuYW1lcyc7XG5pbXBvcnQgRGlmZk1hdGNoUGF0Y2ggZnJvbSAnZGlmZi1tYXRjaC1wYXRjaCc7XG5pbXBvcnQge0RpZmZET019IGZyb20gXCJkaWZmLWRvbVwiO1xuaW1wb3J0IHsgY2hlY2tCbG9ja05vZGUsIGJvZHlUb0h0bWwgfSBmcm9tIFwiLi4vSHRtbFV0aWxzXCI7XG5cbmNvbnN0IGRlY29kZUVudGl0aWVzID0gKGZ1bmN0aW9uKCkge1xuICAgIGxldCB0ZXh0YXJlYSA9IG51bGw7XG4gICAgcmV0dXJuIGZ1bmN0aW9uKHN0cmluZykge1xuICAgICAgICBpZiAoIXRleHRhcmVhKSB7XG4gICAgICAgICAgICB0ZXh0YXJlYSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJ0ZXh0YXJlYVwiKTtcbiAgICAgICAgfVxuICAgICAgICB0ZXh0YXJlYS5pbm5lckhUTUwgPSBzdHJpbmc7XG4gICAgICAgIHJldHVybiB0ZXh0YXJlYS52YWx1ZTtcbiAgICB9O1xufSkoKTtcblxuZnVuY3Rpb24gdGV4dFRvSHRtbCh0ZXh0KSB7XG4gICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICBjb250YWluZXIudGV4dENvbnRlbnQgPSB0ZXh0O1xuICAgIHJldHVybiBjb250YWluZXIuaW5uZXJIVE1MO1xufVxuXG5mdW5jdGlvbiBnZXRTYW5pdGl6ZWRIdG1sQm9keShjb250ZW50KSB7XG4gICAgY29uc3Qgb3B0cyA9IHtcbiAgICAgICAgc3RyaXBSZXBseUZhbGxiYWNrOiB0cnVlLFxuICAgICAgICByZXR1cm5TdHJpbmc6IHRydWUsXG4gICAgfTtcbiAgICBpZiAoY29udGVudC5mb3JtYXQgPT09IFwib3JnLm1hdHJpeC5jdXN0b20uaHRtbFwiKSB7XG4gICAgICAgIHJldHVybiBib2R5VG9IdG1sKGNvbnRlbnQsIG51bGwsIG9wdHMpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIC8vIGNvbnZlcnQgdGhlIHN0cmluZyB0byBzb21ldGhpbmcgdGhhdCBjYW4gYmUgc2FmZWx5XG4gICAgICAgIC8vIGVtYmVkZGVkIGluIGFuIGh0bWwgZG9jdW1lbnQsIGUuZy4gdXNlIGh0bWwgZW50aXRpZXMgd2hlcmUgbmVlZGVkXG4gICAgICAgIC8vIFRoaXMgaXMgYWxzbyBuZWVkZWQgc28gdGhhdCBEaWZmRE9NIHdvdWxkbid0IGludGVycHJldCBzb21ldGhpbmdcbiAgICAgICAgLy8gYXMgYSB0YWcgd2hlbiBzb21lYm9keSB0eXBlcyBlLmcuIFwiPC9zYXJjYXNtPlwiXG5cbiAgICAgICAgLy8gYXMgb3Bwb3NlZCB0byBib2R5VG9IdG1sLCBoZXJlIHdlIGFsc28gcmVuZGVyXG4gICAgICAgIC8vIHRleHQgbWVzc2FnZXMgd2l0aCBkYW5nZXJvdXNseVNldElubmVySFRNTCwgdG8gdW5pZnlcbiAgICAgICAgLy8gdGhlIGNvZGUgcGF0aHMgYW5kIGJlY2F1c2Ugd2UgbmVlZCBodG1sIHRvIHNob3cgZGlmZmVyZW5jZXNcbiAgICAgICAgcmV0dXJuIHRleHRUb0h0bWwoYm9keVRvSHRtbChjb250ZW50LCBudWxsLCBvcHRzKSk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiB3cmFwSW5zZXJ0aW9uKGNoaWxkKSB7XG4gICAgY29uc3Qgd3JhcHBlciA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoY2hlY2tCbG9ja05vZGUoY2hpbGQpID8gXCJkaXZcIiA6IFwic3BhblwiKTtcbiAgICB3cmFwcGVyLmNsYXNzTmFtZSA9IFwibXhfRWRpdEhpc3RvcnlNZXNzYWdlX2luc2VydGlvblwiO1xuICAgIHdyYXBwZXIuYXBwZW5kQ2hpbGQoY2hpbGQpO1xuICAgIHJldHVybiB3cmFwcGVyO1xufVxuXG5mdW5jdGlvbiB3cmFwRGVsZXRpb24oY2hpbGQpIHtcbiAgICBjb25zdCB3cmFwcGVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChjaGVja0Jsb2NrTm9kZShjaGlsZCkgPyBcImRpdlwiIDogXCJzcGFuXCIpO1xuICAgIHdyYXBwZXIuY2xhc3NOYW1lID0gXCJteF9FZGl0SGlzdG9yeU1lc3NhZ2VfZGVsZXRpb25cIjtcbiAgICB3cmFwcGVyLmFwcGVuZENoaWxkKGNoaWxkKTtcbiAgICByZXR1cm4gd3JhcHBlcjtcbn1cblxuZnVuY3Rpb24gZmluZFJlZk5vZGVzKHJvb3QsIHJvdXRlLCBpc0FkZGl0aW9uKSB7XG4gICAgbGV0IHJlZk5vZGUgPSByb290O1xuICAgIGxldCByZWZQYXJlbnROb2RlO1xuICAgIGNvbnN0IGVuZCA9IGlzQWRkaXRpb24gPyByb3V0ZS5sZW5ndGggLSAxIDogcm91dGUubGVuZ3RoO1xuICAgIGZvciAobGV0IGkgPSAwOyBpIDwgZW5kOyArK2kpIHtcbiAgICAgICAgcmVmUGFyZW50Tm9kZSA9IHJlZk5vZGU7XG4gICAgICAgIHJlZk5vZGUgPSByZWZOb2RlLmNoaWxkTm9kZXNbcm91dGVbaV1dO1xuICAgIH1cbiAgICByZXR1cm4ge3JlZk5vZGUsIHJlZlBhcmVudE5vZGV9O1xufVxuXG5mdW5jdGlvbiBkaWZmVHJlZVRvRE9NKGRlc2MpIHtcbiAgICBpZiAoZGVzYy5ub2RlTmFtZSA9PT0gXCIjdGV4dFwiKSB7XG4gICAgICAgIHJldHVybiBzdHJpbmdBc1RleHROb2RlKGRlc2MuZGF0YSk7XG4gICAgfSBlbHNlIHtcbiAgICAgICAgY29uc3Qgbm9kZSA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoZGVzYy5ub2RlTmFtZSk7XG4gICAgICAgIGlmIChkZXNjLmF0dHJpYnV0ZXMpIHtcbiAgICAgICAgICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIE9iamVjdC5lbnRyaWVzKGRlc2MuYXR0cmlidXRlcykpIHtcbiAgICAgICAgICAgICAgICBub2RlLnNldEF0dHJpYnV0ZShrZXksIHZhbHVlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICBpZiAoZGVzYy5jaGlsZE5vZGVzKSB7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IGNoaWxkRGVzYyBvZiBkZXNjLmNoaWxkTm9kZXMpIHtcbiAgICAgICAgICAgICAgICBub2RlLmFwcGVuZENoaWxkKGRpZmZUcmVlVG9ET00oY2hpbGREZXNjKSk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIG5vZGU7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBpbnNlcnRCZWZvcmUocGFyZW50LCBuZXh0U2libGluZywgY2hpbGQpIHtcbiAgICBpZiAobmV4dFNpYmxpbmcpIHtcbiAgICAgICAgcGFyZW50Lmluc2VydEJlZm9yZShjaGlsZCwgbmV4dFNpYmxpbmcpO1xuICAgIH0gZWxzZSB7XG4gICAgICAgIHBhcmVudC5hcHBlbmRDaGlsZChjaGlsZCk7XG4gICAgfVxufVxuXG5mdW5jdGlvbiBpc1JvdXRlT2ZOZXh0U2libGluZyhyb3V0ZTEsIHJvdXRlMikge1xuICAgIC8vIHJvdXRlcyBhcmUgYXJyYXlzIHdpdGggaW5kaWNlcyxcbiAgICAvLyB0byBiZSBpbnRlcnByZXRlZCBhcyBhIHBhdGggaW4gdGhlIGRvbSB0cmVlXG5cbiAgICAvLyBlbnN1cmUgc2FtZSBwYXJlbnRcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IHJvdXRlMS5sZW5ndGggLSAxOyArK2kpIHtcbiAgICAgICAgaWYgKHJvdXRlMVtpXSAhPT0gcm91dGUyW2ldKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICB9XG4gICAgLy8gdGhlIHJvdXRlMiBpcyBvbmx5IGFmZmVjdGVkIGJ5IHRoZSBkaWZmIG9mIHJvdXRlMVxuICAgIC8vIGluc2VydGluZyBhbiBlbGVtZW50IGlmIHRoZSBpbmRleCBhdCB0aGUgbGV2ZWwgb2YgdGhlXG4gICAgLy8gbGFzdCBlbGVtZW50IG9mIHJvdXRlMSBiZWluZyBsYXJnZXJcbiAgICAvLyAoZS5nLiBjb21pbmcgYmVoaW5kIHJvdXRlMSBhdCB0aGF0IGxldmVsKVxuICAgIGNvbnN0IGxhc3REMUlkeCA9IHJvdXRlMS5sZW5ndGggLSAxO1xuICAgIHJldHVybiByb3V0ZTJbbGFzdEQxSWR4XSA+PSByb3V0ZTFbbGFzdEQxSWR4XTtcbn1cblxuZnVuY3Rpb24gYWRqdXN0Um91dGVzKGRpZmYsIHJlbWFpbmluZ0RpZmZzKSB7XG4gICAgaWYgKGRpZmYuYWN0aW9uID09PSBcInJlbW92ZVRleHRFbGVtZW50XCIgfHwgZGlmZi5hY3Rpb24gPT09IFwicmVtb3ZlRWxlbWVudFwiKSB7XG4gICAgICAgIC8vIGFzIHJlbW92ZWQgdGV4dCBpcyBub3QgcmVtb3ZlZCBmcm9tIHRoZSBodG1sLCBidXQgbWFya2VkIGFzIGRlbGV0ZWQsXG4gICAgICAgIC8vIHdlIG5lZWQgdG8gcmVhZGp1c3QgaW5kaWNlcyB0aGF0IGFzc3VtZSB0aGUgY3VycmVudCBub2RlIGhhcyBiZWVuIHJlbW92ZWQuXG4gICAgICAgIGNvbnN0IGFkdmFuY2UgPSAxO1xuICAgICAgICBmb3IgKGNvbnN0IHJkIG9mIHJlbWFpbmluZ0RpZmZzKSB7XG4gICAgICAgICAgICBpZiAoaXNSb3V0ZU9mTmV4dFNpYmxpbmcoZGlmZi5yb3V0ZSwgcmQucm91dGUpKSB7XG4gICAgICAgICAgICAgICAgcmQucm91dGVbZGlmZi5yb3V0ZS5sZW5ndGggLSAxXSArPSBhZHZhbmNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuXG5mdW5jdGlvbiBzdHJpbmdBc1RleHROb2RlKHN0cmluZykge1xuICAgIHJldHVybiBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZShkZWNvZGVFbnRpdGllcyhzdHJpbmcpKTtcbn1cblxuZnVuY3Rpb24gcmVuZGVyRGlmZmVyZW5jZUluRE9NKG9yaWdpbmFsUm9vdE5vZGUsIGRpZmYsIGRpZmZNYXRoUGF0Y2gpIHtcbiAgICBjb25zdCB7cmVmTm9kZSwgcmVmUGFyZW50Tm9kZX0gPSBmaW5kUmVmTm9kZXMob3JpZ2luYWxSb290Tm9kZSwgZGlmZi5yb3V0ZSk7XG4gICAgc3dpdGNoIChkaWZmLmFjdGlvbikge1xuICAgICAgICBjYXNlIFwicmVwbGFjZUVsZW1lbnRcIjoge1xuICAgICAgICAgICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIik7XG4gICAgICAgICAgICBjb25zdCBkZWxOb2RlID0gd3JhcERlbGV0aW9uKGRpZmZUcmVlVG9ET00oZGlmZi5vbGRWYWx1ZSkpO1xuICAgICAgICAgICAgY29uc3QgaW5zTm9kZSA9IHdyYXBJbnNlcnRpb24oZGlmZlRyZWVUb0RPTShkaWZmLm5ld1ZhbHVlKSk7XG4gICAgICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoZGVsTm9kZSk7XG4gICAgICAgICAgICBjb250YWluZXIuYXBwZW5kQ2hpbGQoaW5zTm9kZSk7XG4gICAgICAgICAgICByZWZOb2RlLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGNvbnRhaW5lciwgcmVmTm9kZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjYXNlIFwicmVtb3ZlVGV4dEVsZW1lbnRcIjoge1xuICAgICAgICAgICAgY29uc3QgZGVsTm9kZSA9IHdyYXBEZWxldGlvbihzdHJpbmdBc1RleHROb2RlKGRpZmYudmFsdWUpKTtcbiAgICAgICAgICAgIHJlZk5vZGUucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoZGVsTm9kZSwgcmVmTm9kZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBjYXNlIFwicmVtb3ZlRWxlbWVudFwiOiB7XG4gICAgICAgICAgICBjb25zdCBkZWxOb2RlID0gd3JhcERlbGV0aW9uKGRpZmZUcmVlVG9ET00oZGlmZi5lbGVtZW50KSk7XG4gICAgICAgICAgICByZWZOb2RlLnBhcmVudE5vZGUucmVwbGFjZUNoaWxkKGRlbE5vZGUsIHJlZk5vZGUpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgY2FzZSBcIm1vZGlmeVRleHRFbGVtZW50XCI6IHtcbiAgICAgICAgICAgIGNvbnN0IHRleHREaWZmcyA9IGRpZmZNYXRoUGF0Y2guZGlmZl9tYWluKGRpZmYub2xkVmFsdWUsIGRpZmYubmV3VmFsdWUpO1xuICAgICAgICAgICAgZGlmZk1hdGhQYXRjaC5kaWZmX2NsZWFudXBTZW1hbnRpYyh0ZXh0RGlmZnMpO1xuICAgICAgICAgICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIik7XG4gICAgICAgICAgICBmb3IgKGNvbnN0IFttb2RpZmllciwgdGV4dF0gb2YgdGV4dERpZmZzKSB7XG4gICAgICAgICAgICAgICAgbGV0IHRleHREaWZmTm9kZSA9IHN0cmluZ0FzVGV4dE5vZGUodGV4dCk7XG4gICAgICAgICAgICAgICAgaWYgKG1vZGlmaWVyIDwgMCkge1xuICAgICAgICAgICAgICAgICAgICB0ZXh0RGlmZk5vZGUgPSB3cmFwRGVsZXRpb24odGV4dERpZmZOb2RlKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKG1vZGlmaWVyID4gMCkge1xuICAgICAgICAgICAgICAgICAgICB0ZXh0RGlmZk5vZGUgPSB3cmFwSW5zZXJ0aW9uKHRleHREaWZmTm9kZSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZCh0ZXh0RGlmZk5vZGUpO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmVmTm9kZS5wYXJlbnROb2RlLnJlcGxhY2VDaGlsZChjb250YWluZXIsIHJlZk5vZGUpO1xuICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgICAgY2FzZSBcImFkZEVsZW1lbnRcIjoge1xuICAgICAgICAgICAgY29uc3QgaW5zTm9kZSA9IHdyYXBJbnNlcnRpb24oZGlmZlRyZWVUb0RPTShkaWZmLmVsZW1lbnQpKTtcbiAgICAgICAgICAgIGluc2VydEJlZm9yZShyZWZQYXJlbnROb2RlLCByZWZOb2RlLCBpbnNOb2RlKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGNhc2UgXCJhZGRUZXh0RWxlbWVudFwiOiB7XG4gICAgICAgICAgICAvLyBYWFg6IHNvbWV0aW1lcyBkaWZmRE9NIHNheXMgaW5zZXJ0IGEgbmV3bGluZSB3aGVuIHRoZXJlIHNob3VsZG4ndCBiZSBvbmVcbiAgICAgICAgICAgIC8vIGJ1dCB3ZSBtdXN0IGluc2VydCB0aGUgbm9kZSBhbnl3YXkgc28gdGhhdCB3ZSBkb24ndCBicmVhayB0aGUgcm91dGUgY2hpbGQgSURzLlxuICAgICAgICAgICAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9maWR1c3dyaXRlci9kaWZmRE9NL2lzc3Vlcy8xMDBcbiAgICAgICAgICAgIGNvbnN0IGluc05vZGUgPSB3cmFwSW5zZXJ0aW9uKHN0cmluZ0FzVGV4dE5vZGUoZGlmZi52YWx1ZSAhPT0gXCJcXG5cIiA/IGRpZmYudmFsdWUgOiBcIlwiKSk7XG4gICAgICAgICAgICBpbnNlcnRCZWZvcmUocmVmUGFyZW50Tm9kZSwgcmVmTm9kZSwgaW5zTm9kZSk7XG4gICAgICAgICAgICBicmVhaztcbiAgICAgICAgfVxuICAgICAgICAvLyBlLmcuIHdoZW4gY2hhbmdpbmcgYSB0aGUgaHJlZiBvZiBhIGxpbmssXG4gICAgICAgIC8vIHNob3cgdGhlIGxpbmsgd2l0aCBvbGQgaHJlZiBhcyByZW1vdmVkIGFuZCB3aXRoIHRoZSBuZXcgaHJlZiBhcyBhZGRlZFxuICAgICAgICBjYXNlIFwicmVtb3ZlQXR0cmlidXRlXCI6XG4gICAgICAgIGNhc2UgXCJhZGRBdHRyaWJ1dGVcIjpcbiAgICAgICAgY2FzZSBcIm1vZGlmeUF0dHJpYnV0ZVwiOiB7XG4gICAgICAgICAgICBjb25zdCBkZWxOb2RlID0gd3JhcERlbGV0aW9uKHJlZk5vZGUuY2xvbmVOb2RlKHRydWUpKTtcbiAgICAgICAgICAgIGNvbnN0IHVwZGF0ZWROb2RlID0gcmVmTm9kZS5jbG9uZU5vZGUodHJ1ZSk7XG4gICAgICAgICAgICBpZiAoZGlmZi5hY3Rpb24gPT09IFwiYWRkQXR0cmlidXRlXCIgfHwgZGlmZi5hY3Rpb24gPT09IFwibW9kaWZ5QXR0cmlidXRlXCIpIHtcbiAgICAgICAgICAgICAgICB1cGRhdGVkTm9kZS5zZXRBdHRyaWJ1dGUoZGlmZi5uYW1lLCBkaWZmLm5ld1ZhbHVlKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdXBkYXRlZE5vZGUucmVtb3ZlQXR0cmlidXRlKGRpZmYubmFtZSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBpbnNOb2RlID0gd3JhcEluc2VydGlvbih1cGRhdGVkTm9kZSk7XG4gICAgICAgICAgICBjb25zdCBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KGNoZWNrQmxvY2tOb2RlKHJlZk5vZGUpID8gXCJkaXZcIiA6IFwic3BhblwiKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChkZWxOb2RlKTtcbiAgICAgICAgICAgIGNvbnRhaW5lci5hcHBlbmRDaGlsZChpbnNOb2RlKTtcbiAgICAgICAgICAgIHJlZk5vZGUucGFyZW50Tm9kZS5yZXBsYWNlQ2hpbGQoY29udGFpbmVyLCByZWZOb2RlKTtcbiAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICB9XG4gICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAvLyBTaG91bGQgbm90IGhhcHBlbiAobW9kaWZ5Q29tbWVudCwgPz8/KVxuICAgICAgICAgICAgY29uc29sZS53YXJuKFwiTWVzc2FnZURpZmZVdGlsczo6ZWRpdEJvZHlEaWZmVG9IdG1sOiBkaWZmIGFjdGlvbiBub3Qgc3VwcG9ydGVkIGF0bVwiLCBkaWZmKTtcbiAgICB9XG59XG5cbmZ1bmN0aW9uIHJvdXRlSXNFcXVhbChyMSwgcjIpIHtcbiAgICByZXR1cm4gcjEubGVuZ3RoID09PSByMi5sZW5ndGggJiYgIXIxLnNvbWUoKGUsIGkpID0+IGUgIT09IHIyW2ldKTtcbn1cblxuLy8gd29ya2Fyb3VuZCBmb3IgaHR0cHM6Ly9naXRodWIuY29tL2ZpZHVzd3JpdGVyL2RpZmZET00vaXNzdWVzLzkwXG5mdW5jdGlvbiBmaWx0ZXJDYW5jZWxpbmdPdXREaWZmcyhvcmlnaW5hbERpZmZBY3Rpb25zKSB7XG4gICAgY29uc3QgZGlmZkFjdGlvbnMgPSBvcmlnaW5hbERpZmZBY3Rpb25zLnNsaWNlKCk7XG5cbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRpZmZBY3Rpb25zLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGNvbnN0IGRpZmYgPSBkaWZmQWN0aW9uc1tpXTtcbiAgICAgICAgaWYgKGRpZmYuYWN0aW9uID09PSBcInJlbW92ZVRleHRFbGVtZW50XCIpIHtcbiAgICAgICAgICAgIGNvbnN0IG5leHREaWZmID0gZGlmZkFjdGlvbnNbaSArIDFdO1xuICAgICAgICAgICAgY29uc3QgY2FuY2Vsc091dCA9IG5leHREaWZmICYmXG4gICAgICAgICAgICAgICAgbmV4dERpZmYuYWN0aW9uID09PSBcImFkZFRleHRFbGVtZW50XCIgJiZcbiAgICAgICAgICAgICAgICBuZXh0RGlmZi50ZXh0ID09PSBkaWZmLnRleHQgJiZcbiAgICAgICAgICAgICAgICByb3V0ZUlzRXF1YWwobmV4dERpZmYucm91dGUsIGRpZmYucm91dGUpO1xuXG4gICAgICAgICAgICBpZiAoY2FuY2Vsc091dCkge1xuICAgICAgICAgICAgICAgIGRpZmZBY3Rpb25zLnNwbGljZShpLCAyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiBkaWZmQWN0aW9ucztcbn1cblxuLyoqXG4gKiBSZW5kZXJzIGEgbWVzc2FnZSB3aXRoIHRoZSBjaGFuZ2VzIG1hZGUgaW4gYW4gZWRpdCBzaG93biB2aXN1YWxseS5cbiAqIEBwYXJhbSB7b2JqZWN0fSBvcmlnaW5hbENvbnRlbnQgdGhlIGNvbnRlbnQgZm9yIHRoZSBiYXNlIG1lc3NhZ2VcbiAqIEBwYXJhbSB7b2JqZWN0fSBlZGl0Q29udGVudCB0aGUgY29udGVudCBmb3IgdGhlIGVkaXQgbWVzc2FnZVxuICogQHJldHVybiB7b2JqZWN0fSBhIHJlYWN0IGVsZW1lbnQgc2ltaWxhciB0byB3aGF0IGBib2R5VG9IdG1sYCByZXR1cm5zXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBlZGl0Qm9keURpZmZUb0h0bWwob3JpZ2luYWxDb250ZW50LCBlZGl0Q29udGVudCkge1xuICAgIC8vIHdyYXAgdGhlIGJvZHkgaW4gYSBkaXYsIERpZmZET00gbmVlZHMgYSByb290IGVsZW1lbnRcbiAgICBjb25zdCBvcmlnaW5hbEJvZHkgPSBgPGRpdj4ke2dldFNhbml0aXplZEh0bWxCb2R5KG9yaWdpbmFsQ29udGVudCl9PC9kaXY+YDtcbiAgICBjb25zdCBlZGl0Qm9keSA9IGA8ZGl2PiR7Z2V0U2FuaXRpemVkSHRtbEJvZHkoZWRpdENvbnRlbnQpfTwvZGl2PmA7XG4gICAgY29uc3QgZGQgPSBuZXcgRGlmZkRPTSgpO1xuICAgIC8vIGRpZmZBY3Rpb25zIGlzIGFuIGFycmF5IG9mIG9iamVjdHMgd2l0aCBhdCBsZWFzdCBhIGBhY3Rpb25gIGFuZCBgcm91dGVgXG4gICAgLy8gcHJvcGVydHkuIGBhY3Rpb25gIHRlbGxzIHVzIHdoYXQgdGhlIGRpZmYgb2JqZWN0IGNoYW5nZXMsIGFuZCBgcm91dGVgIHdoZXJlLlxuICAgIC8vIGByb3V0ZWAgaXMgYSBwYXRoIG9uIHRoZSBET00gdHJlZSBleHByZXNzZWQgYXMgYW4gYXJyYXkgb2YgaW5kaWNlcy5cbiAgICBjb25zdCBvcmlnaW5hbGRpZmZBY3Rpb25zID0gZGQuZGlmZihvcmlnaW5hbEJvZHksIGVkaXRCb2R5KTtcbiAgICAvLyB3b3JrIGFyb3VuZCBodHRwczovL2dpdGh1Yi5jb20vZmlkdXN3cml0ZXIvZGlmZkRPTS9pc3N1ZXMvOTBcbiAgICBjb25zdCBkaWZmQWN0aW9ucyA9IGZpbHRlckNhbmNlbGluZ091dERpZmZzKG9yaWdpbmFsZGlmZkFjdGlvbnMpO1xuICAgIC8vIGZvciBkaWZmaW5nIHRleHQgZnJhZ21lbnRzXG4gICAgY29uc3QgZGlmZk1hdGhQYXRjaCA9IG5ldyBEaWZmTWF0Y2hQYXRjaCgpO1xuICAgIC8vIHBhcnNlIHRoZSBiYXNlIGh0bWwgbWVzc2FnZSBhcyBhIERPTSB0cmVlLCB0byB3aGljaCB3ZSdsbCBhcHBseSB0aGUgZGlmZmVyZW5jZXMgZm91bmQuXG4gICAgLy8gZmlzaCBvdXQgdGhlIGRpdiBpbiB3aGljaCB3ZSB3cmFwcGVkIHRoZSBtZXNzYWdlcyBhYm92ZSB3aXRoIGNoaWxkcmVuWzBdLlxuICAgIGNvbnN0IG9yaWdpbmFsUm9vdE5vZGUgPSBuZXcgRE9NUGFyc2VyKCkucGFyc2VGcm9tU3RyaW5nKG9yaWdpbmFsQm9keSwgXCJ0ZXh0L2h0bWxcIikuYm9keS5jaGlsZHJlblswXTtcbiAgICBmb3IgKGxldCBpID0gMDsgaSA8IGRpZmZBY3Rpb25zLmxlbmd0aDsgKytpKSB7XG4gICAgICAgIGNvbnN0IGRpZmYgPSBkaWZmQWN0aW9uc1tpXTtcbiAgICAgICAgcmVuZGVyRGlmZmVyZW5jZUluRE9NKG9yaWdpbmFsUm9vdE5vZGUsIGRpZmYsIGRpZmZNYXRoUGF0Y2gpO1xuICAgICAgICAvLyBEaWZmRE9NIGFzc3VtZXMgaW4gc3Vic2VxdWVudCBkaWZmcyByb3V0ZSBwYXRoIHRoYXRcbiAgICAgICAgLy8gdGhlIGFjdGlvbiB3YXMgYXBwbGllZCAoZS5nLiB0aGF0IGEgcmVtb3ZlRWxlbWVudCBhY3Rpb24gcmVtb3ZlZCB0aGUgZWxlbWVudCkuXG4gICAgICAgIC8vIFRoaXMgaXMgbm90IHRoZSBjYXNlIGZvciB1cy4gV2UgcmVuZGVyIGRpZmZlcmVuY2VzIGluIHRoZSBET00gdHJlZSwgYW5kIGRvbid0IGFwcGx5IHRoZW0uXG4gICAgICAgIC8vIFNvIHdlIG5lZWQgdG8gYWRqdXN0IHRoZSByb3V0ZXMgb2YgdGhlIHJlbWFpbmluZyBkaWZmcyB0byBhY2NvdW50IGZvciB0aGlzLlxuICAgICAgICBhZGp1c3RSb3V0ZXMoZGlmZiwgZGlmZkFjdGlvbnMuc2xpY2UoaSArIDEpKTtcbiAgICB9XG4gICAgLy8gdGFrZSB0aGUgaHRtbCBvdXQgb2YgdGhlIG1vZGlmaWVkIERPTSB0cmVlIGFnYWluXG4gICAgY29uc3Qgc2FmZUJvZHkgPSBvcmlnaW5hbFJvb3ROb2RlLmlubmVySFRNTDtcbiAgICBjb25zdCBjbGFzc05hbWUgPSBjbGFzc05hbWVzKHtcbiAgICAgICAgJ214X0V2ZW50VGlsZV9ib2R5JzogdHJ1ZSxcbiAgICAgICAgJ21hcmtkb3duLWJvZHknOiB0cnVlLFxuICAgIH0pO1xuICAgIHJldHVybiA8c3BhbiBrZXk9XCJib2R5XCIgY2xhc3NOYW1lPXtjbGFzc05hbWV9IGRhbmdlcm91c2x5U2V0SW5uZXJIVE1MPXt7IF9faHRtbDogc2FmZUJvZHkgfX0gZGlyPVwiYXV0b1wiIC8+O1xufVxuIl19