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