UNPKG

matrix-react-sdk

Version:
548 lines (443 loc) 67.9 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _url = _interopRequireDefault(require("url")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _MatrixClientPeg = require("../../../MatrixClientPeg"); var _AccessibleButton = _interopRequireDefault(require("./AccessibleButton")); var _languageHandler = require("../../../languageHandler"); var _AppPermission = _interopRequireDefault(require("./AppPermission")); var _AppWarning = _interopRequireDefault(require("./AppWarning")); var _Spinner = _interopRequireDefault(require("./Spinner")); var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher")); var _ActiveWidgetStore = _interopRequireDefault(require("../../../stores/ActiveWidgetStore")); var _classnames = _interopRequireDefault(require("classnames")); var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore")); var _ContextMenu = require("../../structures/ContextMenu"); var _PersistedElement = _interopRequireWildcard(require("./PersistedElement")); var _WidgetType = require("../../../widgets/WidgetType"); var _StopGapWidget = require("../../../stores/widgets/StopGapWidget"); var _ElementWidgetActions = require("../../../stores/widgets/ElementWidgetActions"); var _matrixWidgetApi = require("matrix-widget-api"); var _WidgetContextMenu = _interopRequireDefault(require("../context_menus/WidgetContextMenu")); var _WidgetAvatar = _interopRequireDefault(require("../avatars/WidgetAvatar")); var _replaceableComponent = require("../../../utils/replaceableComponent"); var _dec, _class, _temp; let AppTile = (_dec = (0, _replaceableComponent.replaceableComponent)("views.elements.AppTile"), _dec(_class = (_temp = class AppTile extends _react.default.Component { constructor(_props) { super(_props); // The key used for PersistedElement (0, _defineProperty2.default)(this, "hasPermissionToLoad", props => { if (this._usingLocalWidget()) return true; if (!props.room) return true; // user widgets always have permissions const currentlyAllowedWidgets = _SettingsStore.default.getValue("allowedWidgets", props.room.roomId); if (currentlyAllowedWidgets[props.app.eventId] === undefined) { return props.userId === props.creatorUserId; } return !!currentlyAllowedWidgets[props.app.eventId]; }); (0, _defineProperty2.default)(this, "onAllowedWidgetsChange", () => { const hasPermissionToLoad = this.hasPermissionToLoad(this.props); if (this.state.hasPermissionToLoad && !hasPermissionToLoad) { // Force the widget to be non-persistent (able to be deleted/forgotten) _ActiveWidgetStore.default.destroyPersistentWidget(this.props.app.id); _PersistedElement.default.destroyElement(this._persistKey); this._sgWidget.stop(); } this.setState({ hasPermissionToLoad }); }); (0, _defineProperty2.default)(this, "_iframeRefChange", ref => { this.iframe = ref; if (ref) { this._sgWidget.start(ref); } else { this._resetWidget(this.props); } }); (0, _defineProperty2.default)(this, "_onWidgetPrepared", () => { this.setState({ loading: false }); }); (0, _defineProperty2.default)(this, "_onWidgetReady", () => { if (_WidgetType.WidgetType.JITSI.matches(this.props.app.type)) { this._sgWidget.widgetApi.transport.send(_ElementWidgetActions.ElementWidgetActions.ClientReady, {}); } }); (0, _defineProperty2.default)(this, "_onAction", payload => { if (payload.widgetId === this.props.app.id) { switch (payload.action) { case 'm.sticker': if (this._sgWidget.widgetApi.hasCapability(_matrixWidgetApi.MatrixCapabilities.StickerSending)) { _dispatcher.default.dispatch({ action: 'post_sticker_message', data: payload.data }); } else { console.warn('Ignoring sticker message. Invalid capability'); } break; } } }); (0, _defineProperty2.default)(this, "_grantWidgetPermission", () => { const roomId = this.props.room.roomId; console.info("Granting permission for widget to load: " + this.props.app.eventId); const current = _SettingsStore.default.getValue("allowedWidgets", roomId); current[this.props.app.eventId] = true; const level = _SettingsStore.default.firstSupportedLevel("allowedWidgets"); _SettingsStore.default.setValue("allowedWidgets", roomId, level, current).then(() => { this.setState({ hasPermissionToLoad: true }); // Fetch a token for the integration manager, now that we're allowed to this._startWidget(); }).catch(err => { console.error(err); // We don't really need to do anything about this - the user will just hit the button again. }); }); (0, _defineProperty2.default)(this, "_onPopoutWidgetClick", () => { // Ensure Jitsi conferences are closed on pop-out, to not confuse the user to join them // twice from the same computer, which Jitsi can have problems with (audio echo/gain-loop). if (_WidgetType.WidgetType.JITSI.matches(this.props.app.type)) { this._endWidgetActions().then(() => { if (this.iframe) { // Reload iframe this.iframe.src = this._sgWidget.embedUrl; this.setState({}); } }); } // Using Object.assign workaround as the following opens in a new window instead of a new tab. // window.open(this._getPopoutUrl(), '_blank', 'noopener=yes'); Object.assign(document.createElement('a'), { target: '_blank', href: this._sgWidget.popoutUrl, rel: 'noreferrer noopener' }).click(); }); (0, _defineProperty2.default)(this, "_onContextMenuClick", () => { this.setState({ menuDisplayed: true }); }); (0, _defineProperty2.default)(this, "_closeContextMenu", () => { this.setState({ menuDisplayed: false }); }); this._persistKey = (0, _PersistedElement.getPersistKey)(this.props.app.id); this._sgWidget = new _StopGapWidget.StopGapWidget(this.props); this._sgWidget.on("preparing", this._onWidgetPrepared); this._sgWidget.on("ready", this._onWidgetReady); this.iframe = null; // ref to the iframe (callback style) this.state = this._getNewState(_props); this._contextMenuButton = /*#__PURE__*/(0, _react.createRef)(); this._allowedWidgetsWatchRef = _SettingsStore.default.watchSetting("allowedWidgets", null, this.onAllowedWidgetsChange); } // This is a function to make the impact of calling SettingsStore slightly less /** * Set initial component state when the App wUrl (widget URL) is being updated. * Component props *must* be passed (rather than relying on this.props). * @param {Object} newProps The new properties of the component * @return {Object} Updated component state to be set with setState */ _getNewState(newProps) { return { initialising: true, // True while we are mangling the widget URL // True while the iframe content is loading loading: this.props.waitForIframeLoad && !_PersistedElement.default.isMounted(this._persistKey), // Assume that widget has permission to load if we are the user who // added it to the room, or if explicitly granted by the user hasPermissionToLoad: this.hasPermissionToLoad(newProps), error: null, widgetPageTitle: newProps.widgetPageTitle, menuDisplayed: false }; } isMixedContent() { const parentContentProtocol = window.location.protocol; const u = _url.default.parse(this.props.app.url); const childContentProtocol = u.protocol; if (parentContentProtocol === 'https:' && childContentProtocol !== 'https:') { console.warn("Refusing to load mixed-content app:", parentContentProtocol, childContentProtocol, window.location, this.props.app.url); return true; } return false; } componentDidMount() { // Only fetch IM token on mount if we're showing and have permission to load if (this.state.hasPermissionToLoad) { this._startWidget(); } // Widget action listeners this.dispatcherRef = _dispatcher.default.register(this._onAction); } componentWillUnmount() { // Widget action listeners if (this.dispatcherRef) _dispatcher.default.unregister(this.dispatcherRef); // if it's not remaining on screen, get rid of the PersistedElement container if (!_ActiveWidgetStore.default.getWidgetPersistence(this.props.app.id)) { _ActiveWidgetStore.default.destroyPersistentWidget(this.props.app.id); _PersistedElement.default.destroyElement(this._persistKey); } if (this._sgWidget) { this._sgWidget.stop(); } _SettingsStore.default.unwatchSetting(this._allowedWidgetsWatchRef); } _resetWidget(newProps) { if (this._sgWidget) { this._sgWidget.stop(); } this._sgWidget = new _StopGapWidget.StopGapWidget(newProps); this._sgWidget.on("preparing", this._onWidgetPrepared); this._sgWidget.on("ready", this._onWidgetReady); this._startWidget(); } _startWidget() { this._sgWidget.prepare().then(() => { this.setState({ initialising: false }); }); } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event UNSAFE_componentWillReceiveProps(nextProps) { // eslint-disable-line camelcase if (nextProps.app.url !== this.props.app.url) { this._getNewState(nextProps); if (this.state.hasPermissionToLoad) { this._resetWidget(nextProps); } } if (nextProps.widgetPageTitle !== this.props.widgetPageTitle) { this.setState({ widgetPageTitle: nextProps.widgetPageTitle }); } } /** * Ends all widget interaction, such as cancelling calls and disabling webcams. * @private * @returns {Promise<*>} Resolves when the widget is terminated, or timeout passed. */ async _endWidgetActions() { // widget migration dev note: async to maintain signature // HACK: This is a really dirty way to ensure that Jitsi cleans up // its hold on the webcam. Without this, the widget holds a media // stream open, even after death. See https://github.com/vector-im/element-web/issues/7351 if (this.iframe) { // In practice we could just do `+= ''` to trick the browser // into thinking the URL changed, however I can foresee this // being optimized out by a browser. Instead, we'll just point // the iframe at a page that is reasonably safe to use in the // event the iframe doesn't wink away. // This is relative to where the Element instance is located. this.iframe.src = 'about:blank'; } if (_WidgetType.WidgetType.JITSI.matches(this.props.app.type)) { _dispatcher.default.dispatch({ action: 'hangup_conference' }); } // Delete the widget from the persisted store for good measure. _PersistedElement.default.destroyElement(this._persistKey); this._sgWidget.stop({ forceDestroy: true }); } formatAppTileName() { let appTileName = "No name"; if (this.props.app.name && this.props.app.name.trim()) { appTileName = this.props.app.name.trim(); } return appTileName; } /** * Whether we're using a local version of the widget rather than loading the * actual widget URL * @returns {bool} true If using a local version of the widget */ _usingLocalWidget() { return _WidgetType.WidgetType.JITSI.matches(this.props.app.type); } _getTileTitle() { const name = this.formatAppTileName(); const titleSpacer = /*#__PURE__*/_react.default.createElement("span", null, "\xA0-\xA0"); let title = ''; if (this.state.widgetPageTitle && this.state.widgetPageTitle !== this.formatAppTileName()) { title = this.state.widgetPageTitle; } return /*#__PURE__*/_react.default.createElement("span", null, /*#__PURE__*/_react.default.createElement(_WidgetAvatar.default, { app: this.props.app }), /*#__PURE__*/_react.default.createElement("b", null, name), /*#__PURE__*/_react.default.createElement("span", null, title ? titleSpacer : '', title)); } // TODO replace with full screen interactions render() { let appTileBody; // Note that there is advice saying allow-scripts shouldn't be used with allow-same-origin // because that would allow the iframe to programmatically remove the sandbox attribute, but // this would only be for content hosted on the same origin as the element client: anything // hosted on the same origin as the client will get the same access as if you clicked // a link to it. const sandboxFlags = "allow-forms allow-popups allow-popups-to-escape-sandbox " + "allow-same-origin allow-scripts allow-presentation"; // Additional iframe feature pemissions // (see - https://sites.google.com/a/chromium.org/dev/Home/chromium-security/deprecating-permissions-in-cross-origin-iframes and https://wicg.github.io/feature-policy/) const iframeFeatures = "microphone; camera; encrypted-media; autoplay; display-capture; clipboard-write;"; const appTileBodyClass = 'mx_AppTileBody' + (this.props.miniMode ? '_mini ' : ' '); const appTileBodyStyles = {}; if (this.props.pointerEvents) { appTileBodyStyles['pointer-events'] = this.props.pointerEvents; } const loadingElement = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AppLoading_spinner_fadeIn" }, /*#__PURE__*/_react.default.createElement(_Spinner.default, { message: (0, _languageHandler._t)("Loading...") })); if (!this.state.hasPermissionToLoad) { // only possible for room widgets, can assert this.props.room here const isEncrypted = _MatrixClientPeg.MatrixClientPeg.get().isRoomEncrypted(this.props.room.roomId); appTileBody = /*#__PURE__*/_react.default.createElement("div", { className: appTileBodyClass, style: appTileBodyStyles }, /*#__PURE__*/_react.default.createElement(_AppPermission.default, { roomId: this.props.room.roomId, creatorUserId: this.props.creatorUserId, url: this._sgWidget.embedUrl, isRoomEncrypted: isEncrypted, onPermissionGranted: this._grantWidgetPermission })); } else if (this.state.initialising) { appTileBody = /*#__PURE__*/_react.default.createElement("div", { className: appTileBodyClass + (this.state.loading ? 'mx_AppLoading' : ''), style: appTileBodyStyles }, loadingElement); } else { if (this.isMixedContent()) { appTileBody = /*#__PURE__*/_react.default.createElement("div", { className: appTileBodyClass, style: appTileBodyStyles }, /*#__PURE__*/_react.default.createElement(_AppWarning.default, { errorMsg: "Error - Mixed content" })); } else { appTileBody = /*#__PURE__*/_react.default.createElement("div", { className: appTileBodyClass + (this.state.loading ? 'mx_AppLoading' : ''), style: appTileBodyStyles }, this.state.loading && loadingElement, /*#__PURE__*/_react.default.createElement("iframe", { allow: iframeFeatures, ref: this._iframeRefChange, src: this._sgWidget.embedUrl, allowFullScreen: true, sandbox: sandboxFlags })); if (!this.props.userWidget) { // All room widgets can theoretically be allowed to remain on screen, so we // wrap them all in a PersistedElement from the get-go. If we wait, the iframe // will be re-mounted later, which means the widget has to start over, which is // bad. // Also wrap the PersistedElement in a div to fix the height, otherwise // AppTile's border is in the wrong place appTileBody = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AppTile_persistedWrapper" }, /*#__PURE__*/_react.default.createElement(_PersistedElement.default, { persistKey: this._persistKey }, appTileBody)); } } } let appTileClasses; if (this.props.miniMode) { appTileClasses = { mx_AppTile_mini: true }; } else if (this.props.fullWidth) { appTileClasses = { mx_AppTileFullWidth: true }; } else { appTileClasses = { mx_AppTile: true }; } appTileClasses = (0, _classnames.default)(appTileClasses); let contextMenu; if (this.state.menuDisplayed) { contextMenu = /*#__PURE__*/_react.default.createElement(_WidgetContextMenu.default, (0, _extends2.default)({}, (0, _ContextMenu.aboveLeftOf)(this._contextMenuButton.current.getBoundingClientRect(), null), { app: this.props.app, onFinished: this._closeContextMenu, showUnpin: !this.props.userWidget, userWidget: this.props.userWidget })); } return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", { className: appTileClasses, id: this.props.app.id }, this.props.showMenubar && /*#__PURE__*/_react.default.createElement("div", { className: "mx_AppTileMenuBar" }, /*#__PURE__*/_react.default.createElement("span", { className: "mx_AppTileMenuBarTitle", style: { pointerEvents: this.props.handleMinimisePointerEvents ? 'all' : false } }, this.props.showTitle && this._getTileTitle()), /*#__PURE__*/_react.default.createElement("span", { className: "mx_AppTileMenuBarWidgets" }, this.props.showPopout && /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, { className: "mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_popout", title: (0, _languageHandler._t)('Popout widget'), onClick: this._onPopoutWidgetClick }), /*#__PURE__*/_react.default.createElement(_ContextMenu.ContextMenuButton, { className: "mx_AppTileMenuBar_iconButton mx_AppTileMenuBar_iconButton_menu", label: (0, _languageHandler._t)("Options"), isExpanded: this.state.menuDisplayed, inputRef: this._contextMenuButton, onClick: this._onContextMenuClick }))), appTileBody), contextMenu); } }, _temp)) || _class); exports.default = AppTile; AppTile.displayName = 'AppTile'; AppTile.propTypes = { app: _propTypes.default.object.isRequired, // If room is not specified then it is an account level widget // which bypasses permission prompts as it was added explicitly by that user room: _propTypes.default.object, // Specifying 'fullWidth' as true will render the app tile to fill the width of the app drawer continer. // This should be set to true when there is only one widget in the app drawer, otherwise it should be false. fullWidth: _propTypes.default.bool, // Optional. If set, renders a smaller view of the widget miniMode: _propTypes.default.bool, // UserId of the current user userId: _propTypes.default.string.isRequired, // UserId of the entity that added / modified the widget creatorUserId: _propTypes.default.string, waitForIframeLoad: _propTypes.default.bool, showMenubar: _propTypes.default.bool, // Optional onEditClickHandler (overrides default behaviour) onEditClick: _propTypes.default.func, // Optional onDeleteClickHandler (overrides default behaviour) onDeleteClick: _propTypes.default.func, // Optional onMinimiseClickHandler onMinimiseClick: _propTypes.default.func, // Optionally hide the tile title showTitle: _propTypes.default.bool, // Optionally handle minimise button pointer events (default false) handleMinimisePointerEvents: _propTypes.default.bool, // Optionally hide the popout widget icon showPopout: _propTypes.default.bool, // Is this an instance of a user widget userWidget: _propTypes.default.bool, // sets the pointer-events property on the iframe pointerEvents: _propTypes.default.string }; AppTile.defaultProps = { waitForIframeLoad: true, showMenubar: true, showTitle: true, showPopout: true, handleMinimisePointerEvents: false, userWidget: false, miniMode: false }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL2VsZW1lbnRzL0FwcFRpbGUuanMiXSwibmFtZXMiOlsiQXBwVGlsZSIsIlJlYWN0IiwiQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsIl91c2luZ0xvY2FsV2lkZ2V0Iiwicm9vbSIsImN1cnJlbnRseUFsbG93ZWRXaWRnZXRzIiwiU2V0dGluZ3NTdG9yZSIsImdldFZhbHVlIiwicm9vbUlkIiwiYXBwIiwiZXZlbnRJZCIsInVuZGVmaW5lZCIsInVzZXJJZCIsImNyZWF0b3JVc2VySWQiLCJoYXNQZXJtaXNzaW9uVG9Mb2FkIiwic3RhdGUiLCJBY3RpdmVXaWRnZXRTdG9yZSIsImRlc3Ryb3lQZXJzaXN0ZW50V2lkZ2V0IiwiaWQiLCJQZXJzaXN0ZWRFbGVtZW50IiwiZGVzdHJveUVsZW1lbnQiLCJfcGVyc2lzdEtleSIsIl9zZ1dpZGdldCIsInN0b3AiLCJzZXRTdGF0ZSIsInJlZiIsImlmcmFtZSIsInN0YXJ0IiwiX3Jlc2V0V2lkZ2V0IiwibG9hZGluZyIsIldpZGdldFR5cGUiLCJKSVRTSSIsIm1hdGNoZXMiLCJ0eXBlIiwid2lkZ2V0QXBpIiwidHJhbnNwb3J0Iiwic2VuZCIsIkVsZW1lbnRXaWRnZXRBY3Rpb25zIiwiQ2xpZW50UmVhZHkiLCJwYXlsb2FkIiwid2lkZ2V0SWQiLCJhY3Rpb24iLCJoYXNDYXBhYmlsaXR5IiwiTWF0cml4Q2FwYWJpbGl0aWVzIiwiU3RpY2tlclNlbmRpbmciLCJkaXMiLCJkaXNwYXRjaCIsImRhdGEiLCJjb25zb2xlIiwid2FybiIsImluZm8iLCJjdXJyZW50IiwibGV2ZWwiLCJmaXJzdFN1cHBvcnRlZExldmVsIiwic2V0VmFsdWUiLCJ0aGVuIiwiX3N0YXJ0V2lkZ2V0IiwiY2F0Y2giLCJlcnIiLCJlcnJvciIsIl9lbmRXaWRnZXRBY3Rpb25zIiwic3JjIiwiZW1iZWRVcmwiLCJPYmplY3QiLCJhc3NpZ24iLCJkb2N1bWVudCIsImNyZWF0ZUVsZW1lbnQiLCJ0YXJnZXQiLCJocmVmIiwicG9wb3V0VXJsIiwicmVsIiwiY2xpY2siLCJtZW51RGlzcGxheWVkIiwiU3RvcEdhcFdpZGdldCIsIm9uIiwiX29uV2lkZ2V0UHJlcGFyZWQiLCJfb25XaWRnZXRSZWFkeSIsIl9nZXROZXdTdGF0ZSIsIl9jb250ZXh0TWVudUJ1dHRvbiIsIl9hbGxvd2VkV2lkZ2V0c1dhdGNoUmVmIiwid2F0Y2hTZXR0aW5nIiwib25BbGxvd2VkV2lkZ2V0c0NoYW5nZSIsIm5ld1Byb3BzIiwiaW5pdGlhbGlzaW5nIiwid2FpdEZvcklmcmFtZUxvYWQiLCJpc01vdW50ZWQiLCJ3aWRnZXRQYWdlVGl0bGUiLCJpc01peGVkQ29udGVudCIsInBhcmVudENvbnRlbnRQcm90b2NvbCIsIndpbmRvdyIsImxvY2F0aW9uIiwicHJvdG9jb2wiLCJ1IiwidXJsIiwicGFyc2UiLCJjaGlsZENvbnRlbnRQcm90b2NvbCIsImNvbXBvbmVudERpZE1vdW50IiwiZGlzcGF0Y2hlclJlZiIsInJlZ2lzdGVyIiwiX29uQWN0aW9uIiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJ1bnJlZ2lzdGVyIiwiZ2V0V2lkZ2V0UGVyc2lzdGVuY2UiLCJ1bndhdGNoU2V0dGluZyIsInByZXBhcmUiLCJVTlNBRkVfY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyIsIm5leHRQcm9wcyIsImZvcmNlRGVzdHJveSIsImZvcm1hdEFwcFRpbGVOYW1lIiwiYXBwVGlsZU5hbWUiLCJuYW1lIiwidHJpbSIsIl9nZXRUaWxlVGl0bGUiLCJ0aXRsZVNwYWNlciIsInRpdGxlIiwicmVuZGVyIiwiYXBwVGlsZUJvZHkiLCJzYW5kYm94RmxhZ3MiLCJpZnJhbWVGZWF0dXJlcyIsImFwcFRpbGVCb2R5Q2xhc3MiLCJtaW5pTW9kZSIsImFwcFRpbGVCb2R5U3R5bGVzIiwicG9pbnRlckV2ZW50cyIsImxvYWRpbmdFbGVtZW50IiwiaXNFbmNyeXB0ZWQiLCJNYXRyaXhDbGllbnRQZWciLCJnZXQiLCJpc1Jvb21FbmNyeXB0ZWQiLCJfZ3JhbnRXaWRnZXRQZXJtaXNzaW9uIiwiX2lmcmFtZVJlZkNoYW5nZSIsInVzZXJXaWRnZXQiLCJhcHBUaWxlQ2xhc3NlcyIsIm14X0FwcFRpbGVfbWluaSIsImZ1bGxXaWR0aCIsIm14X0FwcFRpbGVGdWxsV2lkdGgiLCJteF9BcHBUaWxlIiwiY29udGV4dE1lbnUiLCJnZXRCb3VuZGluZ0NsaWVudFJlY3QiLCJfY2xvc2VDb250ZXh0TWVudSIsInNob3dNZW51YmFyIiwiaGFuZGxlTWluaW1pc2VQb2ludGVyRXZlbnRzIiwic2hvd1RpdGxlIiwic2hvd1BvcG91dCIsIl9vblBvcG91dFdpZGdldENsaWNrIiwiX29uQ29udGV4dE1lbnVDbGljayIsImRpc3BsYXlOYW1lIiwicHJvcFR5cGVzIiwiUHJvcFR5cGVzIiwib2JqZWN0IiwiaXNSZXF1aXJlZCIsImJvb2wiLCJzdHJpbmciLCJvbkVkaXRDbGljayIsImZ1bmMiLCJvbkRlbGV0ZUNsaWNrIiwib25NaW5pbWlzZUNsaWNrIiwiZGVmYXVsdFByb3BzIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7QUFtQkE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7Ozs7SUFHcUJBLE8sV0FEcEIsZ0RBQXFCLHdCQUFyQixDLHlCQUFELE1BQ3FCQSxPQURyQixTQUNxQ0MsZUFBTUMsU0FEM0MsQ0FDcUQ7QUFDakRDLEVBQUFBLFdBQVcsQ0FBQ0MsTUFBRCxFQUFRO0FBQ2YsVUFBTUEsTUFBTixFQURlLENBR2Y7O0FBSGUsK0RBaUJJQSxLQUFELElBQVc7QUFDN0IsVUFBSSxLQUFLQyxpQkFBTCxFQUFKLEVBQThCLE9BQU8sSUFBUDtBQUM5QixVQUFJLENBQUNELEtBQUssQ0FBQ0UsSUFBWCxFQUFpQixPQUFPLElBQVAsQ0FGWSxDQUVDOztBQUU5QixZQUFNQyx1QkFB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QjtBQUNBQyxFQUFBQSxhQUFhLEVBQUVQLG1CQUFVTSxJQW5CVDtBQW9CaEI7QUFDQUUsRUFBQUEsZUFBZSxFQUFFUixtQkFBVU0sSUFyQlg7QUFzQmhCO0FBQ0FaLEVBQUFBLFNBQVMsRUFBRU0sbUJBQVVHLElBdkJMO0FBd0JoQjtBQUNBVixFQUFBQSwyQkFBMkIsRUFBRU8sbUJBQVVHLElBekJ2QjtBQTBCaEI7QUFDQVIsRUFBQUEsVUFBVSxFQUFFSyxtQkFBVUcsSUEzQk47QUE0QmhCO0FBQ0FwQixFQUFBQSxVQUFVLEVBQUVpQixtQkFBVUcsSUE3Qk47QUE4QmhCO0FBQ0E1QixFQUFBQSxhQUFhLEVBQUV5QixtQkFBVUk7QUEvQlQsQ0FBcEI7QUFrQ0F6SixPQUFPLENBQUM4SixZQUFSLEdBQXVCO0FBQ25CeEUsRUFBQUEsaUJBQWlCLEVBQUUsSUFEQTtBQUVuQnVELEVBQUFBLFdBQVcsRUFBRSxJQUZNO0FBR25CRSxFQUFBQSxTQUFTLEVBQUUsSUFIUTtBQUluQkMsRUFBQUEsVUFBVSxFQUFFLElBSk87QUFLbkJGLEVBQUFBLDJCQUEyQixFQUFFLEtBTFY7QUFNbkJWLEVBQUFBLFVBQVUsRUFBRSxLQU5PO0FBT25CVixFQUFBQSxRQUFRLEVBQUU7QUFQUyxDQUF2QiIsInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAxNyBWZWN0b3IgQ3JlYXRpb25zIEx0ZFxuQ29weXJpZ2h0IDIwMTggTmV3IFZlY3RvciBMdGRcbkNvcHlyaWdodCAyMDE5IE1pY2hhZWwgVGVsYXR5bnNraSA8N3QzY2hndXlAZ21haWwuY29tPlxuQ29weXJpZ2h0IDIwMjAgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMCAodGhlIFwiTGljZW5zZVwiKTtcbnlvdSBtYXkgbm90IHVzZSB0aGlzIGZpbGUgZXhjZXB0IGluIGNvbXBsaWFuY2Ugd2l0aCB0aGUgTGljZW5zZS5cbllvdSBtYXkgb2J0YWluIGEgY29weSBvZiB0aGUgTGljZW5zZSBhdFxuXG4gICAgaHR0cDovL3d3dy5hcGFjaGUub3JnL2xpY2Vuc2VzL0xJQ0VOU0UtMi4wXG5cblVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbmRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbldJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxubGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4qL1xuXG5pbXBvcnQgdXJsIGZyb20gJ3VybCc7XG5pbXBvcnQgUmVhY3QsIHtjcmVhdGVSZWZ9IGZyb20gJ3JlYWN0JztcbmltcG9ydCBQcm9wVHlwZXMgZnJvbSAncHJvcC10eXBlcyc7XG5pbXBvcnQge01hdHJpeENsaWVudFBlZ30gZnJvbSAnLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnJztcbmltcG9ydCBBY2Nlc3NpYmxlQnV0dG9uIGZyb20gJy4vQWNjZXNzaWJsZUJ1dHRvbic7XG5pbXBvcnQgeyBfdCB9IGZyb20gJy4uLy4uLy4uL2xhbmd1YWdlSGFuZGxlcic7XG5pbXBvcnQgQXBwUGVybWlzc2lvbiBmcm9tICcuL0FwcFBlcm1pc3Npb24nO1xuaW1wb3J0IEFwcFdhcm5pbmcgZnJvbSAnLi9BcHBXYXJuaW5nJztcbmltcG9ydCBTcGlubmVyIGZyb20gJy4vU3Bpbm5lcic7XG5pbXBvcnQgZGlzIGZyb20gJy4uLy4uLy4uL2Rpc3BhdGNoZXIvZGlzcGF0Y2hlcic7XG5pbXBvcnQgQWN0aXZlV2lkZ2V0U3RvcmUgZnJvbSAnLi4vLi4vLi4vc3RvcmVzL0FjdGl2ZVdpZGdldFN0b3JlJztcbmltcG9ydCBjbGFzc05hbWVzIGZyb20gJ2NsYXNzbmFtZXMnO1xuaW1wb3J0IFNldHRpbmdzU3RvcmUgZnJvbSBcIi4uLy4uLy4uL3NldHRpbmdzL1NldHRpbmdzU3RvcmVcIjtcbmltcG9ydCB7YWJvdmVMZWZ0T2YsIENvbnRleHRNZW51QnV0dG9ufSBmcm9tIFwiLi4vLi4vc3RydWN0dXJlcy9Db250ZXh0TWVudVwiO1xuaW1wb3J0IFBlcnNpc3RlZEVsZW1lbnQsIHtnZXRQZXJzaXN0S2V5fSBmcm9tIFwiLi9QZXJzaXN0ZWRFbGVtZW50XCI7XG5pbXBvcnQge1dpZGdldFR5cGV9IGZyb20gXCIuLi8uLi8uLi93aWRnZXRzL1dpZGdldFR5cGVcIjtcbmltcG9ydCB7U3RvcEdhcFdpZGdldH0gZnJvbSBcIi4uLy4uLy4uL3N0b3Jlcy93aWRnZXRzL1N0b3BHYXBXaWRnZXRcIjtcbmltcG9ydCB7RWxlbWVudFdpZGdldEFjdGlvbnN9IGZyb20gXCIuLi8uLi8uLi9zdG9yZXMvd2lkZ2V0cy9FbGVtZW50V2lkZ2V0QWN0aW9uc1wiO1xuaW1wb3J0IHtNYXRyaXhDYXBhYmlsaXRpZXN9IGZyb20gXCJtYXRyaXgtd2lkZ2V0LWFwaVwiO1xuaW1wb3J0IFJvb21XaWRnZXRDb250ZXh0TWVudSBmcm9tIFwiLi4vY29udGV4dF9tZW51cy9XaWRnZXRDb250ZXh0TWVudVwiO1xuaW1wb3J0IFdpZGdldEF2YXRhciBmcm9tIFwiLi4vYXZhdGFycy9XaWRnZXRBdmF0YXJcIjtcbmltcG9ydCB7cmVwbGFjZWFibGVDb21wb25lbnR9IGZyb20gXCIuLi8uLi8uLi91dGlscy9yZXBsYWNlYWJsZUNvbXBvbmVudFwiO1xuXG5AcmVwbGFjZWFibGVDb21wb25lbnQoXCJ2aWV3cy5lbGVtZW50cy5BcHBUaWxlXCIpXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBBcHBUaWxlIGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHtcbiAgICBjb25zdHJ1Y3Rvcihwcm9wcykge1xuICAgICAgICBzdXBlcihwcm9wcyk7XG5cbiAgICAgICAgLy8gVGhlIGtleSB1c2VkIGZvciBQZXJzaXN0ZWRFbGVtZW50XG4gICAgICAgIHRoaXMuX3BlcnNpc3RLZXkgPSBnZXRQZXJzaXN0S2V5KHRoaXMucHJvcHMuYXBwLmlkKTtcbiAgICAgICAgdGhpcy5fc2dXaWRnZXQgPSBuZXcgU3RvcEdhcFdpZGdldCh0aGlzLnByb3BzKTtcbiAgICAgICAgdGhpcy5fc2dXaWRnZXQub24oXCJwcmVwYXJpbmdcIiwgdGhpcy5fb25XaWRnZXRQcmVwYXJlZCk7XG4gICAgICAgIHRoaXMuX3NnV2lkZ2V0Lm9uKFwicmVhZHlcIiwgdGhpcy5fb25XaWRnZXRSZWFkeSk7XG4gICAgICAgIHRoaXMuaWZyYW1lID0gbnVsbDsgLy8gcmVmIHRvIHRoZSBpZnJhbWUgKGNhbGxiYWNrIHN0eWxlKVxuXG4gICAgICAgIHRoaXMuc3RhdGUgPSB0aGlzLl9nZXROZXdTdGF0ZShwcm9wcyk7XG4gICAgICAgIHRoaXMuX2NvbnRleHRNZW51QnV0dG9uID0gY3JlYXRlUmVmKCk7XG5cbiAgICAgICAgdGhpcy5fYWxsb3dlZFdpZGdldHNXYXRjaFJlZiA9IFNldHRpbmdzU3RvcmUud2F0Y2hTZXR0aW5nKFwiYWxsb3dlZFdpZGdldHNcIiwgbnVsbCwgdGhpcy5vbkFsbG93ZWRXaWRnZXRzQ2hhbmdlKTtcbiAgICB9XG5cbiAgICAvLyBUaGlzIGlzIGEgZnVuY3Rpb24gdG8gbWFrZSB0aGUgaW1wYWN0IG9mIGNhbGxpbmcgU2V0dGluZ3NTdG9yZSBzbGlnaHRseSBsZXNzXG4gICAgaGFzUGVybWlzc2lvblRvTG9hZCA9IChwcm9wcykgPT4ge1xuICAgICAgICBpZiAodGhpcy5fdXNpbmdMb2NhbFdpZGdldCgpKSByZXR1cm4gdHJ1ZTtcbiAgICAgICAgaWYgKCFwcm9wcy5yb29tKSByZXR1cm4gdHJ1ZTsgLy8gdXNlciB3aWRnZXRzIGFsd2F5cyBoYXZlIHBlcm1pc3Npb25zXG5cbiAgICAgICAgY29uc3QgY3VycmVudGx5QWxsb3dlZFdpZGdldHMgPSBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwiYWxsb3dlZFdpZGdldHNcIiwgcHJvcHMucm9vbS5yb29tSWQpO1xuICAgICAgICBpZiAoY3VycmVudGx5QWxsb3dlZFdpZGdldHNbcHJvcHMuYXBwLmV2ZW50SWRdID09PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHJldHVybiBwcm9wcy51c2VySWQgPT09IHByb3BzLmNyZWF0b3JVc2VySWQ7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuICEhY3VycmVudGx5QWxsb3dlZFdpZGdldHNbcHJvcHMuYXBwLmV2ZW50SWRdO1xuICAgIH07XG5cbiAgICAvKipcbiAgICAgKiBTZXQgaW5pdGlhbCBjb21wb25lbnQgc3RhdGUgd2hlbiB0aGUgQXBwIHdVcmwgKHdpZGdldCBVUkwpIGlzIGJlaW5nIHVwZGF0ZWQuXG4gICAgICogQ29tcG9uZW50IHByb3BzICptdXN0KiBiZSBwYXNzZWQgKHJhdGhlciB0aGFuIHJlbHlpbmcgb24gdGhpcy5wcm9wcykuXG4gICAgICogQHBhcmFtICB7T2JqZWN0fSBuZXdQcm9wcyBUaGUgbmV3IHByb3BlcnRpZXMgb2YgdGhlIGNvbXBvbmVudFxuICAgICAqIEByZXR1cm4ge09iamVjdH0gVXBkYXRlZCBjb21wb25lbnQgc3RhdGUgdG8gYmUgc2V0IHdpdGggc2V0U3RhdGVcbiAgICAgKi9cbiAgICBfZ2V0TmV3U3RhdGUobmV3UHJvcHMpIHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIGluaXRpYWxpc2luZzogdHJ1ZSwgLy8gVHJ1ZSB3aGlsZSB3ZSBhcmUgbWFuZ2xpbmcgdGhlIHdpZGdldCBVUkxcbiAgICAgICAgICAgIC8vIFRydWUgd2hpbGUgdGhlIGlmcmFtZSBjb250ZW50IGlzIGxvYWRpbmdcbiAgICAgICAgICAgIGxvYWRpbmc6IHRoaXMucHJvcHMud2FpdEZvcklmcmFtZUxvYWQgJiYgIVBlcnNpc3RlZEVsZW1lbnQuaXNNb3VudGVkKHRoaXMuX3BlcnNpc3RLZXkpLFxuICAgICAgICAgICAgLy8gQXNzdW1lIHRoYXQgd2lkZ2V0IGhhcyBwZXJtaXNzaW9uIHRvIGxvYWQgaWYgd2UgYXJlIHRoZSB1c2VyIHdob1xuICAgICAgICAgICAgLy8gYWRkZWQgaXQgdG8gdGhlIHJvb20sIG9yIGlmIGV4cGxpY2l0bHkgZ3JhbnRlZCBieSB0aGUgdXNlclxuICAgICAgICAgICAgaGFzUGVybWlzc2lvblRvTG9hZDogdGhpcy5oYXNQZXJtaXNzaW9uVG9Mb2FkKG5ld1Byb3BzKSxcbiAgICAgICAgICAgIGVycm9yOiBudWxsLFxuICAgICAgICAgICAgd2lkZ2V0UGFnZVRpdGxlOiBuZXdQcm9wcy53aWRnZXRQYWdlVGl0bGUsXG4gICAgICAgICAgICBtZW51RGlzcGxheWVkOiBmYWxzZSxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBvbkFsbG93ZWRXaWRnZXRzQ2hhbmdlID0gKCkgPT4ge1xuICAgICAgICBjb25zdCBoYXNQZXJtaXNzaW9uVG9Mb2FkID0gdGhpcy5oYXNQZXJtaXNzaW9uVG9Mb2FkKHRoaXMucHJvcHMpO1xuXG4gICAgICAgIGlmICh0aGlzLnN0YXRlLmhhc1Blcm1pc3Npb25Ub0xvYWQgJiYgIWhhc1Blcm1pc3Npb25Ub0xvYWQpIHtcbiAgICAgICAgICAgIC8vIEZvcmNlIHRoZSB3aWRnZXQgdG8gYmUgbm9uLXBlcnNpc3RlbnQgKGFibGUgdG8gYmUgZGVsZXRlZC9mb3Jnb3R0ZW4pXG4gICAgICAgICAgICBBY3RpdmVXaWRnZXRTdG9yZS5kZXN0cm95UGVyc2lzdGVudFdpZGdldCh0aGlzLnByb3BzLmFwcC5pZCk7XG4gICAgICAgICAgICBQZXJzaXN0ZWRFbGVtZW50LmRlc3Ryb3lFbGVtZW50KHRoaXMuX3BlcnNpc3RLZXkpO1xuICAgICAgICAgICAgdGhpcy5fc2dXaWRnZXQuc3RvcCgpO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IGhhc1Blcm1pc3Npb25Ub0xvYWQgfSk7XG4gICAgfTtcblxuICAgIGlzTWl4ZWRDb250ZW50KCkge1xuICAgICAgICBjb25zdCBwYXJlbnRDb250ZW50UHJvdG9jb2wgPSB3aW5kb3cubG9jYXRpb24ucHJvdG9jb2w7XG4gICAgICAgIGNvbnN0IHUgPSB1cmwucGFyc2UodGhpcy5wcm9wcy5hcHAudXJsKTtcbiAgICAgICAgY29uc3QgY2hpbGRDb250ZW50UHJvdG9jb2wgPSB1LnByb3RvY29sO1xuICAgICAgICBpZiAocGFyZW50Q29udGVudFByb3RvY29sID09PSAnaHR0cHM6JyAmJiBjaGlsZENvbnRlbnRQcm90b2NvbCAhPT0gJ2h0dHBzOicpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIlJlZnVzaW5nIHRvIGxvYWQgbWl4ZWQtY29udGVudCBhcHA6XCIsXG4gICAgICAgICAgICAgICAgcGFyZW50Q29udGVudFByb3RvY29sLCBjaGlsZENvbnRlbnRQcm90b2NvbCwgd2luZG93LmxvY2F0aW9uLCB0aGlzLnByb3BzLmFwcC51cmwpO1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIGNvbXBvbmVudERpZE1vdW50KCkge1xuICAgICAgICAvLyBPbmx5IGZldGNoIElNIHRva2VuIG9uIG1vdW50IGlmIHdlJ3JlIHNob3dpbmcgYW5kIGhhdmUgcGVybWlzc2lvbiB0byBsb2FkXG4gICAgICAgIGlmICh0aGlzLnN0YXRlLmhhc1Blcm1pc3Npb25Ub0xvYWQpIHtcbiAgICAgICAgICAgIHRoaXMuX3N0YXJ0V2lkZ2V0KCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBXaWRnZXQgYWN0aW9uIGxpc3RlbmVyc1xuICAgICAgICB0aGlzLmRpc3BhdGNoZXJSZWYgPSBkaXMucmVnaXN0ZXIodGhpcy5fb25BY3Rpb24pO1xuICAgIH1cblxuICAgIGNvbXBvbmVudFdpbGxVbm1vdW50KCkge1xuICAgICAgICAvLyBXaWRnZXQgYWN0aW9uIGxpc3RlbmVyc1xuICAgICAgICBpZiAodGhpcy5kaXNwYXRjaGVyUmVmKSBkaXMudW5yZWdpc3Rlcih0aGlzLmRpc3BhdGNoZXJSZWYpO1xuXG4gICAgICAgIC8vIGlmIGl0J3Mgbm90IHJlbWFpbmluZyBvbiBzY3JlZW4sIGdldCByaWQgb2YgdGhlIFBlcnNpc3RlZEVsZW1lbnQgY29udGFpbmVyXG4gICAgICAgIGlmICghQWN0aXZlV2lkZ2V0U3RvcmUuZ2V0V2lkZ2V0UGVyc2lzdGVuY2UodGhpcy5wcm9wcy5hcHAuaWQpKSB7XG4gICAgICAgICAgICBBY3RpdmVXaWRnZXRTdG9yZS5kZXN0cm95UGVyc2lzdGVudFdpZGdldCh0aGlzLnByb3BzLmFwcC5pZCk7XG4gICAgICAgICAgICBQZXJzaXN0ZWRFbGVtZW50LmRlc3Ryb3lFbGVtZW50KHRoaXMuX3BlcnNpc3RLZXkpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX3NnV2lkZ2V0KSB7XG4gICAgICAgICAgICB0aGlzLl9zZ1dpZGdldC5zdG9wKCk7XG4gICAgICAgIH1cblxuICAgICAgICBTZXR0aW5nc1N0b3JlLnVud2F0Y2hTZXR0aW5nKHRoaXMuX2FsbG93ZWRXaWRnZXRzV2F0Y2hSZWYpO1xuICAgIH1cblxuICAgIF9yZXNldFdpZGdldChuZXdQcm9wcykge1xuICAgICAgICBpZiAodGhpcy5fc2dXaWRnZXQpIHtcbiAgICAgICAgICAgIHRoaXMuX3NnV2lkZ2V0LnN0b3AoKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLl9zZ1dpZGdldCA9IG5ldyBTdG9wR2FwV2lkZ2V0KG5ld1Byb3BzKTtcbiAgICAgICAgdGhpcy5fc2dXaWRnZXQub24oXCJwcmVwYXJpbmdcIiwgdGhpcy5fb25XaWRnZXRQcmVwYXJlZCk7XG4gICAgICAgIHRoaXMuX3NnV2lkZ2V0Lm9uKFwicmVhZHlcIiwgdGhpcy5fb25XaWRnZXRSZWFkeSk7XG4gICAgICAgIHRoaXMuX3N0YXJ0V2lkZ2V0KCk7XG4gICAgfVxuXG4gICAgX3N0YXJ0V2lkZ2V0KCkge1xuICAgICAgICB0aGlzLl9zZ1dpZGdldC5wcmVwYXJlKCkudGhlbigoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnNldFN0YXRlKHtpbml0aWFsaXNpbmc6IGZhbHNlfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIF9pZnJhbWVSZWZDaGFuZ2UgPSAocmVmKSA9PiB7XG4gICAgICAgIHRoaXMuaWZyYW1lID0gcmVmO1xuICAgICAgICBpZiAocmVmKSB7XG4gICAgICAgICAgICB0aGlzLl9zZ1dpZGdldC5zdGFydChyZWYpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5fcmVzZXRXaWRnZXQodGhpcy5wcm9wcyk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgLy8gVE9ETzogW1JFQUNULVdBUk5JTkddIFJlcGxhY2Ugd2l0aCBhcHByb3ByaWF0ZSBsaWZlY3ljbGUgZXZlbnRcbiAgICBVTlNBRkVfY29tcG9uZW50V2lsbFJlY2VpdmVQcm9wcyhuZXh0UHJvcHMpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBjYW1lbGNhc2VcbiAgICAgICAgaWYgKG5leHRQcm9wcy5hcHAudXJsICE9PSB0aGlzLnByb3BzLmFwcC51cmwpIHtcbiAgICAgICAgICAgIHRoaXMuX2dldE5ld1N0YXRlKG5leHRQcm9wcyk7XG4gICAgICAgICAgICBpZiAodGhpcy5zdGF0ZS5oYXNQZXJtaXNzaW9uVG9Mb2FkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5fcmVzZXRXaWRnZXQobmV4dFByb3BzKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChuZXh0UHJvcHMud2lkZ2V0UGFnZVRpdGxlICE9PSB0aGlzLnByb3BzLndpZGdldFBhZ2VUaXRsZSkge1xuICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7XG4gICAgICAgICAgICAgICAgd2lkZ2V0UGFnZVRpdGxlOiBuZXh0UHJvcHMud2lkZ2V0UGFnZVRpdGxlLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBFbmRzIGFsbCB3aWRnZXQgaW50ZXJhY3Rpb24sIHN1Y2ggYXMgY2FuY2VsbGluZyBjYWxscyBhbmQgZGlzYWJsaW5nIHdlYmNhbXMuXG4gICAgICogQHByaXZhdGVcbiAgICAgKiBAcmV0dXJucyB7UHJvbWlzZTwqPn0gUmVzb2x2ZXMgd2hlbiB0aGUgd2lkZ2V0IGlzIHRlcm1pbmF0ZWQsIG9yIHRpbWVvdXQgcGFzc2VkLlxuICAgICAqL1xuICAgIGFzeW5jIF9lbmRXaWRnZXRBY3Rpb25zKCkgeyAvLyB3aWRnZ