UNPKG

matrix-react-sdk

Version:
542 lines (437 loc) 62.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.HiddenImagePlaceholder = exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _MFileBody = _interopRequireDefault(require("./MFileBody")); var _Modal = _interopRequireDefault(require("../../../Modal")); var sdk = _interopRequireWildcard(require("../../../index")); var _DecryptFile = require("../../../utils/DecryptFile"); var _languageHandler = require("../../../languageHandler"); var _SettingsStore = _interopRequireDefault(require("../../../settings/SettingsStore")); var _MatrixClientContext = _interopRequireDefault(require("../../../contexts/MatrixClientContext")); var _InlineSpinner = _interopRequireDefault(require("../elements/InlineSpinner")); var _replaceableComponent = require("../../../utils/replaceableComponent"); var _Media = require("../../../customisations/Media"); var _dec, _class, _class2, _temp; let MImageBody = (_dec = (0, _replaceableComponent.replaceableComponent)("views.messages.MImageBody"), _dec(_class = (_temp = _class2 = class MImageBody extends _react.default.Component { constructor(props) { super(props); this.onImageError = this.onImageError.bind(this); this.onImageLoad = this.onImageLoad.bind(this); this.onImageEnter = this.onImageEnter.bind(this); this.onImageLeave = this.onImageLeave.bind(this); this.onClientSync = this.onClientSync.bind(this); this.onClick = this.onClick.bind(this); this._isGif = this._isGif.bind(this); this.state = { decryptedUrl: null, decryptedThumbnailUrl: null, decryptedBlob: null, error: null, imgError: false, imgLoaded: false, loadedImageDimensions: null, hover: false, showImage: _SettingsStore.default.getValue("showImages") }; this._image = /*#__PURE__*/(0, _react.createRef)(); } // FIXME: factor this out and apply it to MVideoBody and MAudioBody too! onClientSync(syncState, prevState) { if (this.unmounted) return; // Consider the client reconnected if there is no error with syncing. // This means the state could be RECONNECTING, SYNCING, PREPARED or CATCHUP. const reconnected = syncState !== "ERROR" && prevState !== syncState; if (reconnected && this.state.imgError) { // Load the image again this.setState({ imgError: false }); } } showImage() { localStorage.setItem("mx_ShowImage_" + this.props.mxEvent.getId(), "true"); this.setState({ showImage: true }); this._downloadImage(); } onClick(ev) { if (ev.button === 0 && !ev.metaKey) { ev.preventDefault(); if (!this.state.showImage) { this.showImage(); return; } const content = this.props.mxEvent.getContent(); const httpUrl = this._getContentUrl(); const ImageView = sdk.getComponent("elements.ImageView"); const params = { src: httpUrl, name: content.body && content.body.length > 0 ? content.body : (0, _languageHandler._t)('Attachment'), mxEvent: this.props.mxEvent, permalinkCreator: this.props.permalinkCreator }; if (content.info) { params.width = content.info.w; params.height = content.info.h; params.fileSize = content.info.size; } _Modal.default.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); } } _isGif() { const content = this.props.mxEvent.getContent(); return content && content.info && content.info.mimetype === "image/gif"; } onImageEnter(e) { this.setState({ hover: true }); if (!this.state.showImage || !this._isGif() || _SettingsStore.default.getValue("autoplayGifsAndVideos")) { return; } const imgElement = e.target; imgElement.src = this._getContentUrl(); } onImageLeave(e) { this.setState({ hover: false }); if (!this.state.showImage || !this._isGif() || _SettingsStore.default.getValue("autoplayGifsAndVideos")) { return; } const imgElement = e.target; imgElement.src = this._getThumbUrl(); } onImageError() { this.setState({ imgError: true }); } onImageLoad() { this.props.onHeightChanged(); let loadedImageDimensions; if (this._image.current) { const { naturalWidth, naturalHeight } = this._image.current; // this is only used as a fallback in case content.info.w/h is missing loadedImageDimensions = { naturalWidth, naturalHeight }; } this.setState({ imgLoaded: true, loadedImageDimensions }); } _getContentUrl() { const media = (0, _Media.mediaFromContent)(this.props.mxEvent.getContent()); if (media.isEncrypted) { return this.state.decryptedUrl; } else { return media.srcHttp; } } _getThumbUrl() { // FIXME: we let images grow as wide as you like, rather than capped to 800x600. // So either we need to support custom timeline widths here, or reimpose the cap, otherwise the // thumbnail resolution will be unnecessarily reduced. // custom timeline widths seems preferable. const thumbWidth = 800; const thumbHeight = 600; const content = this.props.mxEvent.getContent(); const media = (0, _Media.mediaFromContent)(content); if (media.isEncrypted) { // Don't use the thumbnail for clients wishing to autoplay gifs. if (this.state.decryptedThumbnailUrl) { return this.state.decryptedThumbnailUrl; } return this.state.decryptedUrl; } else if (content.info && content.info.mimetype === "image/svg+xml" && media.hasThumbnail) { // special case to return clientside sender-generated thumbnails for SVGs, if any, // given we deliberately don't thumbnail them serverside to prevent // billion lol attacks and similar return media.getThumbnailHttp(thumbWidth, thumbHeight, 'scale'); } else { // we try to download the correct resolution // for hi-res images (like retina screenshots). // synapse only supports 800x600 thumbnails for now though, // so we'll need to download the original image for this to work // well for now. First, let's try a few cases that let us avoid // downloading the original, including: // - When displaying a GIF, we always want to thumbnail so that we can // properly respect the user's GIF autoplay setting (which relies on // thumbnailing to produce the static preview image) // - On a low DPI device, always thumbnail to save bandwidth // - If there's no sizing info in the event, default to thumbnail const info = content.info; if (this._isGif() || window.devicePixelRatio === 1.0 || !info || !info.w || !info.h || !info.size) { return media.getThumbnailOfSourceHttp(thumbWidth, thumbHeight); } else { // we should only request thumbnails if the image is bigger than 800x600 // (or 1600x1200 on retina) otherwise the image in the timeline will just // end up resampled and de-retina'd for no good reason. // Ideally the server would pregen 1600x1200 thumbnails in order to provide retina // thumbnails, but we don't do this currently in synapse for fear of disk space. // As a compromise, let's switch to non-retina thumbnails only if the original // image is both physically too large and going to be massive to load in the // timeline (e.g. >1MB). const isLargerThanThumbnail = info.w > thumbWidth || info.h > thumbHeight; const isLargeFileSize = info.size > 1 * 1024 * 1024; // 1mb if (isLargeFileSize && isLargerThanThumbnail) { // image is too large physically and bytewise to clutter our timeline so // we ask for a thumbnail, despite knowing that it will be max 800x600 // despite us being retina (as synapse doesn't do 1600x1200 thumbs yet). return media.getThumbnailOfSourceHttp(thumbWidth, thumbHeight); } else { // download the original image otherwise, so we can scale it client side // to take pixelRatio into account. return media.srcHttp; } } } } _downloadImage() { const content = this.props.mxEvent.getContent(); if (content.file !== undefined && this.state.decryptedUrl === null) { let thumbnailPromise = Promise.resolve(null); if (content.info && content.info.thumbnail_file) { thumbnailPromise = (0, _DecryptFile.decryptFile)(content.info.thumbnail_file).then(function (blob) { return URL.createObjectURL(blob); }); } let decryptedBlob; thumbnailPromise.then(thumbnailUrl => { return (0, _DecryptFile.decryptFile)(content.file).then(function (blob) { decryptedBlob = blob; return URL.createObjectURL(blob); }).then(contentUrl => { if (this.unmounted) return; this.setState({ decryptedUrl: contentUrl, decryptedThumbnailUrl: thumbnailUrl, decryptedBlob: decryptedBlob }); }); }).catch(err => { if (this.unmounted) return; console.warn("Unable to decrypt attachment: ", err); // Set a placeholder image when we can't decrypt the image. this.setState({ error: err }); }); } } componentDidMount() { this.unmounted = false; this.context.on('sync', this.onClientSync); const showImage = this.state.showImage || localStorage.getItem("mx_ShowImage_" + this.props.mxEvent.getId()) === "true"; if (showImage) { // Don't download anything becaue we don't want to display anything. this._downloadImage(); this.setState({ showImage: true }); } this._afterComponentDidMount(); } // To be overridden by subclasses (e.g. MStickerBody) for further // initialisation after componentDidMount _afterComponentDidMount() {} componentWillUnmount() { this.unmounted = true; this.context.removeListener('sync', this.onClientSync); this._afterComponentWillUnmount(); if (this.state.decryptedUrl) { URL.revokeObjectURL(this.state.decryptedUrl); } if (this.state.decryptedThumbnailUrl) { URL.revokeObjectURL(this.state.decryptedThumbnailUrl); } } // To be overridden by subclasses (e.g. MStickerBody) for further // cleanup after componentWillUnmount _afterComponentWillUnmount() {} _messageContent(contentUrl, thumbUrl, content) { let infoWidth; let infoHeight; if (content && content.info && content.info.w && content.info.h) { infoWidth = content.info.w; infoHeight = content.info.h; } else { // Whilst the image loads, display nothing. // // Once loaded, use the loaded image dimensions stored in `loadedImageDimensions`. // // By doing this, the image "pops" into the timeline, but is still restricted // by the same width and height logic below. if (!this.state.loadedImageDimensions) { let imageElement; if (!this.state.showImage) { imageElement = /*#__PURE__*/_react.default.createElement(HiddenImagePlaceholder, null); } else { imageElement = /*#__PURE__*/_react.default.createElement("img", { style: { display: 'none' }, src: thumbUrl, ref: this._image, alt: content.body, onError: this.onImageError, onLoad: this.onImageLoad }); } return this.wrapImage(contentUrl, imageElement); } infoWidth = this.state.loadedImageDimensions.naturalWidth; infoHeight = this.state.loadedImageDimensions.naturalHeight; } // The maximum height of the thumbnail as it is rendered as an <img> const maxHeight = Math.min(this.props.maxImageHeight || 600, infoHeight); // The maximum width of the thumbnail, as dictated by its natural // maximum height. const maxWidth = infoWidth * maxHeight / infoHeight; let img = null; let placeholder = null; let gifLabel = null; // e2e image hasn't been decrypted yet if (content.file !== undefined && this.state.decryptedUrl === null) { placeholder = /*#__PURE__*/_react.default.createElement(_InlineSpinner.default, { w: 32, h: 32 }); } else if (!this.state.imgLoaded) { // Deliberately, getSpinner is left unimplemented here, MStickerBody overides placeholder = this.getPlaceholder(); } let showPlaceholder = Boolean(placeholder); if (thumbUrl && !this.state.imgError) { // Restrict the width of the thumbnail here, otherwise it will fill the container // which has the same width as the timeline // mx_MImageBody_thumbnail resizes img to exactly container size img = /*#__PURE__*/_react.default.createElement("img", { className: "mx_MImageBody_thumbnail", src: thumbUrl, ref: this._image, style: { maxWidth: maxWidth + "px" }, alt: content.body, onError: this.onImageError, onLoad: this.onImageLoad, onMouseEnter: this.onImageEnter, onMouseLeave: this.onImageLeave }); } if (!this.state.showImage) { img = /*#__PURE__*/_react.default.createElement(HiddenImagePlaceholder, { style: { maxWidth: maxWidth + "px" } }); showPlaceholder = false; // because we're hiding the image, so don't show the sticker icon. } if (this._isGif() && !_SettingsStore.default.getValue("autoplayGifsAndVideos") && !this.state.hover) { gifLabel = /*#__PURE__*/_react.default.createElement("p", { className: "mx_MImageBody_gifLabel" }, "GIF"); } const thumbnail = /*#__PURE__*/_react.default.createElement("div", { className: "mx_MImageBody_thumbnail_container", style: { maxHeight: maxHeight + "px" } }, /*#__PURE__*/_react.default.createElement("div", { style: { paddingBottom: 100 * infoHeight / infoWidth + '%' } }), showPlaceholder && /*#__PURE__*/_react.default.createElement("div", { className: "mx_MImageBody_thumbnail", style: { // Constrain width here so that spinner appears central to the loaded thumbnail maxWidth: infoWidth + "px" } }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_MImageBody_thumbnail_spinner" }, placeholder)), /*#__PURE__*/_react.default.createElement("div", { style: { display: !showPlaceholder ? undefined : 'none' } }, img, gifLabel), this.state.hover && this.getTooltip()); return this.wrapImage(contentUrl, thumbnail); } // Overidden by MStickerBody wrapImage(contentUrl, children) { return /*#__PURE__*/_react.default.createElement("a", { href: contentUrl, onClick: this.onClick }, children); } // Overidden by MStickerBody getPlaceholder() { // MImageBody doesn't show a placeholder whilst the image loads, (but it could do) return null; } // Overidden by MStickerBody getTooltip() { return null; } // Overidden by MStickerBody getFileBody() { return /*#__PURE__*/_react.default.createElement(_MFileBody.default, (0, _extends2.default)({}, this.props, { decryptedBlob: this.state.decryptedBlob, showGenericPlaceholder: false })); } render() { const content = this.props.mxEvent.getContent(); if (this.state.error !== null) { return /*#__PURE__*/_react.default.createElement("span", { className: "mx_MImageBody" }, /*#__PURE__*/_react.default.createElement("img", { src: require("../../../../res/img/warning.svg"), width: "16", height: "16" }), (0, _languageHandler._t)("Error decrypting image")); } const contentUrl = this._getContentUrl(); let thumbUrl; if (this._isGif() && _SettingsStore.default.getValue("autoplayGifsAndVideos")) { thumbUrl = contentUrl; } else { thumbUrl = this._getThumbUrl(); } const thumbnail = this._messageContent(contentUrl, thumbUrl, content); const fileBody = this.getFileBody(); return /*#__PURE__*/_react.default.createElement("span", { className: "mx_MImageBody" }, thumbnail, fileBody); } }, (0, _defineProperty2.default)(_class2, "propTypes", { /* the MatrixEvent to show */ mxEvent: _propTypes.default.object.isRequired, /* called when the image has loaded */ onHeightChanged: _propTypes.default.func.isRequired, /* the maximum image height to use */ maxImageHeight: _propTypes.default.number, /* the permalinkCreator */ permalinkCreator: _propTypes.default.object }), (0, _defineProperty2.default)(_class2, "contextType", _MatrixClientContext.default), _temp)) || _class); exports.default = MImageBody; class HiddenImagePlaceholder extends _react.default.PureComponent { render() { let className = 'mx_HiddenImagePlaceholder'; if (this.props.hover) className += ' mx_HiddenImagePlaceholder_hover'; return /*#__PURE__*/_react.default.createElement("div", { className: className }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_HiddenImagePlaceholder_button" }, /*#__PURE__*/_react.default.createElement("span", { className: "mx_HiddenImagePlaceholder_eye" }), /*#__PURE__*/_react.default.createElement("span", null, (0, _languageHandler._t)("Show image")))); } } exports.HiddenImagePlaceholder = HiddenImagePlaceholder; (0, _defineProperty2.default)(HiddenImagePlaceholder, "propTypes", { hover: _propTypes.default.bool }); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL21lc3NhZ2VzL01JbWFnZUJvZHkuanMiXSwibmFtZXMiOlsiTUltYWdlQm9keSIsIlJlYWN0IiwiQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsIm9uSW1hZ2VFcnJvciIsImJpbmQiLCJvbkltYWdlTG9hZCIsIm9uSW1hZ2VFbnRlciIsIm9uSW1hZ2VMZWF2ZSIsIm9uQ2xpZW50U3luYyIsIm9uQ2xpY2siLCJfaXNHaWYiLCJzdGF0ZSIsImRlY3J5cHRlZFVybCIsImRlY3J5cHRlZFRodW1ibmFpbFVybCIsImRlY3J5cHRlZEJsb2IiLCJlcnJvciIsImltZ0Vycm9yIiwiaW1nTG9hZGVkIiwibG9hZGVkSW1hZ2VEaW1lbnNpb25zIiwiaG92ZXIiLCJzaG93SW1hZ2UiLCJTZXR0aW5nc1N0b3JlIiwiZ2V0VmFsdWUiLCJfaW1hZ2UiLCJzeW5jU3RhdGUiLCJwcmV2U3RhdGUiLCJ1bm1vdW50ZWQiLCJyZWNvbm5lY3RlZCIsInNldFN0YXRlIiwibG9jYWxTdG9yYWdlIiwic2V0SXRlbSIsIm14RXZlbnQiLCJnZXRJZCIsIl9kb3dubG9hZEltYWdlIiwiZXYiLCJidXR0b24iLCJtZXRhS2V5IiwicHJldmVudERlZmF1bHQiLCJjb250ZW50IiwiZ2V0Q29udGVudCIsImh0dHBVcmwiLCJfZ2V0Q29udGVudFVybCIsIkltYWdlVmlldyIsInNkayIsImdldENvbXBvbmVudCIsInBhcmFtcyIsInNyYyIsIm5hbWUiLCJib2R5IiwibGVuZ3RoIiwicGVybWFsaW5rQ3JlYXRvciIsImluZm8iLCJ3aWR0aCIsInciLCJoZWlnaHQiLCJoIiwiZmlsZVNpemUiLCJzaXplIiwiTW9kYWwiLCJjcmVhdGVEaWFsb2ciLCJtaW1ldHlwZSIsImUiLCJpbWdFbGVtZW50IiwidGFyZ2V0IiwiX2dldFRodW1iVXJsIiwib25IZWlnaHRDaGFuZ2VkIiwiY3VycmVudCIsIm5hdHVyYWxXaWR0aCIsIm5hdHVyYWxIZWlnaHQiLCJtZWRpYSIsImlzRW5jcnlwdGVkIiwic3JjSHR0cCIsInRodW1iV2lkdGgiLCJ0aHVtYkhlaWdodCIsImhhc1RodW1ibmFpbCIsImdldFRodW1ibmFpbEh0dHAiLCJ3aW5kb3ciLCJkZXZpY2VQaXhlbFJhdGlvIiwiZ2V0VGh1bWJuYWlsT2ZTb3VyY2VIdHRwIiwiaXNMYXJnZXJUaGFuVGh1bWJuYWlsIiwiaXNMYXJnZUZpbGVTaXplIiwiZmlsZSIsInVuZGVmaW5lZCIsInRodW1ibmFpbFByb21pc2UiLCJQcm9taXNlIiwicmVzb2x2ZSIsInRodW1ibmFpbF9maWxlIiwidGhlbiIsImJsb2IiLCJVUkwiLCJjcmVhdGVPYmplY3RVUkwiLCJ0aHVtYm5haWxVcmwiLCJjb250ZW50VXJsIiwiY2F0Y2giLCJlcnIiLCJjb25zb2xlIiwid2FybiIsImNvbXBvbmVudERpZE1vdW50IiwiY29udGV4dCIsIm9uIiwiZ2V0SXRlbSIsIl9hZnRlckNvbXBvbmVudERpZE1vdW50IiwiY29tcG9uZW50V2lsbFVubW91bnQiLCJyZW1vdmVMaXN0ZW5lciIsIl9hZnRlckNvbXBvbmVudFdpbGxVbm1vdW50IiwicmV2b2tlT2JqZWN0VVJMIiwiX21lc3NhZ2VDb250ZW50IiwidGh1bWJVcmwiLCJpbmZvV2lkdGgiLCJpbmZvSGVpZ2h0IiwiaW1hZ2VFbGVtZW50IiwiZGlzcGxheSIsIndyYXBJbWFnZSIsIm1heEhlaWdodCIsIk1hdGgiLCJtaW4iLCJtYXhJbWFnZUhlaWdodCIsIm1heFdpZHRoIiwiaW1nIiwicGxhY2Vob2xkZXIiLCJnaWZMYWJlbCIsImdldFBsYWNlaG9sZGVyIiwic2hvd1BsYWNlaG9sZGVyIiwiQm9vbGVhbiIsInRodW1ibmFpbCIsInBhZGRpbmdCb3R0b20iLCJnZXRUb29sdGlwIiwiY2hpbGRyZW4iLCJnZXRGaWxlQm9keSIsInJlbmRlciIsInJlcXVpcmUiLCJmaWxlQm9keSIsIlByb3BUeXBlcyIsIm9iamVjdCIsImlzUmVxdWlyZWQiLCJmdW5jIiwibnVtYmVyIiwiTWF0cml4Q2xpZW50Q29udGV4dCIsIkhpZGRlbkltYWdlUGxhY2Vob2xkZXIiLCJQdXJlQ29tcG9uZW50IiwiY2xhc3NOYW1lIiwiYm9vbCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7O0FBa0JBOztBQUNBOztBQUVBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOzs7O0lBR3FCQSxVLFdBRHBCLGdEQUFxQiwyQkFBckIsQyxtQ0FBRCxNQUNxQkEsVUFEckIsU0FDd0NDLGVBQU1DLFNBRDlDLENBQ3dEO0FBaUJwREMsRUFBQUEsV0FBVyxDQUFDQyxLQUFELEVBQVE7QUFDZixVQUFNQSxLQUFOO0FBRUEsU0FBS0MsWUFBTCxHQUFvQixLQUFLQSxZQUFMLENBQWtCQyxJQUFsQixDQUF1QixJQUF2QixDQUFwQjtBQUNBLFNBQUtDLFdBQUwsR0FBbUIsS0FBS0EsV0FBTCxDQUFpQkQsSUFBakIsQ0FBc0IsSUFBdEIsQ0FBbkI7QUFDQSxTQUFLRSxZQUFMLEdBQW9CLEtBQUtBLFlBQUwsQ0FBa0JGLElBQWxCLENBQXVCLElBQXZCLENBQXBCO0FBQ0EsU0FBS0csWUFBTCxHQUFvQixLQUFLQSxZQUFMLENBQWtCSCxJQUFsQixDQUF1QixJQUF2QixDQUFwQjtBQUNBLFNBQUtJLFlBQUwsR0FBb0IsS0FBS0EsWUFBTCxDQUFrQkosSUFBbEIsQ0FBdUIsSUFBdkIsQ0FBcEI7QUFDQSxTQUFLSyxPQUFMLEdBQWUsS0FBS0EsT0FBTCxDQUFhTCxJQUFiLENBQWtCLElBQWxCLENBQWY7QUFDQSxTQUFLTSxNQUFMLEdBQWMsS0FBS0EsTUFBTCxDQUFZTixJQUFaLENBQWlCLElBQWpCLENBQWQ7QUFFQSxTQUFLTyxLQUFMLEdBQWE7QUFDVEMsTUFBQUEsWUFBWSxFQUFFLElBREw7QUFFVEMsTUFBQUEscUJBQXFCLEVBQUUsSUFGZDtBQUdUQyxNQUFBQSxhQUFhLEVBQUUsSUFITjtBQUlUQyxNQUFBQSxLQUFLLEVBQUUsSUFKRTtBQUtUQyxNQUFBQSxRQUFRLEVBQUUsS0FMRDtBQU1UQyxNQUFBQSxTQUFTLEVBQUUsS0FORjtBQU9UQyxNQUFBQSxxQkFBcUIsRUFBRSxJQVBkO0FBUVRDLE1BQUFBLEtBQUssRUFBRSxLQVJFO0FBU1RDLE1BQUFBLFNBQVMsRUFBRUMsdUJBQWNDLFFBQWQsQ0FBdUIsWUFBdkI7QUFURixLQUFiO0FBWUEsU0FBS0MsTUFBTCxnQkFBYyx1QkFBZDtBQUNILEdBekNtRCxDQTJDcEQ7OztBQUNBZixFQUFBQSxZQUFZLENBQUNnQixTQUFELEVBQVlDLFNBQVosRUFBdUI7QUFDL0IsUUFBSSxLQUFLQyxTQUFULEVBQW9CLE9BRFcsQ0FFL0I7QUFDQTs7QUFDQSxVQUFNQyxXQUFXLEdBQUdILFNBQVMsS0FBSyxPQUFkLElBQXlCQyxTQUFTLEtBQUtELFNBQTNEOztBQUNBLFFBQUlHLFdBQVcsSUFBSSxLQUFLaEIsS0FBTCxDQUFXSyxRQUE5QixFQUF3QztBQUNwQztBQUNBLFdBQUtZLFFBQUwsQ0FBYztBQUNWWixRQUFBQSxRQUFRLEVBQUU7QUFEQSxPQUFkO0FBR0g7QUFDSjs7QUFFREksRUFBQUEsU0FBUyxHQUFHO0FBQ1JTLElBQUFBLFlBQVksQ0FBQ0MsT0FBYixDQUFxQixrQkFBa0IsS0FBSzVCLEtBQUwsQ0FBVzZCLE9BQVgsQ0FBbUJDLEtBQW5CLEVBQXZDLEVBQW1FLE1BQW5FO0FBQ0EsU0FBS0osUUFBTCxDQUFjO0FBQUNSLE1BQUFBLFNBQVMsRUFBRTtBQUFaLEtBQWQ7O0FBQ0EsU0FBS2EsY0FBTDtBQUNIOztBQUVEeEIsRUFBQUEsT0FBTyxDQUFDeUIsRUFBRCxFQUFLO0FBQ1IsUUFBSUEsRUFBRSxDQUFDQyxNQUFILEtBQWMsQ0FBZCxJQUFtQixDQUFDRCxFQUFFLENBQUNFLE9BQTNCLEVBQW9DO0FBQ2hDRixNQUFBQSxFQUFFLENBQUNHLGNBQUg7O0FBQ0EsVUFBSSxDQUFDLEtBQUsxQixLQUFMLENBQVdTLFNBQWhCLEVBQTJCO0FBQ3ZCLGFBQUtBLFNBQUw7QUFDQTtBQUNIOztBQUVELFlBQU1rQixPQUFPLEdBQUcsS0FBS3BDLEtBQUwsQ0FBVzZCLE9BQVgsQ0FBbUJRLFVBQW5CLEVBQWhCOztBQUNBLFlBQU1DLE9BQU8sR0FBRyxLQUFLQyxjQUFMLEVBQWhCOztBQUNBLFlBQU1DLFNBQVMsR0FBR0MsR0FBRyxDQUFDQyxZQUFKLENBQWlCLG9CQUFqQixDQUFsQjtBQUNBLFlBQU1DLE1BQU0sR0FBRztBQUNYQyxRQUFBQSxHQUFHLEVBQUVOLE9BRE07QUFFWE8sUUFBQUEsSUFBSSxFQUFFVCxPQUFPLENBQUNVLElBQVIsSUFBZ0JWLE9BQU8sQ0FBQ1UsSUFBUixDQUFhQyxNQUFiLEdBQXNCLENBQXRDLEdBQTBDWCxPQUFPLENBQUNVLElBQWxELEdBQXlELHlCQUFHLFlBQUgsQ0FGcEQ7QUFHWGpCLFFBQUFBLE9BQU8sRUFBRSxLQUFLN0IsS0FBTCxDQUFXNkIsT0FIVDtBQUlYbUIsUUFBQUEsZ0JBQWdCLEVBQUUsS0FBS2hELEtBQUwsQ0FBV2dEO0FBSmxCLE9BQWY7O0FBT0EsVUFBSVosT0FBTyxDQUFDYSxJQUFaLEVBQWtCO0FBQ2ROLFFBQUFBLE1BQU0sQ0FBQ08sS0FBUCxHQUFlZCxPQUFPLENBQUNhLElBQVIsQ0FBYUUsQ0FBNUI7QUFDQVIsUUFBQUEsTUFBTSxDQUFDUyxNQUFQLEdBQWdCaEIsT0FBTyxDQUFDYSxJQUFSLENBQWFJLENBQTdCO0FBQ0FWLFFBQUFBLE1BQU0sQ0FBQ1csUUFBUCxHQUFrQmxCLE9BQU8sQ0FBQ2EsSUFBUixDQUFhTSxJQUEvQjtBQUNIOztBQUVEQyxxQkFBTUMsWUFBTixDQUFtQmpCLFNBQW5CLEVBQThCRyxNQUE5QixFQUFzQyxvQkFBdEMsRUFBNEQsSUFBNUQsRUFBa0UsSUFBbEU7QUFDSDtBQUNKOztBQUVEbkMsRUFBQUEsTUFBTSxHQUFHO0FBQ0wsVUFBTTRCLE9BQU8sR0FBRyxLQUFLcEMsS0FBTCxDQUFXNkIsT0FBWCxDQUFtQlEsVUFBbkIsRUFBaEI7QUFDQSxXQUNJRCxPQUFPLElBQ1BBLE9BQU8sQ0FBQ2EsSUFEUixJQUVBYixPQUFPLENBQUNhLElBQVIsQ0FBYVMsUUFBYixLQUEwQixXQUg5QjtBQUtIOztBQUVEdEQsRUFBQUEsWUFBWSxDQUFDdUQsQ0FBRCxFQUFJO0FBQ1osU0FBS2pDLFFBQUwsQ0FBYztBQUFFVCxNQUFBQSxLQUFLLEVBQUU7QUFBVCxLQUFkOztBQUVBLFFBQUksQ0FBQyxLQUFLUixLQUFMLENBQVdTLFNBQVosSUFBeUIsQ0FBQyxLQUFLVixNQUFMLEVBQTFCLElBQTJDVyx1QkFBY0MsUUFBZCxDQUF1Qix1QkFBdkIsQ0FBL0MsRUFBZ0c7QUFDNUY7QUFDSDs7QUFDRCxVQUFNd0MsVUFBVSxHQUFHRCxDQUFDLENBQUNFLE1BQXJCO0FBQ0FELElBQUFBLFVBQVUsQ0FBQ2hCLEdBQVgsR0FBaUIsS0FBS0wsY0FBTCxFQUFqQjtBQUNIOztBQUVEbEMsRUFBQUEsWUFBWSxDQUFDc0QsQ0FBRCxFQUFJO0FBQ1osU0FBS2pDLFFBQUwsQ0FBYztBQUFFVCxNQUFBQSxLQUFLLEVBQUU7QUFBVCxLQUFkOztBQUVBLFFBQUksQ0FBQyxLQUFLUixLQUFMLENBQVdTLFNBQVosSUFBeUIsQ0FBQyxLQUFLVixNQUFMLEVBQTFCLElBQTJDVyx1QkFBY0MsUUFBZCxDQUF1Qix1QkFBdkIsQ0FBL0MsRUFBZ0c7QUFDNUY7QUFDSDs7QUFDRCxVQUFNd0MsVUFBVSxHQUFHRCxDQUFDLENBQUNFLE1BQXJCO0FBQ0FELElBQUFBLFVBQVUsQ0FBQ2hCLEdBQVgsR0FBaUIsS0FBS2tCLFlBQUwsRUFBakI7QUFDSDs7QUFFRDdELEVBQUFBLFlBQVksR0FBRztBQUNYLFNBQUt5QixRQUFMLENBQWM7QUFDVlosTUFBQUEsUUFBUSxFQUFFO0FBREEsS0FBZDtBQUdIOztBQUVEWCxFQUFBQSxXQUFXLEdBQUc7QUFDVixTQUFLSCxLQUFMLENBQVcrRCxlQUFYO0FBRUEsUUFBSS9DLHFCQUFKOztBQUVBLFFBQUksS0FBS0ssTUFBTCxDQUFZMkMsT0FBaEIsRUFBeUI7QUFDckIsWUFBTTtBQUFFQyxRQUFBQSxZQUFGO0FBQWdCQyxRQUFBQTtBQUFoQixVQUFrQyxLQUFLN0MsTUFBTCxDQUFZMkMsT0FBcEQsQ0FEcUIsQ0FFckI7O0FBQ0FoRCxNQUFBQSxxQkFBcUIsR0FBRztBQUFFaUQsUUFBQUEsWUFBRjtBQUFnQkMsUUFBQUE7QUFBaEIsT0FBeEI7QUFDSDs7QUFFRCxTQUFLeEMsUUFBTCxDQUFjO0FBQUVYLE1BQUFBLFNBQVMsRUFBRSxJQUFiO0FBQW1CQyxNQUFBQTtBQUFuQixLQUFkO0FBQ0g7O0FBRUR1QixFQUFBQSxjQUFjLEdBQUc7QUFDYixVQUFNNEIsS0FBSyxHQUFHLDZCQUFpQixLQUFLbkUsS0FBTCxDQUFXNkIsT0FBWCxDQUFtQlEsVUFBbkIsRUFBakIsQ0FBZDs7QUFDQSxRQUFJOEIsS0FBSyxDQUFDQyxXQUFWLEVBQXVCO0FBQ25CLGFBQU8sS0FBSzNELEtBQUwsQ0FBV0MsWUFBbEI7QUFDSCxLQUZELE1BRU87QUFDSCxhQUFPeUQsS0FBSyxDQUFDRSxPQUFiO0FBQ0g7QUFDSjs7QUFFRFAsRUFBQUEsWUFBWSxHQUFHO0FBQ1g7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFNUSxVQUFVLEdBQUcsR0FBbkI7QUFDQSxVQUFNQyxXQUFXLEdBQUcsR0FBcEI7QUFFQSxVQUFNbkMsT0FBTyxHQUFHLEtBQUtwQyxLQUFMLENBQVc2QixPQUFYLENBQW1CUSxVQUFuQixFQUFoQjtBQUNBLFVBQU04QixLQUFLLEdBQUcsNkJBQWlCL0IsT0FBakIsQ0FBZDs7QUFFQSxRQUFJK0IsS0FBSyxDQUFDQyxXQUFWLEVBQXVCO0FBQ25CO0FBQ0EsVUFBSSxLQUFLM0QsS0FBTCxDQUFXRSxxQkFBZixFQUFzQztBQUNsQyxlQUFPLEtBQUtGLEtBQUwsQ0FBV0UscUJBQWxCO0FBQ0g7O0FBQ0QsYUFBTyxLQUFLRixLQUFMLENBQVdDLFlBQWxCO0FBQ0gsS0FORCxNQU1PLElBQUkwQixPQUFPLENBQUNhLElBQVIsSUFBZ0JiLE9BQU8sQ0FBQ2EsSUFBUixDQUFhUyxRQUFiLEtBQTBCLGVBQTFDLElBQTZEUyxLQUFLLENBQUNLLFlBQXZFLEVBQXFGO0FBQ3hGO0FBQ0E7QUFDQTtBQUNBLGFBQU9MLEtBQUssQ0FBQ00sZ0JBQU4sQ0FBdUJILFVBQXZCLEVBQW1DQyxXQUFuQyxFQUFnRCxPQUFoRCxDQUFQO0FBQ0gsS0FMTSxNQUtBO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFlBQU10QixJQUFJLEdBQUdiLE9BQU8sQ0FBQ2EsSUFBckI7O0FBQ0EsVUFDSSxLQUFLekMsTUFBTCxNQUNBa0UsTUFBTSxDQUFDQyxnQkFBUCxLQUE0QixHQUQ1QixJQUVDLENBQUMxQixJQUFELElBQVMsQ0FBQ0EsSUFBSSxDQUFDRSxDQUFmLElBQW9CLENBQUNGLElBQUksQ0FBQ0ksQ0FBMUIsSUFBK0IsQ0FBQ0osSUFBSSxDQUFDTSxJQUgxQyxFQUlFO0FBQ0UsZUFBT1ksS0FBSyxDQUFDUyx3QkFBTixDQUErQk4sVUFBL0IsRUFBMkNDLFdBQTNDLENBQVA7QUFDSCxPQU5ELE1BTU87QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBRUEsY0FBTU0scUJBQXFCLEdBQ3ZCNUIsSUFBSSxDQUFDRSxDQUFMLEdBQVNtQixVQUFULElBQ0FyQixJQUFJLENBQUNJLENBQUwsR0FBU2tCLFdBRmI7QUFJQSxjQUFNTyxlQUFlLEdBQUc3QixJQUFJLENBQUNNLElBQUwsR0FBWSxJQUFFLElBQUYsR0FBTyxJQUEzQyxDQWRHLENBYzhDOztBQUVqRCxZQUFJdUIsZUFBZSxJQUFJRCxxQkFBdkIsRUFBOEM7QUFDMUM7QUFDQTtBQUNBO0FBQ0EsaUJBQU9WLEtBQUssQ0FBQ1Msd0JBQU4sQ0FBK0JOLFVBQS9CLEVBQTJDQyxXQUEzQyxDQUFQO0FBQ0gsU0FMRCxNQUtPO0FBQ0g7QUFDQTtBQUNBLGlCQUFPSixLQUFLLENBQUNFLE9BQWI7QUFDSDtBQUNKO0FBQ0o7QUFDSjs7QUFFRHRDLEVBQUFBLGNBQWMsR0FBRztBQUNiLFVBQU1LLE9BQU8sR0FBRyxLQUFLcEMsS0FBTCxDQUFXNkIsT0FBWCxDQUFtQlEsVUFBbkIsRUFBaEI7O0FBQ0EsUUFBSUQsT0FBTyxDQUFDMkMsSUFBUixLQUFpQkMsU0FBakIsSUFBOEIsS0FBS3ZFLEtBQUwsQ0FBV0MsWUFBWCxLQUE0QixJQUE5RCxFQUFvRTtBQUNoRSxVQUFJdUUsZ0JBQWdCLEdBQUdDLE9BQU8sQ0FBQ0MsT0FBUixDQUFnQixJQUFoQixDQUF2Qjs7QUFDQSxVQUFJL0MsT0FBTyxDQUFDYSxJQUFSLElBQWdCYixPQUFPLENBQUNhLElBQVIsQ0FBYW1DLGNBQWpDLEVBQWlEO0FBQzdDSCxRQUFBQSxnQkFBZ0IsR0FBRyw4QkFDZjdDLE9BQU8sQ0FBQ2EsSUFBUixDQUFhbUMsY0FERSxFQUVqQkMsSUFGaUIsQ0FFWixVQUFTQyxJQUFULEVBQWU7QUFDbEIsaUJBQU9DLEdBQUcsQ0FBQ0MsZUFBSixDQUFvQkYsSUFBcEIsQ0FBUDtBQUNILFNBSmtCLENBQW5CO0FBS0g7O0FBQ0QsVUFBSTFFLGFBQUo7QUFDQXFFLE1BQUFBLGdCQUFnQixDQUFDSSxJQUFqQixDQUF1QkksWUFBRCxJQUFrQjtBQUNwQyxlQUFPLDhCQUFZckQsT0FBTyxDQUFDMkMsSUFBcEIsRUFBMEJNLElBQTFCLENBQStCLFVBQVNDLElBQVQsRUFBZTtBQUNqRDFFLFVBQUFBLGFBQWEsR0FBRzBFLElBQWhCO0FBQ0EsaUJBQU9DLEdBQUcsQ0FBQ0MsZUFBSixDQUFvQkYsSUFBcEIsQ0FBUDtBQUNILFNBSE0sRUFHSkQsSUFISSxDQUdFSyxVQUFELElBQWdCO0FBQ3BCLGNBQUksS0FBS2xFLFNBQVQsRUFBb0I7QUFDcEIsZUFBS0UsUUFBTCxDQUFjO0FBQ1ZoQixZQUFBQSxZQUFZLEVBQUVnRixVQURKO0FBRVYvRSxZQUFBQSxxQkFBcUIsRUFBRThFLFlBRmI7QUFHVjdFLFlBQUFBLGFBQWEsRUFBRUE7QUFITCxXQUFkO0FBS0gsU0FWTSxDQUFQO0FBV0gsT0FaRCxFQVlHK0UsS0FaSCxDQVlVQyxHQUFELElBQVM7QUFDZCxZQUFJLEtBQUtwRSxTQUFULEVBQW9CO0FBQ3BCcUUsUUFBQUEsT0FBTyxDQUFDQyxJQUFSLENBQWEsZ0NBQWIsRUFBK0NGLEdBQS9DLEVBRmMsQ0FHZDs7QUFDQSxhQUFLbEUsUUFBTCxDQUFjO0FBQ1ZiLFVBQUFBLEtBQUssRUFBRStFO0FBREcsU0FBZDtBQUdILE9BbkJEO0FBb0JIO0FBQ0o7O0FBRURHLEVBQUFBLGlCQUFpQixHQUFHO0FBQ2hCLFNBQUt2RSxTQUFMLEdBQWlCLEtBQWpCO0FBQ0EsU0FBS3dFLE9BQUwsQ0FBYUMsRUFBYixDQUFnQixNQUFoQixFQUF3QixLQUFLM0YsWUFBN0I7QUFFQSxVQUFNWSxTQUFTLEdBQUcsS0FBS1QsS0FBTCxDQUFXUyxTQUFYLElBQ2RTLFlBQVksQ0FBQ3VFLE9BQWIsQ0FBcUIsa0JBQWtCLEtBQUtsRyxLQUFMLENBQVc2QixPQUFYLENBQW1CQyxLQUFuQixFQUF2QyxNQUF1RSxNQUQzRTs7QUFHQSxRQUFJWixTQUFKLEVBQWU7QUFDWDtBQUNBLFdBQUthLGNBQUw7O0FBQ0EsV0FBS0wsUUFBTCxDQUFjO0FBQUNSLFFBQUFBLFNBQVMsRUFBRTtBQUFaLE9BQWQ7QUFDSDs7QUFFRCxTQUFLaUYsdUJBQUw7QUFDSCxHQTdRbUQsQ0ErUXBEO0FBQ0E7OztBQUNBQSxFQUFBQSx1QkFBdUIsR0FBRyxDQUN6Qjs7QUFFREMsRUFBQUEsb0JBQW9CLEdBQUc7QUFDbkIsU0FBSzVFLFNBQUwsR0FBaUIsSUFBakI7QUFDQSxTQUFLd0UsT0FBTCxDQUFhSyxjQUFiLENBQTRCLE1BQTVCLEVBQW9DLEtBQUsvRixZQUF6Qzs7QUFDQSxTQUFLZ0csMEJBQUw7O0FBRUEsUUFBSSxLQUFLN0YsS0FBTCxDQUFXQyxZQUFmLEVBQTZCO0FBQ3pCNkUsTUFBQUEsR0FBRyxDQUFDZ0IsZUFBSixDQUFvQixLQUFLOUYsS0FBTCxDQUFXQyxZQUEvQjtBQUNIOztBQUNELFFBQUksS0FBS0QsS0FBTCxDQUFXRSxxQkFBZixFQUFzQztBQUNsQzRFLE1BQUFBLEdBQUcsQ0FBQ2dCLGVBQUosQ0FBb0IsS0FBSzlGLEtBQUwsQ0FBV0UscUJBQS9CO0FBQ0g7QUFDSixHQS9SbUQsQ0FpU3BEO0FBQ0E7OztBQUNBMkYsRUFBQUEsMEJBQTBCLEdBQUcsQ0FDNUI7O0FBRURFLEVBQUFBLGVBQWUsQ0FBQ2QsVUFBRCxFQUFhZSxRQUFiLEVBQXVCckUsT0FBdkIsRUFBZ0M7QUFDM0MsUUFBSXNFLFNBQUo7QUFDQSxRQUFJQyxVQUFKOztBQUVBLFFBQUl2RSxPQUFPLElBQUlBLE9BQU8sQ0FBQ2EsSUFBbkIsSUFBMkJiLE9BQU8sQ0FBQ2EsSUFBUixDQUFhRSxDQUF4QyxJQUE2Q2YsT0FBTyxDQUFDYSxJQUFSLENBQWFJLENBQTlELEVBQWlFO0FBQzdEcUQsTUFBQUEsU0FBUyxHQUFHdEUsT0FBTyxDQUFDYSxJQUFSLENBQWFFLENBQXpCO0FBQ0F3RCxNQUFBQSxVQUFVLEdBQUd2RSxPQUFPLENBQUNhLElBQVIsQ0FBYUksQ0FBMUI7QUFDSCxLQUhELE1BR087QUFDSDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxVQUFJLENBQUMsS0FBSzVDLEtBQUwsQ0FBV08scUJBQWhCLEVBQXVDO0FBQ25DLFlBQUk0RixZQUFKOztBQUNBLFlBQUksQ0FBQyxLQUFLbkcsS0FBTCxDQUFXUyxTQUFoQixFQUEyQjtBQUN2QjBGLFVBQUFBLFlBQVksZ0JBQUcsNkJBQUMsc0JBQUQsT0FBZjtBQUNILFNBRkQsTUFFTztBQUNIQSxVQUFBQSxZQUFZLGdCQUNSO0FBQUssWUFBQSxLQUFLLEVBQUU7QUFBQ0MsY0FBQUEsT0FBTyxFQUFFO0FBQVYsYUFBWjtBQUErQixZQUFBLEdBQUcsRUFBRUosUUFBcEM7QUFBOEMsWUFBQSxHQUFHLEVBQUUsS0FBS3BGLE1BQXhEO0FBQ0ksWUFBQSxHQUFHLEVBQUVlLE9BQU8sQ0FBQ1UsSUFEakI7QUFFSSxZQUFBLE9BQU8sRUFBRSxLQUFLN0MsWUFGbEI7QUFHSSxZQUFBLE1BQU0sRUFBRSxLQUFLRTtBQUhqQixZQURKO0FBT0g7O0FBQ0QsZUFBTyxLQUFLMkcsU0FBTCxDQUFlcEIsVUFBZixFQUEyQmtCLFlBQTNCLENBQVA7QUFDSDs7QUFDREYsTUFBQUEsU0FBUyxHQUFHLEtBQUtqRyxLQUFMLENBQVdPLHFCQUFYLENBQWlDaUQsWUFBN0M7QUFDQTBDLE1BQUFBLFVBQVUsR0FBRyxLQUFLbEcsS0FBTCxDQUFXTyxxQkFBWCxDQUFpQ2tELGFBQTlDO0FBQ0gsS0EvQjBDLENBaUMzQzs7O0FBQ0EsVUFBTTZDLFNBQVMsR0FBR0MsSUFBSSxDQUFDQyxHQUFMLENBQVMsS0FBS2pILEtBQUwsQ0FBV2tILGNBQVgsSUFBNkIsR0FBdEMsRUFBMkNQLFVBQTNDLENBQWxCLENBbEMyQyxDQW1DM0M7QUFDQTs7QUFDQSxVQUFNUSxRQUFRLEdBQUdULFNBQVMsR0FBR0ssU0FBWixHQUF3QkosVUFBekM7QUFFQSxRQUFJUyxHQUFHLEdBQUcsSUFBVjtBQUNBLFFBQUlDLFdBQVcsR0FBRyxJQUFsQjtBQUNBLFFBQUlDLFFBQVEsR0FBRyxJQUFmLENBekMyQyxDQTJDM0M7O0FBQ0EsUUFBSWxGLE9BQU8sQ0FBQzJDLElBQVIsS0FBaUJDLFNBQWpCLElBQThCLEtBQUt2RSxLQUFMLENBQVdDLFlBQVgsS0FBNEIsSUFBOUQsRUFBb0U7QUFDaEUyRyxNQUFBQSxXQUFXLGdCQUFHLDZCQUFDLHNCQUFEO0FBQWUsUUFBQSxDQUFDLEVBQUUsRUFBbEI7QUFBc0IsUUFBQSxDQUFDLEVBQUU7QUFBekIsUUFBZDtBQUNILEtBRkQsTUFFTyxJQUFJLENBQUMsS0FBSzVHLEtBQUwsQ0FBV00sU0FBaEIsRUFBMkI7QUFDOUI7QUFDQXNHLE1BQUFBLFdBQVcsR0FBRyxLQUFLRSxjQUFMLEVBQWQ7QUFDSDs7QUFFRCxRQUFJQyxlQUFlLEdBQUdDLE9BQU8sQ0FBQ0osV0FBRCxDQUE3Qjs7QUFFQSxRQUFJWixRQUFRLElBQUksQ0FBQyxLQUFLaEcsS0FBTCxDQUFXSyxRQUE1QixFQUFzQztBQUNsQztBQUNBO0FBQ0E7QUFDQXNHLE1BQUFBLEdBQUcsZ0JBQ0M7QUFBSyxRQUFBLFNBQVMsRUFBQyx5QkFBZjtBQUF5QyxRQUFBLEdBQUcsRUFBRVgsUUFBOUM7QUFBd0QsUUFBQSxHQUFHLEVBQUUsS0FBS3BGLE1BQWxFO0FBQ0ksUUFBQSxLQUFLLEVBQUU7QUFBRThGLFVBQUFBLFFBQVEsRUFBRUEsUUFBUSxHQUFHO0FBQXZCLFNBRFg7QUFFSSxRQUFBLEdBQUcsRUFBRS9FLE9BQU8sQ0FBQ1UsSUFGakI7QUFHSSxRQUFBLE9BQU8sRUFBRSxLQUFLN0MsWUFIbEI7QUFJSSxRQUFBLE1BQU0sRUFBRSxLQUFLRSxXQUpqQjtBQUtJLFFBQUEsWUFBWSxFQUFFLEtBQUtDLFlBTHZCO0FBTUksUUFBQSxZQUFZLEVBQUUsS0FBS0M7QUFOdkIsUUFESjtBQVNIOztBQUVELFFBQUksQ0FBQyxLQUFLSSxLQUFMLENBQVdTLFNBQWhCLEVBQTJCO0FBQ3ZCa0csTUFBQUEsR0FBRyxnQkFBRyw2QkFBQyxzQkFBRDtBQUF3QixRQUFBLEtBQUssRUFBRTtBQUFFRCxVQUFBQSxRQUFRLEVBQUVBLFFBQVEsR0FBRztBQUF2QjtBQUEvQixRQUFOO0FBQ0FLLE1BQUFBLGVBQWUsR0FBRyxLQUFsQixDQUZ1QixDQUVFO0FBQzVCOztBQUVELFFBQUksS0FBS2hILE1BQUwsTUFBaUIsQ0FBQ1csdUJBQWNDLFFBQWQsQ0FBdUIsdUJBQXZCLENBQWxCLElBQXFFLENBQUMsS0FBS1gsS0FBTCxDQUFXUSxLQUFyRixFQUE0RjtBQUN4RnFHLE1BQUFBLFFBQVEsZ0JBQUc7QUFBRyxRQUFBLFNBQVMsRUFBQztBQUFiLGVBQVg7QUFDSDs7QUFFRCxVQUFNSSxTQUFTLGdCQUNYO0FBQUssTUFBQSxTQUFTLEVBQUMsbUNBQWY7QUFBbUQsTUFBQSxLQUFLLEVBQUU7QUFBRVgsUUFBQUEsU0FBUyxFQUFFQSxTQUFTLEdBQUc7QUFBekI7QUFBMUQsb0JBRUk7QUFBSyxNQUFBLEtBQUssRUFBRTtBQUFFWSxRQUFBQSxhQUFhLEVBQUcsTUFBTWhCLFVBQU4sR0FBbUJELFNBQXBCLEdBQWlDO0FBQWxEO0FBQVosTUFGSixFQUdNYyxlQUFlLGlCQUNiO0FBQUssTUFBQSxTQUFTLEVBQUMseUJBQWY7QUFBeUMsTUFBQSxLQUFLLEVBQUU7QUFDNUM7QUFDQUwsUUFBQUEsUUFBUSxFQUFFVCxTQUFTLEdBQUc7QUFGc0I7QUFBaEQsb0JBSUk7QUFBSyxNQUFBLFNBQVMsRUFBQztBQUFmLE9BQ01XLFdBRE4sQ0FKSixDQUpSLGVBY0k7QUFBSyxNQUFBLEtBQUssRUFBRTtBQUFDUixRQUFBQSxPQUFPLEVBQUUsQ0FBQ1csZUFBRCxHQUFtQnhDLFNBQW5CLEdBQStCO0FBQXpDO0FBQVosT0FDTW9DLEdBRE4sRUFFTUUsUUFGTixDQWRKLEVBbUJNLEtBQUs3RyxLQUFMLENBQVdRLEtBQVgsSUFBb0IsS0FBSzJHLFVBQUwsRUFuQjFCLENBREo7O0FBd0JBLFdBQU8sS0FBS2QsU0FBTCxDQUFlcEIsVUFBZixFQUEyQmdDLFNBQTNCLENBQVA7QUFDSCxHQTVZbUQsQ0E4WXBEOzs7QUFDQVosRUFBQUEsU0FBUyxDQUFDcEIsVUFBRCxFQUFhbUMsUUFBYixFQUF1QjtBQUM1Qix3QkFBTztBQUFHLE1BQUEsSUFBSSxFQUFFbkMsVUFBVDtBQUFxQixNQUFBLE9BQU8sRUFBRSxLQUFLbkY7QUFBbkMsT0FDRnNILFFBREUsQ0FBUDtBQUdILEdBblptRCxDQXFacEQ7OztBQUNBTixFQUFBQSxjQUFjLEdBQUc7QUFDYjtBQUNBLFdBQU8sSUFBUDtBQUNILEdBelptRCxDQTJacEQ7OztBQUNBSyxFQUFBQSxVQUFVLEdBQUc7QUFDVCxXQUFPLElBQVA7QUFDSCxHQTlabUQsQ0FnYXBEOzs7QUFDQUUsRUFBQUEsV0FBVyxHQUFHO0FBQ1Ysd0JBQU8sNkJBQUMsa0JBQUQsNkJBQWUsS0FBSzlILEtBQXBCO0FBQTJCLE1BQUEsYUFBYSxFQUFFLEtBQUtTLEtBQUwsQ0FBV0csYUFBckQ7QUFBb0UsTUFBQSxzQkFBc0IsRUFBRTtBQUE1RixPQUFQO0FBQ0g7O0FBRURtSCxFQUFBQSxNQUFNLEdBQUc7QUFDTCxVQUFNM0YsT0FBTyxHQUFHLEtBQUtwQyxLQUFMLENBQVc2QixPQUFYLENBQW1CUSxVQUFuQixFQUFoQjs7QUFFQSxRQUFJLEtBQUs1QixLQUFMLENBQVdJLEtBQVgsS0FBcUIsSUFBekIsRUFBK0I7QUFDM0IsMEJBQ0k7QUFBTSxRQUFBLFNBQVMsRUFBQztBQUFoQixzQkFDSTtBQUFLLFFBQUEsR0FBRyxFQUFFbUgsT0FBTyxDQUFDLGlDQUFELENBQWpCO0FBQXNELFFBQUEsS0FBSyxFQUFDLElBQTVEO0FBQWlFLFFBQUEsTUFBTSxFQUFDO0FBQXhFLFFBREosRUFFTSx5QkFBRyx3QkFBSCxDQUZOLENBREo7QUFNSDs7QUFFRCxVQUFNdEMsVUFBVSxHQUFHLEtBQUtuRCxjQUFMLEVBQW5COztBQUNBLFFBQUlrRSxRQUFKOztBQUNBLFFBQUksS0FBS2pHLE1BQUwsTUFBaUJXLHVCQUFjQyxRQUFkLENBQXVCLHVCQUF2QixDQUFyQixFQUFzRTtBQUNsRXFGLE1BQUFBLFFBQVEsR0FBR2YsVUFBWDtBQUNILEtBRkQsTUFFTztBQUNIZSxNQUFBQSxRQUFRLEdBQUcsS0FBSzNDLFlBQUwsRUFBWDtBQUNIOztBQUVELFVBQU00RCxTQUFTLEdBQUcsS0FBS2xCLGVBQUwsQ0FBcUJkLFVBQXJCLEVBQWlDZSxRQUFqQyxFQUEyQ3JFLE9BQTNDLENBQWxCOztBQUNBLFVBQU02RixRQUFRLEdBQUcsS0FBS0gsV0FBTCxFQUFqQjtBQUVBLHdCQUFPO0FBQU0sTUFBQSxTQUFTLEVBQUM7QUFBaEIsT0FDREosU0FEQyxFQUVETyxRQUZDLENBQVA7QUFJSDs7QUFoY21ELEMsc0RBQ2pDO0FBQ2Y7QUFDQXBHLEVBQUFBLE9BQU8sRUFBRXFHLG1CQUFVQyxNQUFWLENBQWlCQyxVQUZYOztBQUlmO0FBQ0FyRSxFQUFBQSxlQUFlLEVBQUVtRSxtQkFBVUcsSUFBVixDQUFlRCxVQUxqQjs7QUFPZjtBQUNBbEIsRUFBQUEsY0FBYyxFQUFFZ0IsbUJBQVVJLE1BUlg7O0FBVWY7QUFDQXRGLEVBQUFBLGdCQUFnQixFQUFFa0YsbUJBQVVDO0FBWGIsQyx5REFjRUksNEI7OztBQW9ibEIsTUFBTUMsc0JBQU4sU0FBcUMzSSxlQUFNNEksYUFBM0MsQ0FBeUQ7QUFLNURWLEVBQUFBLE1BQU0sR0FBRztBQUNMLFFBQUlXLFNBQVMsR0FBRywyQkFBaEI7QUFDQSxRQUFJLEtBQUsxSSxLQUFMLENBQVdpQixLQUFmLEVBQXNCeUgsU0FBUyxJQUFJLGtDQUFiO0FBQ3RCLHdCQUNJO0FBQUssTUFBQSxTQUFTLEVBQUVBO0FBQWhCLG9CQUNJO0FBQUssTUFBQSxTQUFTLEVBQUM7QUFBZixvQkFDSTtBQUFNLE1BQUEsU0FBUyxFQUFDO0FBQWhCLE1BREosZUFFSSwyQ0FBTyx5QkFBRyxZQUFILENBQVAsQ0FGSixDQURKLENBREo7QUFRSDs7QUFoQjJEOzs7OEJBQW5ERixzQixlQUNVO0FBQ2Z2SCxFQUFBQSxLQUFLLEVBQUVpSCxtQkFBVVM7QUFERixDIiwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDE1LCAyMDE2IE9wZW5NYXJrZXQgTHRkXG5Db3B5cmlnaHQgMjAxOCBOZXcgVmVjdG9yIEx0ZFxuQ29weXJpZ2h0IDIwMTgsIDIwMTkgTWljaGFlbCBUZWxhdHluc2tpIDw3dDNjaGd1eUBnbWFpbC5jb20+XG5cbkxpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG55b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG5Zb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcblxuICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuXG5Vbmxlc3MgcmVxdWlyZWQgYnkgYXBwbGljYWJsZSBsYXcgb3IgYWdyZWVkIHRvIGluIHdyaXRpbmcsIHNvZnR3YXJlXG5kaXN0cmlidXRlZCB1bmRlciB0aGUgTGljZW5zZSBpcyBkaXN0cmlidXRlZCBvbiBhbiBcIkFTIElTXCIgQkFTSVMsXG5XSVRIT1VUIFdBUlJBTlRJRVMgT1IgQ09ORElUSU9OUyBPRiBBTlkgS0lORCwgZWl0aGVyIGV4cHJlc3Mgb3IgaW1wbGllZC5cblNlZSB0aGUgTGljZW5zZSBmb3IgdGhlIHNwZWNpZmljIGxhbmd1YWdlIGdvdmVybmluZyBwZXJtaXNzaW9ucyBhbmRcbmxpbWl0YXRpb25zIHVuZGVyIHRoZSBMaWNlbnNlLlxuKi9cblxuaW1wb3J0IFJlYWN0LCB7Y3JlYXRlUmVmfSBmcm9tICdyZWFjdCc7XG5pbXBvcnQgUHJvcFR5cGVzIGZyb20gJ3Byb3AtdHlwZXMnO1xuXG5pbXBvcnQgTUZpbGVCb2R5IGZyb20gJy4vTUZpbGVCb2R5JztcbmltcG9ydCBNb2RhbCBmcm9tICcuLi8uLi8uLi9Nb2RhbCc7XG5pbXBvcnQgKiBhcyBzZGsgZnJvbSAnLi4vLi4vLi4vaW5kZXgnO1xuaW1wb3J0IHsgZGVjcnlwdEZpbGUgfSBmcm9tICcuLi8uLi8uLi91dGlscy9EZWNyeXB0RmlsZSc7XG5pbXBvcnQgeyBfdCB9IGZyb20gJy4uLy4uLy4uL2xhbmd1YWdlSGFuZGxlcic7XG5pbXBvcnQgU2V0dGluZ3NTdG9yZSBmcm9tIFwiLi4vLi4vLi4vc2V0dGluZ3MvU2V0dGluZ3NTdG9yZVwiO1xuaW1wb3J0IE1hdHJpeENsaWVudENvbnRleHQgZnJvbSBcIi4uLy4uLy4uL2NvbnRleHRzL01hdHJpeENsaWVudENvbnRleHRcIjtcbmltcG9ydCBJbmxpbmVTcGlubmVyIGZyb20gJy4uL2VsZW1lbnRzL0lubGluZVNwaW5uZXInO1xuaW1wb3J0IHtyZXBsYWNlYWJsZUNvbXBvbmVudH0gZnJvbSBcIi4uLy4uLy4uL3V0aWxzL3JlcGxhY2VhYmxlQ29tcG9uZW50XCI7XG5pbXBvcnQge21lZGlhRnJvbUNvbnRlbnR9IGZyb20gXCIuLi8uLi8uLi9jdXN0b21pc2F0aW9ucy9NZWRpYVwiO1xuXG5AcmVwbGFjZWFibGVDb21wb25lbnQoXCJ2aWV3cy5tZXNzYWdlcy5NSW1hZ2VCb2R5XCIpXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBNSW1hZ2VCb2R5IGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHtcbiAgICBzdGF0aWMgcHJvcFR5cGVzID0ge1xuICAgICAgICAvKiB0aGUgTWF0cml4RXZlbnQgdG8gc2hvdyAqL1xuICAgICAgICBteEV2ZW50OiBQcm9wVHlwZXMub2JqZWN0LmlzUmVxdWlyZWQsXG5cbiAgICAgICAgLyogY2FsbGVkIHdoZW4gdGhlIGltYWdlIGhhcyBsb2FkZWQgKi9cbiAgICAgICAgb25IZWlnaHRDaGFuZ2VkOiBQcm9wVHlwZXMuZnVuYy5pc1JlcXVpcmVkLFxuXG4gICAgICAgIC8qIHRoZSBtYXhpbXVtIGltYWdlIGhlaWdodCB0byB1c2UgKi9cbiAgICAgICAgbWF4SW1hZ2VIZWlnaHQ6IFByb3BUeXBlcy5udW1iZXIsXG5cbiAgICAgICAgLyogdGhlIHBlcm1hbGlua0NyZWF0b3IgKi9cbiAgICAgICAgcGVybWFsaW5rQ3JlYXRvcjogUHJvcFR5cGVzLm9iamVjdCxcbiAgICB9O1xuXG4gICAgc3RhdGljIGNvbnRleHRUeXBlID0gTWF0cml4Q2xpZW50Q29udGV4dDtcblxuICAgIGNvbnN0cnVjdG9yKHByb3BzKSB7XG4gICAgICAgIHN1cGVyKHByb3BzKTtcblxuICAgICAgICB0aGlzLm9uSW1hZ2VFcnJvciA9IHRoaXMub25JbWFnZUVycm9yLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMub25JbWFnZUxvYWQgPSB0aGlzLm9uSW1hZ2VMb2FkLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMub25JbWFnZUVudGVyID0gdGhpcy5vbkltYWdlRW50ZXIuYmluZCh0aGlzKTtcbiAgICAgICAgdGhpcy5vbkltYWdlTGVhdmUgPSB0aGlzLm9uSW1hZ2VMZWF2ZS5iaW5kKHRoaXMpO1xuICAgICAgICB0aGlzLm9uQ2xpZW50U3luYyA9IHRoaXMub25DbGllbnRTeW5jLmJpbmQodGhpcyk7XG4gICAgICAgIHRoaXMub25DbGljayA9IHRoaXMub25DbGljay5iaW5kKHRoaXMpO1xuICAgICAgICB0aGlzLl9pc0dpZiA9IHRoaXMuX2lzR2lmLmJpbmQodGhpcyk7XG5cbiAgICAgICAgdGhpcy5zdGF0ZSA9IHtcbiAgICAgICAgICAgIGRlY3J5cHRlZFVybDogbnVsbCxcbiAgICAgICAgICAgIGRlY3J5cHRlZFRodW1ibmFpbFVybDogbnVsbCxcbiAgICAgICAgICAgIGRlY3J5cHRlZEJsb2I6IG51bGwsXG4gICAgICAgICAgICBlcnJvcjogbnVsbCxcbiAgICAgICAgICAgIGltZ0Vycm9yOiBmYWxzZSxcbiAgICAgICAgICAgIGltZ0xvYWRlZDogZmFsc2UsXG4gICAgICAgICAgICBsb2FkZWRJbWFnZURpbWVuc2lvbnM6IG51bGwsXG4gICAgICAgICAgICBob3ZlcjogZmFsc2UsXG4gICAgICAgICAgICBzaG93SW1hZ2U6IFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoXCJzaG93SW1hZ2VzXCIpLFxuICAgICAgICB9O1xuXG4gICAgICAgIHRoaXMuX2ltYWdlID0gY3JlYXRlUmVmKCk7XG4gICAgfVxuXG4gICAgLy8gRklYTUU6IGZhY3RvciB0aGlzIG91dCBhbmQgYXBwbHkgaXQgdG8gTVZpZGVvQm9keSBhbmQgTUF1ZGlvQm9keSB0b28hXG4gICAgb25DbGllbnRTeW5jKHN5bmNTdGF0ZSwgcHJldlN0YXRlKSB7XG4gICAgICAgIGlmICh0aGlzLnVubW91bnRlZCkgcmV0dXJuO1xuICAgICAgICAvLyBDb25zaWRlciB0aGUgY2xpZW50IHJlY29ubmVjdGVkIGlmIHRoZXJlIGlzIG5vIGVycm9yIHdpdGggc3luY2luZy5cbiAgICAgICAgLy8gVGhpcyBtZWFucyB0aGUgc3RhdGUgY291bGQgYmUgUkVDT05ORUNUSU5HLCBTWU5DSU5HLCBQUkVQQVJFRCBvciBDQVRDSFVQLlxuICAgICAgICBjb25zdCByZWNvbm5lY3RlZCA9IHN5bmNTdGF0ZSAhPT0gXCJFUlJPUlwiICYmIHByZXZTdGF0ZSAhPT0gc3luY1N0YXRlO1xuICAgICAgICBpZiAocmVjb25uZWN0ZWQgJiYgdGhpcy5zdGF0ZS5pbWdFcnJvcikge1xuICAgICAgICAgICAgLy8gTG9hZCB0aGUgaW1hZ2UgYWdhaW5cbiAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgICAgIGltZ0Vycm9yOiBmYWxzZSxcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgc2hvd0ltYWdlKCkge1xuICAgICAgICBsb2NhbFN0b3JhZ2Uuc2V0SXRlbShcIm14X1Nob3dJbWFnZV9cIiArIHRoaXMucHJvcHMubXhFdmVudC5nZXRJZCgpLCBcInRydWVcIik7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe3Nob3dJbWFnZTogdHJ1ZX0pO1xuICAgICAgICB0aGlzLl9kb3dubG9hZEltYWdlKCk7XG4gICAgfVxuXG4gICAgb25DbGljayhldikge1xuICAgICAgICBpZiAoZXYuYnV0dG9uID09PSAwICYmICFldi5tZXRhS2V5KSB7XG4gICAgICAgICAgICBldi5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgaWYgKCF0aGlzLnN0YXRlLnNob3dJbWFnZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuc2hvd0ltYWdlKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBjb25zdCBjb250ZW50ID0gdGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQoKTtcbiAgICAgICAgICAgIGNvbnN0IGh0dHBVcmwgPSB0aGlzLl9nZXRDb250ZW50VXJsKCk7XG4gICAgICAgICAgICBjb25zdCBJbWFnZVZpZXcgPSBzZGsuZ2V0Q29tcG9uZW50KFwiZWxlbWVudHMuSW1hZ2VWaWV3XCIpO1xuICAgICAgICAgICAgY29uc3QgcGFyYW1zID0ge1xuICAgICAgICAgICAgICAgIHNyYzogaHR0cFVybCxcbiAgICAgICAgICAgICAgICBuYW1lOiBjb250ZW50LmJvZHkgJiYgY29udGVudC5ib2R5Lmxlbmd0aCA+IDAgPyBjb250ZW50LmJvZHkgOiBfdCgnQXR0YWNobWVudCcpLFxuICAgICAgICAgICAgICAgIG14RXZlbnQ6IHRoaXMucHJvcHMubXhFdmVudCxcbiAgICAgICAgICAgICAgICBwZXJtYWxpbmtDcmVhdG9yOiB0aGlzLnByb3BzLnBlcm1hbGlua0NyZWF0b3IsXG4gICAgICAgICAgICB9O1xuXG4gICAgICAgICAgICBpZiAoY29udGVudC5pbmZvKSB7XG4gICAgICAgICAgICAgICAgcGFyYW1zLndpZHRoID0gY29udGVudC5pbmZvLnc7XG4gICAgICAgICAgICAgICAgcGFyYW1zLmhlaWdodCA9IGNvbnRlbnQuaW5mby5oO1xuICAgICAgICAgICAgICAgIHBhcmFtcy5maWxlU2l6ZSA9IGNvbnRlbnQuaW5mby5zaXplO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBNb2RhbC5jcmVhdGVEaWFsb2coSW1hZ2VWaWV3LCBwYXJhbXMsIFwibXhfRGlhbG9nX2xpZ2h0Ym94XCIsIG51bGwsIHRydWUpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgX2lzR2lmKCkge1xuICAgICAgICBjb25zdCBjb250ZW50ID0gdGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQoKTtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIGNvbnRlbnQgJiZcbiAgICAgICAgICAgIGNvbnRlbnQuaW5mbyAmJlxuICAgICAgICAgICAgY29udGVudC5pbmZvLm1pbWV0eXBlID09PSBcImltYWdlL2dpZlwiXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgb25JbWFnZUVudGVyKGUpIHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IGhvdmVyOiB0cnVlIH0pO1xuXG4gICAgICAgIGlmICghdGhpcy5zdGF0ZS5zaG93SW1hZ2UgfHwgIXRoaXMuX2lzR2lmKCkgfHwgU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImF1dG9wbGF5R2lmc0FuZFZpZGVvc1wiKSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGltZ0VsZW1lbnQgPSBlLnRhcmdldDtcbiAgICAgICAgaW1nRWxlbWVudC5zcmMgPSB0aGlzLl9nZXRDb250ZW50VXJsKCk7XG4gICAgfVxuXG4gICAgb25JbWFnZUxlYXZlKGUpIHtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IGhvdmVyOiBmYWxzZSB9KTtcblxuICAgICAgICBpZiAoIXRoaXMuc3RhdGUuc2hvd0ltYWdlIHx8ICF0aGlzLl9pc0dpZigpIHx8IFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoXCJhdXRvcGxheUdpZnNBbmRWaWRlb3NcIikpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBpbWdFbGVtZW50ID0gZS50YXJnZXQ7XG4gICAgICAgIGltZ0VsZW1lbnQuc3JjID0gdGhpcy5fZ2V0VGh1bWJVcmwoKTtcbiAgICB9XG5cbiAgICBvbkltYWdlRXJyb3IoKSB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoe1xuICAgICAgICAgICAgaW1nRXJyb3I6IHRydWUsXG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIG9uSW1hZ2VMb2FkKCkge1xuICAgICAgICB0aGlzLnByb3BzLm9uSGVpZ2h0Q2hhbmdlZCgpO1xuXG4gICAgICAgIGxldCBsb2FkZWRJbWFnZURpbWVuc2lvbnM7XG5cbiAgICAgICAgaWYgKHRoaXMuX2ltYWdlLmN1cnJlbnQpIHtcbiAgICAgICAgICAgIGNvbnN0IHsgbmF0dXJhbFdpZHRoLCBuYXR1cmFsSGVpZ2h0IH0gPSB0aGlzLl9pbWFnZS5jdXJyZW50O1xuICAgICAgICAgICAgLy8gdGhpcyBpcyBvbmx5IHVzZWQgYXMgYSBmYWxsYmFjayBpbiBjYXNlIGNvbnRlbnQuaW5mby53L2ggaXMgbWlzc2luZ1xuICAgICAgICAgICAgbG9hZGVkSW1hZ2VEaW1lbnNpb25zID0geyBuYXR1cmFsV2lkdGgsIG5hdHVyYWxIZWlnaHQgfTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyBpbWdMb2FkZWQ6IHRydWUsIGxvYWRlZEltYWdlRGltZW5zaW9ucyB9KTtcbiAgICB9XG5cbiAgICBfZ2V0Q29udGVudFVybCgpIHtcbiAgICAgICAgY29uc3QgbWVkaWEgPSBtZWRpYUZyb21Db250ZW50KHRoaXMucHJvcHMubXhFdmVudC5nZXRDb250ZW50KCkpO1xuICAgICAgICBpZiAobWVkaWEuaXNFbmNyeXB0ZWQpIHtcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnN0YXRlLmRlY3J5cHRlZFVybDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHJldHVybiBtZWRpYS5zcmNIdHRwO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgX2dldFRodW1iVXJsKCkge1xuICAgICAgICAvLyBGSVhNRTogd2UgbGV0IGltYWdlcyBncm93IGFzIHdpZGUgYXMgeW91IGxpa2UsIHJhdGhlciB0aGFuIGNhcHBlZCB0byA4MDB4NjAwLlxuICAgICAgICAvLyBTbyBlaXRoZXIgd2UgbmVlZCB0byBzdXBwb3J0IGN1c3RvbSB0aW1lbGluZSB3aWR0aHMgaGVyZSwgb3IgcmVpbXBvc2UgdGhlIGNhcCwgb3RoZXJ3aXNlIHRoZVxuICAgICAgICAvLyB0aHVtYm5haWwgcmVzb2x1dGlvbiB3aWxsIGJlIHVubmVjZXNzYXJpbHkgcmVkdWNlZC5cbiAgICAgICAgLy8gY3VzdG9tIHRpbWVsaW5lIHdpZHRocyBzZWVtcyBwcmVmZXJhYmxlLlxuICAgICAgICBjb25zdCB0aHVtYldpZHRoID0gODAwO1xuICAgICAgICBjb25zdCB0aHVtYkhlaWdodCA9IDYwMDtcblxuICAgICAgICBjb25zdCBjb250ZW50ID0gdGhpcy5wcm9wcy5teEV2ZW50LmdldENvbnRlbnQoKTtcbiAgICAgICAgY29uc3QgbWVkaWEgPSBtZWRpYUZyb21Db250ZW50KGNvbnRlbnQpO1xuXG4gICAgICAgIGlmIChtZWRpYS5pc0VuY3J5cHRlZCkge1xuICAgICAgICAgICAgLy8gRG9uJ3QgdXNlIHRoZSB0aHVtYm5haWwgZm9yIGNsaWVudHMgd2lzaGluZyB0byBhdXRvcGxheSBnaWZzLlxuICAgICAgICAgICAgaWYgKHRoaXMuc3RhdGUuZGVjcnlwdGVkVGh1bWJuYWlsVXJsKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGUuZGVjcnlwdGVkVGh1bWJuYWlsVXJsO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RhdGUuZGVjcnlwdGVkVXJsO1xuICAgICAgICB9IGVsc2UgaWYgKGNvbnRlbnQuaW5mbyAmJiBjb250ZW50LmluZm8ubWltZXR5cGUgPT09IFwiaW1hZ2Uvc3ZnK3htbFwiICYmIG1lZGlhLmhhc1RodW1ibmFpbCkge1xuICAgICAgICAgICAgLy8gc3BlY2lhbCBjYXNlIHRvIHJldHVybiBjbGllbnRzaWRlIHNlbmRlci1nZW5lcmF0ZWQgdGh1bWJuYWlscyBmb3IgU1ZHcywgaWYgYW55LFxuICAgICAgICAgICAgLy8gZ2l2ZW4gd2UgZGVsaWJlcmF0ZWx5IGRvbid0IHRodW1ibmFpbCB0aGVtIHNlcnZlcnNpZGUgdG8gcHJldmVudFxuICAgICAgICAgICAgLy8gYmlsbGlvbiBsb2wgYXR0YWNrcyBhbmQgc2ltaWxhclxuICAgICAgICAgICAgcmV0dXJuIG1lZGlhLmdldFRodW1ibmFpbEh0dHAodGh1bWJXaWR0aCwgdGh1bWJIZWlnaHQsICdzY2FsZScpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgLy8gd2UgdHJ5IHRvIGRvd25sb2FkIHRoZSBjb3JyZWN0IHJlc29sdXRpb25cbiAgICAgICAgICAgIC8vIGZvciBoaS1yZXMgaW1hZ2VzIChsaWtlIHJldGluYSBzY3JlZW5zaG90cykuXG4gICAgICAgICAgICAvLyBzeW5hcHNlIG9ubHkgc3VwcG9ydHMgODAweDYwMCB0aHVtYm5haWxzIGZvciBub3cgdGhvdWdoLFxuICAgICAgICAgICAgLy8gc28gd2UnbGwgbmVlZCB0byBkb3dubG9hZCB0aGUgb3JpZ2luYWwgaW1hZ2UgZm9yIHRoaXMgdG8gd29ya1xuICAgICAgICAgICAgLy8gd2VsbCBmb3Igbm93LiBGaXJzdCwgbGV0J3MgdHJ5IGEgZmV3IGNhc2VzIHRoYXQgbGV0IHVzIGF2b2lkXG4gICAgICAgICAgICAvLyBkb3dubG9hZGluZyB0aGUgb3JpZ2luYWwsIGluY2x1ZGluZzpcbiAgICAgICAgICAgIC8vICAgLSBXaGVuIGRpc3BsYXlpbmcgYSBHSUYsIHdlIGFsd2F5cyB3YW50IHRvIHRodW1ibmFpbCBzbyB0aGF0IHdlIGNhblxuICAgICAgICAgICAgLy8gICAgIHByb3Blcmx5IHJlc3BlY3QgdGhlIHVzZXIncyBHSUYgYXV0b3BsYXkgc2V0dGluZyAod2hpY2ggcmVsaWVzIG9uXG4gICAgICAgICAgICAvLyAgICAgdGh1bWJuYWlsaW5nIHRvIHByb2R1Y2UgdGhlIHN0YXRpYyBwcmV2aWV3IGltYWdlKVxuICAgICAgICAgICAgLy8gICAtIE9uIGEgbG93IERQSSBkZXZpY2UsIGFsd2F5cyB0aHVtYm5haWwgdG8gc2F2ZSBiYW5kd2lkdGhcbiAgICAgICAgICAgIC8vICAgLSBJZiB0aGVyZSdzIG5vIHNpemluZyBpbmZvIGluIHRoZSBldmVudCwgZGVmYXVsdCB0byB0aHVtYm5haWxcbiAgICAgICAgICAgIGNvbnN0IGluZm8gPSBjb250ZW50LmluZm87XG4gICAgICAgICAgICBpZiAoXG4gICAgICAgICAgICAgICAgdGhpcy5faXNHaWYoKSB8fFxuICAgICAgICAgICAgICAgIHdpbmRvdy5kZXZpY2VQaXhlbFJhdGlvID09PSAxLjAgfHxcbiAgICAgICAgICAgICAgICAoIWluZm8gfHwgIWluZm8udyB8fCAhaW5mby5oIHx8ICFpbmZvLnNpemUpXG4gICAgICAgICAgICApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gbWVkaWEuZ2V0VGh1bWJuYWlsT2ZTb3VyY2VIdHRwKHRodW1iV2lkdGgsIHRodW1iSGVpZ2h0KTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgLy8gd2Ugc2hvdWxkIG9ubHkgcmVxdWVzdCB0aHVtYm5haWxzIGlmIHRoZSBpbWFnZSBpcyBiaWdnZXIgdGhhbiA4MDB4NjAwXG4gICAgICAgICAgICAgICAgLy8gKG9yIDE2MDB4MTIwMCBvbiByZXRpbmEpIG90aGVyd2lzZSB0aGUgaW1hZ2UgaW4gdGhlIHRpbWVsaW5lIHdpbGwganVzdFxuICAgICAgICAgICAgICAgIC8vIGVuZCB1cCByZXNhbXBsZWQgYW5kIGRlLXJldGluYSdkIGZvciBubyBnb29kIHJlYXNvbi5cbiAgICAgICAgICAgICAgICAvLyBJZGVhbGx5IHRoZSBzZXJ2ZXIgd291bGQgcHJlZ2VuIDE2MDB4MTIwMCB0aHVtYm5haWxzIGluIG9yZGVyIHRvIHByb3ZpZGUgcmV0aW5hXG4gICAgICAgICAgICAgICAgLy8gdGh1bWJuYWlscywgYnV0IHdlIGRvbid0IGRvIHRoaXMgY3VycmVudGx5IGluIHN5bmFwc2UgZm9yIGZlYXIgb2YgZGlzayBzcGFjZS5cbiAgICAgICAgICAgICAgICAvLyBBcyBhIGNvbXByb21pc2UsIGxldCdzIHN3aXRjaCB0byBub24tcmV0aW5hIHRodW1ibmFpbHMgb25seSBpZiB0aGUgb3JpZ2luYWxcbiAgICAgICAgICAgICAgICAvLyBpbWFnZSBpcyBib3RoIHBoeXNpY2FsbHkgdG9vIGxhcmdlIGFuZCBnb2luZyB0byBiZSBtYXNzaXZlIHRvIGxvYWQgaW4gdGhlXG4gICAgICAgICAgICAgICAgLy8gdGltZWxpbmUgKGUuZy4gPjFNQikuXG5cbiAgICAgICAgICAgICAgICBjb25zdCBpc0xhcmdlclRoYW5UaHVtYm5haWwgPSAoXG4gICAgICAgICAgICAgICAgICAgIGluZm8udyA+IHRodW1iV2lkdGggfHxcbiAgICAgICAgICAgICAgICAgICAgaW5mby5oID4gdGh1bWJIZWlnaHRcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIGNvbnN0IGlzTGFyZ2VGaWxlU2l6ZSA9IGluZm8uc2l6ZSA+IDEqMTAyNCoxMDI0OyAvLyAxbWJcblxuICAgICAgICAgICAgICAgIGlmIChpc0xhcmdlRmlsZVNpemUgJiYgaXNMYXJnZXJUaGFuVGh1bWJuYWlsKSB7XG4gICAgICAgICAgICAgICAgICAgIC8vIGltYWdlIGlzIHRvbyBsYXJnZSBwaHlzaWNhbGx5IGFuZCBieXRld2lzZSB0byBjbHV0dGVyIG91ciB0aW1lbGluZSBzb1xuICAgICAgICAgICAgICAgICAgICAvLyB3ZSBhc2sgZm9yIGEgdGh1bWJuYWlsLCBkZXNwaXRlIGtub3dpbmcgdGhhdCBpdCB3aWxsIGJlIG1heCA4MDB4NjAwXG4gICAgICAgICAgICAgICAgICAgIC8vIGRlc3BpdGUgdXMgYmVpbmcgcmV0aW5hIChhcyBzeW5hcHNlIGRvZXNuJ3QgZG8gMTYwMHgxMjAwIHRodW1icyB5ZXQpLlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gbWVkaWEuZ2V0VGh1bWJuYWlsT2ZTb3VyY2VIdHRwKHRodW1iV2lkdGgsIHRodW1iSGVpZ2h0KTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAvLyBkb3dubG9hZCB0aGUgb3JpZ2luYWwgaW1hZ2Ugb3RoZXJ3aXNlLCBzbyB3ZSBjYW4gc2NhbGUgaXQgY2xpZW50IHNpZGVcbiAgICAgICAgICA