@jsprismarine/prismarine
Version:
Dedicated Minecraft Bedrock Edition server written in TypeScript
494 lines (493 loc) • 74.4 kB
JavaScript
"use strict";
Object.defineProperties(exports, {
__esModule: { value: true },
[Symbol.toStringTag]: { value: "Module" }
});
const require_runtime = require("../_virtual/_rolldown/runtime.cjs.cjs");
const require_item_Item = require("../item/Item.cjs.cjs");
const require_world_chunk_Chunk = require("../world/chunk/Chunk.cjs.cjs");
const require_world_BlockPosition = require("../world/BlockPosition.cjs.cjs");
const require_network_packet_RemoveActorPacket = require("./packet/RemoveActorPacket.cjs.cjs");
const require_network_type_TextType = require("./type/TextType.cjs.cjs");
const require_utils_UUID = require("../utils/UUID.cjs.cjs");
const require_network_packet_AddPlayerPacket = require("./packet/AddPlayerPacket.cjs.cjs");
const require_network_packet_AvailableCommandsPacket = require("./packet/AvailableCommandsPacket.cjs.cjs");
const require_network_packet_BatchPacket = require("./packet/BatchPacket.cjs.cjs");
const require_network_packet_ChunkRadiusUpdatedPacket = require("./packet/ChunkRadiusUpdatedPacket.cjs.cjs");
const require_network_packet_CreativeContentPacket = require("./packet/CreativeContentPacket.cjs.cjs");
const require_network_packet_DisconnectPacket = require("./packet/DisconnectPacket.cjs.cjs");
const require_network_packet_InventoryContentPacket = require("./packet/InventoryContentPacket.cjs.cjs");
const require_network_packet_LevelChunkPacket = require("./packet/LevelChunkPacket.cjs.cjs");
const require_network_packet_MobEquipmentPacket = require("./packet/MobEquipmentPacket.cjs.cjs");
const require_network_type_MovementType = require("./type/MovementType.cjs.cjs");
const require_network_packet_MovePlayerPacket = require("./packet/MovePlayerPacket.cjs.cjs");
const require_network_packet_NetworkChunkPublisherUpdatePacket = require("./packet/NetworkChunkPublisherUpdatePacket.cjs.cjs");
const require_network_packet_PlayStatusPacket = require("./packet/PlayStatusPacket.cjs.cjs");
const require_network_packet_PlayerListPacket = require("./packet/PlayerListPacket.cjs.cjs");
const require_network_packet_SetActorDataPacket = require("./packet/SetActorDataPacket.cjs.cjs");
const require_network_packet_SetPlayerGametypePacket = require("./packet/SetPlayerGametypePacket.cjs.cjs");
const require_network_packet_SetTimePacket = require("./packet/SetTimePacket.cjs.cjs");
const require_network_packet_TextPacket = require("./packet/TextPacket.cjs.cjs");
const require_network_packet_UpdateAdventureSettingsPacket = require("./packet/UpdateAdventureSettingsPacket.cjs.cjs");
const require_network_packet_UpdateAttributesPacket = require("./packet/UpdateAttributesPacket.cjs.cjs");
const require_network_type_CommandParameter = require("./type/CommandParameter.cjs.cjs");
const require_network_type_CommandEnum = require("./type/CommandEnum.cjs.cjs");
const require_command_CommandArguments = require("../command/CommandArguments.cjs.cjs");
const require_inventory_WindowIds = require("../inventory/WindowIds.cjs.cjs");
const require_world_CoordinateUtils = require("../world/CoordinateUtils.cjs.cjs");
const require_network_packet_UpdateAbilitiesPacket = require("./packet/UpdateAbilitiesPacket.cjs.cjs");
const require_network_type_CommandData = require("./type/CommandData.cjs.cjs");
const require_network_type_PermissionType = require("./type/PermissionType.cjs.cjs");
const require_network_type_PlayerPermissionType = require("./type/PlayerPermissionType.cjs.cjs");
let _jsprismarine_bedrock_data = require("@jsprismarine/bedrock-data");
let _jsprismarine_minecraft = require("@jsprismarine/minecraft");
let heap = require("heap");
heap = require_runtime.__toESM(heap, 1);
//#region src/network/PlayerSession.ts
var PlayerSession = class {
connection;
server;
player;
chunkSendQueue = [];
loadedChunks = /* @__PURE__ */ new Set();
loadingChunks = /* @__PURE__ */ new Set();
constructor(server, connection, player) {
this.server = server;
this.connection = connection;
this.player = player;
}
async update(_tick) {
if (this.chunkSendQueue.length > 0) {
const chunksToSend = this.chunkSendQueue.splice(0, Math.min(this.chunkSendQueue.length, 50));
const batch = new require_network_packet_BatchPacket.default();
for (const chunk of chunksToSend) {
const pk = new require_network_packet_LevelChunkPacket.default();
pk.chunkX = chunk.getX();
pk.chunkZ = chunk.getZ();
pk.clientSubChunkRequestsEnabled = false;
pk.subChunkCount = chunk.getTopEmpty() + 4;
pk.data = chunk.networkSerialize();
batch.addPacket(pk);
const hash = require_world_chunk_Chunk.default.packXZ(chunk.getX(), chunk.getZ());
this.loadedChunks.add(hash);
this.loadingChunks.delete(hash);
}
this.connection.sendBatch(batch, false);
}
this.player.viewDistance && await this.needNewChunks();
}
async send(packet) {
this.connection.sendDataPacket(packet);
}
/**
* Notify a client about change(s) to the adventure settings.
*
* @param player - The client-controlled entity
*/
async sendSettings(player) {
const target = player ?? this.player;
const packet = new require_network_packet_UpdateAdventureSettingsPacket.default();
packet.worldImmutable = target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR;
packet.noAttackingPlayers = target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR;
packet.noAttackingMobs = target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR;
packet.autoJump = true;
packet.showNameTags = true;
await this.connection.sendDataPacket(packet);
}
async sendAbilities(player) {
const target = player ?? this.player;
const mainLayer = new require_network_packet_UpdateAbilitiesPacket.AbilityLayer();
mainLayer.layerType = require_network_packet_UpdateAbilitiesPacket.AbilityLayerType.BASE;
mainLayer.layerFlags = new Map([
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.FLY_SPEED, true],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.WALK_SPEED, true],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.MAY_FLY, target.metadata.canFly],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.FLYING, target.isFlying()],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.NO_CLIP, target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.OPERATOR_COMMANDS, target.isOp()],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.TELEPORT, target.isOp()],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.INVULNERABLE, target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.MUTED, false],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.WORLD_BUILDER, false],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.INSTABUILD, target.gamemode === _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.LIGHTNING, false],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.BUILD, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.MINE, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.DOORS_AND_SWITCHES, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.OPEN_CONTAINERS, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.ATTACK_PLAYERS, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR],
[require_network_packet_UpdateAbilitiesPacket.AbilityLayerFlag.ATTACK_MOBS, target.gamemode !== _jsprismarine_minecraft.Gametype.SPECTATOR]
]);
mainLayer.flySpeed = .05;
mainLayer.walkSpeed = .1;
const packet = new require_network_packet_UpdateAbilitiesPacket.default();
packet.commandPermission = target.isOp() ? require_network_type_PermissionType.default.Operator : require_network_type_PermissionType.default.Normal;
packet.playerPermission = target.isOp() ? require_network_type_PlayerPermissionType.default.Operator : require_network_type_PlayerPermissionType.default.Member;
packet.targetActorUniqueId = target.getRuntimeId();
packet.abilityLayers = [mainLayer];
await this.send(packet);
}
async needNewChunks(forceResend = false, dist) {
const currentXChunk = require_world_CoordinateUtils.default.fromBlockToChunk(this.player.getX());
const currentZChunk = require_world_CoordinateUtils.default.fromBlockToChunk(this.player.getZ());
const viewDistance = this.player.viewDistance || dist || 0;
const chunksToSendHeap = new heap.default((a, b) => {
const distXFirst = Math.abs(a[0] - currentXChunk);
const distXSecond = Math.abs(b[0] - currentXChunk);
const distZFirst = Math.abs(a[1] - currentZChunk);
const distZSecond = Math.abs(b[1] - currentZChunk);
return distXFirst + distZFirst > distXSecond + distZSecond ? 1 : -1;
});
for (let sendXChunk = -viewDistance; sendXChunk <= viewDistance; sendXChunk++) for (let sendZChunk = -viewDistance; sendZChunk <= viewDistance; sendZChunk++) {
if (sendXChunk * sendXChunk + sendZChunk * sendZChunk > viewDistance * viewDistance) continue;
const newChunk = [currentXChunk + sendXChunk, currentZChunk + sendZChunk];
const hash = require_world_chunk_Chunk.default.packXZ(newChunk[0], newChunk[1]);
if (forceResend) chunksToSendHeap.push(newChunk);
else if (!this.loadedChunks.has(hash) && !this.loadingChunks.has(hash)) chunksToSendHeap.push(newChunk);
}
while (chunksToSendHeap.size() > 0) {
const closestChunk = chunksToSendHeap.pop();
const hash = require_world_chunk_Chunk.default.packXZ(closestChunk[0], closestChunk[1]);
if (forceResend) if (!this.loadedChunks.has(hash) && !this.loadingChunks.has(hash)) {
this.loadingChunks.add(hash);
await this.requestChunk(closestChunk[0], closestChunk[1]);
} else {
const loadedChunk = await this.player.getWorld().getChunk(closestChunk[0], closestChunk[1]);
await this.sendChunk(loadedChunk);
}
else {
this.loadingChunks.add(hash);
await this.requestChunk(closestChunk[0], closestChunk[1]);
}
}
let unloaded = false;
for (const hash of this.loadedChunks) {
const [x, z] = require_world_chunk_Chunk.default.unpackXZ(hash);
if (Math.abs(x - currentXChunk) > viewDistance || Math.abs(z - currentZChunk) > viewDistance) {
unloaded = true;
this.loadedChunks.delete(hash);
}
}
for (const hash of this.loadingChunks) {
const [x, z] = require_world_chunk_Chunk.default.unpackXZ(hash);
if (Math.abs(x - currentXChunk) > viewDistance || Math.abs(z - currentZChunk) > viewDistance) this.loadingChunks.delete(hash);
}
if (!unloaded && this.chunkSendQueue.length !== 0) await this.sendNetworkChunkPublisher(dist ?? viewDistance, []);
}
async requestChunk(x, z) {
const chunk = await this.player.getWorld().getChunk(x, z);
this.chunkSendQueue.push(chunk);
}
/**
* Clear the currently loaded and loading chunks.
*
* @remarks
* Usually used for changing dimension, world, etc.
*/
async clearChunks() {
this.loadedChunks.clear();
this.loadingChunks.clear();
}
/**
* @TODO: Implement this.
*/
async sendInventory() {
const pk = new require_network_packet_InventoryContentPacket.default();
pk.items = this.player.getInventory().getItems(true);
pk.windowId = require_inventory_WindowIds.WindowIds.INVENTORY;
}
async sendCreativeContents(empty = false) {
const pk = new require_network_packet_CreativeContentPacket.default();
if (empty) {
await this.connection.sendDataPacket(pk);
return;
}
const entries = [...this.player.getServer().getBlockManager().getBlocks(), ...this.player.getServer().getItemManager().getItems()];
_jsprismarine_bedrock_data.creativeitems.forEach((item) => {
pk.items.push(...entries.filter((entry) => {
return entry.meta === (item.damage ?? 0) && entry.getId() === item.id;
}).map((entry) => new require_item_Item.Item({
id: entry.getId(),
name: entry.getName(),
meta: entry.meta
})));
});
await this.connection.sendDataPacket(pk);
}
/**
* Sets the item in the player hand.
* @param {Item} item - The entity.
*/
async sendHandItem(item) {
const pk = new require_network_packet_MobEquipmentPacket.default();
pk.runtimeEntityId = this.player.getRuntimeId();
pk.item = item;
pk.inventorySlot = this.player.getInventory().getHandSlotIndex();
pk.hotbarSlot = this.player.getInventory().getHandSlotIndex();
pk.windowId = 0;
await this.connection.sendDataPacket(pk);
}
/**
* Send the current tick to a client.
* @param {number} tick - The tick
*/
async sendTime(tick) {
const pk = new require_network_packet_SetTimePacket.default();
pk.time = tick;
await this.connection.sendDataPacket(pk);
}
/**
* Send gamemode to a client.
* @param {Gametype} gamemode - the numeric gamemode ID.
*/
async sendGamemode(gamemode) {
const packet = new require_network_packet_SetPlayerGametypePacket.default();
packet.gametype = gamemode ?? this.player.gamemode;
await this.connection.sendDataPacket(packet);
}
async sendNetworkChunkPublisher(distance, savedChunks) {
const pk = new require_network_packet_NetworkChunkPublisherUpdatePacket.default();
pk.position = require_world_BlockPosition.default.fromVector3(this.player.getPosition());
pk.radius = distance << 4;
pk.savedChunks = savedChunks;
await this.connection.sendDataPacket(pk);
}
async sendAvailableCommands() {
const playerEnum = new require_network_type_CommandEnum.CommandEnum();
playerEnum.soft = true;
playerEnum.name = "Player";
playerEnum.values = this.player.getServer().getSessionManager().getAllPlayers().map((player) => player.getName());
const pk = new require_network_packet_AvailableCommandsPacket.default();
pk.softEnums = [playerEnum];
this.server.getCommandManager().getCommandsList().forEach((command) => {
const commandClass = Array.from(this.server.getCommandManager().getCommands().values()).find((cmd) => cmd.name === command[0]);
if (!commandClass) {
this.player.getServer().getLogger().warn(`Can't find corresponding command class for "${command[0]}"`);
return;
}
if (!this.player.getServer().getPermissionManager().can(this.player).execute(commandClass.permission)) return;
const cmd = new require_network_type_CommandData.default();
cmd.commandName = command[0];
cmd.commandDescription = commandClass.description;
if (commandClass.aliases.length > 0) {
const cmdAliases = new require_network_type_CommandEnum.CommandEnum();
cmdAliases.name = `${command[0]}Aliases`;
cmdAliases.values = commandClass.aliases.concat(command[0]);
cmd.aliases = cmdAliases;
}
command[2].forEach((arg, index) => {
const parameters = arg.map((parameter) => {
if (!parameter || !parameter?.getParameters) return [];
const parameters = parameter.getParameters(this.server);
if (parameters) return Array.from(parameters.values());
if (parameter instanceof require_command_CommandArguments.CommandArgumentEntity) return [new require_network_type_CommandParameter.default({
paramName: "target",
paramType: require_network_type_CommandParameter.CommandParameterType.Target
})];
if (parameter instanceof require_command_CommandArguments.CommandArgumentGamemode) return [new require_network_type_CommandParameter.default({
paramName: "gamemode",
paramType: require_network_type_CommandParameter.CommandParameterType.String
})];
if (parameter.constructor.name === "StringArgumentType") return [new require_network_type_CommandParameter.default({
paramName: "value",
paramType: require_network_type_CommandParameter.CommandParameterType.String
})];
if (parameter.constructor.name === "IntegerArgumentType") return [new require_network_type_CommandParameter.default({
paramName: "number",
paramType: require_network_type_CommandParameter.CommandParameterType.Int
})];
this.server.getLogger().warn(`Invalid parameter ${parameter.constructor.name}`);
return [new require_network_type_CommandParameter.default({
paramName: "value",
paramType: require_network_type_CommandParameter.CommandParameterType.String
})];
}).flat();
cmd.overloads[index] = parameters;
});
pk.commandData.push(cmd);
});
await this.getConnection().sendDataPacket(pk);
}
/**
* Set the client's maximum view distance.
*
* @param distance - The view distance
*/
async setViewDistance(distance) {
const packet = new require_network_packet_ChunkRadiusUpdatedPacket.default();
packet.radius = this.player.viewDistance = distance;
await this.connection.sendDataPacket(packet);
}
async sendAttributes(attributes) {
const value = attributes?.getAttributes() ?? this.player.attributes.getAttributes();
const packet = new require_network_packet_UpdateAttributesPacket.default();
packet.runtimeEntityId = this.player.getRuntimeId();
packet.attributes = value.length > 0 ? value : this.player.attributes.getDefaults();
packet.tick = BigInt(this.player.getServer().getTick());
await this.connection.sendDataPacket(packet);
}
async sendMetadata(metadata) {
const packet = new require_network_packet_SetActorDataPacket.default();
packet.runtimeEntityId = this.player.getRuntimeId();
packet.metadata = metadata ?? this.player.metadata;
packet.tick = BigInt(this.player.getServer().getTick());
await this.connection.sendDataPacket(packet);
}
/**
* Send a chat message to the client.
* @param {object} options - The options for the message.
* @param {string} options.message - The message to send.
* @param {string} [options.sourceName=''] - The source of the message.
* @param {string} [options.xuid=''] - The XUID of the player.
* @param {string} [options.platformChatId=''] - The platform chat ID.
* @param {string[]} [options.parameters=[]] - The parameters for the message.
* @param {boolean} [options.needsTranslation=false] - Whether the message needs translation.
* @param {TextType} [options.type=TextType.Raw] - The type of the message.
* @returns {Promise<void>} A promise that resolves when the message is sent.
*/
async sendMessage({ message, sourceName = "", xuid = "", platformChatId = "", parameters = [], needsTranslation = false, type = require_network_type_TextType.default.Raw }) {
if (!message) throw new Error("A message is required");
const pk = new require_network_packet_TextPacket.default();
pk.message = message;
pk.filtered = message;
pk.sourceName = sourceName;
pk.xuid = xuid;
pk.platformChatId = platformChatId;
pk.parameters = parameters;
pk.needsTranslation = needsTranslation;
pk.type = type;
await this.connection.sendDataPacket(pk);
}
async sendChunk(chunk) {
const hash = require_world_chunk_Chunk.default.packXZ(chunk.getX(), chunk.getZ());
const packet = new require_network_packet_LevelChunkPacket.default();
packet.chunkX = chunk.getX();
packet.chunkZ = chunk.getZ();
packet.clientSubChunkRequestsEnabled = false;
packet.subChunkCount = chunk.getTopEmpty() + 4;
packet.data = chunk.networkSerialize();
await this.send(packet);
this.loadingChunks.delete(hash);
this.loadedChunks.add(hash);
}
/**
* Broadcast the movement to a defined player
*/
async broadcastMove(player, mode = require_network_type_MovementType.default.Normal) {
const packet = new require_network_packet_MovePlayerPacket.default();
packet.runtimeEntityId = player.getRuntimeId();
packet.position = player.getPosition();
packet.pitch = player.pitch;
packet.yaw = player.yaw;
packet.headYaw = player.headYaw;
packet.mode = mode;
packet.onGround = player.isOnGround();
if (mode === require_network_type_MovementType.default.Teleport) {
packet.teleportCause = 0;
packet.teleportItemId = 0;
}
packet.ridingEntityRuntimeId = BigInt(0);
packet.tick = BigInt(this.player.getServer().getTick());
await this.send(packet);
}
/**
* Adds the client to the player list of every player inside
* the server and also to the player itself.
*/
async addToPlayerList() {
const entry = new require_network_packet_PlayerListPacket.PlayerListEntry({
uuid: require_utils_UUID.default.fromString(this.player.getUUID()),
runtimeId: this.player.getRuntimeId(),
name: this.player.getName(),
xuid: this.player.xuid,
platformChatId: this.player.platformChatId,
buildPlatform: this.player.device?.os ?? -1,
skin: this.player.skin,
isTeacher: false,
isHost: false
});
this.server.getSessionManager().getPlayerList().set(this.player.getUUID(), entry);
const packet = new require_network_packet_PlayerListPacket.default();
packet.type = require_network_packet_PlayerListPacket.PlayerListAction.TYPE_ADD;
packet.entries.push(entry);
await this.server.broadcastPacket(packet);
}
/**
* Removes a player from other players list
*/
async removeFromPlayerList() {
const entry = new require_network_packet_PlayerListPacket.PlayerListEntry({ uuid: require_utils_UUID.default.fromString(this.player.getUUID()) });
this.server.getSessionManager().getPlayerList().delete(this.player.getUUID());
const packet = new require_network_packet_PlayerListPacket.default();
packet.type = require_network_packet_PlayerListPacket.PlayerListAction.TYPE_REMOVE;
packet.entries.push(entry);
await this.server.broadcastPacket(packet);
}
/**
* Sends the full player list to the player.
*/
async sendPlayerList() {
const packet = new require_network_packet_PlayerListPacket.default();
packet.type = require_network_packet_PlayerListPacket.PlayerListAction.TYPE_ADD;
packet.entries = Array.from(this.server.getSessionManager().getPlayerList().values());
await this.send(packet);
}
/**
* Spawn the player for another player
*
* @param player - the player to send the AddPlayerPacket to
*/
async sendSpawn(player) {
if (!player.getUUID()) {
this.server.getLogger().error(`UUID for player ${player.getName()} is undefined`);
return;
}
const pk = new require_network_packet_AddPlayerPacket.default();
pk.uuid = require_utils_UUID.default.fromString(this.player.getUUID());
pk.runtimeEntityId = this.player.getRuntimeId();
pk.name = this.player.getName();
pk.positionX = this.player.getX();
pk.positionY = this.player.getY();
pk.positionZ = this.player.getZ();
pk.motionX = 0;
pk.motionY = 0;
pk.motionZ = 0;
pk.pitch = this.player.pitch;
pk.yaw = this.player.yaw;
pk.headYaw = this.player.headYaw;
pk.gamemode = this.player.gamemode;
pk.item = this.player.getInventory().getItemInHand();
pk.deviceId = this.player.device?.id ?? "";
pk.metadata = this.player.metadata;
await player.getNetworkSession().send(pk);
await this.sendSettings(player);
}
/**
* Despawn the player entity from another player
*/
async sendDespawn(player) {
const pk = new require_network_packet_RemoveActorPacket.default();
pk.uniqueEntityId = this.player.getRuntimeId();
await player.getNetworkSession().getConnection().sendDataPacket(pk);
}
async sendPlayStatus(status) {
const pk = new require_network_packet_PlayStatusPacket.default();
pk.status = status;
await this.connection.sendDataPacket(pk, true);
}
async kick(reason = "unknown reason") {
const pk = new require_network_packet_DisconnectPacket.default();
pk.skipMessage = false;
pk.message = reason;
await this.connection.sendDataPacket(pk, true);
}
getConnection() {
return this.connection;
}
getPlayer() {
return this.player;
}
};
//#endregion
exports.default = PlayerSession;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUGxheWVyU2Vzc2lvbi5janMuY2pzIiwibmFtZXMiOltdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9uZXR3b3JrL1BsYXllclNlc3Npb24udHMiXSwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgY3JlYXRpdmVpdGVtcyBhcyBDcmVhdGl2ZUl0ZW1zIH0gZnJvbSAnQGpzcHJpc21hcmluZS9iZWRyb2NrLWRhdGEnO1xuaW1wb3J0IHsgR2FtZXR5cGUgfSBmcm9tICdAanNwcmlzbWFyaW5lL21pbmVjcmFmdCc7XG5pbXBvcnQgSGVhcCBmcm9tICdoZWFwJztcbmltcG9ydCB0eXBlIFBsYXllciBmcm9tICcuLi9QbGF5ZXInO1xuaW1wb3J0IHR5cGUgU2VydmVyIGZyb20gJy4uL1NlcnZlcic7XG5pbXBvcnQgdHlwZSB7IENvbW1hbmRBcmd1bWVudCB9IGZyb20gJy4uL2NvbW1hbmQvQ29tbWFuZEFyZ3VtZW50cyc7XG5pbXBvcnQgeyBDb21tYW5kQXJndW1lbnRFbnRpdHksIENvbW1hbmRBcmd1bWVudEdhbWVtb2RlIH0gZnJvbSAnLi4vY29tbWFuZC9Db21tYW5kQXJndW1lbnRzJztcbmltcG9ydCB0eXBlIHsgQXR0cmlidXRlcyB9IGZyb20gJy4uL2VudGl0eS9BdHRyaWJ1dGUnO1xuaW1wb3J0IHR5cGUgeyBNZXRhZGF0YSB9IGZyb20gJy4uL2VudGl0eS9NZXRhZGF0YSc7XG5pbXBvcnQgeyBXaW5kb3dJZHMgfSBmcm9tICcuLi9pbnZlbnRvcnkvV2luZG93SWRzJztcbmltcG9ydCB7IEl0ZW0gfSBmcm9tICcuLi9pdGVtL0l0ZW0nO1xuaW1wb3J0IFVVSUQgZnJvbSAnLi4vdXRpbHMvVVVJRCc7XG5pbXBvcnQgQmxvY2tQb3NpdGlvbiBmcm9tICcuLi93b3JsZC9CbG9ja1Bvc2l0aW9uJztcbmltcG9ydCBDb29yZGluYXRlVXRpbHMgZnJvbSAnLi4vd29ybGQvQ29vcmRpbmF0ZVV0aWxzJztcbmltcG9ydCBDaHVuayBmcm9tICcuLi93b3JsZC9jaHVuay9DaHVuayc7XG5pbXBvcnQgdHlwZSBDbGllbnRDb25uZWN0aW9uIGZyb20gJy4vQ2xpZW50Q29ubmVjdGlvbic7XG5pbXBvcnQgdHlwZSB7IERhdGFQYWNrZXQgfSBmcm9tICcuL1BhY2tldHMnO1xuaW1wb3J0IHsgQmF0Y2hQYWNrZXQgfSBmcm9tICcuL1BhY2tldHMnO1xuaW1wb3J0IEFkZFBsYXllclBhY2tldCBmcm9tICcuL3BhY2tldC9BZGRQbGF5ZXJQYWNrZXQnO1xuaW1wb3J0IEF2YWlsYWJsZUNvbW1hbmRzUGFja2V0IGZyb20gJy4vcGFja2V0L0F2YWlsYWJsZUNvbW1hbmRzUGFja2V0JztcbmltcG9ydCBDaHVua1JhZGl1c1VwZGF0ZWRQYWNrZXQgZnJvbSAnLi9wYWNrZXQvQ2h1bmtSYWRpdXNVcGRhdGVkUGFja2V0JztcbmltcG9ydCBDcmVhdGl2ZUNvbnRlbnRQYWNrZXQgZnJvbSAnLi9wYWNrZXQvQ3JlYXRpdmVDb250ZW50UGFja2V0JztcbmltcG9ydCBEaXNjb25uZWN0UGFja2V0IGZyb20gJy4vcGFja2V0L0Rpc2Nvbm5lY3RQYWNrZXQnO1xuaW1wb3J0IEludmVudG9yeUNvbnRlbnRQYWNrZXQgZnJvbSAnLi9wYWNrZXQvSW52ZW50b3J5Q29udGVudFBhY2tldCc7XG5pbXBvcnQgTGV2ZWxDaHVua1BhY2tldCBmcm9tICcuL3BhY2tldC9MZXZlbENodW5rUGFja2V0JztcbmltcG9ydCBNb2JFcXVpcG1lbnRQYWNrZXQgZnJvbSAnLi9wYWNrZXQvTW9iRXF1aXBtZW50UGFja2V0JztcbmltcG9ydCBNb3ZlUGxheWVyUGFja2V0IGZyb20gJy4vcGFja2V0L01vdmVQbGF5ZXJQYWNrZXQnO1xuaW1wb3J0IHR5cGUgeyBDaHVua0Nvb3JkIH0gZnJvbSAnLi9wYWNrZXQvTmV0d29ya0NodW5rUHVibGlzaGVyVXBkYXRlUGFja2V0JztcbmltcG9ydCBOZXR3b3JrQ2h1bmtQdWJsaXNoZXJVcGRhdGVQYWNrZXQgZnJvbSAnLi9wYWNrZXQvTmV0d29ya0NodW5rUHVibGlzaGVyVXBkYXRlUGFja2V0JztcbmltcG9ydCBQbGF5U3RhdHVzUGFja2V0IGZyb20gJy4vcGFja2V0L1BsYXlTdGF0dXNQYWNrZXQnO1xuaW1wb3J0IFBsYXllckxpc3RQYWNrZXQsIHsgUGxheWVyTGlzdEFjdGlvbiwgUGxheWVyTGlzdEVudHJ5IH0gZnJvbSAnLi9wYWNrZXQvUGxheWVyTGlzdFBhY2tldCc7XG5pbXBvcnQgUmVtb3ZlQWN0b3JQYWNrZXQgZnJvbSAnLi9wYWNrZXQvUmVtb3ZlQWN0b3JQYWNrZXQnO1xuaW1wb3J0IFNldEFjdG9yRGF0YVBhY2tldCBmcm9tICcuL3BhY2tldC9TZXRBY3RvckRhdGFQYWNrZXQnO1xuaW1wb3J0IFNldFBsYXllckdhbWV0eXBlUGFja2V0IGZyb20gJy4vcGFja2V0L1NldFBsYXllckdhbWV0eXBlUGFja2V0JztcbmltcG9ydCBTZXRUaW1lUGFja2V0IGZyb20gJy4vcGFja2V0L1NldFRpbWVQYWNrZXQnO1xuaW1wb3J0IFRleHRQYWNrZXQgZnJvbSAnLi9wYWNrZXQvVGV4dFBhY2tldCc7XG5pbXBvcnQgVXBkYXRlQWJpbGl0aWVzUGFja2V0LCB7XG4gICAgQWJpbGl0eUxheWVyLFxuICAgIEFiaWxpdHlMYXllckZsYWcsXG4gICAgQWJpbGl0eUxheWVyVHlwZVxufSBmcm9tICcuL3BhY2tldC9VcGRhdGVBYmlsaXRpZXNQYWNrZXQnO1xuaW1wb3J0IFVwZGF0ZUFkdmVudHVyZVNldHRpbmdzUGFja2V0IGZyb20gJy4vcGFja2V0L1VwZGF0ZUFkdmVudHVyZVNldHRpbmdzUGFja2V0JztcbmltcG9ydCBVcGRhdGVBdHRyaWJ1dGVzUGFja2V0IGZyb20gJy4vcGFja2V0L1VwZGF0ZUF0dHJpYnV0ZXNQYWNrZXQnO1xuaW1wb3J0IENvbW1hbmREYXRhIGZyb20gJy4vdHlwZS9Db21tYW5kRGF0YSc7XG5pbXBvcnQgeyBDb21tYW5kRW51bSB9IGZyb20gJy4vdHlwZS9Db21tYW5kRW51bSc7XG5pbXBvcnQgQ29tbWFuZFBhcmFtZXRlciwgeyBDb21tYW5kUGFyYW1ldGVyVHlwZSB9IGZyb20gJy4vdHlwZS9Db21tYW5kUGFyYW1ldGVyJztcbmltcG9ydCBNb3ZlbWVudFR5cGUgZnJvbSAnLi90eXBlL01vdmVtZW50VHlwZSc7XG5pbXBvcnQgUGVybWlzc2lvblR5cGUgZnJvbSAnLi90eXBlL1Blcm1pc3Npb25UeXBlJztcbmltcG9ydCBQbGF5ZXJQZXJtaXNzaW9uVHlwZSBmcm9tICcuL3R5cGUvUGxheWVyUGVybWlzc2lvblR5cGUnO1xuaW1wb3J0IFRleHRUeXBlIGZyb20gJy4vdHlwZS9UZXh0VHlwZSc7XG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFBsYXllclNlc3Npb24ge1xuICAgIHByaXZhdGUgY29ubmVjdGlvbjogQ2xpZW50Q29ubmVjdGlvbjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IHNlcnZlcjogU2VydmVyO1xuICAgIHByaXZhdGUgcGxheWVyOiBQbGF5ZXI7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IGNodW5rU2VuZFF1ZXVlOiBDaHVua1tdID0gW107XG4gICAgcHJpdmF0ZSByZWFkb25seSBsb2FkZWRDaHVua3M6IFNldDxiaWdpbnQ+ID0gbmV3IFNldCgpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgbG9hZGluZ0NodW5rczogU2V0PGJpZ2ludD4gPSBuZXcgU2V0KCk7XG5cbiAgICBwdWJsaWMgY29uc3RydWN0b3Ioc2VydmVyOiBTZXJ2ZXIsIGNvbm5lY3Rpb246IENsaWVudENvbm5lY3Rpb24sIHBsYXllcjogUGxheWVyKSB7XG4gICAgICAgIHRoaXMuc2VydmVyID0gc2VydmVyO1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb24gPSBjb25uZWN0aW9uO1xuICAgICAgICB0aGlzLnBsYXllciA9IHBsYXllcjtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgdXBkYXRlKF90aWNrOiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuY2h1bmtTZW5kUXVldWUubGVuZ3RoID4gMCkge1xuICAgICAgICAgICAgY29uc3QgY2h1bmtzVG9TZW5kID0gdGhpcy5jaHVua1NlbmRRdWV1ZS5zcGxpY2UoMCwgTWF0aC5taW4odGhpcy5jaHVua1NlbmRRdWV1ZS5sZW5ndGgsIDUwKSk7XG4gICAgICAgICAgICBjb25zdCBiYXRjaCA9IG5ldyBCYXRjaFBhY2tldCgpO1xuICAgICAgICAgICAgZm9yIChjb25zdCBjaHVuayBvZiBjaHVua3NUb1NlbmQpIHtcbiAgICAgICAgICAgICAgICBjb25zdCBwayA9IG5ldyBMZXZlbENodW5rUGFja2V0KCk7XG4gICAgICAgICAgICAgICAgcGsuY2h1bmtYID0gY2h1bmsuZ2V0WCgpO1xuICAgICAgICAgICAgICAgIHBrLmNodW5rWiA9IGNodW5rLmdldFooKTtcbiAgICAgICAgICAgICAgICBway5jbGllbnRTdWJDaHVua1JlcXVlc3RzRW5hYmxlZCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgIHBrLnN1YkNodW5rQ291bnQgPSBjaHVuay5nZXRUb3BFbXB0eSgpICsgNDtcbiAgICAgICAgICAgICAgICBway5kYXRhID0gY2h1bmsubmV0d29ya1NlcmlhbGl6ZSgpO1xuICAgICAgICAgICAgICAgIGJhdGNoLmFkZFBhY2tldChwayk7XG5cbiAgICAgICAgICAgICAgICBjb25zdCBoYXNoID0gQ2h1bmsucGFja1haKGNodW5rLmdldFgoKSwgY2h1bmsuZ2V0WigpKTtcbiAgICAgICAgICAgICAgICB0aGlzLmxvYWRlZENodW5rcy5hZGQoaGFzaCk7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2FkaW5nQ2h1bmtzLmRlbGV0ZShoYXNoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMuY29ubmVjdGlvbi5zZW5kQmF0Y2goYmF0Y2gsIGZhbHNlKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMucGxheWVyLnZpZXdEaXN0YW5jZSAmJiAoYXdhaXQgdGhpcy5uZWVkTmV3Q2h1bmtzKCkpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kKHBhY2tldDogRGF0YVBhY2tldCkge1xuICAgICAgICB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBOb3RpZnkgYSBjbGllbnQgYWJvdXQgY2hhbmdlKHMpIHRvIHRoZSBhZHZlbnR1cmUgc2V0dGluZ3MuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gcGxheWVyIC0gVGhlIGNsaWVudC1jb250cm9sbGVkIGVudGl0eVxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kU2V0dGluZ3MocGxheWVyPzogUGxheWVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHRhcmdldCA9IHBsYXllciA/PyB0aGlzLnBsYXllcjtcblxuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgVXBkYXRlQWR2ZW50dXJlU2V0dGluZ3NQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LndvcmxkSW1tdXRhYmxlID0gdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1I7XG4gICAgICAgIHBhY2tldC5ub0F0dGFja2luZ1BsYXllcnMgPSB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUjtcbiAgICAgICAgcGFja2V0Lm5vQXR0YWNraW5nTW9icyA9IHRhcmdldC5nYW1lbW9kZSA9PT0gR2FtZXR5cGUuU1BFQ1RBVE9SO1xuICAgICAgICBwYWNrZXQuYXV0b0p1bXAgPSB0cnVlO1xuICAgICAgICBwYWNrZXQuc2hvd05hbWVUYWdzID0gdHJ1ZTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRBYmlsaXRpZXMocGxheWVyPzogUGxheWVyKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHRhcmdldCA9IHBsYXllciA/PyB0aGlzLnBsYXllcjtcblxuICAgICAgICBjb25zdCBtYWluTGF5ZXIgPSBuZXcgQWJpbGl0eUxheWVyKCk7XG4gICAgICAgIG1haW5MYXllci5sYXllclR5cGUgPSBBYmlsaXR5TGF5ZXJUeXBlLkJBU0U7XG4gICAgICAgIG1haW5MYXllci5sYXllckZsYWdzID0gbmV3IE1hcChbXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5GTFlfU1BFRUQsIHRydWVdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuV0FMS19TUEVFRCwgdHJ1ZV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5NQVlfRkxZLCB0YXJnZXQubWV0YWRhdGEuY2FuRmx5XSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkZMWUlORywgdGFyZ2V0LmlzRmx5aW5nKCldLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuTk9fQ0xJUCwgdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuT1BFUkFUT1JfQ09NTUFORFMsIHRhcmdldC5pc09wKCldLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuVEVMRVBPUlQsIHRhcmdldC5pc09wKCldLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuSU5WVUxORVJBQkxFLCB0YXJnZXQuZ2FtZW1vZGUgPT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5NVVRFRCwgZmFsc2VdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuV09STERfQlVJTERFUiwgZmFsc2VdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuSU5TVEFCVUlMRCwgdGFyZ2V0LmdhbWVtb2RlID09PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuTElHSFROSU5HLCBmYWxzZV0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5CVUlMRCwgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuTUlORSwgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdLFxuICAgICAgICAgICAgW0FiaWxpdHlMYXllckZsYWcuRE9PUlNfQU5EX1NXSVRDSEVTLCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5PUEVOX0NPTlRBSU5FUlMsIHRhcmdldC5nYW1lbW9kZSAhPT0gR2FtZXR5cGUuU1BFQ1RBVE9SXSxcbiAgICAgICAgICAgIFtBYmlsaXR5TGF5ZXJGbGFnLkFUVEFDS19QTEFZRVJTLCB0YXJnZXQuZ2FtZW1vZGUgIT09IEdhbWV0eXBlLlNQRUNUQVRPUl0sXG4gICAgICAgICAgICBbQWJpbGl0eUxheWVyRmxhZy5BVFRBQ0tfTU9CUywgdGFyZ2V0LmdhbWVtb2RlICE9PSBHYW1ldHlwZS5TUEVDVEFUT1JdXG4gICAgICAgIF0pO1xuICAgICAgICBtYWluTGF5ZXIuZmx5U3BlZWQgPSAwLjA1O1xuICAgICAgICBtYWluTGF5ZXIud2Fsa1NwZWVkID0gMC4xO1xuXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBVcGRhdGVBYmlsaXRpZXNQYWNrZXQoKTtcbiAgICAgICAgcGFja2V0LmNvbW1hbmRQZXJtaXNzaW9uID0gdGFyZ2V0LmlzT3AoKSA/IFBlcm1pc3Npb25UeXBlLk9wZXJhdG9yIDogUGVybWlzc2lvblR5cGUuTm9ybWFsO1xuICAgICAgICBwYWNrZXQucGxheWVyUGVybWlzc2lvbiA9IHRhcmdldC5pc09wKCkgPyBQbGF5ZXJQZXJtaXNzaW9uVHlwZS5PcGVyYXRvciA6IFBsYXllclBlcm1pc3Npb25UeXBlLk1lbWJlcjtcbiAgICAgICAgcGFja2V0LnRhcmdldEFjdG9yVW5pcXVlSWQgPSB0YXJnZXQuZ2V0UnVudGltZUlkKCk7XG4gICAgICAgIHBhY2tldC5hYmlsaXR5TGF5ZXJzID0gW21haW5MYXllcl07XG4gICAgICAgIGF3YWl0IHRoaXMuc2VuZChwYWNrZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBuZWVkTmV3Q2h1bmtzKGZvcmNlUmVzZW5kID0gZmFsc2UsIGRpc3Q/OiBudW1iZXIpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgY3VycmVudFhDaHVuayA9IENvb3JkaW5hdGVVdGlscy5mcm9tQmxvY2tUb0NodW5rKHRoaXMucGxheWVyLmdldFgoKSk7XG4gICAgICAgIGNvbnN0IGN1cnJlbnRaQ2h1bmsgPSBDb29yZGluYXRlVXRpbHMuZnJvbUJsb2NrVG9DaHVuayh0aGlzLnBsYXllci5nZXRaKCkpO1xuXG4gICAgICAgIGNvbnN0IHZpZXdEaXN0YW5jZSA9IHRoaXMucGxheWVyLnZpZXdEaXN0YW5jZSB8fCBkaXN0IHx8IDA7XG5cbiAgICAgICAgY29uc3QgY2h1bmtzVG9TZW5kSGVhcCA9IG5ldyBIZWFwKChhOiBudW1iZXJbXSwgYjogbnVtYmVyW10pID0+IHtcbiAgICAgICAgICAgIGNvbnN0IGRpc3RYRmlyc3QgPSBNYXRoLmFicyhhWzBdISAtIGN1cnJlbnRYQ2h1bmspO1xuICAgICAgICAgICAgY29uc3QgZGlzdFhTZWNvbmQgPSBNYXRoLmFicyhiWzBdISAtIGN1cnJlbnRYQ2h1bmspO1xuXG4gICAgICAgICAgICBjb25zdCBkaXN0WkZpcnN0ID0gTWF0aC5hYnMoYVsxXSEgLSBjdXJyZW50WkNodW5rKTtcbiAgICAgICAgICAgIGNvbnN0IGRpc3RaU2Vjb25kID0gTWF0aC5hYnMoYlsxXSEgLSBjdXJyZW50WkNodW5rKTtcblxuICAgICAgICAgICAgcmV0dXJuIGRpc3RYRmlyc3QgKyBkaXN0WkZpcnN0ID4gZGlzdFhTZWNvbmQgKyBkaXN0WlNlY29uZCA/IDEgOiAtMTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgZm9yIChsZXQgc2VuZFhDaHVuayA9IC12aWV3RGlzdGFuY2U7IHNlbmRYQ2h1bmsgPD0gdmlld0Rpc3RhbmNlOyBzZW5kWENodW5rKyspIHtcbiAgICAgICAgICAgIGZvciAobGV0IHNlbmRaQ2h1bmsgPSAtdmlld0Rpc3RhbmNlOyBzZW5kWkNodW5rIDw9IHZpZXdEaXN0YW5jZTsgc2VuZFpDaHVuaysrKSB7XG4gICAgICAgICAgICAgICAgaWYgKHNlbmRYQ2h1bmsgKiBzZW5kWENodW5rICsgc2VuZFpDaHVuayAqIHNlbmRaQ2h1bmsgPiB2aWV3RGlzdGFuY2UgKiB2aWV3RGlzdGFuY2UpIGNvbnRpbnVlOyAvLyBlYXJseSBleGl0IGlmIGNodW5rIGlzIG91dHNpZGUgb2YgdmlldyBkaXN0YW5jZVxuICAgICAgICAgICAgICAgIGNvbnN0IG5ld0NodW5rID0gW2N1cnJlbnRYQ2h1bmsgKyBzZW5kWENodW5rLCBjdXJyZW50WkNodW5rICsgc2VuZFpDaHVua107XG4gICAgICAgICAgICAgICAgY29uc3QgaGFzaCA9IENodW5rLnBhY2tYWihuZXdDaHVua1swXSEsIG5ld0NodW5rWzFdISk7XG5cbiAgICAgICAgICAgICAgICBpZiAoZm9yY2VSZXNlbmQpIHtcbiAgICAgICAgICAgICAgICAgICAgY2h1bmtzVG9TZW5kSGVhcC5wdXNoKG5ld0NodW5rKTtcbiAgICAgICAgICAgICAgICB9IGVsc2UgaWYgKCF0aGlzLmxvYWRlZENodW5rcy5oYXMoaGFzaCkgJiYgIXRoaXMubG9hZGluZ0NodW5rcy5oYXMoaGFzaCkpIHtcbiAgICAgICAgICAgICAgICAgICAgY2h1bmtzVG9TZW5kSGVhcC5wdXNoKG5ld0NodW5rKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB3aGlsZSAoY2h1bmtzVG9TZW5kSGVhcC5zaXplKCkgPiAwKSB7XG4gICAgICAgICAgICBjb25zdCBjbG9zZXN0Q2h1bmsgPSBjaHVua3NUb1NlbmRIZWFwLnBvcCgpITtcbiAgICAgICAgICAgIGNvbnN0IGhhc2ggPSBDaHVuay5wYWNrWFooY2xvc2VzdENodW5rWzBdISwgY2xvc2VzdENodW5rWzFdISk7XG4gICAgICAgICAgICBpZiAoZm9yY2VSZXNlbmQpIHtcbiAgICAgICAgICAgICAgICBpZiAoIXRoaXMubG9hZGVkQ2h1bmtzLmhhcyhoYXNoKSAmJiAhdGhpcy5sb2FkaW5nQ2h1bmtzLmhhcyhoYXNoKSkge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLmxvYWRpbmdDaHVua3MuYWRkKGhhc2gpO1xuICAgICAgICAgICAgICAgICAgICBhd2FpdCB0aGlzLnJlcXVlc3RDaHVuayhjbG9zZXN0Q2h1bmtbMF0hLCBjbG9zZXN0Q2h1bmtbMV0hKTtcbiAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBsb2FkZWRDaHVuayA9IGF3YWl0IHRoaXMucGxheWVyLmdldFdvcmxkKCkuZ2V0Q2h1bmsoY2xvc2VzdENodW5rWzBdISwgY2xvc2VzdENodW5rWzFdISk7XG4gICAgICAgICAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VuZENodW5rKGxvYWRlZENodW5rKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5hZGQoaGFzaCk7XG4gICAgICAgICAgICAgICAgYXdhaXQgdGhpcy5yZXF1ZXN0Q2h1bmsoY2xvc2VzdENodW5rWzBdISwgY2xvc2VzdENodW5rWzFdISk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICBsZXQgdW5sb2FkZWQgPSBmYWxzZTtcblxuICAgICAgICBmb3IgKGNvbnN0IGhhc2ggb2YgdGhpcy5sb2FkZWRDaHVua3MpIHtcbiAgICAgICAgICAgIGNvbnN0IFt4LCB6XSA9IENodW5rLnVucGFja1haKGhhc2gpO1xuXG4gICAgICAgICAgICBpZiAoTWF0aC5hYnMoeCEgLSBjdXJyZW50WENodW5rKSA+IHZpZXdEaXN0YW5jZSB8fCBNYXRoLmFicyh6ISAtIGN1cnJlbnRaQ2h1bmspID4gdmlld0Rpc3RhbmNlKSB7XG4gICAgICAgICAgICAgICAgdW5sb2FkZWQgPSB0cnVlO1xuICAgICAgICAgICAgICAgIHRoaXMubG9hZGVkQ2h1bmtzLmRlbGV0ZShoYXNoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGZvciAoY29uc3QgaGFzaCBvZiB0aGlzLmxvYWRpbmdDaHVua3MpIHtcbiAgICAgICAgICAgIGNvbnN0IFt4LCB6XSA9IENodW5rLnVucGFja1haKGhhc2gpO1xuXG4gICAgICAgICAgICBpZiAoTWF0aC5hYnMoeCEgLSBjdXJyZW50WENodW5rKSA+IHZpZXdEaXN0YW5jZSB8fCBNYXRoLmFicyh6ISAtIGN1cnJlbnRaQ2h1bmspID4gdmlld0Rpc3RhbmNlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5sb2FkaW5nQ2h1bmtzLmRlbGV0ZShoYXNoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdW5sb2FkZWQgJiYgdGhpcy5jaHVua1NlbmRRdWV1ZS5sZW5ndGggIT09IDApIHtcbiAgICAgICAgICAgIGF3YWl0IHRoaXMuc2VuZE5ldHdvcmtDaHVua1B1Ymxpc2hlcihkaXN0ID8/IHZpZXdEaXN0YW5jZSwgW10pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHJlcXVlc3RDaHVuayh4OiBudW1iZXIsIHo6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBjaHVuayA9IGF3YWl0IHRoaXMucGxheWVyLmdldFdvcmxkKCkuZ2V0Q2h1bmsoeCwgeik7XG4gICAgICAgIHRoaXMuY2h1bmtTZW5kUXVldWUucHVzaChjaHVuayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQ2xlYXIgdGhlIGN1cnJlbnRseSBsb2FkZWQgYW5kIGxvYWRpbmcgY2h1bmtzLlxuICAgICAqXG4gICAgICogQHJlbWFya3NcbiAgICAgKiBVc3VhbGx5IHVzZWQgZm9yIGNoYW5naW5nIGRpbWVuc2lvbiwgd29ybGQsIGV0Yy5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgY2xlYXJDaHVua3MoKSB7XG4gICAgICAgIHRoaXMubG9hZGVkQ2h1bmtzLmNsZWFyKCk7XG4gICAgICAgIHRoaXMubG9hZGluZ0NodW5rcy5jbGVhcigpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIEBUT0RPOiBJbXBsZW1lbnQgdGhpcy5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEludmVudG9yeSgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGsgPSBuZXcgSW52ZW50b3J5Q29udGVudFBhY2tldCgpO1xuICAgICAgICBway5pdGVtcyA9IHRoaXMucGxheWVyLmdldEludmVudG9yeSgpLmdldEl0ZW1zKHRydWUpO1xuICAgICAgICBway53aW5kb3dJZCA9IFdpbmRvd0lkcy5JTlZFTlRPUlk7IC8vIEludmVudG9yeSB3aW5kb3dcbiAgICAgICAgLy9hd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgICAgICAvL2F3YWl0IHRoaXMuc2VuZEhhbmRJdGVtKHRoaXMucGxheWVyLmdldEludmVudG9yeSgpLmdldEl0ZW1JbkhhbmQoKSk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRDcmVhdGl2ZUNvbnRlbnRzKGVtcHR5ID0gZmFsc2UpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGsgPSBuZXcgQ3JlYXRpdmVDb250ZW50UGFja2V0KCk7XG4gICAgICAgIGlmIChlbXB0eSkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGVudHJpZXMgPSBbXG4gICAgICAgICAgICAuLi50aGlzLnBsYXllci5nZXRTZXJ2ZXIoKS5nZXRCbG9ja01hbmFnZXIoKS5nZXRCbG9ja3MoKSxcbiAgICAgICAgICAgIC4uLnRoaXMucGxheWVyLmdldFNlcnZlcigpLmdldEl0ZW1NYW5hZ2VyKCkuZ2V0SXRlbXMoKVxuICAgICAgICBdO1xuXG4gICAgICAgIC8vIFNvcnQgYmFzZWQgb24gUG1tUCBCZWRyb2NrLWRhdGFcbiAgICAgICAgQ3JlYXRpdmVJdGVtcy5mb3JFYWNoKChpdGVtOiBhbnkpID0+IHtcbiAgICAgICAgICAgIHBrLml0ZW1zLnB1c2goXG4gICAgICAgICAgICAgICAgLi4uZW50cmllc1xuICAgICAgICAgICAgICAgICAgICAuZmlsdGVyKChlbnRyeTogYW55KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gZW50cnkubWV0YSA9PT0gKGl0ZW0uZGFtYWdlID8/IDApICYmIGVudHJ5LmdldElkKCkgPT09IGl0ZW0uaWQ7XG4gICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgIC5tYXAoXG4gICAgICAgICAgICAgICAgICAgICAgICAoZW50cnkpID0+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IEl0ZW0oe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZDogZW50cnkuZ2V0SWQoKSxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZTogZW50cnkuZ2V0TmFtZSgpLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhOiBlbnRyeS5tZXRhXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBrKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBTZXRzIHRoZSBpdGVtIGluIHRoZSBwbGF5ZXIgaGFuZC5cbiAgICAgKiBAcGFyYW0ge0l0ZW19IGl0ZW0gLSBUaGUgZW50aXR5LlxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZW5kSGFuZEl0ZW0oaXRlbTogSXRlbSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwayA9IG5ldyBNb2JFcXVpcG1lbnRQYWNrZXQoKTtcbiAgICAgICAgcGsucnVudGltZUVudGl0eUlkID0gdGhpcy5wbGF5ZXIuZ2V0UnVudGltZUlkKCk7XG4gICAgICAgIHBrLml0ZW0gPSBpdGVtO1xuICAgICAgICBway5pbnZlbnRvcnlTbG90ID0gdGhpcy5wbGF5ZXIuZ2V0SW52ZW50b3J5KCkuZ2V0SGFuZFNsb3RJbmRleCgpO1xuICAgICAgICBway5ob3RiYXJTbG90ID0gdGhpcy5wbGF5ZXIuZ2V0SW52ZW50b3J5KCkuZ2V0SGFuZFNsb3RJbmRleCgpO1xuICAgICAgICBway53aW5kb3dJZCA9IDA7IC8vIEludmVudG9yeSBJRFxuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbmQgdGhlIGN1cnJlbnQgdGljayB0byBhIGNsaWVudC5cbiAgICAgKiBAcGFyYW0ge251bWJlcn0gdGljayAtIFRoZSB0aWNrXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHNlbmRUaW1lKHRpY2s6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwayA9IG5ldyBTZXRUaW1lUGFja2V0KCk7XG4gICAgICAgIHBrLnRpbWUgPSB0aWNrO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGspO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFNlbmQgZ2FtZW1vZGUgdG8gYSBjbGllbnQuXG4gICAgICogQHBhcmFtIHtHYW1ldHlwZX0gZ2FtZW1vZGUgLSB0aGUgbnVtZXJpYyBnYW1lbW9kZSBJRC5cbiAgICAgKi9cbiAgICBwdWJsaWMgYXN5bmMgc2VuZEdhbWVtb2RlKGdhbWVtb2RlPzogR2FtZXR5cGUpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGFja2V0ID0gbmV3IFNldFBsYXllckdhbWV0eXBlUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5nYW1ldHlwZSA9IGdhbWVtb2RlID8/IHRoaXMucGxheWVyLmdhbWVtb2RlO1xuICAgICAgICBhd2FpdCB0aGlzLmNvbm5lY3Rpb24uc2VuZERhdGFQYWNrZXQocGFja2V0KTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXN5bmMgc2VuZE5ldHdvcmtDaHVua1B1Ymxpc2hlcihkaXN0YW5jZTogbnVtYmVyLCBzYXZlZENodW5rczogQ2h1bmtDb29yZFtdKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHBrID0gbmV3IE5ldHdvcmtDaHVua1B1Ymxpc2hlclVwZGF0ZVBhY2tldCgpO1xuICAgICAgICBway5wb3NpdGlvbiA9IEJsb2NrUG9zaXRpb24uZnJvbVZlY3RvcjModGhpcy5wbGF5ZXIuZ2V0UG9zaXRpb24oKSk7XG4gICAgICAgIHBrLnJhZGl1cyA9IGRpc3RhbmNlIDw8IDQ7XG4gICAgICAgIHBrLnNhdmVkQ2h1bmtzID0gc2F2ZWRDaHVua3M7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRBdmFpbGFibGVDb21tYW5kcygpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgY29uc3QgcGxheWVyRW51bSA9IG5ldyBDb21tYW5kRW51bSgpO1xuICAgICAgICBwbGF5ZXJFbnVtLnNvZnQgPSB0cnVlO1xuICAgICAgICBwbGF5ZXJFbnVtLm5hbWUgPSAnUGxheWVyJztcbiAgICAgICAgcGxheWVyRW51bS52YWx1ZXMgPSB0aGlzLnBsYXllclxuICAgICAgICAgICAgLmdldFNlcnZlcigpXG4gICAgICAgICAgICAuZ2V0U2Vzc2lvbk1hbmFnZXIoKVxuICAgICAgICAgICAgLmdldEFsbFBsYXllcnMoKVxuICAgICAgICAgICAgLm1hcCgocGxheWVyKSA9PiBwbGF5ZXIuZ2V0TmFtZSgpKTtcblxuICAgICAgICBjb25zdCBwayA9IG5ldyBBdmFpbGFibGVDb21tYW5kc1BhY2tldCgpO1xuICAgICAgICBway5zb2Z0RW51bXMgPSBbcGxheWVyRW51bV07XG4gICAgICAgIHRoaXMuc2VydmVyXG4gICAgICAgICAgICAuZ2V0Q29tbWFuZE1hbmFnZXIoKVxuICAgICAgICAgICAgLmdldENvbW1hbmRzTGlzdCgpXG4gICAgICAgICAgICAuZm9yRWFjaCgoY29tbWFuZCkgPT4ge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNvbW1hbmRDbGFzcyA9IEFycmF5LmZyb20odGhpcy5zZXJ2ZXIuZ2V0Q29tbWFuZE1hbmFnZXIoKS5nZXRDb21tYW5kcygpLnZhbHVlcygpKS5maW5kKFxuICAgICAgICAgICAgICAgICAgICAoY21kKSA9PiBjbWQubmFtZSA9PT0gY29tbWFuZFswXVxuICAgICAgICAgICAgICAgICk7XG5cbiAgICAgICAgICAgICAgICBpZiAoIWNvbW1hbmRDbGFzcykge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnBsYXllclxuICAgICAgICAgICAgICAgICAgICAgICAgLmdldFNlcnZlcigpXG4gICAgICAgICAgICAgICAgICAgICAgICAuZ2V0TG9nZ2VyKClcbiAgICAgICAgICAgICAgICAgICAgICAgIC53YXJuKGBDYW4ndCBmaW5kIGNvcnJlc3BvbmRpbmcgY29tbWFuZCBjbGFzcyBmb3IgXCIke2NvbW1hbmRbMF19XCJgKTtcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIGlmICghdGhpcy5wbGF5ZXIuZ2V0U2VydmVyKCkuZ2V0UGVybWlzc2lvbk1hbmFnZXIoKS5jYW4odGhpcy5wbGF5ZXIpLmV4ZWN1dGUoY29tbWFuZENsYXNzLnBlcm1pc3Npb24pKVxuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG5cbiAgICAgICAgICAgICAgICBjb25zdCBjbWQgPSBuZXcgQ29tbWFuZERhdGEoKTtcbiAgICAgICAgICAgICAgICBjbWQuY29tbWFuZE5hbWUgPSBjb21tYW5kWzBdO1xuICAgICAgICAgICAgICAgIGNtZC5jb21tYW5kRGVzY3JpcHRpb24gPSBjb21tYW5kQ2xhc3MuZGVzY3JpcHRpb247XG4gICAgICAgICAgICAgICAgaWYgKGNvbW1hbmRDbGFzcy5hbGlhc2VzIS5sZW5ndGggPiAwKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGNtZEFsaWFzZXMgPSBuZXcgQ29tbWFuZEVudW0oKTtcbiAgICAgICAgICAgICAgICAgICAgY21kQWxpYXNlcy5uYW1lID0gYCR7Y29tbWFuZFswXX1BbGlhc2VzYDtcbiAgICAgICAgICAgICAgICAgICAgY21kQWxpYXNlcy52YWx1ZXMgPSBjb21tYW5kQ2xhc3MuYWxpYXNlcyEuY29uY2F0KGNvbW1hbmRbMF0pO1xuICAgICAgICAgICAgICAgICAgICBjbWQuYWxpYXNlcyA9IGNtZEFsaWFzZXM7XG4gICAgICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAgICAgY29tbWFuZFsyXS5mb3JFYWNoKChhcmcsIGluZGV4KSA9PiB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhcmFtZXRlcnMgPSBhcmdcbiAgICAgICAgICAgICAgICAgICAgICAgIC5tYXAoKHBhcmFtZXRlcjogQ29tbWFuZEFyZ3VtZW50IHwgbnVsbCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghcGFyYW1ldGVyIHx8ICEocGFyYW1ldGVyIGFzIGFueSk/LmdldFBhcmFtZXRlcnMpIHJldHVybiBbXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHBhcmFtZXRlcnMgPSBwYXJhbWV0ZXIuZ2V0UGFyYW1ldGVycyh0aGlzLnNlcnZlcik7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhcmFtZXRlcnMpIHJldHVybiBBcnJheS5mcm9tKHBhcmFtZXRlcnMudmFsdWVzKCkpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhcmFtZXRlciBpbnN0YW5jZW9mIENvbW1hbmRBcmd1bWVudEVudGl0eSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBDb21tYW5kUGFyYW1ldGVyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbU5hbWU6ICd0YXJnZXQnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmFtVHlwZTogQ29tbWFuZFBhcmFtZXRlclR5cGUuVGFyZ2V0XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChwYXJhbWV0ZXIgaW5zdGFuY2VvZiBDb21tYW5kQXJndW1lbnRHYW1lbW9kZSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBDb21tYW5kUGFyYW1ldGVyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbU5hbWU6ICdnYW1lbW9kZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1UeXBlOiBDb21tYW5kUGFyYW1ldGVyVHlwZS5TdHJpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhcmFtZXRlci5jb25zdHJ1Y3Rvci5uYW1lID09PSAnU3RyaW5nQXJndW1lbnRUeXBlJylcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5ldyBDb21tYW5kUGFyYW1ldGVyKHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbU5hbWU6ICd2YWx1ZScsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1UeXBlOiBDb21tYW5kUGFyYW1ldGVyVHlwZS5TdHJpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH0pXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIF07XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHBhcmFtZXRlci5jb25zdHJ1Y3Rvci5uYW1lID09PSAnSW50ZWdlckFyZ3VtZW50VHlwZScpXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBbXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXcgQ29tbWFuZFBhcmFtZXRlcih7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1OYW1lOiAnbnVtYmVyJyxcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXJhbVR5cGU6IENvbW1hbmRQYXJhbWV0ZXJUeXBlLkludFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2VydmVyLmdldExvZ2dlcigpLndhcm4oYEludmFsaWQgcGFyYW1ldGVyICR7cGFyYW1ldGVyLmNvbnN0cnVjdG9yLm5hbWV9YCk7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV3IENvbW1hbmRQYXJhbWV0ZXIoe1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1OYW1lOiAndmFsdWUnLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1UeXBlOiBDb21tYW5kUGFyYW1ldGVyVHlwZS5TdHJpbmdcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBdO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgICAgICAgICAgIC5mbGF0KCk7XG4gICAgICAgICAgICAgICAgICAgIGNtZC5vdmVybG9hZHNbaW5kZXhdID0gcGFyYW1ldGVycztcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgICAgICBway5jb21tYW5kRGF0YS5wdXNoKGNtZCk7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICBhd2FpdCB0aGlzLmdldENvbm5lY3Rpb24oKS5zZW5kRGF0YVBhY2tldChwayk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogU2V0IHRoZSBjbGllbnQncyBtYXhpbXVtIHZpZXcgZGlzdGFuY2UuXG4gICAgICpcbiAgICAgKiBAcGFyYW0gZGlzdGFuY2UgLSBUaGUgdmlldyBkaXN0YW5jZVxuICAgICAqL1xuICAgIHB1YmxpYyBhc3luYyBzZXRWaWV3RGlzdGFuY2UoZGlzdGFuY2U6IG51bWJlcik6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgQ2h1bmtSYWRpdXNVcGRhdGVkUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5yYWRpdXMgPSB0aGlzLnBsYXllci52aWV3RGlzdGFuY2UgPSBkaXN0YW5jZTtcbiAgICAgICAgYXdhaXQgdGhpcy5jb25uZWN0aW9uLnNlbmREYXRhUGFja2V0KHBhY2tldCk7XG4gICAgfVxuXG4gICAgcHVibGljIGFzeW5jIHNlbmRBdHRyaWJ1dGVzKGF0dHJpYnV0ZXM/OiBBdHRyaWJ1dGVzKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIGNvbnN0IHZhbHVlID0gYXR0cmlidXRlcz8uZ2V0QXR0cmlidXRlcygpID8/IHRoaXMucGxheWVyLmF0dHJpYnV0ZXMuZ2V0QXR0cmlidXRlcygpO1xuXG4gICAgICAgIGNvbnN0IHBhY2tldCA9IG5ldyBVcGRhdGVBdHRyaWJ1dGVzUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5ydW50aW1lRW50aXR5SWQgPSB0aGlzLnBsYXllci5nZXRSdW50aW1lSWQoKTtcbiAgICAgICAgcGFja2V0LmF0dHJpYnV0ZXMgPSB2YWx1ZS5sZW5ndGggPiAwID8gdmFsdWUgOiB0aGlzLnBsYXllci5hdHRyaWJ1dGVzLmdldERlZmF1bHRzKCk7XG4gICAgICAgIHBhY2tldC50aWNrID0gQmlnSW50KHRoaXMucGxheWVyLmdldFNlcnZlcigpLmdldFRpY2soKSk7XG4gICAgICAgIGF3YWl0IHRoaXMuY29ubmVjdGlvbi5zZW5kRGF0YVBhY2tldChwYWNrZXQpO1xuICAgIH1cblxuICAgIHB1YmxpYyBhc3luYyBzZW5kTWV0YWRhdGEobWV0YWRhdGE/OiBNZXRhZGF0YSk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBwYWNrZXQgPSBuZXcgU2V0QWN0b3JEYXRhUGFja2V0KCk7XG4gICAgICAgIHBhY2tldC5ydW50aW1lRW50aXR5SWQgPSB0aGlzLnBsYXllci5nZXRSdW50aW1lSWQoKTtcbiAgICAgICAgcGFja2V0Lm1ldGFkYXRhID0gbWV0YWRhdGEgPz8gdGhpcy5wbGF5ZXIubWV0YWRhdGE7XG4gICAgICAgIHBhY2tldC50aWNrID0gQmlnSW50KHRoaXMucGxheWVyL