matrix-react-sdk
Version:
SDK for matrix.org using React
582 lines (567 loc) • 101 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 = _interopRequireWildcard(require("react"));
var _reactDom = _interopRequireDefault(require("react-dom"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _compoundWeb = require("@vector-im/compound-web");
var HtmlUtils = _interopRequireWildcard(require("../../../HtmlUtils"));
var _DateUtils = require("../../../DateUtils");
var _Modal = _interopRequireDefault(require("../../../Modal"));
var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher"));
var _languageHandler = require("../../../languageHandler");
var _ContextMenu = _interopRequireWildcard(require("../../structures/ContextMenu"));
var ContextMenu = _ContextMenu;
var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore"));
var _pillify = require("../../../utils/pillify");
var _tooltipify = require("../../../utils/tooltipify");
var _IntegrationManagers = require("../../../integrations/IntegrationManagers");
var _Permalinks = require("../../../utils/permalinks/Permalinks");
var _strings = require("../../../utils/strings");
var _UIStore = _interopRequireDefault(require("../../../stores/UIStore"));
var _actions = require("../../../dispatcher/actions");
var _GenericTextContextMenu = _interopRequireDefault(require("../context_menus/GenericTextContextMenu"));
var _Spoiler = _interopRequireDefault(require("../elements/Spoiler"));
var _QuestionDialog = _interopRequireDefault(require("../dialogs/QuestionDialog"));
var _MessageEditHistoryDialog = _interopRequireDefault(require("../dialogs/MessageEditHistoryDialog"));
var _EditMessageComposer = _interopRequireDefault(require("../rooms/EditMessageComposer"));
var _LinkPreviewGroup = _interopRequireDefault(require("../rooms/LinkPreviewGroup"));
var _RoomContext = _interopRequireDefault(require("../../../contexts/RoomContext"));
var _AccessibleButton = _interopRequireDefault(require("../elements/AccessibleButton"));
var _linkifyMatrix = require("../../../linkify-matrix");
var _Reply = require("../../../utils/Reply");
var _wysiwyg_composer = require("../rooms/wysiwyg_composer");
var _MatrixClientPeg = require("../../../MatrixClientPeg");
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; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /*
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.
*/
const MAX_HIGHLIGHT_LENGTH = 4096;
class TextualBody extends _react.default.Component {
constructor(...args) {
super(...args);
(0, _defineProperty2.default)(this, "contentRef", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "unmounted", false);
(0, _defineProperty2.default)(this, "pills", []);
(0, _defineProperty2.default)(this, "tooltips", []);
(0, _defineProperty2.default)(this, "state", {
links: [],
widgetHidden: false
});
(0, _defineProperty2.default)(this, "onCancelClick", () => {
this.setState({
widgetHidden: true
});
// FIXME: persist this somewhere smarter than local storage
if (global.localStorage) {
global.localStorage.setItem("hide_preview_" + this.props.mxEvent.getId(), "1");
}
this.forceUpdate();
});
(0, _defineProperty2.default)(this, "onEmoteSenderClick", () => {
const mxEvent = this.props.mxEvent;
_dispatcher.default.dispatch({
action: _actions.Action.ComposerInsert,
userId: mxEvent.getSender(),
timelineRenderingType: this.context.timelineRenderingType
});
});
/**
* This acts as a fallback in-app navigation handler for any body links that
* were ignored as part of linkification because they were already links
* to start with (e.g. pills, links in the content).
*/
(0, _defineProperty2.default)(this, "onBodyLinkClick", e => {
let target = e.target;
// links processed by linkifyjs have their own handler so don't handle those here
if (target.classList.contains(_linkifyMatrix.options.className)) return;
if (target.nodeName !== "A") {
// Jump to parent as the `<a>` may contain children, e.g. an anchor wrapping an inline code section
target = target.closest("a");
}
if (!target) return;
const localHref = (0, _Permalinks.tryTransformPermalinkToLocalHref)(target.href);
if (localHref !== target.href) {
// it could be converted to a localHref -> therefore handle locally
e.preventDefault();
window.location.hash = localHref;
}
});
(0, _defineProperty2.default)(this, "getEventTileOps", () => ({
isWidgetHidden: () => {
return this.state.widgetHidden;
},
unhideWidget: () => {
this.setState({
widgetHidden: false
});
if (global.localStorage) {
global.localStorage.removeItem("hide_preview_" + this.props.mxEvent.getId());
}
}
}));
(0, _defineProperty2.default)(this, "onStarterLinkClick", (starterLink, ev) => {
ev.preventDefault();
// We need to add on our scalar token to the starter link, but we may not have one!
// In addition, we can't fetch one on click and then go to it immediately as that
// is then treated as a popup!
// We can get around this by fetching one now and showing a "confirmation dialog" (hurr hurr)
// which requires the user to click through and THEN we can open the link in a new tab because
// the window.open command occurs in the same stack frame as the onClick callback.
const managers = _IntegrationManagers.IntegrationManagers.sharedInstance();
if (!managers.hasManager()) {
managers.openNoManagerDialog();
return;
}
// Go fetch a scalar token
const integrationManager = managers.getPrimaryManager();
const scalarClient = integrationManager?.getScalarClient();
scalarClient?.connect().then(() => {
const completeUrl = scalarClient.getStarterLink(starterLink);
const integrationsUrl = integrationManager.uiUrl;
_Modal.default.createDialog(_QuestionDialog.default, {
title: (0, _languageHandler._t)("timeline|scalar_starter_link|dialog_title"),
description: /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("timeline|scalar_starter_link|dialog_description", {
integrationsUrl: integrationsUrl
})),
button: (0, _languageHandler._t)("action|continue"),
onFinished(confirmed) {
if (!confirmed) {
return;
}
const width = window.screen.width > 1024 ? 1024 : window.screen.width;
const height = window.screen.height > 800 ? 800 : window.screen.height;
const left = (window.screen.width - width) / 2;
const top = (window.screen.height - height) / 2;
const features = `height=${height}, width=${width}, top=${top}, left=${left},`;
const wnd = window.open(completeUrl, "_blank", features);
wnd.opener = null;
}
});
});
});
(0, _defineProperty2.default)(this, "openHistoryDialog", async () => {
_Modal.default.createDialog(_MessageEditHistoryDialog.default, {
mxEvent: this.props.mxEvent
});
});
}
componentDidMount() {
if (!this.props.editState) {
this.applyFormatting();
}
}
applyFormatting() {
// Function is only called from render / componentDidMount → contentRef is set
const content = this.contentRef.current;
const showLineNumbers = _SettingsStore.default.getValue("showCodeLineNumbers");
this.activateSpoilers([content]);
HtmlUtils.linkifyElement(content);
(0, _pillify.pillifyLinks)(_MatrixClientPeg.MatrixClientPeg.safeGet(), [content], this.props.mxEvent, this.pills);
this.calculateUrlPreview();
// tooltipifyLinks AFTER calculateUrlPreview because the DOM inside the tooltip
// container is empty before the internal component has mounted so calculateUrlPreview
// won't find any anchors
(0, _tooltipify.tooltipifyLinks)([content], this.pills, this.tooltips);
if (this.props.mxEvent.getContent().format === "org.matrix.custom.html") {
// Handle expansion and add buttons
const pres = _reactDom.default.findDOMNode(this).getElementsByTagName("pre");
if (pres.length > 0) {
for (let i = 0; i < pres.length; i++) {
// If there already is a div wrapping the codeblock we want to skip this.
// This happens after the codeblock was edited.
if (pres[i].parentElement?.className == "mx_EventTile_pre_container") continue;
// Add code element if it's missing since we depend on it
if (pres[i].getElementsByTagName("code").length == 0) {
this.addCodeElement(pres[i]);
}
// Wrap a div around <pre> so that the copy button can be correctly positioned
// when the <pre> overflows and is scrolled horizontally.
const div = this.wrapInDiv(pres[i]);
this.handleCodeBlockExpansion(pres[i]);
this.addCodeExpansionButton(div, pres[i]);
this.addCodeCopyButton(div);
if (showLineNumbers) {
this.addLineNumbers(pres[i]);
}
}
}
// Highlight code
const codes = _reactDom.default.findDOMNode(this).getElementsByTagName("code");
if (codes.length > 0) {
// Do this asynchronously: parsing code takes time and we don't
// need to block the DOM update on it.
window.setTimeout(() => {
if (this.unmounted) return;
for (let i = 0; i < codes.length; i++) {
this.highlightCode(codes[i]);
}
}, 10);
}
}
}
addCodeElement(pre) {
const code = document.createElement("code");
code.append(...pre.childNodes);
pre.appendChild(code);
}
addCodeExpansionButton(div, pre) {
// Calculate how many percent does the pre element take up.
// If it's less than 30% we don't add the expansion button.
// We also round the number as it sometimes can be 29.99...
const percentageOfViewport = Math.round(pre.offsetHeight / _UIStore.default.instance.windowHeight * 100);
// TODO: additionally show the button if it's an expanded quoted message
if (percentageOfViewport < 30) return;
const button = document.createElement("span");
button.className = "mx_EventTile_button ";
if (pre.className == "mx_EventTile_collapsedCodeBlock") {
button.className += "mx_EventTile_expandButton";
} else {
button.className += "mx_EventTile_collapseButton";
}
button.onclick = async () => {
button.className = "mx_EventTile_button ";
if (pre.className == "mx_EventTile_collapsedCodeBlock") {
pre.className = "";
button.className += "mx_EventTile_collapseButton";
} else {
pre.className = "mx_EventTile_collapsedCodeBlock";
button.className += "mx_EventTile_expandButton";
}
// By expanding/collapsing we changed
// the height, therefore we call this
this.props.onHeightChanged?.();
};
div.appendChild(button);
}
addCodeCopyButton(div) {
const button = document.createElement("span");
button.className = "mx_EventTile_button mx_EventTile_copyButton ";
// Check if expansion button exists. If so we put the copy button to the bottom
const expansionButtonExists = div.getElementsByClassName("mx_EventTile_button");
if (expansionButtonExists.length > 0) button.className += "mx_EventTile_buttonBottom";
button.onclick = async () => {
const copyCode = button.parentElement?.getElementsByTagName("code")[0];
const successful = copyCode?.textContent ? await (0, _strings.copyPlaintext)(copyCode.textContent) : false;
const buttonRect = button.getBoundingClientRect();
const {
close
} = ContextMenu.createMenu(_GenericTextContextMenu.default, _objectSpread(_objectSpread({}, (0, _ContextMenu.toRightOf)(buttonRect, 0)), {}, {
chevronFace: _ContextMenu.ChevronFace.None,
message: successful ? (0, _languageHandler._t)("common|copied") : (0, _languageHandler._t)("error|failed_copy")
}));
button.onmouseleave = close;
};
div.appendChild(button);
}
wrapInDiv(pre) {
const div = document.createElement("div");
div.className = "mx_EventTile_pre_container";
// Insert containing div in place of <pre> block
pre.parentNode?.replaceChild(div, pre);
// Append <pre> block and copy button to container
div.appendChild(pre);
return div;
}
handleCodeBlockExpansion(pre) {
if (!_SettingsStore.default.getValue("expandCodeByDefault")) {
pre.className = "mx_EventTile_collapsedCodeBlock";
}
}
addLineNumbers(pre) {
// Calculate number of lines in pre
const number = pre.innerHTML.replace(/\n(<\/code>)?$/, "").split(/\n/).length;
const lineNumbers = document.createElement("span");
lineNumbers.className = "mx_EventTile_lineNumbers";
// Iterate through lines starting with 1 (number of the first line is 1)
for (let i = 1; i <= number; i++) {
const s = document.createElement("span");
s.textContent = i.toString();
lineNumbers.appendChild(s);
}
pre.prepend(lineNumbers);
pre.append(document.createElement("span"));
}
async highlightCode(code) {
const {
default: highlight
} = await Promise.resolve().then(() => _interopRequireWildcard(require("highlight.js")));
if (code.textContent && code.textContent.length > MAX_HIGHLIGHT_LENGTH) {
console.log("Code block is bigger than highlight limit (" + code.textContent.length + " > " + MAX_HIGHLIGHT_LENGTH + "): not highlighting");
return;
}
let advertisedLang;
for (const cl of code.className.split(/\s+/)) {
if (cl.startsWith("language-")) {
const maybeLang = cl.split("-", 2)[1];
if (highlight.getLanguage(maybeLang)) {
advertisedLang = maybeLang;
break;
}
}
}
if (advertisedLang) {
// If the code says what language it is, highlight it in that language
// We don't use highlightElement here because we can't force language detection
// off. It should use the one we've found in the CSS class but we'd rather pass
// it in explicitly to make sure.
code.innerHTML = highlight.highlight(code.textContent ?? "", {
language: advertisedLang
}).value;
} else if (_SettingsStore.default.getValue("enableSyntaxHighlightLanguageDetection") && code.parentElement instanceof HTMLPreElement) {
// User has language detection enabled and the code is within a pre
// we only auto-highlight if the code block is in a pre), so highlight
// the block with auto-highlighting enabled.
// We pass highlightjs the text to highlight rather than letting it
// work on the DOM with highlightElement because that also adds CSS
// classes to the pre/code element that we don't want (the CSS
// conflicts with our own).
code.innerHTML = highlight.highlightAuto(code.textContent ?? "").value;
}
}
componentDidUpdate(prevProps) {
if (!this.props.editState) {
const stoppedEditing = prevProps.editState && !this.props.editState;
const messageWasEdited = prevProps.replacingEventId !== this.props.replacingEventId;
if (messageWasEdited || stoppedEditing) {
this.applyFormatting();
}
}
}
componentWillUnmount() {
this.unmounted = true;
(0, _pillify.unmountPills)(this.pills);
(0, _tooltipify.unmountTooltips)(this.tooltips);
this.pills = [];
this.tooltips = [];
}
shouldComponentUpdate(nextProps, nextState) {
//console.info("shouldComponentUpdate: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
// exploit that events are immutable :)
return nextProps.mxEvent.getId() !== this.props.mxEvent.getId() || nextProps.highlights !== this.props.highlights || nextProps.replacingEventId !== this.props.replacingEventId || nextProps.highlightLink !== this.props.highlightLink || nextProps.showUrlPreview !== this.props.showUrlPreview || nextProps.editState !== this.props.editState || nextState.links !== this.state.links || nextState.widgetHidden !== this.state.widgetHidden || nextProps.isSeeingThroughMessageHiddenForModeration !== this.props.isSeeingThroughMessageHiddenForModeration;
}
calculateUrlPreview() {
//console.info("calculateUrlPreview: ShowUrlPreview for %s is %s", this.props.mxEvent.getId(), this.props.showUrlPreview);
if (this.props.showUrlPreview && this.contentRef.current) {
// pass only the first child which is the event tile otherwise this recurses on edited events
let links = this.findLinks([this.contentRef.current]);
if (links.length) {
// de-duplicate the links using a set here maintains the order
links = Array.from(new Set(links));
this.setState({
links
});
// lazy-load the hidden state of the preview widget from localstorage
if (window.localStorage) {
const hidden = !!window.localStorage.getItem("hide_preview_" + this.props.mxEvent.getId());
this.setState({
widgetHidden: hidden
});
}
} else if (this.state.links.length) {
this.setState({
links: []
});
}
}
}
activateSpoilers(nodes) {
let node = nodes[0];
while (node) {
if (node.tagName === "SPAN" && typeof node.getAttribute("data-mx-spoiler") === "string") {
const spoilerContainer = document.createElement("span");
const reason = node.getAttribute("data-mx-spoiler") ?? undefined;
node.removeAttribute("data-mx-spoiler"); // we don't want to recurse
const spoiler = /*#__PURE__*/_react.default.createElement(_compoundWeb.TooltipProvider, null, /*#__PURE__*/_react.default.createElement(_Spoiler.default, {
reason: reason,
contentHtml: node.outerHTML
}));
_reactDom.default.render(spoiler, spoilerContainer);
node.parentNode?.replaceChild(spoilerContainer, node);
node = spoilerContainer;
}
if (node.childNodes && node.childNodes.length) {
this.activateSpoilers(node.childNodes);
}
node = node.nextSibling;
}
}
findLinks(nodes) {
let links = [];
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i];
if (node.tagName === "A" && node.getAttribute("href")) {
if (this.isLinkPreviewable(node)) {
links.push(node.getAttribute("href"));
}
} else if (node.tagName === "PRE" || node.tagName === "CODE" || node.tagName === "BLOCKQUOTE") {
continue;
} else if (node.children && node.children.length) {
links = links.concat(this.findLinks(node.children));
}
}
return links;
}
isLinkPreviewable(node) {
// don't try to preview relative links
const href = node.getAttribute("href") ?? "";
if (!href.startsWith("http://") && !href.startsWith("https://")) {
return false;
}
const url = node.getAttribute("href");
const host = url?.match(/^https?:\/\/(.*?)(\/|$)/)?.[1];
// never preview permalinks (if anything we should give a smart
// preview of the room/user they point to: nobody needs to be reminded
// what the matrix.to site looks like).
if (!host || (0, _Permalinks.isPermalinkHost)(host)) return false;
// as a random heuristic to avoid highlighting things like "foo.pl"
// we require the linked text to either include a / (either from http://
// or from a full foo.bar/baz style schemeless URL) - or be a markdown-style
// link, in which case we check the target text differs from the link value.
// TODO: make this configurable?
if (node.textContent?.includes("/")) {
return true;
}
if (node.textContent?.toLowerCase().trim().startsWith(host.toLowerCase())) {
// it's a "foo.pl" style link
return false;
} else {
// it's a [foo bar](http://foo.com) style link
return true;
}
}
renderEditedMarker() {
const date = this.props.mxEvent.replacingEventDate();
const dateString = date && (0, _DateUtils.formatDate)(date);
return /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
className: "mx_EventTile_edited",
onClick: this.openHistoryDialog,
"aria-label": (0, _languageHandler._t)("timeline|edits|tooltip_label", {
date: dateString
}),
title: (0, _languageHandler._t)("timeline|edits|tooltip_title", {
date: dateString
}),
caption: (0, _languageHandler._t)("timeline|edits|tooltip_sub")
}, /*#__PURE__*/_react.default.createElement("span", null, `(${(0, _languageHandler._t)("common|edited")})`));
}
/**
* Render a marker informing the user that, while they can see the message,
* it is hidden for other users.
*/
renderPendingModerationMarker() {
let text;
const visibility = this.props.mxEvent.messageVisibility();
switch (visibility.visible) {
case true:
throw new Error("renderPendingModerationMarker should only be applied to hidden messages");
case false:
if (visibility.reason) {
text = (0, _languageHandler._t)("timeline|pending_moderation_reason", {
reason: visibility.reason
});
} else {
text = (0, _languageHandler._t)("timeline|pending_moderation");
}
break;
}
return /*#__PURE__*/_react.default.createElement("span", {
className: "mx_EventTile_pendingModeration"
}, `(${text})`);
}
render() {
if (this.props.editState) {
const isWysiwygComposerEnabled = _SettingsStore.default.getValue("feature_wysiwyg_composer");
return isWysiwygComposerEnabled ? /*#__PURE__*/_react.default.createElement(_wysiwyg_composer.EditWysiwygComposer, {
editorStateTransfer: this.props.editState,
className: "mx_EventTile_content"
}) : /*#__PURE__*/_react.default.createElement(_EditMessageComposer.default, {
editState: this.props.editState,
className: "mx_EventTile_content"
});
}
const mxEvent = this.props.mxEvent;
const content = mxEvent.getContent();
const isNotice = content.msgtype === _matrix.MsgType.Notice;
const isEmote = content.msgtype === _matrix.MsgType.Emote;
const isCaption = [_matrix.MsgType.Image, _matrix.MsgType.File, _matrix.MsgType.Audio, _matrix.MsgType.Video].includes(content.msgtype);
const willHaveWrapper = this.props.replacingEventId || this.props.isSeeingThroughMessageHiddenForModeration || isEmote;
// only strip reply if this is the original replying event, edits thereafter do not have the fallback
const stripReply = !mxEvent.replacingEvent() && !!(0, _Reply.getParentEventId)(mxEvent);
const htmlOpts = {
disableBigEmoji: isEmote || !_SettingsStore.default.getValue("TextualBody.enableBigEmoji"),
// Part of Replies fallback support
stripReplyFallback: stripReply
};
let body = willHaveWrapper ? HtmlUtils.bodyToSpan(content, this.props.highlights, htmlOpts, this.contentRef, false) : HtmlUtils.bodyToDiv(content, this.props.highlights, htmlOpts, this.contentRef);
if (this.props.replacingEventId) {
body = /*#__PURE__*/_react.default.createElement("div", {
dir: "auto",
className: "mx_EventTile_annotated"
}, body, this.renderEditedMarker());
}
if (this.props.isSeeingThroughMessageHiddenForModeration) {
body = /*#__PURE__*/_react.default.createElement("div", {
dir: "auto",
className: "mx_EventTile_annotated"
}, body, this.renderPendingModerationMarker());
}
if (this.props.highlightLink) {
body = /*#__PURE__*/_react.default.createElement("a", {
href: this.props.highlightLink
}, body);
} else if (content.data && typeof content.data["org.matrix.neb.starter_link"] === "string") {
body = /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "link_inline",
onClick: this.onStarterLinkClick.bind(this, content.data["org.matrix.neb.starter_link"])
}, body);
}
let widgets;
if (this.state.links.length && !this.state.widgetHidden && this.props.showUrlPreview) {
widgets = /*#__PURE__*/_react.default.createElement(_LinkPreviewGroup.default, {
links: this.state.links,
mxEvent: this.props.mxEvent,
onCancelClick: this.onCancelClick,
onHeightChanged: this.props.onHeightChanged
});
}
if (isEmote) {
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MEmoteBody mx_EventTile_content",
onClick: this.onBodyLinkClick,
dir: "auto"
}, "*\xA0", /*#__PURE__*/_react.default.createElement("span", {
className: "mx_MEmoteBody_sender",
onClick: this.onEmoteSenderClick
}, mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender()), "\xA0", body, widgets);
}
if (isNotice) {
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MNoticeBody mx_EventTile_content",
onClick: this.onBodyLinkClick
}, body, widgets);
}
if (isCaption) {
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MTextBody mx_EventTile_caption",
onClick: this.onBodyLinkClick
}, body, widgets);
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_MTextBody mx_EventTile_content",
onClick: this.onBodyLinkClick
}, body, widgets);
}
}
exports.default = TextualBody;
(0, _defineProperty2.default)(TextualBody, "contextType", _RoomContext.default);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfcmVhY3REb20iLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiX21hdHJpeCIsIl9jb21wb3VuZFdlYiIsIkh0bWxVdGlscyIsIl9EYXRlVXRpbHMiLCJfTW9kYWwiLCJfZGlzcGF0Y2hlciIsIl9sYW5ndWFnZUhhbmRsZXIiLCJfQ29udGV4dE1lbnUiLCJDb250ZXh0TWVudSIsIl9TZXR0aW5nc1N0b3JlIiwiX3BpbGxpZnkiLCJfdG9vbHRpcGlmeSIsIl9JbnRlZ3JhdGlvbk1hbmFnZXJzIiwiX1Blcm1hbGlua3MiLCJfc3RyaW5ncyIsIl9VSVN0b3JlIiwiX2FjdGlvbnMiLCJfR2VuZXJpY1RleHRDb250ZXh0TWVudSIsIl9TcG9pbGVyIiwiX1F1ZXN0aW9uRGlhbG9nIiwiX01lc3NhZ2VFZGl0SGlzdG9yeURpYWxvZyIsIl9FZGl0TWVzc2FnZUNvbXBvc2VyIiwiX0xpbmtQcmV2aWV3R3JvdXAiLCJfUm9vbUNvbnRleHQiLCJfQWNjZXNzaWJsZUJ1dHRvbiIsIl9saW5raWZ5TWF0cml4IiwiX1JlcGx5IiwiX3d5c2l3eWdfY29tcG9zZXIiLCJfTWF0cml4Q2xpZW50UGVnIiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0Iiwib3duS2V5cyIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiTUFYX0hJR0hMSUdIVF9MRU5HVEgiLCJUZXh0dWFsQm9keSIsIlJlYWN0IiwiQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJhcmdzIiwiY3JlYXRlUmVmIiwibGlua3MiLCJ3aWRnZXRIaWRkZW4iLCJzZXRTdGF0ZSIsImdsb2JhbCIsImxvY2FsU3RvcmFnZSIsInNldEl0ZW0iLCJwcm9wcyIsIm14RXZlbnQiLCJnZXRJZCIsImZvcmNlVXBkYXRlIiwiZGlzIiwiZGlzcGF0Y2giLCJhY3Rpb24iLCJBY3Rpb24iLCJDb21wb3Nlckluc2VydCIsInVzZXJJZCIsImdldFNlbmRlciIsInRpbWVsaW5lUmVuZGVyaW5nVHlwZSIsImNvbnRleHQiLCJ0YXJnZXQiLCJjbGFzc0xpc3QiLCJjb250YWlucyIsImxpbmtpZnlPcHRzIiwiY2xhc3NOYW1lIiwibm9kZU5hbWUiLCJjbG9zZXN0IiwibG9jYWxIcmVmIiwidHJ5VHJhbnNmb3JtUGVybWFsaW5rVG9Mb2NhbEhyZWYiLCJocmVmIiwicHJldmVudERlZmF1bHQiLCJ3aW5kb3ciLCJsb2NhdGlvbiIsImhhc2giLCJpc1dpZGdldEhpZGRlbiIsInN0YXRlIiwidW5oaWRlV2lkZ2V0IiwicmVtb3ZlSXRlbSIsInN0YXJ0ZXJMaW5rIiwiZXYiLCJtYW5hZ2VycyIsIkludGVncmF0aW9uTWFuYWdlcnMiLCJzaGFyZWRJbnN0YW5jZSIsImhhc01hbmFnZXIiLCJvcGVuTm9NYW5hZ2VyRGlhbG9nIiwiaW50ZWdyYXRpb25NYW5hZ2VyIiwiZ2V0UHJpbWFyeU1hbmFnZXIiLCJzY2FsYXJDbGllbnQiLCJnZXRTY2FsYXJDbGllbnQiLCJjb25uZWN0IiwidGhlbiIsImNvbXBsZXRlVXJsIiwiZ2V0U3RhcnRlckxpbmsiLCJpbnRlZ3JhdGlvbnNVcmwiLCJ1aVVybCIsIk1vZGFsIiwiY3JlYXRlRGlhbG9nIiwiUXVlc3Rpb25EaWFsb2ciLCJ0aXRsZSIsIl90IiwiZGVzY3JpcHRpb24iLCJjcmVhdGVFbGVtZW50IiwiYnV0dG9uIiwib25GaW5pc2hlZCIsImNvbmZpcm1lZCIsIndpZHRoIiwic2NyZWVuIiwiaGVpZ2h0IiwibGVmdCIsInRvcCIsImZlYXR1cmVzIiwid25kIiwib3BlbiIsIm9wZW5lciIsIk1lc3NhZ2VFZGl0SGlzdG9yeURpYWxvZyIsImNvbXBvbmVudERpZE1vdW50IiwiZWRpdFN0YXRlIiwiYXBwbHlGb3JtYXR0aW5nIiwiY29udGVudCIsImNvbnRlbnRSZWYiLCJjdXJyZW50Iiwic2hvd0xpbmVOdW1iZXJzIiwiU2V0dGluZ3NTdG9yZSIsImdldFZhbHVlIiwiYWN0aXZhdGVTcG9pbGVycyIsImxpbmtpZnlFbGVtZW50IiwicGlsbGlmeUxpbmtzIiwiTWF0cml4Q2xpZW50UGVnIiwic2FmZUdldCIsInBpbGxzIiwiY2FsY3VsYXRlVXJsUHJldmlldyIsInRvb2x0aXBpZnlMaW5rcyIsInRvb2x0aXBzIiwiZ2V0Q29udGVudCIsImZvcm1hdCIsInByZXMiLCJSZWFjdERPTSIsImZpbmRET01Ob2RlIiwiZ2V0RWxlbWVudHNCeVRhZ05hbWUiLCJwYXJlbnRFbGVtZW50IiwiYWRkQ29kZUVsZW1lbnQiLCJkaXYiLCJ3cmFwSW5EaXYiLCJoYW5kbGVDb2RlQmxvY2tFeHBhbnNpb24iLCJhZGRDb2RlRXhwYW5zaW9uQnV0dG9uIiwiYWRkQ29kZUNvcHlCdXR0b24iLCJhZGRMaW5lTnVtYmVycyIsImNvZGVzIiwic2V0VGltZW91dCIsInVubW91bnRlZCIsImhpZ2hsaWdodENvZGUiLCJwcmUiLCJjb2RlIiwiZG9jdW1lbnQiLCJhcHBlbmQiLCJjaGlsZE5vZGVzIiwiYXBwZW5kQ2hpbGQiLCJwZXJjZW50YWdlT2ZWaWV3cG9ydCIsIk1hdGgiLCJyb3VuZCIsIm9mZnNldEhlaWdodCIsIlVJU3RvcmUiLCJpbnN0YW5jZSIsIndpbmRvd0hlaWdodCIsIm9uY2xpY2siLCJvbkhlaWdodENoYW5nZWQiLCJleHBhbnNpb25CdXR0b25FeGlzdHMiLCJnZXRFbGVtZW50c0J5Q2xhc3NOYW1lIiwiY29weUNvZGUiLCJzdWNjZXNzZnVsIiwidGV4dENvbnRlbnQiLCJjb3B5UGxhaW50ZXh0IiwiYnV0dG9uUmVjdCIsImdldEJvdW5kaW5nQ2xpZW50UmVjdCIsImNsb3NlIiwiY3JlYXRlTWVudSIsIkdlbmVyaWNUZXh0Q29udGV4dE1lbnUiLCJ0b1JpZ2h0T2YiLCJjaGV2cm9uRmFjZSIsIkNoZXZyb25GYWNlIiwiTm9uZSIsIm1lc3NhZ2UiLCJvbm1vdXNlbGVhdmUiLCJwYXJlbnROb2RlIiwicmVwbGFjZUNoaWxkIiwibnVtYmVyIiwiaW5uZXJIVE1MIiwicmVwbGFjZSIsInNwbGl0IiwibGluZU51bWJlcnMiLCJzIiwidG9TdHJpbmciLCJwcmVwZW5kIiwiaGlnaGxpZ2h0IiwiUHJvbWlzZSIsInJlc29sdmUiLCJjb25zb2xlIiwibG9nIiwiYWR2ZXJ0aXNlZExhbmciLCJjbCIsInN0YXJ0c1dpdGgiLCJtYXliZUxhbmciLCJnZXRMYW5ndWFnZSIsImxhbmd1YWdlIiwidmFsdWUiLCJIVE1MUHJlRWxlbWVudCIsImhpZ2hsaWdodEF1dG8iLCJjb21wb25lbnREaWRVcGRhdGUiLCJwcmV2UHJvcHMiLCJzdG9wcGVkRWRpdGluZyIsIm1lc3NhZ2VXYXNFZGl0ZWQiLCJyZXBsYWNpbmdFdmVudElkIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJ1bm1vdW50UGlsbHMiLCJ1bm1vdW50VG9vbHRpcHMiLCJzaG91bGRDb21wb25lbnRVcGRhdGUiLCJuZXh0UHJvcHMiLCJuZXh0U3RhdGUiLCJoaWdobGlnaHRzIiwiaGlnaGxpZ2h0TGluayIsInNob3dVcmxQcmV2aWV3IiwiaXNTZWVpbmdUaHJvdWdoTWVzc2FnZUhpZGRlbkZvck1vZGVyYXRpb24iLCJmaW5kTGlua3MiLCJBcnJheSIsImZyb20iLCJTZXQiLCJoaWRkZW4iLCJnZXRJdGVtIiwibm9kZXMiLCJub2RlIiwidGFnTmFtZSIsImdldEF0dHJpYnV0ZSIsInNwb2lsZXJDb250YWluZXIiLCJyZWFzb24iLCJ1bmRlZmluZWQiLCJyZW1vdmVBdHRyaWJ1dGUiLCJzcG9pbGVyIiwiVG9vbHRpcFByb3ZpZGVyIiwiY29udGVudEh0bWwiLCJvdXRlckhUTUwiLCJyZW5kZXIiLCJuZXh0U2libGluZyIsImlzTGlua1ByZXZpZXdhYmxlIiwiY2hpbGRyZW4iLCJjb25jYXQiLCJ1cmwiLCJob3N0IiwibWF0Y2giLCJpc1Blcm1hbGlua0hvc3QiLCJpbmNsdWRlcyIsInRvTG93ZXJDYXNlIiwidHJpbSIsInJlbmRlckVkaXRlZE1hcmtlciIsImRhdGUiLCJyZXBsYWNpbmdFdmVudERhdGUiLCJkYXRlU3RyaW5nIiwiZm9ybWF0RGF0ZSIsIm9uQ2xpY2siLCJvcGVuSGlzdG9yeURpYWxvZyIsImNhcHRpb24iLCJyZW5kZXJQZW5kaW5nTW9kZXJhdGlvbk1hcmtlciIsInRleHQiLCJ2aXNpYmlsaXR5IiwibWVzc2FnZVZpc2liaWxpdHkiLCJ2aXNpYmxlIiwiRXJyb3IiLCJpc1d5c2l3eWdDb21wb3NlckVuYWJsZWQiLCJFZGl0V3lzaXd5Z0NvbXBvc2VyIiwiZWRpdG9yU3RhdGVUcmFuc2ZlciIsImlzTm90aWNlIiwibXNndHlwZSIsIk1zZ1R5cGUiLCJOb3RpY2UiLCJpc0Vtb3RlIiwiRW1vdGUiLCJpc0NhcHRpb24iLCJJbWFnZSIsIkZpbGUiLCJBdWRpbyIsIlZpZGVvIiwid2lsbEhhdmVXcmFwcGVyIiwic3RyaXBSZXBseSIsInJlcGxhY2luZ0V2ZW50IiwiZ2V0UGFyZW50RXZlbnRJZCIsImh0bWxPcHRzIiwiZGlzYWJsZUJpZ0Vtb2ppIiwic3RyaXBSZXBseUZhbGxiYWNrIiwiYm9keSIsImJvZHlUb1NwYW4iLCJib2R5VG9EaXYiLCJkaXIiLCJkYXRhIiwia2luZCIsIm9uU3RhcnRlckxpbmtDbGljayIsImJpbmQiLCJ3aWRnZXRzIiwib25DYW5jZWxDbGljayIsIm9uQm9keUxpbmtDbGljayIsIm9uRW1vdGVTZW5kZXJDbGljayIsInNlbmRlciIsIm5hbWUiLCJleHBvcnRzIiwiUm9vbUNvbnRleHQiXSwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aWV3cy9tZXNzYWdlcy9UZXh0dWFsQm9keS50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMTUtMjAyMSBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgUmVhY3QsIHsgY3JlYXRlUmVmLCBTeW50aGV0aWNFdmVudCwgTW91c2VFdmVudCB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IFJlYWN0RE9NIGZyb20gXCJyZWFjdC1kb21cIjtcbmltcG9ydCB7IE1zZ1R5cGUgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbWF0cml4XCI7XG5pbXBvcnQgeyBUb29sdGlwUHJvdmlkZXIgfSBmcm9tIFwiQHZlY3Rvci1pbS9jb21wb3VuZC13ZWJcIjtcblxuaW1wb3J0ICogYXMgSHRtbFV0aWxzIGZyb20gXCIuLi8uLi8uLi9IdG1sVXRpbHNcIjtcbmltcG9ydCB7IGZvcm1hdERhdGUgfSBmcm9tIFwiLi4vLi4vLi4vRGF0ZVV0aWxzXCI7XG5pbXBvcnQgTW9kYWwgZnJvbSBcIi4uLy4uLy4uL01vZGFsXCI7XG5pbXBvcnQgZGlzIGZyb20gXCIuLi8uLi8uLi9kaXNwYXRjaGVyL2Rpc3BhdGNoZXJcIjtcbmltcG9ydCB7IF90IH0gZnJvbSBcIi4uLy4uLy4uL2xhbmd1YWdlSGFuZGxlclwiO1xuaW1wb3J0ICogYXMgQ29udGV4dE1lbnUgZnJvbSBcIi4uLy4uL3N0cnVjdHVyZXMvQ29udGV4dE1lbnVcIjtcbmltcG9ydCB7IENoZXZyb25GYWNlLCB0b1JpZ2h0T2YgfSBmcm9tIFwiLi4vLi4vc3RydWN0dXJlcy9Db250ZXh0TWVudVwiO1xuaW1wb3J0IFNldHRpbmdzU3RvcmUgZnJvbSBcIi4uLy4uLy4uL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCB7IHBpbGxpZnlMaW5rcywgdW5tb3VudFBpbGxzIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3BpbGxpZnlcIjtcbmltcG9ydCB7IHRvb2x0aXBpZnlMaW5rcywgdW5tb3VudFRvb2x0aXBzIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3Rvb2x0aXBpZnlcIjtcbmltcG9ydCB7IEludGVncmF0aW9uTWFuYWdlcnMgfSBmcm9tIFwiLi4vLi4vLi4vaW50ZWdyYXRpb25zL0ludGVncmF0aW9uTWFuYWdlcnNcIjtcbmltcG9ydCB7IGlzUGVybWFsaW5rSG9zdCwgdHJ5VHJhbnNmb3JtUGVybWFsaW5rVG9Mb2NhbEhyZWYgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvcGVybWFsaW5rcy9QZXJtYWxpbmtzXCI7XG5pbXBvcnQgeyBjb3B5UGxhaW50ZXh0IH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3N0cmluZ3NcIjtcbmltcG9ydCBVSVN0b3JlIGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvVUlTdG9yZVwiO1xuaW1wb3J0IHsgQWN0aW9uIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvYWN0aW9uc1wiO1xuaW1wb3J0IEdlbmVyaWNUZXh0Q29udGV4dE1lbnUgZnJvbSBcIi4uL2NvbnRleHRfbWVudXMvR2VuZXJpY1RleHRDb250ZXh0TWVudVwiO1xuaW1wb3J0IFNwb2lsZXIgZnJvbSBcIi4uL2VsZW1lbnRzL1Nwb2lsZXJcIjtcbmltcG9ydCBRdWVzdGlvbkRpYWxvZyBmcm9tIFwiLi4vZGlhbG9ncy9RdWVzdGlvbkRpYWxvZ1wiO1xuaW1wb3J0IE1lc3NhZ2VFZGl0SGlzdG9yeURpYWxvZyBmcm9tIFwiLi4vZGlhbG9ncy9NZXNzYWdlRWRpdEhpc3RvcnlEaWFsb2dcIjtcbmltcG9ydCBFZGl0TWVzc2FnZUNvbXBvc2VyIGZyb20gXCIuLi9yb29tcy9FZGl0TWVzc2FnZUNvbXBvc2VyXCI7XG5pbXBvcnQgTGlua1ByZXZpZXdHcm91cCBmcm9tIFwiLi4vcm9vbXMvTGlua1ByZXZpZXdHcm91cFwiO1xuaW1wb3J0IHsgSUJvZHlQcm9wcyB9IGZyb20gXCIuL0lCb2R5UHJvcHNcIjtcbmltcG9ydCBSb29tQ29udGV4dCBmcm9tIFwiLi4vLi4vLi4vY29udGV4dHMvUm9vbUNvbnRleHRcIjtcbmltcG9ydCBBY2Nlc3NpYmxlQnV0dG9uIGZyb20gXCIuLi9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgeyBvcHRpb25zIGFzIGxpbmtpZnlPcHRzIH0gZnJvbSBcIi4uLy4uLy4uL2xpbmtpZnktbWF0cml4XCI7XG5pbXBvcnQgeyBnZXRQYXJlbnRFdmVudElkIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL1JlcGx5XCI7XG5pbXBvcnQgeyBFZGl0V3lzaXd5Z0NvbXBvc2VyIH0gZnJvbSBcIi4uL3Jvb21zL3d5c2l3eWdfY29tcG9zZXJcIjtcbmltcG9ydCB7IElFdmVudFRpbGVPcHMgfSBmcm9tIFwiLi4vcm9vbXMvRXZlbnRUaWxlXCI7XG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tIFwiLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnXCI7XG5cbmNvbnN0IE1BWF9ISUdITElHSFRfTEVOR1RIID0gNDA5NjtcblxuaW50ZXJmYWNlIElTdGF0ZSB7XG4gICAgLy8gdGhlIFVSTHMgKGlmIGFueSkgdG8gYmUgcHJldmlld2VkIHdpdGggYSBMaW5rUHJldmlld1dpZGdldCBpbnNpZGUgdGhpcyBUZXh0dWFsQm9keS5cbiAgICBsaW5rczogc3RyaW5nW107XG5cbiAgICAvLyB0cmFjayB3aGV0aGVyIHRoZSBwcmV2aWV3IHdpZGdldCBpcyBoaWRkZW5cbiAgICB3aWRnZXRIaWRkZW46IGJvb2xlYW47XG59XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFRleHR1YWxCb2R5IGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50PElCb2R5UHJvcHMsIElTdGF0ZT4ge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgY29udGVudFJlZiA9IGNyZWF0ZVJlZjxIVE1MRGl2RWxlbWVudD4oKTtcblxuICAgIHByaXZhdGUgdW5tb3VudGVkID0gZmFsc2U7XG4gICAgcHJpdmF0ZSBwaWxsczogRWxlbWVudFtdID0gW107XG4gICAgcHJpdmF0ZSB0b29sdGlwczogRWxlbWVudFtdID0gW107XG5cbiAgICBwdWJsaWMgc3RhdGljIGNvbnRleHRUeXBlID0gUm9vbUNvbnRleHQ7XG4gICAgcHVibGljIGRlY2xhcmUgY29udGV4dDogUmVhY3QuQ29udGV4dFR5cGU8dHlwZW9mIFJvb21Db250ZXh0PjtcblxuICAgIHB1YmxpYyBzdGF0ZSA9IHtcbiAgICAgICAgbGlua3M6IFtdLFxuICAgICAgICB3aWRnZXRIaWRkZW46IGZhbHNlLFxuICAgIH07XG5cbiAgICBwdWJsaWMgY29tcG9uZW50RGlkTW91bnQoKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5wcm9wcy5lZGl0U3RhdGUpIHtcbiAgICAgICAgICAgIHRoaXMuYXBwbHlGb3JtYXR0aW5nKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIGFwcGx5Rm9ybWF0dGluZygpOiB2b2lkIHtcbiAgICAgICAgLy8gRnVuY3Rpb24gaXMgb25seSBjYWxsZWQgZnJvbSByZW5kZXIgLyBjb21wb25lbnREaWRNb3VudCDihpIgY29udGVudFJlZiBpcyBzZXRcbiAgICAgICAgY29uc3QgY29udGVudCA9IHRoaXMuY29udGVudFJlZi5jdXJyZW50ITtcblxuICAgICAgICBjb25zdCBzaG93TGluZU51bWJlcnMgPSBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwic2hvd0NvZGVMaW5lTnVtYmVyc1wiKTtcbiAgICAgICAgdGhpcy5hY3RpdmF0ZVNwb2lsZXJzKFtjb250ZW50XSk7XG5cbiAgICAgICAgSHRtbFV0aWxzLmxpbmtpZnlFbGVtZW50KGNvbnRlbnQpO1xuICAgICAgICBwaWxsaWZ5TGlua3MoTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKSwgW2NvbnRlbnRdLCB0aGlzLnByb3BzLm14RXZlbnQsIHRoaXMucGlsbHMpO1xuXG4gICAgICAgIHRoaXMuY2FsY3VsYXRlVXJsUHJldmlldygpO1xuXG4gICAgICAgIC8vIHRvb2x0aXBpZnlMaW5rcyBBRlRFUiBjYWxjdWxhdGVVcmxQcmV2aWV3IGJlY2F1c2UgdGhlIERPTSBpbnNpZGUgdGhlIHRvb2x0aXBcbiAgICAgICAgLy8gY29udGFpbmVyIGlzIGVtcHR5IGJlZm9yZSB0aGUgaW50ZXJuYWwgY29tcG9uZW50IGhhcyBtb3VudGVkIHNvIGNhbGN1bGF0ZVVybFByZXZpZXdcbiAgICAgICAgLy8gd29uJ3QgZmluZCBhbnkgYW5jaG9yc1xuICAgICAgICB0b29sdGlwaWZ5TGlua3MoW2NvbnRlbnRdLCB0aGlzLnBpbGxzLCB0aGlzLnRvb2x0aXBzKTtcblxuICAgICAgICBpZiAodGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQoKS5mb3JtYXQgPT09IFwib3JnLm1hdHJpeC5jdXN0b20uaHRtbFwiKSB7XG4gICAgICAgICAgICAvLyBIYW5kbGUgZXhwYW5zaW9uIGFuZCBhZGQgYnV0dG9uc1xuICAgICAgICAgICAgY29uc3QgcHJlcyA9IChSZWFjdERPTS5maW5kRE9NTm9kZSh0aGlzKSBhcyBFbGVtZW50KS5nZXRFbGVtZW50c0J5VGFnTmFtZShcInByZVwiKTtcbiAgICAgICAgICAgIGlmIChwcmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgICAgICBmb3IgKGxldCBpID0gMDsgaSA8IHByZXMubGVuZ3RoOyBpKyspIHtcbiAgICAgICAgICAgICAgICAgICAgLy8gSWYgdGhlcmUgYWxyZWFkeSBpcyBhIGRpdiB3cmFwcGluZyB0aGUgY29kZWJsb2NrIHdlIHdhbnQgdG8gc2tpcCB0aGlzLlxuICAgICAgICAgICAgICAgICAgICAvLyBUaGlzIGhhcHBlbnMgYWZ0ZXIgdGhlIGNvZGVibG9jayB3YXMgZWRpdGVkLlxuICAgICAgICAgICAgICAgICAgICBpZiAocHJlc1tpXS5wYXJlbnRFbGVtZW50Py5jbGFzc05hbWUgPT0gXCJteF9FdmVudFRpbGVfcHJlX2NvbnRhaW5lclwiKSBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgLy8gQWRkIGNvZGUgZWxlbWVudCBpZiBpdCdzIG1pc3Npbmcgc2luY2Ugd2UgZGVwZW5kIG9uIGl0XG4gICAgICAgICAgICAgICAgICAgIGlmIChwcmVzW2ldLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwiY29kZVwiKS5sZW5ndGggPT0gMCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRDb2RlRWxlbWVudChwcmVzW2ldKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAvLyBXcmFwIGEgZGl2IGFyb3VuZCA8cHJlPiBzbyB0aGF0IHRoZSBjb3B5IGJ1dHRvbiBjYW4gYmUgY29ycmVjdGx5IHBvc2l0aW9uZWRcbiAgICAgICAgICAgICAgICAgICAgLy8gd2hlbiB0aGUgPHByZT4gb3ZlcmZsb3dzIGFuZCBpcyBzY3JvbGxlZCBob3Jpem9udGFsbHkuXG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGRpdiA9IHRoaXMud3JhcEluRGl2KHByZXNbaV0pO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmhhbmRsZUNvZGVCbG9ja0V4cGFuc2lvbihwcmVzW2ldKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRDb2RlRXhwYW5zaW9uQnV0dG9uKGRpdiwgcHJlc1tpXSk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuYWRkQ29kZUNvcHlCdXR0b24oZGl2KTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNob3dMaW5lTnVtYmVycykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5hZGRMaW5lTnVtYmVycyhwcmVzW2ldKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIC8vIEhpZ2hsaWdodCBjb2RlXG4gICAgICAgICAgICBjb25zdCBjb2RlcyA9IChSZWFjdERPTS5maW5kRE9NTm9kZSh0aGlzKSBhcyBFbGVtZW50KS5nZXRFbGVtZW50c0J5VGFnTmFtZShcImNvZGVcIik7XG4gICAgICAgICAgICBpZiAoY29kZXMubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgICAgIC8vIERvIHRoaXMgYXN5bmNocm9ub3VzbHk6IHBhcnNpbmcgY29kZSB0YWtlcyB0aW1lIGFuZCB3ZSBkb24ndFxuICAgICAgICAgICAgICAgIC8vIG5lZWQgdG8gYmxvY2sgdGhlIERPTSB1cGRhdGUgb24gaXQuXG4gICAgICAgICAgICAgICAgd2luZG93LnNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICBpZiAodGhpcy51bm1vdW50ZWQpIHJldHVybjtcbiAgICAgICAgICAgICAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCBjb2Rlcy5sZW5ndGg7IGkrKykge1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5oaWdobGlnaHRDb2RlKGNvZGVzW2ldKTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0sIDEwKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYWRkQ29kZUVsZW1lbnQocHJlOiBIVE1MUHJlRWxlbWVudCk6IHZvaWQge1xuICAgICAgICBjb25zdCBjb2RlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImNvZGVcIik7XG4gICAgICAgIGNvZGUuYXBwZW5kKC4uLnByZS5jaGlsZE5vZGVzKTtcbiAgICAgICAgcHJlLmFwcGVuZENoaWxkKGNvZGUpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYWRkQ29kZUV4cGFuc2lvbkJ1dHRvbihkaXY6IEhUTUxEaXZFbGVtZW50LCBwcmU6IEhUTUxQcmVFbGVtZW50KTogdm9pZCB7XG4gICAgICAgIC8vIENhbGN1bGF0ZSBob3cgbWFueSBwZXJjZW50IGRvZXMgdGhlIHByZSBlbGVtZW50IHRha2UgdXAuXG4gICAgICAgIC8vIElmIGl0J3MgbGVzcyB0aGFuIDMwJSB3ZSBkb24ndCBhZGQgdGhlIGV4cGFuc2lvbiBidXR0b24uXG4gICAgICAgIC8vIFdlIGFsc28gcm91bmQgdGhlIG51bWJlciBhcyBpdCBzb21ldGltZXMgY2FuIGJlIDI5Ljk5Li4uXG4gICAgICAgIGNvbnN0IHBlcmNlbnRhZ2VPZlZpZXdwb3J0ID0gTWF0aC5yb3VuZCgocHJlLm9mZnNldEhlaWdodCAvIFVJU3RvcmUuaW5zdGFuY2Uud2luZG93SGVpZ2h0KSAqIDEwMCk7XG4gICAgICAgIC8vIFRPRE86IGFkZGl0aW9uYWxseSBzaG93IHRoZSBidXR0b24gaWYgaXQncyBhbiBleHBhbmRlZCBxdW90ZWQgbWVzc2FnZVxuICAgICAgICBpZiAocGVyY2VudGFnZU9mVmlld3BvcnQgPCAzMCkgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IGJ1dHRvbiA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIpO1xuICAgICAgICBidXR0b24uY2xhc3NOYW1lID0gXCJteF9FdmVudFRpbGVfYnV0dG9uIFwiO1xuICAgICAgICBpZiAocHJlLmNsYXNzTmFtZSA9PSBcIm14X0V2ZW50VGlsZV9jb2xsYXBzZWRDb2RlQmxvY2tcIikge1xuICAgICAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSArPSBcIm14X0V2ZW50VGlsZV9leHBhbmRCdXR0b25cIjtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGJ1dHRvbi5jbGFzc05hbWUgKz0gXCJteF9FdmVudFRpbGVfY29sbGFwc2VCdXR0b25cIjtcbiAgICAgICAgfVxuXG4gICAgICAgIGJ1dHRvbi5vbmNsaWNrID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSA9IFwibXhfRXZlbnRUaWxlX2J1dHRvbiBcIjtcbiAgICAgICAgICAgIGlmIChwcmUuY2xhc3NOYW1lID09IFwibXhfRXZlbnRUaWxlX2NvbGxhcHNlZENvZGVCbG9ja1wiKSB7XG4gICAgICAgICAgICAgICAgcHJlLmNsYXNzTmFtZSA9IFwiXCI7XG4gICAgICAgICAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSArPSBcIm14X0V2ZW50VGlsZV9jb2xsYXBzZUJ1dHRvblwiO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwcmUuY2xhc3NOYW1lID0gXCJteF9FdmVudFRpbGVfY29sbGFwc2VkQ29kZUJsb2NrXCI7XG4gICAgICAgICAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSArPSBcIm14X0V2ZW50VGlsZV9leHBhbmRCdXR0b25cIjtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gQnkgZXhwYW5kaW5nL2NvbGxhcHNpbmcgd2UgY2hhbmdlZFxuICAgICAgICAgICAgLy8gdGhlIGhlaWdodCwgdGhlcmVmb3JlIHdlIGNhbGwgdGhpc1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkhlaWdodENoYW5nZWQ/LigpO1xuICAgICAgICB9O1xuXG4gICAgICAgIGRpdi5hcHBlbmRDaGlsZChidXR0b24pO1xuICAgIH1cblxuICAgIHByaXZhdGUgYWRkQ29kZUNvcHlCdXR0b24oZGl2OiBIVE1MRGl2RWxlbWVudCk6IHZvaWQge1xuICAgICAgICBjb25zdCBidXR0b24gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3BhblwiKTtcbiAgICAgICAgYnV0dG9uLmNsYXNzTmFtZSA9IFwibXhfRXZlbnRUaWxlX2J1dHRvbiBteF9FdmVudFRpbGVfY29weUJ1dHRvbiBcIjtcblxuICAgICAgICAvLyBDaGVjayBpZiBleHBhbnNpb24gYnV0dG9uIGV4aXN0cy4gSWYgc28gd2UgcHV0IHRoZSBjb3B5IGJ1dHRvbiB0byB0aGUgYm90dG9tXG4gICAgICAgIGNvbnN0IGV4cGFuc2lvbkJ1dHRvbkV4aXN0cyA9IGRpdi5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKFwibXhfRXZlbnRUaWxlX2J1dHRvblwiKTtcbiAgICAgICAgaWYgKGV4cGFuc2lvbkJ1dHRvbkV4aXN0cy5sZW5ndGggPiAwKSBidXR0b24uY2xhc3NOYW1lICs9IFwibXhfRXZlbnRUaWxlX2J1dHRvbkJvdHRvbVwiO1xuXG4gICAgICAgIGJ1dHRvbi5vbmNsaWNrID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICAgICAgY29uc3QgY29weUNvZGUgPSBidXR0b24ucGFyZW50RWxlbWVudD8uZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJjb2RlXCIpWzBdO1xuICAgICAgICAgICAgY29uc3Qgc3VjY2Vzc2Z1bCA9IGNvcHlDb2RlPy50ZXh0Q29udGVudCA/IGF3YWl0IGNvcHlQbGFpbnRleHQoY29weUNvZGUudGV4dENvbnRlbnQpIDogZmFsc2U7XG5cbiAgICAgICAgICAgIGNvbnN0IGJ1dHRvblJlY3QgPSBidXR0b24uZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG4gICAgICAgICAgICBjb25zdCB7IGNsb3NlIH0gPSBDb250ZXh0TWVudS5jcmVhdGVNZW51KEdlbmVyaWNUZXh0Q29udGV4dE1lbnUsIHtcbiAgICAgICAgICAgICAgICAuLi50b1JpZ2h0T2YoYnV0dG9uUmVjdCwgMCksXG4gICAgICAgICAgICAgICAgY2hldnJvbkZhY2U6IENoZXZyb25GYWNlLk5vbmUsXG4gICAgICAgICAgICAgICAgbWVzc2FnZTogc3VjY2Vzc2Z1bCA/IF90KFwiY29tbW9ufGNvcGllZFwiKSA6IF90KFwiZXJyb3J8ZmFpbGVkX2NvcHlcIiksXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIGJ1dHRvbi5vbm1vdXNlbGVhdmUgPSBjbG9zZTtcbiAgICAgICAgfTtcblxuICAgICAgICBkaXYuYXBwZW5kQ2hpbGQoYnV0dG9uKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHdyYXBJbkRpdihwcmU6IEhUTUxQcmVFbGVtZW50KTogSFRNTERpdkVsZW1lbnQge1xuICAgICAgICBjb25zdCBkaXYgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgICAgICBkaXYuY2xhc3NOYW1lID0gXCJteF9FdmVudFRpbGVfcHJlX2NvbnRhaW5lclwiO1xuXG4gICAgICAgIC8vIEluc2VydCBjb250YWluaW5nIGRpdiBpbiBwbGFjZSBvZiA8cHJlPiBibG9ja1xuICAgICAgICBwcmUucGFyZW50Tm9kZT8ucmVwbGFjZUNoaWxkKGRpdiwgcHJlKTtcbiAgICAgICAgLy8gQXBwZW5kIDxwcmU+IGJsb2NrIGFuZCBjb3B5IGJ1dHRvbiB0byBjb250YWluZXJcbiAgICAgICAgZGl2LmFwcGVuZENoaWxkKHByZSk7XG5cbiAgICAgICAgcmV0dXJuIGRpdjtcbiAgICB9XG5cbiAgICBwcml2YXRlIGhhbmRsZUNvZGVCbG9ja0V4cGFuc2lvbihwcmU6IEhUTUxQcmVFbGVtZW50KTogdm9pZCB7XG4gICAgICAgIGlmICghU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImV4cGFuZENvZGVCeURlZmF1bHRcIikpIHtcbiAgICAgICAgICAgIHByZS5jbGFzc05hbWUgPSBcIm14X0V2ZW50VGlsZV9jb2xsYXBzZWRDb2RlQmxvY2tcIjtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYWRkTGluZU51bWJlcnMocHJlOiBIVE1MUHJlRWxlbWVudCk6IHZvaWQge1xuICAgICAgICAvLyBDYWxjdWxhdGUgbnVtYmVyIG9mIGxpbmVzIGluIHByZVxuICAgICAgICBjb25zdCBudW1iZXIgPSBwcmUuaW5uZXJIVE1MLnJlcGxhY2UoL1xcbig8XFwvY29kZT4pPyQvLCBcIlwiKS5zcGxpdCgvXFxuLykubGVuZ3RoO1xuICAgICAgICBjb25zdCBsaW5lTnVtYmVycyA9IGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJzcGFuXCIpO1xuICAgICAgICBsaW5lTnVtYmVycy5jbGFzc05hbWUgPSBcIm14X0V2ZW50VGlsZV9saW5lTnVtYmVyc1wiO1xuICAgICAgICAvLyBJdGVyYXRlIHRocm91Z2ggbGluZXMgc3RhcnRpbmcgd2l0aCAxIChudW1iZXIgb2YgdGhlIGZpcnN0IGxpbmUgaXMgMSlcbiAgICAgICAgZm9yIChsZXQgaSA9IDE7IGkgPD0gbnVtYmVyOyBpKyspIHtcbiAgICAgICAgICAgIGNvbnN0IHMgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3BhblwiKTtcbiAgICAgICAgICAgIHMudGV4dENvbnRlbnQgPSBpLnRvU3RyaW5nKCk7XG4gICAgICAgICAgICBsaW5lTnVtYmVycy5hcHBlbmRDaGlsZChzKTtcbiAgICAgICAgfVxuICAgICAgICBwcmUucHJlcGVuZChsaW5lTnVtYmVycyk7XG4gICAgICAgIHByZS5hcHBlbmQoZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIikpO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgaGlnaGxpZ2h0Q29kZShjb2RlOiBIVE1MRWxlbWVudCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCB7IGRlZmF1bHQ6IGhpZ2hsaWdodCB9ID0gYXdhaXQgaW1wb3J0KFwiaGlnaGxpZ2h0LmpzXCIpO1xuXG4gICAgICAgIGlmIChjb2RlLnRleHRDb250ZW50ICYmIGNvZGUudGV4dENvbnRlbnQubGVuZ3RoID4gTUFYX0hJR0hMSUdIVF9MRU5HVEgpIHtcbiAgICAgICAgICAgIGNvbnNvbGUubG9nKFxuICAgICAgICAgICAgICAgIFwiQ29kZSBibG9jayBpcyBiaWdnZXIgdGhhbiBoaWdobGlnaHQgbGltaXQgKFwiICtcbiAgICAgICAgICAgICAgICAgICAgY29kZS50ZXh0Q29udGVudC5sZW5ndGggK1xuICAgICAgICAgICAgICAgICAgICBcIiA+IFwiICtcbiAgICAgICAgICAgICAgICAgICAgTUFYX0hJR0hMSUdIVF9MRU5HVEggK1xuICAgICAgICAgICAgICAgICAgICBcIik6IG5vdCBoaWdobGlnaHRpbmdcIixcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgYWR2ZXJ0aXNlZExhbmc7XG4gICAgICAgIGZvciAoY29uc3QgY2wgb2YgY29kZS5jbGFzc05hbWUuc3BsaXQoL1xccysvKSkge1xuICAgICAgICAgICAgaWYgKGNsLnN0YXJ0c1dpdGgoXCJsYW5ndWFnZS1cIikpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBtYXliZUxhbmcgPSBjbC5zcGxpdChcIi1cIiwgMilbMV07XG4gICAgICAgICAgICAgICAgaWYgKGhpZ2hsaWdodC5nZXRMYW5ndWFnZShtYXliZUxhbmcpKSB7XG4gICAgICAgICAgICAgICAgICAgIGFkdmVydGlzZWRMYW5nID0gbWF5YmVMYW5nO1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoYWR2ZXJ0aXNlZExhbmcpIHtcbiAgICAgICAgICAgIC8vIElmIHRoZSBjb2RlIHNheXMgd2hhdCBsYW5ndWFnZSBpdCBpcywgaGlnaGxpZ2h0IGl0IGluIHRoYXQgbGFuZ3VhZ2VcbiAgICAgICAgICAgIC8vIFdlIGRvbid0IHVzZSBoaWdobGlnaHRFbGVtZW50IGhlcmUgYmVjYXVzZSB3ZSBjYW4ndCBmb3JjZSBsYW5ndWFnZSBkZXRlY3Rpb25cbiAgICAgICAgICAgIC8vIG9mZi4gSXQgc2hvdWxkIHVzZSB0aGUgb25lIHdlJ3ZlIGZvdW5kIGluIHRoZSBDU1MgY2xhc3MgYnV0IHdlJ2QgcmF0aGVyIHBhc3NcbiAgICAgICAgICAgIC8vIGl0IGluIGV4cGxpY2l0bHkgdG8gbWFrZSBzdXJlLlxuICAgICAgICAgICAgY29kZS5pbm5lckhUTUwgPSBoaWdobGlnaHQuaGlnaGxpZ2h0KGNvZGUudGV4dENvbnRlbnQgPz8gXCJcIiwgeyBsYW5ndWFnZTogYWR2ZXJ0aXNlZExhbmcgfSkudmFsdWU7XG4gICAgICAgIH0gZWxzZSBpZiAoXG4gICAgICAgICAgICBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwiZW5hYmxlU3ludGF4SGlnaGxpZ2h0TGFuZ3VhZ2VEZXRlY3Rpb25cIikgJiZcbiAgICAgICAgICAgIGNvZGUucGFyZW50RWxlbWVudCBpbnN0YW5jZW9mIEhUTUxQcmVFbGVtZW50XG4gICAgICAgICkge1xuICAgICAgICAgICAgLy8gVXNlciBoYXMgbGFuZ3VhZ2UgZGV0ZWN0aW9uIGVuYWJsZWQgYW5kIHRoZSBjb2RlIGlzIHdpdGhpbiBhIHByZVxuICAgICAgICAgICAgLy8gd2Ugb25seSBhdXRvLWhpZ2hsaWdodCBpZiB0aGUgY29kZSBibG9jayBpcyBpbiBhIHByZSksIHNvIGhpZ2hsaWdodFxuICAgICAgICAgICAgLy8gdGhlIGJsb2NrIHdpdGggYXV0by1oaWdobGlnaHRpbmcgZW5hYmxlZC5cbiAgICAgICAgICAgIC8vIFdlIHBhc3MgaGlnaGxpZ2h0anMgdGhlIHRleHQgdG8gaGlnaGxpZ2h0IHJhdGhlciB0aGFuIGxldHRpbmcgaXRcbiAgICAgICAgICAgIC8vIHdvcmsgb24gdGhlIERPTSB3aXRoIGhpZ2hsaWdodEVsZW1lbnQgYmVjYXVzZSB0aGF0IGFsc28gYWRkcyBDU1NcbiAgICAgICAgICAgIC8vIGNsYXNzZXMgdG8gdGhl