UNPKG

matrix-react-sdk

Version:
764 lines (650 loc) 97.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 _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _react = _interopRequireWildcard(require("react")); var _propTypes = _interopRequireDefault(require("prop-types")); var _languageHandler = require("../../../languageHandler"); var sdk = _interopRequireWildcard(require("../../../index")); var _MatrixClientPeg = require("../../../MatrixClientPeg"); var _dispatcher = _interopRequireDefault(require("../../../dispatcher/dispatcher")); var _UserAddress = require("../../../UserAddress.js"); var _GroupStore = _interopRequireDefault(require("../../../stores/GroupStore")); var Email = _interopRequireWildcard(require("../../../email")); var _IdentityAuthClient = _interopRequireDefault(require("../../../IdentityAuthClient")); var _IdentityServerUtils = require("../../../utils/IdentityServerUtils"); var _UrlUtils = require("../../../utils/UrlUtils"); var _promise = require("../../../utils/promise"); var _Keyboard = require("../../../Keyboard"); var _actions = require("../../../dispatcher/actions"); var _replaceableComponent = require("../../../utils/replaceableComponent"); var _dec, _class, _class2, _temp; const TRUNCATE_QUERY_LIST = 40; const QUERY_USER_DIRECTORY_DEBOUNCE_MS = 200; const addressTypeName = { 'mx-user-id': (0, _languageHandler._td)("Matrix ID"), 'mx-room-id': (0, _languageHandler._td)("Matrix Room ID"), 'email': (0, _languageHandler._td)("email address") }; let AddressPickerDialog = (_dec = (0, _replaceableComponent.replaceableComponent)("views.dialogs.AddressPickerDialog"), _dec(_class = (_temp = _class2 = class AddressPickerDialog extends _react.default.Component { constructor(props) { super(props); (0, _defineProperty2.default)(this, "onButtonClick", () => { let selectedList = this.state.selectedList.slice(); // Check the text input field to see if user has an unconverted address // If there is and it's valid add it to the local selectedList if (this._textinput.current.value !== '') { selectedList = this._addAddressesToList([this._textinput.current.value]); if (selectedList === null) return; } this.props.onFinished(true, selectedList); }); (0, _defineProperty2.default)(this, "onCancel", () => { this.props.onFinished(false); }); (0, _defineProperty2.default)(this, "onKeyDown", e => { const textInput = this._textinput.current ? this._textinput.current.value : undefined; if (e.key === _Keyboard.Key.ESCAPE) { e.stopPropagation(); e.preventDefault(); this.props.onFinished(false); } else if (e.key === _Keyboard.Key.ARROW_UP) { e.stopPropagation(); e.preventDefault(); if (this.addressSelector) this.addressSelector.moveSelectionUp(); } else if (e.key === _Keyboard.Key.ARROW_DOWN) { e.stopPropagation(); e.preventDefault(); if (this.addressSelector) this.addressSelector.moveSelectionDown(); } else if (this.state.suggestedList.length > 0 && [_Keyboard.Key.COMMA, _Keyboard.Key.ENTER, _Keyboard.Key.TAB].includes(e.key)) { e.stopPropagation(); e.preventDefault(); if (this.addressSelector) this.addressSelector.chooseSelection(); } else if (textInput.length === 0 && this.state.selectedList.length && e.key === _Keyboard.Key.BACKSPACE) { e.stopPropagation(); e.preventDefault(); this.onDismissed(this.state.selectedList.length - 1)(); } else if (e.key === _Keyboard.Key.ENTER) { e.stopPropagation(); e.preventDefault(); if (textInput === '') { // if there's nothing in the input box, submit the form this.onButtonClick(); } else { this._addAddressesToList([textInput]); } } else if (textInput && (e.key === _Keyboard.Key.COMMA || e.key === _Keyboard.Key.TAB)) { e.stopPropagation(); e.preventDefault(); this._addAddressesToList([textInput]); } }); (0, _defineProperty2.default)(this, "onQueryChanged", ev => { const query = ev.target.value; if (this.queryChangedDebouncer) { clearTimeout(this.queryChangedDebouncer); } // Only do search if there is something to search if (query.length > 0 && query !== '@' && query.length >= 2) { this.queryChangedDebouncer = setTimeout(() => { if (this.props.pickerType === 'user') { if (this.props.groupId) { this._doNaiveGroupSearch(query); } else if (this.state.serverSupportsUserDirectory) { this._doUserDirectorySearch(query); } else { this._doLocalSearch(query); } } else if (this.props.pickerType === 'room') { if (this.props.groupId) { this._doNaiveGroupRoomSearch(query); } else { this._doRoomSearch(query); } } else { console.error('Unknown pickerType', this.props.pickerType); } }, QUERY_USER_DIRECTORY_DEBOUNCE_MS); } else { this.setState({ suggestedList: [], query: "", searchError: null }); } }); (0, _defineProperty2.default)(this, "onDismissed", index => () => { const selectedList = this.state.selectedList.slice(); selectedList.splice(index, 1); this.setState({ selectedList, suggestedList: [], query: "" }); if (this._cancelThreepidLookup) this._cancelThreepidLookup(); }); (0, _defineProperty2.default)(this, "onClick", index => () => { this.onSelected(index); }); (0, _defineProperty2.default)(this, "onSelected", index => { const selectedList = this.state.selectedList.slice(); selectedList.push(this._getFilteredSuggestions()[index]); this.setState({ selectedList, suggestedList: [], query: "" }); if (this._cancelThreepidLookup) this._cancelThreepidLookup(); }); (0, _defineProperty2.default)(this, "_onPaste", e => { // Prevent the text being pasted into the textarea e.preventDefault(); const text = e.clipboardData.getData("text"); // Process it as a list of addresses to add instead this._addAddressesToList(text.split(/[\s,]+/)); }); (0, _defineProperty2.default)(this, "onUseDefaultIdentityServerClick", e => { e.preventDefault(); // Update the IS in account data. Actually using it may trigger terms. // eslint-disable-next-line react-hooks/rules-of-hooks (0, _IdentityServerUtils.useDefaultIdentityServer)(); // Add email as a valid address type. const { validAddressTypes } = this.state; validAddressTypes.push('email'); this.setState({ validAddressTypes }); }); (0, _defineProperty2.default)(this, "onManageSettingsClick", e => { e.preventDefault(); _dispatcher.default.fire(_actions.Action.ViewUserSettings); this.onCancel(); }); this._textinput = /*#__PURE__*/(0, _react.createRef)(); let _validAddressTypes = this.props.validAddressTypes; // Remove email from validAddressTypes if no IS is configured. It may be added at a later stage by the user if (!_MatrixClientPeg.MatrixClientPeg.get().getIdentityServerUrl() && _validAddressTypes.includes("email")) { _validAddressTypes = _validAddressTypes.filter(type => type !== "email"); } this.state = { // Whether to show an error message because of an invalid address invalidAddressError: false, // List of UserAddressType objects representing // the list of addresses we're going to invite selectedList: [], // Whether a search is ongoing busy: false, // An error message generated during the user directory search searchError: null, // Whether the server supports the user_directory API serverSupportsUserDirectory: true, // The query being searched for query: "", // List of UserAddressType objects representing the set of // auto-completion results for the current search query. suggestedList: [], // List of address types initialised from props, but may change while the // dialog is open and represents the supported list of address types at this time. validAddressTypes: _validAddressTypes }; } componentDidMount() { if (this.props.focus) { // Set the cursor at the end of the text input this._textinput.current.value = this.props.value; } } getPlaceholder() { const { placeholder } = this.props; if (typeof placeholder === "string") { return placeholder; } // Otherwise it's a function, as checked by prop types. return placeholder(this.state.validAddressTypes); } _doNaiveGroupSearch(query) { const lowerCaseQuery = query.toLowerCase(); this.setState({ busy: true, query, searchError: null }); _MatrixClientPeg.MatrixClientPeg.get().getGroupUsers(this.props.groupId).then(resp => { const results = []; resp.chunk.forEach(u => { const userIdMatch = u.user_id.toLowerCase().includes(lowerCaseQuery); const displayNameMatch = (u.displayname || '').toLowerCase().includes(lowerCaseQuery); if (!(userIdMatch || displayNameMatch)) { return; } results.push({ user_id: u.user_id, avatar_url: u.avatar_url, display_name: u.displayname }); }); this._processResults(results, query); }).catch(err => { console.error('Error whilst searching group rooms: ', err); this.setState({ searchError: err.errcode ? err.message : (0, _languageHandler._t)('Something went wrong!') }); }).then(() => { this.setState({ busy: false }); }); } _doNaiveGroupRoomSearch(query) { const lowerCaseQuery = query.toLowerCase(); const results = []; _GroupStore.default.getGroupRooms(this.props.groupId).forEach(r => { const nameMatch = (r.name || '').toLowerCase().includes(lowerCaseQuery); const topicMatch = (r.topic || '').toLowerCase().includes(lowerCaseQuery); const aliasMatch = (r.canonical_alias || '').toLowerCase().includes(lowerCaseQuery); if (!(nameMatch || topicMatch || aliasMatch)) { return; } results.push({ room_id: r.room_id, avatar_url: r.avatar_url, name: r.name || r.canonical_alias }); }); this._processResults(results, query); this.setState({ busy: false }); } _doRoomSearch(query) { const lowerCaseQuery = query.toLowerCase(); const rooms = _MatrixClientPeg.MatrixClientPeg.get().getRooms(); const results = []; rooms.forEach(room => { let rank = Infinity; const nameEvent = room.currentState.getStateEvents('m.room.name', ''); const name = nameEvent ? nameEvent.getContent().name : ''; const canonicalAlias = room.getCanonicalAlias(); const aliasEvents = room.currentState.getStateEvents('m.room.aliases'); const aliases = aliasEvents.map(ev => ev.getContent().aliases).reduce((a, b) => { return a.concat(b); }, []); const nameMatch = (name || '').toLowerCase().includes(lowerCaseQuery); let aliasMatch = false; let shortestMatchingAliasLength = Infinity; aliases.forEach(alias => { if ((alias || '').toLowerCase().includes(lowerCaseQuery)) { aliasMatch = true; if (shortestMatchingAliasLength > alias.length) { shortestMatchingAliasLength = alias.length; } } }); if (!(nameMatch || aliasMatch)) { return; } if (aliasMatch) { // A shorter matching alias will give a better rank rank = shortestMatchingAliasLength; } const avatarEvent = room.currentState.getStateEvents('m.room.avatar', ''); const avatarUrl = avatarEvent ? avatarEvent.getContent().url : undefined; results.push({ rank, room_id: room.roomId, avatar_url: avatarUrl, name: name || canonicalAlias || aliases[0] || (0, _languageHandler._t)('Unnamed Room') }); }); // Sort by rank ascending (a high rank being less relevant) const sortedResults = results.sort((a, b) => { return a.rank - b.rank; }); this._processResults(sortedResults, query); this.setState({ busy: false }); } _doUserDirectorySearch(query) { this.setState({ busy: true, query, searchError: null }); _MatrixClientPeg.MatrixClientPeg.get().searchUserDirectory({ term: query }).then(resp => { // The query might have changed since we sent the request, so ignore // responses for anything other than the latest query. if (this.state.query !== query) { return; } this._processResults(resp.results, query); }).catch(err => { console.error('Error whilst searching user directory: ', err); this.setState({ searchError: err.errcode ? err.message : (0, _languageHandler._t)('Something went wrong!') }); if (err.errcode === 'M_UNRECOGNIZED') { this.setState({ serverSupportsUserDirectory: false }); // Do a local search immediately this._doLocalSearch(query); } }).then(() => { this.setState({ busy: false }); }); } _doLocalSearch(query) { this.setState({ query, searchError: null }); const queryLowercase = query.toLowerCase(); const results = []; _MatrixClientPeg.MatrixClientPeg.get().getUsers().forEach(user => { if (user.userId.toLowerCase().indexOf(queryLowercase) === -1 && user.displayName.toLowerCase().indexOf(queryLowercase) === -1) { return; } // Put results in the format of the new API results.push({ user_id: user.userId, display_name: user.displayName, avatar_url: user.avatarUrl }); }); this._processResults(results, query); } _processResults(results, query) { const suggestedList = []; results.forEach(result => { if (result.room_id) { const client = _MatrixClientPeg.MatrixClientPeg.get(); const room = client.getRoom(result.room_id); if (room) { const tombstone = room.currentState.getStateEvents('m.room.tombstone', ''); if (tombstone && tombstone.getContent() && tombstone.getContent()["replacement_room"]) { const replacementRoom = client.getRoom(tombstone.getContent()["replacement_room"]); // Skip rooms with tombstones where we are also aware of the replacement room. if (replacementRoom) return; } } suggestedList.push({ addressType: 'mx-room-id', address: result.room_id, displayName: result.name, avatarMxc: result.avatar_url, isKnown: true }); return; } if (!this.props.includeSelf && result.user_id === _MatrixClientPeg.MatrixClientPeg.get().credentials.userId) { return; } // Return objects, structure of which is defined // by UserAddressType suggestedList.push({ addressType: 'mx-user-id', address: result.user_id, displayName: result.display_name, avatarMxc: result.avatar_url, isKnown: true }); }); // If the query is a valid address, add an entry for that // This is important, otherwise there's no way to invite // a perfectly valid address if there are close matches. const addrType = (0, _UserAddress.getAddressType)(query); if (this.state.validAddressTypes.includes(addrType)) { if (addrType === 'email' && !Email.looksValid(query)) { this.setState({ searchError: (0, _languageHandler._t)("That doesn't look like a valid email address") }); return; } suggestedList.unshift({ addressType: addrType, address: query, isKnown: false }); if (this._cancelThreepidLookup) this._cancelThreepidLookup(); if (addrType === 'email') { this._lookupThreepid(addrType, query); } } this.setState({ suggestedList, invalidAddressError: false }, () => { if (this.addressSelector) this.addressSelector.moveSelectionTop(); }); } _addAddressesToList(addressTexts) { const selectedList = this.state.selectedList.slice(); let hasError = false; addressTexts.forEach(addressText => { addressText = addressText.trim(); const addrType = (0, _UserAddress.getAddressType)(addressText); const addrObj = { addressType: addrType, address: addressText, isKnown: false }; if (!this.state.validAddressTypes.includes(addrType)) { hasError = true; } else if (addrType === 'mx-user-id') { const user = _MatrixClientPeg.MatrixClientPeg.get().getUser(addrObj.address); if (user) { addrObj.displayName = user.displayName; addrObj.avatarMxc = user.avatarUrl; addrObj.isKnown = true; } } else if (addrType === 'mx-room-id') { const room = _MatrixClientPeg.MatrixClientPeg.get().getRoom(addrObj.address); if (room) { addrObj.displayName = room.name; addrObj.avatarMxc = room.avatarUrl; addrObj.isKnown = true; } } selectedList.push(addrObj); }); this.setState({ selectedList, suggestedList: [], query: "", invalidAddressError: hasError ? true : this.state.invalidAddressError }); if (this._cancelThreepidLookup) this._cancelThreepidLookup(); return hasError ? null : selectedList; } async _lookupThreepid(medium, address) { let cancelled = false; // Note that we can't safely remove this after we're done // because we don't know that it's the same one, so we just // leave it: it's replacing the old one each time so it's // not like they leak. this._cancelThreepidLookup = function () { cancelled = true; }; // wait a bit to let the user finish typing await (0, _promise.sleep)(500); if (cancelled) return null; try { const authClient = new _IdentityAuthClient.default(); const identityAccessToken = await authClient.getAccessToken(); if (cancelled) return null; const lookup = await _MatrixClientPeg.MatrixClientPeg.get().lookupThreePid(medium, address, undefined /* callback */ , identityAccessToken); if (cancelled || lookup === null || !lookup.mxid) return null; const profile = await _MatrixClientPeg.MatrixClientPeg.get().getProfileInfo(lookup.mxid); if (cancelled || profile === null) return null; this.setState({ suggestedList: [{ // a UserAddressType addressType: medium, address: address, displayName: profile.displayname, avatarMxc: profile.avatar_url, isKnown: true }] }); } catch (e) { console.error(e); this.setState({ searchError: (0, _languageHandler._t)('Something went wrong!') }); } } _getFilteredSuggestions() { // map addressType => set of addresses to avoid O(n*m) operation const selectedAddresses = {}; this.state.selectedList.forEach(({ address, addressType }) => { if (!selectedAddresses[addressType]) selectedAddresses[addressType] = new Set(); selectedAddresses[addressType].add(address); }); // Filter out any addresses in the above already selected addresses (matching both type and address) return this.state.suggestedList.filter(({ address, addressType }) => { return !(selectedAddresses[addressType] && selectedAddresses[addressType].has(address)); }); } render() { const BaseDialog = sdk.getComponent('views.dialogs.BaseDialog'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); const AddressSelector = sdk.getComponent("elements.AddressSelector"); this.scrollElement = null; let inputLabel; if (this.props.description) { inputLabel = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_label" }, /*#__PURE__*/_react.default.createElement("label", { htmlFor: "textinput" }, this.props.description)); } const query = []; // create the invite list if (this.state.selectedList.length > 0) { const AddressTile = sdk.getComponent("elements.AddressTile"); for (let i = 0; i < this.state.selectedList.length; i++) { query.push( /*#__PURE__*/_react.default.createElement(AddressTile, { key: i, address: this.state.selectedList[i], canDismiss: true, onDismissed: this.onDismissed(i), showAddress: this.props.pickerType === 'user' })); } } // Add the query at the end query.push( /*#__PURE__*/_react.default.createElement("textarea", { key: this.state.selectedList.length, onPaste: this._onPaste, rows: "1", id: "textinput", ref: this._textinput, className: "mx_AddressPickerDialog_input", onChange: this.onQueryChanged, placeholder: this.getPlaceholder(), defaultValue: this.props.value, autoFocus: this.props.focus })); const filteredSuggestedList = this._getFilteredSuggestions(); let error; let addressSelector; if (this.state.invalidAddressError) { const validTypeDescriptions = this.state.validAddressTypes.map(t => (0, _languageHandler._t)(addressTypeName[t])); error = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_error" }, (0, _languageHandler._t)("You have entered an invalid address."), /*#__PURE__*/_react.default.createElement("br", null), (0, _languageHandler._t)("Try using one of the following valid address types: %(validTypesList)s.", { validTypesList: validTypeDescriptions.join(", ") })); } else if (this.state.searchError) { error = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_error" }, this.state.searchError); } else if (this.state.query.length > 0 && filteredSuggestedList.length === 0 && !this.state.busy) { error = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_error" }, (0, _languageHandler._t)("No results")); } else { addressSelector = /*#__PURE__*/_react.default.createElement(AddressSelector, { ref: ref => { this.addressSelector = ref; }, addressList: filteredSuggestedList, showAddress: this.props.pickerType === 'user', onSelected: this.onSelected, truncateAt: TRUNCATE_QUERY_LIST }); } let identityServer; // If picker cannot currently accept e-mail but should be able to if (this.props.pickerType === 'user' && !this.state.validAddressTypes.includes('email') && this.props.validAddressTypes.includes('email')) { const defaultIdentityServerUrl = (0, _IdentityServerUtils.getDefaultIdentityServerUrl)(); if (defaultIdentityServerUrl) { identityServer = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_identityServer" }, (0, _languageHandler._t)("Use an identity server to invite by email. " + "<default>Use the default (%(defaultIdentityServerName)s)</default> " + "or manage in <settings>Settings</settings>.", { defaultIdentityServerName: (0, _UrlUtils.abbreviateUrl)(defaultIdentityServerUrl) }, { default: sub => /*#__PURE__*/_react.default.createElement("a", { href: "#", onClick: this.onUseDefaultIdentityServerClick }, sub), settings: sub => /*#__PURE__*/_react.default.createElement("a", { href: "#", onClick: this.onManageSettingsClick }, sub) })); } else { identityServer = /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_identityServer" }, (0, _languageHandler._t)("Use an identity server to invite by email. " + "Manage in <settings>Settings</settings>.", {}, { settings: sub => /*#__PURE__*/_react.default.createElement("a", { href: "#", onClick: this.onManageSettingsClick }, sub) })); } } return /*#__PURE__*/_react.default.createElement(BaseDialog, { className: "mx_AddressPickerDialog", onKeyDown: this.onKeyDown, onFinished: this.props.onFinished, title: this.props.title }, inputLabel, /*#__PURE__*/_react.default.createElement("div", { className: "mx_Dialog_content" }, /*#__PURE__*/_react.default.createElement("div", { className: "mx_AddressPickerDialog_inputContainer" }, query), error, addressSelector, this.props.extraNode, identityServer), /*#__PURE__*/_react.default.createElement(DialogButtons, { primaryButton: this.props.button, onPrimaryButtonClick: this.onButtonClick, onCancel: this.onCancel })); } }, (0, _defineProperty2.default)(_class2, "propTypes", { title: _propTypes.default.string.isRequired, description: _propTypes.default.node, // Extra node inserted after picker input, dropdown and errors extraNode: _propTypes.default.node, value: _propTypes.default.string, placeholder: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.func]), roomId: _propTypes.default.string, button: _propTypes.default.string, focus: _propTypes.default.bool, validAddressTypes: _propTypes.default.arrayOf(_propTypes.default.oneOf(_UserAddress.addressTypes)), onFinished: _propTypes.default.func.isRequired, groupId: _propTypes.default.string, // The type of entity to search for. Default: 'user'. pickerType: _propTypes.default.oneOf(['user', 'room']), // Whether the current user should be included in the addresses returned. Only // applicable when pickerType is `user`. Default: false. includeSelf: _propTypes.default.bool }), (0, _defineProperty2.default)(_class2, "defaultProps", { value: "", focus: true, validAddressTypes: _UserAddress.addressTypes, pickerType: 'user', includeSelf: false }), _temp)) || _class); exports.default = AddressPickerDialog; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3ZpZXdzL2RpYWxvZ3MvQWRkcmVzc1BpY2tlckRpYWxvZy5qcyJdLCJuYW1lcyI6WyJUUlVOQ0FURV9RVUVSWV9MSVNUIiwiUVVFUllfVVNFUl9ESVJFQ1RPUllfREVCT1VOQ0VfTVMiLCJhZGRyZXNzVHlwZU5hbWUiLCJBZGRyZXNzUGlja2VyRGlhbG9nIiwiUmVhY3QiLCJDb21wb25lbnQiLCJjb25zdHJ1Y3RvciIsInByb3BzIiwic2VsZWN0ZWRMaXN0Iiwic3RhdGUiLCJzbGljZSIsIl90ZXh0aW5wdXQiLCJjdXJyZW50IiwidmFsdWUiLCJfYWRkQWRkcmVzc2VzVG9MaXN0Iiwib25GaW5pc2hlZCIsImUiLCJ0ZXh0SW5wdXQiLCJ1bmRlZmluZWQiLCJrZXkiLCJLZXkiLCJFU0NBUEUiLCJzdG9wUHJvcGFnYXRpb24iLCJwcmV2ZW50RGVmYXVsdCIsIkFSUk9XX1VQIiwiYWRkcmVzc1NlbGVjdG9yIiwibW92ZVNlbGVjdGlvblVwIiwiQVJST1dfRE9XTiIsIm1vdmVTZWxlY3Rpb25Eb3duIiwic3VnZ2VzdGVkTGlzdCIsImxlbmd0aCIsIkNPTU1BIiwiRU5URVIiLCJUQUIiLCJpbmNsdWRlcyIsImNob29zZVNlbGVjdGlvbiIsIkJBQ0tTUEFDRSIsIm9uRGlzbWlzc2VkIiwib25CdXR0b25DbGljayIsImV2IiwicXVlcnkiLCJ0YXJnZXQiLCJxdWVyeUNoYW5nZWREZWJvdW5jZXIiLCJjbGVhclRpbWVvdXQiLCJzZXRUaW1lb3V0IiwicGlja2VyVHlwZSIsImdyb3VwSWQiLCJfZG9OYWl2ZUdyb3VwU2VhcmNoIiwic2VydmVyU3VwcG9ydHNVc2VyRGlyZWN0b3J5IiwiX2RvVXNlckRpcmVjdG9yeVNlYXJjaCIsIl9kb0xvY2FsU2VhcmNoIiwiX2RvTmFpdmVHcm91cFJvb21TZWFyY2giLCJfZG9Sb29tU2VhcmNoIiwiY29uc29sZSIsImVycm9yIiwic2V0U3RhdGUiLCJzZWFyY2hFcnJvciIsImluZGV4Iiwic3BsaWNlIiwiX2NhbmNlbFRocmVlcGlkTG9va3VwIiwib25TZWxlY3RlZCIsInB1c2giLCJfZ2V0RmlsdGVyZWRTdWdnZXN0aW9ucyIsInRleHQiLCJjbGlwYm9hcmREYXRhIiwiZ2V0RGF0YSIsInNwbGl0IiwidmFsaWRBZGRyZXNzVHlwZXMiLCJkaXMiLCJmaXJlIiwiQWN0aW9uIiwiVmlld1VzZXJTZXR0aW5ncyIsIm9uQ2FuY2VsIiwiTWF0cml4Q2xpZW50UGVnIiwiZ2V0IiwiZ2V0SWRlbnRpdHlTZXJ2ZXJVcmwiLCJmaWx0ZXIiLCJ0eXBlIiwiaW52YWxpZEFkZHJlc3NFcnJvciIsImJ1c3kiLCJjb21wb25lbnREaWRNb3VudCIsImZvY3VzIiwiZ2V0UGxhY2Vob2xkZXIiLCJwbGFjZWhvbGRlciIsImxvd2VyQ2FzZVF1ZXJ5IiwidG9Mb3dlckNhc2UiLCJnZXRHcm91cFVzZXJzIiwidGhlbiIsInJlc3AiLCJyZXN1bHRzIiwiY2h1bmsiLCJmb3JFYWNoIiwidSIsInVzZXJJZE1hdGNoIiwidXNlcl9pZCIsImRpc3BsYXlOYW1lTWF0Y2giLCJkaXNwbGF5bmFtZSIsImF2YXRhcl91cmwiLCJkaXNwbGF5X25hbWUiLCJfcHJvY2Vzc1Jlc3VsdHMiLCJjYXRjaCIsImVyciIsImVycmNvZGUiLCJtZXNzYWdlIiwiR3JvdXBTdG9yZSIsImdldEdyb3VwUm9vbXMiLCJyIiwibmFtZU1hdGNoIiwibmFtZSIsInRvcGljTWF0Y2giLCJ0b3BpYyIsImFsaWFzTWF0Y2giLCJjYW5vbmljYWxfYWxpYXMiLCJyb29tX2lkIiwicm9vbXMiLCJnZXRSb29tcyIsInJvb20iLCJyYW5rIiwiSW5maW5pdHkiLCJuYW1lRXZlbnQiLCJjdXJyZW50U3RhdGUiLCJnZXRTdGF0ZUV2ZW50cyIsImdldENvbnRlbnQiLCJjYW5vbmljYWxBbGlhcyIsImdldENhbm9uaWNhbEFsaWFzIiwiYWxpYXNFdmVudHMiLCJhbGlhc2VzIiwibWFwIiwicmVkdWNlIiwiYSIsImIiLCJjb25jYXQiLCJzaG9ydGVzdE1hdGNoaW5nQWxpYXNMZW5ndGgiLCJhbGlhcyIsImF2YXRhckV2ZW50IiwiYXZhdGFyVXJsIiwidXJsIiwicm9vbUlkIiwic29ydGVkUmVzdWx0cyIsInNvcnQiLCJzZWFyY2hVc2VyRGlyZWN0b3J5IiwidGVybSIsInF1ZXJ5TG93ZXJjYXNlIiwiZ2V0VXNlcnMiLCJ1c2VyIiwidXNlcklkIiwiaW5kZXhPZiIsImRpc3BsYXlOYW1lIiwicmVzdWx0IiwiY2xpZW50IiwiZ2V0Um9vbSIsInRvbWJzdG9uZSIsInJlcGxhY2VtZW50Um9vbSIsImFkZHJlc3NUeXBlIiwiYWRkcmVzcyIsImF2YXRhck14YyIsImlzS25vd24iLCJpbmNsdWRlU2VsZiIsImNyZWRlbnRpYWxzIiwiYWRkclR5cGUiLCJFbWFpbCIsImxvb2tzVmFsaWQiLCJ1bnNoaWZ0IiwiX2xvb2t1cFRocmVlcGlkIiwibW92ZVNlbGVjdGlvblRvcCIsImFkZHJlc3NUZXh0cyIsImhhc0Vycm9yIiwiYWRkcmVzc1RleHQiLCJ0cmltIiwiYWRkck9iaiIsImdldFVzZXIiLCJtZWRpdW0iLCJjYW5jZWxsZWQiLCJhdXRoQ2xpZW50IiwiSWRlbnRpdHlBdXRoQ2xpZW50IiwiaWRlbnRpdHlBY2Nlc3NUb2tlbiIsImdldEFjY2Vzc1Rva2VuIiwibG9va3VwIiwibG9va3VwVGhyZWVQaWQiLCJteGlkIiwicHJvZmlsZSIsImdldFByb2ZpbGVJbmZvIiwic2VsZWN0ZWRBZGRyZXNzZXMiLCJTZXQiLCJhZGQiLCJoYXMiLCJyZW5kZXIiLCJCYXNlRGlhbG9nIiwic2RrIiwiZ2V0Q29tcG9uZW50IiwiRGlhbG9nQnV0dG9ucyIsIkFkZHJlc3NTZWxlY3RvciIsInNjcm9sbEVsZW1lbnQiLCJpbnB1dExhYmVsIiwiZGVzY3JpcHRpb24iLCJBZGRyZXNzVGlsZSIsImkiLCJfb25QYXN0ZSIsIm9uUXVlcnlDaGFuZ2VkIiwiZmlsdGVyZWRTdWdnZXN0ZWRMaXN0IiwidmFsaWRUeXBlRGVzY3JpcHRpb25zIiwidCIsInZhbGlkVHlwZXNMaXN0Iiwiam9pbiIsInJlZiIsImlkZW50aXR5U2VydmVyIiwiZGVmYXVsdElkZW50aXR5U2VydmVyVXJsIiwiZGVmYXVsdElkZW50aXR5U2VydmVyTmFtZSIsImRlZmF1bHQiLCJzdWIiLCJvblVzZURlZmF1bHRJZGVudGl0eVNlcnZlckNsaWNrIiwic2V0dGluZ3MiLCJvbk1hbmFnZVNldHRpbmdzQ2xpY2siLCJvbktleURvd24iLCJ0aXRsZSIsImV4dHJhTm9kZSIsImJ1dHRvbiIsIlByb3BUeXBlcyIsInN0cmluZyIsImlzUmVxdWlyZWQiLCJub2RlIiwib25lT2ZUeXBlIiwiZnVuYyIsImJvb2wiLCJhcnJheU9mIiwib25lT2YiLCJhZGRyZXNzVHlwZXMiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7QUFtQkE7O0FBQ0E7O0FBRUE7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7O0FBQ0E7Ozs7QUFFQSxNQUFNQSxtQkFBbUIsR0FBRyxFQUE1QjtBQUNBLE1BQU1DLGdDQUFnQyxHQUFHLEdBQXpDO0FBRUEsTUFBTUMsZUFBZSxHQUFHO0FBQ3BCLGdCQUFjLDBCQUFJLFdBQUosQ0FETTtBQUVwQixnQkFBYywwQkFBSSxnQkFBSixDQUZNO0FBR3BCLFdBQVMsMEJBQUksZUFBSjtBQUhXLENBQXhCO0lBT3FCQyxtQixXQURwQixnREFBcUIsbUNBQXJCLEMsbUNBQUQsTUFDcUJBLG1CQURyQixTQUNpREMsZUFBTUMsU0FEdkQsQ0FDaUU7QUE2QjdEQyxFQUFBQSxXQUFXLENBQUNDLEtBQUQsRUFBUTtBQUNmLFVBQU1BLEtBQU47QUFEZSx5REFrREgsTUFBTTtBQUNsQixVQUFJQyxZQUFZLEdBQUcsS0FBS0MsS0FBTCxDQUFXRCxZQUFYLENBQXdCRSxLQUF4QixFQUFuQixDQURrQixDQUVsQjtBQUNBOztBQUNBLFVBQUksS0FBS0MsVUFBTCxDQUFnQkMsT0FBaEIsQ0FBd0JDLEtBQXhCLEtBQWtDLEVBQXRDLEVBQTBDO0FBQ3RDTCxRQUFBQSxZQUFZ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