UNPKG

matrix-react-sdk

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