matrix-react-sdk
Version:
SDK for matrix.org using React
251 lines (236 loc) • 33.6 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.MemberListStore = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _types = require("matrix-js-sdk/src/types");
var _SettingsStore = _interopRequireDefault(require("../settings/SettingsStore"));
var _SdkConfig = _interopRequireDefault(require("../SdkConfig"));
/*
Copyright 2024 New Vector Ltd.
Copyright 2022 The Matrix.org Foundation C.I.C.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only
Please see LICENSE files in the repository root for full details.
*/
// Regex applied to filter our punctuation in member names before applying sort, to fuzzy it a little
// matches all ASCII punctuation: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
const SORT_REGEX = /[\x21-\x2F\x3A-\x40\x5B-\x60\x7B-\x7E]+/g;
/**
* A class for storing application state for MemberList.
*/
class MemberListStore {
constructor(stores) {
// cache of Display Name -> name to sort based on. This strips out special symbols like @.
(0, _defineProperty2.default)(this, "sortNames", new Map());
// list of room IDs that have been lazy loaded
(0, _defineProperty2.default)(this, "loadedRooms", new Set());
(0, _defineProperty2.default)(this, "collator", void 0);
this.stores = stores;
}
/**
* Load the member list. Call this whenever the list may have changed.
* @param roomId The room to load the member list in
* @param searchQuery Optional search query to filter the list.
* @returns A list of filtered and sorted room members, grouped by membership.
*/
async loadMemberList(roomId, searchQuery) {
if (!this.stores.client) {
return {
joined: [],
invited: []
};
}
const language = _SettingsStore.default.getValue("language");
this.collator = new Intl.Collator(language, {
sensitivity: "base",
ignorePunctuation: false
});
const members = await this.loadMembers(roomId);
// Filter then sort as it's more efficient than sorting tons of members we will just filter out later.
// Also sort each group, as there's no point comparing invited/joined users when they aren't in the same list!
const membersByMembership = this.filterMembers(members, searchQuery);
membersByMembership.joined.sort((a, b) => {
return this.sortMembers(a, b);
});
membersByMembership.invited.sort((a, b) => {
return this.sortMembers(a, b);
});
return {
joined: membersByMembership.joined,
invited: membersByMembership.invited
};
}
async loadMembers(roomId) {
const room = this.stores.client.getRoom(roomId);
if (!room) {
return [];
}
if (!this.isLazyLoadingEnabled(roomId) || this.loadedRooms.has(roomId)) {
// nice and easy, we must already have all the members so just return them.
return this.loadMembersInRoom(room);
}
// lazy loading is enabled. There are two kinds of lazy loading:
// - With storage: most members are in indexedDB, we just need a small delta via /members.
// Valid for normal sync in normal windows.
// - Without storage: nothing in indexedDB, we need to load all via /members. Valid for
// Sliding Sync and incognito windows (non-Sliding Sync).
if (!this.isLazyMemberStorageEnabled()) {
// pull straight from the server. Don't use a since token as we don't have earlier deltas
// accumulated.
room.currentState.markOutOfBandMembersStarted();
const response = await this.stores.client.members(roomId, undefined, _types.KnownMembership.Leave);
const memberEvents = response.chunk.map(this.stores.client.getEventMapper());
room.currentState.setOutOfBandMembers(memberEvents);
} else {
// load using traditional lazy loading
try {
await room.loadMembersIfNeeded();
} catch (ex) {
/* already logged in RoomView */
}
}
// remember that we have loaded the members so we don't hit /members all the time. We
// will forget this on refresh which is fine as we only store the data in-memory.
this.loadedRooms.add(roomId);
return this.loadMembersInRoom(room);
}
loadMembersInRoom(room) {
const allMembers = Object.values(room.currentState.members);
allMembers.forEach(member => {
// work around a race where you might have a room member object
// before the user object exists. This may or may not cause
// https://github.com/vector-im/vector-web/issues/186
if (!member.user) {
member.user = this.stores.client.getUser(member.userId) || undefined;
}
// XXX: this user may have no lastPresenceTs value!
// the right solution here is to fix the race rather than leave it as 0
});
return allMembers;
}
/**
* Check if this room should be lazy loaded. Lazy loading means fetching the member list in
* a delayed or incremental fashion. It means the `Room` object doesn't have all the members.
* @param roomId The room to check if lazy loading is enabled
* @returns True if enabled
*/
isLazyLoadingEnabled(roomId) {
if (_SettingsStore.default.getValue("feature_sliding_sync")) {
// only unencrypted rooms use lazy loading
return !this.stores.client.isRoomEncrypted(roomId);
}
return this.stores.client.hasLazyLoadMembersEnabled();
}
/**
* Check if lazy member storage is supported.
* @returns True if there is storage for lazy loading members
*/
isLazyMemberStorageEnabled() {
if (_SettingsStore.default.getValue("feature_sliding_sync")) {
return false;
}
return this.stores.client.hasLazyLoadMembersEnabled();
}
isPresenceEnabled() {
if (!this.stores.client) {
return true;
}
const enablePresenceByHsUrl = _SdkConfig.default.get("enable_presence_by_hs_url");
return enablePresenceByHsUrl?.[this.stores.client.baseUrl] ?? true;
}
/**
* Filter out members based on an optional search query. Groups by membership state.
* @param members The list of members to filter.
* @param query The textual query to filter based on.
* @returns An object with a list of joined and invited users respectively.
*/
filterMembers(members, query) {
const result = {
joined: [],
invited: []
};
members.forEach(m => {
if (m.membership !== _types.KnownMembership.Join && m.membership !== _types.KnownMembership.Invite) {
return; // bail early for left/banned users
}
if (query) {
query = query.toLowerCase();
const matchesName = m.name.toLowerCase().includes(query);
const matchesId = m.userId.toLowerCase().includes(query);
if (!matchesName && !matchesId) {
return;
}
}
switch (m.membership) {
case _types.KnownMembership.Join:
result.joined.push(m);
break;
case _types.KnownMembership.Invite:
result.invited.push(m);
break;
}
});
return result;
}
/**
* Sort algorithm for room members.
* @param memberA
* @param memberB
* @returns Negative if A comes before B, 0 if A and B are equivalent, Positive is A comes after B.
*/
sortMembers(memberA, memberB) {
// order by presence, with "active now" first.
// ...and then by power level
// ...and then by last active
// ...and then alphabetically.
// We could tiebreak instead by "last recently spoken in this room" if we wanted to.
const userA = memberA.user;
const userB = memberB.user;
if (!userA && !userB) return 0;
if (userA && !userB) return -1;
if (!userA && userB) return 1;
const showPresence = this.isPresenceEnabled();
// First by presence
if (showPresence) {
const convertPresence = p => p === "unavailable" ? "online" : p;
const presenceIndex = p => {
const order = ["active", "online", "offline"];
const idx = order.indexOf(convertPresence(p));
return idx === -1 ? order.length : idx; // unknown states at the end
};
const idxA = presenceIndex(userA.currentlyActive ? "active" : userA.presence);
const idxB = presenceIndex(userB.currentlyActive ? "active" : userB.presence);
if (idxA !== idxB) {
return idxA - idxB;
}
}
// Second by power level
if (memberA.powerLevel !== memberB.powerLevel) {
return memberB.powerLevel - memberA.powerLevel;
}
// Third by last active
if (showPresence && userA.getLastActiveTs() !== userB.getLastActiveTs()) {
return userB.getLastActiveTs() - userA.getLastActiveTs();
}
// Fourth by name (alphabetical)
return this.collator.compare(this.canonicalisedName(memberA.name), this.canonicalisedName(memberB.name));
}
/**
* Calculate the canonicalised name for the input name.
* @param name The member display name
* @returns The name to sort on
*/
canonicalisedName(name) {
let result = this.sortNames.get(name);
if (result) {
return result;
}
result = (name[0] === "@" ? name.slice(1) : name).replace(SORT_REGEX, "");
this.sortNames.set(name, result);
return result;
}
}
exports.MemberListStore = MemberListStore;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfdHlwZXMiLCJyZXF1aXJlIiwiX1NldHRpbmdzU3RvcmUiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwiX1Nka0NvbmZpZyIsIlNPUlRfUkVHRVgiLCJNZW1iZXJMaXN0U3RvcmUiLCJjb25zdHJ1Y3RvciIsInN0b3JlcyIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJkZWZhdWx0IiwiTWFwIiwiU2V0IiwibG9hZE1lbWJlckxpc3QiLCJyb29tSWQiLCJzZWFyY2hRdWVyeSIsImNsaWVudCIsImpvaW5lZCIsImludml0ZWQiLCJsYW5ndWFnZSIsIlNldHRpbmdzU3RvcmUiLCJnZXRWYWx1ZSIsImNvbGxhdG9yIiwiSW50bCIsIkNvbGxhdG9yIiwic2Vuc2l0aXZpdHkiLCJpZ25vcmVQdW5jdHVhdGlvbiIsIm1lbWJlcnMiLCJsb2FkTWVtYmVycyIsIm1lbWJlcnNCeU1lbWJlcnNoaXAiLCJmaWx0ZXJNZW1iZXJzIiwic29ydCIsImEiLCJiIiwic29ydE1lbWJlcnMiLCJyb29tIiwiZ2V0Um9vbSIsImlzTGF6eUxvYWRpbmdFbmFibGVkIiwibG9hZGVkUm9vbXMiLCJoYXMiLCJsb2FkTWVtYmVyc0luUm9vbSIsImlzTGF6eU1lbWJlclN0b3JhZ2VFbmFibGVkIiwiY3VycmVudFN0YXRlIiwibWFya091dE9mQmFuZE1lbWJlcnNTdGFydGVkIiwicmVzcG9uc2UiLCJ1bmRlZmluZWQiLCJLbm93bk1lbWJlcnNoaXAiLCJMZWF2ZSIsIm1lbWJlckV2ZW50cyIsImNodW5rIiwibWFwIiwiZ2V0RXZlbnRNYXBwZXIiLCJzZXRPdXRPZkJhbmRNZW1iZXJzIiwibG9hZE1lbWJlcnNJZk5lZWRlZCIsImV4IiwiYWRkIiwiYWxsTWVtYmVycyIsIk9iamVjdCIsInZhbHVlcyIsImZvckVhY2giLCJtZW1iZXIiLCJ1c2VyIiwiZ2V0VXNlciIsInVzZXJJZCIsImlzUm9vbUVuY3J5cHRlZCIsImhhc0xhenlMb2FkTWVtYmVyc0VuYWJsZWQiLCJpc1ByZXNlbmNlRW5hYmxlZCIsImVuYWJsZVByZXNlbmNlQnlIc1VybCIsIlNka0NvbmZpZyIsImdldCIsImJhc2VVcmwiLCJxdWVyeSIsInJlc3VsdCIsIm0iLCJtZW1iZXJzaGlwIiwiSm9pbiIsIkludml0ZSIsInRvTG93ZXJDYXNlIiwibWF0Y2hlc05hbWUiLCJuYW1lIiwiaW5jbHVkZXMiLCJtYXRjaGVzSWQiLCJwdXNoIiwibWVtYmVyQSIsIm1lbWJlckIiLCJ1c2VyQSIsInVzZXJCIiwic2hvd1ByZXNlbmNlIiwiY29udmVydFByZXNlbmNlIiwicCIsInByZXNlbmNlSW5kZXgiLCJvcmRlciIsImlkeCIsImluZGV4T2YiLCJsZW5ndGgiLCJpZHhBIiwiY3VycmVudGx5QWN0aXZlIiwicHJlc2VuY2UiLCJpZHhCIiwicG93ZXJMZXZlbCIsImdldExhc3RBY3RpdmVUcyIsImNvbXBhcmUiLCJjYW5vbmljYWxpc2VkTmFtZSIsInNvcnROYW1lcyIsInNsaWNlIiwicmVwbGFjZSIsInNldCIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi9zcmMvc3RvcmVzL01lbWJlckxpc3RTdG9yZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAyMiBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgeyBSb29tLCBSb29tTWVtYmVyIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL21hdHJpeFwiO1xuaW1wb3J0IHsgS25vd25NZW1iZXJzaGlwIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL3R5cGVzXCI7XG5cbmltcG9ydCBTZXR0aW5nc1N0b3JlIGZyb20gXCIuLi9zZXR0aW5ncy9TZXR0aW5nc1N0b3JlXCI7XG5pbXBvcnQgeyBTZGtDb250ZXh0Q2xhc3MgfSBmcm9tIFwiLi4vY29udGV4dHMvU0RLQ29udGV4dFwiO1xuaW1wb3J0IFNka0NvbmZpZyBmcm9tIFwiLi4vU2RrQ29uZmlnXCI7XG5cbi8vIFJlZ2V4IGFwcGxpZWQgdG8gZmlsdGVyIG91ciBwdW5jdHVhdGlvbiBpbiBtZW1iZXIgbmFtZXMgYmVmb3JlIGFwcGx5aW5nIHNvcnQsIHRvIGZ1enp5IGl0IGEgbGl0dGxlXG4vLyBtYXRjaGVzIGFsbCBBU0NJSSBwdW5jdHVhdGlvbjogIVwiIyQlJicoKSorLC0uLzo7PD0+P0BbXFxdXl9ge3x9flxuY29uc3QgU09SVF9SRUdFWCA9IC9bXFx4MjEtXFx4MkZcXHgzQS1cXHg0MFxceDVCLVxceDYwXFx4N0ItXFx4N0VdKy9nO1xuXG4vKipcbiAqIEEgY2xhc3MgZm9yIHN0b3JpbmcgYXBwbGljYXRpb24gc3RhdGUgZm9yIE1lbWJlckxpc3QuXG4gKi9cbmV4cG9ydCBjbGFzcyBNZW1iZXJMaXN0U3RvcmUge1xuICAgIC8vIGNhY2hlIG9mIERpc3BsYXkgTmFtZSAtPiBuYW1lIHRvIHNvcnQgYmFzZWQgb24uIFRoaXMgc3RyaXBzIG91dCBzcGVjaWFsIHN5bWJvbHMgbGlrZSBALlxuICAgIHByaXZhdGUgcmVhZG9ubHkgc29ydE5hbWVzID0gbmV3IE1hcDxzdHJpbmcsIHN0cmluZz4oKTtcbiAgICAvLyBsaXN0IG9mIHJvb20gSURzIHRoYXQgaGF2ZSBiZWVuIGxhenkgbG9hZGVkXG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2FkZWRSb29tcyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuXG4gICAgcHJpdmF0ZSBjb2xsYXRvcj86IEludGwuQ29sbGF0b3I7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBzdG9yZXM6IFNka0NvbnRleHRDbGFzcykge31cblxuICAgIC8qKlxuICAgICAqIExvYWQgdGhlIG1lbWJlciBsaXN0LiBDYWxsIHRoaXMgd2hlbmV2ZXIgdGhlIGxpc3QgbWF5IGhhdmUgY2hhbmdlZC5cbiAgICAgKiBAcGFyYW0gcm9vbUlkIFRoZSByb29tIHRvIGxvYWQgdGhlIG1lbWJlciBsaXN0IGluXG4gICAgICogQHBhcmFtIHNlYXJjaFF1ZXJ5IE9wdGlvbmFsIHNlYXJjaCBxdWVyeSB0byBmaWx0ZXIgdGhlIGxpc3QuXG4gICAgICogQHJldHVybnMgQSBsaXN0IG9mIGZpbHRlcmVkIGFuZCBzb3J0ZWQgcm9vbSBtZW1iZXJzLCBncm91cGVkIGJ5IG1lbWJlcnNoaXAuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIGxvYWRNZW1iZXJMaXN0KFxuICAgICAgICByb29tSWQ6IHN0cmluZyxcbiAgICAgICAgc2VhcmNoUXVlcnk/OiBzdHJpbmcsXG4gICAgKTogUHJvbWlzZTxSZWNvcmQ8XCJqb2luZWRcIiB8IFwiaW52aXRlZFwiLCBSb29tTWVtYmVyW10+PiB7XG4gICAgICAgIGlmICghdGhpcy5zdG9yZXMuY2xpZW50KSB7XG4gICAgICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgICAgIGpvaW5lZDogW10sXG4gICAgICAgICAgICAgICAgaW52aXRlZDogW10sXG4gICAgICAgICAgICB9O1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGxhbmd1YWdlID0gU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImxhbmd1YWdlXCIpO1xuICAgICAgICB0aGlzLmNvbGxhdG9yID0gbmV3IEludGwuQ29sbGF0b3IobGFuZ3VhZ2UsIHsgc2Vuc2l0aXZpdHk6IFwiYmFzZVwiLCBpZ25vcmVQdW5jdHVhdGlvbjogZmFsc2UgfSk7XG4gICAgICAgIGNvbnN0IG1lbWJlcnMgPSBhd2FpdCB0aGlzLmxvYWRNZW1iZXJzKHJvb21JZCk7XG4gICAgICAgIC8vIEZpbHRlciB0aGVuIHNvcnQgYXMgaXQncyBtb3JlIGVmZmljaWVudCB0aGFuIHNvcnRpbmcgdG9ucyBvZiBtZW1iZXJzIHdlIHdpbGwganVzdCBmaWx0ZXIgb3V0IGxhdGVyLlxuICAgICAgICAvLyBBbHNvIHNvcnQgZWFjaCBncm91cCwgYXMgdGhlcmUncyBubyBwb2ludCBjb21wYXJpbmcgaW52aXRlZC9qb2luZWQgdXNlcnMgd2hlbiB0aGV5IGFyZW4ndCBpbiB0aGUgc2FtZSBsaXN0IVxuICAgICAgICBjb25zdCBtZW1iZXJzQnlNZW1iZXJzaGlwID0gdGhpcy5maWx0ZXJNZW1iZXJzKG1lbWJlcnMsIHNlYXJjaFF1ZXJ5KTtcbiAgICAgICAgbWVtYmVyc0J5TWVtYmVyc2hpcC5qb2luZWQuc29ydCgoYTogUm9vbU1lbWJlciwgYjogUm9vbU1lbWJlcikgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc29ydE1lbWJlcnMoYSwgYik7XG4gICAgICAgIH0pO1xuICAgICAgICBtZW1iZXJzQnlNZW1iZXJzaGlwLmludml0ZWQuc29ydCgoYTogUm9vbU1lbWJlciwgYjogUm9vbU1lbWJlcikgPT4ge1xuICAgICAgICAgICAgcmV0dXJuIHRoaXMuc29ydE1lbWJlcnMoYSwgYik7XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgam9pbmVkOiBtZW1iZXJzQnlNZW1iZXJzaGlwLmpvaW5lZCxcbiAgICAgICAgICAgIGludml0ZWQ6IG1lbWJlcnNCeU1lbWJlcnNoaXAuaW52aXRlZCxcbiAgICAgICAgfTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGFzeW5jIGxvYWRNZW1iZXJzKHJvb21JZDogc3RyaW5nKTogUHJvbWlzZTxBcnJheTxSb29tTWVtYmVyPj4ge1xuICAgICAgICBjb25zdCByb29tID0gdGhpcy5zdG9yZXMuY2xpZW50IS5nZXRSb29tKHJvb21JZCk7XG4gICAgICAgIGlmICghcm9vbSkge1xuICAgICAgICAgICAgcmV0dXJuIFtdO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF0aGlzLmlzTGF6eUxvYWRpbmdFbmFibGVkKHJvb21JZCkgfHwgdGhpcy5sb2FkZWRSb29tcy5oYXMocm9vbUlkKSkge1xuICAgICAgICAgICAgLy8gbmljZSBhbmQgZWFzeSwgd2UgbXVzdCBhbHJlYWR5IGhhdmUgYWxsIHRoZSBtZW1iZXJzIHNvIGp1c3QgcmV0dXJuIHRoZW0uXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5sb2FkTWVtYmVyc0luUm9vbShyb29tKTtcbiAgICAgICAgfVxuICAgICAgICAvLyBsYXp5IGxvYWRpbmcgaXMgZW5hYmxlZC4gVGhlcmUgYXJlIHR3byBraW5kcyBvZiBsYXp5IGxvYWRpbmc6XG4gICAgICAgIC8vIC0gV2l0aCBzdG9yYWdlOiBtb3N0IG1lbWJlcnMgYXJlIGluIGluZGV4ZWREQiwgd2UganVzdCBuZWVkIGEgc21hbGwgZGVsdGEgdmlhIC9tZW1iZXJzLlxuICAgICAgICAvLyAgIFZhbGlkIGZvciBub3JtYWwgc3luYyBpbiBub3JtYWwgd2luZG93cy5cbiAgICAgICAgLy8gLSBXaXRob3V0IHN0b3JhZ2U6IG5vdGhpbmcgaW4gaW5kZXhlZERCLCB3ZSBuZWVkIHRvIGxvYWQgYWxsIHZpYSAvbWVtYmVycy4gVmFsaWQgZm9yXG4gICAgICAgIC8vICAgU2xpZGluZyBTeW5jIGFuZCBpbmNvZ25pdG8gd2luZG93cyAobm9uLVNsaWRpbmcgU3luYykuXG4gICAgICAgIGlmICghdGhpcy5pc0xhenlNZW1iZXJTdG9yYWdlRW5hYmxlZCgpKSB7XG4gICAgICAgICAgICAvLyBwdWxsIHN0cmFpZ2h0IGZyb20gdGhlIHNlcnZlci4gRG9uJ3QgdXNlIGEgc2luY2UgdG9rZW4gYXMgd2UgZG9uJ3QgaGF2ZSBlYXJsaWVyIGRlbHRhc1xuICAgICAgICAgICAgLy8gYWNjdW11bGF0ZWQuXG4gICAgICAgICAgICByb29tLmN1cnJlbnRTdGF0ZS5tYXJrT3V0T2ZCYW5kTWVtYmVyc1N0YXJ0ZWQoKTtcbiAgICAgICAgICAgIGNvbnN0IHJlc3BvbnNlID0gYXdhaXQgdGhpcy5zdG9yZXMuY2xpZW50IS5tZW1iZXJzKHJvb21JZCwgdW5kZWZpbmVkLCBLbm93bk1lbWJlcnNoaXAuTGVhdmUpO1xuICAgICAgICAgICAgY29uc3QgbWVtYmVyRXZlbnRzID0gcmVzcG9uc2UuY2h1bmsubWFwKHRoaXMuc3RvcmVzLmNsaWVudCEuZ2V0RXZlbnRNYXBwZXIoKSk7XG4gICAgICAgICAgICByb29tLmN1cnJlbnRTdGF0ZS5zZXRPdXRPZkJhbmRNZW1iZXJzKG1lbWJlckV2ZW50cyk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAvLyBsb2FkIHVzaW5nIHRyYWRpdGlvbmFsIGxhenkgbG9hZGluZ1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICBhd2FpdCByb29tLmxvYWRNZW1iZXJzSWZOZWVkZWQoKTtcbiAgICAgICAgICAgIH0gY2F0Y2ggKGV4KSB7XG4gICAgICAgICAgICAgICAgLyogYWxyZWFkeSBsb2dnZWQgaW4gUm9vbVZpZXcgKi9cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICAvLyByZW1lbWJlciB0aGF0IHdlIGhhdmUgbG9hZGVkIHRoZSBtZW1iZXJzIHNvIHdlIGRvbid0IGhpdCAvbWVtYmVycyBhbGwgdGhlIHRpbWUuIFdlXG4gICAgICAgIC8vIHdpbGwgZm9yZ2V0IHRoaXMgb24gcmVmcmVzaCB3aGljaCBpcyBmaW5lIGFzIHdlIG9ubHkgc3RvcmUgdGhlIGRhdGEgaW4tbWVtb3J5LlxuICAgICAgICB0aGlzLmxvYWRlZFJvb21zLmFkZChyb29tSWQpO1xuICAgICAgICByZXR1cm4gdGhpcy5sb2FkTWVtYmVyc0luUm9vbShyb29tKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGxvYWRNZW1iZXJzSW5Sb29tKHJvb206IFJvb20pOiBBcnJheTxSb29tTWVtYmVyPiB7XG4gICAgICAgIGNvbnN0IGFsbE1lbWJlcnMgPSBPYmplY3QudmFsdWVzKHJvb20uY3VycmVudFN0YXRlLm1lbWJlcnMpO1xuICAgICAgICBhbGxNZW1iZXJzLmZvckVhY2goKG1lbWJlcikgPT4ge1xuICAgICAgICAgICAgLy8gd29yayBhcm91bmQgYSByYWNlIHdoZXJlIHlvdSBtaWdodCBoYXZlIGEgcm9vbSBtZW1iZXIgb2JqZWN0XG4gICAgICAgICAgICAvLyBiZWZvcmUgdGhlIHVzZXIgb2JqZWN0IGV4aXN0cy4gVGhpcyBtYXkgb3IgbWF5IG5vdCBjYXVzZVxuICAgICAgICAgICAgLy8gaHR0cHM6Ly9naXRodWIuY29tL3ZlY3Rvci1pbS92ZWN0b3Itd2ViL2lzc3Vlcy8xODZcbiAgICAgICAgICAgIGlmICghbWVtYmVyLnVzZXIpIHtcbiAgICAgICAgICAgICAgICBtZW1iZXIudXNlciA9IHRoaXMuc3RvcmVzLmNsaWVudCEuZ2V0VXNlcihtZW1iZXIudXNlcklkKSB8fCB1bmRlZmluZWQ7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICAvLyBYWFg6IHRoaXMgdXNlciBtYXkgaGF2ZSBubyBsYXN0UHJlc2VuY2VUcyB2YWx1ZSFcbiAgICAgICAgICAgIC8vIHRoZSByaWdodCBzb2x1dGlvbiBoZXJlIGlzIHRvIGZpeCB0aGUgcmFjZSByYXRoZXIgdGhhbiBsZWF2ZSBpdCBhcyAwXG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gYWxsTWVtYmVycztcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDaGVjayBpZiB0aGlzIHJvb20gc2hvdWxkIGJlIGxhenkgbG9hZGVkLiBMYXp5IGxvYWRpbmcgbWVhbnMgZmV0Y2hpbmcgdGhlIG1lbWJlciBsaXN0IGluXG4gICAgICogYSBkZWxheWVkIG9yIGluY3JlbWVudGFsIGZhc2hpb24uIEl0IG1lYW5zIHRoZSBgUm9vbWAgb2JqZWN0IGRvZXNuJ3QgaGF2ZSBhbGwgdGhlIG1lbWJlcnMuXG4gICAgICogQHBhcmFtIHJvb21JZCBUaGUgcm9vbSB0byBjaGVjayBpZiBsYXp5IGxvYWRpbmcgaXMgZW5hYmxlZFxuICAgICAqIEByZXR1cm5zIFRydWUgaWYgZW5hYmxlZFxuICAgICAqL1xuICAgIHByaXZhdGUgaXNMYXp5TG9hZGluZ0VuYWJsZWQocm9vbUlkOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKFNldHRpbmdzU3RvcmUuZ2V0VmFsdWUoXCJmZWF0dXJlX3NsaWRpbmdfc3luY1wiKSkge1xuICAgICAgICAgICAgLy8gb25seSB1bmVuY3J5cHRlZCByb29tcyB1c2UgbGF6eSBsb2FkaW5nXG4gICAgICAgICAgICByZXR1cm4gIXRoaXMuc3RvcmVzLmNsaWVudCEuaXNSb29tRW5jcnlwdGVkKHJvb21JZCk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuc3RvcmVzLmNsaWVudCEuaGFzTGF6eUxvYWRNZW1iZXJzRW5hYmxlZCgpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIENoZWNrIGlmIGxhenkgbWVtYmVyIHN0b3JhZ2UgaXMgc3VwcG9ydGVkLlxuICAgICAqIEByZXR1cm5zIFRydWUgaWYgdGhlcmUgaXMgc3RvcmFnZSBmb3IgbGF6eSBsb2FkaW5nIG1lbWJlcnNcbiAgICAgKi9cbiAgICBwcml2YXRlIGlzTGF6eU1lbWJlclN0b3JhZ2VFbmFibGVkKCk6IGJvb2xlYW4ge1xuICAgICAgICBpZiAoU2V0dGluZ3NTdG9yZS5nZXRWYWx1ZShcImZlYXR1cmVfc2xpZGluZ19zeW5jXCIpKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMuc3RvcmVzLmNsaWVudCEuaGFzTGF6eUxvYWRNZW1iZXJzRW5hYmxlZCgpO1xuICAgIH1cblxuICAgIHB1YmxpYyBpc1ByZXNlbmNlRW5hYmxlZCgpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKCF0aGlzLnN0b3Jlcy5jbGllbnQpIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGVuYWJsZVByZXNlbmNlQnlIc1VybCA9IFNka0NvbmZpZy5nZXQoXCJlbmFibGVfcHJlc2VuY2VfYnlfaHNfdXJsXCIpO1xuICAgICAgICByZXR1cm4gZW5hYmxlUHJlc2VuY2VCeUhzVXJsPy5bdGhpcy5zdG9yZXMuY2xpZW50IS5iYXNlVXJsXSA/PyB0cnVlO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEZpbHRlciBvdXQgbWVtYmVycyBiYXNlZCBvbiBhbiBvcHRpb25hbCBzZWFyY2ggcXVlcnkuIEdyb3VwcyBieSBtZW1iZXJzaGlwIHN0YXRlLlxuICAgICAqIEBwYXJhbSBtZW1iZXJzIFRoZSBsaXN0IG9mIG1lbWJlcnMgdG8gZmlsdGVyLlxuICAgICAqIEBwYXJhbSBxdWVyeSBUaGUgdGV4dHVhbCBxdWVyeSB0byBmaWx0ZXIgYmFzZWQgb24uXG4gICAgICogQHJldHVybnMgQW4gb2JqZWN0IHdpdGggYSBsaXN0IG9mIGpvaW5lZCBhbmQgaW52aXRlZCB1c2VycyByZXNwZWN0aXZlbHkuXG4gICAgICovXG4gICAgcHJpdmF0ZSBmaWx0ZXJNZW1iZXJzKG1lbWJlcnM6IEFycmF5PFJvb21NZW1iZXI+LCBxdWVyeT86IHN0cmluZyk6IFJlY29yZDxcImpvaW5lZFwiIHwgXCJpbnZpdGVkXCIsIFJvb21NZW1iZXJbXT4ge1xuICAgICAgICBjb25zdCByZXN1bHQ6IFJlY29yZDxcImpvaW5lZFwiIHwgXCJpbnZpdGVkXCIsIFJvb21NZW1iZXJbXT4gPSB7XG4gICAgICAgICAgICBqb2luZWQ6IFtdLFxuICAgICAgICAgICAgaW52aXRlZDogW10sXG4gICAgICAgIH07XG4gICAgICAgIG1lbWJlcnMuZm9yRWFjaCgobSkgPT4ge1xuICAgICAgICAgICAgaWYgKG0ubWVtYmVyc2hpcCAhPT0gS25vd25NZW1iZXJzaGlwLkpvaW4gJiYgbS5tZW1iZXJzaGlwICE9PSBLbm93bk1lbWJlcnNoaXAuSW52aXRlKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuOyAvLyBiYWlsIGVhcmx5IGZvciBsZWZ0L2Jhbm5lZCB1c2Vyc1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHF1ZXJ5KSB7XG4gICAgICAgICAgICAgICAgcXVlcnkgPSBxdWVyeS50b0xvd2VyQ2FzZSgpO1xuICAgICAgICAgICAgICAgIGNvbnN0IG1hdGNoZXNOYW1lID0gbS5uYW1lLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMocXVlcnkpO1xuICAgICAgICAgICAgICAgIGNvbnN0IG1hdGNoZXNJZCA9IG0udXNlcklkLnRvTG93ZXJDYXNlKCkuaW5jbHVkZXMocXVlcnkpO1xuICAgICAgICAgICAgICAgIGlmICghbWF0Y2hlc05hbWUgJiYgIW1hdGNoZXNJZCkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICAgICAgc3dpdGNoIChtLm1lbWJlcnNoaXApIHtcbiAgICAgICAgICAgICAgICBjYXNlIEtub3duTWVtYmVyc2hpcC5Kb2luOlxuICAgICAgICAgICAgICAgICAgICByZXN1bHQuam9pbmVkLnB1c2gobSk7XG4gICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgIGNhc2UgS25vd25NZW1iZXJzaGlwLkludml0ZTpcbiAgICAgICAgICAgICAgICAgICAgcmVzdWx0Lmludml0ZWQucHVzaChtKTtcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNvcnQgYWxnb3JpdGhtIGZvciByb29tIG1lbWJlcnMuXG4gICAgICogQHBhcmFtIG1lbWJlckFcbiAgICAgKiBAcGFyYW0gbWVtYmVyQlxuICAgICAqIEByZXR1cm5zIE5lZ2F0aXZlIGlmIEEgY29tZXMgYmVmb3JlIEIsIDAgaWYgQSBhbmQgQiBhcmUgZXF1aXZhbGVudCwgUG9zaXRpdmUgaXMgQSBjb21lcyBhZnRlciBCLlxuICAgICAqL1xuICAgIHByaXZhdGUgc29ydE1lbWJlcnMobWVtYmVyQTogUm9vbU1lbWJlciwgbWVtYmVyQjogUm9vbU1lbWJlcik6IG51bWJlciB7XG4gICAgICAgIC8vIG9yZGVyIGJ5IHByZXNlbmNlLCB3aXRoIFwiYWN0aXZlIG5vd1wiIGZpcnN0LlxuICAgICAgICAvLyAuLi5hbmQgdGhlbiBieSBwb3dlciBsZXZlbFxuICAgICAgICAvLyAuLi5hbmQgdGhlbiBieSBsYXN0IGFjdGl2ZVxuICAgICAgICAvLyAuLi5hbmQgdGhlbiBhbHBoYWJldGljYWxseS5cbiAgICAgICAgLy8gV2UgY291bGQgdGllYnJlYWsgaW5zdGVhZCBieSBcImxhc3QgcmVjZW50bHkgc3Bva2VuIGluIHRoaXMgcm9vbVwiIGlmIHdlIHdhbnRlZCB0by5cblxuICAgICAgICBjb25zdCB1c2VyQSA9IG1lbWJlckEudXNlcjtcbiAgICAgICAgY29uc3QgdXNlckIgPSBtZW1iZXJCLnVzZXI7XG5cbiAgICAgICAgaWYgKCF1c2VyQSAmJiAhdXNlckIpIHJldHVybiAwO1xuICAgICAgICBpZiAodXNlckEgJiYgIXVzZXJCKSByZXR1cm4gLTE7XG4gICAgICAgIGlmICghdXNlckEgJiYgdXNlckIpIHJldHVybiAxO1xuXG4gICAgICAgIGNvbnN0IHNob3dQcmVzZW5jZSA9IHRoaXMuaXNQcmVzZW5jZUVuYWJsZWQoKTtcblxuICAgICAgICAvLyBGaXJzdCBieSBwcmVzZW5jZVxuICAgICAgICBpZiAoc2hvd1ByZXNlbmNlKSB7XG4gICAgICAgICAgICBjb25zdCBjb252ZXJ0UHJlc2VuY2UgPSAocDogc3RyaW5nKTogc3RyaW5nID0+IChwID09PSBcInVuYXZhaWxhYmxlXCIgPyBcIm9ubGluZVwiIDogcCk7XG4gICAgICAgICAgICBjb25zdCBwcmVzZW5jZUluZGV4ID0gKHA6IHN0cmluZyk6IG51bWJlciA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3Qgb3JkZXIgPSBbXCJhY3RpdmVcIiwgXCJvbmxpbmVcIiwgXCJvZmZsaW5lXCJdO1xuICAgICAgICAgICAgICAgIGNvbnN0IGlkeCA9IG9yZGVyLmluZGV4T2YoY29udmVydFByZXNlbmNlKHApKTtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWR4ID09PSAtMSA/IG9yZGVyLmxlbmd0aCA6IGlkeDsgLy8gdW5rbm93biBzdGF0ZXMgYXQgdGhlIGVuZFxuICAgICAgICAgICAgfTtcblxuICAgICAgICAgICAgY29uc3QgaWR4QSA9IHByZXNlbmNlSW5kZXgodXNlckEhLmN1cnJlbnRseUFjdGl2ZSA/IFwiYWN0aXZlXCIgOiB1c2VyQSEucHJlc2VuY2UpO1xuICAgICAgICAgICAgY29uc3QgaWR4QiA9IHByZXNlbmNlSW5kZXgodXNlckIhLmN1cnJlbnRseUFjdGl2ZSA/IFwiYWN0aXZlXCIgOiB1c2VyQiEucHJlc2VuY2UpO1xuICAgICAgICAgICAgaWYgKGlkeEEgIT09IGlkeEIpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gaWR4QSAtIGlkeEI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICAvLyBTZWNvbmQgYnkgcG93ZXIgbGV2ZWxcbiAgICAgICAgaWYgKG1lbWJlckEucG93ZXJMZXZlbCAhPT0gbWVtYmVyQi5wb3dlckxldmVsKSB7XG4gICAgICAgICAgICByZXR1cm4gbWVtYmVyQi5wb3dlckxldmVsIC0gbWVtYmVyQS5wb3dlckxldmVsO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVGhpcmQgYnkgbGFzdCBhY3RpdmVcbiAgICAgICAgaWYgKHNob3dQcmVzZW5jZSAmJiB1c2VyQSEuZ2V0TGFzdEFjdGl2ZVRzKCkgIT09IHVzZXJCIS5nZXRMYXN0QWN0aXZlVHMoKSkge1xuICAgICAgICAgICAgcmV0dXJuIHVzZXJCIS5nZXRMYXN0QWN0aXZlVHMoKSAtIHVzZXJBIS5nZXRMYXN0QWN0aXZlVHMoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEZvdXJ0aCBieSBuYW1lIChhbHBoYWJldGljYWwpXG4gICAgICAgIHJldHVybiB0aGlzLmNvbGxhdG9yIS5jb21wYXJlKHRoaXMuY2Fub25pY2FsaXNlZE5hbWUobWVtYmVyQS5uYW1lKSwgdGhpcy5jYW5vbmljYWxpc2VkTmFtZShtZW1iZXJCLm5hbWUpKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDYWxjdWxhdGUgdGhlIGNhbm9uaWNhbGlzZWQgbmFtZSBmb3IgdGhlIGlucHV0IG5hbWUuXG4gICAgICogQHBhcmFtIG5hbWUgVGhlIG1lbWJlciBkaXNwbGF5IG5hbWVcbiAgICAgKiBAcmV0dXJucyBUaGUgbmFtZSB0byBzb3J0IG9uXG4gICAgICovXG4gICAgcHJpdmF0ZSBjYW5vbmljYWxpc2VkTmFtZShuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgICAgICBsZXQgcmVzdWx0ID0gdGhpcy5zb3J0TmFtZXMuZ2V0KG5hbWUpO1xuICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICByZXR1cm4gcmVzdWx0O1xuICAgICAgICB9XG4gICAgICAgIHJlc3VsdCA9IChuYW1lWzBdID09PSBcIkBcIiA/IG5hbWUuc2xpY2UoMSkgOiBuYW1lKS5yZXBsYWNlKFNPUlRfUkVHRVgsIFwiXCIpO1xuICAgICAgICB0aGlzLnNvcnROYW1lcy5zZXQobmFtZSwgcmVzdWx0KTtcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBU0EsSUFBQUEsTUFBQSxHQUFBQyxPQUFBO0FBRUEsSUFBQUMsY0FBQSxHQUFBQyxzQkFBQSxDQUFBRixPQUFBO0FBRUEsSUFBQUcsVUFBQSxHQUFBRCxzQkFBQSxDQUFBRixPQUFBO0FBYkE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBU0E7QUFDQTtBQUNBLE1BQU1JLFVBQVUsR0FBRywwQ0FBMEM7O0FBRTdEO0FBQ0E7QUFDQTtBQUNPLE1BQU1DLGVBQWUsQ0FBQztFQVFsQkMsV0FBV0EsQ0FBa0JDLE1BQXVCLEVBQUU7SUFQN0Q7SUFBQSxJQUFBQyxnQkFBQSxDQUFBQyxPQUFBLHFCQUM2QixJQUFJQyxHQUFHLENBQWlCLENBQUM7SUFDdEQ7SUFBQSxJQUFBRixnQkFBQSxDQUFBQyxPQUFBLHVCQUMrQixJQUFJRSxHQUFHLENBQVMsQ0FBQztJQUFBLElBQUFILGdCQUFBLENBQUFDLE9BQUE7SUFBQSxLQUlaRixNQUF1QixHQUF2QkEsTUFBdUI7RUFBRzs7RUFFOUQ7QUFDSjtBQUNBO0FBQ0E7QUFDQTtB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QUFHWCxLQUFLLENBQUVZLFFBQVEsQ0FBQztNQUMvRSxNQUFNQyxJQUFJLEdBQUdSLGFBQWEsQ0FBQ0osS0FBSyxDQUFFVSxlQUFlLEdBQUcsUUFBUSxHQUFHVixLQUFLLENBQUVXLFFBQVEsQ0FBQztNQUMvRSxJQUFJRixJQUFJLEtBQUtHLElBQUksRUFBRTtRQUNmLE9BQU9ILElBQUksR0FBR0csSUFBSTtNQUN0QjtJQUNKOztJQUVBO0lBQ0EsSUFBSWYsT0FBTyxDQUFDZ0IsVUFBVSxLQUFLZixPQUFPLENBQUNlLFVBQVUsRUFBRTtNQUMzQyxPQUFPZixPQUFPLENBQUNlLFVBQVUsR0FBR2hCLE9BQU8sQ0FBQ2dCLFVBQVU7SUFDbEQ7O0lBRUE7SUFDQSxJQUFJWixZQUFZLElBQUlGLEtBQUssQ0FBRWUsZUFBZSxDQUFDLENBQUMsS0FBS2QsS0FBSyxDQUFFYyxlQUFlLENBQUMsQ0FBQyxFQUFFO01BQ3ZFLE9BQU9kLEtBQUssQ0FBRWMsZUFBZSxDQUFDLENBQUMsR0FBR2YsS0FBSyxDQUFFZSxlQUFlLENBQUMsQ0FBQztJQUM5RDs7SUFFQTtJQUNBLE9BQU8sSUFBSSxDQUFDOUUsUUFBUSxDQUFFK0UsT0FBTyxDQUFDLElBQUksQ0FBQ0MsaUJBQWlCLENBQUNuQixPQUFPLENBQUNKLElBQUksQ0FBQyxFQUFFLElBQUksQ0FBQ3VCLGlCQUFpQixDQUFDbEIsT0FBTyxDQUFDTCxJQUFJLENBQUMsQ0FBQztFQUM3Rzs7RUFFQTtBQUNKO0FBQ0E7QUFDQTtBQUNBO0VBQ1l1QixpQkFBaUJBLENBQUN2QixJQUFZLEVBQVU7SUFDNUMsSUFBSVAsTUFBTSxHQUFHLElBQUksQ0FBQytCLFNBQVMsQ0FBQ2xDLEdBQUcsQ0FBQ1UsSUFBSSxDQUFDO0lBQ3JDLElBQUlQLE1BQU0sRUFBRTtNQUNSLE9BQU9BLE1BQU07SUFDakI7SUFDQUEsTUFBTSxHQUFHLENBQUNPLElBQUksQ0FBQyxDQUFDLENBQUMsS0FBSyxHQUFHLEdBQUdBLElBQUksQ0FBQ3lCLEtBQUssQ0FBQyxDQUFDLENBQUMsR0FBR3pCLElBQUksRUFBRTBCLE9BQU8sQ0FBQ3BHLFVBQVUsRUFBRSxFQUFFLENBQUM7SUFDekUsSUFBSSxDQUFDa0csU0FBUyxDQUFDRyxHQUFHLENBQUMzQixJQUFJLEVBQUVQLE1BQU0sQ0FBQztJQUNoQyxPQUFPQSxNQUFNO0VBQ2pCO0FBQ0o7QUFBQ21DLE9BQUEsQ0FBQXJHLGVBQUEsR0FBQUEsZUFBQSIsImlnbm9yZUxpc3QiOltdfQ==