matrix-react-sdk
Version:
SDK for matrix.org using React
569 lines (552 loc) • 85 kB
JavaScript
"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