UNPKG

matrix-react-sdk

Version:
390 lines (384 loc) 76.8 kB
"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