matrix-react-sdk
Version:
SDK for matrix.org using React
390 lines (384 loc) • 76.8 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.contextMenuBelow = exports.RoomTile = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _matrix = require("matrix-js-sdk/src/matrix");
var _types = require("matrix-js-sdk/src/types");
var _classnames = _interopRequireDefault(require("classnames"));
var _RovingTabIndex = require("../../../accessibility/RovingTabIndex");
var _AccessibleButton = _interopRequireDefault(require("../../views/elements/AccessibleButton"));
var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher"));
var _actions = require("../../../dispatcher/actions");
var _languageHandler = require("../../../languageHandler");
var _ContextMenu = require("../../structures/ContextMenu");
var _models = require("../../../stores/room-list/models");
var _MessagePreviewStore = require("../../../stores/room-list/MessagePreviewStore");
var _DecoratedRoomAvatar = _interopRequireDefault(require("../avatars/DecoratedRoomAvatar"));
var _RoomNotifs = require("../../../RoomNotifs");
var _MatrixClientPeg = require("../../../MatrixClientPeg");
var _RoomNotificationContextMenu = require("../context_menus/RoomNotificationContextMenu");
var _NotificationBadge = _interopRequireDefault(require("./NotificationBadge"));
var _RoomNotificationStateStore = require("../../../stores/notifications/RoomNotificationStateStore");
var _NotificationState = require("../../../stores/notifications/NotificationState");
var _EchoChamber = require("../../../stores/local-echo/EchoChamber");
var _RoomEchoChamber = require("../../../stores/local-echo/RoomEchoChamber");
var _GenericEchoChamber = require("../../../stores/local-echo/GenericEchoChamber");
var _PosthogTrackers = _interopRequireDefault(require("../../../PosthogTrackers"));
var _KeyboardShortcuts = require("../../../accessibility/KeyboardShortcuts");
var _KeyBindingsManager = require("../../../KeyBindingsManager");
var _RoomGeneralContextMenu = require("../context_menus/RoomGeneralContextMenu");
var _CallStore = require("../../../stores/CallStore");
var _SDKContext = require("../../../contexts/SDKContext");
var _voiceBroadcast = require("../../../voice-broadcast");
var _RoomTileSubtitle = require("./RoomTileSubtitle");
var _UIComponents = require("../../../customisations/helpers/UIComponents");
var _UIFeature = require("../../../settings/UIFeature");
var _membership = require("../../../utils/membership");
var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/*
Copyright 2024 New Vector Ltd.
Copyright 2018 Michael Telatynski <7t3chguy@gmail.com>
Copyright 2015-2017 , 2019-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 messagePreviewId = roomId => `mx_RoomTile_messagePreview_${roomId}`;
const contextMenuBelow = elementRect => {
// align the context menu's icons with the icon which opened the context menu
const left = elementRect.left + window.scrollX - 9;
const top = elementRect.bottom + window.scrollY + 17;
const chevronFace = _ContextMenu.ChevronFace.None;
return {
left,
top,
chevronFace
};
};
exports.contextMenuBelow = contextMenuBelow;
class RoomTile extends _react.default.PureComponent {
constructor(props) {
super(props);
(0, _defineProperty2.default)(this, "dispatcherRef", void 0);
(0, _defineProperty2.default)(this, "roomTileRef", /*#__PURE__*/(0, _react.createRef)());
(0, _defineProperty2.default)(this, "notificationState", void 0);
(0, _defineProperty2.default)(this, "roomProps", void 0);
(0, _defineProperty2.default)(this, "onRoomNameUpdate", room => {
this.forceUpdate();
});
(0, _defineProperty2.default)(this, "onNotificationUpdate", () => {
this.forceUpdate(); // notification state changed - update
});
(0, _defineProperty2.default)(this, "onRoomPropertyUpdate", property => {
if (property === _RoomEchoChamber.CachedRoomKey.NotificationVolume) this.onNotificationUpdate();
// else ignore - not important for this tile
});
(0, _defineProperty2.default)(this, "onAction", payload => {
if (payload.action === _actions.Action.ViewRoom && payload.room_id === this.props.room.roomId && payload.show_room_tile) {
setTimeout(() => {
this.scrollIntoView();
});
}
});
(0, _defineProperty2.default)(this, "onRoomPreviewChanged", room => {
if (this.props.room && room.roomId === this.props.room.roomId) {
this.generatePreview();
}
});
(0, _defineProperty2.default)(this, "onCallChanged", (call, roomId) => {
if (roomId === this.props.room?.roomId) this.setState({
call
});
});
(0, _defineProperty2.default)(this, "scrollIntoView", () => {
if (!this.roomTileRef.current) return;
this.roomTileRef.current.scrollIntoView({
block: "nearest",
behavior: "auto"
});
});
(0, _defineProperty2.default)(this, "onTileClick", async ev => {
ev.preventDefault();
ev.stopPropagation();
const action = (0, _KeyBindingsManager.getKeyBindingsManager)().getAccessibilityAction(ev);
const clearSearch = [_KeyboardShortcuts.KeyBindingAction.Enter, _KeyboardShortcuts.KeyBindingAction.Space].includes(action);
_dispatcher.default.dispatch({
action: _actions.Action.ViewRoom,
show_room_tile: true,
// make sure the room is visible in the list
room_id: this.props.room.roomId,
clear_search: clearSearch,
metricsTrigger: "RoomList",
metricsViaKeyboard: ev.type !== "click"
});
});
(0, _defineProperty2.default)(this, "onActiveRoomUpdate", isActive => {
this.setState({
selected: isActive
});
});
(0, _defineProperty2.default)(this, "onNotificationsMenuOpenClick", ev => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target;
this.setState({
notificationsMenuPosition: target.getBoundingClientRect()
});
_PosthogTrackers.default.trackInteraction("WebRoomListRoomTileNotificationsMenu", ev);
});
(0, _defineProperty2.default)(this, "onCloseNotificationsMenu", () => {
this.setState({
notificationsMenuPosition: null
});
});
(0, _defineProperty2.default)(this, "onGeneralMenuOpenClick", ev => {
ev.preventDefault();
ev.stopPropagation();
const target = ev.target;
this.setState({
generalMenuPosition: target.getBoundingClientRect()
});
});
(0, _defineProperty2.default)(this, "onContextMenu", ev => {
// If we don't have a context menu to show, ignore the action.
if (!this.showContextMenu) return;
ev.preventDefault();
ev.stopPropagation();
this.setState({
generalMenuPosition: {
left: ev.clientX,
bottom: ev.clientY
}
});
});
(0, _defineProperty2.default)(this, "onCloseGeneralMenu", () => {
this.setState({
generalMenuPosition: null
});
});
this.state = {
selected: _SDKContext.SdkContextClass.instance.roomViewStore.getRoomId() === this.props.room.roomId,
notificationsMenuPosition: null,
generalMenuPosition: null,
call: _CallStore.CallStore.instance.getCall(this.props.room.roomId),
// generatePreview() will return nothing if the user has previews disabled
messagePreview: null
};
this.generatePreview();
this.notificationState = _RoomNotificationStateStore.RoomNotificationStateStore.instance.getRoomState(this.props.room);
this.roomProps = _EchoChamber.EchoChamber.forRoom(this.props.room);
}
get showContextMenu() {
return this.props.tag !== _models.DefaultTagID.Invite && this.props.room.getMyMembership() !== _types.KnownMembership.Knock && !(0, _membership.isKnockDenied)(this.props.room) && (0, _UIComponents.shouldShowComponent)(_UIFeature.UIComponent.RoomOptionsMenu);
}
get showMessagePreview() {
return !this.props.isMinimized && this.props.showMessagePreview;
}
componentDidUpdate(prevProps, prevState) {
const showMessageChanged = prevProps.showMessagePreview !== this.props.showMessagePreview;
const minimizedChanged = prevProps.isMinimized !== this.props.isMinimized;
if (showMessageChanged || minimizedChanged) {
this.generatePreview();
}
if (prevProps.room?.roomId !== this.props.room?.roomId) {
_MessagePreviewStore.MessagePreviewStore.instance.off(_MessagePreviewStore.MessagePreviewStore.getPreviewChangedEventName(prevProps.room), this.onRoomPreviewChanged);
_MessagePreviewStore.MessagePreviewStore.instance.on(_MessagePreviewStore.MessagePreviewStore.getPreviewChangedEventName(this.props.room), this.onRoomPreviewChanged);
prevProps.room?.off(_matrix.RoomEvent.Name, this.onRoomNameUpdate);
this.props.room?.on(_matrix.RoomEvent.Name, this.onRoomNameUpdate);
}
}
componentDidMount() {
// when we're first rendered (or our sublist is expanded) make sure we are visible if we're active
if (this.state.selected) {
this.scrollIntoView();
}
_SDKContext.SdkContextClass.instance.roomViewStore.addRoomListener(this.props.room.roomId, this.onActiveRoomUpdate);
this.dispatcherRef = _dispatcher.default.register(this.onAction);
_MessagePreviewStore.MessagePreviewStore.instance.on(_MessagePreviewStore.MessagePreviewStore.getPreviewChangedEventName(this.props.room), this.onRoomPreviewChanged);
this.notificationState.on(_NotificationState.NotificationStateEvents.Update, this.onNotificationUpdate);
this.roomProps.on(_GenericEchoChamber.PROPERTY_UPDATED, this.onRoomPropertyUpdate);
this.props.room.on(_matrix.RoomEvent.Name, this.onRoomNameUpdate);
_CallStore.CallStore.instance.on(_CallStore.CallStoreEvent.Call, this.onCallChanged);
// Recalculate the call for this room, since it could've changed between
// construction and mounting
this.setState({
call: _CallStore.CallStore.instance.getCall(this.props.room.roomId)
});
}
componentWillUnmount() {
_SDKContext.SdkContextClass.instance.roomViewStore.removeRoomListener(this.props.room.roomId, this.onActiveRoomUpdate);
_MessagePreviewStore.MessagePreviewStore.instance.off(_MessagePreviewStore.MessagePreviewStore.getPreviewChangedEventName(this.props.room), this.onRoomPreviewChanged);
this.props.room.off(_matrix.RoomEvent.Name, this.onRoomNameUpdate);
if (this.dispatcherRef) _dispatcher.default.unregister(this.dispatcherRef);
this.notificationState.off(_NotificationState.NotificationStateEvents.Update, this.onNotificationUpdate);
this.roomProps.off(_GenericEchoChamber.PROPERTY_UPDATED, this.onRoomPropertyUpdate);
_CallStore.CallStore.instance.off(_CallStore.CallStoreEvent.Call, this.onCallChanged);
}
async generatePreview() {
if (!this.showMessagePreview) {
return;
}
const messagePreview = (await _MessagePreviewStore.MessagePreviewStore.instance.getPreviewForRoom(this.props.room, this.props.tag)) ?? null;
this.setState({
messagePreview
});
}
renderNotificationsMenu(isActive) {
if (_MatrixClientPeg.MatrixClientPeg.safeGet().isGuest() || this.props.tag === _models.DefaultTagID.Archived || !this.showContextMenu || this.props.isMinimized) {
// the menu makes no sense in these cases so do not show one
return null;
}
const state = this.roomProps.notificationVolume;
const classes = (0, _classnames.default)("mx_RoomTile_notificationsButton", {
// Show bell icon for the default case too.
mx_RoomNotificationContextMenu_iconBell: state === _RoomNotifs.RoomNotifState.AllMessages,
mx_RoomNotificationContextMenu_iconBellDot: state === _RoomNotifs.RoomNotifState.AllMessagesLoud,
mx_RoomNotificationContextMenu_iconBellMentions: state === _RoomNotifs.RoomNotifState.MentionsOnly,
mx_RoomNotificationContextMenu_iconBellCrossed: state === _RoomNotifs.RoomNotifState.Mute,
// Only show the icon by default if the room is overridden to muted.
// TODO: [FTUE Notifications] Probably need to detect global mute state
mx_RoomTile_notificationsButton_show: state === _RoomNotifs.RoomNotifState.Mute
});
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_ContextMenu.ContextMenuTooltipButton, {
className: classes,
onClick: this.onNotificationsMenuOpenClick,
title: (0, _languageHandler._t)("room_list|notification_options"),
isExpanded: !!this.state.notificationsMenuPosition,
tabIndex: isActive ? 0 : -1
}), this.state.notificationsMenuPosition && /*#__PURE__*/_react.default.createElement(_RoomNotificationContextMenu.RoomNotificationContextMenu, (0, _extends2.default)({}, contextMenuBelow(this.state.notificationsMenuPosition), {
onFinished: this.onCloseNotificationsMenu,
room: this.props.room
})));
}
renderGeneralMenu() {
if (!this.showContextMenu) return null; // no menu to show
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_ContextMenu.ContextMenuTooltipButton, {
className: "mx_RoomTile_menuButton",
onClick: this.onGeneralMenuOpenClick,
title: (0, _languageHandler._t)("room|context_menu|title"),
isExpanded: !!this.state.generalMenuPosition
}), this.state.generalMenuPosition && /*#__PURE__*/_react.default.createElement(_RoomGeneralContextMenu.RoomGeneralContextMenu, (0, _extends2.default)({}, contextMenuBelow(this.state.generalMenuPosition), {
onFinished: this.onCloseGeneralMenu,
room: this.props.room,
onPostFavoriteClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuFavouriteToggle", ev),
onPostInviteClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuInviteItem", ev),
onPostSettingsClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuSettingsItem", ev),
onPostLeaveClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuLeaveItem", ev),
onPostMarkAsReadClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuMarkRead", ev),
onPostMarkAsUnreadClick: ev => _PosthogTrackers.default.trackInteraction("WebRoomListRoomTileContextMenuMarkUnread", ev)
})));
}
/**
* RoomTile has a subtile if one of the following applies:
* - there is a call
* - there is a live voice broadcast
* - message previews are enabled and there is a previewable message
*/
get shouldRenderSubtitle() {
return !!this.state.call || this.props.hasLiveVoiceBroadcast || this.props.showMessagePreview && !!this.state.messagePreview;
}
render() {
const classes = (0, _classnames.default)({
mx_RoomTile: true,
mx_RoomTile_sticky: _SettingsStore.default.getValue("feature_ask_to_join") && (this.props.room.getMyMembership() === _types.KnownMembership.Knock || (0, _membership.isKnockDenied)(this.props.room)),
mx_RoomTile_selected: this.state.selected,
mx_RoomTile_hasMenuOpen: !!(this.state.generalMenuPosition || this.state.notificationsMenuPosition),
mx_RoomTile_minimized: this.props.isMinimized
});
let name = this.props.room.name;
if (typeof name !== "string") name = "";
name = name.replace(":", ":\u200b"); // add a zero-width space to allow linewrapping after the colon
let badge;
if (!this.props.isMinimized && this.notificationState) {
// aria-hidden because we summarise the unread count/highlight status in a manual aria-label below
badge = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_RoomTile_badgeContainer",
"aria-hidden": "true"
}, /*#__PURE__*/_react.default.createElement(_NotificationBadge.default, {
notification: this.notificationState,
roomId: this.props.room.roomId
}));
}
const subtitle = this.shouldRenderSubtitle ? /*#__PURE__*/_react.default.createElement(_RoomTileSubtitle.RoomTileSubtitle, {
call: this.state.call,
hasLiveVoiceBroadcast: this.props.hasLiveVoiceBroadcast,
messagePreview: this.state.messagePreview,
roomId: this.props.room.roomId,
showMessagePreview: this.props.showMessagePreview
}) : null;
const titleClasses = (0, _classnames.default)({
mx_RoomTile_title: true,
mx_RoomTile_titleWithSubtitle: !!subtitle,
mx_RoomTile_titleHasUnreadEvents: this.notificationState.isUnread
});
const titleContainer = this.props.isMinimized ? null : /*#__PURE__*/_react.default.createElement("div", {
className: "mx_RoomTile_titleContainer"
}, /*#__PURE__*/_react.default.createElement("div", {
title: name,
className: titleClasses,
tabIndex: -1
}, /*#__PURE__*/_react.default.createElement("span", {
dir: "auto"
}, name)), subtitle);
let ariaLabel = name;
// The following labels are written in such a fashion to increase screen reader efficiency (speed).
if (this.props.tag === _models.DefaultTagID.Invite) {
// append nothing
} else if (this.notificationState.hasMentions) {
ariaLabel += " " + (0, _languageHandler._t)("a11y|n_unread_messages_mentions", {
count: this.notificationState.count
});
} else if (this.notificationState.hasUnreadCount) {
ariaLabel += " " + (0, _languageHandler._t)("a11y|n_unread_messages", {
count: this.notificationState.count
});
} else if (this.notificationState.isUnread) {
ariaLabel += " " + (0, _languageHandler._t)("a11y|unread_messages");
}
let ariaDescribedBy;
if (this.showMessagePreview) {
ariaDescribedBy = messagePreviewId(this.props.room.roomId);
}
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement(_RovingTabIndex.RovingTabIndexWrapper, {
inputRef: this.roomTileRef
}, ({
onFocus,
isActive,
ref
}) => /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
onFocus: onFocus,
tabIndex: isActive ? 0 : -1,
ref: ref,
className: classes,
onClick: this.onTileClick,
onContextMenu: this.onContextMenu,
role: "treeitem",
"aria-label": ariaLabel,
"aria-selected": this.state.selected,
"aria-describedby": ariaDescribedBy,
title: this.props.isMinimized && !this.state.generalMenuPosition ? name : undefined
}, /*#__PURE__*/_react.default.createElement(_DecoratedRoomAvatar.default, {
room: this.props.room,
size: "32px",
displayBadge: this.props.isMinimized,
tooltipProps: {
tabIndex: isActive ? 0 : -1
}
}), titleContainer, badge, this.renderGeneralMenu(), this.renderNotificationsMenu(isActive))));
}
}
exports.RoomTile = RoomTile;
const RoomTileHOC = props => {
const hasLiveVoiceBroadcast = (0, _voiceBroadcast.useHasRoomLiveVoiceBroadcast)(props.room);
return /*#__PURE__*/_react.default.createElement(RoomTile, (0, _extends2.default)({}, props, {
hasLiveVoiceBroadcast: hasLiveVoiceBroadcast
}));
};
var _default = exports.default = RoomTileHOC;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfbWF0cml4IiwiX3R5cGVzIiwiX2NsYXNzbmFtZXMiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiX1JvdmluZ1RhYkluZGV4IiwiX0FjY2Vzc2libGVCdXR0b24iLCJfZGlzcGF0Y2hlciIsIl9hY3Rpb25zIiwiX2xhbmd1YWdlSGFuZGxlciIsIl9Db250ZXh0TWVudSIsIl9tb2RlbHMiLCJfTWVzc2FnZVByZXZpZXdTdG9yZSIsIl9EZWNvcmF0ZWRSb29tQXZhdGFyIiwiX1Jvb21Ob3RpZnMiLCJfTWF0cml4Q2xpZW50UGVnIiwiX1Jvb21Ob3RpZmljYXRpb25Db250ZXh0TWVudSIsIl9Ob3RpZmljYXRpb25CYWRnZSIsIl9Sb29tTm90aWZpY2F0aW9uU3RhdGVTdG9yZSIsIl9Ob3RpZmljYXRpb25TdGF0ZSIsIl9FY2hvQ2hhbWJlciIsIl9Sb29tRWNob0NoYW1iZXIiLCJfR2VuZXJpY0VjaG9DaGFtYmVyIiwiX1Bvc3Rob2dUcmFja2VycyIsIl9LZXlib2FyZFNob3J0Y3V0cyIsIl9LZXlCaW5kaW5nc01hbmFnZXIiLCJfUm9vbUdlbmVyYWxDb250ZXh0TWVudSIsIl9DYWxsU3RvcmUiLCJfU0RLQ29udGV4dCIsIl92b2ljZUJyb2FkY2FzdCIsIl9Sb29tVGlsZVN1YnRpdGxlIiwiX1VJQ29tcG9uZW50cyIsIl9VSUZlYXR1cmUiLCJfbWVtYmVyc2hpcCIsIl9TZXR0aW5nc1N0b3JlIiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0IiwibWVzc2FnZVByZXZpZXdJZCIsInJvb21JZCIsImNvbnRleHRNZW51QmVsb3ciLCJlbGVtZW50UmVjdCIsImxlZnQiLCJ3aW5kb3ciLCJzY3JvbGxYIiwidG9wIiwiYm90dG9tIiwic2Nyb2xsWSIsImNoZXZyb25GYWNlIiwiQ2hldnJvbkZhY2UiLCJOb25lIiwiZXhwb3J0cyIsIlJvb21UaWxlIiwiUmVhY3QiLCJQdXJlQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJjcmVhdGVSZWYiLCJyb29tIiwiZm9yY2VVcGRhdGUiLCJwcm9wZXJ0eSIsIkNhY2hlZFJvb21LZXkiLCJOb3RpZmljYXRpb25Wb2x1bWUiLCJvbk5vdGlmaWNhdGlvblVwZGF0ZSIsInBheWxvYWQiLCJhY3Rpb24iLCJBY3Rpb24iLCJWaWV3Um9vbSIsInJvb21faWQiLCJzaG93X3Jvb21fdGlsZSIsInNldFRpbWVvdXQiLCJzY3JvbGxJbnRvVmlldyIsImdlbmVyYXRlUHJldmlldyIsInNldFN0YXRlIiwicm9vbVRpbGVSZWYiLCJjdXJyZW50IiwiYmxvY2siLCJiZWhhdmlvciIsImV2IiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJnZXRLZXlCaW5kaW5nc01hbmFnZXIiLCJnZXRBY2Nlc3NpYmlsaXR5QWN0aW9uIiwiY2xlYXJTZWFyY2giLCJLZXlCaW5kaW5nQWN0aW9uIiwiRW50ZXIiLCJTcGFjZSIsImluY2x1ZGVzIiwiZGVmYXVsdERpc3BhdGNoZXIiLCJkaXNwYXRjaCIsImNsZWFyX3NlYXJjaCIsIm1ldHJpY3NUcmlnZ2VyIiwibWV0cmljc1ZpYUtleWJvYXJkIiwidHlwZSIsImlzQWN0aXZlIiwic2VsZWN0ZWQiLCJ0YXJnZXQiLCJub3RpZmljYXRpb25zTWVudVBvc2l0aW9uIiwiZ2V0Qm91bmRpbmdDbGllbnRSZWN0IiwiUG9zdGhvZ1RyYWNrZXJzIiwidHJhY2tJbnRlcmFjdGlvbiIsImdlbmVyYWxNZW51UG9zaXRpb24iLCJzaG93Q29udGV4dE1lbnUiLCJjbGllbnRYIiwiY2xpZW50WSIsInN0YXRlIiwiU2RrQ29udGV4dENsYXNzIiwiaW5zdGFuY2UiLCJyb29tVmlld1N0b3JlIiwiZ2V0Um9vbUlkIiwiQ2FsbFN0b3JlIiwiZ2V0Q2FsbCIsIm1lc3NhZ2VQcmV2aWV3Iiwibm90aWZpY2F0aW9uU3RhdGUiLCJSb29tTm90aWZpY2F0aW9uU3RhdGVTdG9yZSIsImdldFJvb21TdGF0ZSIsInJvb21Qcm9wcyIsIkVjaG9DaGFtYmVyIiwiZm9yUm9vbSIsInRhZyIsIkRlZmF1bHRUYWdJRCIsIkludml0ZSIsImdldE15TWVtYmVyc2hpcCIsIktub3duTWVtYmVyc2hpcCIsIktub2NrIiwiaXNLbm9ja0RlbmllZCIsInNob3VsZFNob3dDb21wb25lbnQiLCJVSUNvbXBvbmVudCIsIlJvb21PcHRpb25zTWVudSIsInNob3dNZXNzYWdlUHJldmlldyIsImlzTWluaW1pemVkIiwiY29tcG9uZW50RGlkVXBkYXRlIiwicHJldlByb3BzIiwicHJldlN0YXRlIiwic2hvd01lc3NhZ2VDaGFuZ2VkIiwibWluaW1pemVkQ2hhbmdlZCIsIk1lc3NhZ2VQcmV2aWV3U3RvcmUiLCJvZmYiLCJnZXRQcmV2aWV3Q2hhbmdlZEV2ZW50TmFtZSIsIm9uUm9vbVByZXZpZXdDaGFuZ2VkIiwib24iLCJSb29tRXZlbnQiLCJOYW1lIiwib25Sb29tTmFtZVVwZGF0ZSIsImNvbXBvbmVudERpZE1vdW50IiwiYWRkUm9vbUxpc3RlbmVyIiwib25BY3RpdmVSb29tVXBkYXRlIiwiZGlzcGF0Y2hlclJlZiIsInJlZ2lzdGVyIiwib25BY3Rpb24iLCJOb3RpZmljYXRpb25TdGF0ZUV2ZW50cyIsIlVwZGF0ZSIsIlBST1BFUlRZX1VQREFURUQiLCJvblJvb21Qcm9wZXJ0eVVwZGF0ZSIsIkNhbGxTdG9yZUV2ZW50IiwiQ2FsbCIsIm9uQ2FsbENoYW5nZWQiLCJjb21wb25lbnRXaWxsVW5tb3VudCIsInJlbW92ZVJvb21MaXN0ZW5lciIsInVucmVnaXN0ZXIiLCJnZXRQcmV2aWV3Rm9yUm9vbSIsInJlbmRlck5vdGlmaWNhdGlvbnNNZW51IiwiTWF0cml4Q2xpZW50UGVnIiwic2FmZUdldCIsImlzR3Vlc3QiLCJBcmNoaXZlZCIsIm5vdGlmaWNhdGlvblZvbHVtZSIsImNsYXNzZXMiLCJjbGFzc05hbWVzIiwibXhfUm9vbU5vdGlmaWNhdGlvbkNvbnRleHRNZW51X2ljb25CZWxsIiwiUm9vbU5vdGlmU3RhdGUiLCJBbGxNZXNzYWdlcyIsIm14X1Jvb21Ob3RpZmljYXRpb25Db250ZXh0TWVudV9pY29uQmVsbERvdCIsIkFsbE1lc3NhZ2VzTG91ZCIsIm14X1Jvb21Ob3RpZmljYXRpb25Db250ZXh0TWVudV9pY29uQmVsbE1lbnRpb25zIiwiTWVudGlvbnNPbmx5IiwibXhfUm9vbU5vdGlmaWNhdGlvbkNvbnRleHRNZW51X2ljb25CZWxsQ3Jvc3NlZCIsIk11dGUiLCJteF9Sb29tVGlsZV9ub3RpZmljYXRpb25zQnV0dG9uX3Nob3ciLCJjcmVhdGVFbGVtZW50IiwiRnJhZ21lbnQiLCJDb250ZXh0TWVudVRvb2x0aXBCdXR0b24iLCJjbGFzc05hbWUiLCJvbkNsaWNrIiwib25Ob3RpZmljYXRpb25zTWVudU9wZW5DbGljayIsInRpdGxlIiwiX3QiLCJpc0V4cGFuZGVkIiwidGFiSW5kZXgiLCJSb29tTm90aWZpY2F0aW9uQ29udGV4dE1lbnUiLCJfZXh0ZW5kczIiLCJvbkZpbmlzaGVkIiwib25DbG9zZU5vdGlmaWNhdGlvbnNNZW51IiwicmVuZGVyR2VuZXJhbE1lbnUiLCJvbkdlbmVyYWxNZW51T3BlbkNsaWNrIiwiUm9vbUdlbmVyYWxDb250ZXh0TWVudSIsIm9uQ2xvc2VHZW5lcmFsTWVudSIsIm9uUG9zdEZhdm9yaXRlQ2xpY2siLCJvblBvc3RJbnZpdGVDbGljayIsIm9uUG9zdFNldHRpbmdzQ2xpY2siLCJvblBvc3RMZWF2ZUNsaWNrIiwib25Qb3N0TWFya0FzUmVhZENsaWNrIiwib25Qb3N0TWFya0FzVW5yZWFkQ2xpY2siLCJzaG91bGRSZW5kZXJTdWJ0aXRsZSIsImhhc0xpdmVWb2ljZUJyb2FkY2FzdCIsInJlbmRlciIsIm14X1Jvb21UaWxlIiwibXhfUm9vbVRpbGVfc3RpY2t5IiwiU2V0dGluZ3NTdG9yZSIsImdldFZhbHVlIiwibXhfUm9vbVRpbGVfc2VsZWN0ZWQiLCJteF9Sb29tVGlsZV9oYXNNZW51T3BlbiIsIm14X1Jvb21UaWxlX21pbmltaXplZCIsIm5hbWUiLCJyZXBsYWNlIiwiYmFkZ2UiLCJub3RpZmljYXRpb24iLCJzdWJ0aXRsZSIsIlJvb21UaWxlU3VidGl0bGUiLCJ0aXRsZUNsYXNzZXMiLCJteF9Sb29tVGlsZV90aXRsZSIsIm14X1Jvb21UaWxlX3RpdGxlV2l0aFN1YnRpdGxlIiwibXhfUm9vbVRpbGVfdGl0bGVIYXNVbnJlYWRFdmVudHMiLCJpc1VucmVhZCIsInRpdGxlQ29udGFpbmVyIiwiZGlyIiwiYXJpYUxhYmVsIiwiaGFzTWVudGlvbnMiLCJjb3VudCIsImhhc1VucmVhZENvdW50IiwiYXJpYURlc2NyaWJlZEJ5IiwiUm92aW5nVGFiSW5kZXhXcmFwcGVyIiwiaW5wdXRSZWYiLCJvbkZvY3VzIiwicmVmIiwib25UaWxlQ2xpY2siLCJvbkNvbnRleHRNZW51Iiwicm9sZSIsInVuZGVmaW5lZCIsInNpemUiLCJkaXNwbGF5QmFkZ2UiLCJ0b29sdGlwUHJvcHMiLCJSb29tVGlsZUhPQyIsInVzZUhhc1Jvb21MaXZlVm9pY2VCcm9hZGNhc3QiLCJfZGVmYXVsdCJdLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL3Jvb21zL1Jvb21UaWxlLnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxOCBNaWNoYWVsIFRlbGF0eW5za2kgPDd0M2NoZ3V5QGdtYWlsLmNvbT5cbkNvcHlyaWdodCAyMDE1LTIwMTcgLCAyMDE5LTIwMjEgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IFJlYWN0LCB7IGNyZWF0ZVJlZiB9IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHsgUm9vbSwgUm9vbUV2ZW50IH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21hdHJpeFwiO1xuaW1wb3J0IHsgS25vd25NZW1iZXJzaGlwIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL3R5cGVzXCI7XG5pbXBvcnQgY2xhc3NOYW1lcyBmcm9tIFwiY2xhc3NuYW1lc1wiO1xuXG5pbXBvcnQgdHlwZSB7IENhbGwgfSBmcm9tIFwiLi4vLi4vLi4vbW9kZWxzL0NhbGxcIjtcbmltcG9ydCB7IFJvdmluZ1RhYkluZGV4V3JhcHBlciB9IGZyb20gXCIuLi8uLi8uLi9hY2Nlc3NpYmlsaXR5L1JvdmluZ1RhYkluZGV4XCI7XG5pbXBvcnQgQWNjZXNzaWJsZUJ1dHRvbiwgeyBCdXR0b25FdmVudCB9IGZyb20gXCIuLi8uLi92aWV3cy9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgZGVmYXVsdERpc3BhdGNoZXIgZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvZGlzcGF0Y2hlclwiO1xuaW1wb3J0IHsgQWN0aW9uIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvYWN0aW9uc1wiO1xuaW1wb3J0IHsgX3QgfSBmcm9tIFwiLi4vLi4vLi4vbGFuZ3VhZ2VIYW5kbGVyXCI7XG5pbXBvcnQgeyBDaGV2cm9uRmFjZSwgQ29udGV4dE1lbnVUb29sdGlwQnV0dG9uLCBNZW51UHJvcHMgfSBmcm9tIFwiLi4vLi4vc3RydWN0dXJlcy9Db250ZXh0TWVudVwiO1xuaW1wb3J0IHsgRGVmYXVsdFRhZ0lELCBUYWdJRCB9IGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvcm9vbS1saXN0L21vZGVsc1wiO1xuaW1wb3J0IHsgTWVzc2FnZVByZXZpZXcsIE1lc3NhZ2VQcmV2aWV3U3RvcmUgfSBmcm9tIFwiLi4vLi4vLi4vc3RvcmVzL3Jvb20tbGlzdC9NZXNzYWdlUHJldmlld1N0b3JlXCI7XG5pbXBvcnQgRGVjb3JhdGVkUm9vbUF2YXRhciBmcm9tIFwiLi4vYXZhdGFycy9EZWNvcmF0ZWRSb29tQXZhdGFyXCI7XG5pbXBvcnQgeyBSb29tTm90aWZTdGF0ZSB9IGZyb20gXCIuLi8uLi8uLi9Sb29tTm90aWZzXCI7XG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tIFwiLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnXCI7XG5pbXBvcnQgeyBSb29tTm90aWZpY2F0aW9uQ29udGV4dE1lbnUgfSBmcm9tIFwiLi4vY29udGV4dF9tZW51cy9Sb29tTm90aWZpY2F0aW9uQ29udGV4dE1lbnVcIjtcbmltcG9ydCBOb3RpZmljYXRpb25CYWRnZSBmcm9tIFwiLi9Ob3RpZmljYXRpb25CYWRnZVwiO1xuaW1wb3J0IHsgQWN0aW9uUGF5bG9hZCB9IGZyb20gXCIuLi8uLi8uLi9kaXNwYXRjaGVyL3BheWxvYWRzXCI7XG5pbXBvcnQgeyBSb29tTm90aWZpY2F0aW9uU3RhdGVTdG9yZSB9IGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvbm90aWZpY2F0aW9ucy9Sb29tTm90aWZpY2F0aW9uU3RhdGVTdG9yZVwiO1xuaW1wb3J0IHsgTm90aWZpY2F0aW9uU3RhdGUsIE5vdGlmaWNhdGlvblN0YXRlRXZlbnRzIH0gZnJvbSBcIi4uLy4uLy4uL3N0b3Jlcy9ub3RpZmljYXRpb25zL05vdGlmaWNhdGlvblN0YXRlXCI7XG5pbXBvcnQgeyBFY2hvQ2hhbWJlciB9IGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvbG9jYWwtZWNoby9FY2hvQ2hhbWJlclwiO1xuaW1wb3J0IHsgQ2FjaGVkUm9vbUtleSwgUm9vbUVjaG9DaGFtYmVyIH0gZnJvbSBcIi4uLy4uLy4uL3N0b3Jlcy9sb2NhbC1lY2hvL1Jvb21FY2hvQ2hhbWJlclwiO1xuaW1wb3J0IHsgUFJPUEVSVFlfVVBEQVRFRCB9IGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvbG9jYWwtZWNoby9HZW5lcmljRWNob0NoYW1iZXJcIjtcbmltcG9ydCBQb3N0aG9nVHJhY2tlcnMgZnJvbSBcIi4uLy4uLy4uL1Bvc3Rob2dUcmFja2Vyc1wiO1xuaW1wb3J0IHsgVmlld1Jvb21QYXlsb2FkIH0gZnJvbSBcIi4uLy4uLy4uL2Rpc3BhdGNoZXIvcGF5bG9hZHMvVmlld1Jvb21QYXlsb2FkXCI7XG5pbXBvcnQgeyBLZXlCaW5kaW5nQWN0aW9uIH0gZnJvbSBcIi4uLy4uLy4uL2FjY2Vzc2liaWxpdHkvS2V5Ym9hcmRTaG9ydGN1dHNcIjtcbmltcG9ydCB7IGdldEtleUJpbmRpbmdzTWFuYWdlciB9IGZyb20gXCIuLi8uLi8uLi9LZXlCaW5kaW5nc01hbmFnZXJcIjtcbmltcG9ydCB7IFJvb21HZW5lcmFsQ29udGV4dE1lbnUgfSBmcm9tIFwiLi4vY29udGV4dF9tZW51cy9Sb29tR2VuZXJhbENvbnRleHRNZW51XCI7XG5pbXBvcnQgeyBDYWxsU3RvcmUsIENhbGxTdG9yZUV2ZW50IH0gZnJvbSBcIi4uLy4uLy4uL3N0b3Jlcy9DYWxsU3RvcmVcIjtcbmltcG9ydCB7IFNka0NvbnRleHRDbGFzcyB9IGZyb20gXCIuLi8uLi8uLi9jb250ZXh0cy9TREtDb250ZXh0XCI7XG5pbXBvcnQgeyB1c2VIYXNSb29tTGl2ZVZvaWNlQnJvYWRjYXN0IH0gZnJvbSBcIi4uLy4uLy4uL3ZvaWNlLWJyb2FkY2FzdFwiO1xuaW1wb3J0IHsgUm9vbVRpbGVTdWJ0aXRsZSB9IGZyb20gXCIuL1Jvb21UaWxlU3VidGl0bGVcIjtcbmltcG9ydCB7IHNob3VsZFNob3dDb21wb25lbnQgfSBmcm9tIFwiLi4vLi4vLi4vY3VzdG9taXNhdGlvbnMvaGVscGVycy9VSUNvbXBvbmVudHNcIjtcbmltcG9ydCB7IFVJQ29tcG9uZW50IH0gZnJvbSBcIi4uLy4uLy4uL3NldHRpbmdzL1VJRmVhdHVyZVwiO1xuaW1wb3J0IHsgaXNLbm9ja0RlbmllZCB9IGZyb20gXCIuLi8uLi8uLi91dGlscy9tZW1iZXJzaGlwXCI7XG5pbXBvcnQgU2V0dGluZ3NTdG9yZSBmcm9tIFwiLi4vLi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NTdG9yZVwiO1xuXG5pbnRlcmZhY2UgUHJvcHMge1xuICAgIHJvb206IFJvb207XG4gICAgc2hvd01lc3NhZ2VQcmV2aWV3OiBib29sZWFuO1xuICAgIGlzTWluaW1pemVkOiBib29sZWFuO1xuICAgIHRhZzogVGFnSUQ7XG59XG5cbmludGVyZmFjZSBDbGFzc1Byb3BzIGV4dGVuZHMgUHJvcHMge1xuICAgIGhhc0xpdmVWb2ljZUJyb2FkY2FzdDogYm9vbGVhbjtcbn1cblxudHlwZSBQYXJ0aWFsRE9NUmVjdCA9IFBpY2s8RE9NUmVjdCwgXCJsZWZ0XCIgfCBcImJvdHRvbVwiPjtcblxuaW50ZXJmYWNlIFN0YXRlIHtcbiAgICBzZWxlY3RlZDogYm9vbGVhbjtcbiAgICBub3RpZmljYXRpb25zTWVudVBvc2l0aW9uOiBQYXJ0aWFsRE9NUmVjdCB8IG51bGw7XG4gICAgZ2VuZXJhbE1lbnVQb3NpdGlvbjogUGFydGlhbERPTVJlY3QgfCBudWxsO1xuICAgIGNhbGw6IENhbGwgfCBudWxsO1xuICAgIG1lc3NhZ2VQcmV2aWV3OiBNZXNzYWdlUHJldmlldyB8IG51bGw7XG59XG5cbmNvbnN0IG1lc3NhZ2VQcmV2aWV3SWQgPSAocm9vbUlkOiBzdHJpbmcpOiBzdHJpbmcgPT4gYG14X1Jvb21UaWxlX21lc3NhZ2VQcmV2aWV3XyR7cm9vbUlkfWA7XG5cbmV4cG9ydCBjb25zdCBjb250ZXh0TWVudUJlbG93ID0gKGVsZW1lbnRSZWN0OiBQYXJ0aWFsRE9NUmVjdCk6IE1lbnVQcm9wcyA9PiB7XG4gICAgLy8gYWxpZ24gdGhlIGNvbnRleHQgbWVudSdzIGljb25zIHdpdGggdGhlIGljb24gd2hpY2ggb3BlbmVkIHRoZSBjb250ZXh0IG1lbnVcbiAgICBjb25zdCBsZWZ0ID0gZWxlbWVudFJlY3QubGVmdCArIHdpbmRvdy5zY3JvbGxYIC0gOTtcbiAgICBjb25zdCB0b3AgPSBlbGVtZW50UmVjdC5ib3R0b20gKyB3aW5kb3cuc2Nyb2xsWSArIDE3O1xuICAgIGNvbnN0IGNoZXZyb25GYWNlID0gQ2hldnJvbkZhY2UuTm9uZTtcbiAgICByZXR1cm4geyBsZWZ0LCB0b3AsIGNoZXZyb25GYWNlIH07XG59O1xuXG5leHBvcnQgY2xhc3MgUm9vbVRpbGUgZXh0ZW5kcyBSZWFjdC5QdXJlQ29tcG9uZW50PENsYXNzUHJvcHMsIFN0YXRlPiB7XG4gICAgcHJpdmF0ZSBkaXNwYXRjaGVyUmVmPzogc3RyaW5nO1xuICAgIHByaXZhdGUgcm9vbVRpbGVSZWYgPSBjcmVhdGVSZWY8SFRNTERpdkVsZW1lbnQ+KCk7XG4gICAgcHJpdmF0ZSBub3RpZmljYXRpb25TdGF0ZTogTm90aWZpY2F0aW9uU3RhdGU7XG4gICAgcHJpdmF0ZSByb29tUHJvcHM6IFJvb21FY2hvQ2hhbWJlcjtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcm9wczogQ2xhc3NQcm9wcykge1xuICAgICAgICBzdXBlcihwcm9wcyk7XG5cbiAgICAgICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgICAgICAgIHNlbGVjdGVkOiBTZGtDb250ZXh0Q2xhc3MuaW5zdGFuY2Uucm9vbVZpZXdTdG9yZS5nZXRSb29tSWQoKSA9PT0gdGhpcy5wcm9wcy5yb29tLnJvb21JZCxcbiAgICAgICAgICAgIG5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb246IG51bGwsXG4gICAgICAgICAgICBnZW5lcmFsTWVudVBvc2l0aW9uOiBudWxsLFxuICAgICAgICAgICAgY2FsbDogQ2FsbFN0b3JlLmluc3RhbmNlLmdldENhbGwodGhpcy5wcm9wcy5yb29tLnJvb21JZCksXG4gICAgICAgICAgICAvLyBnZW5lcmF0ZVByZXZpZXcoKSB3aWxsIHJldHVybiBub3RoaW5nIGlmIHRoZSB1c2VyIGhhcyBwcmV2aWV3cyBkaXNhYmxlZFxuICAgICAgICAgICAgbWVzc2FnZVByZXZpZXc6IG51bGwsXG4gICAgICAgIH07XG4gICAgICAgIHRoaXMuZ2VuZXJhdGVQcmV2aWV3KCk7XG5cbiAgICAgICAgdGhpcy5ub3RpZmljYXRpb25TdGF0ZSA9IFJvb21Ob3RpZmljYXRpb25TdGF0ZVN0b3JlLmluc3RhbmNlLmdldFJvb21TdGF0ZSh0aGlzLnByb3BzLnJvb20pO1xuICAgICAgICB0aGlzLnJvb21Qcm9wcyA9IEVjaG9DaGFtYmVyLmZvclJvb20odGhpcy5wcm9wcy5yb29tKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIG9uUm9vbU5hbWVVcGRhdGUgPSAocm9vbTogUm9vbSk6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLmZvcmNlVXBkYXRlKCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25Ob3RpZmljYXRpb25VcGRhdGUgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuZm9yY2VVcGRhdGUoKTsgLy8gbm90aWZpY2F0aW9uIHN0YXRlIGNoYW5nZWQgLSB1cGRhdGVcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblJvb21Qcm9wZXJ0eVVwZGF0ZSA9IChwcm9wZXJ0eTogQ2FjaGVkUm9vbUtleSk6IHZvaWQgPT4ge1xuICAgICAgICBpZiAocHJvcGVydHkgPT09IENhY2hlZFJvb21LZXkuTm90aWZpY2F0aW9uVm9sdW1lKSB0aGlzLm9uTm90aWZpY2F0aW9uVXBkYXRlKCk7XG4gICAgICAgIC8vIGVsc2UgaWdub3JlIC0gbm90IGltcG9ydGFudCBmb3IgdGhpcyB0aWxlXG4gICAgfTtcblxuICAgIHByaXZhdGUgZ2V0IHNob3dDb250ZXh0TWVudSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIHRoaXMucHJvcHMudGFnICE9PSBEZWZhdWx0VGFnSUQuSW52aXRlICYmXG4gICAgICAgICAgICB0aGlzLnByb3BzLnJvb20uZ2V0TXlNZW1iZXJzaGlwKCkgIT09IEtub3duTWVtYmVyc2hpcC5Lbm9jayAmJlxuICAgICAgICAgICAgIWlzS25vY2tEZW5pZWQodGhpcy5wcm9wcy5yb29tKSAmJlxuICAgICAgICAgICAgc2hvdWxkU2hvd0NvbXBvbmVudChVSUNvbXBvbmVudC5Sb29tT3B0aW9uc01lbnUpXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXQgc2hvd01lc3NhZ2VQcmV2aWV3KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gIXRoaXMucHJvcHMuaXNNaW5pbWl6ZWQgJiYgdGhpcy5wcm9wcy5zaG93TWVzc2FnZVByZXZpZXc7XG4gICAgfVxuXG4gICAgcHVibGljIGNvbXBvbmVudERpZFVwZGF0ZShwcmV2UHJvcHM6IFJlYWRvbmx5PFByb3BzPiwgcHJldlN0YXRlOiBSZWFkb25seTxTdGF0ZT4pOiB2b2lkIHtcbiAgICAgICAgY29uc3Qgc2hvd01lc3NhZ2VDaGFuZ2VkID0gcHJldlByb3BzLnNob3dNZXNzYWdlUHJldmlldyAhPT0gdGhpcy5wcm9wcy5zaG93TWVzc2FnZVByZXZpZXc7XG4gICAgICAgIGNvbnN0IG1pbmltaXplZENoYW5nZWQgPSBwcmV2UHJvcHMuaXNNaW5pbWl6ZWQgIT09IHRoaXMucHJvcHMuaXNNaW5pbWl6ZWQ7XG4gICAgICAgIGlmIChzaG93TWVzc2FnZUNoYW5nZWQgfHwgbWluaW1pemVkQ2hhbmdlZCkge1xuICAgICAgICAgICAgdGhpcy5nZW5lcmF0ZVByZXZpZXcoKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAocHJldlByb3BzLnJvb20/LnJvb21JZCAhPT0gdGhpcy5wcm9wcy5yb29tPy5yb29tSWQpIHtcbiAgICAgICAgICAgIE1lc3NhZ2VQcmV2aWV3U3RvcmUuaW5zdGFuY2Uub2ZmKFxuICAgICAgICAgICAgICAgIE1lc3NhZ2VQcmV2aWV3U3RvcmUuZ2V0UHJldmlld0NoYW5nZWRFdmVudE5hbWUocHJldlByb3BzLnJvb20pLFxuICAgICAgICAgICAgICAgIHRoaXMub25Sb29tUHJldmlld0NoYW5nZWQsXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgTWVzc2FnZVByZXZpZXdTdG9yZS5pbnN0YW5jZS5vbihcbiAgICAgICAgICAgICAgICBNZXNzYWdlUHJldmlld1N0b3JlLmdldFByZXZpZXdDaGFuZ2VkRXZlbnROYW1lKHRoaXMucHJvcHMucm9vbSksXG4gICAgICAgICAgICAgICAgdGhpcy5vblJvb21QcmV2aWV3Q2hhbmdlZCxcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICBwcmV2UHJvcHMucm9vbT8ub2ZmKFJvb21FdmVudC5OYW1lLCB0aGlzLm9uUm9vbU5hbWVVcGRhdGUpO1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5yb29tPy5vbihSb29tRXZlbnQuTmFtZSwgdGhpcy5vblJvb21OYW1lVXBkYXRlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBjb21wb25lbnREaWRNb3VudCgpOiB2b2lkIHtcbiAgICAgICAgLy8gd2hlbiB3ZSdyZSBmaXJzdCByZW5kZXJlZCAob3Igb3VyIHN1Ymxpc3QgaXMgZXhwYW5kZWQpIG1ha2Ugc3VyZSB3ZSBhcmUgdmlzaWJsZSBpZiB3ZSdyZSBhY3RpdmVcbiAgICAgICAgaWYgKHRoaXMuc3RhdGUuc2VsZWN0ZWQpIHtcbiAgICAgICAgICAgIHRoaXMuc2Nyb2xsSW50b1ZpZXcoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIFNka0NvbnRleHRDbGFzcy5pbnN0YW5jZS5yb29tVmlld1N0b3JlLmFkZFJvb21MaXN0ZW5lcih0aGlzLnByb3BzLnJvb20ucm9vbUlkLCB0aGlzLm9uQWN0aXZlUm9vbVVwZGF0ZSk7XG4gICAgICAgIHRoaXMuZGlzcGF0Y2hlclJlZiA9IGRlZmF1bHREaXNwYXRjaGVyLnJlZ2lzdGVyKHRoaXMub25BY3Rpb24pO1xuICAgICAgICBNZXNzYWdlUHJldmlld1N0b3JlLmluc3RhbmNlLm9uKFxuICAgICAgICAgICAgTWVzc2FnZVByZXZpZXdTdG9yZS5nZXRQcmV2aWV3Q2hhbmdlZEV2ZW50TmFtZSh0aGlzLnByb3BzLnJvb20pLFxuICAgICAgICAgICAgdGhpcy5vblJvb21QcmV2aWV3Q2hhbmdlZCxcbiAgICAgICAgKTtcbiAgICAgICAgdGhpcy5ub3RpZmljYXRpb25TdGF0ZS5vbihOb3RpZmljYXRpb25TdGF0ZUV2ZW50cy5VcGRhdGUsIHRoaXMub25Ob3RpZmljYXRpb25VcGRhdGUpO1xuICAgICAgICB0aGlzLnJvb21Qcm9wcy5vbihQUk9QRVJUWV9VUERBVEVELCB0aGlzLm9uUm9vbVByb3BlcnR5VXBkYXRlKTtcbiAgICAgICAgdGhpcy5wcm9wcy5yb29tLm9uKFJvb21FdmVudC5OYW1lLCB0aGlzLm9uUm9vbU5hbWVVcGRhdGUpO1xuICAgICAgICBDYWxsU3RvcmUuaW5zdGFuY2Uub24oQ2FsbFN0b3JlRXZlbnQuQ2FsbCwgdGhpcy5vbkNhbGxDaGFuZ2VkKTtcblxuICAgICAgICAvLyBSZWNhbGN1bGF0ZSB0aGUgY2FsbCBmb3IgdGhpcyByb29tLCBzaW5jZSBpdCBjb3VsZCd2ZSBjaGFuZ2VkIGJldHdlZW5cbiAgICAgICAgLy8gY29uc3RydWN0aW9uIGFuZCBtb3VudGluZ1xuICAgICAgICB0aGlzLnNldFN0YXRlKHsgY2FsbDogQ2FsbFN0b3JlLmluc3RhbmNlLmdldENhbGwodGhpcy5wcm9wcy5yb29tLnJvb21JZCkgfSk7XG4gICAgfVxuXG4gICAgcHVibGljIGNvbXBvbmVudFdpbGxVbm1vdW50KCk6IHZvaWQge1xuICAgICAgICBTZGtDb250ZXh0Q2xhc3MuaW5zdGFuY2Uucm9vbVZpZXdTdG9yZS5yZW1vdmVSb29tTGlzdGVuZXIodGhpcy5wcm9wcy5yb29tLnJvb21JZCwgdGhpcy5vbkFjdGl2ZVJvb21VcGRhdGUpO1xuICAgICAgICBNZXNzYWdlUHJldmlld1N0b3JlLmluc3RhbmNlLm9mZihcbiAgICAgICAgICAgIE1lc3NhZ2VQcmV2aWV3U3RvcmUuZ2V0UHJldmlld0NoYW5nZWRFdmVudE5hbWUodGhpcy5wcm9wcy5yb29tKSxcbiAgICAgICAgICAgIHRoaXMub25Sb29tUHJldmlld0NoYW5nZWQsXG4gICAgICAgICk7XG4gICAgICAgIHRoaXMucHJvcHMucm9vbS5vZmYoUm9vbUV2ZW50Lk5hbWUsIHRoaXMub25Sb29tTmFtZVVwZGF0ZSk7XG4gICAgICAgIGlmICh0aGlzLmRpc3BhdGNoZXJSZWYpIGRlZmF1bHREaXNwYXRjaGVyLnVucmVnaXN0ZXIodGhpcy5kaXNwYXRjaGVyUmVmKTtcbiAgICAgICAgdGhpcy5ub3RpZmljYXRpb25TdGF0ZS5vZmYoTm90aWZpY2F0aW9uU3RhdGVFdmVudHMuVXBkYXRlLCB0aGlzLm9uTm90aWZpY2F0aW9uVXBkYXRlKTtcbiAgICAgICAgdGhpcy5yb29tUHJvcHMub2ZmKFBST1BFUlRZX1VQREFURUQsIHRoaXMub25Sb29tUHJvcGVydHlVcGRhdGUpO1xuICAgICAgICBDYWxsU3RvcmUuaW5zdGFuY2Uub2ZmKENhbGxTdG9yZUV2ZW50LkNhbGwsIHRoaXMub25DYWxsQ2hhbmdlZCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvbkFjdGlvbiA9IChwYXlsb2FkOiBBY3Rpb25QYXlsb2FkKTogdm9pZCA9PiB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIHBheWxvYWQuYWN0aW9uID09PSBBY3Rpb24uVmlld1Jvb20gJiZcbiAgICAgICAgICAgIHBheWxvYWQucm9vbV9pZCA9PT0gdGhpcy5wcm9wcy5yb29tLnJvb21JZCAmJlxuICAgICAgICAgICAgcGF5bG9hZC5zaG93X3Jvb21fdGlsZVxuICAgICAgICApIHtcbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMuc2Nyb2xsSW50b1ZpZXcoKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25Sb29tUHJldmlld0NoYW5nZWQgPSAocm9vbTogUm9vbSk6IHZvaWQgPT4ge1xuICAgICAgICBpZiAodGhpcy5wcm9wcy5yb29tICYmIHJvb20ucm9vbUlkID09PSB0aGlzLnByb3BzLnJvb20ucm9vbUlkKSB7XG4gICAgICAgICAgICB0aGlzLmdlbmVyYXRlUHJldmlldygpO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25DYWxsQ2hhbmdlZCA9IChjYWxsOiBDYWxsLCByb29tSWQ6IHN0cmluZyk6IHZvaWQgPT4ge1xuICAgICAgICBpZiAocm9vbUlkID09PSB0aGlzLnByb3BzLnJvb20/LnJvb21JZCkgdGhpcy5zZXRTdGF0ZSh7IGNhbGwgfSk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgYXN5bmMgZ2VuZXJhdGVQcmV2aWV3KCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAoIXRoaXMuc2hvd01lc3NhZ2VQcmV2aWV3KSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCBtZXNzYWdlUHJldmlldyA9XG4gICAgICAgICAgICAoYXdhaXQgTWVzc2FnZVByZXZpZXdTdG9yZS5pbnN0YW5jZS5nZXRQcmV2aWV3Rm9yUm9vbSh0aGlzLnByb3BzLnJvb20sIHRoaXMucHJvcHMudGFnKSkgPz8gbnVsbDtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IG1lc3NhZ2VQcmV2aWV3IH0pO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2Nyb2xsSW50b1ZpZXcgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGlmICghdGhpcy5yb29tVGlsZVJlZi5jdXJyZW50KSByZXR1cm47XG4gICAgICAgIHRoaXMucm9vbVRpbGVSZWYuY3VycmVudC5zY3JvbGxJbnRvVmlldyh7XG4gICAgICAgICAgICBibG9jazogXCJuZWFyZXN0XCIsXG4gICAgICAgICAgICBiZWhhdmlvcjogXCJhdXRvXCIsXG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uVGlsZUNsaWNrID0gYXN5bmMgKGV2OiBCdXR0b25FdmVudCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBldi5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTtcblxuICAgICAgICBjb25zdCBhY3Rpb24gPSBnZXRLZXlCaW5kaW5nc01hbmFnZXIoKS5nZXRBY2Nlc3NpYmlsaXR5QWN0aW9uKGV2IGFzIFJlYWN0LktleWJvYXJkRXZlbnQpO1xuICAgICAgICBjb25zdCBjbGVhclNlYXJjaCA9IChbS2V5QmluZGluZ0FjdGlvbi5FbnRlciwgS2V5QmluZGluZ0FjdGlvbi5TcGFjZV0gYXMgQXJyYXk8c3RyaW5nIHwgdW5kZWZpbmVkPikuaW5jbHVkZXMoXG4gICAgICAgICAgICBhY3Rpb24sXG4gICAgICAgICk7XG5cbiAgICAgICAgZGVmYXVsdERpc3BhdGNoZXIuZGlzcGF0Y2g8Vmlld1Jvb21QYXlsb2FkPih7XG4gICAgICAgICAgICBhY3Rpb246IEFjdGlvbi5WaWV3Um9vbSxcbiAgICAgICAgICAgIHNob3dfcm9vbV90aWxlOiB0cnVlLCAvLyBtYWtlIHN1cmUgdGhlIHJvb20gaXMgdmlzaWJsZSBpbiB0aGUgbGlzdFxuICAgICAgICAgICAgcm9vbV9pZDogdGhpcy5wcm9wcy5yb29tLnJvb21JZCxcbiAgICAgICAgICAgIGNsZWFyX3NlYXJjaDogY2xlYXJTZWFyY2gsXG4gICAgICAgICAgICBtZXRyaWNzVHJpZ2dlcjogXCJSb29tTGlzdFwiLFxuICAgICAgICAgICAgbWV0cmljc1ZpYUtleWJvYXJkOiBldi50eXBlICE9PSBcImNsaWNrXCIsXG4gICAgICAgIH0pO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uQWN0aXZlUm9vbVVwZGF0ZSA9IChpc0FjdGl2ZTogYm9vbGVhbik6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLnNldFN0YXRlKHsgc2VsZWN0ZWQ6IGlzQWN0aXZlIH0pO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uTm90aWZpY2F0aW9uc01lbnVPcGVuQ2xpY2sgPSAoZXY6IEJ1dHRvbkV2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIGV2LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIGV2LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICBjb25zdCB0YXJnZXQgPSBldi50YXJnZXQgYXMgSFRNTEJ1dHRvbkVsZW1lbnQ7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyBub3RpZmljYXRpb25zTWVudVBvc2l0aW9uOiB0YXJnZXQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCkgfSk7XG5cbiAgICAgICAgUG9zdGhvZ1RyYWNrZXJzLnRyYWNrSW50ZXJhY3Rpb24oXCJXZWJSb29tTGlzdFJvb21UaWxlTm90aWZpY2F0aW9uc01lbnVcIiwgZXYpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uQ2xvc2VOb3RpZmljYXRpb25zTWVudSA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IG5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb246IG51bGwgfSk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25HZW5lcmFsTWVudU9wZW5DbGljayA9IChldjogQnV0dG9uRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgZXYucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgZXYuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgIGNvbnN0IHRhcmdldCA9IGV2LnRhcmdldCBhcyBIVE1MQnV0dG9uRWxlbWVudDtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IGdlbmVyYWxNZW51UG9zaXRpb246IHRhcmdldC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSB9KTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkNvbnRleHRNZW51ID0gKGV2OiBSZWFjdC5Nb3VzZUV2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIC8vIElmIHdlIGRvbid0IGhhdmUgYSBjb250ZXh0IG1lbnUgdG8gc2hvdywgaWdub3JlIHRoZSBhY3Rpb24uXG4gICAgICAgIGlmICghdGhpcy5zaG93Q29udGV4dE1lbnUpIHJldHVybjtcblxuICAgICAgICBldi5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICBnZW5lcmFsTWVudVBvc2l0aW9uOiB7XG4gICAgICAgICAgICAgICAgbGVmdDogZXYuY2xpZW50WCxcbiAgICAgICAgICAgICAgICBib3R0b206IGV2LmNsaWVudFksXG4gICAgICAgICAgICB9LFxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkNsb3NlR2VuZXJhbE1lbnUgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyBnZW5lcmFsTWVudVBvc2l0aW9uOiBudWxsIH0pO1xuICAgIH07XG5cbiAgICBwcml2YXRlIHJlbmRlck5vdGlmaWNhdGlvbnNNZW51KGlzQWN0aXZlOiBib29sZWFuKTogUmVhY3QuUmVhY3RFbGVtZW50IHwgbnVsbCB7XG4gICAgICAgIGlmIChcbiAgICAgICAgICAgIE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCkuaXNHdWVzdCgpIHx8XG4gICAgICAgICAgICB0aGlzLnByb3BzLnRhZyA9PT0gRGVmYXVsdFRhZ0lELkFyY2hpdmVkIHx8XG4gICAgICAgICAgICAhdGhpcy5zaG93Q29udGV4dE1lbnUgfHxcbiAgICAgICAgICAgIHRoaXMucHJvcHMuaXNNaW5pbWl6ZWRcbiAgICAgICAgKSB7XG4gICAgICAgICAgICAvLyB0aGUgbWVudSBtYWtlcyBubyBzZW5zZSBpbiB0aGVzZSBjYXNlcyBzbyBkbyBub3Qgc2hvdyBvbmVcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgc3RhdGUgPSB0aGlzLnJvb21Qcm9wcy5ub3RpZmljYXRpb25Wb2x1bWU7XG5cbiAgICAgICAgY29uc3QgY2xhc3NlcyA9IGNsYXNzTmFtZXMoXCJteF9Sb29tVGlsZV9ub3RpZmljYXRpb25zQnV0dG9uXCIsIHtcbiAgICAgICAgICAgIC8vIFNob3cgYmVsbCBpY29uIGZvciB0aGUgZGVmYXVsdCBjYXNlIHRvby5cbiAgICAgICAgICAgIG14X1Jvb21Ob3RpZmljYXRpb25Db250ZXh0TWVudV9pY29uQmVsbDogc3RhdGUgPT09IFJvb21Ob3RpZlN0YXRlLkFsbE1lc3NhZ2VzLFxuICAgICAgICAgICAgbXhfUm9vbU5vdGlmaWNhdGlvbkNvbnRleHRNZW51X2ljb25CZWxsRG90OiBzdGF0ZSA9PT0gUm9vbU5vdGlmU3RhdGUuQWxsTWVzc2FnZXNMb3VkLFxuICAgICAgICAgICAgbXhfUm9vbU5vdGlmaWNhdGlvbkNvbnRleHRNZW51X2ljb25CZWxsTWVudGlvbnM6IHN0YXRlID09PSBSb29tTm90aWZTdGF0ZS5NZW50aW9uc09ubHksXG4gICAgICAgICAgICBteF9Sb29tTm90aWZpY2F0aW9uQ29udGV4dE1lbnVfaWNvbkJlbGxDcm9zc2VkOiBzdGF0ZSA9PT0gUm9vbU5vdGlmU3RhdGUuTXV0ZSxcblxuICAgICAgICAgICAgLy8gT25seSBzaG93IHRoZSBpY29uIGJ5IGRlZmF1bHQgaWYgdGhlIHJvb20gaXMgb3ZlcnJpZGRlbiB0byBtdXRlZC5cbiAgICAgICAgICAgIC8vIFRPRE86IFtGVFVFIE5vdGlmaWNhdGlvbnNdIFByb2JhYmx5IG5lZWQgdG8gZGV0ZWN0IGdsb2JhbCBtdXRlIHN0YXRlXG4gICAgICAgICAgICBteF9Sb29tVGlsZV9ub3RpZmljYXRpb25zQnV0dG9uX3Nob3c6IHN0YXRlID09PSBSb29tTm90aWZTdGF0ZS5NdXRlLFxuICAgICAgICB9KTtcblxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPFJlYWN0LkZyYWdtZW50PlxuICAgICAgICAgICAgICAgIDxDb250ZXh0TWVudVRvb2x0aXBCdXR0b25cbiAgICAgICAgICAgICAgICAgICAgY2xhc3NOYW1lPXtjbGFzc2VzfVxuICAgICAgICAgICAgICAgICAgICBvbkNsaWNrPXt0aGlzLm9uTm90aWZpY2F0aW9uc01lbnVPcGVuQ2xpY2t9XG4gICAgICAgICAgICAgICAgICAgIHRpdGxlPXtfdChcInJvb21fbGlzdHxub3RpZmljYXRpb25fb3B0aW9uc1wiKX1cbiAgICAgICAgICAgICAgICAgICAgaXNFeHBhbmRlZD17ISF0aGlzLnN0YXRlLm5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb259XG4gICAgICAgICAgICAgICAgICAgIHRhYkluZGV4PXtpc0FjdGl2ZSA/IDAgOiAtMX1cbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgICAgIHt0aGlzLnN0YXRlLm5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb24gJiYgKFxuICAgICAgICAgICAgICAgICAgICA8Um9vbU5vdGlmaWNhdGlvbkNvbnRleHRNZW51XG4gICAgICAgICAgICAgICAgICAgICAgICB7Li4uY29udGV4dE1lbnVCZWxvdyh0aGlzLnN0YXRlLm5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb24pfVxuICAgICAgICAgICAgICAgICAgICAgICAgb25GaW5pc2hlZD17dGhpcy5vbkNsb3NlTm90aWZpY2F0aW9uc01lbnV9XG4gICAgICAgICAgICAgICAgICAgICAgICByb29tPXt0aGlzLnByb3BzLnJvb219XG4gICAgICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAgKX1cbiAgICAgICAgICAgIDwvUmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZW5kZXJHZW5lcmFsTWVudSgpOiBSZWFjdC5SZWFjdEVsZW1lbnQgfCBudWxsIHtcbiAgICAgICAgaWYgKCF0aGlzLnNob3dDb250ZXh0TWVudSkgcmV0dXJuIG51bGw7IC8vIG5vIG1lbnUgdG8gc2hvd1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPFJlYWN0LkZyYWdtZW50PlxuICAgICAgICAgICAgICAgIDxDb250ZXh0TWVudVRvb2x0aXBCdXR0b25cbiAgICAgICAgICAgICAgICAgICAgY2xhc3NOYW1lPVwibXhfUm9vbVRpbGVfbWVudUJ1dHRvblwiXG4gICAgICAgICAgICAgICAgICAgIG9uQ2xpY2s9e3RoaXMub25HZW5lcmFsTWVudU9wZW5DbGlja31cbiAgICAgICAgICAgICAgICAgICAgdGl0bGU9e190KFwicm9vbXxjb250ZXh0X21lbnV8dGl0bGVcIil9XG4gICAgICAgICAgICAgICAgICAgIGlzRXhwYW5kZWQ9eyEhdGhpcy5zdGF0ZS5nZW5lcmFsTWVudVBvc2l0aW9ufVxuICAgICAgICAgICAgICAgIC8+XG4gICAgICAgICAgICAgICAge3RoaXMuc3RhdGUuZ2VuZXJhbE1lbnVQb3NpdGlvbiAmJiAoXG4gICAgICAgICAgICAgICAgICAgIDxSb29tR2VuZXJhbENvbnRleHRNZW51XG4gICAgICAgICAgICAgICAgICAgICAgICB7Li4uY29udGV4dE1lbnVCZWxvdyh0aGlzLnN0YXRlLmdlbmVyYWxNZW51UG9zaXRpb24pfVxuICAgICAgICAgICAgICAgICAgICAgICAgb25GaW5pc2hlZD17dGhpcy5vbkNsb3NlR2VuZXJhbE1lbnV9XG4gICAgICAgICAgICAgICAgICAgICAgICByb29tPXt0aGlzLnByb3BzLnJvb219XG4gICAgICAgICAgICAgICAgICAgICAgICBvblBvc3RGYXZvcml0ZUNsaWNrPXsoZXY6IEJ1dHRvbkV2ZW50KSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc3Rob2dUcmFja2Vycy50cmFja0ludGVyYWN0aW9uKFwiV2ViUm9vbUxpc3RSb29tVGlsZUNvbnRleHRNZW51RmF2b3VyaXRlVG9nZ2xlXCIsIGV2KVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgb25Qb3N0SW52aXRlQ2xpY2s9eyhldjogQnV0dG9uRXZlbnQpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zdGhvZ1RyYWNrZXJzLnRyYWNrSW50ZXJhY3Rpb24oXCJXZWJSb29tTGlzdFJvb21UaWxlQ29udGV4dE1lbnVJbnZpdGVJdGVtXCIsIGV2KVxuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgb25Qb3N0U2V0dGluZ3NDbGljaz17KGV2OiBCdXR0b25FdmVudCkgPT5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBQb3N0aG9nVHJhY2tlcnMudHJhY2tJbnRlcmFjdGlvbihcIldlYlJvb21MaXN0Um9vbVRpbGVDb250ZXh0TWVudVNldHRpbmdzSXRlbVwiLCBldilcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIG9uUG9zdExlYXZlQ2xpY2s9eyhldjogQnV0dG9uRXZlbnQpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zdGhvZ1RyYWNrZXJzLnRyYWNrSW50ZXJhY3Rpb24oXCJXZWJSb29tTGlzdFJvb21UaWxlQ29udGV4dE1lbnVMZWF2ZUl0ZW1cIiwgZXYpXG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBvblBvc3RNYXJrQXNSZWFkQ2xpY2s9eyhldjogQnV0dG9uRXZlbnQpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgUG9zdGhvZ1RyYWNrZXJzLnRyYWNrSW50ZXJhY3Rpb24oXCJXZWJSb29tTGlzdFJvb21UaWxlQ29udGV4dE1lbnVNYXJrUmVhZFwiLCBldilcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIG9uUG9zdE1hcmtBc1VucmVhZENsaWNrPXsoZXY6IEJ1dHRvbkV2ZW50KSA9PlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBvc3Rob2dUcmFja2Vycy50cmFja0ludGVyYWN0aW9uKFwiV2ViUm9vbUxpc3RSb29tVGlsZUNvbnRleHRNZW51TWFya1VucmVhZFwiLCBldilcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICApfVxuICAgICAgICAgICAgPC9SZWFjdC5GcmFnbWVudD5cbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSb29tVGlsZSBoYXMgYSBzdWJ0aWxlIGlmIG9uZSBvZiB0aGUgZm9sbG93aW5nIGFwcGxpZXM6XG4gICAgICogLSB0aGVyZSBpcyBhIGNhbGxcbiAgICAgKiAtIHRoZXJlIGlzIGEgbGl2ZSB2b2ljZSBicm9hZGNhc3RcbiAgICAgKiAtIG1lc3NhZ2UgcHJldmlld3MgYXJlIGVuYWJsZWQgYW5kIHRoZXJlIGlzIGEgcHJldmlld2FibGUgbWVzc2FnZVxuICAgICAqL1xuICAgIHByaXZhdGUgZ2V0IHNob3VsZFJlbmRlclN1YnRpdGxlKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgISF0aGlzLnN0YXRlLmNhbGwgfHxcbiAgICAgICAgICAgIHRoaXMucHJvcHMuaGFzTGl2ZVZvaWNlQnJvYWRjYXN0IHx8XG4gICAgICAgICAgICAodGhpcy5wcm9wcy5zaG93TWVzc2FnZVByZXZpZXcgJiYgISF0aGlzLnN0YXRlLm1lc3NhZ2VQcmV2aWV3KVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyByZW5kZXIoKTogUmVhY3QuUmVhY3RFbGVtZW50IHtcbiAgICAgICAgY29uc3QgY2xhc3NlcyA9IGNsYXNzTmFtZXMoe1xuICAgICAgICAgICAgbXhfUm9vbVRpbGU6IHRydWUsXG4gICAgICAgICAgICBteF9Sb29tVGlsZV9zdGlja3k6XG4gICAgICAgICAgICAgICAgU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImZlYXR1cmVfYXNrX3RvX2pvaW5cIikgJiZcbiAgICAgICAgICAgICAgICAodGhpcy5wcm9wcy5yb29tLmdldE15TWVtYmVyc2hpcCgpID09PSBLbm93bk1lbWJlcnNoaXAuS25vY2sgfHwgaXNLbm9ja0RlbmllZCh0aGlzLnByb3BzLnJvb20pKSxcbiAgICAgICAgICAgIG14X1Jvb21UaWxlX3NlbGVjdGVkOiB0aGlzLnN0YXRlLnNlbGVjdGVkLFxuICAgICAgICAgICAgbXhfUm9vbVRpbGVfaGFzTWVudU9wZW46ICEhKHRoaXMuc3RhdGUuZ2VuZXJhbE1lbnVQb3NpdGlvbiB8fCB0aGlzLnN0YXRlLm5vdGlmaWNhdGlvbnNNZW51UG9zaXRpb24pLFxuICAgICAgICAgICAgbXhfUm9vbVRpbGVfbWluaW1pemVkOiB0aGlzLnByb3BzLmlzTWluaW1pemVkLFxuICAgICAgICB9KTtcblxuICAgICAgICBsZXQgbmFtZSA9IHRoaXMucHJvcHMucm9vbS5uYW1lO1xuICAgICAgICBpZiAodHlwZW9mIG5hbWUgIT09IFwic3RyaW5nXCIpIG5hbWUgPSBcIlwiO1xuICAgICAgICBuYW1lID0gbmFtZS5yZXBsYWNlKFwiOlwiLCBcIjpcXHUyMDBiXCIpOyAvLyBhZGQgYSB6ZXJvLXdpZHRoIHNwYWNlIHRvIGFsbG93IGxpbmV3cmFwcGluZyBhZnRlciB0aGUgY29sb25cblxuICAgICAgICBsZXQgYmFkZ2U6IFJlYWN0LlJlYWN0Tm9kZTtcbiAgICAgICAgaWYgKCF0aGlzLnByb3BzLmlzTWluaW1pemVkICYmIHRoaXMubm90aWZpY2F0aW9uU3RhdGUpIHtcbiAgICAgICAgICAgIC8vIGFyaWEtaGlkZGVuIGJlY2F1c2Ugd2Ugc3VtbWFyaXNlIHRoZSB1bnJlYWQgY291bnQvaGlnaGxpZ2h0IHN0YXR1cyBpbiBhIG1hbnVhbCBhcmlhLWxhYmVsIGJlbG93XG4gICAgICAgICAgICBiYWRnZSA9IChcbiAgICAgICAgICAgICAgICA8