matrix-react-sdk
Version:
SDK for matrix.org using React
513 lines (505 loc) • 91.1 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _MatrixClientPeg = require("../../../MatrixClientPeg");
var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher"));
var _languageHandler = require("../../../languageHandler");
var _Modal = _interopRequireDefault(require("../../../Modal"));
var _Resend = _interopRequireDefault(require("../../../Resend"));
var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore"));
var _HtmlUtils = require("../../../HtmlUtils");
var _EventUtils = require("../../../utils/EventUtils");
var _IconizedContextMenu = _interopRequireWildcard(require("./IconizedContextMenu"));
var _actions = require("../../../dispatcher/actions");
var _strings = require("../../../utils/strings");
var _ContextMenu = _interopRequireWildcard(require("../../structures/ContextMenu"));
var _ReactionPicker = _interopRequireDefault(require("../emojipicker/ReactionPicker"));
var _ViewSource = _interopRequireDefault(require("../../structures/ViewSource"));
var _ConfirmRedactDialog = require("../dialogs/ConfirmRedactDialog");
var _ShareDialog = _interopRequireDefault(require("../dialogs/ShareDialog"));
var _RoomContext = _interopRequireWildcard(require("../../../contexts/RoomContext"));
var _EndPollDialog = _interopRequireDefault(require("../dialogs/EndPollDialog"));
var _MPollBody = require("../messages/MPollBody");
var _location = require("../../../utils/location");
var _getForwardableEvent = require("../../../events/forward/getForwardableEvent");
var _getShareableLocationEvent = require("../../../events/location/getShareableLocationEvent");
var _context = require("../right_panel/context");
var _PinningUtils = _interopRequireDefault(require("../../../utils/PinningUtils"));
var _PosthogTrackers = _interopRequireDefault(require("../../../PosthogTrackers.ts"));
const _excluded = ["mxEvent", "rightClick", "link", "eventTileOps", "reactions", "collapseReplyChain"];
/*
Copyright 2024 New Vector Ltd.
Copyright 2015-2023 The Matrix.org Foundation C.I.C.
Copyright 2021, 2022 Šimon Brandner <simon.bra.ag@gmail.com>
Copyright 2019 Michael Telatynski <7t3chguy@gmail.com>
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
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; }
const ReplyInThreadButton = ({
mxEvent,
closeMenu
}) => {
const context = (0, _react.useContext)(_context.CardContext);
const relationType = mxEvent?.getRelation()?.rel_type;
// Can't create a thread from an event with an existing relation
if (Boolean(relationType) && relationType !== _matrix.RelationType.Thread) return null;
const onClick = () => {
if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
_dispatcher.default.dispatch({
action: _actions.Action.ShowThread,
rootEvent: mxEvent.getThread().rootEvent,
initialEvent: mxEvent,
scroll_into_view: true,
highlighted: true,
push: context.isCard
});
} else {
_dispatcher.default.dispatch({
action: _actions.Action.ShowThread,
rootEvent: mxEvent,
push: context.isCard
});
}
closeMenu();
};
return /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconReplyInThread",
label: (0, _languageHandler._t)("action|reply_in_thread"),
onClick: onClick
});
};
class MessageContextMenu extends _react.default.Component {
// XXX Ref to a functional component
constructor(props, context) {
super(props, context);
(0, _defineProperty2.default)(this, "reactButtonRef", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "checkPermissions", () => {
const cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
const room = cli.getRoom(this.props.mxEvent.getRoomId());
// We explicitly decline to show the redact option on ACL events as it has a potential
// to obliterate the room - https://github.com/matrix-org/synapse/issues/4042
// Similarly for encryption events, since redacting them "breaks everything"
const canRedact = !!room?.currentState.maySendRedactionForEvent(this.props.mxEvent, cli.getSafeUserId()) && this.props.mxEvent.getType() !== _matrix.EventType.RoomServerAcl && this.props.mxEvent.getType() !== _matrix.EventType.RoomEncryption;
const canPin = _PinningUtils.default.canPin(cli, this.props.mxEvent) || _PinningUtils.default.canUnpin(cli, this.props.mxEvent);
this.setState({
canRedact,
canPin
});
});
(0, _defineProperty2.default)(this, "onResendReactionsClick", () => {
for (const reaction of this.getUnsentReactions()) {
_Resend.default.resend(_MatrixClientPeg.MatrixClientPeg.safeGet(), reaction);
}
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onJumpToRelatedEventClick", relatedEventId => {
_dispatcher.default.dispatch({
action: "view_room",
room_id: this.props.mxEvent.getRoomId(),
event_id: relatedEventId,
highlighted: true
});
});
(0, _defineProperty2.default)(this, "onReportEventClick", () => {
_dispatcher.default.dispatch({
action: _actions.Action.OpenReportEventDialog,
event: this.props.mxEvent
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onViewSourceClick", () => {
_Modal.default.createDialog(_ViewSource.default, {
mxEvent: this.props.mxEvent
}, "mx_Dialog_viewsource");
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onRedactClick", () => {
const {
mxEvent,
onCloseDialog
} = this.props;
(0, _ConfirmRedactDialog.createRedactEventDialog)({
mxEvent,
onCloseDialog
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onForwardClick", forwardableEvent => () => {
_dispatcher.default.dispatch({
action: _actions.Action.OpenForwardDialog,
event: forwardableEvent,
permalinkCreator: this.props.permalinkCreator
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onPinClick", isPinned => {
// Pin or unpin in background
_PinningUtils.default.pinOrUnpinEvent(_MatrixClientPeg.MatrixClientPeg.safeGet(), this.props.mxEvent);
_PosthogTrackers.default.trackPinUnpinMessage(isPinned ? "Pin" : "Unpin", "Timeline");
this.closeMenu();
});
(0, _defineProperty2.default)(this, "closeMenu", () => {
this.props.onFinished();
});
(0, _defineProperty2.default)(this, "onUnhidePreviewClick", () => {
this.props.eventTileOps?.unhideWidget();
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onShareClick", e => {
e.preventDefault();
_Modal.default.createDialog(_ShareDialog.default, {
target: this.props.mxEvent,
permalinkCreator: this.props.permalinkCreator
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onCopyLinkClick", e => {
e.preventDefault(); // So that we don't open the permalink
if (!this.props.link) return;
(0, _strings.copyPlaintext)(this.props.link);
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onCollapseReplyChainClick", () => {
this.props.collapseReplyChain?.();
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onCopyClick", () => {
(0, _strings.copyPlaintext)((0, _strings.getSelectedText)());
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onEditClick", () => {
(0, _EventUtils.editEvent)(_MatrixClientPeg.MatrixClientPeg.safeGet(), this.props.mxEvent, this.context.timelineRenderingType, this.props.getRelationsForEvent);
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onReplyClick", () => {
_dispatcher.default.dispatch({
action: "reply_to_event",
event: this.props.mxEvent,
context: this.context.timelineRenderingType
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onReactClick", () => {
this.setState({
reactionPickerDisplayed: true
});
});
(0, _defineProperty2.default)(this, "onCloseReactionPicker", () => {
this.setState({
reactionPickerDisplayed: false
});
this.closeMenu();
});
(0, _defineProperty2.default)(this, "onEndPollClick", () => {
const matrixClient = _MatrixClientPeg.MatrixClientPeg.safeGet();
_Modal.default.createDialog(_EndPollDialog.default, {
matrixClient,
event: this.props.mxEvent,
getRelationsForEvent: this.props.getRelationsForEvent
}, "mx_Dialog_endPoll");
this.closeMenu();
});
(0, _defineProperty2.default)(this, "viewInRoom", () => {
_dispatcher.default.dispatch({
action: _actions.Action.ViewRoom,
event_id: this.props.mxEvent.getId(),
highlighted: true,
room_id: this.props.mxEvent.getRoomId(),
metricsTrigger: undefined // room doesn't change
});
this.closeMenu();
});
this.state = {
canRedact: false,
canPin: false,
reactionPickerDisplayed: false
};
}
componentDidMount() {
_MatrixClientPeg.MatrixClientPeg.safeGet().on(_matrix.RoomMemberEvent.PowerLevel, this.checkPermissions);
// re-check the permissions on send progress (`maySendRedactionForEvent` only returns true for events that have
// been fully sent and echoed back, and we want to ensure the "Remove" option is added once that happens.)
this.props.mxEvent.on(_matrix.MatrixEventEvent.Status, this.checkPermissions);
this.checkPermissions();
}
componentWillUnmount() {
const cli = _MatrixClientPeg.MatrixClientPeg.get();
if (cli) {
cli.removeListener(_matrix.RoomMemberEvent.PowerLevel, this.checkPermissions);
}
this.props.mxEvent.removeListener(_matrix.MatrixEventEvent.Status, this.checkPermissions);
}
canEndPoll(mxEvent) {
return _matrix.M_POLL_START.matches(mxEvent.getType()) && this.state.canRedact && !(0, _MPollBody.isPollEnded)(mxEvent, _MatrixClientPeg.MatrixClientPeg.safeGet());
}
getReactions(filter) {
const cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
const room = cli.getRoom(this.props.mxEvent.getRoomId());
const eventId = this.props.mxEvent.getId();
return room?.getPendingEvents().filter(e => {
const relation = e.getRelation();
return relation?.rel_type === _matrix.RelationType.Annotation && relation.event_id === eventId && filter(e);
}) ?? [];
}
getUnsentReactions() {
return this.getReactions(e => e.status === _matrix.EventStatus.NOT_SENT);
}
render() {
const cli = _MatrixClientPeg.MatrixClientPeg.safeGet();
const me = cli.getUserId();
const _this$props = this.props,
{
mxEvent,
rightClick,
link,
eventTileOps,
reactions,
collapseReplyChain
} = _this$props,
other = (0, _objectWithoutProperties2.default)(_this$props, _excluded);
delete other.getRelationsForEvent;
delete other.permalinkCreator;
const eventStatus = mxEvent.status;
const unsentReactionsCount = this.getUnsentReactions().length;
const contentActionable = (0, _EventUtils.isContentActionable)(mxEvent);
const permalink = this.props.permalinkCreator?.forEvent(this.props.mxEvent.getId());
// status is SENT before remote-echo, null after
const isSent = !eventStatus || eventStatus === _matrix.EventStatus.SENT;
const {
timelineRenderingType,
canReact,
canSendMessages
} = this.context;
const isThread = timelineRenderingType === _RoomContext.TimelineRenderingType.Thread || timelineRenderingType === _RoomContext.TimelineRenderingType.ThreadsList;
const isThreadRootEvent = isThread && mxEvent?.getThread()?.rootEvent === mxEvent;
let resendReactionsButton;
if (!mxEvent.isRedacted() && unsentReactionsCount !== 0) {
resendReactionsButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconResend",
label: (0, _languageHandler._t)("timeline|context_menu|resent_unsent_reactions", {
unsentCount: unsentReactionsCount
}),
onClick: this.onResendReactionsClick
});
}
let redactButton;
if (isSent && this.state.canRedact) {
redactButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconRedact",
label: (0, _languageHandler._t)("action|remove"),
onClick: this.onRedactClick
});
}
let openInMapSiteButton;
const shareableLocationEvent = (0, _getShareableLocationEvent.getShareableLocationEvent)(mxEvent, cli);
if (shareableLocationEvent) {
const mapSiteLink = (0, _location.createMapSiteLinkFromEvent)(shareableLocationEvent);
openInMapSiteButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconOpenInMapSite",
onClick: null,
label: (0, _languageHandler._t)("timeline|context_menu|open_in_osm"),
element: "a",
href: mapSiteLink,
target: "_blank",
rel: "noreferrer noopener"
});
}
let forwardButton;
const forwardableEvent = (0, _getForwardableEvent.getForwardableEvent)(mxEvent, cli);
if (contentActionable && forwardableEvent) {
forwardButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconForward",
label: (0, _languageHandler._t)("action|forward"),
onClick: this.onForwardClick(forwardableEvent)
});
}
// This is specifically not behind the developerMode flag to give people insight into the Matrix
const viewSourceButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconSource",
label: (0, _languageHandler._t)("timeline|context_menu|view_source"),
onClick: this.onViewSourceClick
});
let unhidePreviewButton;
if (eventTileOps?.isWidgetHidden()) {
unhidePreviewButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconUnhidePreview",
label: (0, _languageHandler._t)("timeline|context_menu|show_url_preview"),
onClick: this.onUnhidePreviewClick
});
}
let permalinkButton;
if (permalink) {
permalinkButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconPermalink",
onClick: this.onShareClick,
label: (0, _languageHandler._t)("action|share"),
element: "a",
href: permalink,
target: "_blank",
rel: "noreferrer noopener"
});
}
let endPollButton;
if (this.canEndPoll(mxEvent)) {
endPollButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconEndPoll",
label: (0, _languageHandler._t)("poll|end_title"),
onClick: this.onEndPollClick
});
}
// Bridges can provide a 'external_url' to link back to the source.
let externalURLButton;
if (typeof mxEvent.getContent().external_url === "string" && (0, _HtmlUtils.isUrlPermitted)(mxEvent.getContent().external_url)) {
externalURLButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconLink",
onClick: this.closeMenu,
label: (0, _languageHandler._t)("timeline|context_menu|external_url"),
element: "a",
target: "_blank",
rel: "noreferrer noopener",
href: mxEvent.getContent().external_url
});
}
let collapseReplyChainButton;
if (collapseReplyChain) {
collapseReplyChainButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconCollapse",
label: (0, _languageHandler._t)("timeline|context_menu|collapse_reply_thread"),
onClick: this.onCollapseReplyChainClick
});
}
let jumpToRelatedEventButton;
const relatedEventId = mxEvent.relationEventId;
if (relatedEventId && _SettingsStore.default.getValue("developerMode")) {
jumpToRelatedEventButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_jumpToEvent",
label: (0, _languageHandler._t)("timeline|context_menu|view_related_event"),
onClick: () => this.onJumpToRelatedEventClick(relatedEventId)
});
}
let reportEventButton;
if (mxEvent.getSender() !== me) {
reportEventButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconReport",
label: (0, _languageHandler._t)("timeline|context_menu|report"),
onClick: this.onReportEventClick
});
}
let copyLinkButton;
if (link) {
copyLinkButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconCopy",
onClick: this.onCopyLinkClick,
label: (0, _languageHandler._t)("action|copy_link"),
element: "a",
href: link,
target: "_blank",
rel: "noreferrer noopener"
});
}
let copyButton;
if (rightClick && (0, _strings.getSelectedText)()) {
copyButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconCopy",
label: (0, _languageHandler._t)("action|copy"),
triggerOnMouseDown: true // We use onMouseDown so that the selection isn't cleared when we click
,
onClick: this.onCopyClick
});
}
let editButton;
if (rightClick && (0, _EventUtils.canEditContent)(cli, mxEvent)) {
editButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconEdit",
label: (0, _languageHandler._t)("action|edit"),
onClick: this.onEditClick
});
}
let replyButton;
if (rightClick && contentActionable && canSendMessages) {
replyButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconReply",
label: (0, _languageHandler._t)("action|reply"),
onClick: this.onReplyClick
});
}
let replyInThreadButton;
if (rightClick && contentActionable && canSendMessages && _matrix.Thread.hasServerSideSupport && timelineRenderingType !== _RoomContext.TimelineRenderingType.Thread) {
replyInThreadButton = /*#__PURE__*/_react.default.createElement(ReplyInThreadButton, {
mxEvent: mxEvent,
closeMenu: this.closeMenu
});
}
let reactButton;
if (rightClick && contentActionable && canReact) {
reactButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconReact",
label: (0, _languageHandler._t)("action|react"),
onClick: this.onReactClick,
inputRef: this.reactButtonRef
});
}
let pinButton;
if (rightClick && this.state.canPin) {
const isPinned = _PinningUtils.default.isPinned(_MatrixClientPeg.MatrixClientPeg.safeGet(), this.props.mxEvent);
pinButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: isPinned ? "mx_MessageContextMenu_iconUnpin" : "mx_MessageContextMenu_iconPin",
label: isPinned ? (0, _languageHandler._t)("action|unpin") : (0, _languageHandler._t)("action|pin"),
onClick: () => this.onPinClick(isPinned)
});
}
let viewInRoomButton;
if (isThreadRootEvent) {
viewInRoomButton = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOption, {
iconClassName: "mx_MessageContextMenu_iconViewInRoom",
label: (0, _languageHandler._t)("timeline|mab|view_in_room"),
onClick: this.viewInRoom
});
}
let nativeItemsList;
if (copyButton || copyLinkButton) {
nativeItemsList = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOptionList, null, copyButton, copyLinkButton);
}
let quickItemsList;
if (editButton || replyButton || reactButton || pinButton) {
quickItemsList = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOptionList, null, reactButton, replyButton, replyInThreadButton, editButton, pinButton);
}
const commonItemsList = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOptionList, null, viewInRoomButton, openInMapSiteButton, endPollButton, forwardButton, permalinkButton, reportEventButton, externalURLButton, jumpToRelatedEventButton, unhidePreviewButton, viewSourceButton, resendReactionsButton, collapseReplyChainButton);
let redactItemList;
if (redactButton) {
redactItemList = /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.IconizedContextMenuOptionList, {
red: true
}, redactButton);
}
let reactionPicker;
if (this.state.reactionPickerDisplayed) {
const buttonRect = this.reactButtonRef.current?.getBoundingClientRect();
reactionPicker = /*#__PURE__*/_react.default.createElement(_ContextMenu.default, (0, _extends2.default)({}, (0, _ContextMenu.toRightOf)(buttonRect), {
onFinished: this.closeMenu,
managed: false
}), /*#__PURE__*/_react.default.createElement(_ReactionPicker.default, {
mxEvent: mxEvent,
onFinished: this.onCloseReactionPicker,
reactions: reactions
}));
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_IconizedContextMenu.default, (0, _extends2.default)({}, other, {
className: "mx_MessageContextMenu",
compact: true,
"data-testid": "mx_MessageContextMenu"
}), nativeItemsList, quickItemsList, commonItemsList, redactItemList), reactionPicker);
}
}
exports.default = MessageContextMenu;
(0, _defineProperty2.default)(MessageContextMenu, "contextType", _RoomContext.default);
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfbWF0cml4IiwiX01hdHJpeENsaWVudFBlZyIsIl9kaXNwYXRjaGVyIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9sYW5ndWFnZUhhbmRsZXIiLCJfTW9kYWwiLCJfUmVzZW5kIiwiX1NldHRpbmdzU3RvcmUiLCJfSHRtbFV0aWxzIiwiX0V2ZW50VXRpbHMiLCJfSWNvbml6ZWRDb250ZXh0TWVudSIsIl9hY3Rpb25zIiwiX3N0cmluZ3MiLCJfQ29udGV4dE1lbnUiLCJfUmVhY3Rpb25QaWNrZXIiLCJfVmlld1NvdXJjZSIsIl9Db25maXJtUmVkYWN0RGlhbG9nIiwiX1NoYXJlRGlhbG9nIiwiX1Jvb21Db250ZXh0IiwiX0VuZFBvbGxEaWFsb2ciLCJfTVBvbGxCb2R5IiwiX2xvY2F0aW9uIiwiX2dldEZvcndhcmRhYmxlRXZlbnQiLCJfZ2V0U2hhcmVhYmxlTG9jYXRpb25FdmVudCIsIl9jb250ZXh0IiwiX1Bpbm5pbmdVdGlscyIsIl9Qb3N0aG9nVHJhY2tlcnMiLCJfZXhjbHVkZWQiLCJfZ2V0UmVxdWlyZVdpbGRjYXJkQ2FjaGUiLCJlIiwiV2Vha01hcCIsInIiLCJ0IiwiX19lc01vZHVsZSIsImRlZmF1bHQiLCJoYXMiLCJnZXQiLCJuIiwiX19wcm90b19fIiwiYSIsIk9iamVjdCIsImRlZmluZVByb3BlcnR5IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yIiwidSIsImhhc093blByb3BlcnR5IiwiY2FsbCIsImkiLCJzZXQiLCJSZXBseUluVGhyZWFkQnV0dG9uIiwibXhFdmVudCIsImNsb3NlTWVudSIsImNvbnRleHQiLCJ1c2VDb250ZXh0IiwiQ2FyZENvbnRleHQiLCJyZWxhdGlvblR5cGUiLCJnZXRSZWxhdGlvbiIsInJlbF90eXBlIiwiQm9vbGVhbiIsIlJlbGF0aW9uVHlwZSIsIlRocmVhZCIsIm9uQ2xpY2siLCJnZXRUaHJlYWQiLCJpc1RocmVhZFJvb3QiLCJkaXMiLCJkaXNwYXRjaCIsImFjdGlvbiIsIkFjdGlvbiIsIlNob3dUaHJlYWQiLCJyb290RXZlbnQiLCJpbml0aWFsRXZlbnQiLCJzY3JvbGxfaW50b192aWV3IiwiaGlnaGxpZ2h0ZWQiLCJwdXNoIiwiaXNDYXJkIiwiY3JlYXRlRWxlbWVudCIsIkljb25pemVkQ29udGV4dE1lbnVPcHRpb24iLCJpY29uQ2xhc3NOYW1lIiwibGFiZWwiLCJfdCIsIk1lc3NhZ2VDb250ZXh0TWVudSIsIlJlYWN0IiwiQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJjcmVhdGVSZWYiLCJjbGkiLCJNYXRyaXhDbGllbnRQZWciLCJzYWZlR2V0Iiwicm9vbSIsImdldFJvb20iLCJnZXRSb29tSWQiLCJjYW5SZWRhY3QiLCJjdXJyZW50U3RhdGUiLCJtYXlTZW5kUmVkYWN0aW9uRm9yRXZlbnQiLCJnZXRTYWZlVXNlcklkIiwiZ2V0VHlwZSIsIkV2ZW50VHlwZSIsIlJvb21TZXJ2ZXJBY2wiLCJSb29tRW5jcnlwdGlvbiIsImNhblBpbiIsIlBpbm5pbmdVdGlscyIsImNhblVucGluIiwic2V0U3RhdGUiLCJyZWFjdGlvbiIsImdldFVuc2VudFJlYWN0aW9ucyIsIlJlc2VuZCIsInJlc2VuZCIsInJlbGF0ZWRFdmVudElkIiwicm9vbV9pZCIsImV2ZW50X2lkIiwiT3BlblJlcG9ydEV2ZW50RGlhbG9nIiwiZXZlbnQiLCJNb2RhbCIsImNyZWF0ZURpYWxvZyIsIlZpZXdTb3VyY2UiLCJvbkNsb3NlRGlhbG9nIiwiY3JlYXRlUmVkYWN0RXZlbnREaWFsb2ciLCJmb3J3YXJkYWJsZUV2ZW50IiwiT3BlbkZvcndhcmREaWFsb2ciLCJwZXJtYWxpbmtDcmVhdG9yIiwiaXNQaW5uZWQiLCJwaW5PclVucGluRXZlbnQiLCJQb3N0aG9nVHJhY2tlcnMiLCJ0cmFja1BpblVucGluTWVzc2FnZSIsIm9uRmluaXNoZWQiLCJldmVudFRpbGVPcHMiLCJ1bmhpZGVXaWRnZXQiLCJwcmV2ZW50RGVmYXVsdCIsIlNoYXJlRGlhbG9nIiwidGFyZ2V0IiwibGluayIsImNvcHlQbGFpbnRleHQiLCJjb2xsYXBzZVJlcGx5Q2hhaW4iLCJnZXRTZWxlY3RlZFRleHQiLCJlZGl0RXZlbnQiLCJ0aW1lbGluZVJlbmRlcmluZ1R5cGUiLCJnZXRSZWxhdGlvbnNGb3JFdmVudCIsInJlYWN0aW9uUGlja2VyRGlzcGxheWVkIiwibWF0cml4Q2xpZW50IiwiRW5kUG9sbERpYWxvZyIsIlZpZXdSb29tIiwiZ2V0SWQiLCJtZXRyaWNzVHJpZ2dlciIsInVuZGVmaW5lZCIsInN0YXRlIiwiY29tcG9uZW50RGlkTW91bnQiLCJvbiIsIlJvb21NZW1iZXJFdmVudCIsIlBvd2VyTGV2ZWwiLCJjaGVja1Blcm1pc3Npb25zIiwiTWF0cml4RXZlbnRFdmVudCIsIlN0YXR1cyIsImNvbXBvbmVudFdpbGxVbm1vdW50IiwicmVtb3ZlTGlzdGVuZXIiLCJjYW5FbmRQb2xsIiwiTV9QT0xMX1NUQVJUIiwibWF0Y2hlcyIsImlzUG9sbEVuZGVkIiwiZ2V0UmVhY3Rpb25zIiwiZmlsdGVyIiwiZXZlbnRJZCIsImdldFBlbmRpbmdFdmVudHMiLCJyZWxhdGlvbiIsIkFubm90YXRpb24iLCJzdGF0dXMiLCJFdmVudFN0YXR1cyIsIk5PVF9TRU5UIiwicmVuZGVyIiwibWUiLCJnZXRVc2VySWQiLCJfdGhpcyRwcm9wcyIsInJpZ2h0Q2xpY2siLCJyZWFjdGlvbnMiLCJvdGhlciIsIl9vYmplY3RXaXRob3V0UHJvcGVydGllczIiLCJldmVudFN0YXR1cyIsInVuc2VudFJlYWN0aW9uc0NvdW50IiwibGVuZ3RoIiwiY29udGVudEFjdGlvbmFibGUiLCJpc0NvbnRlbnRBY3Rpb25hYmxlIiwicGVybWFsaW5rIiwiZm9yRXZlbnQiLCJpc1NlbnQiLCJTRU5UIiwiY2FuUmVhY3QiLCJjYW5TZW5kTWVzc2FnZXMiLCJpc1RocmVhZCIsIlRpbWVsaW5lUmVuZGVyaW5nVHlwZSIsIlRocmVhZHNMaXN0IiwiaXNUaHJlYWRSb290RXZlbnQiLCJyZXNlbmRSZWFjdGlvbnNCdXR0b24iLCJpc1JlZGFjdGVkIiwidW5zZW50Q291bnQiLCJvblJlc2VuZFJlYWN0aW9uc0NsaWNrIiwicmVkYWN0QnV0dG9uIiwib25SZWRhY3RDbGljayIsIm9wZW5Jbk1hcFNpdGVCdXR0b24iLCJzaGFyZWFibGVMb2NhdGlvbkV2ZW50IiwiZ2V0U2hhcmVhYmxlTG9jYXRpb25FdmVudCIsIm1hcFNpdGVMaW5rIiwiY3JlYXRlTWFwU2l0ZUxpbmtGcm9tRXZlbnQiLCJlbGVtZW50IiwiaHJlZiIsInJlbCIsImZvcndhcmRCdXR0b24iLCJnZXRGb3J3YXJkYWJsZUV2ZW50Iiwib25Gb3J3YXJkQ2xpY2siLCJ2aWV3U291cmNlQnV0dG9uIiwib25WaWV3U291cmNlQ2xpY2siLCJ1bmhpZGVQcmV2aWV3QnV0dG9uIiwiaXNXaWRnZXRIaWRkZW4iLCJvblVuaGlkZVByZXZpZXdDbGljayIsInBlcm1hbGlua0J1dHRvbiIsIm9uU2hhcmVDbGljayIsImVuZFBvbGxCdXR0b24iLCJvbkVuZFBvbGxDbGljayIsImV4dGVybmFsVVJMQnV0dG9uIiwiZ2V0Q29udGVudCIsImV4dGVybmFsX3VybCIsImlzVXJsUGVybWl0dGVkIiwiY29sbGFwc2VSZXBseUNoYWluQnV0dG9uIiwib25Db2xsYXBzZVJlcGx5Q2hhaW5DbGljayIsImp1bXBUb1JlbGF0ZWRFdmVudEJ1dHRvbiIsInJlbGF0aW9uRXZlbnRJZCIsIlNldHRpbmdzU3RvcmUiLCJnZXRWYWx1ZSIsIm9uSnVtcFRvUmVsYXRlZEV2ZW50Q2xpY2siLCJyZXBvcnRFdmVudEJ1dHRvbiIsImdldFNlbmRlciIsIm9uUmVwb3J0RXZlbnRDbGljayIsImNvcHlMaW5rQnV0dG9uIiwib25Db3B5TGlua0NsaWNrIiwiY29weUJ1dHRvbiIsInRyaWdnZXJPbk1vdXNlRG93biIsIm9uQ29weUNsaWNrIiwiZWRpdEJ1dHRvbiIsImNhbkVkaXRDb250ZW50Iiwib25FZGl0Q2xpY2siLCJyZXBseUJ1dHRvbiIsIm9uUmVwbHlDbGljayIsInJlcGx5SW5UaHJlYWRCdXR0b24iLCJoYXNTZXJ2ZXJTaWRlU3VwcG9ydCIsInJlYWN0QnV0dG9uIiwib25SZWFjdENsaWNrIiwiaW5wdXRSZWYiLCJyZWFjdEJ1dHRvblJlZiIsInBpbkJ1dHRvbiIsIm9uUGluQ2xpY2siLCJ2aWV3SW5Sb29tQnV0dG9uIiwidmlld0luUm9vbSIsIm5hdGl2ZUl0ZW1zTGlzdCIsIkljb25pemVkQ29udGV4dE1lbnVPcHRpb25MaXN0IiwicXVpY2tJdGVtc0xpc3QiLCJjb21tb25JdGVtc0xpc3QiLCJyZWRhY3RJdGVtTGlzdCIsInJlZCIsInJlYWN0aW9uUGlja2VyIiwiYnV0dG9uUmVjdCIsImN1cnJlbnQiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJfZXh0ZW5kczIiLCJ0b1JpZ2h0T2YiLCJtYW5hZ2VkIiwib25DbG9zZVJlYWN0aW9uUGlja2VyIiwiRnJhZ21lbnQiLCJjbGFzc05hbWUiLCJjb21wYWN0IiwiZXhwb3J0cyIsIlJvb21Db250ZXh0Il0sInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdmlld3MvY29udGV4dF9tZW51cy9NZXNzYWdlQ29udGV4dE1lbnUudHN4Il0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDE1LTIwMjMgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cbkNvcHlyaWdodCAyMDIxLCAyMDIyIMWgaW1vbiBCcmFuZG5lciA8c2ltb24uYnJhLmFnQGdtYWlsLmNvbT5cbkNvcHlyaWdodCAyMDE5IE1pY2hhZWwgVGVsYXR5bnNraSA8N3QzY2hndXlAZ21haWwuY29tPlxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgUmVhY3QsIHsgY3JlYXRlUmVmLCB1c2VDb250ZXh0IH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQge1xuICAgIEV2ZW50U3RhdHVzLFxuICAgIE1hdHJpeEV2ZW50LFxuICAgIE1hdHJpeEV2ZW50RXZlbnQsXG4gICAgUm9vbU1lbWJlckV2ZW50LFxuICAgIEV2ZW50VHlwZSxcbiAgICBSZWxhdGlvblR5cGUsXG4gICAgUmVsYXRpb25zLFxuICAgIFRocmVhZCxcbiAgICBNX1BPTExfU1RBUlQsXG59IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcblxuaW1wb3J0IHsgTWF0cml4Q2xpZW50UGVnIH0gZnJvbSBcIi4uLy4uLy4uL01hdHJpeENsaWVudFBlZ1wiO1xuaW1wb3J0IGRpcyBmcm9tIFwiLi4vLi4vLi4vZGlzcGF0Y2hlci9kaXNwYXRjaGVyXCI7XG5pbXBvcnQgeyBfdCB9IGZyb20gXCIuLi8uLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCBNb2RhbCBmcm9tIFwiLi4vLi4vLi4vTW9kYWxcIjtcbmltcG9ydCBSZXNlbmQgZnJvbSBcIi4uLy4uLy4uL1Jlc2VuZFwiO1xuaW1wb3J0IFNldHRpbmdzU3RvcmUgZnJvbSBcIi4uLy4uLy4uL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCB7IGlzVXJsUGVybWl0dGVkIH0gZnJvbSBcIi4uLy4uLy4uL0h0bWxVdGlsc1wiO1xuaW1wb3J0IHsgY2FuRWRpdENvbnRlbnQsIGVkaXRFdmVudCwgaXNDb250ZW50QWN0aW9uYWJsZSB9IGZyb20gXCIuLi8uLi8uLi91dGlscy9FdmVudFV0aWxzXCI7XG5pbXBvcnQgSWNvbml6ZWRDb250ZXh0TWVudSwgeyBJY29uaXplZENvbnRleHRNZW51T3B0aW9uLCBJY29uaXplZENvbnRleHRNZW51T3B0aW9uTGlzdCB9IGZyb20gXCIuL0ljb25pemVkQ29udGV4dE1lbnVcIjtcbmltcG9ydCB7IEFjdGlvbiB9IGZyb20gXCIuLi8uLi8uLi9kaXNwYXRjaGVyL2FjdGlvbnNcIjtcbmltcG9ydCB7IFJvb21QZXJtYWxpbmtDcmVhdG9yIH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3Blcm1hbGlua3MvUGVybWFsaW5rc1wiO1xuaW1wb3J0IHsgQnV0dG9uRXZlbnQgfSBmcm9tIFwiLi4vZWxlbWVudHMvQWNjZXNzaWJsZUJ1dHRvblwiO1xuaW1wb3J0IHsgY29weVBsYWludGV4dCwgZ2V0U2VsZWN0ZWRUZXh0IH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3N0cmluZ3NcIjtcbmltcG9ydCBDb250ZXh0TWVudSwgeyB0b1JpZ2h0T2YsIE1lbnVQcm9wcyB9IGZyb20gXCIuLi8uLi9zdHJ1Y3R1cmVzL0NvbnRleHRNZW51XCI7XG5pbXBvcnQgUmVhY3Rpb25QaWNrZXIgZnJvbSBcIi4uL2Vtb2ppcGlja2VyL1JlYWN0aW9uUGlja2VyXCI7XG5pbXBvcnQgVmlld1NvdXJjZSBmcm9tIFwiLi4vLi4vc3RydWN0dXJlcy9WaWV3U291cmNlXCI7XG5pbXBvcnQgeyBjcmVhdGVSZWRhY3RFdmVudERpYWxvZyB9IGZyb20gXCIuLi9kaWFsb2dzL0NvbmZpcm1SZWRhY3REaWFsb2dcIjtcbmltcG9ydCBTaGFyZURpYWxvZyBmcm9tIFwiLi4vZGlhbG9ncy9TaGFyZURpYWxvZ1wiO1xuaW1wb3J0IFJvb21Db250ZXh0LCB7IFRpbWVsaW5lUmVuZGVyaW5nVHlwZSB9IGZyb20gXCIuLi8uLi8uLi9jb250ZXh0cy9Sb29tQ29udGV4dFwiO1xuaW1wb3J0IEVuZFBvbGxEaWFsb2cgZnJvbSBcIi4uL2RpYWxvZ3MvRW5kUG9sbERpYWxvZ1wiO1xuaW1wb3J0IHsgaXNQb2xsRW5kZWQgfSBmcm9tIFwiLi4vbWVzc2FnZXMvTVBvbGxCb2R5XCI7XG5pbXBvcnQgeyBWaWV3Um9vbVBheWxvYWQgfSBmcm9tIFwiLi4vLi4vLi4vZGlzcGF0Y2hlci9wYXlsb2Fkcy9WaWV3Um9vbVBheWxvYWRcIjtcbmltcG9ydCB7IEdldFJlbGF0aW9uc0ZvckV2ZW50LCBJRXZlbnRUaWxlT3BzIH0gZnJvbSBcIi4uL3Jvb21zL0V2ZW50VGlsZVwiO1xuaW1wb3J0IHsgT3BlbkZvcndhcmREaWFsb2dQYXlsb2FkIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvcGF5bG9hZHMvT3BlbkZvcndhcmREaWFsb2dQYXlsb2FkXCI7XG5pbXBvcnQgeyBPcGVuUmVwb3J0RXZlbnREaWFsb2dQYXlsb2FkIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvcGF5bG9hZHMvT3BlblJlcG9ydEV2ZW50RGlhbG9nUGF5bG9hZFwiO1xuaW1wb3J0IHsgY3JlYXRlTWFwU2l0ZUxpbmtGcm9tRXZlbnQgfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvbG9jYXRpb25cIjtcbmltcG9ydCB7IGdldEZvcndhcmRhYmxlRXZlbnQgfSBmcm9tIFwiLi4vLi4vLi4vZXZlbnRzL2ZvcndhcmQvZ2V0Rm9yd2FyZGFibGVFdmVudFwiO1xuaW1wb3J0IHsgZ2V0U2hhcmVhYmxlTG9jYXRpb25FdmVudCB9IGZyb20gXCIuLi8uLi8uLi9ldmVudHMvbG9jYXRpb24vZ2V0U2hhcmVhYmxlTG9jYXRpb25FdmVudFwiO1xuaW1wb3J0IHsgU2hvd1RocmVhZFBheWxvYWQgfSBmcm9tIFwiLi4vLi4vLi4vZGlzcGF0Y2hlci9wYXlsb2Fkcy9TaG93VGhyZWFkUGF5bG9hZFwiO1xuaW1wb3J0IHsgQ2FyZENvbnRleHQgfSBmcm9tIFwiLi4vcmlnaHRfcGFuZWwvY29udGV4dFwiO1xuaW1wb3J0IFBpbm5pbmdVdGlscyBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvUGlubmluZ1V0aWxzXCI7XG5pbXBvcnQgUG9zdGhvZ1RyYWNrZXJzIGZyb20gXCIuLi8uLi8uLi9Qb3N0aG9nVHJhY2tlcnMudHNcIjtcblxuaW50ZXJmYWNlIElSZXBseUluVGhyZWFkQnV0dG9uIHtcbiAgICBteEV2ZW50OiBNYXRyaXhFdmVudDtcbiAgICBjbG9zZU1lbnU6ICgpID0+IHZvaWQ7XG59XG5cbmNvbnN0IFJlcGx5SW5UaHJlYWRCdXR0b246IFJlYWN0LkZDPElSZXBseUluVGhyZWFkQnV0dG9uPiA9ICh7IG14RXZlbnQsIGNsb3NlTWVudSB9KSA9PiB7XG4gICAgY29uc3QgY29udGV4dCA9IHVzZUNvbnRleHQoQ2FyZENvbnRleHQpO1xuICAgIGNvbnN0IHJlbGF0aW9uVHlwZSA9IG14RXZlbnQ/LmdldFJlbGF0aW9uKCk/LnJlbF90eXBlO1xuXG4gICAgLy8gQ2FuJ3QgY3JlYXRlIGEgdGhyZWFkIGZyb20gYW4gZXZlbnQgd2l0aCBhbiBleGlzdGluZyByZWxhdGlvblxuICAgIGlmIChCb29sZWFuKHJlbGF0aW9uVHlwZSkgJiYgcmVsYXRpb25UeXBlICE9PSBSZWxhdGlvblR5cGUuVGhyZWFkKSByZXR1cm4gbnVsbDtcblxuICAgIGNvbnN0IG9uQ2xpY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGlmIChteEV2ZW50LmdldFRocmVhZCgpICYmICFteEV2ZW50LmlzVGhyZWFkUm9vdCkge1xuICAgICAgICAgICAgZGlzLmRpc3BhdGNoPFNob3dUaHJlYWRQYXlsb2FkPih7XG4gICAgICAgICAgICAgICAgYWN0aW9uOiBBY3Rpb24uU2hvd1RocmVhZCxcbiAgICAgICAgICAgICAgICByb290RXZlbnQ6IG14RXZlbnQuZ2V0VGhyZWFkKCkhLnJvb3RFdmVudCEsXG4gICAgICAgICAgICAgICAgaW5pdGlhbEV2ZW50OiBteEV2ZW50LFxuICAgICAgICAgICAgICAgIHNjcm9sbF9pbnRvX3ZpZXc6IHRydWUsXG4gICAgICAgICAgICAgICAgaGlnaGxpZ2h0ZWQ6IHRydWUsXG4gICAgICAgICAgICAgICAgcHVzaDogY29udGV4dC5pc0NhcmQsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRpcy5kaXNwYXRjaDxTaG93VGhyZWFkUGF5bG9hZD4oe1xuICAgICAgICAgICAgICAgIGFjdGlvbjogQWN0aW9uLlNob3dUaHJlYWQsXG4gICAgICAgICAgICAgICAgcm9vdEV2ZW50OiBteEV2ZW50LFxuICAgICAgICAgICAgICAgIHB1c2g6IGNvbnRleHQuaXNDYXJkLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICAgICAgY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHJldHVybiAoXG4gICAgICAgIDxJY29uaXplZENvbnRleHRNZW51T3B0aW9uXG4gICAgICAgICAgICBpY29uQ2xhc3NOYW1lPVwibXhfTWVzc2FnZUNvbnRleHRNZW51X2ljb25SZXBseUluVGhyZWFkXCJcbiAgICAgICAgICAgIGxhYmVsPXtfdChcImFjdGlvbnxyZXBseV9pbl90aHJlYWRcIil9XG4gICAgICAgICAgICBvbkNsaWNrPXtvbkNsaWNrfVxuICAgICAgICAvPlxuICAgICk7XG59O1xuXG5pbnRlcmZhY2UgSVByb3BzIGV4dGVuZHMgTWVudVByb3BzIHtcbiAgICAvKiB0aGUgTWF0cml4RXZlbnQgYXNzb2NpYXRlZCB3aXRoIHRoZSBjb250ZXh0IG1lbnUgKi9cbiAgICBteEV2ZW50OiBNYXRyaXhFdmVudDtcbiAgICAvLyBBbiBvcHRpb25hbCBFdmVudFRpbGVPcHMgaW1wbGVtZW50YXRpb24gdGhhdCBjYW4gYmUgdXNlZCB0byB1bmhpZGUgcHJldmlldyB3aWRnZXRzXG4gICAgZXZlbnRUaWxlT3BzPzogSUV2ZW50VGlsZU9wcztcbiAgICAvLyBDYWxsYmFjayBjYWxsZWQgd2hlbiB0aGUgbWVudSBpcyBkaXNtaXNzZWRcbiAgICBwZXJtYWxpbmtDcmVhdG9yPzogUm9vbVBlcm1hbGlua0NyZWF0b3I7XG4gICAgLyogYW4gb3B0aW9uYWwgZnVuY3Rpb24gdG8gYmUgY2FsbGVkIHdoZW4gdGhlIHVzZXIgY2xpY2tzIGNvbGxhcHNlIHRocmVhZCwgaWYgbm90IHByb3ZpZGVkIGhpZGUgYnV0dG9uICovXG4gICAgY29sbGFwc2VSZXBseUNoYWluPygpOiB2b2lkO1xuICAgIC8qIGNhbGxiYWNrIGNhbGxlZCB3aGVuIHRoZSBtZW51IGlzIGRpc21pc3NlZCAqL1xuICAgIG9uRmluaXNoZWQoKTogdm9pZDtcbiAgICAvLyBJZiB0aGUgbWVudSBpcyBpbnNpZGUgYSBkaWFsb2csIHdlIHNvbWV0aW1lcyBuZWVkIHRvIGNsb3NlIHRoYXQgZGlhbG9nIGFmdGVyIGNsaWNrIChmb3J3YXJkaW5nKVxuICAgIG9uQ2xvc2VEaWFsb2c/KCk6IHZvaWQ7XG4gICAgLy8gVHJ1ZSBpZiB0aGUgbWVudSBpcyBiZWluZyB1c2VkIGFzIGEgcmlnaHQgY2xpY2sgbWVudVxuICAgIHJpZ2h0Q2xpY2s/OiBib29sZWFuO1xuICAgIC8vIFRoZSBSZWxhdGlvbnMgbW9kZWwgZnJvbSB0aGUgSlMgU0RLIGZvciByZWFjdGlvbnMgdG8gYG14RXZlbnRgXG4gICAgcmVhY3Rpb25zPzogUmVsYXRpb25zIHwgbnVsbDtcbiAgICAvLyBBIHBlcm1hbGluayB0byB0aGlzIGV2ZW50IG9yIGFuIGhyZWYgb2YgYW4gYW5jaG9yIGVsZW1lbnQgdGhlIHVzZXIgaGFzIGNsaWNrZWRcbiAgICBsaW5rPzogc3RyaW5nO1xuXG4gICAgZ2V0UmVsYXRpb25zRm9yRXZlbnQ/OiBHZXRSZWxhdGlvbnNGb3JFdmVudDtcbn1cblxuaW50ZXJmYWNlIElTdGF0ZSB7XG4gICAgY2FuUmVkYWN0OiBib29sZWFuO1xuICAgIGNhblBpbjogYm9vbGVhbjtcbiAgICByZWFjdGlvblBpY2tlckRpc3BsYXllZDogYm9vbGVhbjtcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgTWVzc2FnZUNvbnRleHRNZW51IGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50PElQcm9wcywgSVN0YXRlPiB7XG4gICAgcHVibGljIHN0YXRpYyBjb250ZXh0VHlwZSA9IFJvb21Db250ZXh0O1xuICAgIHB1YmxpYyBkZWNsYXJlIGNvbnRleHQ6IFJlYWN0LkNvbnRleHRUeXBlPHR5cGVvZiBSb29tQ29udGV4dD47XG5cbiAgICBwcml2YXRlIHJlYWN0QnV0dG9uUmVmID0gY3JlYXRlUmVmPGFueT4oKTsgLy8gWFhYIFJlZiB0byBhIGZ1bmN0aW9uYWwgY29tcG9uZW50XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IocHJvcHM6IElQcm9wcywgY29udGV4dDogUmVhY3QuQ29udGV4dFR5cGU8dHlwZW9mIFJvb21Db250ZXh0Pikge1xuICAgICAgICBzdXBlcihwcm9wcywgY29udGV4dCk7XG5cbiAgICAgICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgICAgICAgIGNhblJlZGFjdDogZmFsc2UsXG4gICAgICAgICAgICBjYW5QaW46IGZhbHNlLFxuICAgICAgICAgICAgcmVhY3Rpb25QaWNrZXJEaXNwbGF5ZWQ6IGZhbHNlLFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHB1YmxpYyBjb21wb25lbnREaWRNb3VudCgpOiB2b2lkIHtcbiAgICAgICAgTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKS5vbihSb29tTWVtYmVyRXZlbnQuUG93ZXJMZXZlbCwgdGhpcy5jaGVja1Blcm1pc3Npb25zKTtcblxuICAgICAgICAvLyByZS1jaGVjayB0aGUgcGVybWlzc2lvbnMgb24gc2VuZCBwcm9ncmVzcyAoYG1heVNlbmRSZWRhY3Rpb25Gb3JFdmVudGAgb25seSByZXR1cm5zIHRydWUgZm9yIGV2ZW50cyB0aGF0IGhhdmVcbiAgICAgICAgLy8gYmVlbiBmdWxseSBzZW50IGFuZCBlY2hvZWQgYmFjaywgYW5kIHdlIHdhbnQgdG8gZW5zdXJlIHRoZSBcIlJlbW92ZVwiIG9wdGlvbiBpcyBhZGRlZCBvbmNlIHRoYXQgaGFwcGVucy4pXG4gICAgICAgIHRoaXMucHJvcHMubXhFdmVudC5vbihNYXRyaXhFdmVudEV2ZW50LlN0YXR1cywgdGhpcy5jaGVja1Blcm1pc3Npb25zKTtcblxuICAgICAgICB0aGlzLmNoZWNrUGVybWlzc2lvbnMoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgY29tcG9uZW50V2lsbFVubW91bnQoKTogdm9pZCB7XG4gICAgICAgIGNvbnN0IGNsaSA9IE1hdHJpeENsaWVudFBlZy5nZXQoKTtcbiAgICAgICAgaWYgKGNsaSkge1xuICAgICAgICAgICAgY2xpLnJlbW92ZUxpc3RlbmVyKFJvb21NZW1iZXJFdmVudC5Qb3dlckxldmVsLCB0aGlzLmNoZWNrUGVybWlzc2lvbnMpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMucHJvcHMubXhFdmVudC5yZW1vdmVMaXN0ZW5lcihNYXRyaXhFdmVudEV2ZW50LlN0YXR1cywgdGhpcy5jaGVja1Blcm1pc3Npb25zKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNoZWNrUGVybWlzc2lvbnMgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGNvbnN0IGNsaSA9IE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCk7XG4gICAgICAgIGNvbnN0IHJvb20gPSBjbGkuZ2V0Um9vbSh0aGlzLnByb3BzLm14RXZlbnQuZ2V0Um9vbUlkKCkpO1xuXG4gICAgICAgIC8vIFdlIGV4cGxpY2l0bHkgZGVjbGluZSB0byBzaG93IHRoZSByZWRhY3Qgb3B0aW9uIG9uIEFDTCBldmVudHMgYXMgaXQgaGFzIGEgcG90ZW50aWFsXG4gICAgICAgIC8vIHRvIG9ibGl0ZXJhdGUgdGhlIHJvb20gLSBodHRwczovL2dpdGh1Yi5jb20vbWF0cml4LW9yZy9zeW5hcHNlL2lzc3Vlcy80MDQyXG4gICAgICAgIC8vIFNpbWlsYXJseSBmb3IgZW5jcnlwdGlvbiBldmVudHMsIHNpbmNlIHJlZGFjdGluZyB0aGVtIFwiYnJlYWtzIGV2ZXJ5dGhpbmdcIlxuICAgICAgICBjb25zdCBjYW5SZWRhY3QgPVxuICAgICAgICAgICAgISFyb29tPy5jdXJyZW50U3RhdGUubWF5U2VuZFJlZGFjdGlvbkZvckV2ZW50KHRoaXMucHJvcHMubXhFdmVudCwgY2xpLmdldFNhZmVVc2VySWQoKSkgJiZcbiAgICAgICAgICAgIHRoaXMucHJvcHMubXhFdmVudC5nZXRUeXBlKCkgIT09IEV2ZW50VHlwZS5Sb29tU2VydmVyQWNsICYmXG4gICAgICAgICAgICB0aGlzLnByb3BzLm14RXZlbnQuZ2V0VHlwZSgpICE9PSBFdmVudFR5cGUuUm9vbUVuY3J5cHRpb247XG5cbiAgICAgICAgY29uc3QgY2FuUGluID0gUGlubmluZ1V0aWxzLmNhblBpbihjbGksIHRoaXMucHJvcHMubXhFdmVudCkgfHwgUGlubmluZ1V0aWxzLmNhblVucGluKGNsaSwgdGhpcy5wcm9wcy5teEV2ZW50KTtcblxuICAgICAgICB0aGlzLnNldFN0YXRlKHsgY2FuUmVkYWN0LCBjYW5QaW4gfSk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgY2FuRW5kUG9sbChteEV2ZW50OiBNYXRyaXhFdmVudCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgTV9QT0xMX1NUQVJULm1hdGNoZXMobXhFdmVudC5nZXRUeXBlKCkpICYmXG4gICAgICAgICAgICB0aGlzLnN0YXRlLmNhblJlZGFjdCAmJlxuICAgICAgICAgICAgIWlzUG9sbEVuZGVkKG14RXZlbnQsIE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCkpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvblJlc2VuZFJlYWN0aW9uc0NsaWNrID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICBmb3IgKGNvbnN0IHJlYWN0aW9uIG9mIHRoaXMuZ2V0VW5zZW50UmVhY3Rpb25zKCkpIHtcbiAgICAgICAgICAgIFJlc2VuZC5yZXNlbmQoTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKSwgcmVhY3Rpb24pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25KdW1wVG9SZWxhdGVkRXZlbnRDbGljayA9IChyZWxhdGVkRXZlbnRJZDogc3RyaW5nKTogdm9pZCA9PiB7XG4gICAgICAgIGRpcy5kaXNwYXRjaCh7XG4gICAgICAgICAgICBhY3Rpb246IFwidmlld19yb29tXCIsXG4gICAgICAgICAgICByb29tX2lkOiB0aGlzLnByb3BzLm14RXZlbnQuZ2V0Um9vbUlkKCksXG4gICAgICAgICAgICBldmVudF9pZDogcmVsYXRlZEV2ZW50SWQsXG4gICAgICAgICAgICBoaWdobGlnaHRlZDogdHJ1ZSxcbiAgICAgICAgfSk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25SZXBvcnRFdmVudENsaWNrID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICBkaXMuZGlzcGF0Y2g8T3BlblJlcG9ydEV2ZW50RGlhbG9nUGF5bG9hZD4oe1xuICAgICAgICAgICAgYWN0aW9uOiBBY3Rpb24uT3BlblJlcG9ydEV2ZW50RGlhbG9nLFxuICAgICAgICAgICAgZXZlbnQ6IHRoaXMucHJvcHMubXhFdmVudCxcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25WaWV3U291cmNlQ2xpY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIE1vZGFsLmNyZWF0ZURpYWxvZyhcbiAgICAgICAgICAgIFZpZXdTb3VyY2UsXG4gICAgICAgICAgICB7XG4gICAgICAgICAgICAgICAgbXhFdmVudDogdGhpcy5wcm9wcy5teEV2ZW50LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIFwibXhfRGlhbG9nX3ZpZXdzb3VyY2VcIixcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblJlZGFjdENsaWNrID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICBjb25zdCB7IG14RXZlbnQsIG9uQ2xvc2VEaWFsb2cgfSA9IHRoaXMucHJvcHM7XG4gICAgICAgIGNyZWF0ZVJlZGFjdEV2ZW50RGlhbG9nKHtcbiAgICAgICAgICAgIG14RXZlbnQsXG4gICAgICAgICAgICBvbkNsb3NlRGlhbG9nLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkZvcndhcmRDbGljayA9IChmb3J3YXJkYWJsZUV2ZW50OiBNYXRyaXhFdmVudCkgPT4gKCk6IHZvaWQgPT4ge1xuICAgICAgICBkaXMuZGlzcGF0Y2g8T3BlbkZvcndhcmREaWFsb2dQYXlsb2FkPih7XG4gICAgICAgICAgICBhY3Rpb246IEFjdGlvbi5PcGVuRm9yd2FyZERpYWxvZyxcbiAgICAgICAgICAgIGV2ZW50OiBmb3J3YXJkYWJsZUV2ZW50LFxuICAgICAgICAgICAgcGVybWFsaW5rQ3JlYXRvcjogdGhpcy5wcm9wcy5wZXJtYWxpbmtDcmVhdG9yLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblBpbkNsaWNrID0gKGlzUGlubmVkOiBib29sZWFuKTogdm9pZCA9PiB7XG4gICAgICAgIC8vIFBpbiBvciB1bnBpbiBpbiBiYWNrZ3JvdW5kXG4gICAgICAgIFBpbm5pbmdVdGlscy5waW5PclVucGluRXZlbnQoTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKSwgdGhpcy5wcm9wcy5teEV2ZW50KTtcbiAgICAgICAgUG9zdGhvZ1RyYWNrZXJzLnRyYWNrUGluVW5waW5NZXNzYWdlKGlzUGlubmVkID8gXCJQaW5cIiA6IFwiVW5waW5cIiwgXCJUaW1lbGluZVwiKTtcblxuICAgICAgICB0aGlzLmNsb3NlTWVudSgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIGNsb3NlTWVudSA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5wcm9wcy5vbkZpbmlzaGVkKCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25VbmhpZGVQcmV2aWV3Q2xpY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMucHJvcHMuZXZlbnRUaWxlT3BzPy51bmhpZGVXaWRnZXQoKTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblNoYXJlQ2xpY2sgPSAoZTogQnV0dG9uRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBNb2RhbC5jcmVhdGVEaWFsb2coU2hhcmVEaWFsb2csIHtcbiAgICAgICAgICAgIHRhcmdldDogdGhpcy5wcm9wcy5teEV2ZW50LFxuICAgICAgICAgICAgcGVybWFsaW5rQ3JlYXRvcjogdGhpcy5wcm9wcy5wZXJtYWxpbmtDcmVhdG9yLFxuICAgICAgICB9KTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkNvcHlMaW5rQ2xpY2sgPSAoZTogQnV0dG9uRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpOyAvLyBTbyB0aGF0IHdlIGRvbid0IG9wZW4gdGhlIHBlcm1hbGlua1xuICAgICAgICBpZiAoIXRoaXMucHJvcHMubGluaykgcmV0dXJuO1xuICAgICAgICBjb3B5UGxhaW50ZXh0KHRoaXMucHJvcHMubGluayk7XG4gICAgICAgIHRoaXMuY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25Db2xsYXBzZVJlcGx5Q2hhaW5DbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5wcm9wcy5jb2xsYXBzZVJlcGx5Q2hhaW4/LigpO1xuICAgICAgICB0aGlzLmNsb3NlTWVudSgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uQ29weUNsaWNrID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICBjb3B5UGxhaW50ZXh0KGdldFNlbGVjdGVkVGV4dCgpKTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkVkaXRDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgZWRpdEV2ZW50KFxuICAgICAgICAgICAgTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKSxcbiAgICAgICAgICAgIHRoaXMucHJvcHMubXhFdmVudCxcbiAgICAgICAgICAgIHRoaXMuY29udGV4dC50aW1lbGluZVJlbmRlcmluZ1R5cGUsXG4gICAgICAgICAgICB0aGlzLnByb3BzLmdldFJlbGF0aW9uc0ZvckV2ZW50LFxuICAgICAgICApO1xuICAgICAgICB0aGlzLmNsb3NlTWVudSgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uUmVwbHlDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgZGlzLmRpc3BhdGNoKHtcbiAgICAgICAgICAgIGFjdGlvbjogXCJyZXBseV90b19ldmVudFwiLFxuICAgICAgICAgICAgZXZlbnQ6IHRoaXMucHJvcHMubXhFdmVudCxcbiAgICAgICAgICAgIGNvbnRleHQ6IHRoaXMuY29udGV4dC50aW1lbGluZVJlbmRlcmluZ1R5cGUsXG4gICAgICAgIH0pO1xuICAgICAgICB0aGlzLmNsb3NlTWVudSgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uUmVhY3RDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHJlYWN0aW9uUGlja2VyRGlzcGxheWVkOiB0cnVlIH0pO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uQ2xvc2VSZWFjdGlvblBpY2tlciA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHJlYWN0aW9uUGlja2VyRGlzcGxheWVkOiBmYWxzZSB9KTtcbiAgICAgICAgdGhpcy5jbG9zZU1lbnUoKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkVuZFBvbGxDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgY29uc3QgbWF0cml4Q2xpZW50ID0gTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKTtcbiAgICAgICAgTW9kYWwuY3JlYXRlRGlhbG9nKFxuICAgICAgICAgICAgRW5kUG9sbERpYWxvZyxcbiAgICAgICAgICAgIHtcbiAgICAgICAgICAgICAgICBtYXRyaXhDbGllbnQsXG4gICAgICAgICAgICAgICAgZXZlbnQ6IHRoaXMucHJvcHMubXhFdmVudCxcbiAgICAgICAgICAgICAgICBnZXRSZWxhdGlvbnNGb3JFdmVudDogdGhpcy5wcm9wcy5nZXRSZWxhdGlvbnNGb3JFdmVudCxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBcIm14X0RpYWxvZ19lbmRQb2xsXCIsXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMuY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgZ2V0UmVhY3Rpb25zKGZpbHRlcjogKGU6IE1hdHJpeEV2ZW50KSA9PiBib29sZWFuKTogTWF0cml4RXZlbnRbXSB7XG4gICAgICAgIGNvbnN0IGNsaSA9IE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCk7XG4gICAgICAgIGNvbnN0IHJvb20gPSBjbGkuZ2V0Um9vbSh0aGlzLnByb3BzLm14RXZlbnQuZ2V0Um9vbUlkKCkpO1xuICAgICAgICBjb25zdCBldmVudElkID0gdGhpcy5wcm9wcy5teEV2ZW50LmdldElkKCk7XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICByb29tPy5nZXRQZW5kaW5nRXZlbnRzKCkuZmlsdGVyKChlKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgcmVsYXRpb24gPSBlLmdldFJlbGF0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlbGF0aW9uPy5yZWxfdHlwZSA9PT0gUmVsYXRpb25UeXBlLkFubm90YXRpb24gJiYgcmVsYXRpb24uZXZlbnRfaWQgPT09IGV2ZW50SWQgJiYgZmlsdGVyKGUpO1xuICAgICAgICAgICAgfSkgPz8gW11cbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGdldFVuc2VudFJlYWN0aW9ucygpOiBNYXRyaXhFdmVudFtdIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuZ2V0UmVhY3Rpb25zKChlKSA9PiBlLnN0YXR1cyA9PT0gRXZlbnRTdGF0dXMuTk9UX1NFTlQpO1xuICAgIH1cblxuICAgIHByaXZhdGUgdmlld0luUm9vbSA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgZGlzLmRpc3BhdGNoPFZpZXdSb29tUGF5bG9hZD4oe1xuICAgICAgICAgICAgYWN0aW9uOiBBY3Rpb24uVmlld1Jvb20sXG4gICAgICAgICAgICBldmVudF9pZDogdGhpcy5wcm9wcy5teEV2ZW50LmdldElkKCksXG4gICAgICAgICAgICBoaWdobGlnaHRlZDogdHJ1ZSxcbiAgICAgICAgICAgIHJvb21faWQ6IHRoaXMucHJvcHMubXhFdmVudC5nZXRSb29tSWQoKSxcbiAgICAgICAgICAgIG1ldHJpY3NUcmlnZ2VyOiB1bmRlZmluZWQsIC8vIHJvb20gZG9lc24ndCBjaGFuZ2VcbiAgICAgICAgfSk7XG4gICAgICAgIHRoaXMuY2xvc2VNZW51KCk7XG4gICAgfTtcblxuICAgIHB1YmxpYyByZW5kZXIoKTogUmVhY3QuUmVhY3ROb2RlIHtcbiAgICAgICAgY29uc3QgY2xpID0gTWF0cml4Q2xpZW50UGVnLnNhZmVHZXQoKTtcbiAgICAgICAgY29uc3QgbWUgPSBjbGkuZ2V0VXNlcklkKCk7XG4gICAgICAgIGNvbnN0IHsgbXhFdmVudCwgcmlnaHRDbGljaywgbGluaywgZXZlbnRUaWxlT3BzLCByZWFjdGlvbnMsIGNvbGxhcHNlUmVwbHlDaGFpbiwgLi4ub3RoZXIgfSA9IHRoaXMucHJvcHM7XG4gICAgICAgIGRlbGV0ZSBvdGhlci5nZXRSZWxhdGlvbnNGb3JFdmVudDtcbiAgICAgICAgZGVsZXRlIG90aGVyLnBlcm1hbGlua0NyZWF0b3I7XG5cbiAgICAgICAgY29uc3QgZXZlbnRTdGF0dXMgPSBteEV2ZW50LnN0YXR1cztcbiAgICAgICAgY29uc3QgdW5zZW50UmVhY3Rpb25zQ291bnQgPSB0aGlzLmdldFVuc2VudFJlYWN0aW9ucygpLmxlbmd0aDtcbiAgICAgICAgY29uc3QgY29udGVudEFjdGlvbmFibGUgPSBpc0NvbnRlbnRBY3Rpb25hYmxlKG14RXZlbnQpO1xuICAgICAgICBjb25zdCBwZXJtYWxpbmsgPSB0aGlzLnByb3BzLnBlcm1hbGlua0NyZWF0b3I/LmZvckV2ZW50KHRoaXMucHJvcHMubXhFdmVudC5nZXRJZCgpISk7XG4gICAgICAgIC8vIHN0YXR1cyBpcyBTRU5UIGJlZm9yZSByZW1vdGUtZWNobywgbnVsbCBhZnRlclxuICAgICAgICBjb25zdCBpc1NlbnQgPSAhZXZlbnRTdGF0dXMgfHwgZXZlbnRTdGF0dXMgPT09IEV2ZW50U3RhdHVzLlNFTlQ7XG4gICAgICAgIGNvbnN0IHsgdGltZWxpbmVSZW5kZXJpbmdUeXBlLCBjYW5SZWFjdCwgY2FuU2VuZE1lc3NhZ2VzIH0gPSB0aGlzLmNvbnRleHQ7XG4gICAgICAgIGNvbnN0IGlzVGhyZWFkID1cbiAgICAgICAgICAgIHRpbWVsaW5lUmVuZGVyaW5nVHlwZSA9PT0gVGltZWxpbmVSZW5kZXJpbmdUeXBlLlRocmVhZCB8fFxuICAgICAgICAgICAgdGltZWxpbmVSZW5kZXJpbmdUeXBlID09PSBUaW1lbGluZVJlbmRlcmluZ1R5cGUuVGhyZWFkc0xpc3Q7XG4gICAgICAgIGNvbnN0IGlzVGhyZWFkUm9vdEV2ZW50ID0gaXNUaHJlYWQgJiYgbXhFdmVudD8uZ2V0VGhyZWFkKCk/LnJvb3RFdmVudCA9PT0gbXhFdmVudDtcblxuICAgICAgICBsZXQgcmVzZW5kUmVhY3Rpb25zQnV0dG9uOiBKU1guRWxlbWVudCB8IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKCFteEV2ZW50LmlzUmVkYWN0ZWQoKSAmJiB1bnNlbnRSZWFjdGlvbnNDb3VudCAhPT0gMCkge1xuICAgICAgICAgICAgcmVzZW5kUmVhY3Rpb25zQnV0dG9uID0gKFxuICAgICAgICAgICAgICAgIDxJY29uaXplZENvbnRleHRNZW51T3B0aW9uXG4gICAgICAgICAgICAgICAgICAgIGljb25DbGFzc05hbWU9XCJteF9NZXNzYWdlQ29udGV4dE1lbnVfaWNvblJlc2VuZFwiXG4gICAgICAgICAgICAgICAgICAgIGxhYmVsPXtfdChcInRpbWVsaW5lfGNvbnRleHRfbWVudXxyZXNlbnRfdW5zZW50X3JlYWN0aW9uc1wiLCB7IHVuc2VudENvdW50OiB1bnNlbnRSZWFjdGlvbnNDb3VudCB9KX1cbiAgICAgICAgICAgICAgICAgICAgb25DbGljaz17dGhpcy5vblJlc2VuZFJlYWN0aW9uc0NsaWNrfVxuICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHJlZGFjdEJ1dHRvbjogSlNYLkVsZW1lbnQgfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChpc1NlbnQgJiYgdGhpcy5zdGF0ZS5jYW5SZWRhY3QpIHtcbiAgICAgICAgICAgIHJlZGFjdEJ1dHRvbiA9IChcbiAgICAgICAgICAgICAgICA8SWNvbml6ZWRDb250ZXh0TWVudU9wdGlvblxuICAgICAgICAgICAgICAgICAgICBpY29uQ2xhc3NOYW1lPVwibXhfTWVzc2FnZUNvbnRleHRNZW51X2ljb25SZWRhY3RcIlxuICAgICAgICAgICAgICAgICAgICBsYWJlbD17X3QoXCJhY3Rpb258cmVtb3ZlXCIpfVxuICAgICAgICAgICAgICAgICAgICBvbkNsaWNrPXt0aGlzLm9uUmVkYWN0Q2xpY2t9XG4gICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cblxuICAgICAgICBsZ