matrix-react-sdk
Version:
SDK for matrix.org using React
314 lines (301 loc) • 52 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DOWNLOAD_ICON_URL = void 0;
exports.computedStyle = computedStyle;
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _logger = require("matrix-js-sdk/src/logger");
var _compoundWeb = require("@vector-im/compound-web");
var _icons = require("@vector-im/compound-design-tokens/assets/web/icons");
var _languageHandler = require("../../../languageHandler");
var _Modal = _interopRequireDefault(require("../../../Modal"));
var _AccessibleButton = _interopRequireDefault(require("../elements/AccessibleButton"));
var _Media = require("../../../customisations/Media");
var _ErrorDialog = _interopRequireDefault(require("../dialogs/ErrorDialog"));
var _FileUtils = require("../../../utils/FileUtils");
var _FileDownloader = require("../../../utils/FileDownloader");
var _TextWithTooltip = _interopRequireDefault(require("../elements/TextWithTooltip"));
var _RoomContext = _interopRequireWildcard(require("../../../contexts/RoomContext"));
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 2015-2021 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.
*/
let DOWNLOAD_ICON_URL = exports.DOWNLOAD_ICON_URL = void 0; // cached copy of the download.svg asset for the sandboxed iframe later on
async function cacheDownloadIcon() {
if (DOWNLOAD_ICON_URL) return; // cached already
// eslint-disable-next-line @typescript-eslint/no-var-requires
const svg = await fetch(require("@vector-im/compound-design-tokens/icons/download.svg").default).then(r => r.text());
exports.DOWNLOAD_ICON_URL = DOWNLOAD_ICON_URL = "data:image/svg+xml;base64," + window.btoa(svg);
}
// Cache the asset immediately
// noinspection JSIgnoredPromiseFromCall
cacheDownloadIcon();
// User supplied content can contain scripts, we have to be careful that
// we don't accidentally run those script within the same origin as the
// client. Otherwise those scripts written by remote users can read
// the access token and end-to-end keys that are in local storage.
//
// For attachments downloaded directly from the homeserver we can use
// Content-Security-Policy headers to disable script execution.
//
// But attachments with end-to-end encryption are more difficult to handle.
// We need to decrypt the attachment on the client and then display it.
// To display the attachment we need to turn the decrypted bytes into a URL.
//
// There are two ways to turn bytes into URLs, data URL and blob URLs.
// Data URLs aren't suitable for downloading a file because Chrome has a
// 2MB limit on the size of URLs that can be viewed in the browser or
// downloaded. This limit does not seem to apply when the url is used as
// the source attribute of an image tag.
//
// Blob URLs are generated using window.URL.createObjectURL and unfortunately
// for our purposes they inherit the origin of the page that created them.
// This means that any scripts that run when the URL is viewed will be able
// to access local storage.
//
// The easiest solution is to host the code that generates the blob URL on
// a different domain to the client.
// Another possibility is to generate the blob URL within a sandboxed iframe.
// The downside of using a second domain is that it complicates hosting,
// the downside of using a sandboxed iframe is that the browers are overly
// restrictive in what you are allowed to do with the generated URL.
/**
* Get the current CSS style for a DOMElement.
* @param {HTMLElement} element The element to get the current style of.
* @return {string} The CSS style encoded as a string.
*/
function computedStyle(element) {
if (!element) {
return "";
}
const style = window.getComputedStyle(element, null);
let cssText = style.cssText;
// noinspection EqualityComparisonWithCoercionJS
if (cssText == "") {
// Firefox doesn't implement ".cssText" for computed styles.
// https://bugzilla.mozilla.org/show_bug.cgi?id=137687
for (const rule of style) {
cssText += rule + ":";
cssText += style.getPropertyValue(rule) + ";";
}
}
return cssText;
}
class MFileBody extends _react.default.Component {
constructor(...args) {
super(...args);
(0, _defineProperty2.default)(this, "state", {});
(0, _defineProperty2.default)(this, "iframe", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "dummyLink", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "userDidClick", false);
(0, _defineProperty2.default)(this, "fileDownloader", new _FileDownloader.FileDownloader(() => this.iframe.current));
(0, _defineProperty2.default)(this, "decryptFile", async () => {
if (this.state.decryptedBlob) {
return;
}
try {
this.userDidClick = true;
this.setState({
decryptedBlob: await this.props.mediaEventHelper.sourceBlob.value
});
} catch (err) {
_logger.logger.warn("Unable to decrypt attachment: ", err);
_Modal.default.createDialog(_ErrorDialog.default, {
title: (0, _languageHandler._t)("common|error"),
description: (0, _languageHandler._t)("timeline|m.file|error_decrypting")
});
}
});
(0, _defineProperty2.default)(this, "onPlaceholderClick", async () => {
const mediaHelper = this.props.mediaEventHelper;
if (mediaHelper?.media.isEncrypted) {
await this.decryptFile();
this.downloadFile(this.fileName, this.linkText);
} else {
// As a button we're missing the `download` attribute for styling reasons, so
// download with the file downloader.
this.fileDownloader.download({
blob: await mediaHelper.sourceBlob.value,
name: this.fileName
});
}
});
}
getContentUrl() {
if (this.props.forExport) return null;
const media = (0, _Media.mediaFromContent)(this.props.mxEvent.getContent());
return media.srcHttp;
}
get content() {
return this.props.mxEvent.getContent();
}
get fileName() {
return this.content.body && this.content.body.length > 0 ? this.content.body : (0, _languageHandler._t)("common|attachment");
}
get linkText() {
return (0, _FileUtils.downloadLabelForFile)(this.content, true);
}
downloadFile(fileName, text) {
if (!this.state.decryptedBlob) return;
this.fileDownloader.download({
blob: this.state.decryptedBlob,
name: fileName,
autoDownload: this.userDidClick,
opts: {
imgSrc: DOWNLOAD_ICON_URL,
imgStyle: null,
style: computedStyle(this.dummyLink.current),
textContent: text
}
});
}
componentDidUpdate(prevProps, prevState) {
if (this.props.onHeightChanged && !prevState.decryptedBlob && this.state.decryptedBlob) {
this.props.onHeightChanged();
}
}
render() {
const isEncrypted = this.props.mediaEventHelper?.media.isEncrypted;
const contentUrl = this.getContentUrl();
const contentFileSize = this.content.info ? this.content.info.size : null;
const fileType = this.content.info?.mimetype ?? "application/octet-stream";
let showDownloadLink = !this.props.showGenericPlaceholder || this.context.timelineRenderingType !== _RoomContext.TimelineRenderingType.Room && this.context.timelineRenderingType !== _RoomContext.TimelineRenderingType.Search && this.context.timelineRenderingType !== _RoomContext.TimelineRenderingType.Pinned;
let placeholder = null;
if (this.props.showGenericPlaceholder) {
placeholder = /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
className: "mx_MediaBody mx_MFileBody_info",
onClick: this.onPlaceholderClick
}, /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody_info_icon"
}), /*#__PURE__*/_react.default.createElement(_TextWithTooltip.default, {
tooltip: (0, _FileUtils.presentableTextForFile)(this.content, (0, _languageHandler._t)("common|attachment"), true)
}, /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody_info_filename"
}, (0, _FileUtils.presentableTextForFile)(this.content, (0, _languageHandler._t)("common|attachment"), true, true))));
showDownloadLink = false;
}
if (this.props.forExport) {
const content = this.props.mxEvent.getContent();
// During export, the content url will point to the MSC, which will later point to a local url
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody"
}, /*#__PURE__*/_react.default.createElement("a", {
href: content.file?.url || content.url
}, placeholder));
}
if (this.context.timelineRenderingType === _RoomContext.TimelineRenderingType.Thread) {
showDownloadLink = false;
}
if (isEncrypted) {
if (!this.state.decryptedBlob) {
// Need to decrypt the attachment
// Wait for the user to click on the link before downloading
// and decrypting the attachment.
// This button should actually Download because usercontent/ will try to click itself
// but it is not guaranteed between various browsers' settings.
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody"
}, placeholder, showDownloadLink && /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MFileBody_download"
}, /*#__PURE__*/_react.default.createElement(_compoundWeb.Button, {
size: "sm",
kind: "secondary",
Icon: _icons.DownloadIcon,
onClick: this.decryptFile
}, this.linkText)));
}
const url = "usercontent/"; // XXX: this path should probably be passed from the skin
// If the attachment is encrypted then put the link inside an iframe.
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody"
}, placeholder, showDownloadLink && /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MFileBody_download"
}, /*#__PURE__*/_react.default.createElement("div", {
"aria-hidden": true,
style: {
display: "none"
}
}, /*#__PURE__*/_react.default.createElement(_compoundWeb.Button, {
size: "sm",
kind: "secondary",
Icon: _icons.DownloadIcon,
as: "a",
ref: this.dummyLink
})), /*#__PURE__*/_react.default.createElement("iframe", {
"aria-hidden": true,
title: (0, _FileUtils.presentableTextForFile)(this.content, (0, _languageHandler._t)("common|attachment"), true, true),
src: url,
onLoad: () => this.downloadFile(this.fileName, this.linkText),
ref: this.iframe,
sandbox: "allow-scripts allow-downloads"
})));
} else if (contentUrl) {
const downloadProps = {
target: "_blank",
rel: "noreferrer noopener",
// We set the href regardless of whether or not we intercept the download
// because we don't really want to convert the file to a blob eagerly, and
// still want "open in new tab" and "save link as" to work.
href: contentUrl
};
// Blobs can only have up to 500mb, so if the file reports as being too large then
// we won't try and convert it. Likewise, if the file size is unknown then we'll assume
// it is too big. There is the risk of the reported file size and the actual file size
// being different, however the user shouldn't normally run into this problem.
const fileTooBig = typeof contentFileSize === "number" ? contentFileSize > 524288000 : true;
if (["application/pdf"].includes(fileType) && !fileTooBig) {
// We want to force a download on this type, so use an onClick handler.
downloadProps["onClick"] = e => {
_logger.logger.log(`Downloading ${fileType} as blob (unencrypted)`);
// Avoid letting the <a> do its thing
e.preventDefault();
e.stopPropagation();
// Start a fetch for the download
// Based upon https://stackoverflow.com/a/49500465
this.props.mediaEventHelper?.sourceBlob.value.then(blob => {
const blobUrl = URL.createObjectURL(blob);
// We have to create an anchor to download the file
const tempAnchor = document.createElement("a");
tempAnchor.download = this.fileName;
tempAnchor.href = blobUrl;
document.body.appendChild(tempAnchor); // for firefox: https://stackoverflow.com/a/32226068
tempAnchor.click();
tempAnchor.remove();
});
};
} else {
// Else we are hoping the browser will do the right thing
downloadProps["download"] = this.fileName;
}
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody"
}, placeholder, showDownloadLink && /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MFileBody_download"
}, /*#__PURE__*/_react.default.createElement(_compoundWeb.Button, (0, _extends2.default)({
size: "sm",
kind: "secondary",
Icon: _icons.DownloadIcon,
as: "a"
}, downloadProps), this.linkText)));
} else {
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MFileBody"
}, placeholder, (0, _languageHandler._t)("timeline|m.file|error_invalid"));
}
}
}
exports.default = MFileBody;
(0, _defineProperty2.default)(MFileBody, "contextType", _RoomContext.default);
(0, _defineProperty2.default)(MFileBody, "defaultProps", {
showGenericPlaceholder: true
});
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfbG9nZ2VyIiwiX2NvbXBvdW5kV2ViIiwiX2ljb25zIiwiX2xhbmd1YWdlSGFuZGxlciIsIl9Nb2RhbCIsIl9pbnRlcm9wUmVxdWlyZURlZmF1bHQiLCJfQWNjZXNzaWJsZUJ1dHRvbiIsIl9NZWRpYSIsIl9FcnJvckRpYWxvZyIsIl9GaWxlVXRpbHMiLCJfRmlsZURvd25sb2FkZXIiLCJfVGV4dFdpdGhUb29sdGlwIiwiX1Jvb21Db250ZXh0IiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0IiwiRE9XTkxPQURfSUNPTl9VUkwiLCJleHBvcnRzIiwiY2FjaGVEb3dubG9hZEljb24iLCJzdmciLCJmZXRjaCIsInRoZW4iLCJ0ZXh0Iiwid2luZG93IiwiYnRvYSIsImNvbXB1dGVkU3R5bGUiLCJlbGVtZW50Iiwic3R5bGUiLCJnZXRDb21wdXRlZFN0eWxlIiwiY3NzVGV4dCIsInJ1bGUiLCJnZXRQcm9wZXJ0eVZhbHVlIiwiTUZpbGVCb2R5IiwiUmVhY3QiLCJDb21wb25lbnQiLCJjb25zdHJ1Y3RvciIsImFyZ3MiLCJfZGVmaW5lUHJvcGVydHkyIiwiY3JlYXRlUmVmIiwiRmlsZURvd25sb2FkZXIiLCJpZnJhbWUiLCJjdXJyZW50Iiwic3RhdGUiLCJkZWNyeXB0ZWRCbG9iIiwidXNlckRpZENsaWNrIiwic2V0U3RhdGUiLCJwcm9wcyIsIm1lZGlhRXZlbnRIZWxwZXIiLCJzb3VyY2VCbG9iIiwidmFsdWUiLCJlcnIiLCJsb2dnZXIiLCJ3YXJuIiwiTW9kYWwiLCJjcmVhdGVEaWFsb2ciLCJFcnJvckRpYWxvZyIsInRpdGxlIiwiX3QiLCJkZXNjcmlwdGlvbiIsIm1lZGlhSGVscGVyIiwibWVkaWEiLCJpc0VuY3J5cHRlZCIsImRlY3J5cHRGaWxlIiwiZG93bmxvYWRGaWxlIiwiZmlsZU5hbWUiLCJsaW5rVGV4dCIsImZpbGVEb3dubG9hZGVyIiwiZG93bmxvYWQiLCJibG9iIiwibmFtZSIsImdldENvbnRlbnRVcmwiLCJmb3JFeHBvcnQiLCJtZWRpYUZyb21Db250ZW50IiwibXhFdmVudCIsImdldENvbnRlbnQiLCJzcmNIdHRwIiwiY29udGVudCIsImJvZHkiLCJsZW5ndGgiLCJkb3dubG9hZExhYmVsRm9yRmlsZSIsImF1dG9Eb3dubG9hZCIsIm9wdHMiLCJpbWdTcmMiLCJpbWdTdHlsZSIsImR1bW15TGluayIsInRleHRDb250ZW50IiwiY29tcG9uZW50RGlkVXBkYXRlIiwicHJldlByb3BzIiwicHJldlN0YXRlIiwib25IZWlnaHRDaGFuZ2VkIiwicmVuZGVyIiwiY29udGVudFVybCIsImNvbnRlbnRGaWxlU2l6ZSIsImluZm8iLCJzaXplIiwiZmlsZVR5cGUiLCJtaW1ldHlwZSIsInNob3dEb3dubG9hZExpbmsiLCJzaG93R2VuZXJpY1BsYWNlaG9sZGVyIiwiY29udGV4dCIsInRpbWVsaW5lUmVuZGVyaW5nVHlwZSIsIlRpbWVsaW5lUmVuZGVyaW5nVHlwZSIsIlJvb20iLCJTZWFyY2giLCJQaW5uZWQiLCJwbGFjZWhvbGRlciIsImNyZWF0ZUVsZW1lbnQiLCJjbGFzc05hbWUiLCJvbkNsaWNrIiwib25QbGFjZWhvbGRlckNsaWNrIiwidG9vbHRpcCIsInByZXNlbnRhYmxlVGV4dEZvckZpbGUiLCJocmVmIiwiZmlsZSIsInVybCIsIlRocmVhZCIsIkJ1dHRvbiIsImtpbmQiLCJJY29uIiwiRG93bmxvYWRJY29uIiwiZGlzcGxheSIsImFzIiwicmVmIiwic3JjIiwib25Mb2FkIiwic2FuZGJveCIsImRvd25sb2FkUHJvcHMiLCJ0YXJnZXQiLCJyZWwiLCJmaWxlVG9vQmlnIiwiaW5jbHVkZXMiLCJsb2ciLCJwcmV2ZW50RGVmYXVsdCIsInN0b3BQcm9wYWdhdGlvbiIsImJsb2JVcmwiLCJVUkwiLCJjcmVhdGVPYmplY3RVUkwiLCJ0ZW1wQW5jaG9yIiwiZG9jdW1lbnQiLCJhcHBlbmRDaGlsZCIsImNsaWNrIiwicmVtb3ZlIiwiX2V4dGVuZHMyIiwiUm9vbUNvbnRleHQiXSwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aWV3cy9tZXNzYWdlcy9NRmlsZUJvZHkudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDE1LTIwMjEgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IFJlYWN0LCB7IEFsbEhUTUxBdHRyaWJ1dGVzLCBjcmVhdGVSZWYgfSBmcm9tIFwicmVhY3RcIjtcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9sb2dnZXJcIjtcbmltcG9ydCB7IE1lZGlhRXZlbnRDb250ZW50IH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL3R5cGVzXCI7XG5pbXBvcnQgeyBCdXR0b24gfSBmcm9tIFwiQHZlY3Rvci1pbS9jb21wb3VuZC13ZWJcIjtcbmltcG9ydCB7IERvd25sb2FkSWNvbiB9IGZyb20gXCJAdmVjdG9yLWltL2NvbXBvdW5kLWRlc2lnbi10b2tlbnMvYXNzZXRzL3dlYi9pY29uc1wiO1xuXG5pbXBvcnQgeyBfdCB9IGZyb20gXCIuLi8uLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCBNb2RhbCBmcm9tIFwiLi4vLi4vLi4vTW9kYWxcIjtcbmltcG9ydCBBY2Nlc3NpYmxlQnV0dG9uIGZyb20gXCIuLi9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgeyBtZWRpYUZyb21Db250ZW50IH0gZnJvbSBcIi4uLy4uLy4uL2N1c3RvbWlzYXRpb25zL01lZGlhXCI7XG5pbXBvcnQgRXJyb3JEaWFsb2cgZnJvbSBcIi4uL2RpYWxvZ3MvRXJyb3JEaWFsb2dcIjtcbmltcG9ydCB7IGRvd25sb2FkTGFiZWxGb3JGaWxlLCBwcmVzZW50YWJsZVRleHRGb3JGaWxlIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL0ZpbGVVdGlsc1wiO1xuaW1wb3J0IHsgSUJvZHlQcm9wcyB9IGZyb20gXCIuL0lCb2R5UHJvcHNcIjtcbmltcG9ydCB7IEZpbGVEb3dubG9hZGVyIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL0ZpbGVEb3dubG9hZGVyXCI7XG5pbXBvcnQgVGV4dFdpdGhUb29sdGlwIGZyb20gXCIuLi9lbGVtZW50cy9UZXh0V2l0aFRvb2x0aXBcIjtcbmltcG9ydCBSb29tQ29udGV4dCwgeyBUaW1lbGluZVJlbmRlcmluZ1R5cGUgfSBmcm9tIFwiLi4vLi4vLi4vY29udGV4dHMvUm9vbUNvbnRleHRcIjtcblxuZXhwb3J0IGxldCBET1dOTE9BRF9JQ09OX1VSTDogc3RyaW5nOyAvLyBjYWNoZWQgY29weSBvZiB0aGUgZG93bmxvYWQuc3ZnIGFzc2V0IGZvciB0aGUgc2FuZGJveGVkIGlmcmFtZSBsYXRlciBvblxuXG5hc3luYyBmdW5jdGlvbiBjYWNoZURvd25sb2FkSWNvbigpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBpZiAoRE9XTkxPQURfSUNPTl9VUkwpIHJldHVybjsgLy8gY2FjaGVkIGFscmVhZHlcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXZhci1yZXF1aXJlc1xuICAgIGNvbnN0IHN2ZyA9IGF3YWl0IGZldGNoKHJlcXVpcmUoXCJAdmVjdG9yLWltL2NvbXBvdW5kLWRlc2lnbi10b2tlbnMvaWNvbnMvZG93bmxvYWQuc3ZnXCIpLmRlZmF1bHQpLnRoZW4oKHIpID0+XG4gICAgICAgIHIudGV4dCgpLFxuICAgICk7XG4gICAgRE9XTkxPQURfSUNPTl9VUkwgPSBcImRhdGE6aW1hZ2Uvc3ZnK3htbDtiYXNlNjQsXCIgKyB3aW5kb3cuYnRvYShzdmcpO1xufVxuXG4vLyBDYWNoZSB0aGUgYXNzZXQgaW1tZWRpYXRlbHlcbi8vIG5vaW5zcGVjdGlvbiBKU0lnbm9yZWRQcm9taXNlRnJvbUNhbGxcbmNhY2hlRG93bmxvYWRJY29uKCk7XG5cbi8vIFVzZXIgc3VwcGxpZWQgY29udGVudCBjYW4gY29udGFpbiBzY3JpcHRzLCB3ZSBoYXZlIHRvIGJlIGNhcmVmdWwgdGhhdFxuLy8gd2UgZG9uJ3QgYWNjaWRlbnRhbGx5IHJ1biB0aG9zZSBzY3JpcHQgd2l0aGluIHRoZSBzYW1lIG9yaWdpbiBhcyB0aGVcbi8vIGNsaWVudC4gT3RoZXJ3aXNlIHRob3NlIHNjcmlwdHMgd3JpdHRlbiBieSByZW1vdGUgdXNlcnMgY2FuIHJlYWRcbi8vIHRoZSBhY2Nlc3MgdG9rZW4gYW5kIGVuZC10by1lbmQga2V5cyB0aGF0IGFyZSBpbiBsb2NhbCBzdG9yYWdlLlxuLy9cbi8vIEZvciBhdHRhY2htZW50cyBkb3dubG9hZGVkIGRpcmVjdGx5IGZyb20gdGhlIGhvbWVzZXJ2ZXIgd2UgY2FuIHVzZVxuLy8gQ29udGVudC1TZWN1cml0eS1Qb2xpY3kgaGVhZGVycyB0byBkaXNhYmxlIHNjcmlwdCBleGVjdXRpb24uXG4vL1xuLy8gQnV0IGF0dGFjaG1lbnRzIHdpdGggZW5kLXRvLWVuZCBlbmNyeXB0aW9uIGFyZSBtb3JlIGRpZmZpY3VsdCB0byBoYW5kbGUuXG4vLyBXZSBuZWVkIHRvIGRlY3J5cHQgdGhlIGF0dGFjaG1lbnQgb24gdGhlIGNsaWVudCBhbmQgdGhlbiBkaXNwbGF5IGl0LlxuLy8gVG8gZGlzcGxheSB0aGUgYXR0YWNobWVudCB3ZSBuZWVkIHRvIHR1cm4gdGhlIGRlY3J5cHRlZCBieXRlcyBpbnRvIGEgVVJMLlxuLy9cbi8vIFRoZXJlIGFyZSB0d28gd2F5cyB0byB0dXJuIGJ5dGVzIGludG8gVVJMcywgZGF0YSBVUkwgYW5kIGJsb2IgVVJMcy5cbi8vIERhdGEgVVJMcyBhcmVuJ3Qgc3VpdGFibGUgZm9yIGRvd25sb2FkaW5nIGEgZmlsZSBiZWNhdXNlIENocm9tZSBoYXMgYVxuLy8gMk1CIGxpbWl0IG9uIHRoZSBzaXplIG9mIFVSTHMgdGhhdCBjYW4gYmUgdmlld2VkIGluIHRoZSBicm93c2VyIG9yXG4vLyBkb3dubG9hZGVkLiBUaGlzIGxpbWl0IGRvZXMgbm90IHNlZW0gdG8gYXBwbHkgd2hlbiB0aGUgdXJsIGlzIHVzZWQgYXNcbi8vIHRoZSBzb3VyY2UgYXR0cmlidXRlIG9mIGFuIGltYWdlIHRhZy5cbi8vXG4vLyBCbG9iIFVSTHMgYXJlIGdlbmVyYXRlZCB1c2luZyB3aW5kb3cuVVJMLmNyZWF0ZU9iamVjdFVSTCBhbmQgdW5mb3J0dW5hdGVseVxuLy8gZm9yIG91ciBwdXJwb3NlcyB0aGV5IGluaGVyaXQgdGhlIG9yaWdpbiBvZiB0aGUgcGFnZSB0aGF0IGNyZWF0ZWQgdGhlbS5cbi8vIFRoaXMgbWVhbnMgdGhhdCBhbnkgc2NyaXB0cyB0aGF0IHJ1biB3aGVuIHRoZSBVUkwgaXMgdmlld2VkIHdpbGwgYmUgYWJsZVxuLy8gdG8gYWNjZXNzIGxvY2FsIHN0b3JhZ2UuXG4vL1xuLy8gVGhlIGVhc2llc3Qgc29sdXRpb24gaXMgdG8gaG9zdCB0aGUgY29kZSB0aGF0IGdlbmVyYXRlcyB0aGUgYmxvYiBVUkwgb25cbi8vIGEgZGlmZmVyZW50IGRvbWFpbiB0byB0aGUgY2xpZW50LlxuLy8gQW5vdGhlciBwb3NzaWJpbGl0eSBpcyB0byBnZW5lcmF0ZSB0aGUgYmxvYiBVUkwgd2l0aGluIGEgc2FuZGJveGVkIGlmcmFtZS5cbi8vIFRoZSBkb3duc2lkZSBvZiB1c2luZyBhIHNlY29uZCBkb21haW4gaXMgdGhhdCBpdCBjb21wbGljYXRlcyBob3N0aW5nLFxuLy8gdGhlIGRvd25zaWRlIG9mIHVzaW5nIGEgc2FuZGJveGVkIGlmcmFtZSBpcyB0aGF0IHRoZSBicm93ZXJzIGFyZSBvdmVybHlcbi8vIHJlc3RyaWN0aXZlIGluIHdoYXQgeW91IGFyZSBhbGxvd2VkIHRvIGRvIHdpdGggdGhlIGdlbmVyYXRlZCBVUkwuXG5cbi8qKlxuICogR2V0IHRoZSBjdXJyZW50IENTUyBzdHlsZSBmb3IgYSBET01FbGVtZW50LlxuICogQHBhcmFtIHtIVE1MRWxlbWVudH0gZWxlbWVudCBUaGUgZWxlbWVudCB0byBnZXQgdGhlIGN1cnJlbnQgc3R5bGUgb2YuXG4gKiBAcmV0dXJuIHtzdHJpbmd9IFRoZSBDU1Mgc3R5bGUgZW5jb2RlZCBhcyBhIHN0cmluZy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNvbXB1dGVkU3R5bGUoZWxlbWVudDogSFRNTEVsZW1lbnQgfCBudWxsKTogc3RyaW5nIHtcbiAgICBpZiAoIWVsZW1lbnQpIHtcbiAgICAgICAgcmV0dXJuIFwiXCI7XG4gICAgfVxuICAgIGNvbnN0IHN0eWxlID0gd2luZG93LmdldENvbXB1dGVkU3R5bGUoZWxlbWVudCwgbnVsbCk7XG4gICAgbGV0IGNzc1RleHQgPSBzdHlsZS5jc3NUZXh0O1xuICAgIC8vIG5vaW5zcGVjdGlvbiBFcXVhbGl0eUNvbXBhcmlzb25XaXRoQ29lcmNpb25KU1xuICAgIGlmIChjc3NUZXh0ID09IFwiXCIpIHtcbiAgICAgICAgLy8gRmlyZWZveCBkb2Vzbid0IGltcGxlbWVudCBcIi5jc3NUZXh0XCIgZm9yIGNvbXB1dGVkIHN0eWxlcy5cbiAgICAgICAgLy8gaHR0cHM6Ly9idWd6aWxsYS5tb3ppbGxhLm9yZy9zaG93X2J1Zy5jZ2k/aWQ9MTM3Njg3XG4gICAgICAgIGZvciAoY29uc3QgcnVsZSBvZiBzdHlsZSkge1xuICAgICAgICAgICAgY3NzVGV4dCArPSBydWxlICsgXCI6XCI7XG4gICAgICAgICAgICBjc3NUZXh0ICs9IHN0eWxlLmdldFByb3BlcnR5VmFsdWUocnVsZSkgKyBcIjtcIjtcbiAgICAgICAgfVxuICAgIH1cbiAgICByZXR1cm4gY3NzVGV4dDtcbn1cblxuaW50ZXJmYWNlIElQcm9wcyBleHRlbmRzIElCb2R5UHJvcHMge1xuICAgIC8qIHdoZXRoZXIgb3Igbm90IHRvIHNob3cgdGhlIGRlZmF1bHQgcGxhY2Vob2xkZXIgZm9yIHRoZSBmaWxlLiBEZWZhdWx0cyB0byB0cnVlLiAqL1xuICAgIHNob3dHZW5lcmljUGxhY2Vob2xkZXI6IGJvb2xlYW47XG59XG5cbmludGVyZmFjZSBJU3RhdGUge1xuICAgIGRlY3J5cHRlZEJsb2I/OiBCbG9iO1xufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNRmlsZUJvZHkgZXh0ZW5kcyBSZWFjdC5Db21wb25lbnQ8SVByb3BzLCBJU3RhdGU+IHtcbiAgICBwdWJsaWMgc3RhdGljIGNvbnRleHRUeXBlID0gUm9vbUNvbnRleHQ7XG4gICAgcHVibGljIGRlY2xhcmUgY29udGV4dDogUmVhY3QuQ29udGV4dFR5cGU8dHlwZW9mIFJvb21Db250ZXh0PjtcblxuICAgIHB1YmxpYyBzdGF0ZTogSVN0YXRlID0ge307XG5cbiAgICBwdWJsaWMgc3RhdGljIGRlZmF1bHRQcm9wcyA9IHtcbiAgICAgICAgc2hvd0dlbmVyaWNQbGFjZWhvbGRlcjogdHJ1ZSxcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBpZnJhbWU6IFJlYWN0LlJlZk9iamVjdDxIVE1MSUZyYW1lRWxlbWVudD4gPSBjcmVhdGVSZWYoKTtcbiAgICBwcml2YXRlIGR1bW15TGluazogUmVhY3QuUmVmT2JqZWN0PEhUTUxBbmNob3JFbGVtZW50PiA9IGNyZWF0ZVJlZigpO1xuICAgIHByaXZhdGUgdXNlckRpZENsaWNrID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBmaWxlRG93bmxvYWRlcjogRmlsZURvd25sb2FkZXIgPSBuZXcgRmlsZURvd25sb2FkZXIoKCkgPT4gdGhpcy5pZnJhbWUuY3VycmVudCk7XG5cbiAgICBwcml2YXRlIGdldENvbnRlbnRVcmwoKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgICAgIGlmICh0aGlzLnByb3BzLmZvckV4cG9ydCkgcmV0dXJuIG51bGw7XG4gICAgICAgIGNvbnN0IG1lZGlhID0gbWVkaWFGcm9tQ29udGVudCh0aGlzLnByb3BzLm14RXZlbnQuZ2V0Q29udGVudCgpKTtcbiAgICAgICAgcmV0dXJuIG1lZGlhLnNyY0h0dHA7XG4gICAgfVxuICAgIHByaXZhdGUgZ2V0IGNvbnRlbnQoKTogTWVkaWFFdmVudENvbnRlbnQge1xuICAgICAgICByZXR1cm4gdGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQ8TWVkaWFFdmVudENvbnRlbnQ+KCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXQgZmlsZU5hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuY29udGVudC5ib2R5ICYmIHRoaXMuY29udGVudC5ib2R5Lmxlbmd0aCA+IDAgPyB0aGlzLmNvbnRlbnQuYm9keSA6IF90KFwiY29tbW9ufGF0dGFjaG1lbnRcIik7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXQgbGlua1RleHQoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIGRvd25sb2FkTGFiZWxGb3JGaWxlKHRoaXMuY29udGVudCwgdHJ1ZSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBkb3dubG9hZEZpbGUoZmlsZU5hbWU6IHN0cmluZywgdGV4dDogc3RyaW5nKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5zdGF0ZS5kZWNyeXB0ZWRCbG9iKSByZXR1cm47XG4gICAgICAgIHRoaXMuZmlsZURvd25sb2FkZXIuZG93bmxvYWQoe1xuICAgICAgICAgICAgYmxvYjogdGhpcy5zdGF0ZS5kZWNyeXB0ZWRCbG9iLFxuICAgICAgICAgICAgbmFtZTogZmlsZU5hbWUsXG4gICAgICAgICAgICBhdXRvRG93bmxvYWQ6IHRoaXMudXNlckRpZENsaWNrLFxuICAgICAgICAgICAgb3B0czoge1xuICAgICAgICAgICAgICAgIGltZ1NyYzogRE9XTkxPQURfSUNPTl9VUkwsXG4gICAgICAgICAgICAgICAgaW1nU3R5bGU6IG51bGwsXG4gICAgICAgICAgICAgICAgc3R5bGU6IGNvbXB1dGVkU3R5bGUodGhpcy5kdW1teUxpbmsuY3VycmVudCksXG4gICAgICAgICAgICAgICAgdGV4dENvbnRlbnQ6IHRleHQsXG4gICAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY29tcG9uZW50RGlkVXBkYXRlKHByZXZQcm9wczogSVByb3BzLCBwcmV2U3RhdGU6IElTdGF0ZSk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5wcm9wcy5vbkhlaWdodENoYW5nZWQgJiYgIXByZXZTdGF0ZS5kZWNyeXB0ZWRCbG9iICYmIHRoaXMuc3RhdGUuZGVjcnlwdGVkQmxvYikge1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkhlaWdodENoYW5nZWQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgZGVjcnlwdEZpbGUgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICAgIGlmICh0aGlzLnN0YXRlLmRlY3J5cHRlZEJsb2IpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICB0cnkge1xuICAgICAgICAgICAgdGhpcy51c2VyRGlkQ2xpY2sgPSB0cnVlO1xuICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgZGVjcnlwdGVkQmxvYjogYXdhaXQgdGhpcy5wcm9wcy5tZWRpYUV2ZW50SGVscGVyIS5zb3VyY2VCbG9iLnZhbHVlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgICAgICAgbG9nZ2VyLndhcm4oXCJVbmFibGUgdG8gZGVjcnlwdCBhdHRhY2htZW50OiBcIiwgZXJyKTtcbiAgICAgICAgICAgIE1vZGFsLmNyZWF0ZURpYWxvZyhFcnJvckRpYWxvZywge1xuICAgICAgICAgICAgICAgIHRpdGxlOiBfdChcImNvbW1vbnxlcnJvclwiKSxcbiAgICAgICAgICAgICAgICBkZXNjcmlwdGlvbjogX3QoXCJ0aW1lbGluZXxtLmZpbGV8ZXJyb3JfZGVjcnlwdGluZ1wiKSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25QbGFjZWhvbGRlckNsaWNrID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBjb25zdCBtZWRpYUhlbHBlciA9IHRoaXMucHJvcHMubWVkaWFFdmVudEhlbHBlcjtcbiAgICAgICAgaWYgKG1lZGlhSGVscGVyPy5tZWRpYS5pc0VuY3J5cHRlZCkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5kZWNyeXB0RmlsZSgpO1xuICAgICAgICAgICAgdGhpcy5kb3dubG9hZEZpbGUodGhpcy5maWxlTmFtZSwgdGhpcy5saW5rVGV4dCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBBcyBhIGJ1dHRvbiB3ZSdyZSBtaXNzaW5nIHRoZSBgZG93bmxvYWRgIGF0dHJpYnV0ZSBmb3Igc3R5bGluZyByZWFzb25zLCBzb1xuICAgICAgICAgICAgLy8gZG93bmxvYWQgd2l0aCB0aGUgZmlsZSBkb3dubG9hZGVyLlxuICAgICAgICAgICAgdGhpcy5maWxlRG93bmxvYWRlci5kb3dubG9hZCh7XG4gICAgICAgICAgICAgICAgYmxvYjogYXdhaXQgbWVkaWFIZWxwZXIhLnNvdXJjZUJsb2IudmFsdWUsXG4gICAgICAgICAgICAgICAgbmFtZTogdGhpcy5maWxlTmFtZSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHB1YmxpYyByZW5kZXIoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgICAgICAgY29uc3QgaXNFbmNyeXB0ZWQgPSB0aGlzLnByb3BzLm1lZGlhRXZlbnRIZWxwZXI/Lm1lZGlhLmlzRW5jcnlwdGVkO1xuICAgICAgICBjb25zdCBjb250ZW50VXJsID0gdGhpcy5nZXRDb250ZW50VXJsKCk7XG4gICAgICAgIGNvbnN0IGNvbnRlbnRGaWxlU2l6ZSA9IHRoaXMuY29udGVudC5pbmZvID8gdGhpcy5jb250ZW50LmluZm8uc2l6ZSA6IG51bGw7XG4gICAgICAgIGNvbnN0IGZpbGVUeXBlID0gdGhpcy5jb250ZW50LmluZm8/Lm1pbWV0eXBlID8/IFwiYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtXCI7XG5cbiAgICAgICAgbGV0IHNob3dEb3dubG9hZExpbmsgPVxuICAgICAgICAgICAgIXRoaXMucHJvcHMuc2hvd0dlbmVyaWNQbGFjZWhvbGRlciB8fFxuICAgICAgICAgICAgKHRoaXMuY29udGV4dC50aW1lbGluZVJlbmRlcmluZ1R5cGUgIT09IFRpbWVsaW5lUmVuZGVyaW5nVHlwZS5Sb29tICYmXG4gICAgICAgICAgICAgICAgdGhpcy5jb250ZXh0LnRpbWVsaW5lUmVuZGVyaW5nVHlwZSAhPT0gVGltZWxpbmVSZW5kZXJpbmdUeXBlLlNlYXJjaCAmJlxuICAgICAgICAgICAgICAgIHRoaXMuY29udGV4dC50aW1lbGluZVJlbmRlcmluZ1R5cGUgIT09IFRpbWVsaW5lUmVuZGVyaW5nVHlwZS5QaW5uZWQpO1xuXG4gICAgICAgIGxldCBwbGFjZWhvbGRlcjogUmVhY3QuUmVhY3ROb2RlID0gbnVsbDtcbiAgICAgICAgaWYgKHRoaXMucHJvcHMuc2hvd0dlbmVyaWNQbGFjZWhvbGRlcikge1xuICAgICAgICAgICAgcGxhY2Vob2xkZXIgPSAoXG4gICAgICAgICAgICAgICAgPEFjY2Vzc2libGVCdXR0b24gY2xhc3NOYW1lPVwibXhfTWVkaWFCb2R5IG14X01GaWxlQm9keV9pbmZvXCIgb25DbGljaz17dGhpcy5vblBsYWNlaG9sZGVyQ2xpY2t9PlxuICAgICAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9XCJteF9NRmlsZUJvZHlfaW5mb19pY29uXCIgLz5cbiAgICAgICAgICAgICAgICAgICAgPFRleHRXaXRoVG9vbHRpcCB0b29sdGlwPXtwcmVzZW50YWJsZVRleHRGb3JGaWxlKHRoaXMuY29udGVudCwgX3QoXCJjb21tb258YXR0YWNobWVudFwiKSwgdHJ1ZSl9PlxuICAgICAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPVwibXhfTUZpbGVCb2R5X2luZm9fZmlsZW5hbWVcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7cHJlc2VudGFibGVUZXh0Rm9yRmlsZSh0aGlzLmNvbnRlbnQsIF90KFwiY29tbW9ufGF0dGFjaG1lbnRcIiksIHRydWUsIHRydWUpfVxuICAgICAgICAgICAgICAgICAgICAgICAgPC9zcGFuPlxuICAgICAgICAgICAgICAgICAgICA8L1RleHRXaXRoVG9vbHRpcD5cbiAgICAgICAgICAgICAgICA8L0FjY2Vzc2libGVCdXR0b24+XG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgc2hvd0Rvd25sb2FkTGluayA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMucHJvcHMuZm9yRXhwb3J0KSB7XG4gICAgICAgICAgICBjb25zdCBjb250ZW50ID0gdGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQoKTtcbiAgICAgICAgICAgIC8vIER1cmluZyBleHBvcnQsIHRoZSBjb250ZW50IHVybCB3aWxsIHBvaW50IHRvIHRoZSBNU0MsIHdoaWNoIHdpbGwgbGF0ZXIgcG9pbnQgdG8gYSBsb2NhbCB1cmxcbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPVwibXhfTUZpbGVCb2R5XCI+XG4gICAgICAgICAgICAgICAgICAgIDxhIGhyZWY9e2NvbnRlbnQuZmlsZT8udXJsIHx8IGNvbnRlbnQudXJsfT57cGxhY2Vob2xkZXJ9PC9hPlxuICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5jb250ZXh0LnRpbWVsaW5lUmVuZGVyaW5nVHlwZSA9PT0gVGltZWxpbmVSZW5kZXJpbmdUeXBlLlRocmVhZCkge1xuICAgICAgICAgICAgc2hvd0Rvd25sb2FkTGluayA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGlzRW5jcnlwdGVkKSB7XG4gICAgICAgICAgICBpZiAoIXRoaXMuc3RhdGUuZGVjcnlwdGVkQmxvYikge1xuICAgICAgICAgICAgICAgIC8vIE5lZWQgdG8gZGVjcnlwdCB0aGUgYXR0YWNobWVudFxuICAgICAgICAgICAgICAgIC8vIFdhaXQgZm9yIHRoZSB1c2VyIHRvIGNsaWNrIG9uIHRoZSBsaW5rIGJlZm9yZSBkb3dubG9hZGluZ1xuICAgICAgICAgICAgICAgIC8vIGFuZCBkZWNyeXB0aW5nIHRoZSBhdHRhY2htZW50LlxuXG4gICAgICAgICAgICAgICAgLy8gVGhpcyBidXR0b24gc2hvdWxkIGFjdHVhbGx5IERvd25sb2FkIGJlY2F1c2UgdXNlcmNvbnRlbnQvIHdpbGwgdHJ5IHRvIGNsaWNrIGl0c2VsZlxuICAgICAgICAgICAgICAgIC8vIGJ1dCBpdCBpcyBub3QgZ3VhcmFudGVlZCBiZXR3ZWVuIHZhcmlvdXMgYnJvd3NlcnMnIHNldHRpbmdzLlxuICAgICAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT1cIm14X01GaWxlQm9keVwiPlxuICAgICAgICAgICAgICAgICAgICAgICAge3BsYWNlaG9sZGVyfVxuICAgICAgICAgICAgICAgICAgICAgICAge3Nob3dEb3dubG9hZExpbmsgJiYgKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPVwibXhfTUZpbGVCb2R5X2Rvd25sb2FkXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxCdXR0b24gc2l6ZT1cInNtXCIga2luZD1cInNlY29uZGFyeVwiIEljb249e0Rvd25sb2FkSWNvbn0gb25DbGljaz17dGhpcy5kZWNyeXB0RmlsZX0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB7dGhpcy5saW5rVGV4dH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9CdXR0b24+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgY29uc3QgdXJsID0gXCJ1c2VyY29udGVudC9cIjsgLy8gWFhYOiB0aGlzIHBhdGggc2hvdWxkIHByb2JhYmx5IGJlIHBhc3NlZCBmcm9tIHRoZSBza2luXG5cbiAgICAgICAgICAgIC8vIElmIHRoZSBhdHRhY2htZW50IGlzIGVuY3J5cHRlZCB0aGVuIHB1dCB0aGUgbGluayBpbnNpZGUgYW4gaWZyYW1lLlxuICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9XCJteF9NRmlsZUJvZHlcIj5cbiAgICAgICAgICAgICAgICAgICAge3BsYWNlaG9sZGVyfVxuICAgICAgICAgICAgICAgICAgICB7c2hvd0Rvd25sb2FkTGluayAmJiAoXG4gICAgICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cIm14X01GaWxlQm9keV9kb3dubG9hZFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgYXJpYS1oaWRkZW4gc3R5bGU9e3sgZGlzcGxheTogXCJub25lXCIgfX0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsvKlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiBBZGQgZHVtbXkgY29weSBvZiB0aGUgYnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqIFdlJ2xsIHVzZSBpdCB0byBsZWFybiBob3cgdGhlIGRvd25sb2FkIGJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKiB3b3VsZCBoYXZlIGJlZW4gc3R5bGVkIGlmIGl0IHdhcyByZW5kZXJlZCBpbmxpbmUuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAqL31cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgey8qIHRoaXMgdmlvbGF0ZXMgbXVsdGlwbGUgZXNsaW50IHJ1bGVzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc28gaWdub3JlIGl0IGNvbXBsZXRlbHkgKi99XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxCdXR0b24gc2l6ZT1cInNtXCIga2luZD1cInNlY29uZGFyeVwiIEljb249e0Rvd25sb2FkSWNvbn0gYXM9XCJhXCIgcmVmPXt0aGlzLmR1bW15TGlua30gLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7LypcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUT0RPOiBNb3ZlIGlmcmFtZSAoYW5kIGR1bW15IGxpbmspIGludG8gRmlsZURvd25sb2FkZXIuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgV2UgY3VycmVudGx5IGhhdmUgaXQgc2V0IHVwIHRoaXMgd2F5IGJlY2F1c2Ugb2Ygc3R5bGVzIGFwcGxpZWQgdG8gdGhlIGlmcmFtZVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGl0c2VsZiB3aGljaCBjYW5ub3QgYmUgZWFzaWx5IGhhbmRsZWQvb3ZlcnJpZGRlbiBieSB0aGUgRmlsZURvd25sb2FkZXIuIEluXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZnV0dXJlLCB0aGUgZG93bmxvYWQgbGluayBtYXkgZGlzYXBwZWFyIGVudGlyZWx5IGF0IHdoaWNoIHBvaW50IGl0IGNvdWxkIGFsc29cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZSBzdWl0YWJsZSB0byBqdXN0IHJlbW92ZSB0aGlzIGJpdCBvZiBjb2RlLlxuICAgICAgICAgICAgICAgICAgICAgICAgICovfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpZnJhbWVcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJpYS1oaWRkZW5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGU9e3ByZXNlbnRhYmxlVGV4dEZvckZpbGUodGhpcy5jb250ZW50LCBfdChcImNvbW1vbnxhdHRhY2htZW50XCIpLCB0cnVlLCB0cnVlKX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3JjPXt1cmx9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uTG9hZD17KCkgPT4gdGhpcy5kb3dubG9hZEZpbGUodGhpcy5maWxlTmFtZSwgdGhpcy5saW5rVGV4dCl9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZj17dGhpcy5pZnJhbWV9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNhbmRib3g9XCJhbGxvdy1zY3JpcHRzIGFsbG93LWRvd25sb2Fkc1wiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICk7XG4gICAgICAgIH0gZWxzZSBpZiAoY29udGVudFVybCkge1xuICAgICAgICAgICAgY29uc3QgZG93bmxvYWRQcm9wczogUGljazxcbiAgICAgICAgICAgICAgICBBbGxIVE1MQXR0cmlidXRlczxIVE1MQW5jaG9yRWxlbWVudD4sXG4gICAgICAgICAgICAgICAgXCJ0YXJnZXRcIiB8IFwicmVsXCIgfCBcImhyZWZcIiB8IFwib25DbGlja1wiIHwgXCJkb3dubG9hZFwiXG4gICAgICAgICAgICA+ID0ge1xuICAgICAgICAgICAgICAgIHRhcmdldDogXCJfYmxhbmtcIixcbiAgICAgICAgICAgICAgICByZWw6IFwibm9yZWZlcnJlciBub29wZW5lclwiLFxuXG4gICAgICAgICAgICAgICAgLy8gV2Ugc2V0IHRoZSBocmVmIHJlZ2FyZGxlc3Mgb2Ygd2hldGhlciBvciBub3Qgd2UgaW50ZXJjZXB0IHRoZSBkb3dubG9hZFxuICAgICAgICAgICAgICAgIC8vIGJlY2F1c2Ugd2UgZG9uJ3QgcmVhbGx5IHdhbnQgdG8gY29udmVydCB0aGUgZmlsZSB0byBhIGJsb2IgZWFnZXJseSwgYW5kXG4gICAgICAgICAgICAgICAgLy8gc3RpbGwgd2FudCBcIm9wZW4gaW4gbmV3IHRhYlwiIGFuZCBcInNhdmUgbGluayBhc1wiIHRvIHdvcmsuXG4gICAgICAgICAgICAgICAgaHJlZjogY29udGVudFVybCxcbiAgICAgICAgICAgIH07XG5cbiAgICAgICAgICAgIC8vIEJsb2JzIGNhbiBvbmx5IGhhdmUgdXAgdG8gNTAwbWIsIHNvIGlmIHRoZSBmaWxlIHJlcG9ydHMgYXMgYmVpbmcgdG9vIGxhcmdlIHRoZW5cbiAgICAgICAgICAgIC8vIHdlIHdvbid0IHRyeSBhbmQgY29udmVydCBpdC4gTGlrZXdpc2UsIGlmIHRoZSBmaWxlIHNpemUgaXMgdW5rbm93biB0aGVuIHdlJ2xsIGFzc3VtZVxuICAgICAgICAgICAgLy8gaXQgaXMgdG9vIGJpZy4gVGhlcmUgaXMgdGhlIHJpc2sgb2YgdGhlIHJlcG9ydGVkIGZpbGUgc2l6ZSBhbmQgdGhlIGFjdHVhbCBmaWxlIHNpemVcbiAgICAgICAgICAgIC8vIGJlaW5nIGRpZmZlcmVudCwgaG93ZXZlciB0aGUgdXNlciBzaG91bGRuJ3Qgbm9ybWFsbHkgcnVuIGludG8gdGhpcyBwcm9ibGVtLlxuICAgICAgICAgICAgY29uc3QgZmlsZVRvb0JpZyA9IHR5cGVvZiBjb250ZW50RmlsZVNpemUgPT09IFwibnVtYmVyXCIgPyBjb250ZW50RmlsZVNpemUgPiA1MjQyODgwMDAgOiB0cnVlO1xuXG4gICAgICAgICAgICBpZiAoW1wiYXBwbGljYXRpb24vcGRmXCJdLmluY2x1ZGVzKGZpbGVUeXBlKSAmJiAhZmlsZVRvb0JpZykge1xuICAgICAgICAgICAgICAgIC8vIFdlIHdhbnQgdG8gZm9yY2UgYSBkb3dubG9hZCBvbiB0aGlzIHR5cGUsIHNvIHVzZSBhbiBvbkNsaWNrIGhhbmRsZXIuXG4gICAgICAgICAgICAgICAgZG93bmxvYWRQcm9wc1tcIm9uQ2xpY2tcIl0gPSAoZSkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBsb2dnZXIubG9nKGBEb3dubG9hZGluZyAke2ZpbGVUeXBlfSBhcyBibG9iICh1bmVuY3J5cHRlZClgKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBBdm9pZCBsZXR0aW5nIHRoZSA8YT4gZG8gaXRzIHRoaW5nXG4gICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcblxuICAgICAgICAgICAgICAgICAgICAvLyBTdGFydCBhIGZldGNoIGZvciB0aGUgZG93bmxvYWRcbiAgICAgICAgICAgICAgICAgICAgLy8gQmFzZWQgdXBvbiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvNDk1MDA0NjVcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5wcm9wcy5tZWRpYUV2ZW50SGVscGVyPy5zb3VyY2VCbG9iLnZhbHVlLnRoZW4oKGJsb2IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGJsb2JVcmwgPSBVUkwuY3JlYXRlT2JqZWN0VVJMKGJsb2IpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBXZSBoYXZlIHRvIGNyZWF0ZSBhbiBhbmNob3IgdG8gZG93bmxvYWQgdGhlIGZpbGVcbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHRlbXBBbmNob3IgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiYVwiKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBBbmNob3IuZG93bmxvYWQgPSB0aGlzLmZpbGVOYW1lO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGVtcEFuY2hvci5ocmVmID0gYmxvYlVybDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQodGVtcEFuY2hvcik7IC8vIGZvciBmaXJlZm94OiBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMzIyMjYwNjhcbiAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBBbmNob3IuY2xpY2soKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRlbXBBbmNob3IucmVtb3ZlKCk7XG4gICAgICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIH07XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIEVsc2Ugd2UgYXJlIGhvcGluZyB0aGUgYnJvd3NlciB3aWxsIGRvIHRoZSByaWdodCB0aGluZ1xuICAgICAgICAgICAgICAgIGRvd25sb2FkUHJvcHNbXCJkb3dubG9hZFwiXSA9IHRoaXMuZmlsZU5hbWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgPHNwYW4gY2xhc3NOYW1lPVwibXhfTUZpbGVCb2R5XCI+XG4gICAgICAgICAgICAgICAgICAgIHtwbGFjZWhvbGRlcn1cbiAgICAgICAgICAgICAgICAgICAge3Nob3dEb3dubG9hZExpbmsgJiYgKFxuICAgICAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9NRmlsZUJvZHlfZG93bmxvYWRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8QnV0dG9uIHNpemU9XCJzbVwiIGtpbmQ9XCJzZWNvbmRhcnlcIiBJY29uPXtEb3dubG9hZEljb259IGFzPVwiYVwiIHsuLi5kb3dubG9hZFByb3BzfT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAge3RoaXMubGlua1RleHR9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9CdXR0b24+XG4gICAgICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgICAgICA8c3BhbiBjbGFzc05hbWU9XCJteF9NRmlsZUJvZHlcIj5cbiAgICAgICAgICAgICAgICAgICAge3BsYWNlaG9sZGVyfVxuICAgICAgICAgICAgICAgICAgICB7X3QoXCJ0aW1lbGluZXxtLmZpbGV8ZXJyb3JfaW52YWxpZFwiKX1cbiAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgfVxufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7OztBQVFBLElBQUFBLE1BQUEsR0FBQUMsdUJBQUEsQ0FBQUMsT0FBQTtBQUNBLElBQUFDLE9BQUEsR0FBQUQsT0FBQTtBQUVBLElBQUFFLFlBQUEsR0FBQUYsT0FBQTtBQUNBLElBQUFHLE1BQUEsR0FBQUgsT0FBQTtBQUVBLElBQUFJLGdCQUFBLEdBQUFKLE9BQUE7QUFDQSxJQUFBSyxNQUFBLEdBQUFDLHNCQUFBLENBQUFOLE9BQUE7QUFDQSxJQUFBTyxpQkFBQSxHQUFBRCxzQkFBQSxDQUFBTixPQUFBO0FBQ0EsSUFBQVEsTUFBQSxHQUFBUixPQUFBO0FBQ0EsSUFBQVMsWUFBQSxHQUFBSCxzQkFBQSxDQUFBTixPQUFBO0FBQ0EsSUFBQVUsVUFBQSxHQUFBVixPQUFBO0FBRUEsSUFBQVcsZUFBQSxHQUFBWCxPQUFBO0FBQ0EsSUFBQVksZ0JBQUEsR0FBQU4sc0JBQUEsQ0FBQU4sT0FBQTtBQUNBLElBQUFhLFlBQUEsR0FBQWQsdUJBQUEsQ0FBQUMsT0FBQTtBQUFtRixTQUFBYyx5QkFBQUMsQ0FBQSw2QkFBQUMsT0FBQSxtQkFBQUMsQ0FBQSxPQUFBRCxPQUFBLElBQUFFLENBQUEsT0FBQUYsT0FBQSxZQUFBRix3QkFBQSxZQUFBQSxDQUFBQyxDQUFBLFdBQUFBLENBQUEsR0FBQUcsQ0FBQSxHQUFBRCxDQUFBLEtBQUFGLENBQUE7QUFBQSxTQUFBaEIsd0JB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