matrix-react-sdk
Version:
SDK for matrix.org using React
553 lines (540 loc) • 74.3 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.Type = exports.PlainPart = exports.PillPart = exports.PartCreator = exports.EmojiPart = exports.CommandPartCreator = void 0;
exports.getAutoCompleteCreator = getAutoCompleteCreator;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _autocomplete = _interopRequireDefault(require("./autocomplete"));
var _HtmlUtils = require("../HtmlUtils");
var Avatar = _interopRequireWildcard(require("../Avatar"));
var _dispatcher = _interopRequireDefault(require("../dispatcher/dispatcher"));
var _actions = require("../dispatcher/actions");
var _SettingsStore = _interopRequireDefault(require("../settings/SettingsStore"));
var _strings = require("../utils/strings");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/*
Copyright 2019-2024 New Vector Ltd.
Copyright 2019 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.
*/
const REGIONAL_EMOJI_SEPARATOR = String.fromCodePoint(0x200b);
let Type = exports.Type = /*#__PURE__*/function (Type) {
Type["Plain"] = "plain";
Type["Newline"] = "newline";
Type["Emoji"] = "emoji";
Type["Command"] = "command";
Type["UserPill"] = "user-pill";
Type["RoomPill"] = "room-pill";
Type["AtRoomPill"] = "at-room-pill";
Type["PillCandidate"] = "pill-candidate";
return Type;
}({});
class BasePart {
constructor(text = "") {
(0, _defineProperty2.default)(this, "_text", void 0);
this._text = text;
}
// chr can also be a grapheme cluster
acceptsInsertion(chr, offset, inputType) {
return true;
}
acceptsRemoval(position, chr) {
return true;
}
merge(part) {
return false;
}
split(offset) {
const splitText = this.text.slice(offset);
this._text = this.text.slice(0, offset);
return new PlainPart(splitText);
}
// removes len chars, or returns the plain text this part should be replaced with
// if the part would become invalid if it removed everything.
remove(offset, len) {
// validate
const strWithRemoval = this.text.slice(0, offset) + this.text.slice(offset + len);
for (let i = offset; i < len + offset; ++i) {
const chr = this.text.charAt(i);
if (!this.acceptsRemoval(i, chr)) {
return strWithRemoval;
}
}
this._text = strWithRemoval;
}
// append str, returns the remaining string if a character was rejected.
appendUntilRejected(str, inputType) {
const offset = this.text.length;
// Take a copy as we will be taking chunks off the start of the string as we process them
// To only need to grapheme split the bits of the string we're working on.
let buffer = str;
while (buffer) {
const char = (0, _strings.getFirstGrapheme)(buffer);
if (!this.acceptsInsertion(char, offset + str.length - buffer.length, inputType)) {
break;
}
buffer = buffer.slice(char.length);
}
this._text += str.slice(0, str.length - buffer.length);
return buffer || undefined;
}
// inserts str at offset if all the characters in str were accepted, otherwise don't do anything
// return whether the str was accepted or not.
validateAndInsert(offset, str, inputType) {
for (let i = 0; i < str.length; ++i) {
const chr = str.charAt(i);
if (!this.acceptsInsertion(chr, offset + i, inputType)) {
return false;
}
}
const beforeInsert = this._text.slice(0, offset);
const afterInsert = this._text.slice(offset);
this._text = beforeInsert + str + afterInsert;
return true;
}
createAutoComplete(updateCallback) {}
trim(len) {
const remaining = this._text.slice(len);
this._text = this._text.slice(0, len);
return remaining;
}
get text() {
return this._text;
}
get canEdit() {
return true;
}
get acceptsCaret() {
return this.canEdit;
}
toString() {
return `${this.type}(${this.text})`;
}
serialize() {
return {
type: this.type,
text: this.text
};
}
}
class PlainBasePart extends BasePart {
acceptsInsertion(chr, offset, inputType) {
if (chr === "\n" || _HtmlUtils.EMOJI_REGEX.test(chr)) {
return false;
}
// when not pasting or dropping text, reject characters that should start a pill candidate
if (inputType !== "insertFromPaste" && inputType !== "insertFromDrop") {
if (chr !== "@" && chr !== "#" && chr !== ":" && chr !== "+") {
return true;
}
// split if we are at the beginning of the part text
if (offset === 0) {
return false;
}
// or split if the previous character is a space or regional emoji separator
// or if it is a + and this is a :
return this._text[offset - 1] !== " " && this._text[offset - 1] !== REGIONAL_EMOJI_SEPARATOR && (this._text[offset - 1] !== "+" || chr !== ":");
}
return true;
}
toDOMNode() {
return document.createTextNode(this.text);
}
merge(part) {
if (part.type === this.type) {
this._text = this.text + part.text;
return true;
}
return false;
}
updateDOMNode(node) {
if (node.textContent !== this.text) {
node.textContent = this.text;
}
}
canUpdateDOMNode(node) {
return node.nodeType === Node.TEXT_NODE;
}
}
// exported for unit tests, should otherwise only be used through PartCreator
class PlainPart extends PlainBasePart {
get type() {
return Type.Plain;
}
}
exports.PlainPart = PlainPart;
class PillPart extends BasePart {
constructor(resourceId, label) {
super(label);
(0, _defineProperty2.default)(this, "onClick", void 0);
this.resourceId = resourceId;
}
acceptsInsertion(chr) {
return chr !== " ";
}
acceptsRemoval(position, chr) {
return position !== 0; //if you remove initial # or @, pill should become plain
}
toDOMNode() {
const container = document.createElement("span");
container.setAttribute("spellcheck", "false");
container.setAttribute("contentEditable", "false");
if (this.onClick) container.onclick = this.onClick;
container.className = this.className;
container.appendChild(document.createTextNode(this.text));
this.setAvatar(container);
return container;
}
updateDOMNode(node) {
const textNode = node.childNodes[0];
if (textNode.textContent !== this.text) {
textNode.textContent = this.text;
}
if (node.className !== this.className) {
node.className = this.className;
}
if (this.onClick && node.onclick !== this.onClick) {
node.onclick = this.onClick;
}
this.setAvatar(node);
}
canUpdateDOMNode(node) {
return node.nodeType === Node.ELEMENT_NODE && node.nodeName === "SPAN" && node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE;
}
// helper method for subclasses
setAvatarVars(node, avatarUrl, initialLetter) {
const avatarBackground = `url('${avatarUrl}')`;
const avatarLetter = `'${initialLetter}'`;
// check if the value is changing,
// otherwise the avatars flicker on every keystroke while updating.
if (node.style.getPropertyValue("--avatar-background") !== avatarBackground) {
node.style.setProperty("--avatar-background", avatarBackground);
}
if (node.style.getPropertyValue("--avatar-letter") !== avatarLetter) {
node.style.setProperty("--avatar-letter", avatarLetter);
}
}
serialize() {
return {
type: this.type,
text: this.text,
resourceId: this.resourceId
};
}
get canEdit() {
return false;
}
}
exports.PillPart = PillPart;
class NewlinePart extends BasePart {
acceptsInsertion(chr, offset) {
return offset === 0 && chr === "\n";
}
acceptsRemoval(position, chr) {
return true;
}
toDOMNode() {
return document.createElement("br");
}
merge() {
return false;
}
updateDOMNode() {}
canUpdateDOMNode(node) {
return node.tagName === "BR";
}
get type() {
return Type.Newline;
}
// this makes the cursor skip this part when it is inserted
// rather than trying to append to it, which is what we want.
// As a newline can also be only one character, it makes sense
// as it can only be one character long. This caused #9741.
get canEdit() {
return false;
}
}
class EmojiPart extends BasePart {
acceptsInsertion(chr, offset) {
return _HtmlUtils.EMOJI_REGEX.test(chr);
}
acceptsRemoval(position, chr) {
return false;
}
toDOMNode() {
const span = document.createElement("span");
span.className = "mx_Emoji";
span.setAttribute("title", (0, _HtmlUtils.unicodeToShortcode)(this.text));
span.appendChild(document.createTextNode(this.text));
return span;
}
updateDOMNode(node) {
const textNode = node.childNodes[0];
if (textNode.textContent !== this.text) {
node.setAttribute("title", (0, _HtmlUtils.unicodeToShortcode)(this.text));
textNode.textContent = this.text;
}
}
canUpdateDOMNode(node) {
return node.className === "mx_Emoji";
}
get type() {
return Type.Emoji;
}
get canEdit() {
return false;
}
get acceptsCaret() {
return true;
}
}
exports.EmojiPart = EmojiPart;
class RoomPillPart extends PillPart {
constructor(resourceId, label, room) {
super(resourceId, label);
this.room = room;
}
setAvatar(node) {
let initialLetter = "";
let avatarUrl = Avatar.avatarUrlForRoom(this.room ?? null, 16, 16, "crop");
if (!avatarUrl) {
initialLetter = Avatar.getInitialLetter(this.room?.name || this.resourceId) ?? "";
avatarUrl = Avatar.defaultAvatarUrlForString(this.room?.roomId ?? this.resourceId);
}
this.setAvatarVars(node, avatarUrl, initialLetter);
}
get type() {
return Type.RoomPill;
}
get className() {
return "mx_Pill " + (this.room?.isSpaceRoom() ? "mx_SpacePill" : "mx_RoomPill");
}
}
class AtRoomPillPart extends RoomPillPart {
constructor(text, room) {
super(text, text, room);
}
get type() {
return Type.AtRoomPill;
}
serialize() {
return {
type: this.type,
text: this.text
};
}
}
class UserPillPart extends PillPart {
constructor(userId, displayName, member) {
super(userId, displayName);
(0, _defineProperty2.default)(this, "onClick", () => {
_dispatcher.default.dispatch({
action: _actions.Action.ViewUser,
member: this.member
});
});
this.member = member;
}
get type() {
return Type.UserPill;
}
get className() {
return "mx_UserPill mx_Pill";
}
setAvatar(node) {
if (!this.member) {
return;
}
const name = this.member.name || this.member.userId;
const defaultAvatarUrl = Avatar.defaultAvatarUrlForString(this.member.userId);
const avatarUrl = Avatar.avatarUrlForMember(this.member, 16, 16, "crop");
let initialLetter = "";
if (avatarUrl === defaultAvatarUrl) {
initialLetter = Avatar.getInitialLetter(name) ?? "";
}
this.setAvatarVars(node, avatarUrl, initialLetter);
}
}
class PillCandidatePart extends PlainBasePart {
constructor(text, autoCompleteCreator) {
super(text);
this.autoCompleteCreator = autoCompleteCreator;
}
createAutoComplete(updateCallback) {
return this.autoCompleteCreator.create?.(updateCallback);
}
acceptsInsertion(chr, offset, inputType) {
if (offset === 0) {
return true;
} else {
return super.acceptsInsertion(chr, offset, inputType);
}
}
merge() {
return false;
}
acceptsRemoval(position, chr) {
return true;
}
get type() {
return Type.PillCandidate;
}
}
function getAutoCompleteCreator(getAutocompleterComponent, updateQuery) {
return partCreator => {
return updateCallback => {
return new _autocomplete.default(updateCallback, getAutocompleterComponent, updateQuery, partCreator);
};
};
}
class PartCreator {
constructor(room, client, autoCompleteCreator = null) {
(0, _defineProperty2.default)(this, "autoCompleteCreator", void 0);
this.room = room;
this.client = client;
// pre-create the creator as an object even without callback so it can already be passed
// to PillCandidatePart (e.g. while deserializing) and set later on
this.autoCompleteCreator = {
create: autoCompleteCreator?.(this)
};
}
setAutoCompleteCreator(autoCompleteCreator) {
this.autoCompleteCreator.create = autoCompleteCreator(this);
}
createPartForInput(input, partIndex, inputType) {
switch (input[0]) {
case "#":
case "@":
case ":":
case "+":
return this.pillCandidate("");
case "\n":
return new NewlinePart();
default:
if (_HtmlUtils.EMOJI_REGEX.test((0, _strings.getFirstGrapheme)(input))) {
return new EmojiPart();
}
return new PlainPart();
}
}
createDefaultPart(text) {
return this.plain(text);
}
deserializePart(part) {
switch (part.type) {
case Type.Plain:
return this.plain(part.text);
case Type.Newline:
return this.newline();
case Type.Emoji:
return this.emoji(part.text);
case Type.AtRoomPill:
return this.atRoomPill(part.text);
case Type.PillCandidate:
return this.pillCandidate(part.text);
case Type.RoomPill:
return part.resourceId ? this.roomPill(part.resourceId) : undefined;
case Type.UserPill:
return part.resourceId ? this.userPill(part.text, part.resourceId) : undefined;
}
}
plain(text) {
return new PlainPart(text);
}
newline() {
return new NewlinePart("\n");
}
emoji(text) {
return new EmojiPart(text);
}
pillCandidate(text) {
return new PillCandidatePart(text, this.autoCompleteCreator);
}
roomPill(alias, roomId) {
let room;
if (roomId || alias[0] !== "#") {
room = this.client.getRoom(roomId || alias) ?? undefined;
} else {
room = this.client.getRooms().find(r => {
return r.getCanonicalAlias() === alias || r.getAltAliases().includes(alias);
});
}
return new RoomPillPart(alias, room ? room.name : alias, room);
}
atRoomPill(text) {
return new AtRoomPillPart(text, this.room);
}
userPill(displayName, userId) {
const member = this.room.getMember(userId);
return new UserPillPart(userId, displayName, member || undefined);
}
static isRegionalIndicator(c) {
const codePoint = c.codePointAt(0) ?? 0;
return codePoint != 0 && c.length == 2 && 0x1f1e6 <= codePoint && codePoint <= 0x1f1ff;
}
plainWithEmoji(text) {
const parts = [];
let plainText = "";
for (const data of _strings.graphemeSegmenter.segment(text)) {
if (_HtmlUtils.EMOJI_REGEX.test(data.segment)) {
if (plainText) {
parts.push(this.plain(plainText));
plainText = "";
}
parts.push(this.emoji(data.segment));
if (PartCreator.isRegionalIndicator(text)) {
parts.push(this.plain(REGIONAL_EMOJI_SEPARATOR));
}
} else {
plainText += data.segment;
}
}
if (plainText) {
parts.push(this.plain(plainText));
}
return parts;
}
createMentionParts(insertTrailingCharacter, displayName, userId) {
const pill = this.userPill(displayName, userId);
if (!_SettingsStore.default.getValue("MessageComposerInput.insertTrailingColon")) {
insertTrailingCharacter = false;
}
const postfix = this.plain(insertTrailingCharacter ? ": " : " ");
return [pill, postfix];
}
}
// part creator that support auto complete for /commands,
// used in SendMessageComposer
exports.PartCreator = PartCreator;
class CommandPartCreator extends PartCreator {
createPartForInput(text, partIndex) {
// at beginning and starts with /? create
if (partIndex === 0 && text[0] === "/") {
// text will be inserted by model, so pass empty string
return this.command("");
} else {
return super.createPartForInput(text, partIndex);
}
}
command(text) {
return new CommandPart(text, this.autoCompleteCreator);
}
deserializePart(part) {
if (part.type === Type.Command) {
return this.command(part.text);
} else {
return super.deserializePart(part);
}
}
}
exports.CommandPartCreator = CommandPartCreator;
class CommandPart extends PillCandidatePart {
get type() {
return Type.Command;
}
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYXV0b2NvbXBsZXRlIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsInJlcXVpcmUiLCJfSHRtbFV0aWxzIiwiQXZhdGFyIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfZGlzcGF0Y2hlciIsIl9hY3Rpb25zIiwiX1NldHRpbmdzU3RvcmUiLCJfc3RyaW5ncyIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIlJFR0lPTkFMX0VNT0pJX1NFUEFSQVRPUiIsIlN0cmluZyIsImZyb21Db2RlUG9pbnQiLCJUeXBlIiwiZXhwb3J0cyIsIkJhc2VQYXJ0IiwiY29uc3RydWN0b3IiLCJ0ZXh0IiwiX2RlZmluZVByb3BlcnR5MiIsIl90ZXh0IiwiYWNjZXB0c0luc2VydGlvbiIsImNociIsIm9mZnNldCIsImlucHV0VHlwZSIsImFjY2VwdHNSZW1vdmFsIiwicG9zaXRpb24iLCJtZXJnZSIsInBhcnQiLCJzcGxpdCIsInNwbGl0VGV4dCIsInNsaWNlIiwiUGxhaW5QYXJ0IiwicmVtb3ZlIiwibGVuIiwic3RyV2l0aFJlbW92YWwiLCJjaGFyQXQiLCJhcHBlbmRVbnRpbFJlamVjdGVkIiwic3RyIiwibGVuZ3RoIiwiYnVmZmVyIiwiY2hhciIsImdldEZpcnN0R3JhcGhlbWUiLCJ1bmRlZmluZWQiLCJ2YWxpZGF0ZUFuZEluc2VydCIsImJlZm9yZUluc2VydCIsImFmdGVySW5zZXJ0IiwiY3JlYXRlQXV0b0NvbXBsZXRlIiwidXBkYXRlQ2FsbGJhY2siLCJ0cmltIiwicmVtYWluaW5nIiwiY2FuRWRpdCIsImFjY2VwdHNDYXJldCIsInRvU3RyaW5nIiwidHlwZSIsInNlcmlhbGl6ZSIsIlBsYWluQmFzZVBhcnQiLCJFTU9KSV9SRUdFWCIsInRlc3QiLCJ0b0RPTU5vZGUiLCJkb2N1bWVudCIsImNyZWF0ZVRleHROb2RlIiwidXBkYXRlRE9NTm9kZSIsIm5vZGUiLCJ0ZXh0Q29udGVudCIsImNhblVwZGF0ZURPTU5vZGUiLCJub2RlVHlwZSIsIk5vZGUiLCJURVhUX05PREUiLCJQbGFpbiIsIlBpbGxQYXJ0IiwicmVzb3VyY2VJZCIsImxhYmVsIiwiY29udGFpbmVyIiwiY3JlYXRlRWxlbWVudCIsInNldEF0dHJpYnV0ZSIsIm9uQ2xpY2siLCJvbmNsaWNrIiwiY2xhc3NOYW1lIiwiYXBwZW5kQ2hpbGQiLCJzZXRBdmF0YXIiLCJ0ZXh0Tm9kZSIsImNoaWxkTm9kZXMiLCJFTEVNRU5UX05PREUiLCJub2RlTmFtZSIsInNldEF2YXRhclZhcnMiLCJhdmF0YXJVcmwiLCJpbml0aWFsTGV0dGVyIiwiYXZhdGFyQmFja2dyb3VuZCIsImF2YXRhckxldHRlciIsInN0eWxlIiwiZ2V0UHJvcGVydHlWYWx1ZSIsInNldFByb3BlcnR5IiwiTmV3bGluZVBhcnQiLCJ0YWdOYW1lIiwiTmV3bGluZSIsIkVtb2ppUGFydCIsInNwYW4iLCJ1bmljb2RlVG9TaG9ydGNvZGUiLCJFbW9qaSIsIlJvb21QaWxsUGFydCIsInJvb20iLCJhdmF0YXJVcmxGb3JSb29tIiwiZ2V0SW5pdGlhbExldHRlciIsIm5hbWUiLCJkZWZhdWx0QXZhdGFyVXJsRm9yU3RyaW5nIiwicm9vbUlkIiwiUm9vbVBpbGwiLCJpc1NwYWNlUm9vbSIsIkF0Um9vbVBpbGxQYXJ0IiwiQXRSb29tUGlsbCIsIlVzZXJQaWxsUGFydCIsInVzZXJJZCIsImRpc3BsYXlOYW1lIiwibWVtYmVyIiwiZGVmYXVsdERpc3BhdGNoZXIiLCJkaXNwYXRjaCIsImFjdGlvbiIsIkFjdGlvbiIsIlZpZXdVc2VyIiwiVXNlclBpbGwiLCJkZWZhdWx0QXZhdGFyVXJsIiwiYXZhdGFyVXJsRm9yTWVtYmVyIiwiUGlsbENhbmRpZGF0ZVBhcnQiLCJhdXRvQ29tcGxldGVDcmVhdG9yIiwiY3JlYXRlIiwiUGlsbENhbmRpZGF0ZSIsImdldEF1dG9Db21wbGV0ZUNyZWF0b3IiLCJnZXRBdXRvY29tcGxldGVyQ29tcG9uZW50IiwidXBkYXRlUXVlcnkiLCJwYXJ0Q3JlYXRvciIsIkF1dG9jb21wbGV0ZVdyYXBwZXJNb2RlbCIsIlBhcnRDcmVhdG9yIiwiY2xpZW50Iiwic2V0QXV0b0NvbXBsZXRlQ3JlYXRvciIsImNyZWF0ZVBhcnRGb3JJbnB1dCIsImlucHV0IiwicGFydEluZGV4IiwicGlsbENhbmRpZGF0ZSIsImNyZWF0ZURlZmF1bHRQYXJ0IiwicGxhaW4iLCJkZXNlcmlhbGl6ZVBhcnQiLCJuZXdsaW5lIiwiZW1vamkiLCJhdFJvb21QaWxsIiwicm9vbVBpbGwiLCJ1c2VyUGlsbCIsImFsaWFzIiwiZ2V0Um9vbSIsImdldFJvb21zIiwiZmluZCIsImdldENhbm9uaWNhbEFsaWFzIiwiZ2V0QWx0QWxpYXNlcyIsImluY2x1ZGVzIiwiZ2V0TWVtYmVyIiwiaXNSZWdpb25hbEluZGljYXRvciIsImMiLCJjb2RlUG9pbnQiLCJjb2RlUG9pbnRBdCIsInBsYWluV2l0aEVtb2ppIiwicGFydHMiLCJwbGFpblRleHQiLCJkYXRhIiwiZ3JhcGhlbWVTZWdtZW50ZXIiLCJzZWdtZW50IiwicHVzaCIsImNyZWF0ZU1lbnRpb25QYXJ0cyIsImluc2VydFRyYWlsaW5nQ2hhcmFjdGVyIiwicGlsbCIsIlNldHRpbmdzU3RvcmUiLCJnZXRWYWx1ZSIsInBvc3RmaXgiLCJDb21tYW5kUGFydENyZWF0b3IiLCJjb21tYW5kIiwiQ29tbWFuZFBhcnQiLCJDb21tYW5kIl0sInNvdXJjZXMiOlsiLi4vLi4vc3JjL2VkaXRvci9wYXJ0cy50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMTktMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDE5IFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IE1hdHJpeENsaWVudCwgUm9vbU1lbWJlciwgUm9vbSB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcblxuaW1wb3J0IEF1dG9jb21wbGV0ZVdyYXBwZXJNb2RlbCwgeyBHZXRBdXRvY29tcGxldGVyQ29tcG9uZW50LCBVcGRhdGVDYWxsYmFjaywgVXBkYXRlUXVlcnkgfSBmcm9tIFwiLi9hdXRvY29tcGxldGVcIjtcbmltcG9ydCB7IEVNT0pJX1JFR0VYLCB1bmljb2RlVG9TaG9ydGNvZGUgfSBmcm9tIFwiLi4vSHRtbFV0aWxzXCI7XG5pbXBvcnQgKiBhcyBBdmF0YXIgZnJvbSBcIi4uL0F2YXRhclwiO1xuaW1wb3J0IGRlZmF1bHREaXNwYXRjaGVyIGZyb20gXCIuLi9kaXNwYXRjaGVyL2Rpc3BhdGNoZXJcIjtcbmltcG9ydCB7IEFjdGlvbiB9IGZyb20gXCIuLi9kaXNwYXRjaGVyL2FjdGlvbnNcIjtcbmltcG9ydCBTZXR0aW5nc1N0b3JlIGZyb20gXCIuLi9zZXR0aW5ncy9TZXR0aW5nc1N0b3JlXCI7XG5pbXBvcnQgeyBnZXRGaXJzdEdyYXBoZW1lLCBncmFwaGVtZVNlZ21lbnRlciB9IGZyb20gXCIuLi91dGlscy9zdHJpbmdzXCI7XG5cbmNvbnN0IFJFR0lPTkFMX0VNT0pJX1NFUEFSQVRPUiA9IFN0cmluZy5mcm9tQ29kZVBvaW50KDB4MjAwYik7XG5cbmludGVyZmFjZSBJU2VyaWFsaXplZFBhcnQge1xuICAgIHR5cGU6IFR5cGUuUGxhaW4gfCBUeXBlLk5ld2xpbmUgfCBUeXBlLkVtb2ppIHwgVHlwZS5Db21tYW5kIHwgVHlwZS5QaWxsQ2FuZGlkYXRlO1xuICAgIHRleHQ6IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIElTZXJpYWxpemVkUGlsbFBhcnQge1xuICAgIHR5cGU6IFR5cGUuQXRSb29tUGlsbCB8IFR5cGUuUm9vbVBpbGwgfCBUeXBlLlVzZXJQaWxsO1xuICAgIHRleHQ6IHN0cmluZztcbiAgICByZXNvdXJjZUlkPzogc3RyaW5nO1xufVxuXG5leHBvcnQgdHlwZSBTZXJpYWxpemVkUGFydCA9IElTZXJpYWxpemVkUGFydCB8IElTZXJpYWxpemVkUGlsbFBhcnQ7XG5cbmV4cG9ydCBlbnVtIFR5cGUge1xuICAgIFBsYWluID0gXCJwbGFpblwiLFxuICAgIE5ld2xpbmUgPSBcIm5ld2xpbmVcIixcbiAgICBFbW9qaSA9IFwiZW1vamlcIixcbiAgICBDb21tYW5kID0gXCJjb21tYW5kXCIsXG4gICAgVXNlclBpbGwgPSBcInVzZXItcGlsbFwiLFxuICAgIFJvb21QaWxsID0gXCJyb29tLXBpbGxcIixcbiAgICBBdFJvb21QaWxsID0gXCJhdC1yb29tLXBpbGxcIixcbiAgICBQaWxsQ2FuZGlkYXRlID0gXCJwaWxsLWNhbmRpZGF0ZVwiLFxufVxuXG5pbnRlcmZhY2UgSUJhc2VQYXJ0IHtcbiAgICB0ZXh0OiBzdHJpbmc7XG4gICAgdHlwZTogVHlwZS5QbGFpbiB8IFR5cGUuTmV3bGluZSB8IFR5cGUuRW1vamk7XG4gICAgY2FuRWRpdDogYm9vbGVhbjtcbiAgICBhY2NlcHRzQ2FyZXQ6IGJvb2xlYW47XG5cbiAgICBjcmVhdGVBdXRvQ29tcGxldGUodXBkYXRlQ2FsbGJhY2s6IFVwZGF0ZUNhbGxiYWNrKTogdm9pZDtcblxuICAgIHNlcmlhbGl6ZSgpOiBTZXJpYWxpemVkUGFydDtcbiAgICByZW1vdmUob2Zmc2V0OiBudW1iZXIsIGxlbjogbnVtYmVyKTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIHNwbGl0KG9mZnNldDogbnVtYmVyKTogSUJhc2VQYXJ0O1xuICAgIHZhbGlkYXRlQW5kSW5zZXJ0KG9mZnNldDogbnVtYmVyLCBzdHI6IHN0cmluZywgaW5wdXRUeXBlOiBzdHJpbmcgfCB1bmRlZmluZWQpOiBib29sZWFuO1xuICAgIGFwcGVuZFVudGlsUmVqZWN0ZWQoc3RyOiBzdHJpbmcsIGlucHV0VHlwZTogc3RyaW5nIHwgdW5kZWZpbmVkKTogc3RyaW5nIHwgdW5kZWZpbmVkO1xuICAgIHVwZGF0ZURPTU5vZGUobm9kZTogTm9kZSk6IHZvaWQ7XG4gICAgY2FuVXBkYXRlRE9NTm9kZShub2RlOiBOb2RlKTogYm9vbGVhbjtcbiAgICB0b0RPTU5vZGUoKTogTm9kZTtcblxuICAgIG1lcmdlPyhwYXJ0OiBQYXJ0KTogYm9vbGVhbjtcbn1cblxuaW50ZXJmYWNlIElQaWxsQ2FuZGlkYXRlUGFydCBleHRlbmRzIE9taXQ8SUJhc2VQYXJ0LCBcInR5cGVcIiB8IFwiY3JlYXRlQXV0b0NvbXBsZXRlXCI+IHtcbiAgICB0eXBlOiBUeXBlLlBpbGxDYW5kaWRhdGUgfCBUeXBlLkNvbW1hbmQ7XG4gICAgY3JlYXRlQXV0b0NvbXBsZXRlKHVwZGF0ZUNhbGxiYWNrOiBVcGRhdGVDYWxsYmFjayk6IEF1dG9jb21wbGV0ZVdyYXBwZXJNb2RlbCB8IHVuZGVmaW5lZDtcbn1cblxuaW50ZXJmYWNlIElQaWxsUGFydCBleHRlbmRzIE9taXQ8SUJhc2VQYXJ0LCBcInR5cGVcIiB8IFwicmVzb3VyY2VJZFwiPiB7XG4gICAgdHlwZTogVHlwZS5BdFJvb21QaWxsIHwgVHlwZS5Sb29tUGlsbCB8IFR5cGUuVXNlclBpbGw7XG4gICAgcmVzb3VyY2VJZDogc3RyaW5nO1xufVxuXG5leHBvcnQgdHlwZSBQYXJ0ID0gSUJhc2VQYXJ0IHwgSVBpbGxDYW5kaWRhdGVQYXJ0IHwgSVBpbGxQYXJ0O1xuXG5hYnN0cmFjdCBjbGFzcyBCYXNlUGFydCB7XG4gICAgcHJvdGVjdGVkIF90ZXh0OiBzdHJpbmc7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3IodGV4dCA9IFwiXCIpIHtcbiAgICAgICAgdGhpcy5fdGV4dCA9IHRleHQ7XG4gICAgfVxuXG4gICAgLy8gY2hyIGNhbiBhbHNvIGJlIGEgZ3JhcGhlbWUgY2x1c3RlclxuICAgIHByb3RlY3RlZCBhY2NlcHRzSW5zZXJ0aW9uKGNocjogc3RyaW5nLCBvZmZzZXQ6IG51bWJlciwgaW5wdXRUeXBlOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIGFjY2VwdHNSZW1vdmFsKHBvc2l0aW9uOiBudW1iZXIsIGNocjogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHB1YmxpYyBtZXJnZShwYXJ0OiBQYXJ0KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgc3BsaXQob2Zmc2V0OiBudW1iZXIpOiBJQmFzZVBhcnQge1xuICAgICAgICBjb25zdCBzcGxpdFRleHQgPSB0aGlzLnRleHQuc2xpY2Uob2Zmc2V0KTtcbiAgICAgICAgdGhpcy5fdGV4dCA9IHRoaXMudGV4dC5zbGljZSgwLCBvZmZzZXQpO1xuICAgICAgICByZXR1cm4gbmV3IFBsYWluUGFydChzcGxpdFRleHQpO1xuICAgIH1cblxuICAgIC8vIHJlbW92ZXMgbGVuIGNoYXJzLCBvciByZXR1cm5zIHRoZSBwbGFpbiB0ZXh0IHRoaXMgcGFydCBzaG91bGQgYmUgcmVwbGFjZWQgd2l0aFxuICAgIC8vIGlmIHRoZSBwYXJ0IHdvdWxkIGJlY29tZSBpbnZhbGlkIGlmIGl0IHJlbW92ZWQgZXZlcnl0aGluZy5cbiAgICBwdWJsaWMgcmVtb3ZlKG9mZnNldDogbnVtYmVyLCBsZW46IG51bWJlcik6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgICAgIC8vIHZhbGlkYXRlXG4gICAgICAgIGNvbnN0IHN0cldpdGhSZW1vdmFsID0gdGhpcy50ZXh0LnNsaWNlKDAsIG9mZnNldCkgKyB0aGlzLnRleHQuc2xpY2Uob2Zmc2V0ICsgbGVuKTtcbiAgICAgICAgZm9yIChsZXQgaSA9IG9mZnNldDsgaSA8IGxlbiArIG9mZnNldDsgKytpKSB7XG4gICAgICAgICAgICBjb25zdCBjaHIgPSB0aGlzLnRleHQuY2hhckF0KGkpO1xuICAgICAgICAgICAgaWYgKCF0aGlzLmFjY2VwdHNSZW1vdmFsKGksIGNocikpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gc3RyV2l0aFJlbW92YWw7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5fdGV4dCA9IHN0cldpdGhSZW1vdmFsO1xuICAgIH1cblxuICAgIC8vIGFwcGVuZCBzdHIsIHJldHVybnMgdGhlIHJlbWFpbmluZyBzdHJpbmcgaWYgYSBjaGFyYWN0ZXIgd2FzIHJlamVjdGVkLlxuICAgIHB1YmxpYyBhcHBlbmRVbnRpbFJlamVjdGVkKHN0cjogc3RyaW5nLCBpbnB1dFR5cGU6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG4gICAgICAgIGNvbnN0IG9mZnNldCA9IHRoaXMudGV4dC5sZW5ndGg7XG4gICAgICAgIC8vIFRha2UgYSBjb3B5IGFzIHdlIHdpbGwgYmUgdGFraW5nIGNodW5rcyBvZmYgdGhlIHN0YXJ0IG9mIHRoZSBzdHJpbmcgYXMgd2UgcHJvY2VzcyB0aGVtXG4gICAgICAgIC8vIFRvIG9ubHkgbmVlZCB0byBncmFwaGVtZSBzcGxpdCB0aGUgYml0cyBvZiB0aGUgc3RyaW5nIHdlJ3JlIHdvcmtpbmcgb24uXG4gICAgICAgIGxldCBidWZmZXIgPSBzdHI7XG4gICAgICAgIHdoaWxlIChidWZmZXIpIHtcbiAgICAgICAgICAgIGNvbnN0IGNoYXIgPSBnZXRGaXJzdEdyYXBoZW1lKGJ1ZmZlcik7XG4gICAgICAgICAgICBpZiAoIXRoaXMuYWNjZXB0c0luc2VydGlvbihjaGFyLCBvZmZzZXQgKyBzdHIubGVuZ3RoIC0gYnVmZmVyLmxlbmd0aCwgaW5wdXRUeXBlKSkge1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgYnVmZmVyID0gYnVmZmVyLnNsaWNlKGNoYXIubGVuZ3RoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3RleHQgKz0gc3RyLnNsaWNlKDAsIHN0ci5sZW5ndGggLSBidWZmZXIubGVuZ3RoKTtcbiAgICAgICAgcmV0dXJuIGJ1ZmZlciB8fCB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgLy8gaW5zZXJ0cyBzdHIgYXQgb2Zmc2V0IGlmIGFsbCB0aGUgY2hhcmFjdGVycyBpbiBzdHIgd2VyZSBhY2NlcHRlZCwgb3RoZXJ3aXNlIGRvbid0IGRvIGFueXRoaW5nXG4gICAgLy8gcmV0dXJuIHdoZXRoZXIgdGhlIHN0ciB3YXMgYWNjZXB0ZWQgb3Igbm90LlxuICAgIHB1YmxpYyB2YWxpZGF0ZUFuZEluc2VydChvZmZzZXQ6IG51bWJlciwgc3RyOiBzdHJpbmcsIGlucHV0VHlwZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgc3RyLmxlbmd0aDsgKytpKSB7XG4gICAgICAgICAgICBjb25zdCBjaHIgPSBzdHIuY2hhckF0KGkpO1xuICAgICAgICAgICAgaWYgKCF0aGlzLmFjY2VwdHNJbnNlcnRpb24oY2hyLCBvZmZzZXQgKyBpLCBpbnB1dFR5cGUpKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGNvbnN0IGJlZm9yZUluc2VydCA9IHRoaXMuX3RleHQuc2xpY2UoMCwgb2Zmc2V0KTtcbiAgICAgICAgY29uc3QgYWZ0ZXJJbnNlcnQgPSB0aGlzLl90ZXh0LnNsaWNlKG9mZnNldCk7XG4gICAgICAgIHRoaXMuX3RleHQgPSBiZWZvcmVJbnNlcnQgKyBzdHIgKyBhZnRlckluc2VydDtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcHVibGljIGNyZWF0ZUF1dG9Db21wbGV0ZSh1cGRhdGVDYWxsYmFjazogVXBkYXRlQ2FsbGJhY2spOiB2b2lkIHt9XG5cbiAgICBwcm90ZWN0ZWQgdHJpbShsZW46IG51bWJlcik6IHN0cmluZyB7XG4gICAgICAgIGNvbnN0IHJlbWFpbmluZyA9IHRoaXMuX3RleHQuc2xpY2UobGVuKTtcbiAgICAgICAgdGhpcy5fdGV4dCA9IHRoaXMuX3RleHQuc2xpY2UoMCwgbGVuKTtcbiAgICAgICAgcmV0dXJuIHJlbWFpbmluZztcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0IHRleHQoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3RleHQ7XG4gICAgfVxuXG4gICAgcHVibGljIGFic3RyYWN0IGdldCB0eXBlKCk6IFR5cGU7XG5cbiAgICBwdWJsaWMgZ2V0IGNhbkVkaXQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiB0cnVlO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgYWNjZXB0c0NhcmV0KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdGhpcy5jYW5FZGl0O1xuICAgIH1cblxuICAgIHB1YmxpYyB0b1N0cmluZygpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gYCR7dGhpcy50eXBlfSgke3RoaXMudGV4dH0pYDtcbiAgICB9XG5cbiAgICBwdWJsaWMgc2VyaWFsaXplKCk6IFNlcmlhbGl6ZWRQYXJ0IHtcbiAgICAgICAgcmV0dXJuIHtcbiAgICAgICAgICAgIHR5cGU6IHRoaXMudHlwZSBhcyBJU2VyaWFsaXplZFBhcnRbXCJ0eXBlXCJdLFxuICAgICAgICAgICAgdGV4dDogdGhpcy50ZXh0LFxuICAgICAgICB9O1xuICAgIH1cblxuICAgIHB1YmxpYyBhYnN0cmFjdCB1cGRhdGVET01Ob2RlKG5vZGU6IE5vZGUpOiB2b2lkO1xuICAgIHB1YmxpYyBhYnN0cmFjdCBjYW5VcGRhdGVET01Ob2RlKG5vZGU6IE5vZGUpOiBib29sZWFuO1xuICAgIHB1YmxpYyBhYnN0cmFjdCB0b0RPTU5vZGUoKTogTm9kZTtcbn1cblxuYWJzdHJhY3QgY2xhc3MgUGxhaW5CYXNlUGFydCBleHRlbmRzIEJhc2VQYXJ0IHtcbiAgICBwcm90ZWN0ZWQgYWNjZXB0c0luc2VydGlvbihjaHI6IHN0cmluZywgb2Zmc2V0OiBudW1iZXIsIGlucHV0VHlwZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIGlmIChjaHIgPT09IFwiXFxuXCIgfHwgRU1PSklfUkVHRVgudGVzdChjaHIpKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgLy8gd2hlbiBub3QgcGFzdGluZyBvciBkcm9wcGluZyB0ZXh0LCByZWplY3QgY2hhcmFjdGVycyB0aGF0IHNob3VsZCBzdGFydCBhIHBpbGwgY2FuZGlkYXRlXG4gICAgICAgIGlmIChpbnB1dFR5cGUgIT09IFwiaW5zZXJ0RnJvbVBhc3RlXCIgJiYgaW5wdXRUeXBlICE9PSBcImluc2VydEZyb21Ecm9wXCIpIHtcbiAgICAgICAgICAgIGlmIChjaHIgIT09IFwiQFwiICYmIGNociAhPT0gXCIjXCIgJiYgY2hyICE9PSBcIjpcIiAmJiBjaHIgIT09IFwiK1wiKSB7XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIHNwbGl0IGlmIHdlIGFyZSBhdCB0aGUgYmVnaW5uaW5nIG9mIHRoZSBwYXJ0IHRleHRcbiAgICAgICAgICAgIGlmIChvZmZzZXQgPT09IDApIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIG9yIHNwbGl0IGlmIHRoZSBwcmV2aW91cyBjaGFyYWN0ZXIgaXMgYSBzcGFjZSBvciByZWdpb25hbCBlbW9qaSBzZXBhcmF0b3JcbiAgICAgICAgICAgIC8vIG9yIGlmIGl0IGlzIGEgKyBhbmQgdGhpcyBpcyBhIDpcbiAgICAgICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICAgICAgdGhpcy5fdGV4dFtvZmZzZXQgLSAxXSAhPT0gXCIgXCIgJiZcbiAgICAgICAgICAgICAgICB0aGlzLl90ZXh0W29mZnNldCAtIDFdICE9PSBSRUdJT05BTF9FTU9KSV9TRVBBUkFUT1IgJiZcbiAgICAgICAgICAgICAgICAodGhpcy5fdGV4dFtvZmZzZXQgLSAxXSAhPT0gXCIrXCIgfHwgY2hyICE9PSBcIjpcIilcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcHVibGljIHRvRE9NTm9kZSgpOiBOb2RlIHtcbiAgICAgICAgcmV0dXJuIGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRoaXMudGV4dCk7XG4gICAgfVxuXG4gICAgcHVibGljIG1lcmdlKHBhcnQ6IFBhcnQpOiBib29sZWFuIHtcbiAgICAgICAgaWYgKHBhcnQudHlwZSA9PT0gdGhpcy50eXBlKSB7XG4gICAgICAgICAgICB0aGlzLl90ZXh0ID0gdGhpcy50ZXh0ICsgcGFydC50ZXh0O1xuICAgICAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHB1YmxpYyB1cGRhdGVET01Ob2RlKG5vZGU6IE5vZGUpOiB2b2lkIHtcbiAgICAgICAgaWYgKG5vZGUudGV4dENvbnRlbnQgIT09IHRoaXMudGV4dCkge1xuICAgICAgICAgICAgbm9kZS50ZXh0Q29udGVudCA9IHRoaXMudGV4dDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBjYW5VcGRhdGVET01Ob2RlKG5vZGU6IE5vZGUpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIG5vZGUubm9kZVR5cGUgPT09IE5vZGUuVEVYVF9OT0RFO1xuICAgIH1cbn1cblxuLy8gZXhwb3J0ZWQgZm9yIHVuaXQgdGVzdHMsIHNob3VsZCBvdGhlcndpc2Ugb25seSBiZSB1c2VkIHRocm91Z2ggUGFydENyZWF0b3JcbmV4cG9ydCBjbGFzcyBQbGFpblBhcnQgZXh0ZW5kcyBQbGFpbkJhc2VQYXJ0IGltcGxlbWVudHMgSUJhc2VQYXJ0IHtcbiAgICBwdWJsaWMgZ2V0IHR5cGUoKTogSUJhc2VQYXJ0W1widHlwZVwiXSB7XG4gICAgICAgIHJldHVybiBUeXBlLlBsYWluO1xuICAgIH1cbn1cblxuZXhwb3J0IGFic3RyYWN0IGNsYXNzIFBpbGxQYXJ0IGV4dGVuZHMgQmFzZVBhcnQgaW1wbGVtZW50cyBJUGlsbFBhcnQge1xuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHVibGljIHJlc291cmNlSWQ6IHN0cmluZyxcbiAgICAgICAgbGFiZWw6IHN0cmluZyxcbiAgICApIHtcbiAgICAgICAgc3VwZXIobGFiZWwpO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhY2NlcHRzSW5zZXJ0aW9uKGNocjogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBjaHIgIT09IFwiIFwiO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhY2NlcHRzUmVtb3ZhbChwb3NpdGlvbjogbnVtYmVyLCBjaHI6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gcG9zaXRpb24gIT09IDA7IC8vaWYgeW91IHJlbW92ZSBpbml0aWFsICMgb3IgQCwgcGlsbCBzaG91bGQgYmVjb21lIHBsYWluXG4gICAgfVxuXG4gICAgcHVibGljIHRvRE9NTm9kZSgpOiBOb2RlIHtcbiAgICAgICAgY29uc3QgY29udGFpbmVyID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcInNwYW5cIik7XG4gICAgICAgIGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoXCJzcGVsbGNoZWNrXCIsIFwiZmFsc2VcIik7XG4gICAgICAgIGNvbnRhaW5lci5zZXRBdHRyaWJ1dGUoXCJjb250ZW50RWRpdGFibGVcIiwgXCJmYWxzZVwiKTtcbiAgICAgICAgaWYgKHRoaXMub25DbGljaykgY29udGFpbmVyLm9uY2xpY2sgPSB0aGlzLm9uQ2xpY2s7XG4gICAgICAgIGNvbnRhaW5lci5jbGFzc05hbWUgPSB0aGlzLmNsYXNzTmFtZTtcbiAgICAgICAgY29udGFpbmVyLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRoaXMudGV4dCkpO1xuICAgICAgICB0aGlzLnNldEF2YXRhcihjb250YWluZXIpO1xuICAgICAgICByZXR1cm4gY29udGFpbmVyO1xuICAgIH1cblxuICAgIHB1YmxpYyB1cGRhdGVET01Ob2RlKG5vZGU6IEhUTUxFbGVtZW50KTogdm9pZCB7XG4gICAgICAgIGNvbnN0IHRleHROb2RlID0gbm9kZS5jaGlsZE5vZGVzWzBdO1xuICAgICAgICBpZiAodGV4dE5vZGUudGV4dENvbnRlbnQgIT09IHRoaXMudGV4dCkge1xuICAgICAgICAgICAgdGV4dE5vZGUudGV4dENvbnRlbnQgPSB0aGlzLnRleHQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKG5vZGUuY2xhc3NOYW1lICE9PSB0aGlzLmNsYXNzTmFtZSkge1xuICAgICAgICAgICAgbm9kZS5jbGFzc05hbWUgPSB0aGlzLmNsYXNzTmFtZTtcbiAgICAgICAgfVxuICAgICAgICBpZiAodGhpcy5vbkNsaWNrICYmIG5vZGUub25jbGljayAhPT0gdGhpcy5vbkNsaWNrKSB7XG4gICAgICAgICAgICBub2RlLm9uY2xpY2sgPSB0aGlzLm9uQ2xpY2s7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5zZXRBdmF0YXIobm9kZSk7XG4gICAgfVxuXG4gICAgcHVibGljIGNhblVwZGF0ZURPTU5vZGUobm9kZTogSFRNTEVsZW1lbnQpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIChcbiAgICAgICAgICAgIG5vZGUubm9kZVR5cGUgPT09IE5vZGUuRUxFTUVOVF9OT0RFICYmXG4gICAgICAgICAgICBub2RlLm5vZGVOYW1lID09PSBcIlNQQU5cIiAmJlxuICAgICAgICAgICAgbm9kZS5jaGlsZE5vZGVzLmxlbmd0aCA9PT0gMSAmJlxuICAgICAgICAgICAgbm9kZS5jaGlsZE5vZGVzWzBdLm5vZGVUeXBlID09PSBOb2RlLlRFWFRfTk9ERVxuICAgICAgICApO1xuICAgIH1cblxuICAgIC8vIGhlbHBlciBtZXRob2QgZm9yIHN1YmNsYXNzZXNcbiAgICBwcm90ZWN0ZWQgc2V0QXZhdGFyVmFycyhub2RlOiBIVE1MRWxlbWVudCwgYXZhdGFyVXJsOiBzdHJpbmcsIGluaXRpYWxMZXR0ZXI6IHN0cmluZyk6IHZvaWQge1xuICAgICAgICBjb25zdCBhdmF0YXJCYWNrZ3JvdW5kID0gYHVybCgnJHthdmF0YXJVcmx9JylgO1xuICAgICAgICBjb25zdCBhdmF0YXJMZXR0ZXIgPSBgJyR7aW5pdGlhbExldHRlcn0nYDtcbiAgICAgICAgLy8gY2hlY2sgaWYgdGhlIHZhbHVlIGlzIGNoYW5naW5nLFxuICAgICAgICAvLyBvdGhlcndpc2UgdGhlIGF2YXRhcnMgZmxpY2tlciBvbiBldmVyeSBrZXlzdHJva2Ugd2hpbGUgdXBkYXRpbmcuXG4gICAgICAgIGlmIChub2RlLnN0eWxlLmdldFByb3BlcnR5VmFsdWUoXCItLWF2YXRhci1iYWNrZ3JvdW5kXCIpICE9PSBhdmF0YXJCYWNrZ3JvdW5kKSB7XG4gICAgICAgICAgICBub2RlLnN0eWxlLnNldFByb3BlcnR5KFwiLS1hdmF0YXItYmFja2dyb3VuZFwiLCBhdmF0YXJCYWNrZ3JvdW5kKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAobm9kZS5zdHlsZS5nZXRQcm9wZXJ0eVZhbHVlKFwiLS1hdmF0YXItbGV0dGVyXCIpICE9PSBhdmF0YXJMZXR0ZXIpIHtcbiAgICAgICAgICAgIG5vZGUuc3R5bGUuc2V0UHJvcGVydHkoXCItLWF2YXRhci1sZXR0ZXJcIiwgYXZhdGFyTGV0dGVyKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBzZXJpYWxpemUoKTogSVNlcmlhbGl6ZWRQaWxsUGFydCB7XG4gICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB0eXBlOiB0aGlzLnR5cGUsXG4gICAgICAgICAgICB0ZXh0OiB0aGlzLnRleHQsXG4gICAgICAgICAgICByZXNvdXJjZUlkOiB0aGlzLnJlc291cmNlSWQsXG4gICAgICAgIH07XG4gICAgfVxuXG4gICAgcHVibGljIGdldCBjYW5FZGl0KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuXG4gICAgcHVibGljIGFic3RyYWN0IGdldCB0eXBlKCk6IElQaWxsUGFydFtcInR5cGVcIl07XG5cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3QgZ2V0IGNsYXNzTmFtZSgpOiBzdHJpbmc7XG5cbiAgICBwcm90ZWN0ZWQgb25DbGljaz86ICgpID0+IHZvaWQ7XG5cbiAgICBwcm90ZWN0ZWQgYWJzdHJhY3Qgc2V0QXZhdGFyKG5vZGU6IEhUTUxFbGVtZW50KTogdm9pZDtcbn1cblxuY2xhc3MgTmV3bGluZVBhcnQgZXh0ZW5kcyBCYXNlUGFydCBpbXBsZW1lbnRzIElCYXNlUGFydCB7XG4gICAgcHJvdGVjdGVkIGFjY2VwdHNJbnNlcnRpb24oY2hyOiBzdHJpbmcsIG9mZnNldDogbnVtYmVyKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBvZmZzZXQgPT09IDAgJiYgY2hyID09PSBcIlxcblwiO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBhY2NlcHRzUmVtb3ZhbChwb3NpdGlvbjogbnVtYmVyLCBjaHI6IHN0cmluZyk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICBwdWJsaWMgdG9ET01Ob2RlKCk6IE5vZGUge1xuICAgICAgICByZXR1cm4gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImJyXCIpO1xuICAgIH1cblxuICAgIHB1YmxpYyBtZXJnZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHB1YmxpYyB1cGRhdGVET01Ob2RlKCk6IHZvaWQge31cblxuICAgIHB1YmxpYyBjYW5VcGRhdGVET01Ob2RlKG5vZGU6IEhUTUxFbGVtZW50KTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBub2RlLnRhZ05hbWUgPT09IFwiQlJcIjtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0IHR5cGUoKTogSUJhc2VQYXJ0W1widHlwZVwiXSB7XG4gICAgICAgIHJldHVybiBUeXBlLk5ld2xpbmU7XG4gICAgfVxuXG4gICAgLy8gdGhpcyBtYWtlcyB0aGUgY3Vyc29yIHNraXAgdGhpcyBwYXJ0IHdoZW4gaXQgaXMgaW5zZXJ0ZWRcbiAgICAvLyByYXRoZXIgdGhhbiB0cnlpbmcgdG8gYXBwZW5kIHRvIGl0LCB3aGljaCBpcyB3aGF0IHdlIHdhbnQuXG4gICAgLy8gQXMgYSBuZXdsaW5lIGNhbiBhbHNvIGJlIG9ubHkgb25lIGNoYXJhY3RlciwgaXQgbWFrZXMgc2Vuc2VcbiAgICAvLyBhcyBpdCBjYW4gb25seSBiZSBvbmUgY2hhcmFjdGVyIGxvbmcuIFRoaXMgY2F1c2VkICM5NzQxLlxuICAgIHB1YmxpYyBnZXQgY2FuRWRpdCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbn1cblxuZXhwb3J0IGNsYXNzIEVtb2ppUGFydCBleHRlbmRzIEJhc2VQYXJ0IGltcGxlbWVudHMgSUJhc2VQYXJ0IHtcbiAgICBwcm90ZWN0ZWQgYWNjZXB0c0luc2VydGlvbihjaHI6IHN0cmluZywgb2Zmc2V0OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIEVNT0pJX1JFR0VYLnRlc3QoY2hyKTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYWNjZXB0c1JlbW92YWwocG9zaXRpb246IG51bWJlciwgY2hyOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHB1YmxpYyB0b0RPTU5vZGUoKTogTm9kZSB7XG4gICAgICAgIGNvbnN0IHNwYW4gPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwic3BhblwiKTtcbiAgICAgICAgc3Bhbi5jbGFzc05hbWUgPSBcIm14X0Vtb2ppXCI7XG4gICAgICAgIHNwYW4uc2V0QXR0cmlidXRlKFwidGl0bGVcIiwgdW5pY29kZVRvU2hvcnRjb2RlKHRoaXMudGV4dCkpO1xuICAgICAgICBzcGFuLmFwcGVuZENoaWxkKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKHRoaXMudGV4dCkpO1xuICAgICAgICByZXR1cm4gc3BhbjtcbiAgICB9XG5cbiAgICBwdWJsaWMgdXBkYXRlRE9NTm9kZShub2RlOiBIVE1MRWxlbWVudCk6IHZvaWQge1xuICAgICAgICBjb25zdCB0ZXh0Tm9kZSA9IG5vZGUuY2hpbGROb2Rlc1swXTtcbiAgICAgICAgaWYgKHRleHROb2RlLnRleHRDb250ZW50ICE9PSB0aGlzLnRleHQpIHtcbiAgICAgICAgICAgIG5vZGUuc2V0QXR0cmlidXRlKFwidGl0bGVcIiwgdW5pY29kZVRvU2hvcnRjb2RlKHRoaXMudGV4dCkpO1xuICAgICAgICAgICAgdGV4dE5vZGUudGV4dENvbnRlbnQgPSB0aGlzLnRleHQ7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgY2FuVXBkYXRlRE9NTm9kZShub2RlOiBIVE1MRWxlbWVudCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gbm9kZS5jbGFzc05hbWUgPT09IFwibXhfRW1vamlcIjtcbiAgICB9XG5cbiAgICBwdWJsaWMgZ2V0IHR5cGUoKTogSUJhc2VQYXJ0W1widHlwZVwiXSB7XG4gICAgICAgIHJldHVybiBUeXBlLkVtb2ppO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgY2FuRWRpdCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgYWNjZXB0c0NhcmV0KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG59XG5cbmNsYXNzIFJvb21QaWxsUGFydCBleHRlbmRzIFBpbGxQYXJ0IHtcbiAgICBwdWJsaWMgY29uc3RydWN0b3IoXG4gICAgICAgIHJlc291cmNlSWQ6IHN0cmluZyxcbiAgICAgICAgbGFiZWw6IHN0cmluZyxcbiAgICAgICAgcHJpdmF0ZSByb29tPzogUm9vbSxcbiAgICApIHtcbiAgICAgICAgc3VwZXIocmVzb3VyY2VJZCwgbGFiZWwpO1xuICAgIH1cblxuICAgIHByb3RlY3RlZCBzZXRBdmF0YXIobm9kZTogSFRNTEVsZW1lbnQpOiB2b2lkIHtcbiAgICAgICAgbGV0IGluaXRpYWxMZXR0ZXIgPSBcIlwiO1xuICAgICAgICBsZXQgYXZhdGFyVXJsID0gQXZhdGFyLmF2YXRhclVybEZvclJvb20odGhpcy5yb29tID8/IG51bGwsIDE2LCAxNiwgXCJjcm9wXCIpO1xuICAgICAgICBpZiAoIWF2YXRhclVybCkge1xuICAgICAgICAgICAgaW5pdGlhbExldHRlciA9IEF2YXRhci5nZXRJbml0aWFsTGV0dGVyKHRoaXMucm9vbT8ubmFtZSB8fCB0aGlzLnJlc291cmNlSWQpID8/IFwiXCI7XG4gICAgICAgICAgICBhdmF0YXJVcmwgPSBBdmF0YXIuZGVmYXVsdEF2YXRhclVybEZvclN0cmluZyh0aGlzLnJvb20/LnJvb21JZCA/PyB0aGlzLnJlc291cmNlSWQpO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2V0QXZhdGFyVmFycyhub2RlLCBhdmF0YXJVcmwsIGluaXRpYWxMZXR0ZXIpO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgdHlwZSgpOiBJUGlsbFBhcnRbXCJ0eXBlXCJdIHtcbiAgICAgICAgcmV0dXJuIFR5cGUuUm9vbVBpbGw7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIGdldCBjbGFzc05hbWUoKTogc3RyaW5nIHtcbiAgICAgICAgcmV0dXJuIFwibXhfUGlsbCBcIiArICh0aGlzLnJvb20/LmlzU3BhY2VSb29tKCkgPyBcIm14X1NwYWNlUGlsbFwiIDogXCJteF9Sb29tUGlsbFwiKTtcbiAgICB9XG59XG5cbmNsYXNzIEF0Um9vbVBpbGxQYXJ0IGV4dGVuZHMgUm9vbVBpbGxQYXJ0IHtcbiAgICBwdWJsaWMgY29uc3RydWN0b3IodGV4dDogc3RyaW5nLCByb29tOiBSb29tKSB7XG4gICAgICAgIHN1cGVyKHRleHQsIHRleHQsIHJvb20pO1xuICAgIH1cblxuICAgIHB1YmxpYyBnZXQgdHlwZSgpOiBJUGlsbFBhcnRbXCJ0eXBlXCJdIHtcbiAgICAgICAgcmV0dXJuIFR5cGUuQXRSb29tUGlsbDtcbiAgICB9XG5cbiAgICBwdWJsaWMgc2VyaWFsaXplKCk6IElTZXJpYWxpemVkUGlsbFBhcnQge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgdHlwZTogdGhpcy50eXBlLFxuICAgICAgICAgICAgdGV4dDogdGhpcy50ZXh0LFxuICAgICAgICB9O1xuICAgIH1cbn1cblxuY2xhc3MgVXNlclBpbGxQYXJ0IGV4dGVuZHMgUGlsbFBhcnQge1xuICAgIHB1YmxpYyBjb25zdHJ1Y3RvcihcbiAgICAgICAgdXNlcklkOiBzdHJpbmcsXG4gICAgICAgIGRpc3BsYXlOYW1lOiBzdHJpbmcsXG4gICAgICAgIHByaXZhdGUgbWVtYmVyPzogUm9vbU1lbWJlcixcbiAgICApIHtcbiAgICAgICAgc3VwZXIodXNlcklkLCBkaXNwbGF5TmFtZSk7XG4gICAgfVxuXG4gICAgcHVibGljIGdldCB0eXBlKCk6IElQaWxsUGFydFtcInR5cGVcIl0ge1xuICAgICAgICByZXR1cm4gVHlwZS5Vc2VyUGlsbDtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgZ2V0IGNsYXNzTmFtZSgpOiBzdHJpbmcge1xuICAgICAgICByZXR1cm4gXCJteF9Vc2VyUGlsbCBteF9QaWxsXCI7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIHNldEF2YXRhcihub2RlOiBIVE1MRWxlbWVudCk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMubWVtYmVyKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgbmFtZSA9IHRoaXMubWVtYmVyLm5hbWUgfHwgdGhpcy5tZW1iZXIudXNlcklkO1xuICAgICAgICBjb25zdCBkZWZhdWx0QXZhdGFyVXJsID0gQXZhdGFyLmRlZmF1bHRBdmF0YXJVcmxGb3JTdHJpbmcodGhpcy5tZW1iZXIudXNlcklkKTtcbiAgICAgICAgY29uc3QgYXZhdGFyVXJsID0gQXZhdGFyLmF2YXRhclVybEZvck1lbWJlcih0aGlzLm1lbWJlciwgMTYsIDE2LCBcImNyb3BcIik7XG4gICAgICAgIGxldCBpbml0aWFsTGV0dGVyID0gXCJcIjtcbiAgICAgICAgaWYgKGF2YXRhclVybCA9PT0gZGVmYXVsdEF2YXRhclVybCkge1xuICAgICAgICAgICAgaW5pdGlhbExldHRlciA9IEF2YXRhci5nZXRJbml0aWFsTGV0dGVyKG5hbWUpID8/IFwiXCI7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5zZXRBdmF0YXJWYXJzKG5vZGUsIGF2YXRhclVybCwgaW5pdGlhbExldHRlcik7XG4gICAgfVxuXG4gICAgcHJvdGVjdGVkIG9uQ2xpY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGRlZmF1bHREaXNwYXRjaGVyLmRpc3BhdGNoKHtcbiAgICAgICAgICAgIGFjdGlvbjogQWN0aW9uLlZpZXdVc2VyLFxuICAgICAgICAgICAgbWVtYmVyOiB0aGlzLm1lbWJlcixcbiAgICAgICAgfSk7XG4gICAgfTtcbn1cblxuY2xhc3MgUGlsbENhbmRpZGF0ZVBhcnQgZXh0ZW5kcyBQbGFpbkJhc2VQYXJ0IGltcGxlbWVudHMgSVBpbGxDYW5kaWRhdGVQYXJ0IHtcbiAgICBwdWJsaWMgY29uc3RydWN0b3IoXG4gICAgICAgIHRleHQ6IHN0cmluZyxcbiAgICAgICAgcHJpdmF0ZSBhdXRvQ29tcGxldGVDcmVhdG9yOiBJQXV0b2NvbXBsZXRlQ3JlYXRvcixcbiAgICApIHtcbiAgICAgICAgc3VwZXIodGV4dCk7XG4gICAgfVxuXG4gICAgcHVibGljIGNyZWF0ZUF1dG9Db21wbGV0ZSh1cGRhdGVDYWxsYmFjazogVXBkYXRlQ2FsbGJhY2spOiBBdXRvY29tcGxldGVXcmFwcGVyTW9kZWwgfCB1bmRlZmluZWQge1xuICAgICAgICByZXR1cm4gdGhpcy5hdXRvQ29tcGxldGVDcmVhdG9yLmNyZWF0ZT8uKHVwZGF0ZUNhbGxiYWNrKTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYWNjZXB0c0luc2VydGlvbihjaHI6IHN0cmluZywgb2Zmc2V0OiBudW1iZXIsIGlucHV0VHlwZTogc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgICAgIGlmIChvZmZzZXQgPT09IDApIHtcbiAgICAgICAgICAgIHJldHVybiB0cnVlO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmV0dXJuIHN1cGVyLmFjY2VwdHNJbnNlcnRpb24oY2hyLCBvZmZzZXQsIGlucHV0VHlwZSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgbWVyZ2UoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG5cbiAgICBwcm90ZWN0ZWQgYWNjZXB0c1JlbW92YWwocG9zaXRpb246IG51bWJlciwgY2hyOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRydWU7XG4gICAgfVxuXG4gICAgcHVibGljIGdldCB0eXBlKCk6IElQaWxsQ2FuZGlkYXRlUGFydFtcInR5cGVcIl0ge1xuICAgICAgICByZXR1cm4gVHlwZS5QaWxsQ2FuZGlkYXRlO1xuICAgIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGdldEF1dG9Db21wbGV0ZUNyZWF0b3IoZ2V0QXV0b2NvbXBsZXRlckNvbXBvbmVudDogR2V0QXV0b2NvbXBsZXRlckNvbXBvbmVudCwgdXBkYXRlUXVlcnk6IFVwZGF0ZVF1ZXJ5KSB7XG4gICAgcmV0dXJuIChwYXJ0Q3JlYXRvcjogUGFydENyZWF0b3IpID0+IHtcbiAgICAgICAgcmV0dXJuICh1cGRhdGVDYWxsYmFjazogVXBkYXRlQ2FsbGJhY2spID0+IHtcbiAgICAgICAgICAgIHJldHVybiBuZXcgQXV0b2NvbXBsZXRlV3JhcHBlck1vZGVsKHVwZGF0ZUNhbGxiYWNrLCBnZXRBdXRvY29tcGxldGVyQ29tcG9uZW50LCB1cGRhdGVRdWVyeSwgcGFydENyZWF0b3IpO1xuICAgICAgICB9O1xuICAgIH07XG59XG5cbnR5cGUgQXV0b0NvbXBsZXRlQ3JlYXRvciA9IFJldHVyblR5cGU8dHlwZW9mIGdldEF1dG9Db21wbGV0ZUNyZWF0b3I+O1xuXG5pbnRlcmZhY2UgSUF1dG9jb21wbGV0ZUNyZWF0b3Ige1xuICAgIGNyZWF0ZTogKCh1cGRhdGVDYWxsYmFjazogVXBkYXRlQ2FsbGJhY2spID0+IEF1dG9jb21wbGV0ZVdyYXBwZXJNb2RlbCkgfCB1bmRlZmluZWQ7XG59XG5cbmV4cG9ydCBjbGFzcyBQYXJ0Q3JlYXRvciB7XG4gICAgcHJvdGVjdGVkIHJlYWRvbmx5IGF1dG9Db21wbGV0ZUNyZWF0b3I6IElBdXRvY29tcGxldGVDcmVhdG9yO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IHJvb206IFJvb20sXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50OiBNYXRyaXhDbGllbnQsXG4gICAgICAgIGF1dG9Db21wbGV0ZUNyZWF0b3I6IEF1dG9Db21wbGV0ZUNyZWF0b3IgfCBudWxsID0gbnVsbCxcbiAgICApIHtcbiAgICAgICAgLy8gcHJlLWNyZWF0ZSB0aGUgY3JlYXRvciBhcyBhbiBvYmplY3QgZXZlbiB3aXRob3V0IGNhbGxiYWNrIHNvIGl0IGNhbiBhbHJlYWR5IGJlIHBhc3NlZFxuICAgICAgICAvLyB0byBQaWxsQ2FuZGlkYXRlUGFydCAoZS5nLiB3aGlsZSBkZXNlcmlhbGl6aW5nKSBhbmQgc2V0IGxhdGVyIG9uXG4gICAgICAgIHRoaXMuYXV0b0NvbXBsZXRlQ3JlYXRvciA9IHsgY3JlYXRlOiBhdXRvQ29tcGxldGVDcmVhdG9yPy4odGhpcykgfTtcbiAgICB9XG5cbiAgICBwdWJsaWMgc2V0QXV0b0NvbXBsZXRlQ3JlYXRvcihhdXRvQ29tcGxldGVDcmVhdG9yOiBBdXRvQ29tcGxldGVDcmVhdG9yKTogdm9pZCB7XG4gICAgICAgIHRoaXMuYXV0b0NvbXBsZXRlQ3JlYXRvci5jcmVhdGUgPSBhdXRvQ29tcGxldGVDcmVhdG9yKHRoaXMpO1xuICAgIH1cblxuICAgIHB1YmxpYyBjcmVhdGVQYXJ0Rm9ySW5wdXQoaW5wdXQ6IHN0cmluZywgcGFydEluZGV4OiBudW1iZXIsIGlucHV0VHlwZT86IHN0cmluZyk6IFBhcnQge1xuICAgICAgICBzd2l0Y2ggKGlucHV0WzBdKSB7XG4gICAgICAgICAgICBjYXNlIFwiI1wiOlxuICAgICAgICAgICAgY2FzZSBcIkBcIjpcbiAgICAgICAgICAgIGNhc2UgXCI6XCI6XG4gICAgICAgICAgICBjYXNlIFwiK1wiOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnBpbGxDYW5kaWRhdGUoXCJcIik7XG4gICAgICAgICAgICBjYXNlIFwiXFxuXCI6XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBOZXdsaW5lUGFydCgpO1xuICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICBpZiAoRU1PSklfUkVHRVgudGVzdChnZXRGaXJzdEdyYXBoZW1lKGlucHV0KSkpIHtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBFbW9qaVBhcnQoKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgcmV0dXJuIG5ldyBQbGFpblBhcnQoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBjcmVhdGVEZWZhdWx0UGFydCh0ZXh0OiBzdHJpbmcpOiBQYXJ0IHtcbiAgICAgICAgcmV0dXJuIHRoaXMucGxhaW4odGV4dCk7XG4gICAgfVxuXG4gICAgcHVibGljIGRlc2VyaWFsaXplUGFydChwYXJ0OiBTZXJpYWxpemVkUGFydCk6IFBhcnQgfCB1bmRlZmluZWQge1xuICAgICAgICBzd2l0Y2ggKHBhcnQudHlwZSkge1xuICAgICAgICAgICAgY2FzZSBUeXBlLlBsYWluOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnBsYWluKHBhcnQudGV4dCk7XG4gICAgICAgICAgICBjYXNlIFR5cGUuTmV3bGluZTpcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5uZXdsaW5lKCk7XG4gICAgICAgICAgICBjYXNlIFR5cGUuRW1vamk6XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuZW1vamkocGFydC50ZXh0KTtcbiAgICAgICAgICAgIGNhc2UgVHlwZS5BdFJvb21QaWxsOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmF0Um9vbVBpbGwocGFydC50ZXh0KTtcbiAgICAgICAgICAgIGNhc2UgVHlwZS5QaWxsQ2FuZGlkYXRlOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnBpbGxDYW5kaWRhdGUocGFydC50ZXh0KTtcbiAgICAgICAgICAgIGNhc2UgVHlwZS5Sb29tUGlsbDpcbiAgICAgICAgICAgICAgICByZXR1cm4gcGFydC5yZXNvdXJjZUlkID8gdGhpcy5yb29tUGlsbChwYXJ0LnJlc291cmNlSWQpIDogdW5kZWZpbmVkO1xuICAgICAgICAgICAgY2FzZSBUeXBlLlVzZXJQaWxsOlxuICAgICAgICAgICAgICAgIHJldHVybiBwYXJ0LnJlc291cmNlSWQgPyB0aGlzLnVzZXJQaWxsKHBhcnQudGV4dCwgcGFydC5yZXNvdXJjZUlkKSA6IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBwbGFpbih0ZXh0OiBzdHJpbmcpOiBQbGFpblBhcnQge1xuICAgICAgICByZXR1cm4gbmV3IFBsYWluUGFydCh0ZXh0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmV3bGluZSgpOiBOZXdsaW5lUGFydCB7XG4gICAgICAgIHJldHVybiBuZXcgTmV3bGluZVBhcnQoXCJcXG5cIik7XG4gICAgfVxuXG4gICAgcHVibGljIGVtb2ppKHRleHQ6IHN0cmluZyk6IEVtb2ppUGFydCB7XG4gICAgICAgIHJldHVybiBuZXcgRW1vamlQYXJ0KHRleHQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBwaWxsQ2FuZGlkYXRlKHRleHQ6IHN0cmluZyk6IFBpbGxDYW5kaWRhdGVQYXJ0IHtcbiAgICAgICAgcmV0dXJuIG5ldyBQaWxsQ2FuZGlkYXRlUGFydCh0ZXh0LCB0aGlzLmF1dG9Db21wbGV0ZUNyZWF0b3IpO1xuICAgIH1cblxuICAgIHB1YmxpYyByb29tUGlsbChhbGlhczogc3RyaW5nLCByb29tSWQ/OiBzdHJpbmcpOiBSb29tUGlsbFBhcnQge1xuICAgICAgICBsZXQgcm9vbTogUm9vbSB8IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKHJvb21JZCB8fCBhbGlhc1swXSAhPT0gXCIjXCIpIHtcbiAgICAgICAgICAgIHJvb20gPSB0aGlzLmNsaWVudC5nZXRSb29tKHJvb21JZCB8fCBhbGlhcykgPz8gdW5kZWZpbmVkO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcm9vbSA9IHRoaXMuY2xpZW50LmdldFJvb21zKCkuZmluZCgocikgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiByLmdldENhbm9uaWNhbEFsaWFzKCkgPT09IGFsaWFzIHx8IHIuZ2V0QWx0QWxpYXNlcygpLmluY2x1ZGVzKGFsaWFzKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiBuZXcgUm9vbVBpbGxQYXJ0KGFsaWFzLCByb29tID8gcm9vbS5uYW1lIDogYWxpYXMsIHJvb20pO1xuICAgIH1cblxuICAgIHB1YmxpYyBhdFJvb21QaWxsKHRleHQ6IHN0cmluZyk6IEF0Um9vbVBpbGxQYXJ0IHtcbiAgICAgICAgcmV0dXJuIG5ldyBBdFJvb21QaWxsUGFydCh0ZXh0LCB0aGlzLnJvb20pO1xuICAgIH1cblxuICAgIHB1YmxpYyB1c2VyUGlsbChkaXNwbGF5TmFtZTogc3RyaW5nLCB1c2VySWQ6IHN0cmluZyk6IFVzZXJQaWxsUGFydCB7XG4gICAgICAgIGNvbnN0IG1lbWJlciA9IHRoaXMucm9vbS5nZXRNZW1iZXIodXNlcklkKTtcbiAgICAgICAgcmV0dXJuIG5ldyBVc2VyUGlsbFBhcnQodXNlcklkLCBkaXNwbGF5TmFtZSwgbWVtYmVyIHx8IHVuZGVmaW5lZCk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzdGF0aWMgaXNSZWdpb25hbEluZGljYXRvcihjOiBzdHJpbmcpOiBib29sZWFuIHtcbiAgICAgICAgY29uc3QgY29kZVBvaW50ID0gYy5jb2RlUG9pbnRBdCgwKSA/PyAwO1xuICAgICAgICByZXR1cm4gY29kZVBvaW50ICE9IDAgJiYgYy5sZW5ndGggPT0gMiAmJiAweDFmMWU2IDw9IGNvZGVQb2ludCAmJiBjb2RlUG9pbnQgPD0gMHgxZjFmZjtcbiAgICB9XG5cbiAgICBwdWJsaWMgcGxhaW5XaXRoRW1vamkodGV4dDogc3RyaW5nKTogKFBsYWluUGFydCB8IEVtb2ppUGFydClbXSB7XG4gICAgICAgIGNvbnN0IHBhcnRzOiAoUGxhaW5QYXJ0IHwgRW1vamlQYXJ0KVtdID0gW107XG4gICAgICAgIGxldCBwbGFpblRleHQgPSBcIlwiO1xuXG4gICAgICAgIGZvciAoY29uc3QgZGF0YSBvZiBncmFwaGVtZVNlZ21lbnRlci5zZWdtZW50KHRleHQpKSB7XG4gICAgICAgICAgICBpZiAoRU1PSklfUkVHRVgudGVzdChkYXRhLnNlZ21lbnQpKSB7XG4gICAgICAgICAgICAgICAgaWYgKHBsYWluVGV4dCkge1xuICAgICAgICAgICAgICAgICAgICBwYXJ0cy5wdXNoKHRoaXMucGxhaW4ocGxhaW5UZXh0KSk7XG4gICAgICAgICAgICAgICAgICAgIHBsYWluVGV4dCA9IFwiXCI7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHBhcnRzLnB1c2godGhpcy5lbW9qaShkYXRhLnNlZ21lbnQpKTtcbiAgICAgICAgICAgICAgICBpZiAoUGFydENyZWF0b3IuaXNSZWdpb25hbEluZGljYXRvcih0ZXh0KSkge1xuICAgICAgICAgICAgICAgICAgICBwYXJ0cy5wdXNoKHRoaXMucGxhaW4oUkVHSU9OQUxfRU1PSklfU0VQQVJBVE9SKSk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBwbGFpblRleHQgKz0gZGF0YS5zZWdtZW50O1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIGlmIChwbGFpblRleHQpIHtcbiAgICAgICAgICAgIHBhcnRzLnB1c2godGhpcy5wbGFpbihwbGFpblRleHQpKTtcbiAgICAgICAgfVxuICAgICAgICByZXR1cm4gcGFydHM7XG4gICAgfVxuXG4gICAgcHVibGljIGNyZWF0ZU1lbnRpb25QYXJ0cyhcbiAgICAgICAgaW5zZXJ0VHJhaWxpbmdDaGFyYWN0ZXI6IGJvb2xlYW4sXG4gICAgICAgIGRpc3BsYXlOYW1lOiBzdHJpbmcsXG4gICAgICAgIHVzZXJJZDogc3RyaW5nLFxuICAgICk6IFtVc2VyUGlsbFBhcnQsIFBsYWluUGFydF0ge1xuICAgICAgICBjb25zdCBwaWxsID0gdGhpcy51c2VyUGlsbChkaXNwbGF5TmFtZSwgdXNlcklkKTtcbiAgICAgICAgaWYgKCFT