matrix-react-sdk
Version:
SDK for matrix.org using React
402 lines (399 loc) • 73.7 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _server = require("react-dom/server");
var _logger = require("matrix-js-sdk/src/logger");
var _escapeHtml = _interopRequireDefault(require("escape-html"));
var _compoundWeb = require("@vector-im/compound-web");
var _Exporter = _interopRequireDefault(require("./Exporter"));
var _Media = require("../../customisations/Media");
var _Layout = require("../../settings/enums/Layout");
var _MessagePanel = require("../../components/structures/MessagePanel");
var _DateUtils = require("../../DateUtils");
var _Permalinks = require("../permalinks/Permalinks");
var _languageHandler = require("../../languageHandler");
var Avatar = _interopRequireWildcard(require("../../Avatar"));
var _EventTile = _interopRequireDefault(require("../../components/views/rooms/EventTile"));
var _DateSeparator = _interopRequireDefault(require("../../components/views/messages/DateSeparator"));
var _BaseAvatar = _interopRequireDefault(require("../../components/views/avatars/BaseAvatar"));
var _MatrixClientContext = _interopRequireDefault(require("../../contexts/MatrixClientContext"));
var _exportCSS = _interopRequireDefault(require("./exportCSS"));
var _TextForEvent = require("../../TextForEvent");
var _EventTileFactory = require("../../events/EventTileFactory");
var _exportJS = _interopRequireDefault(require("!!raw-loader!./exportJS"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/*
Copyright 2024 New Vector Ltd.
Copyright 2021-2023 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
class HTMLExporter extends _Exporter.default {
constructor(room, exportType, exportOptions, setProgressText) {
super(room, exportType, exportOptions, setProgressText);
(0, _defineProperty2.default)(this, "avatars", void 0);
(0, _defineProperty2.default)(this, "permalinkCreator", void 0);
(0, _defineProperty2.default)(this, "totalSize", void 0);
(0, _defineProperty2.default)(this, "mediaOmitText", void 0);
this.avatars = new Map();
this.permalinkCreator = new _Permalinks.RoomPermalinkCreator(this.room);
this.totalSize = 0;
this.mediaOmitText = !this.exportOptions.attachmentsIncluded ? (0, _languageHandler._t)("export_chat|media_omitted") : (0, _languageHandler._t)("export_chat|media_omitted_file_size");
}
async getRoomAvatar() {
let blob = undefined;
const avatarUrl = Avatar.avatarUrlForRoom(this.room, 32, 32, "crop");
const avatarPath = "room.png";
if (avatarUrl) {
try {
const image = await fetch(avatarUrl);
blob = await image.blob();
this.totalSize += blob.size;
this.addFile(avatarPath, blob);
} catch (err) {
_logger.logger.log("Failed to fetch room's avatar" + err);
}
}
const avatar = /*#__PURE__*/_react.default.createElement(_BaseAvatar.default, {
size: "32px",
name: this.room.name,
title: this.room.name,
url: blob ? avatarPath : ""
});
return (0, _server.renderToStaticMarkup)(avatar);
}
async wrapHTML(content, currentPage, nbPages) {
const roomAvatar = await this.getRoomAvatar();
const exportDate = (0, _DateUtils.formatFullDateNoDayNoTime)(new Date());
const creator = this.room.currentState.getStateEvents(_matrix.EventType.RoomCreate, "")?.getSender();
const creatorName = (creator ? this.room.getMember(creator)?.rawDisplayName : creator) || creator;
const exporter = this.room.client.getSafeUserId();
const exporterName = this.room.getMember(exporter)?.rawDisplayName;
const topic = this.room.currentState.getStateEvents(_matrix.EventType.RoomTopic, "")?.getContent()?.topic || "";
const safeCreatedText = (0, _escapeHtml.default)((0, _languageHandler._t)("export_chat|creator_summary", {
creatorName
}));
const safeExporter = (0, _escapeHtml.default)(exporter);
const safeRoomName = (0, _escapeHtml.default)(this.room.name);
const safeTopic = (0, _escapeHtml.default)(topic);
const safeExportedText = (0, _server.renderToStaticMarkup)( /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("export_chat|export_info", {
exportDate
}, {
roomName: () => /*#__PURE__*/_react.default.createElement("strong", null, safeRoomName),
exporterDetails: () => /*#__PURE__*/_react.default.createElement("a", {
href: `https://matrix.to/#/${encodeURIComponent(exporter)}`,
target: "_blank",
rel: "noopener noreferrer"
}, exporterName ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("strong", null, (0, _escapeHtml.default)(exporterName)), "I ", " (" + safeExporter + ")") : /*#__PURE__*/_react.default.createElement("strong", null, safeExporter))
})));
const safeTopicText = topic ? (0, _languageHandler._t)("export_chat|topic", {
topic: safeTopic
}) : "";
const previousMessagesLink = (0, _server.renderToStaticMarkup)(currentPage !== 0 ? /*#__PURE__*/_react.default.createElement("div", {
style: {
textAlign: "center"
}
}, /*#__PURE__*/_react.default.createElement("a", {
href: `./messages${currentPage === 1 ? "" : currentPage}.html`,
style: {
fontWeight: "bold"
}
}, (0, _languageHandler._t)("export_chat|previous_page"))) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null));
const nextMessagesLink = (0, _server.renderToStaticMarkup)(currentPage < nbPages - 1 ? /*#__PURE__*/_react.default.createElement("div", {
style: {
textAlign: "center",
margin: "10px"
}
}, /*#__PURE__*/_react.default.createElement("a", {
href: "./messages" + (currentPage + 2) + ".html",
style: {
fontWeight: "bold"
}
}, (0, _languageHandler._t)("export_chat|next_page"))) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null));
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link href="css/style.css" rel="stylesheet" />
<script src="js/script.js"></script>
<title>${(0, _languageHandler._t)("export_chat|html_title")}</title>
</head>
<body style="height: 100vh;" class="cpd-theme-light">
<div id="matrixchat" style="height: 100%; overflow: auto">
<div class="mx_MatrixChat_wrapper" aria-hidden="false">
<div class="mx_MatrixChat">
<main class="mx_RoomView">
<div class="mx_LegacyRoomHeader light-panel">
<div class="mx_LegacyRoomHeader_wrapper" aria-owns="mx_RightPanel">
<div class="mx_LegacyRoomHeader_avatar">
<div class="mx_DecoratedRoomAvatar">
${roomAvatar}
</div>
</div>
<div class="mx_LegacyRoomHeader_name">
<div
dir="auto"
class="mx_LegacyRoomHeader_nametext"
title="${safeRoomName}"
>
${safeRoomName}
</div>
</div>
<div class="mx_LegacyRoomHeader_topic" dir="auto"> ${safeTopic} </div>
</div>
</div>
${previousMessagesLink}
<div class="mx_MainSplit">
<div class="mx_RoomView_body">
<div
class="mx_RoomView_timeline mx_RoomView_timeline_rr_enabled"
>
<div
class="
mx_AutoHideScrollbar
mx_ScrollPanel
mx_RoomView_messagePanel
"
>
<div class="mx_RoomView_messageListWrapper">
<ol
class="mx_RoomView_MessageList"
aria-live="polite"
role="list"
>
${currentPage == 0 ? `<div class="mx_NewRoomIntro">
${roomAvatar}
<h2> ${safeRoomName} </h2>
<p> ${safeCreatedText} <br/><br/> ${safeExportedText} </p>
<br/>
<p> ${safeTopicText} </p>
</div>` : ""}
${content}
</ol>
</div>
</div>
</div>
<div class="mx_RoomView_statusArea">
<div class="mx_RoomView_statusAreaBox">
<div class="mx_RoomView_statusAreaBox_line"></div>
</div>
</div>
</div>
</div>
${nextMessagesLink}
</main>
</div>
</div>
</div>
<div id="snackbar"/>
</body>
</html>`;
}
getAvatarURL(event) {
const member = event.sender;
const avatarUrl = member?.getMxcAvatarUrl();
return avatarUrl ? (0, _Media.mediaFromMxc)(avatarUrl).getThumbnailOfSourceHttp(30, 30, "crop") : null;
}
async saveAvatarIfNeeded(event) {
const member = event.sender;
if (!this.avatars.has(member.userId)) {
try {
const avatarUrl = this.getAvatarURL(event);
this.avatars.set(member.userId, true);
const image = await fetch(avatarUrl);
const blob = await image.blob();
this.addFile(`users/${member.userId.replace(/:/g, "-")}.png`, blob);
} catch (err) {
_logger.logger.log("Failed to fetch user's avatar" + err);
}
}
}
getDateSeparator(event) {
const ts = event.getTs();
const dateSeparator = /*#__PURE__*/_react.default.createElement("li", {
key: ts
}, /*#__PURE__*/_react.default.createElement(_DateSeparator.default, {
forExport: true,
key: ts,
roomId: event.getRoomId(),
ts: ts
}));
return (0, _server.renderToStaticMarkup)(dateSeparator);
}
needsDateSeparator(event, prevEvent) {
if (!prevEvent) return true;
return (0, _DateUtils.wantsDateSeparator)(prevEvent.getDate() || undefined, event.getDate() || undefined);
}
getEventTile(mxEv, continuation) {
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_Export_EventWrapper",
id: mxEv.getId()
}, /*#__PURE__*/_react.default.createElement(_MatrixClientContext.default.Provider, {
value: this.room.client
}, /*#__PURE__*/_react.default.createElement(_compoundWeb.TooltipProvider, null, /*#__PURE__*/_react.default.createElement(_EventTile.default, {
mxEvent: mxEv,
continuation: continuation,
isRedacted: mxEv.isRedacted(),
replacingEventId: mxEv.replacingEventId(),
forExport: true,
alwaysShowTimestamps: true,
showUrlPreview: false,
checkUnmounting: () => false,
isTwelveHour: false,
last: false,
lastInSection: false,
permalinkCreator: this.permalinkCreator,
lastSuccessful: false,
isSelectedEvent: false,
showReactions: false,
layout: _Layout.Layout.Group,
showReadReceipts: false
}))));
}
async getEventTileMarkup(mxEv, continuation, filePath) {
const avatarUrl = this.getAvatarURL(mxEv);
const hasAvatar = !!avatarUrl;
if (hasAvatar) await this.saveAvatarIfNeeded(mxEv);
const EventTile = this.getEventTile(mxEv, continuation);
let eventTileMarkup;
if (mxEv.getContent().msgtype == _matrix.MsgType.Emote || mxEv.getContent().msgtype == _matrix.MsgType.Notice || mxEv.getContent().msgtype === _matrix.MsgType.Text) {
// to linkify textual events, we'll need lifecycle methods which won't be invoked in renderToString
// So, we'll have to render the component into a temporary root element
const tempRoot = document.createElement("div");
_reactDom.default.render(EventTile, tempRoot);
eventTileMarkup = tempRoot.innerHTML;
} else {
eventTileMarkup = (0, _server.renderToStaticMarkup)(EventTile);
}
if (filePath) {
const mxc = mxEv.getContent().url ?? mxEv.getContent().file?.url;
eventTileMarkup = eventTileMarkup.split(mxc).join(filePath);
}
eventTileMarkup = eventTileMarkup.replace(/<span class="mx_MFileBody_info_icon".*?>.*?<\/span>/, "");
if (hasAvatar) {
eventTileMarkup = eventTileMarkup.replace(encodeURI(avatarUrl).replace(/&/g, "&"), `users/${mxEv.sender.userId.replace(/:/g, "-")}.png`);
}
return eventTileMarkup;
}
createModifiedEvent(text, mxEv, italic = true) {
const modifiedContent = {
msgtype: _matrix.MsgType.Text,
body: `${text}`,
format: "org.matrix.custom.html",
formatted_body: `${text}`
};
if (italic) {
modifiedContent.formatted_body = "<em>" + modifiedContent.formatted_body + "</em>";
modifiedContent.body = "*" + modifiedContent.body + "*";
}
const modifiedEvent = new _matrix.MatrixEvent();
modifiedEvent.event = mxEv.event;
modifiedEvent.sender = mxEv.sender;
modifiedEvent.event.type = "m.room.message";
modifiedEvent.event.content = modifiedContent;
return modifiedEvent;
}
async createMessageBody(mxEv, joined = false) {
let eventTile;
try {
if (this.isAttachment(mxEv)) {
if (this.exportOptions.attachmentsIncluded) {
try {
const blob = await this.getMediaBlob(mxEv);
if (this.totalSize + blob.size > this.exportOptions.maxSize) {
eventTile = await this.getEventTileMarkup(this.createModifiedEvent(this.mediaOmitText, mxEv), joined);
} else {
this.totalSize += blob.size;
const filePath = this.getFilePath(mxEv);
eventTile = await this.getEventTileMarkup(mxEv, joined, filePath);
if (this.totalSize == this.exportOptions.maxSize) {
this.exportOptions.attachmentsIncluded = false;
}
this.addFile(filePath, blob);
}
} catch (e) {
_logger.logger.log("Error while fetching file" + e);
eventTile = await this.getEventTileMarkup(this.createModifiedEvent((0, _languageHandler._t)("export_chat|error_fetching_file"), mxEv), joined);
}
} else {
eventTile = await this.getEventTileMarkup(this.createModifiedEvent(this.mediaOmitText, mxEv), joined);
}
} else {
eventTile = await this.getEventTileMarkup(mxEv, joined);
}
} catch (e) {
// TODO: Handle callEvent errors
_logger.logger.error(e);
eventTile = await this.getEventTileMarkup(this.createModifiedEvent((0, _TextForEvent.textForEvent)(mxEv, this.room.client), mxEv, false), joined);
}
return eventTile;
}
async createHTML(events, start, currentPage, nbPages) {
let content = "";
let prevEvent = null;
for (let i = start; i < Math.min(start + 1000, events.length); i++) {
const event = events[i];
this.updateProgress((0, _languageHandler._t)("export_chat|processing_event_n", {
number: i + 1,
total: events.length
}), false, true);
if (this.cancelled) return this.cleanUp();
if (!(0, _EventTileFactory.haveRendererForEvent)(event, this.room.client, false)) continue;
content += this.needsDateSeparator(event, prevEvent) ? this.getDateSeparator(event) : "";
const shouldBeJoined = !this.needsDateSeparator(event, prevEvent) && (0, _MessagePanel.shouldFormContinuation)(prevEvent, event, this.room.client, false);
const body = await this.createMessageBody(event, shouldBeJoined);
this.totalSize += Buffer.byteLength(body);
content += body;
prevEvent = event;
}
return this.wrapHTML(content, currentPage, nbPages);
}
async export() {
this.updateProgress((0, _languageHandler._t)("export_chat|starting_export"));
const fetchStart = performance.now();
const res = await this.getRequiredEvents();
const fetchEnd = performance.now();
this.updateProgress((0, _languageHandler._t)("export_chat|fetched_n_events_in_time", {
count: res.length,
seconds: (fetchEnd - fetchStart) / 1000
}), true, false);
this.updateProgress((0, _languageHandler._t)("export_chat|creating_html"));
const usedClasses = new Set();
for (let page = 0; page < res.length / 1000; page++) {
const html = await this.createHTML(res, page * 1000, page, res.length / 1000);
const document = new DOMParser().parseFromString(html, "text/html");
document.querySelectorAll("*").forEach(element => {
element.classList.forEach(c => usedClasses.add(c));
});
this.addFile(`messages${page ? page + 1 : ""}.html`, new Blob([html]));
}
const exportCSS = await (0, _exportCSS.default)(usedClasses);
this.addFile("css/style.css", new Blob([exportCSS]));
this.addFile("js/script.js", new Blob([_exportJS.default]));
await this.downloadZIP();
const exportEnd = performance.now();
if (this.cancelled) {
_logger.logger.info("Export cancelled successfully");
} else {
this.updateProgress((0, _languageHandler._t)("export_chat|export_successful"));
this.updateProgress((0, _languageHandler._t)("export_chat|exported_n_events_in_time", {
count: res.length,
seconds: (exportEnd - fetchStart) / 1000
}));
}
this.cleanUp();
}
}
exports.default = HTMLExporter;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9yZWFjdERvbSIsIl9tYXRyaXgiLCJfc2VydmVyIiwiX2xvZ2dlciIsIl9lc2NhcGVIdG1sIiwiX2NvbXBvdW5kV2ViIiwiX0V4cG9ydGVyIiwiX01lZGlhIiwiX0xheW91dCIsIl9NZXNzYWdlUGFuZWwiLCJfRGF0ZVV0aWxzIiwiX1Blcm1hbGlua3MiLCJfbGFuZ3VhZ2VIYW5kbGVyIiwiQXZhdGFyIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfRXZlbnRUaWxlIiwiX0RhdGVTZXBhcmF0b3IiLCJfQmFzZUF2YXRhciIsIl9NYXRyaXhDbGllbnRDb250ZXh0IiwiX2V4cG9ydENTUyIsIl9UZXh0Rm9yRXZlbnQiLCJfRXZlbnRUaWxlRmFjdG9yeSIsIl9leHBvcnRKUyIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIkhUTUxFeHBvcnRlciIsIkV4cG9ydGVyIiwiY29uc3RydWN0b3IiLCJyb29tIiwiZXhwb3J0VHlwZSIsImV4cG9ydE9wdGlvbnMiLCJzZXRQcm9ncmVzc1RleHQiLCJfZGVmaW5lUHJvcGVydHkyIiwiYXZhdGFycyIsIk1hcCIsInBlcm1hbGlua0NyZWF0b3IiLCJSb29tUGVybWFsaW5rQ3JlYXRvciIsInRvdGFsU2l6ZSIsIm1lZGlhT21pdFRleHQiLCJhdHRhY2htZW50c0luY2x1ZGVkIiwiX3QiLCJnZXRSb29tQXZhdGFyIiwiYmxvYiIsInVuZGVmaW5lZCIsImF2YXRhclVybCIsImF2YXRhclVybEZvclJvb20iLCJhdmF0YXJQYXRoIiwiaW1hZ2UiLCJmZXRjaCIsInNpemUiLCJhZGRGaWxlIiwiZXJyIiwibG9nZ2VyIiwibG9nIiwiYXZhdGFyIiwiY3JlYXRlRWxlbWVudCIsIm5hbWUiLCJ0aXRsZSIsInVybCIsInJlbmRlclRvU3RhdGljTWFya3VwIiwid3JhcEhUTUwiLCJjb250ZW50IiwiY3VycmVudFBhZ2UiLCJuYlBhZ2VzIiwicm9vbUF2YXRhciIsImV4cG9ydERhdGUiLCJmb3JtYXRGdWxsRGF0ZU5vRGF5Tm9UaW1lIiwiRGF0ZSIsImNyZWF0b3IiLCJjdXJyZW50U3RhdGUiLCJnZXRTdGF0ZUV2ZW50cyIsIkV2ZW50VHlwZSIsIlJvb21DcmVhdGUiLCJnZXRTZW5kZXIiLCJjcmVhdG9yTmFtZSIsImdldE1lbWJlciIsInJhd0Rpc3BsYXlOYW1lIiwiZXhwb3J0ZXIiLCJjbGllbnQiLCJnZXRTYWZlVXNlcklkIiwiZXhwb3J0ZXJOYW1lIiwidG9waWMiLCJSb29tVG9waWMiLCJnZXRDb250ZW50Iiwic2FmZUNyZWF0ZWRUZXh0IiwiZXNjYXBlSHRtbCIsInNhZmVFeHBvcnRlciIsInNhZmVSb29tTmFtZSIsInNhZmVUb3BpYyIsInNhZmVFeHBvcnRlZFRleHQiLCJyb29tTmFtZSIsImV4cG9ydGVyRGV0YWlscyIsImhyZWYiLCJlbmNvZGVVUklDb21wb25lbnQiLCJ0YXJnZXQiLCJyZWwiLCJGcmFnbWVudCIsInNhZmVUb3BpY1RleHQiLCJwcmV2aW91c01lc3NhZ2VzTGluayIsInN0eWxlIiwidGV4dEFsaWduIiwiZm9udFdlaWdodCIsIm5leHRNZXNzYWdlc0xpbmsiLCJtYXJnaW4iLCJnZXRBdmF0YXJVUkwiLCJldmVudCIsIm1lbWJlciIsInNlbmRlciIsImdldE14Y0F2YXRhclVybCIsIm1lZGlhRnJvbU14YyIsImdldFRodW1ibmFpbE9mU291cmNlSHR0cCIsInNhdmVBdmF0YXJJZk5lZWRlZCIsInVzZXJJZCIsInJlcGxhY2UiLCJnZXREYXRlU2VwYXJhdG9yIiwidHMiLCJnZXRUcyIsImRhdGVTZXBhcmF0b3IiLCJrZXkiLCJmb3JFeHBvcnQiLCJyb29tSWQiLCJnZXRSb29tSWQiLCJuZWVkc0RhdGVTZXBhcmF0b3IiLCJwcmV2RXZlbnQiLCJ3YW50c0RhdGVTZXBhcmF0b3IiLCJnZXREYXRlIiwiZ2V0RXZlbnRUaWxlIiwibXhFdiIsImNvbnRpbnVhdGlvbiIsImNsYXNzTmFtZSIsImlkIiwiZ2V0SWQiLCJQcm92aWRlciIsInZhbHVlIiwiVG9vbHRpcFByb3ZpZGVyIiwibXhFdmVudCIsImlzUmVkYWN0ZWQiLCJyZXBsYWNpbmdFdmVudElkIiwiYWx3YXlzU2hvd1RpbWVzdGFtcHMiLCJzaG93VXJsUHJldmlldyIsImNoZWNrVW5tb3VudGluZyIsImlzVHdlbHZlSG91ciIsImxhc3QiLCJsYXN0SW5TZWN0aW9uIiwibGFzdFN1Y2Nlc3NmdWwiLCJpc1NlbGVjdGVkRXZlbnQiLCJzaG93UmVhY3Rpb25zIiwibGF5b3V0IiwiTGF5b3V0IiwiR3JvdXAiLCJzaG93UmVhZFJlY2VpcHRzIiwiZ2V0RXZlbnRUaWxlTWFya3VwIiwiZmlsZVBhdGgiLCJoYXNBdmF0YXIiLCJFdmVudFRpbGUiLCJldmVudFRpbGVNYXJrdXAiLCJtc2d0eXBlIiwiTXNnVHlwZSIsIkVtb3RlIiwiTm90aWNlIiwiVGV4dCIsInRlbXBSb290IiwiZG9jdW1lbnQiLCJSZWFjdERPTSIsInJlbmRlciIsImlubmVySFRNTCIsIm14YyIsImZpbGUiLCJzcGxpdCIsImpvaW4iLCJlbmNvZGVVUkkiLCJjcmVhdGVNb2RpZmllZEV2ZW50IiwidGV4dCIsIml0YWxpYyIsIm1vZGlmaWVkQ29udGVudCIsImJvZHkiLCJmb3JtYXQiLCJmb3JtYXR0ZWRfYm9keSIsIm1vZGlmaWVkRXZlbnQiLCJNYXRyaXhFdmVudCIsInR5cGUiLCJjcmVhdGVNZXNzYWdlQm9keSIsImpvaW5lZCIsImV2ZW50VGlsZSIsImlzQXR0YWNobWVudCIsImdldE1lZGlhQmxvYiIsIm1heFNpemUiLCJnZXRGaWxlUGF0aCIsImVycm9yIiwidGV4dEZvckV2ZW50IiwiY3JlYXRlSFRNTCIsImV2ZW50cyIsInN0YXJ0IiwiTWF0aCIsIm1pbiIsImxlbmd0aCIsInVwZGF0ZVByb2dyZXNzIiwibnVtYmVyIiwidG90YWwiLCJjYW5jZWxsZWQiLCJjbGVhblVwIiwiaGF2ZVJlbmRlcmVyRm9yRXZlbnQiLCJzaG91bGRCZUpvaW5lZCIsInNob3VsZEZvcm1Db250aW51YXRpb24iLCJCdWZmZXIiLCJieXRlTGVuZ3RoIiwiZXhwb3J0IiwiZmV0Y2hTdGFydCIsInBlcmZvcm1hbmNlIiwibm93IiwicmVzIiwiZ2V0UmVxdWlyZWRFdmVudHMiLCJmZXRjaEVuZCIsImNvdW50Iiwic2Vjb25kcyIsInVzZWRDbGFzc2VzIiwiU2V0IiwicGFnZSIsImh0bWwiLCJET01QYXJzZXIiLCJwYXJzZUZyb21TdHJpbmciLCJxdWVyeVNlbGVjdG9yQWxsIiwiZm9yRWFjaCIsImVsZW1lbnQiLCJjbGFzc0xpc3QiLCJjIiwiYWRkIiwiQmxvYiIsImV4cG9ydENTUyIsImdldEV4cG9ydENTUyIsImV4cG9ydEpTIiwiZG93bmxvYWRaSVAiLCJleHBvcnRFbmQiLCJpbmZvIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy91dGlscy9leHBvcnRVdGlscy9IdG1sRXhwb3J0LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAyMS0yMDIzIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCBSZWFjdCBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCBSZWFjdERPTSBmcm9tIFwicmVhY3QtZG9tXCI7XG5pbXBvcnQgeyBSb29tLCBNYXRyaXhFdmVudCwgRXZlbnRUeXBlLCBNc2dUeXBlIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21hdHJpeFwiO1xuaW1wb3J0IHsgcmVuZGVyVG9TdGF0aWNNYXJrdXAgfSBmcm9tIFwicmVhY3QtZG9tL3NlcnZlclwiO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2xvZ2dlclwiO1xuaW1wb3J0IGVzY2FwZUh0bWwgZnJvbSBcImVzY2FwZS1odG1sXCI7XG5pbXBvcnQgeyBUb29sdGlwUHJvdmlkZXIgfSBmcm9tIFwiQHZlY3Rvci1pbS9jb21wb3VuZC13ZWJcIjtcblxuaW1wb3J0IEV4cG9ydGVyIGZyb20gXCIuL0V4cG9ydGVyXCI7XG5pbXBvcnQgeyBtZWRpYUZyb21NeGMgfSBmcm9tIFwiLi4vLi4vY3VzdG9taXNhdGlvbnMvTWVkaWFcIjtcbmltcG9ydCB7IExheW91dCB9IGZyb20gXCIuLi8uLi9zZXR0aW5ncy9lbnVtcy9MYXlvdXRcIjtcbmltcG9ydCB7IHNob3VsZEZvcm1Db250aW51YXRpb24gfSBmcm9tIFwiLi4vLi4vY29tcG9uZW50cy9zdHJ1Y3R1cmVzL01lc3NhZ2VQYW5lbFwiO1xuaW1wb3J0IHsgZm9ybWF0RnVsbERhdGVOb0RheU5vVGltZSwgd2FudHNEYXRlU2VwYXJhdG9yIH0gZnJvbSBcIi4uLy4uL0RhdGVVdGlsc1wiO1xuaW1wb3J0IHsgUm9vbVBlcm1hbGlua0NyZWF0b3IgfSBmcm9tIFwiLi4vcGVybWFsaW5rcy9QZXJtYWxpbmtzXCI7XG5pbXBvcnQgeyBfdCB9IGZyb20gXCIuLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCAqIGFzIEF2YXRhciBmcm9tIFwiLi4vLi4vQXZhdGFyXCI7XG5pbXBvcnQgRXZlbnRUaWxlIGZyb20gXCIuLi8uLi9jb21wb25lbnRzL3ZpZXdzL3Jvb21zL0V2ZW50VGlsZVwiO1xuaW1wb3J0IERhdGVTZXBhcmF0b3IgZnJvbSBcIi4uLy4uL2NvbXBvbmVudHMvdmlld3MvbWVzc2FnZXMvRGF0ZVNlcGFyYXRvclwiO1xuaW1wb3J0IEJhc2VBdmF0YXIgZnJvbSBcIi4uLy4uL2NvbXBvbmVudHMvdmlld3MvYXZhdGFycy9CYXNlQXZhdGFyXCI7XG5pbXBvcnQgeyBFeHBvcnRUeXBlLCBJRXhwb3J0T3B0aW9ucyB9IGZyb20gXCIuL2V4cG9ydFV0aWxzXCI7XG5pbXBvcnQgTWF0cml4Q2xpZW50Q29udGV4dCBmcm9tIFwiLi4vLi4vY29udGV4dHMvTWF0cml4Q2xpZW50Q29udGV4dFwiO1xuaW1wb3J0IGdldEV4cG9ydENTUyBmcm9tIFwiLi9leHBvcnRDU1NcIjtcbmltcG9ydCB7IHRleHRGb3JFdmVudCB9IGZyb20gXCIuLi8uLi9UZXh0Rm9yRXZlbnRcIjtcbmltcG9ydCB7IGhhdmVSZW5kZXJlckZvckV2ZW50IH0gZnJvbSBcIi4uLy4uL2V2ZW50cy9FdmVudFRpbGVGYWN0b3J5XCI7XG5cbmltcG9ydCBleHBvcnRKUyBmcm9tIFwiISFyYXctbG9hZGVyIS4vZXhwb3J0SlNcIjtcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgSFRNTEV4cG9ydGVyIGV4dGVuZHMgRXhwb3J0ZXIge1xuICAgIHByb3RlY3RlZCBhdmF0YXJzOiBNYXA8c3RyaW5nLCBib29sZWFuPjtcbiAgICBwcm90ZWN0ZWQgcGVybWFsaW5rQ3JlYXRvcjogUm9vbVBlcm1hbGlua0NyZWF0b3I7XG4gICAgcHJvdGVjdGVkIHRvdGFsU2l6ZTogbnVtYmVyO1xuICAgIHByb3RlY3RlZCBtZWRpYU9taXRUZXh0OiBzdHJpbmc7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IoXG4gICAgICAgIHJvb206IFJvb20sXG4gICAgICAgIGV4cG9ydFR5cGU6IEV4cG9ydFR5cGUsXG4gICAgICAgIGV4cG9ydE9wdGlvbnM6IElFeHBvcnRPcHRpb25zLFxuICAgICAgICBzZXRQcm9ncmVzc1RleHQ6IFJlYWN0LkRpc3BhdGNoPFJlYWN0LlNldFN0YXRlQWN0aW9uPHN0cmluZz4+LFxuICAgICkge1xuICAgICAgICBzdXBlcihyb29tLCBleHBvcnRUeXBlLCBleHBvcnRPcHRpb25zLCBzZXRQcm9ncmVzc1RleHQpO1xuICAgICAgICB0aGlzLmF2YXRhcnMgPSBuZXcgTWFwPHN0cmluZywgYm9vbGVhbj4oKTtcbiAgICAgICAgdGhpcy5wZXJtYWxpbmtDcmVhdG9yID0gbmV3IFJvb21QZXJtYWxpbmtDcmVhdG9yKHRoaXMucm9vbSk7XG4gICAgICAgIHRoaXMudG90YWxTaXplID0gMDtcbiAgICAgICAgdGhpcy5tZWRpYU9taXRUZXh0ID0gIXRoaXMuZXhwb3J0T3B0aW9ucy5hdHRhY2htZW50c0luY2x1ZGVkXG4gICAgICAgICAgICA/IF90KFwiZXhwb3J0X2NoYXR8bWVkaWFfb21pdHRlZFwiKVxuICAgICAgICAgICAgOiBfdChcImV4cG9ydF9jaGF0fG1lZGlhX29taXR0ZWRfZmlsZV9zaXplXCIpO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhc3luYyBnZXRSb29tQXZhdGFyKCk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgIGxldCBibG9iOiBCbG9iIHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICAgICAgICBjb25zdCBhdmF0YXJVcmwgPSBBdmF0YXIuYXZhdGFyVXJsRm9yUm9vbSh0aGlzLnJvb20sIDMyLCAzMiwgXCJjcm9wXCIpO1xuICAgICAgICBjb25zdCBhdmF0YXJQYXRoID0gXCJyb29tLnBuZ1wiO1xuICAgICAgICBpZiAoYXZhdGFyVXJsKSB7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGltYWdlID0gYXdhaXQgZmV0Y2goYXZhdGFyVXJsKTtcbiAgICAgICAgICAgICAgICBibG9iID0gYXdhaXQgaW1hZ2UuYmxvYigpO1xuICAgICAgICAgICAgICAgIHRoaXMudG90YWxTaXplICs9IGJsb2Iuc2l6ZTtcbiAgICAgICAgICAgICAgICB0aGlzLmFkZEZpbGUoYXZhdGFyUGF0aCwgYmxvYik7XG4gICAgICAgICAgICB9IGNhdGNoIChlcnIpIHtcbiAgICAgICAgICAgICAgICBsb2dnZXIubG9nKFwiRmFpbGVkIHRvIGZldGNoIHJvb20ncyBhdmF0YXJcIiArIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgYXZhdGFyID0gKFxuICAgICAgICAgICAgPEJhc2VBdmF0YXIgc2l6ZT1cIjMycHhcIiBuYW1lPXt0aGlzLnJvb20ubmFtZX0gdGl0bGU9e3RoaXMucm9vbS5uYW1lfSB1cmw9e2Jsb2IgPyBhdmF0YXJQYXRoIDogXCJcIn0gLz5cbiAgICAgICAgKTtcbiAgICAgICAgcmV0dXJuIHJlbmRlclRvU3RhdGljTWFya3VwKGF2YXRhcik7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIGFzeW5jIHdyYXBIVE1MKGNvbnRlbnQ6IHN0cmluZywgY3VycmVudFBhZ2U6IG51bWJlciwgbmJQYWdlczogbnVtYmVyKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgY29uc3Qgcm9vbUF2YXRhciA9IGF3YWl0IHRoaXMuZ2V0Um9vbUF2YXRhcigpO1xuICAgICAgICBjb25zdCBleHBvcnREYXRlID0gZm9ybWF0RnVsbERhdGVOb0RheU5vVGltZShuZXcgRGF0ZSgpKTtcbiAgICAgICAgY29uc3QgY3JlYXRvciA9IHRoaXMucm9vbS5jdXJyZW50U3RhdGUuZ2V0U3RhdGVFdmVudHMoRXZlbnRUeXBlLlJvb21DcmVhdGUsIFwiXCIpPy5nZXRTZW5kZXIoKTtcbiAgICAgICAgY29uc3QgY3JlYXRvck5hbWUgPSAoY3JlYXRvciA/IHRoaXMucm9vbS5nZXRNZW1iZXIoY3JlYXRvcik/LnJhd0Rpc3BsYXlOYW1lIDogY3JlYXRvcikgfHwgY3JlYXRvcjtcbiAgICAgICAgY29uc3QgZXhwb3J0ZXIgPSB0aGlzLnJvb20uY2xpZW50LmdldFNhZmVVc2VySWQoKTtcbiAgICAgICAgY29uc3QgZXhwb3J0ZXJOYW1lID0gdGhpcy5yb29tLmdldE1lbWJlcihleHBvcnRlcik/LnJhd0Rpc3BsYXlOYW1lO1xuICAgICAgICBjb25zdCB0b3BpYyA9IHRoaXMucm9vbS5jdXJyZW50U3RhdGUuZ2V0U3RhdGVFdmVudHMoRXZlbnRUeXBlLlJvb21Ub3BpYywgXCJcIik/LmdldENvbnRlbnQoKT8udG9waWMgfHwgXCJcIjtcblxuICAgICAgICBjb25zdCBzYWZlQ3JlYXRlZFRleHQgPSBlc2NhcGVIdG1sKFxuICAgICAgICAgICAgX3QoXCJleHBvcnRfY2hhdHxjcmVhdG9yX3N1bW1hcnlcIiwge1xuICAgICAgICAgICAgICAgIGNyZWF0b3JOYW1lLFxuICAgICAgICAgICAgfSksXG4gICAgICAgICk7XG4gICAgICAgIGNvbnN0IHNhZmVFeHBvcnRlciA9IGVzY2FwZUh0bWwoZXhwb3J0ZXIpO1xuICAgICAgICBjb25zdCBzYWZlUm9vbU5hbWUgPSBlc2NhcGVIdG1sKHRoaXMucm9vbS5uYW1lKTtcbiAgICAgICAgY29uc3Qgc2FmZVRvcGljID0gZXNjYXBlSHRtbCh0b3BpYyk7XG4gICAgICAgIGNvbnN0IHNhZmVFeHBvcnRlZFRleHQgPSByZW5kZXJUb1N0YXRpY01hcmt1cChcbiAgICAgICAgICAgIDxwPlxuICAgICAgICAgICAgICAgIHtfdChcbiAgICAgICAgICAgICAgICAgICAgXCJleHBvcnRfY2hhdHxleHBvcnRfaW5mb1wiLFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICBleHBvcnREYXRlLFxuICAgICAgICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgICAgICAgICByb29tTmFtZTogKCkgPT4gPHN0cm9uZz57c2FmZVJvb21OYW1lfTwvc3Ryb25nPixcbiAgICAgICAgICAgICAgICAgICAgICAgIGV4cG9ydGVyRGV0YWlsczogKCkgPT4gKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxhXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhyZWY9e2BodHRwczovL21hdHJpeC50by8jLyR7ZW5jb2RlVVJJQ29tcG9uZW50KGV4cG9ydGVyKX1gfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0YXJnZXQ9XCJfYmxhbmtcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWw9XCJub29wZW5lciBub3JlZmVycmVyXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtleHBvcnRlck5hbWUgPyAoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzdHJvbmc+e2VzY2FwZUh0bWwoZXhwb3J0ZXJOYW1lKX08L3N0cm9uZz5JIHtcIiAoXCIgKyBzYWZlRXhwb3J0ZXIgKyBcIilcIn1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIDogKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHN0cm9uZz57c2FmZUV4cG9ydGVyfTwvc3Ryb25nPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvYT5cbiAgICAgICAgICAgICAgICAgICAgICAgICksXG4gICAgICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvcD4sXG4gICAgICAgICk7XG5cbiAgICAgICAgY29uc3Qgc2FmZVRvcGljVGV4dCA9IHRvcGljID8gX3QoXCJleHBvcnRfY2hhdHx0b3BpY1wiLCB7IHRvcGljOiBzYWZlVG9waWMgfSkgOiBcIlwiO1xuICAgICAgICBjb25zdCBwcmV2aW91c01lc3NhZ2VzTGluayA9IHJlbmRlclRvU3RhdGljTWFya3VwKFxuICAgICAgICAgICAgY3VycmVudFBhZ2UgIT09IDAgPyAoXG4gICAgICAgICAgICAgICAgPGRpdiBzdHlsZT17eyB0ZXh0QWxpZ246IFwiY2VudGVyXCIgfX0+XG4gICAgICAgICAgICAgICAgICAgIDxhIGhyZWY9e2AuL21lc3NhZ2VzJHtjdXJyZW50UGFnZSA9PT0gMSA/IFwiXCIgOiBjdXJyZW50UGFnZX0uaHRtbGB9IHN0eWxlPXt7IGZvbnRXZWlnaHQ6IFwiYm9sZFwiIH19PlxuICAgICAgICAgICAgICAgICAgICAgICAge190KFwiZXhwb3J0X2NoYXR8cHJldmlvdXNfcGFnZVwiKX1cbiAgICAgICAgICAgICAgICAgICAgPC9hPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICA8PjwvPlxuICAgICAgICAgICAgKSxcbiAgICAgICAgKTtcblxuICAgICAgICBjb25zdCBuZXh0TWVzc2FnZXNMaW5rID0gcmVuZGVyVG9TdGF0aWNNYXJrdXAoXG4gICAgICAgICAgICBjdXJyZW50UGFnZSA8IG5iUGFnZXMgLSAxID8gKFxuICAgICAgICAgICAgICAgIDxkaXYgc3R5bGU9e3sgdGV4dEFsaWduOiBcImNlbnRlclwiLCBtYXJnaW46IFwiMTBweFwiIH19PlxuICAgICAgICAgICAgICAgICAgICA8YSBocmVmPXtcIi4vbWVzc2FnZXNcIiArIChjdXJyZW50UGFnZSArIDIpICsgXCIuaHRtbFwifSBzdHlsZT17eyBmb250V2VpZ2h0OiBcImJvbGRcIiB9fT5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtfdChcImV4cG9ydF9jaGF0fG5leHRfcGFnZVwiKX1cbiAgICAgICAgICAgICAgICAgICAgPC9hPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgKSA6IChcbiAgICAgICAgICAgICAgICA8PjwvPlxuICAgICAgICAgICAgKSxcbiAgICAgICAgKTtcblxuICAgICAgICByZXR1cm4gYFxuICAgICAgICAgIDwhRE9DVFlQRSBodG1sPlxuICAgICAgICAgICAgPGh0bWwgbGFuZz1cImVuXCI+XG4gICAgICAgICAgICA8aGVhZD5cbiAgICAgICAgICAgICAgICA8bWV0YSBjaGFyc2V0PVwiVVRGLThcIiAvPlxuICAgICAgICAgICAgICAgIDxtZXRhIGh0dHAtZXF1aXY9XCJYLVVBLUNvbXBhdGlibGVcIiBjb250ZW50PVwiSUU9ZWRnZVwiIC8+XG4gICAgICAgICAgICAgICAgPG1ldGEgbmFtZT1cInZpZXdwb3J0XCIgY29udGVudD1cIndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjBcIiAvPlxuICAgICAgICAgICAgICAgIDxsaW5rIGhyZWY9XCJjc3Mvc3R5bGUuY3NzXCIgcmVsPVwic3R5bGVzaGVldFwiIC8+XG4gICAgICAgICAgICAgICAgPHNjcmlwdCBzcmM9XCJqcy9zY3JpcHQuanNcIj48L3NjcmlwdD5cbiAgICAgICAgICAgICAgICA8dGl0bGU+JHtfdChcImV4cG9ydF9jaGF0fGh0bWxfdGl0bGVcIil9PC90aXRsZT5cbiAgICAgICAgICAgIDwvaGVhZD5cbiAgICAgICAgICAgIDxib2R5IHN0eWxlPVwiaGVpZ2h0OiAxMDB2aDtcIiBjbGFzcz1cImNwZC10aGVtZS1saWdodFwiPlxuICAgICAgICAgICAgICAgIDxkaXYgaWQ9XCJtYXRyaXhjaGF0XCIgc3R5bGU9XCJoZWlnaHQ6IDEwMCU7IG92ZXJmbG93OiBhdXRvXCI+XG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJteF9NYXRyaXhDaGF0X3dyYXBwZXJcIiBhcmlhLWhpZGRlbj1cImZhbHNlXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfTWF0cml4Q2hhdFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgPG1haW4gY2xhc3M9XCJteF9Sb29tVmlld1wiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJteF9MZWdhY3lSb29tSGVhZGVyIGxpZ2h0LXBhbmVsXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm14X0xlZ2FjeVJvb21IZWFkZXJfd3JhcHBlclwiIGFyaWEtb3ducz1cIm14X1JpZ2h0UGFuZWxcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm14X0xlZ2FjeVJvb21IZWFkZXJfYXZhdGFyXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJteF9EZWNvcmF0ZWRSb29tQXZhdGFyXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICR7cm9vbUF2YXRhcn1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfTGVnYWN5Um9vbUhlYWRlcl9uYW1lXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpcj1cImF1dG9cIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJteF9MZWdhY3lSb29tSGVhZGVyX25hbWV0ZXh0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlPVwiJHtzYWZlUm9vbU5hbWV9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJHtzYWZlUm9vbU5hbWV9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm14X0xlZ2FjeVJvb21IZWFkZXJfdG9waWNcIiBkaXI9XCJhdXRvXCI+ICR7c2FmZVRvcGljfSA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAke3ByZXZpb3VzTWVzc2FnZXNMaW5rfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJteF9NYWluU3BsaXRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfUm9vbVZpZXdfYm9keVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwibXhfUm9vbVZpZXdfdGltZWxpbmUgbXhfUm9vbVZpZXdfdGltZWxpbmVfcnJfZW5hYmxlZFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG14X0F1dG9IaWRlU2Nyb2xsYmFyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBteF9TY3JvbGxQYW5lbFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbXhfUm9vbVZpZXdfbWVzc2FnZVBhbmVsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfUm9vbVZpZXdfbWVzc2FnZUxpc3RXcmFwcGVyXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8b2xcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbGFzcz1cIm14X1Jvb21WaWV3X01lc3NhZ2VMaXN0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcmlhLWxpdmU9XCJwb2xpdGVcIlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvbGU9XCJsaXN0XCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICR7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY3VycmVudFBhZ2UgPT0gMFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA/IGA8ZGl2IGNsYXNzPVwibXhfTmV3Um9vbUludHJvXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICR7cm9vbUF2YXRhcn1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGgyPiAke3NhZmVSb29tTmFtZX0gPC9oMj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPHA+ICR7c2FmZUNyZWF0ZWRUZXh0fSA8YnIvPjxici8+ICR7c2FmZUV4cG9ydGVkVGV4dH0gPC9wPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8YnIvPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cD4gJHtzYWZlVG9waWNUZXh0fSA8L3A+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+YFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA6IFwiXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICR7Y29udGVudH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvb2w+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfUm9vbVZpZXdfc3RhdHVzQXJlYVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzPVwibXhfUm9vbVZpZXdfc3RhdHVzQXJlYUJveFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzcz1cIm14X1Jvb21WaWV3X3N0YXR1c0FyZWFCb3hfbGluZVwiPjwvZGl2PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgJHtuZXh0TWVzc2FnZXNMaW5rfVxuICAgICAgICAgICAgICAgICAgICAgICAgPC9tYWluPlxuICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDxkaXYgaWQ9XCJzbmFja2JhclwiLz5cbiAgICAgICAgICAgIDwvYm9keT5cbiAgICAgICAgPC9odG1sPmA7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIGdldEF2YXRhclVSTChldmVudDogTWF0cml4RXZlbnQpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgY29uc3QgbWVtYmVyID0gZXZlbnQuc2VuZGVyO1xuICAgICAgICBjb25zdCBhdmF0YXJVcmwgPSBtZW1iZXI/LmdldE14Y0F2YXRhclVybCgpO1xuICAgICAgICByZXR1cm4gYXZhdGFyVXJsID8gbWVkaWFGcm9tTXhjKGF2YXRhclVybCkuZ2V0VGh1bWJuYWlsT2ZTb3VyY2VIdHRwKDMwLCAzMCwgXCJjcm9wXCIpIDogbnVsbDtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYXN5bmMgc2F2ZUF2YXRhcklmTmVlZGVkKGV2ZW50OiBNYXRyaXhFdmVudCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBtZW1iZXIgPSBldmVudC5zZW5kZXIhO1xuICAgICAgICBpZiAoIXRoaXMuYXZhdGFycy5oYXMobWVtYmVyLnVzZXJJZCkpIHtcbiAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgY29uc3QgYXZhdGFyVXJsID0gdGhpcy5nZXRBdmF0YXJVUkwoZXZlbnQpO1xuICAgICAgICAgICAgICAgIHRoaXMuYXZhdGFycy5zZXQobWVtYmVyLnVzZXJJZCwgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgY29uc3QgaW1hZ2UgPSBhd2FpdCBmZXRjaChhdmF0YXJVcmwhKTtcbiAgICAgICAgICAgICAgICBjb25zdCBibG9iID0gYXdhaXQgaW1hZ2UuYmxvYigpO1xuICAgICAgICAgICAgICAgIHRoaXMuYWRkRmlsZShgdXNlcnMvJHttZW1iZXIudXNlcklkLnJlcGxhY2UoLzovZywgXCItXCIpfS5wbmdgLCBibG9iKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgICAgIGxvZ2dlci5sb2coXCJGYWlsZWQgdG8gZmV0Y2ggdXNlcidzIGF2YXRhclwiICsgZXJyKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByb3RlY3RlZCBnZXREYXRlU2VwYXJhdG9yKGV2ZW50OiBNYXRyaXhFdmVudCk6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IHRzID0gZXZlbnQuZ2V0VHMoKTtcbiAgICAgICAgY29uc3QgZGF0ZVNlcGFyYXRvciA9IChcbiAgICAgICAgICAgIDxsaSBrZXk9e3RzfT5cbiAgICAgICAgICAgICAgICA8RGF0ZVNlcGFyYXRvciBmb3JFeHBvcnQ9e3RydWV9IGtleT17dHN9IHJvb21JZD17ZXZlbnQuZ2V0Um9vbUlkKCkhfSB0cz17dHN9IC8+XG4gICAgICAgICAgICA8L2xpPlxuICAgICAgICApO1xuICAgICAgICByZXR1cm4gcmVuZGVyVG9TdGF0aWNNYXJrdXAoZGF0ZVNlcGFyYXRvcik7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIG5lZWRzRGF0ZVNlcGFyYXRvcihldmVudDogTWF0cml4RXZlbnQsIHByZXZFdmVudDogTWF0cml4RXZlbnQgfCBudWxsKTogYm9vbGVhbiB7XG4gICAgICAgIGlmICghcHJldkV2ZW50KSByZXR1cm4gdHJ1ZTtcbiAgICAgICAgcmV0dXJuIHdhbnRzRGF0ZVNlcGFyYXRvcihwcmV2RXZlbnQuZ2V0RGF0ZSgpIHx8IHVuZGVmaW5lZCwgZXZlbnQuZ2V0RGF0ZSgpIHx8IHVuZGVmaW5lZCk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldEV2ZW50VGlsZShteEV2OiBNYXRyaXhFdmVudCwgY29udGludWF0aW9uOiBib29sZWFuKTogSlNYLkVsZW1lbnQge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9FeHBvcnRfRXZlbnRXcmFwcGVyXCIgaWQ9e214RXYuZ2V0SWQoKX0+XG4gICAgICAgICAgICAgICAgPE1hdHJpeENsaWVudENvbnRleHQuUHJvdmlkZXIgdmFsdWU9e3RoaXMucm9vbS5jbGllbnR9PlxuICAgICAgICAgICAgICAgICAgICA8VG9vbHRpcFByb3ZpZGVyPlxuICAgICAgICAgICAgICAgICAgICAgICAgPEV2ZW50VGlsZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG14RXZlbnQ9e214RXZ9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udGludWF0aW9uPXtjb250aW51YXRpb259XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNSZWRhY3RlZD17bXhFdi5pc1JlZGFjdGVkKCl9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwbGFjaW5nRXZlbnRJZD17bXhFdi5yZXBsYWNpbmdFdmVudElkKCl9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9yRXhwb3J0PXt0cnVlfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFsd2F5c1Nob3dUaW1lc3RhbXBzPXt0cnVlfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dVcmxQcmV2aWV3PXtmYWxzZX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVja1VubW91bnRpbmc9eygpID0+IGZhbHNlfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzVHdlbHZlSG91cj17ZmFsc2V9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdD17ZmFsc2V9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFzdEluU2VjdGlvbj17ZmFsc2V9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcGVybWFsaW5rQ3JlYXRvcj17dGhpcy5wZXJtYWxpbmtDcmVhdG9yfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RTdWNjZXNzZnVsPXtmYWxzZX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1NlbGVjdGVkRXZlbnQ9e2ZhbHNlfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dSZWFjdGlvbnM9e2ZhbHNlfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxheW91dD17TGF5b3V0Lkdyb3VwfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dSZWFkUmVjZWlwdHM9e2ZhbHNlfVxuICAgICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgPC9Ub29sdGlwUHJvdmlkZXI+XG4gICAgICAgICAgICAgICAgPC9NYXRyaXhDbGllbnRDb250ZXh0LlByb3ZpZGVyPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIGFzeW5jIGdldEV2ZW50VGlsZU1hcmt1cChteEV2OiBNYXRyaXhFdmVudCwgY29udGludWF0aW9uOiBib29sZWFuLCBmaWxlUGF0aD86IHN0cmluZyk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgICAgIGNvbnN0IGF2YXRhclVybCA9IHRoaXMuZ2V0QXZhdGFyVVJMKG14RXYpO1xuICAgICAgICBjb25zdCBoYXNBdmF0YXIgPSAhIWF2YXRhclVybDtcbiAgICAgICAgaWYgKGhhc0F2YXRhcikgYXdhaXQgdGhpcy5zYXZlQXZhdGFySWZOZWVkZWQobXhFdik7XG4gICAgICAgIGNvbnN0IEV2ZW50VGlsZSA9IHRoaXMuZ2V0RXZlbnRUaWxlKG14RXYsIGNvbnRpbnVhdGlvbik7XG4gICAgICAgIGxldCBldmVudFRpbGVNYXJrdXA6IHN0cmluZztcblxuICAgICAgICBpZiAoXG4gICAgICAgICAgICBteEV2LmdldENvbnRlbnQoKS5tc2d0eXBlID09IE1zZ1R5cGUuRW1vdGUgfHxcbiAgICAgICAgICAgIG14RXYuZ2V0Q29udGVudCgpLm1zZ3R5cGUgPT0gTXNnVHlwZS5Ob3RpY2UgfHxcbiAgICAgICAgICAgIG14RXYuZ2V0Q29udGVudCgpLm1zZ3R5cGUgPT09IE1zZ1R5cGUuVGV4dFxuICAgICAgICApIHtcbiAgICAgICAgICAgIC8vIHRvIGxpbmtpZnkgdGV4dHVhbCBldmVudHMsIHdlJ2xsIG5lZWQgbGlmZWN5Y2xlIG1ldGhvZHMgd2hpY2ggd29uJ3QgYmUgaW52b2tlZCBpbiByZW5kZXJUb1N0cmluZ1xuICAgICAgICAgICAgLy8gU28sIHdlJ2xsIGhhdmUgdG8gcmVuZGVyIHRoZSBjb21wb25lbnQgaW50byBhIHRlbXBvcmFyeSByb290IGVsZW1lbnRcbiAgICAgICAgICAgIGNvbnN0IHRlbXBSb290ID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICAgICAgICAgIFJlYWN0RE9NLnJlbmRlcihFdmVudFRpbGUsIHRlbXBSb290KTtcbiAgICAgICAgICAgIGV2ZW50VGlsZU1hcmt1cCA9IHRlbXBSb290LmlubmVySFRNTDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGV2ZW50VGlsZU1hcmt1cCA9IHJlbmRlclRvU3RhdGljTWFya3VwKEV2ZW50VGlsZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZmlsZVBhdGgpIHtcbiAgICAgICAgICAgIGNvbnN0IG14YyA9IG14RXYuZ2V0Q29udGVudCgpLnVybCA/PyBteEV2LmdldENvbnRlbnQoKS5maWxlPy51cmw7XG4gICAgICAgICAgICBldmVudFRpbGVNYXJrdXAgPSBldmVudFRpbGVNYXJrdXAuc3BsaXQobXhjKS5qb2luKGZpbGVQYXRoKTtcbiAgICAgICAgfVxuICAgICAgICBldmVudFRpbGVNYXJrdXAgPSBldmVudFRpbGVNYXJrdXAucmVwbGFjZSgvPHNwYW4gY2xhc3M9XCJteF9NRmlsZUJvZHlfaW5mb19pY29uXCIuKj8+Lio/PFxcL3NwYW4+LywgXCJcIik7XG4gICAgICAgIGlmIChoYXNBdmF0YXIpIHtcbiAgICAgICAgICAgIGV2ZW50VGlsZU1hcmt1cCA9IGV2ZW50VGlsZU1hcmt1cC5yZXBsYWNlKFxuICAgICAgICAgICAgICAgIGVuY29kZVVSSShhdmF0YXJVcmwpLnJlcGxhY2UoLyYvZywgXCImYW1wO1wiKSxcbiAgICAgICAgICAgICAgICBgdXNlcnMvJHtteEV2LnNlbmRlciEudXNlcklkLnJlcGxhY2UoLzovZywgXCItXCIpfS5wbmdgLFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gZXZlbnRUaWxlTWFya3VwO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBjcmVhdGVNb2RpZmllZEV2ZW50KHRleHQ6IHN0cmluZywgbXhFdjogTWF0cml4RXZlbnQsIGl0YWxpYyA9IHRydWUpOiBNYXRyaXhFdmVudCB7XG4gICAgICAgIGNvbnN0IG1vZGlmaWVkQ29udGVudCA9IHtcbiAgICAgICAgICAgIG1zZ3R5cGU6IE1zZ1R5cGUuVGV4dCxcbiAgICAgICAgICAgIGJvZHk6IGAke3RleHR9YCxcbiAgICAgICAgICAgIGZvcm1hdDogXCJvcmcubWF0cml4LmN1c3RvbS5odG1sXCIsXG4gICAgICAgICAgICBmb3JtYXR0ZWRfYm9keTogYCR7dGV4dH1gLFxuICAgICAgICB9O1xuICAgICAgICBpZiAoaXRhbGljKSB7XG4gICAgICAgICAgICBtb2RpZmllZENvbnRlbnQuZm9ybWF0dGVkX2JvZHkgPSBcIjxlbT5cIiArIG1vZGlmaWVkQ29udGVudC5mb3JtYXR0ZWRfYm9keSArIFwiPC9lbT5cIjtcbiAgICAgICAgICAgIG1vZGlmaWVkQ29udGVudC5ib2R5ID0gXCIqXCIgKyBtb2RpZmllZENvbnRlbnQuYm9keSArIFwiKlwiO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IG1vZGlmaWVkRXZlbnQgPSBuZXcgTWF0cml4RXZlbnQoKTtcbiAgICAgICAgbW9kaWZpZWRFdmVudC5ldmVudCA9IG14RXYuZXZlbnQ7XG4gICAgICAgIG1vZGlmaWVkRXZlbnQuc2VuZGVyID0gbXhFdi5zZW5kZXI7XG4gICAgICAgIG1vZGlmaWVkRXZlbnQuZXZlbnQudHlwZSA9IFwibS5yb29tLm1lc3NhZ2VcIjtcbiAgICAgICAgbW9kaWZpZWRFdmVudC5ldmVudC5jb250ZW50ID0gbW9kaWZpZWRDb250ZW50O1xuICAgICAgICByZXR1cm4gbW9kaWZpZWRFdmVudDtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYXN5bmMgY3JlYXRlTWVzc2FnZUJvZHkobXhFdjogTWF0cml4RXZlbnQsIGpvaW5lZCA9IGZhbHNlKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgbGV0IGV2ZW50VGlsZTogc3RyaW5nO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKHRoaXMuaXNBdHRhY2htZW50KG14RXYpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuZXhwb3J0T3B0aW9ucy5hdHRhY2htZW50c0luY2x1ZGVkKSB7XG4gICAgICAgICAgICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBibG9iID0gYXdhaXQgdGhpcy5nZXRNZWRpYUJsb2IobXhFdik7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy50b3RhbFNpemUgKyBibG9iLnNpemUgPiB0aGlzLmV4cG9ydE9wdGlvbnMubWF4U2l6ZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50VGlsZSA9IGF3YWl0IHRoaXMuZ2V0RXZlbnRUaWxlTWFya3VwKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLmNyZWF0ZU1vZGlmaWVkRXZlbnQodGhpcy5tZWRpYU9taXRUZXh0LCBteEV2KSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgam9pbmVkLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMudG90YWxTaXplICs9IGJsb2Iuc2l6ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBmaWxlUGF0aCA9IHRoaXMuZ2V0RmlsZVBhdGgobXhFdik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZXZlbnRUaWxlID0gYXdhaXQgdGhpcy5nZXRFdmVudFRpbGVNYXJrdXAobXhFdiwgam9pbmVkLCBmaWxlUGF0aCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMudG90YWxTaXplID09IHRoaXMuZXhwb3J0T3B0aW9ucy5tYXhTaXplKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuZXhwb3J0T3B0aW9ucy5hdHRhY2htZW50c0luY2x1ZGVkID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuYWRkRmlsZShmaWxlUGF0aCwgYmxvYik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2dlci5sb2coXCJFcnJvciB3aGlsZSBmZXRjaGluZyBmaWxlXCIgKyBlKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGV2ZW50VGlsZSA9IGF3YWl0IHRoaXMuZ2V0RXZlbnRUaWxlTWFya3VwKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY3JlYXRlTW9kaWZpZWRFdmVudChfdChcImV4cG9ydF9jaGF0fGVycm9yX2ZldGNoaW5nX2ZpbGVcIiksIG14RXYpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGpvaW5lZCxcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBldmVudFRpbGUgPSBhd2FpdCB0aGlzLmdldEV2ZW50VGlsZU1hcmt1cChcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuY3JlYXRlTW9kaWZpZWRFdmVudCh0aGlzLm1lZGlhT21pdFRleHQsIG14RXYpLFxuICAgICAgICAgICAgICAgICAgICAgICAgam9pbmVkLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgZXZlbnRUaWxlID0gYXdhaXQgdGhpcy5nZXRFdmVudFRpbGVNYXJrdXAobXhFdiwgam9pbmVkKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBjYXRjaCAoZSkge1xuICAgICAgICAgICAgLy8gVE9ETzogSGFuZGxlIGNhbGxFdmVudCBlcnJvcnNcbiAgICAgICAgICAgIGxvZ2dlci5lcnJvcihlKTtcbiAgICAgICAgICAgIGV2ZW50VGlsZSA9IGF3YWl0IHRoaXMuZ2V0RXZlbnRUaWxlTWFya3VwKFxuICAgICAgICAgICAgICAgIHRoaXMuY3JlYXRlTW9kaWZpZWRFdmVudCh0ZXh0Rm9yRXZlbnQobXhFdiwgdGhpcy5yb29tLmNsaWVudCksIG14RXYsIGZhbHNlKSxcbiAgICAgICAgICAgICAgICBqb2luZWQsXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIGV2ZW50VGlsZTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYXN5bmMgY3JlYXRlSFRNTChcbiAgICAgICAgZXZlbnRzOiBNYXRyaXhFdmVudFtdLFxuICAgICAgICBzdGFydDogbnVtYmVyLFxuICAgICAgICBjdXJyZW50UGFnZTogbnVtYmVyLFxuICAgICAgICBuYlBhZ2VzOiBudW1iZXIsXG4gICAgKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgICAgICAgbGV0IGNvbnRlbnQgPSBcIlwiO1xuICAgICAgICBsZXQgcHJldkV2ZW50OiBNYXRyaXhFdmVudCB8IG51bGwgPSBudWxsO1xuICAgICAgICBmb3IgKGxldCBpID0gc3RhcnQ7IGkgPCBNYXRoLm1pbihzdGFydCArIDEwMDAsIGV2ZW50cy5sZW5ndGgpOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnN0IGV2ZW50ID0gZXZlbnRzW2ldO1xuICAgICAgICAgICAgdGhpcy51cGRhdGVQcm9ncmVzcyhcbiAgICAgICAgICAgICAgICBfdChcImV4cG9ydF9jaGF0fHByb2Nlc3NpbmdfZXZlbnRfblwiLCB7XG4gICAgICAgICAgICAgICAgICAgIG51bWJlcjogaSArIDEsXG4gICAgICAgICAgICAgICAgICAgIHRvdGFsOiBldmVudHMubGVuZ3RoLFxuICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICAgIGZhbHNlLFxuICAgICAgICAgICAgICAgIHRydWUsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgaWYgKHRoaXMuY2FuY2