UNPKG

matrix-react-sdk

Version:
569 lines (552 loc) 85 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ChevronFace = void 0; Object.defineProperty(exports, "ContextMenuButton", { enumerable: true, get: function () { return _ContextMenuButton.ContextMenuButton; } }); Object.defineProperty(exports, "ContextMenuTooltipButton", { enumerable: true, get: function () { return _ContextMenuTooltipButton.ContextMenuTooltipButton; } }); Object.defineProperty(exports, "MenuItem", { enumerable: true, get: function () { return _MenuItem.MenuItem; } }); Object.defineProperty(exports, "MenuItemCheckbox", { enumerable: true, get: function () { return _MenuItemCheckbox.MenuItemCheckbox; } }); Object.defineProperty(exports, "MenuItemRadio", { enumerable: true, get: function () { return _MenuItemRadio.MenuItemRadio; } }); Object.defineProperty(exports, "StyledMenuItemCheckbox", { enumerable: true, get: function () { return _StyledMenuItemCheckbox.StyledMenuItemCheckbox; } }); Object.defineProperty(exports, "StyledMenuItemRadio", { enumerable: true, get: function () { return _StyledMenuItemRadio.StyledMenuItemRadio; } }); exports.alwaysMenuProps = exports.alwaysAboveRightOf = exports.aboveRightOf = exports.aboveLeftOf = void 0; exports.createMenu = createMenu; exports.useContextMenu = exports.toRightOf = exports.toLeftOrRightOf = exports.toLeftOf = 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 _reactDom = _interopRequireDefault(require("react-dom")); var _classnames = _interopRequireDefault(require("classnames")); var _reactFocusLock = _interopRequireDefault(require("react-focus-lock")); var _compoundWeb = require("@vector-im/compound-web"); var _UIStore = _interopRequireDefault(require("../../stores/UIStore")); var _RovingTabIndex = require("../../accessibility/RovingTabIndex"); var _KeyboardShortcuts = require("../../accessibility/KeyboardShortcuts"); var _KeyBindingsManager = require("../../KeyBindingsManager"); var _Modal = _interopRequireWildcard(require("../../Modal")); var _ContextMenuButton = require("../../accessibility/context_menu/ContextMenuButton"); var _ContextMenuTooltipButton = require("../../accessibility/context_menu/ContextMenuTooltipButton"); var _MenuItem = require("../../accessibility/context_menu/MenuItem"); var _MenuItemCheckbox = require("../../accessibility/context_menu/MenuItemCheckbox"); var _MenuItemRadio = require("../../accessibility/context_menu/MenuItemRadio"); var _StyledMenuItemCheckbox = require("../../accessibility/context_menu/StyledMenuItemCheckbox"); var _StyledMenuItemRadio = require("../../accessibility/context_menu/StyledMenuItemRadio"); const _excluded = ["top", "bottom", "left", "right", "bottomAligned", "rightAligned", "menuClassName", "menuHeight", "menuWidth", "menuPaddingLeft", "menuPaddingRight", "menuPaddingBottom", "menuPaddingTop", "zIndex", "children", "focusLock", "managed", "wrapperClassName", "chevronFace", "chevronOffset", "mountAsChild"], _excluded2 = ["hasBackground", "onFinished"]; /* Copyright 2024 New Vector Ltd. Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2018 New Vector Ltd Copyright 2015, 2016 OpenMarket Ltd 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; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } // Shamelessly ripped off Modal.js. There's probably a better way // of doing reusable widgets like dialog boxes & menus where we go and // pass in a custom control as the actual body. const WINDOW_PADDING = 10; const ContextualMenuContainerId = "mx_ContextualMenu_Container"; function getOrCreateContainer() { let container = document.getElementById(ContextualMenuContainerId); if (!container) { container = document.createElement("div"); container.id = ContextualMenuContainerId; document.body.appendChild(container); } return container; } let ChevronFace = exports.ChevronFace = /*#__PURE__*/function (ChevronFace) { ChevronFace["Top"] = "top"; ChevronFace["Bottom"] = "bottom"; ChevronFace["Left"] = "left"; ChevronFace["Right"] = "right"; ChevronFace["None"] = "none"; return ChevronFace; }({}); // Generic ContextMenu Portal wrapper // all options inside the menu should be of role=menuitem/menuitemcheckbox/menuitemradiobutton and have tabIndex={-1} // this will allow the ContextMenu to manage its own focus using arrow keys as per the ARIA guidelines. class ContextMenu extends _react.default.PureComponent { constructor(props) { super(props); (0, _defineProperty2.default)(this, "initialFocus", void 0); (0, _defineProperty2.default)(this, "onModalOpen", () => { this.props.onFinished?.(); }); (0, _defineProperty2.default)(this, "collectContextMenuRect", element => { // We don't need to clean up when unmounting, so ignore if (!element) return; const first = element.querySelector('[role^="menuitem"]') || element.querySelector("[tabindex]"); if (first) { first.focus(); } this.setState({ contextMenuElem: element }); }); (0, _defineProperty2.default)(this, "onContextMenu", e => { if (this.props.onFinished) { this.props.onFinished(); e.preventDefault(); e.stopPropagation(); const x = e.clientX; const y = e.clientY; // XXX: This isn't pretty but the only way to allow opening a different context menu on right click whilst // a context menu and its click-guard are up without completely rewriting how the context menus work. setTimeout(() => { const clickEvent = new MouseEvent("contextmenu", { clientX: x, clientY: y, screenX: 0, screenY: 0, button: 0, // Left relatedTarget: null }); document.elementFromPoint(x, y)?.dispatchEvent(clickEvent); }, 0); } }); (0, _defineProperty2.default)(this, "onContextMenuPreventBubbling", e => { // stop propagation so that any context menu handlers don't leak out of this context menu // but do not inhibit the default browser menu e.stopPropagation(); }); // Prevent clicks on the background from going through to the component which opened the menu. (0, _defineProperty2.default)(this, "onFinished", ev => { ev.stopPropagation(); ev.preventDefault(); this.props.onFinished?.(); }); (0, _defineProperty2.default)(this, "onClick", ev => { // Don't allow clicks to escape the context menu wrapper ev.stopPropagation(); if (this.props.closeOnInteraction) { this.props.onFinished?.(); } }); // We now only handle closing the ContextMenu in this keyDown handler. // All of the item/option navigation is delegated to RovingTabIndex. (0, _defineProperty2.default)(this, "onKeyDown", ev => { ev.stopPropagation(); // prevent keyboard propagating out of the context menu, we're focus-locked const action = (0, _KeyBindingsManager.getKeyBindingsManager)().getAccessibilityAction(ev); // If someone is managing their own focus, we will only exit for them with Escape. // They are probably using props.focusLock along with this option as well. if (!this.props.managed) { if (action === _KeyboardShortcuts.KeyBindingAction.Escape) { this.props.onFinished(); } return; } // When an <input> is focused, only handle the Escape key if ((0, _RovingTabIndex.checkInputableElement)(ev.target) && action !== _KeyboardShortcuts.KeyBindingAction.Escape) { return; } if ([_KeyboardShortcuts.KeyBindingAction.Escape, // You can only navigate the ContextMenu by arrow keys and Home/End (see RovingTabIndex). // Tabbing to the next section of the page, will close the ContextMenu. _KeyboardShortcuts.KeyBindingAction.Tab, // When someone moves left or right along a <Toolbar /> (like the // MessageActionBar), we should close any ContextMenu that is open. _KeyboardShortcuts.KeyBindingAction.ArrowLeft, _KeyboardShortcuts.KeyBindingAction.ArrowRight].includes(action)) { this.props.onFinished(); } }); this.state = {}; // persist what had focus when we got initialized so we can return it after this.initialFocus = document.activeElement; } componentDidMount() { _Modal.default.on(_Modal.ModalManagerEvent.Opened, this.onModalOpen); } componentWillUnmount() { _Modal.default.off(_Modal.ModalManagerEvent.Opened, this.onModalOpen); // return focus to the thing which had it before us this.initialFocus.focus(); } renderMenu(hasBackground = this.props.hasBackground) { const position = {}; const _this$props = this.props, { top, bottom, left, right, bottomAligned, rightAligned, menuClassName, menuHeight, menuWidth, menuPaddingLeft, menuPaddingRight, menuPaddingBottom, menuPaddingTop, zIndex, children, focusLock, managed, wrapperClassName, chevronFace: propsChevronFace, chevronOffset: propsChevronOffset, mountAsChild } = _this$props, props = (0, _objectWithoutProperties2.default)(_this$props, _excluded); if (top) { position.top = top; } else { position.bottom = bottom; } let chevronFace; if (left) { position.left = left; chevronFace = ChevronFace.Left; } else { position.right = right; chevronFace = ChevronFace.Right; } const contextMenuRect = this.state.contextMenuElem ? this.state.contextMenuElem.getBoundingClientRect() : null; const chevronOffset = {}; if (propsChevronFace) { chevronFace = propsChevronFace; } const hasChevron = chevronFace && chevronFace !== ChevronFace.None; if (chevronFace === ChevronFace.Top || chevronFace === ChevronFace.Bottom) { chevronOffset.left = propsChevronOffset; } else { chevronOffset.top = propsChevronOffset; } // If we know the dimensions of the context menu, adjust its position to // keep it within the bounds of the (padded) window const { windowWidth, windowHeight } = _UIStore.default.instance; if (contextMenuRect) { if (position.top !== undefined) { let maxTop = windowHeight - WINDOW_PADDING; if (!bottomAligned) { maxTop -= contextMenuRect.height; } position.top = Math.min(position.top, maxTop); // Adjust the chevron if necessary if (chevronOffset.top !== undefined) { chevronOffset.top = propsChevronOffset + top - position.top; } } else if (position.bottom !== undefined) { position.bottom = Math.min(position.bottom, windowHeight - contextMenuRect.height - WINDOW_PADDING); if (chevronOffset.top !== undefined) { chevronOffset.top = propsChevronOffset + position.bottom - bottom; } } if (position.left !== undefined) { let maxLeft = windowWidth - WINDOW_PADDING; if (!rightAligned) { maxLeft -= contextMenuRect.width; } position.left = Math.min(position.left, maxLeft); if (chevronOffset.left !== undefined) { chevronOffset.left = propsChevronOffset + left - position.left; } } else if (position.right !== undefined) { position.right = Math.min(position.right, windowWidth - contextMenuRect.width - WINDOW_PADDING); if (chevronOffset.left !== undefined) { chevronOffset.left = propsChevronOffset + position.right - right; } } } let chevron; if (hasChevron) { chevron = /*#__PURE__*/_react.default.createElement("div", { style: chevronOffset, className: "mx_ContextualMenu_chevron_" + chevronFace }); } const menuClasses = (0, _classnames.default)({ mx_ContextualMenu: true, /** * In some cases we may get the number of 0, which still means that we're supposed to properly * add the specific position class, but as it was falsy things didn't work as intended. * In addition, defensively check for counter cases where we may get more than one value, * even if we shouldn't. */ mx_ContextualMenu_left: !hasChevron && position.left !== undefined && !position.right, mx_ContextualMenu_right: !hasChevron && position.right !== undefined && !position.left, mx_ContextualMenu_top: !hasChevron && position.top !== undefined && !position.bottom, mx_ContextualMenu_bottom: !hasChevron && position.bottom !== undefined && !position.top, mx_ContextualMenu_withChevron_left: chevronFace === ChevronFace.Left, mx_ContextualMenu_withChevron_right: chevronFace === ChevronFace.Right, mx_ContextualMenu_withChevron_top: chevronFace === ChevronFace.Top, mx_ContextualMenu_withChevron_bottom: chevronFace === ChevronFace.Bottom, mx_ContextualMenu_rightAligned: rightAligned === true, mx_ContextualMenu_bottomAligned: bottomAligned === true }, menuClassName); const menuStyle = {}; if (menuWidth) { menuStyle.width = menuWidth; } if (menuHeight) { menuStyle.height = menuHeight; } if (!isNaN(Number(menuPaddingTop))) { menuStyle["paddingTop"] = menuPaddingTop; } if (!isNaN(Number(menuPaddingLeft))) { menuStyle["paddingLeft"] = menuPaddingLeft; } if (!isNaN(Number(menuPaddingBottom))) { menuStyle["paddingBottom"] = menuPaddingBottom; } if (!isNaN(Number(menuPaddingRight))) { menuStyle["paddingRight"] = menuPaddingRight; } const wrapperStyle = {}; if (!isNaN(Number(zIndex))) { menuStyle["zIndex"] = zIndex + 1; wrapperStyle["zIndex"] = zIndex; } let background; if (hasBackground) { background = /*#__PURE__*/_react.default.createElement("div", { className: "mx_ContextualMenu_background", style: wrapperStyle, onClick: this.onFinished, onContextMenu: this.onContextMenu }); } let body = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, chevron, children); if (focusLock) { body = /*#__PURE__*/_react.default.createElement(_reactFocusLock.default, null, body); } // filter props that are invalid for DOM elements const { hasBackground: _hasBackground, // eslint-disable-line @typescript-eslint/no-unused-vars onFinished: _onFinished // eslint-disable-line @typescript-eslint/no-unused-vars } = props, divProps = (0, _objectWithoutProperties2.default)(props, _excluded2); return /*#__PURE__*/_react.default.createElement(_RovingTabIndex.RovingTabIndexProvider, { handleHomeEnd: true, handleUpDown: true, onKeyDown: this.onKeyDown }, ({ onKeyDownHandler }) => /*#__PURE__*/_react.default.createElement("div", { className: (0, _classnames.default)("mx_ContextualMenu_wrapper", wrapperClassName), style: _objectSpread(_objectSpread({}, position), wrapperStyle), onClick: this.onClick, onKeyDown: onKeyDownHandler, onContextMenu: this.onContextMenuPreventBubbling }, background, /*#__PURE__*/_react.default.createElement("div", (0, _extends2.default)({ className: menuClasses, style: menuStyle, ref: this.collectContextMenuRect, role: managed ? "menu" : undefined }, divProps), body))); } render() { if (this.props.mountAsChild) { // Render as a child of the current parent return this.renderMenu(); } else { // Render as a child of a container at the root of the DOM return /*#__PURE__*/_reactDom.default.createPortal(this.renderMenu(), getOrCreateContainer()); } } } exports.default = ContextMenu; (0, _defineProperty2.default)(ContextMenu, "defaultProps", { hasBackground: true, managed: true }); // Placement method for <ContextMenu /> to position context menu to right of elementRect with chevronOffset const toRightOf = (elementRect, chevronOffset = 12) => { const left = elementRect.right + window.scrollX + 3; let top = elementRect.top + elementRect.height / 2 + window.scrollY; top -= chevronOffset + 8; // where 8 is half the height of the chevron return { left, top, chevronOffset }; }; exports.toRightOf = toRightOf; // Placement method for <ContextMenu /> to position context menu to left of elementRect with chevronOffset const toLeftOf = (elementRect, chevronOffset = 12) => { const right = _UIStore.default.instance.windowWidth - elementRect.left + window.scrollX - 3; let top = elementRect.top + elementRect.height / 2 + window.scrollY; top -= chevronOffset + 8; // where 8 is half the height of the chevron return { right, top, chevronOffset }; }; /** * Placement method for <ContextMenu /> to position context menu of or right of elementRect * depending on which side has more space. */ exports.toLeftOf = toLeftOf; const toLeftOrRightOf = (elementRect, chevronOffset = 12) => { const spaceToTheLeft = elementRect.left; const spaceToTheRight = _UIStore.default.instance.windowWidth - elementRect.right; if (spaceToTheLeft > spaceToTheRight) { return toLeftOf(elementRect, chevronOffset); } return toRightOf(elementRect, chevronOffset); }; // Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect, // and either above or below: wherever there is more space (maybe this should be aboveOrBelowLeftOf?) exports.toLeftOrRightOf = toLeftOrRightOf; const aboveLeftOf = (elementRect, chevronFace = ChevronFace.None, vPadding = 0) => { const menuOptions = { chevronFace }; const buttonRight = elementRect.right + window.scrollX; const buttonBottom = elementRect.bottom + window.scrollY; const buttonTop = elementRect.top + window.scrollY; // Align the right edge of the menu to the right edge of the button menuOptions.right = _UIStore.default.instance.windowWidth - buttonRight; // Align the menu vertically on whichever side of the button has more space available. if (buttonBottom < _UIStore.default.instance.windowHeight / 2) { menuOptions.top = buttonBottom + vPadding; } else { menuOptions.bottom = _UIStore.default.instance.windowHeight - buttonTop + vPadding; } return menuOptions; }; // Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the right of elementRect, // and either above or below: wherever there is more space (maybe this should be aboveOrBelowRightOf?) exports.aboveLeftOf = aboveLeftOf; const aboveRightOf = (elementRect, chevronFace = ChevronFace.None, vPadding = 0) => { const menuOptions = { chevronFace }; const buttonLeft = elementRect.left + window.scrollX; const buttonBottom = elementRect.bottom + window.scrollY; const buttonTop = elementRect.top + window.scrollY; // Align the left edge of the menu to the left edge of the button menuOptions.left = buttonLeft; // Align the menu vertically on whichever side of the button has more space available. if (buttonBottom < _UIStore.default.instance.windowHeight / 2) { menuOptions.top = buttonBottom + vPadding; } else { menuOptions.bottom = _UIStore.default.instance.windowHeight - buttonTop + vPadding; } return menuOptions; }; // Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the left of elementRect // and always above elementRect exports.aboveRightOf = aboveRightOf; const alwaysMenuProps = (elementRect, chevronFace = ChevronFace.None, vPadding = 0) => { const menuOptions = { chevronFace }; const buttonRight = elementRect.right + window.scrollX; const buttonTop = elementRect.top + window.scrollY; // Align the right edge of the menu to the right edge of the button menuOptions.right = _UIStore.default.instance.windowWidth - buttonRight; // Align the menu vertically above the menu menuOptions.bottom = _UIStore.default.instance.windowHeight - buttonTop + vPadding; return menuOptions; }; // Placement method for <ContextMenu /> to position context menu right-aligned and flowing to the right of elementRect // and always above elementRect exports.alwaysMenuProps = alwaysMenuProps; const alwaysAboveRightOf = (elementRect, chevronFace = ChevronFace.None, vPadding = 0) => { const menuOptions = { chevronFace }; const buttonLeft = elementRect.left + window.scrollX; const buttonTop = elementRect.top + window.scrollY; // Align the left edge of the menu to the left edge of the button menuOptions.left = buttonLeft; // Align the menu vertically above the menu menuOptions.bottom = _UIStore.default.instance.windowHeight - buttonTop + vPadding; return menuOptions; }; exports.alwaysAboveRightOf = alwaysAboveRightOf; // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-constraint const useContextMenu = inputRef => { let button = (0, _react.useRef)(null); if (inputRef) { // if we are given a ref, use it instead of ours button = inputRef; } const [isOpen, setIsOpen] = (0, _react.useState)(false); const open = ev => { ev?.preventDefault(); ev?.stopPropagation(); setIsOpen(true); }; const close = ev => { ev?.preventDefault(); ev?.stopPropagation(); setIsOpen(false); }; return [button.current ? isOpen : false, button, open, close, setIsOpen]; }; // XXX: Deprecated, used only for dynamic Tooltips. Avoid using at all costs. exports.useContextMenu = useContextMenu; function createMenu(ElementClass, props) { const onFinished = function (...args) { _reactDom.default.unmountComponentAtNode(getOrCreateContainer()); props?.onFinished?.apply(null, args); }; const menu = /*#__PURE__*/_react.default.createElement(_compoundWeb.TooltipProvider, null, /*#__PURE__*/_react.default.createElement(ContextMenu, (0, _extends2.default)({}, props, { mountAsChild: true, hasBackground: false, onFinished: onFinished // eslint-disable-line react/jsx-no-bind , windowResize: onFinished // eslint-disable-line react/jsx-no-bind }), /*#__PURE__*/_react.default.createElement(ElementClass, (0, _extends2.default)({}, props, { onFinished: onFinished })))); _reactDom.default.render(menu, getOrCreateContainer()); return { close: onFinished }; } // re-export the semantic helper components for simplicity //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsInJlcXVpcmUiLCJfcmVhY3REb20iLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiX2NsYXNzbmFtZXMiLCJfcmVhY3RGb2N1c0xvY2siLCJfY29tcG91bmRXZWIiLCJfVUlTdG9yZSIsIl9Sb3ZpbmdUYWJJbmRleCIsIl9LZXlib2FyZFNob3J0Y3V0cyIsIl9LZXlCaW5kaW5nc01hbmFnZXIiLCJfTW9kYWwiLCJfQ29udGV4dE1lbnVCdXR0b24iLCJfQ29udGV4dE1lbnVUb29sdGlwQnV0dG9uIiwiX01lbnVJdGVtIiwiX01lbnVJdGVtQ2hlY2tib3giLCJfTWVudUl0ZW1SYWRpbyIsIl9TdHlsZWRNZW51SXRlbUNoZWNrYm94IiwiX1N0eWxlZE1lbnVJdGVtUmFkaW8iLCJfZXhjbHVkZWQiLCJfZXhjbHVkZWQyIiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0Iiwib3duS2V5cyIsImtleXMiLCJnZXRPd25Qcm9wZXJ0eVN5bWJvbHMiLCJvIiwiZmlsdGVyIiwiZW51bWVyYWJsZSIsInB1c2giLCJhcHBseSIsIl9vYmplY3RTcHJlYWQiLCJhcmd1bWVudHMiLCJsZW5ndGgiLCJmb3JFYWNoIiwiX2RlZmluZVByb3BlcnR5MiIsImdldE93blByb3BlcnR5RGVzY3JpcHRvcnMiLCJkZWZpbmVQcm9wZXJ0aWVzIiwiV0lORE9XX1BBRERJTkciLCJDb250ZXh0dWFsTWVudUNvbnRhaW5lcklkIiwiZ2V0T3JDcmVhdGVDb250YWluZXIiLCJjb250YWluZXIiLCJkb2N1bWVudCIsImdldEVsZW1lbnRCeUlkIiwiY3JlYXRlRWxlbWVudCIsImlkIiwiYm9keSIsImFwcGVuZENoaWxkIiwiQ2hldnJvbkZhY2UiLCJleHBvcnRzIiwiQ29udGV4dE1lbnUiLCJSZWFjdCIsIlB1cmVDb21wb25lbnQiLCJjb25zdHJ1Y3RvciIsInByb3BzIiwib25GaW5pc2hlZCIsImVsZW1lbnQiLCJmaXJzdCIsInF1ZXJ5U2VsZWN0b3IiLCJmb2N1cyIsInNldFN0YXRlIiwiY29udGV4dE1lbnVFbGVtIiwicHJldmVudERlZmF1bHQiLCJzdG9wUHJvcGFnYXRpb24iLCJ4IiwiY2xpZW50WCIsInkiLCJjbGllbnRZIiwic2V0VGltZW91dCIsImNsaWNrRXZlbnQiLCJNb3VzZUV2ZW50Iiwic2NyZWVuWCIsInNjcmVlblkiLCJidXR0b24iLCJyZWxhdGVkVGFyZ2V0IiwiZWxlbWVudEZyb21Qb2ludCIsImRpc3BhdGNoRXZlbnQiLCJldiIsImNsb3NlT25JbnRlcmFjdGlvbiIsImFjdGlvbiIsImdldEtleUJpbmRpbmdzTWFuYWdlciIsImdldEFjY2Vzc2liaWxpdHlBY3Rpb24iLCJtYW5hZ2VkIiwiS2V5QmluZGluZ0FjdGlvbiIsIkVzY2FwZSIsImNoZWNrSW5wdXRhYmxlRWxlbWVudCIsInRhcmdldCIsIlRhYiIsIkFycm93TGVmdCIsIkFycm93UmlnaHQiLCJpbmNsdWRlcyIsInN0YXRlIiwiaW5pdGlhbEZvY3VzIiwiYWN0aXZlRWxlbWVudCIsImNvbXBvbmVudERpZE1vdW50IiwiTW9kYWwiLCJvbiIsIk1vZGFsTWFuYWdlckV2ZW50IiwiT3BlbmVkIiwib25Nb2RhbE9wZW4iLCJjb21wb25lbnRXaWxsVW5tb3VudCIsIm9mZiIsInJlbmRlck1lbnUiLCJoYXNCYWNrZ3JvdW5kIiwicG9zaXRpb24iLCJfdGhpcyRwcm9wcyIsInRvcCIsImJvdHRvbSIsImxlZnQiLCJyaWdodCIsImJvdHRvbUFsaWduZWQiLCJyaWdodEFsaWduZWQiLCJtZW51Q2xhc3NOYW1lIiwibWVudUhlaWdodCIsIm1lbnVXaWR0aCIsIm1lbnVQYWRkaW5nTGVmdCIsIm1lbnVQYWRkaW5nUmlnaHQiLCJtZW51UGFkZGluZ0JvdHRvbSIsIm1lbnVQYWRkaW5nVG9wIiwiekluZGV4IiwiY2hpbGRyZW4iLCJmb2N1c0xvY2siLCJ3cmFwcGVyQ2xhc3NOYW1lIiwiY2hldnJvbkZhY2UiLCJwcm9wc0NoZXZyb25GYWNlIiwiY2hldnJvbk9mZnNldCIsInByb3BzQ2hldnJvbk9mZnNldCIsIm1vdW50QXNDaGlsZCIsIl9vYmplY3RXaXRob3V0UHJvcGVydGllczIiLCJMZWZ0IiwiUmlnaHQiLCJjb250ZXh0TWVudVJlY3QiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJoYXNDaGV2cm9uIiwiTm9uZSIsIlRvcCIsIkJvdHRvbSIsIndpbmRvd1dpZHRoIiwid2luZG93SGVpZ2h0IiwiVUlTdG9yZSIsImluc3RhbmNlIiwidW5kZWZpbmVkIiwibWF4VG9wIiwiaGVpZ2h0IiwiTWF0aCIsIm1pbiIsIm1heExlZnQiLCJ3aWR0aCIsImNoZXZyb24iLCJzdHlsZSIsImNsYXNzTmFtZSIsIm1lbnVDbGFzc2VzIiwiY2xhc3NOYW1lcyIsIm14X0NvbnRleHR1YWxNZW51IiwibXhfQ29udGV4dHVhbE1lbnVfbGVmdCIsIm14X0NvbnRleHR1YWxNZW51X3JpZ2h0IiwibXhfQ29udGV4dHVhbE1lbnVfdG9wIiwibXhfQ29udGV4dHVhbE1lbnVfYm90dG9tIiwibXhfQ29udGV4dHVhbE1lbnVfd2l0aENoZXZyb25fbGVmdCIsIm14X0NvbnRleHR1YWxNZW51X3dpdGhDaGV2cm9uX3JpZ2h0IiwibXhfQ29udGV4dHVhbE1lbnVfd2l0aENoZXZyb25fdG9wIiwibXhfQ29udGV4dHVhbE1lbnVfd2l0aENoZXZyb25fYm90dG9tIiwibXhfQ29udGV4dHVhbE1lbnVfcmlnaHRBbGlnbmVkIiwibXhfQ29udGV4dHVhbE1lbnVfYm90dG9tQWxpZ25lZCIsIm1lbnVTdHlsZSIsImlzTmFOIiwiTnVtYmVyIiwid3JhcHBlclN0eWxlIiwiYmFja2dyb3VuZCIsIm9uQ2xpY2siLCJvbkNvbnRleHRNZW51IiwiRnJhZ21lbnQiLCJfaGFzQmFja2dyb3VuZCIsIl9vbkZpbmlzaGVkIiwiZGl2UHJvcHMiLCJSb3ZpbmdUYWJJbmRleFByb3ZpZGVyIiwiaGFuZGxlSG9tZUVuZCIsImhhbmRsZVVwRG93biIsIm9uS2V5RG93biIsIm9uS2V5RG93bkhhbmRsZXIiLCJvbkNvbnRleHRNZW51UHJldmVudEJ1YmJsaW5nIiwiX2V4dGVuZHMyIiwicmVmIiwiY29sbGVjdENvbnRleHRNZW51UmVjdCIsInJvbGUiLCJyZW5kZXIiLCJSZWFjdERPTSIsImNyZWF0ZVBvcnRhbCIsInRvUmlnaHRPZiIsImVsZW1lbnRSZWN0Iiwid2luZG93Iiwic2Nyb2xsWCIsInNjcm9sbFkiLCJ0b0xlZnRPZiIsInRvTGVmdE9yUmlnaHRPZiIsInNwYWNlVG9UaGVMZWZ0Iiwic3BhY2VUb1RoZVJpZ2h0IiwiYWJvdmVMZWZ0T2YiLCJ2UGFkZGluZyIsIm1lbnVPcHRpb25zIiwiYnV0dG9uUmlnaHQiLCJidXR0b25Cb3R0b20iLCJidXR0b25Ub3AiLCJhYm92ZVJpZ2h0T2YiLCJidXR0b25MZWZ0IiwiYWx3YXlzTWVudVByb3BzIiwiYWx3YXlzQWJvdmVSaWdodE9mIiwidXNlQ29udGV4dE1lbnUiLCJpbnB1dFJlZiIsInVzZVJlZiIsImlzT3BlbiIsInNldElzT3BlbiIsInVzZVN0YXRlIiwib3BlbiIsImNsb3NlIiwiY3VycmVudCIsImNyZWF0ZU1lbnUiLCJFbGVtZW50Q2xhc3MiLCJhcmdzIiwidW5tb3VudENvbXBvbmVudEF0Tm9kZSIsIm1lbnUiLCJUb29sdGlwUHJvdmlkZXIiLCJ3aW5kb3dSZXNpemUiXSwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvY29tcG9uZW50cy9zdHJ1Y3R1cmVzL0NvbnRleHRNZW51LnRzeCJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxOSBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuQ29weXJpZ2h0IDIwMTggTmV3IFZlY3RvciBMdGRcbkNvcHlyaWdodCAyMDE1LCAyMDE2IE9wZW5NYXJrZXQgTHRkXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCBSZWFjdCwgeyBDU1NQcm9wZXJ0aWVzLCBSZWZPYmplY3QsIFN5bnRoZXRpY0V2ZW50LCB1c2VSZWYsIHVzZVN0YXRlIH0gZnJvbSBcInJlYWN0XCI7XG5pbXBvcnQgUmVhY3RET00gZnJvbSBcInJlYWN0LWRvbVwiO1xuaW1wb3J0IGNsYXNzTmFtZXMgZnJvbSBcImNsYXNzbmFtZXNcIjtcbmltcG9ydCBGb2N1c0xvY2sgZnJvbSBcInJlYWN0LWZvY3VzLWxvY2tcIjtcbmltcG9ydCB7IFRvb2x0aXBQcm92aWRlciB9IGZyb20gXCJAdmVjdG9yLWltL2NvbXBvdW5kLXdlYlwiO1xuXG5pbXBvcnQgeyBXcml0ZWFibGUgfSBmcm9tIFwiLi4vLi4vQHR5cGVzL2NvbW1vblwiO1xuaW1wb3J0IFVJU3RvcmUgZnJvbSBcIi4uLy4uL3N0b3Jlcy9VSVN0b3JlXCI7XG5pbXBvcnQgeyBjaGVja0lucHV0YWJsZUVsZW1lbnQsIFJvdmluZ1RhYkluZGV4UHJvdmlkZXIgfSBmcm9tIFwiLi4vLi4vYWNjZXNzaWJpbGl0eS9Sb3ZpbmdUYWJJbmRleFwiO1xuaW1wb3J0IHsgS2V5QmluZGluZ0FjdGlvbiB9IGZyb20gXCIuLi8uLi9hY2Nlc3NpYmlsaXR5L0tleWJvYXJkU2hvcnRjdXRzXCI7XG5pbXBvcnQgeyBnZXRLZXlCaW5kaW5nc01hbmFnZXIgfSBmcm9tIFwiLi4vLi4vS2V5QmluZGluZ3NNYW5hZ2VyXCI7XG5pbXBvcnQgTW9kYWwsIHsgTW9kYWxNYW5hZ2VyRXZlbnQgfSBmcm9tIFwiLi4vLi4vTW9kYWxcIjtcblxuLy8gU2hhbWVsZXNzbHkgcmlwcGVkIG9mZiBNb2RhbC5qcy4gIFRoZXJlJ3MgcHJvYmFibHkgYSBiZXR0ZXIgd2F5XG4vLyBvZiBkb2luZyByZXVzYWJsZSB3aWRnZXRzIGxpa2UgZGlhbG9nIGJveGVzICYgbWVudXMgd2hlcmUgd2UgZ28gYW5kXG4vLyBwYXNzIGluIGEgY3VzdG9tIGNvbnRyb2wgYXMgdGhlIGFjdHVhbCBib2R5LlxuXG5jb25zdCBXSU5ET1dfUEFERElORyA9IDEwO1xuY29uc3QgQ29udGV4dHVhbE1lbnVDb250YWluZXJJZCA9IFwibXhfQ29udGV4dHVhbE1lbnVfQ29udGFpbmVyXCI7XG5cbmZ1bmN0aW9uIGdldE9yQ3JlYXRlQ29udGFpbmVyKCk6IEhUTUxEaXZFbGVtZW50IHtcbiAgICBsZXQgY29udGFpbmVyID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoQ29udGV4dHVhbE1lbnVDb250YWluZXJJZCkgYXMgSFRNTERpdkVsZW1lbnQ7XG5cbiAgICBpZiAoIWNvbnRhaW5lcikge1xuICAgICAgICBjb250YWluZXIgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgICAgICBjb250YWluZXIuaWQgPSBDb250ZXh0dWFsTWVudUNvbnRhaW5lcklkO1xuICAgICAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGNvbnRhaW5lcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIGNvbnRhaW5lcjtcbn1cblxuZXhwb3J0IGludGVyZmFjZSBJUG9zaXRpb24ge1xuICAgIHRvcD86IG51bWJlcjtcbiAgICBib3R0b20/OiBudW1iZXI7XG4gICAgbGVmdD86IG51bWJlcjtcbiAgICByaWdodD86IG51bWJlcjtcbiAgICByaWdodEFsaWduZWQ/OiBib29sZWFuO1xuICAgIGJvdHRvbUFsaWduZWQ/OiBib29sZWFuO1xufVxuXG5leHBvcnQgZW51bSBDaGV2cm9uRmFjZSB7XG4gICAgVG9wID0gXCJ0b3BcIixcbiAgICBCb3R0b20gPSBcImJvdHRvbVwiLFxuICAgIExlZnQgPSBcImxlZnRcIixcbiAgICBSaWdodCA9IFwicmlnaHRcIixcbiAgICBOb25lID0gXCJub25lXCIsXG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgTWVudVByb3BzIGV4dGVuZHMgSVBvc2l0aW9uIHtcbiAgICBtZW51V2lkdGg/OiBudW1iZXI7XG4gICAgbWVudUhlaWdodD86IG51bWJlcjtcblxuICAgIGNoZXZyb25PZmZzZXQ/OiBudW1iZXI7XG4gICAgY2hldnJvbkZhY2U/OiBDaGV2cm9uRmFjZTtcblxuICAgIG1lbnVQYWRkaW5nVG9wPzogbnVtYmVyO1xuICAgIG1lbnVQYWRkaW5nQm90dG9tPzogbnVtYmVyO1xuICAgIG1lbnVQYWRkaW5nTGVmdD86IG51bWJlcjtcbiAgICBtZW51UGFkZGluZ1JpZ2h0PzogbnVtYmVyO1xuXG4gICAgekluZGV4PzogbnVtYmVyO1xufVxuXG5leHBvcnQgaW50ZXJmYWNlIElQcm9wcyBleHRlbmRzIE1lbnVQcm9wcyB7XG4gICAgLy8gSWYgdHJ1ZSwgaW5zZXJ0IGFuIGludmlzaWJsZSBzY3JlZW4tc2l6ZWQgZWxlbWVudCBiZWhpbmQgdGhlIG1lbnUgdGhhdCB3aGVuIGNsaWNrZWQgd2lsbCBjbG9zZSBpdC5cbiAgICBoYXNCYWNrZ3JvdW5kPzogYm9vbGVhbjtcbiAgICAvLyB3aGV0aGVyIHRoaXMgY29udGV4dCBtZW51IHNob3VsZCBiZSBmb2N1cyBtYW5hZ2VkLiBJZiBmYWxzZSBpdCBtdXN0IGhhbmRsZSBpdHNlbGZcbiAgICBtYW5hZ2VkPzogYm9vbGVhbjtcbiAgICB3cmFwcGVyQ2xhc3NOYW1lPzogc3RyaW5nO1xuICAgIG1lbnVDbGFzc05hbWU/OiBzdHJpbmc7XG5cbiAgICAvLyBJZiB0cnVlLCB0aGlzIGNvbnRleHQgbWVudSB3aWxsIGJlIG1vdW50ZWQgYXMgYSBjaGlsZCB0byB0aGUgcGFyZW50IGNvbnRhaW5lci4gT3RoZXJ3aXNlXG4gICAgLy8gaXQgd2lsbCBiZSBtb3VudGVkIHRvIGEgY29udGFpbmVyIGF0IHRoZSByb290IG9mIHRoZSBET00uXG4gICAgbW91bnRBc0NoaWxkPzogYm9vbGVhbjtcblxuICAgIC8vIElmIHNwZWNpZmllZCwgY29udGVudHMgd2lsbCBiZSB3cmFwcGVkIGluIGEgRm9jdXNMb2NrLCB0aGlzIGlzIG9ubHkgbmVlZGVkIGlmIHRoZSBjb250ZXh0IG1lbnUgaXMgYmVpbmcgcmVuZGVyZWRcbiAgICAvLyB3aXRoaW4gYW4gZXhpc3RpbmcgRm9jdXNMb2NrIGUuZyBpbnNpZGUgYSBtb2RhbC5cbiAgICBmb2N1c0xvY2s/OiBib29sZWFuO1xuXG4gICAgLy8gY2FsbCBvbkZpbmlzaGVkIG9uIGFueSBpbnRlcmFjdGlvbiB3aXRoIHRoZSBtZW51XG4gICAgY2xvc2VPbkludGVyYWN0aW9uPzogYm9vbGVhbjtcblxuICAgIC8vIEZ1bmN0aW9uIHRvIGJlIGNhbGxlZCBvbiBtZW51IGNsb3NlXG4gICAgb25GaW5pc2hlZCgpOiB2b2lkO1xuICAgIC8vIG9uIHJlc2l6ZSBjYWxsYmFja1xuICAgIHdpbmRvd1Jlc2l6ZT8oKTogdm9pZDtcbn1cblxuaW50ZXJmYWNlIElTdGF0ZSB7XG4gICAgY29udGV4dE1lbnVFbGVtPzogSFRNTERpdkVsZW1lbnQ7XG59XG5cbi8vIEdlbmVyaWMgQ29udGV4dE1lbnUgUG9ydGFsIHdyYXBwZXJcbi8vIGFsbCBvcHRpb25zIGluc2lkZSB0aGUgbWVudSBzaG91bGQgYmUgb2Ygcm9sZT1tZW51aXRlbS9tZW51aXRlbWNoZWNrYm94L21lbnVpdGVtcmFkaW9idXR0b24gYW5kIGhhdmUgdGFiSW5kZXg9ey0xfVxuLy8gdGhpcyB3aWxsIGFsbG93IHRoZSBDb250ZXh0TWVudSB0byBtYW5hZ2UgaXRzIG93biBmb2N1cyB1c2luZyBhcnJvdyBrZXlzIGFzIHBlciB0aGUgQVJJQSBndWlkZWxpbmVzLlxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgQ29udGV4dE1lbnUgZXh0ZW5kcyBSZWFjdC5QdXJlQ29tcG9uZW50PFJlYWN0LlByb3BzV2l0aENoaWxkcmVuPElQcm9wcz4sIElTdGF0ZT4ge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgaW5pdGlhbEZvY3VzOiBIVE1MRWxlbWVudDtcblxuICAgIHB1YmxpYyBzdGF0aWMgZGVmYXVsdFByb3BzID0ge1xuICAgICAgICBoYXNCYWNrZ3JvdW5kOiB0cnVlLFxuICAgICAgICBtYW5hZ2VkOiB0cnVlLFxuICAgIH07XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IocHJvcHM6IElQcm9wcykge1xuICAgICAgICBzdXBlcihwcm9wcyk7XG5cbiAgICAgICAgdGhpcy5zdGF0ZSA9IHt9O1xuXG4gICAgICAgIC8vIHBlcnNpc3Qgd2hhdCBoYWQgZm9jdXMgd2hlbiB3ZSBnb3QgaW5pdGlhbGl6ZWQgc28gd2UgY2FuIHJldHVybiBpdCBhZnRlclxuICAgICAgICB0aGlzLmluaXRpYWxGb2N1cyA9IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQ7XG4gICAgfVxuXG4gICAgcHVibGljIGNvbXBvbmVudERpZE1vdW50KCk6IHZvaWQge1xuICAgICAgICBNb2RhbC5vbihNb2RhbE1hbmFnZXJFdmVudC5PcGVuZWQsIHRoaXMub25Nb2RhbE9wZW4pO1xuICAgIH1cblxuICAgIHB1YmxpYyBjb21wb25lbnRXaWxsVW5tb3VudCgpOiB2b2lkIHtcbiAgICAgICAgTW9kYWwub2ZmKE1vZGFsTWFuYWdlckV2ZW50Lk9wZW5lZCwgdGhpcy5vbk1vZGFsT3Blbik7XG4gICAgICAgIC8vIHJldHVybiBmb2N1cyB0byB0aGUgdGhpbmcgd2hpY2ggaGFkIGl0IGJlZm9yZSB1c1xuICAgICAgICB0aGlzLmluaXRpYWxGb2N1cy5mb2N1cygpO1xuICAgIH1cblxuICAgIHByaXZhdGUgb25Nb2RhbE9wZW4gPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIHRoaXMucHJvcHMub25GaW5pc2hlZD8uKCk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgY29sbGVjdENvbnRleHRNZW51UmVjdCA9IChlbGVtZW50OiBIVE1MRGl2RWxlbWVudCk6IHZvaWQgPT4ge1xuICAgICAgICAvLyBXZSBkb24ndCBuZWVkIHRvIGNsZWFuIHVwIHdoZW4gdW5tb3VudGluZywgc28gaWdub3JlXG4gICAgICAgIGlmICghZWxlbWVudCkgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IGZpcnN0ID1cbiAgICAgICAgICAgIGVsZW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oJ1tyb2xlXj1cIm1lbnVpdGVtXCJdJykgfHxcbiAgICAgICAgICAgIGVsZW1lbnQucXVlcnlTZWxlY3RvcjxIVE1MRWxlbWVudD4oXCJbdGFiaW5kZXhdXCIpO1xuXG4gICAgICAgIGlmIChmaXJzdCkge1xuICAgICAgICAgICAgZmlyc3QuZm9jdXMoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgY29udGV4dE1lbnVFbGVtOiBlbGVtZW50LFxuICAgICAgICB9KTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkNvbnRleHRNZW51ID0gKGU6IFJlYWN0Lk1vdXNlRXZlbnQpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKHRoaXMucHJvcHMub25GaW5pc2hlZCkge1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkZpbmlzaGVkKCk7XG5cbiAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICBjb25zdCB4ID0gZS5jbGllbnRYO1xuICAgICAgICAgICAgY29uc3QgeSA9IGUuY2xpZW50WTtcblxuICAgICAgICAgICAgLy8gWFhYOiBUaGlzIGlzbid0IHByZXR0eSBidXQgdGhlIG9ubHkgd2F5IHRvIGFsbG93IG9wZW5pbmcgYSBkaWZmZXJlbnQgY29udGV4dCBtZW51IG9uIHJpZ2h0IGNsaWNrIHdoaWxzdFxuICAgICAgICAgICAgLy8gYSBjb250ZXh0IG1lbnUgYW5kIGl0cyBjbGljay1ndWFyZCBhcmUgdXAgd2l0aG91dCBjb21wbGV0ZWx5IHJld3JpdGluZyBob3cgdGhlIGNvbnRleHQgbWVudXMgd29yay5cbiAgICAgICAgICAgIHNldFRpbWVvdXQoKCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNsaWNrRXZlbnQgPSBuZXcgTW91c2VFdmVudChcImNvbnRleHRtZW51XCIsIHtcbiAgICAgICAgICAgICAgICAgICAgY2xpZW50WDogeCxcbiAgICAgICAgICAgICAgICAgICAgY2xpZW50WTogeSxcbiAgICAgICAgICAgICAgICAgICAgc2NyZWVuWDogMCxcbiAgICAgICAgICAgICAgICAgICAgc2NyZWVuWTogMCxcbiAgICAgICAgICAgICAgICAgICAgYnV0dG9uOiAwLCAvLyBMZWZ0XG4gICAgICAgICAgICAgICAgICAgIHJlbGF0ZWRUYXJnZXQ6IG51bGwsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludCh4LCB5KT8uZGlzcGF0Y2hFdmVudChjbGlja0V2ZW50KTtcbiAgICAgICAgICAgIH0sIDApO1xuICAgICAgICB9XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25Db250ZXh0TWVudVByZXZlbnRCdWJibGluZyA9IChlOiBSZWFjdC5Nb3VzZUV2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIC8vIHN0b3AgcHJvcGFnYXRpb24gc28gdGhhdCBhbnkgY29udGV4dCBtZW51IGhhbmRsZXJzIGRvbid0IGxlYWsgb3V0IG9mIHRoaXMgY29udGV4dCBtZW51XG4gICAgICAgIC8vIGJ1dCBkbyBub3QgaW5oaWJpdCB0aGUgZGVmYXVsdCBicm93c2VyIG1lbnVcbiAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB9O1xuXG4gICAgLy8gUHJldmVudCBjbGlja3Mgb24gdGhlIGJhY2tncm91bmQgZnJvbSBnb2luZyB0aHJvdWdoIHRvIHRoZSBjb21wb25lbnQgd2hpY2ggb3BlbmVkIHRoZSBtZW51LlxuICAgIHByaXZhdGUgb25GaW5pc2hlZCA9IChldjogUmVhY3QuTW91c2VFdmVudCk6IHZvaWQgPT4ge1xuICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgZXYucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5wcm9wcy5vbkZpbmlzaGVkPy4oKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvbkNsaWNrID0gKGV2OiBSZWFjdC5Nb3VzZUV2ZW50KTogdm9pZCA9PiB7XG4gICAgICAgIC8vIERvbid0IGFsbG93IGNsaWNrcyB0byBlc2NhcGUgdGhlIGNvbnRleHQgbWVudSB3cmFwcGVyXG4gICAgICAgIGV2LnN0b3BQcm9wYWdhdGlvbigpO1xuXG4gICAgICAgIGlmICh0aGlzLnByb3BzLmNsb3NlT25JbnRlcmFjdGlvbikge1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkZpbmlzaGVkPy4oKTtcbiAgICAgICAgfVxuICAgIH07XG5cbiAgICAvLyBXZSBub3cgb25seSBoYW5kbGUgY2xvc2luZyB0aGUgQ29udGV4dE1lbnUgaW4gdGhpcyBrZXlEb3duIGhhbmRsZXIuXG4gICAgLy8gQWxsIG9mIHRoZSBpdGVtL29wdGlvbiBuYXZpZ2F0aW9uIGlzIGRlbGVnYXRlZCB0byBSb3ZpbmdUYWJJbmRleC5cbiAgICBwcml2YXRlIG9uS2V5RG93biA9IChldjogUmVhY3QuS2V5Ym9hcmRFdmVudCk6IHZvaWQgPT4ge1xuICAgICAgICBldi5zdG9wUHJvcGFnYXRpb24oKTsgLy8gcHJldmVudCBrZXlib2FyZCBwcm9wYWdhdGluZyBvdXQgb2YgdGhlIGNvbnRleHQgbWVudSwgd2UncmUgZm9jdXMtbG9ja2VkXG5cbiAgICAgICAgY29uc3QgYWN0aW9uID0gZ2V0S2V5QmluZGluZ3NNYW5hZ2VyKCkuZ2V0QWNjZXNzaWJpbGl0eUFjdGlvbihldik7XG5cbiAgICAgICAgLy8gSWYgc29tZW9uZSBpcyBtYW5hZ2luZyB0aGVpciBvd24gZm9jdXMsIHdlIHdpbGwgb25seSBleGl0IGZvciB0aGVtIHdpdGggRXNjYXBlLlxuICAgICAgICAvLyBUaGV5IGFyZSBwcm9iYWJseSB1c2luZyBwcm9wcy5mb2N1c0xvY2sgYWxvbmcgd2l0aCB0aGlzIG9wdGlvbiBhcyB3ZWxsLlxuICAgICAgICBpZiAoIXRoaXMucHJvcHMubWFuYWdlZCkge1xuICAgICAgICAgICAgaWYgKGFjdGlvbiA9PT0gS2V5QmluZGluZ0FjdGlvbi5Fc2NhcGUpIHtcbiAgICAgICAgICAgICAgICB0aGlzLnByb3BzLm9uRmluaXNoZWQoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFdoZW4gYW4gPGlucHV0PiBpcyBmb2N1c2VkLCBvbmx5IGhhbmRsZSB0aGUgRXNjYXBlIGtleVxuICAgICAgICBpZiAoY2hlY2tJbnB1dGFibGVFbGVtZW50KGV2LnRhcmdldCBhcyBIVE1MRWxlbWVudCkgJiYgYWN0aW9uICE9PSBLZXlCaW5kaW5nQWN0aW9uLkVzY2FwZSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKFxuICAgICAgICAgICAgW1xuICAgICAgICAgICAgICAgIEtleUJpbmRpbmdBY3Rpb24uRXNjYXBlLFxuICAgICAgICAgICAgICAgIC8vIFlvdSBjYW4gb25seSBuYXZpZ2F0ZSB0aGUgQ29udGV4dE1lbnUgYnkgYXJyb3cga2V5cyBhbmQgSG9tZS9FbmQgKHNlZSBSb3ZpbmdUYWJJbmRleCkuXG4gICAgICAgICAgICAgICAgLy8gVGFiYmluZyB0byB0aGUgbmV4dCBzZWN0aW9uIG9mIHRoZSBwYWdlLCB3aWxsIGNsb3NlIHRoZSBDb250ZXh0TWVudS5cbiAgICAgICAgICAgICAgICBLZXlCaW5kaW5nQWN0aW9uLlRhYixcbiAgICAgICAgICAgICAgICAvLyBXaGVuIHNvbWVvbmUgbW92ZXMgbGVmdCBvciByaWdodCBhbG9uZyBhIDxUb29sYmFyIC8+IChsaWtlIHRoZVxuICAgICAgICAgICAgICAgIC8vIE1lc3NhZ2VBY3Rpb25CYXIpLCB3ZSBzaG91bGQgY2xvc2UgYW55IENvbnRleHRNZW51IHRoYXQgaXMgb3Blbi5cbiAgICAgICAgICAgICAgICBLZXlCaW5kaW5nQWN0aW9uLkFycm93TGVmdCxcbiAgICAgICAgICAgICAgICBLZXlCaW5kaW5nQWN0aW9uLkFycm93UmlnaHQsXG4gICAgICAgICAgICBdLmluY2x1ZGVzKGFjdGlvbiEpXG4gICAgICAgICkge1xuICAgICAgICAgICAgdGhpcy5wcm9wcy5vbkZpbmlzaGVkKCk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHJvdGVjdGVkIHJlbmRlck1lbnUoaGFzQmFja2dyb3VuZCA9IHRoaXMucHJvcHMuaGFzQmFja2dyb3VuZCk6IEpTWC5FbGVtZW50IHtcbiAgICAgICAgY29uc3QgcG9zaXRpb246IFBhcnRpYWw8V3JpdGVhYmxlPERPTVJlY3Q+PiA9IHt9O1xuICAgICAgICBjb25zdCB7XG4gICAgICAgICAgICB0b3AsXG4gICAgICAgICAgICBib3R0b20sXG4gICAgICAgICAgICBsZWZ0LFxuICAgICAgICAgICAgcmlnaHQsXG4gICAgICAgICAgICBib3R0b21BbGlnbmVkLFxuICAgICAgICAgICAgcmlnaHRBbGlnbmVkLFxuICAgICAgICAgICAgbWVudUNsYXNzTmFtZSxcbiAgICAgICAgICAgIG1lbnVIZWlnaHQsXG4gICAgICAgICAgICBtZW51V2lkdGgsXG4gICAgICAgICAgICBtZW51UGFkZGluZ0xlZnQsXG4gICAgICAgICAgICBtZW51UGFkZGluZ1JpZ2h0LFxuICAgICAgICAgICAgbWVudVBhZGRpbmdCb3R0b20sXG4gICAgICAgICAgICBtZW51UGFkZGluZ1RvcCxcbiAgICAgICAgICAgIHpJbmRleCxcbiAgICAgICAgICAgIGNoaWxkcmVuLFxuICAgICAgICAgICAgZm9jdXNMb2NrLFxuICAgICAgICAgICAgbWFuYWdlZCxcbiAgICAgICAgICAgIHdyYXBwZXJDbGFzc05hbWUsXG4gICAgICAgICAgICBjaGV2cm9uRmFjZTogcHJvcHNDaGV2cm9uRmFjZSxcbiAgICAgICAgICAgIGNoZXZyb25PZmZzZXQ6IHByb3BzQ2hldnJvbk9mZnNldCxcbiAgICAgICAgICAgIG1vdW50QXNDaGlsZCxcbiAgICAgICAgICAgIC4uLnByb3BzXG4gICAgICAgIH0gPSB0aGlzLnByb3BzO1xuXG4gICAgICAgIGlmICh0b3ApIHtcbiAgICAgICAgICAgIHBvc2l0aW9uLnRvcCA9IHRvcDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHBvc2l0aW9uLmJvdHRvbSA9IGJvdHRvbTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjaGV2cm9uRmFjZTogQ2hldnJvbkZhY2U7XG4gICAgICAgIGlmIChsZWZ0KSB7XG4gICAgICAgICAgICBwb3NpdGlvbi5sZWZ0ID0gbGVmdDtcbiAgICAgICAgICAgIGNoZXZyb25GYWNlID0gQ2hldnJvbkZhY2UuTGVmdDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHBvc2l0aW9uLnJpZ2h0ID0gcmlnaHQ7XG4gICAgICAgICAgICBjaGV2cm9uRmFjZSA9IENoZXZyb25GYWNlLlJpZ2h0O1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgY29udGV4dE1lbnVSZWN0ID0gdGhpcy5zdGF0ZS5jb250ZXh0TWVudUVsZW0gPyB0aGlzLnN0YXRlLmNvbnRleHRNZW51RWxlbS5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKSA6IG51bGw7XG5cbiAgICAgICAgY29uc3QgY2hldnJvbk9mZnNldDogQ1NTUHJvcGVydGllcyA9IHt9O1xuICAgICAgICBpZiAocHJvcHNDaGV2cm9uRmFjZSkge1xuICAgICAgICAgICAgY2hldnJvbkZhY2UgPSBwcm9wc0NoZXZyb25GYWNlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGhhc0NoZXZyb24gPSBjaGV2cm9uRmFjZSAmJiBjaGV2cm9uRmFjZSAhPT0gQ2hldnJvbkZhY2UuTm9uZTtcblxuICAgICAgICBpZiAoY2hldnJvbkZhY2UgPT09IENoZXZyb25GYWNlLlRvcCB8fCBjaGV2cm9uRmFjZSA9PT0gQ2hldnJvbkZhY2UuQm90dG9tKSB7XG4gICAgICAgICAgICBjaGV2cm9uT2Zmc2V0LmxlZnQgPSBwcm9wc0NoZXZyb25PZmZzZXQ7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjaGV2cm9uT2Zmc2V0LnRvcCA9IHByb3BzQ2hldnJvbk9mZnNldDtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIElmIHdlIGtub3cgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIGNvbnRleHQgbWVudSwgYWRqdXN0IGl0cyBwb3NpdGlvbiB0b1xuICAgICAgICAvLyBrZWVwIGl0IHdpdGhpbiB0aGUgYm91bmRzIG9mIHRoZSAocGFkZGVkKSB3aW5kb3dcbiAgICAgICAgY29uc3QgeyB3aW5kb3dXaWR0aCwgd2luZG93SGVpZ2h0IH0gPSBVSVN0b3JlLmluc3RhbmNlO1xuICAgICAgICBpZiAoY29udGV4dE1lbnVSZWN0KSB7XG4gICAgICAgICAgICBpZiAocG9zaXRpb24udG9wICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBsZXQgbWF4VG9wID0gd2luZG93SGVpZ2h0IC0gV0lORE9XX1BBRERJTkc7XG4gICAgICAgICAgICAgICAgaWYgKCFib3R0b21BbGlnbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIG1heFRvcCAtPSBjb250ZXh0TWVudVJlY3QuaGVpZ2h0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBwb3NpdGlvbi50b3AgPSBNYXRoLm1pbihwb3NpdGlvbi50b3AsIG1heFRvcCk7XG4gICAgICAgICAgICAgICAgLy8gQWRqdXN0IHRoZSBjaGV2cm9uIGlmIG5lY2Vzc2FyeVxuICAgICAgICAgICAgICAgIGlmIChjaGV2cm9uT2Zmc2V0LnRvcCAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNoZXZyb25PZmZzZXQudG9wID0gcHJvcHNDaGV2cm9uT2Zmc2V0ISArIHRvcCEgLSBwb3NpdGlvbi50b3A7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIGlmIChwb3NpdGlvbi5ib3R0b20gIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgIHBvc2l0aW9uLmJvdHRvbSA9IE1hdGgubWluKHBvc2l0aW9uLmJvdHRvbSwgd2luZG93SGVpZ2h0IC0gY29udGV4dE1lbnVSZWN0LmhlaWdodCAtIFdJTkRPV19QQURESU5HKTtcbiAgICAgICAgICAgICAgICBpZiAoY2hldnJvbk9mZnNldC50b3AgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgICAgICAgICBjaGV2cm9uT2Zmc2V0LnRvcCA9IHByb3BzQ2hldnJvbk9mZnNldCEgKyBwb3NpdGlvbi5ib3R0b20gLSBib3R0b20hO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGlmIChwb3NpdGlvbi5sZWZ0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBsZXQgbWF4TGVmdCA9IHdpbmRvd1dpZHRoIC0gV0lORE9XX1BBRERJTkc7XG4gICAgICAgICAgICAgICAgaWYgKCFyaWdodEFsaWduZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgbWF4TGVmdCAtPSBjb250ZXh0TWVudVJlY3Qud2lkdGg7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHBvc2l0aW9uLmxlZnQgPSBNYXRoLm1pbihwb3NpdGlvbi5sZWZ0LCBtYXhMZWZ0KTtcbiAgICAgICAgICAgICAgICBpZiAoY2hldnJvbk9mZnNldC5sZWZ0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY2hldnJvbk9mZnNldC5sZWZ0ID0gcHJvcHNDaGV2cm9uT2Zmc2V0ISArIGxlZnQhIC0gcG9zaXRpb24ubGVmdDtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2UgaWYgKHBvc2l0aW9uLnJpZ2h0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICBwb3NpdGlvbi5yaWdodCA9IE1hdGgubWluKHBvc2l0aW9uLnJpZ2h0LCB3aW5kb3dXaWR0aCAtIGNvbnRleHRNZW51UmVjdC53aWR0aCAtIFdJTkRPV19QQURESU5HKTtcbiAgICAgICAgICAgICAgICBpZiAoY2hldnJvbk9mZnNldC5sZWZ0ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY2hldnJvbk9mZnNldC5sZWZ0ID0gcHJvcHNDaGV2cm9uT2Zmc2V0ISArIHBvc2l0aW9uLnJpZ2h0IC0gcmlnaHQhO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBjaGV2cm9uO1xuICAgICAgICBpZiAoaGFzQ2hldnJvbikge1xuICAgICAgICAgICAgY2hldnJvbiA9IDxkaXYgc3R5bGU9e2NoZXZyb25PZmZzZXR9IGNsYXNzTmFtZT17XCJteF9Db250ZXh0dWFsTWVudV9jaGV2cm9uX1wiICsgY2hldnJvbkZhY2V9IC8+O1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgbWVudUNsYXNzZXMgPSBjbGFzc05hbWVzKFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIG14X0NvbnRleHR1YWxNZW51OiB0cnVlLFxuICAgICAgICAgICAgICAgIC8qKlxuICAgICAgICAgICAgICAgICAqIEluIHNvbWUgY2FzZXMgd2UgbWF5IGdldCB0aGUgbnVtYmVyIG9mIDAsIHdoaWNoIHN0aWxsIG1lYW5zIHRoYXQgd2UncmUgc3VwcG9zZWQgdG8gcHJvcGVybHlcbiAgICAgICAgICAgICAgICAgKiBhZGQgdGhlIHNwZWNpZmljIHBvc2l0aW9uIGNsYXNzLCBidXQgYXMgaXQgd2FzIGZhbHN5IHRoaW5ncyBkaWRuJ3Qgd29yayBhcyBpbnRlbmRlZC5cbiAgICAgICAgICAgICAgICAgKiBJbiBhZGRpdGlvbiwgZGVmZW5zaXZlbHkgY2hlY2sgZm9yIGNvdW50ZXIgY2FzZXMgd2hlcmUgd2UgbWF5IGdldCBtb3JlIHRoYW4gb25lIHZhbHVlLFxuICAgICAgICAgICAgICAgICAqIGV2ZW4gaWYgd2Ugc2hvdWxkbid0LlxuICAgICAgICAgICAgICAgICAqL1xuICAgICAgICAgICAgICAgIG14X0NvbnRleHR1YWxNZW51X2xlZnQ6ICFoYXNDaGV2cm9uICYmIHBvc2l0aW9uLmxlZnQgIT09IHVuZGVmaW5lZCAmJiAhcG9zaXRpb24ucmlnaHQsXG4gICAgICAgICAgICAgICAgbXhfQ29udGV4dHVhbE1lbnVfcmlnaHQ6ICFoYXNDaGV2cm9uICYmIHBvc2l0aW9uLnJpZ2h0ICE9PSB1bmRlZmluZWQgJiYgIXBvc2l0aW9uLmxlZnQsXG4gICAgICAgICAgICAgICAgbXhfQ29udGV4dHVhbE1lbnVfdG9wOiAhaGFzQ2hldnJvbiAmJiBwb3NpdGlvbi50b3AgIT09IHVuZGVmaW5lZCAmJiAhcG9zaXRpb24uYm90dG9tLFxuICAgICAgICAgICAgICAgIG14X0NvbnRleHR1YWxNZW51X2JvdHRvbTogIWhhc0NoZXZyb24gJiYgcG9zaXRpb24uYm90dG9tICE9PSB1bmRlZmluZWQgJiYgIXBvc2l0aW9uLnRvcCxcbiAgICAgICAgICAgICAgICBteF9Db250ZXh0dWFsTWVudV93aXRoQ2hldnJvbl9sZWZ0OiBjaGV2cm9uRmFjZSA9PT0gQ2hldnJvbkZhY2UuTGVmdCxcbiAgICAgICAgICAgICAgICBteF9Db250ZXh0dWFsTWVudV93aXRoQ2hldnJvbl9yaWdodDogY2hldnJvbkZhY2UgPT09IENoZXZyb25GYWNlLlJpZ2h0LFxuICAgICAgICAgICAgICAgIG14X0NvbnRleHR1YWxNZW51X3dpdGhDaGV2cm9uX3RvcDogY2hldnJvbkZhY2UgPT09IENoZXZyb25GYWNlLlRvcCxcbiAgICAgICAgICAgICAgICBteF9Db250ZXh0dWFsTWVudV93aXRoQ2hldnJvbl9ib3R0b206IGNoZXZyb25GYWNlID09PSBDaGV2cm9uRmFjZS5Cb3R0b20sXG4gICAgICAgICAgICAgICAgbXhfQ29udGV4dHVhbE1lbnVfcmlnaHRBbGlnbmVkOiByaWdodEFsaWduZWQgPT09IHRydWUsXG4gICAgICAgICAgICAgICAgbXhfQ29udGV4dHVhbE1lbnVfYm90dG9tQWxpZ25lZDogYm90dG9tQWxpZ25lZCA9PT0gdHJ1ZSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgICBtZW51Q2xhc3NOYW1lLFxuICAgICAgICApO1xuXG4gICAgICAgIGNvbnN0IG1lbnVTdHlsZTogQ1NTUHJvcGVydGllcyA9IHt9O1xuICAgICAgICBpZiAobWVudVdpZHRoKSB7XG4gICAgICAgICAgICBtZW51U3R5bGUud2lkdGggPSBtZW51V2lkdGg7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAobWVudUhlaWdodCkge1xuICAgICAgICAgICAgbWVudVN0eWxlLmhlaWdodCA9IG1lbnVIZWlnaHQ7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoIWlzTmFOKE51bWJlcihtZW51UGFkZGluZ1RvcCkpKSB7XG4gICAgICAgICAgICBtZW51U3R5bGVbXCJwYWRkaW5nVG9wXCJdID0gbWVudVBhZGRpbmdUb3A7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCFpc05hTihOdW1iZXIobWVudVBhZGRpbmdMZWZ0KSkpIHtcbiAgICAgICAgICAgIG1lbnVTdHlsZVtcInBhZGRpbmdMZWZ0XCJdID0gbWVudVBhZGRpbmdMZWZ0O1xuICAgICAgICB9XG4gICAgICAgIGlmICghaXNOYU4oTnVtYmVyKG1lbnVQYWRkaW5nQm90dG9tKSkpIHtcbiAgICAgICAgICAgIG1lbnVTdHlsZVtcInBhZGRpbmdCb3R0b21cIl0gPSBtZW51UGFkZGluZ0JvdHRvbTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIWlzTmFOKE51bWJlcihtZW51UGFkZGluZ1JpZ2h0KSkpIHtcbiAgICAgICAgICAgIG1lbnVTdHlsZVtcInBhZGRpbmdSaWdodFwiXSA9IG1lbnVQYWRkaW5nUmlnaHQ7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB3cmFwcGVyU3R5bGU6IENTU1Byb3BlcnRpZXMgPSB7fTtcbiAgICAgICAgaWYgKCFpc05hTihOdW1iZXIoekluZGV4KSkpIHtcbiAgICAgICAgICAgIG1lbnVTdHlsZVtcInpJbmRleFwiXSA9IHpJbmRleCEgKyAxO1xuICAgICAgICAgICAgd3JhcHBlclN0eWxlW1wiekluZGV4XCJdID0gekluZGV4O1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IGJhY2tncm91bmQ6IEpTWC5FbGVtZW50O1xuICAgICAgICBpZiAoaGFzQmFja2dyb3VuZCkge1xuICAgICAgICAgICAgYmFja2dyb3VuZCA9IChcbiAgICAgICAgICAgICAgICA8ZGl2XG4gICAgICAgICAgICAgICAgICAgIGNsYXNzTmFtZT1cIm14X0NvbnRleHR1YWxNZW51X2JhY2tncm91bmRcIlxuICAgICAgICAgICAgICAgICAgICBzdHlsZT17d3JhcHBlclN0eWxlfVxuICAgICAgICAgICAgICAgICAgICBvbkNsaWNrPXt0aGlzLm9uRmluaXNoZWR9XG4gICAgICAgICAgICAgICAgICAgIG9uQ29udGV4dE1lbnU9e3RoaXMub25Db250ZXh0TWVudX1cbiAgICAgICAgICAgICAgICAvPlxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBib2R5ID0gKFxuICAgICAgICAgICAgPD5cbiAgICAgICAgICAgICAgICB7Y2hldnJvbn1cbiAgICAgICAgICAgICAgICB7Y2hpbGRyZW59XG4gICAgICAgICAgICA8Lz5cbiAgICAgICAgKTtcblxuICAgICAgICBpZiAoZm9jdXNMb2NrKSB7XG4gICAgICAgICAgICBib2R5ID0gPEZvY3VzTG9jaz57Ym9keX08L0ZvY3VzTG9jaz47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBmaWx0ZXIgcHJvcHMgdGhhdCBhcmUgaW52YWxpZCBmb3IgRE9NIGVsZW1lbnRzXG4gICAgICAgIGNvbnN0IHtcbiAgICAgICAgICAgIGhhc0JhY2tncm91bmQ6IF9oYXNCYWNrZ3JvdW5kLCAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby11bnVzZWQtdmFyc1xuICAgICAgICAgICAgb25GaW5pc2hlZDogX29uRmluaXNoZWQsIC8vIGVzbGludC1kaXNhYmxlLWxpbmUgQHR5cGVzY3JpcHQtZXNsaW50L25vLXVudXNlZC12YXJzXG4gICAgICAgICAgICAuLi5kaXZQcm9wc1xuICAgICAgICB9ID0gcHJvcHM7XG5cbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIDxSb3ZpbmdUYWJJbmRleFByb3ZpZGVyIGhhbmRsZUhvbWVFbmQgaGFuZGxlVXBEb3duIG9uS2V5RG93bj17dGhpcy5vbkt