UNPKG

@pazznetwork/ngx-chat

Version:

[![Build status](https://api.travis-ci.com/pazznetwork/ngx-chat.svg?branch=master)](https://travis-ci.com/pazznetwork/ngx-chat) [![Coverage](https://coveralls.io/repos/github/pazznetwork/ngx-chat/badge.svg?branch=master)](https://coveralls.io/github/pazzn

244 lines 54.8 kB
import { Component, Inject, Input, Optional, ViewChild, ViewChildren, } from '@angular/core'; import { Subject } from 'rxjs'; import { debounceTime, filter, takeUntil } from 'rxjs/operators'; import { Direction } from '../../core/message'; import { BlockPlugin } from '../../services/adapters/xmpp/plugins/block.plugin'; import { MessageArchivePlugin } from '../../services/adapters/xmpp/plugins/message-archive.plugin'; import { CHAT_SERVICE_TOKEN } from '../../services/chat-service'; import { REPORT_USER_INJECTION_TOKEN } from '../../services/report-user-service'; import { ChatMessageComponent } from '../chat-message/chat-message.component'; import { MultiUserChatPlugin } from '../../services/adapters/xmpp/plugins/multi-user-chat/multi-user-chat.plugin'; import * as i0 from "@angular/core"; import * as i1 from "../../services/chat-list-state.service"; import * as i2 from "../../services/chat-message-list-registry.service"; import * as i3 from "../../services/contact-factory.service"; import * as i4 from "@angular/common"; import * as i5 from "../chat-message/chat-message.component"; import * as i6 from "../chat-message-simple/chat-message-simple.component"; import * as i7 from "../../directives/intersection-observer.directive"; var SubscriptionAction; (function (SubscriptionAction) { SubscriptionAction[SubscriptionAction["PENDING_REQUEST"] = 0] = "PENDING_REQUEST"; SubscriptionAction[SubscriptionAction["SHOW_BLOCK_ACTIONS"] = 1] = "SHOW_BLOCK_ACTIONS"; SubscriptionAction[SubscriptionAction["NO_PENDING_REQUEST"] = 2] = "NO_PENDING_REQUEST"; })(SubscriptionAction || (SubscriptionAction = {})); export class ChatMessageListComponent { constructor(chatListService, chatService, chatMessageListRegistry, reportUserService, changeDetectorRef, contactFactory) { this.chatListService = chatListService; this.chatService = chatService; this.chatMessageListRegistry = chatMessageListRegistry; this.reportUserService = reportUserService; this.changeDetectorRef = changeDetectorRef; this.contactFactory = contactFactory; this.Direction = Direction; this.SubscriptionAction = SubscriptionAction; this.subscriptionAction = SubscriptionAction.NO_PENDING_REQUEST; this.onTop$ = new Subject(); this.ngDestroy = new Subject(); this.isAtBottom = true; this.bottomLeftAt = 0; this.oldestVisibleMessageBeforeLoading = null; this.pendingRoomInvite = null; this.blockPlugin = this.chatService.getPlugin(BlockPlugin); } async ngOnInit() { this.onTop$ .pipe(filter(event => event.isIntersecting), debounceTime(1000)) .subscribe(() => this.loadOlderMessagesBeforeViewport()); if (this.recipient.recipientType === 'contact') { this.recipient.pendingIn$ .pipe(filter(pendingIn => pendingIn === true), takeUntil(this.ngDestroy)) .subscribe(() => { this.subscriptionAction = SubscriptionAction.PENDING_REQUEST; this.scheduleScrollToLastMessage(); }); this.recipient.pendingRoomInvite$ .pipe(filter(invite => invite != null), takeUntil(this.ngDestroy)) .subscribe((invite) => this.pendingRoomInvite = invite); } this.chatMessageListRegistry.incrementOpenWindowCount(this.recipient); } async ngAfterViewInit() { this.chatMessageViewChildrenList.changes .subscribe(() => { if (this.oldestVisibleMessageBeforeLoading) { this.scrollToMessage(this.oldestVisibleMessageBeforeLoading); } this.oldestVisibleMessageBeforeLoading = null; }); const messages$ = this.recipient.messages$; messages$ .pipe(debounceTime(10), filter(() => this.isNearBottom()), takeUntil(this.ngDestroy)) .subscribe((_) => this.scheduleScrollToLastMessage()); if (this.recipient.messages.length < 10) { await this.loadMessages(); // in case insufficient old messages are displayed } this.scheduleScrollToLastMessage(); } ngOnChanges(changes) { const contact = changes.contact; if (contact && contact.previousValue && contact.currentValue) { this.chatMessageListRegistry.decrementOpenWindowCount(contact.previousValue); this.chatMessageListRegistry.incrementOpenWindowCount(contact.currentValue); } if (contact && contact.currentValue) { this.scheduleScrollToLastMessage(); } } ngOnDestroy() { this.ngDestroy.next(); this.chatMessageListRegistry.decrementOpenWindowCount(this.recipient); } acceptSubscriptionRequest(event) { event.preventDefault(); if (this.subscriptionAction === SubscriptionAction.PENDING_REQUEST) { this.chatService.addContact(this.recipient.jidBare.toString()); this.subscriptionAction = SubscriptionAction.NO_PENDING_REQUEST; this.scheduleScrollToLastMessage(); } } denySubscriptionRequest(event) { event.preventDefault(); if (this.subscriptionAction === SubscriptionAction.PENDING_REQUEST) { this.chatService.removeContact(this.recipient.jidBare.toString()); this.subscriptionAction = SubscriptionAction.SHOW_BLOCK_ACTIONS; this.scheduleScrollToLastMessage(); } } scheduleScrollToLastMessage() { setTimeout(() => this.scrollToLastMessage(), 0); } blockContact($event) { $event.preventDefault(); this.blockPlugin.blockJid(this.recipient.jidBare.toString()); this.chatListService.closeChat(this.recipient); this.subscriptionAction = SubscriptionAction.NO_PENDING_REQUEST; } blockContactAndReport($event) { if (this.recipient.recipientType !== 'contact') { return; } $event.preventDefault(); this.reportUserService.reportUser(this.recipient); this.blockContact($event); } dismissBlockOptions($event) { $event.preventDefault(); this.subscriptionAction = SubscriptionAction.NO_PENDING_REQUEST; } subscriptionActionShown() { if (this.recipient.recipientType !== 'contact') { return false; } return this.subscriptionAction === SubscriptionAction.PENDING_REQUEST || (this.blockPlugin.supportsBlock$.getValue() === true && this.subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS); } async loadOlderMessagesBeforeViewport() { if (this.isLoadingHistory() || this.isNearBottom()) { return; } try { this.oldestVisibleMessageBeforeLoading = this.recipient.oldestMessage; await this.loadMessages(); } catch (e) { this.oldestVisibleMessageBeforeLoading = null; } } onBottom(event) { this.isAtBottom = event.isIntersecting; if (event.isIntersecting) { this.isAtBottom = true; } else { this.isAtBottom = false; this.bottomLeftAt = Date.now(); } } getOrCreateContactWithFullJid(message) { if (this.recipient.recipientType === 'contact') { // this is not a multi user chat, just use recipient as contact return this.recipient; } const roomMessage = message; let matchingContact = this.chatService.contacts$.getValue().find(contact => contact.jidFull.equals(roomMessage.from)); if (!matchingContact) { matchingContact = this.contactFactory.createContact(roomMessage.from.toString(), roomMessage.from.resource); this.chatService.contacts$.next([matchingContact].concat(this.chatService.contacts$.getValue())); } return matchingContact; } showPendingRoomInvite() { if (this.recipient.recipientType !== 'contact') { return false; } return this.pendingRoomInvite; } async acceptRoomInvite(event) { event.preventDefault(); await this.chatService.getPlugin(MultiUserChatPlugin).joinRoom(this.pendingRoomInvite.roomJid); this.recipient.pendingRoomInvite$.next(null); this.pendingRoomInvite = null; } async declineRoomInvite(event) { event.preventDefault(); await this.chatService.getPlugin(MultiUserChatPlugin).declineRoomInvite(this.pendingRoomInvite.roomJid); this.recipient.pendingRoomInvite$.next(null); this.pendingRoomInvite = null; this.chatService.removeContact(this.recipient.jidBare.toString()); } scrollToLastMessage() { if (this.chatMessageAreaElement) { this.chatMessageAreaElement.nativeElement.scrollTop = this.chatMessageAreaElement.nativeElement.scrollHeight; this.isAtBottom = true; // in some browsers the intersection observer does not emit when scrolling programmatically } } scrollToMessage(message) { if (this.chatMessageAreaElement) { const htmlIdAttribute = 'message-' + message.id; const messageElement = document.getElementById(htmlIdAttribute); messageElement.scrollIntoView(false); } } async loadMessages() { try { // improve performance when loading lots of old messages this.changeDetectorRef.detach(); await this.chatService.getPlugin(MessageArchivePlugin).loadMostRecentUnloadedMessages(this.recipient); } finally { this.changeDetectorRef.reattach(); } } isNearBottom() { return this.isAtBottom || Date.now() - this.bottomLeftAt < 1000; } isLoadingHistory() { return !!this.oldestVisibleMessageBeforeLoading; } } ChatMessageListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ChatMessageListComponent, deps: [{ token: i1.ChatListStateService }, { token: CHAT_SERVICE_TOKEN }, { token: i2.ChatMessageListRegistryService }, { token: REPORT_USER_INJECTION_TOKEN, optional: true }, { token: i0.ChangeDetectorRef }, { token: i3.ContactFactoryService }], target: i0.ɵɵFactoryTarget.Component }); ChatMessageListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: ChatMessageListComponent, selector: "ngx-chat-message-list", inputs: { recipient: "recipient", showAvatars: "showAvatars" }, viewQueries: [{ propertyName: "chatMessageAreaElement", first: true, predicate: ["messageArea"], descendants: true }, { propertyName: "chatMessageViewChildrenList", predicate: ChatMessageComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"chat-messages\" #messageArea>\n\n <div class=\"chat-messages-start\" (ngxChatIntersectionObserver)=\"onTop$.next($event)\"></div>\n\n <ng-container *ngFor=\"let dateMessagesGroup of recipient.dateMessagesGroups\">\n <div class=\"chat-messages-date-group\">{{dateMessagesGroup.date | date:chatService.translations.dateFormat:undefined:chatService.translations.locale}}</div>\n <ngx-chat-message\n *ngFor=\"let message of dateMessagesGroup.messages\"\n [id]=\"'message-' + message.id\"\n [class.chat-message--in]=\"message.direction === Direction.in\"\n [class.chat-message--out]=\"message.direction === Direction.out\"\n [contact]=\"getOrCreateContactWithFullJid(message)\"\n [nick]=\"message.direction === Direction.in ? getOrCreateContactWithFullJid(message).name : ''\"\n [avatar]=\"getOrCreateContactWithFullJid(message).avatar\"\n [message]=\"message\"\n [showAvatars]=\"showAvatars\">\n </ngx-chat-message>\n </ng-container>\n\n <div class=\"chat-messages-empty\" *ngIf=\"recipient.messages.length === 0\">\n {{chatService.translations.noMessages}}\n </div>\n\n <ngx-chat-message-simple\n [direction]=\"Direction.in\"\n [avatar]=\"undefined\"\n [footerHidden]=\"true\"\n *ngIf=\"showPendingRoomInvite()\"\n >\n <ul class=\"chat-presence-subscription-actions\">\n <li>\n <a (click)=\"acceptRoomInvite($event)\" href=\"#\">Join</a>\n </li>\n <li>\n <a (click)=\"declineRoomInvite($event)\" href=\"#\">Decline</a>\n </li>\n </ul>\n </ngx-chat-message-simple>\n\n <ngx-chat-message-simple\n [direction]=\"Direction.in\"\n [avatar]=\"showAvatars ? recipient.avatar : undefined\"\n [footerHidden]=\"true\"\n *ngIf=\"subscriptionActionShown()\"\n >\n <span>\n {{chatService.translations.subscriptionRequestMessage}}\n </span>\n <ul class=\"chat-presence-subscription-actions\">\n <li>\n <span class=\"action-disabled\"\n *ngIf=\"subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\"\n >{{chatService.translations.acceptSubscriptionRequest}}</span>\n\n <a *ngIf=\"subscriptionAction === SubscriptionAction.PENDING_REQUEST\"\n (click)=\"acceptSubscriptionRequest($event)\"\n href=\"#\"\n >{{chatService.translations.acceptSubscriptionRequest}}</a>\n </li>\n\n <li>\n <span class=\"action-disabled\"\n *ngIf=\"subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\"\n >{{chatService.translations.denySubscriptionRequest}}</span>\n\n <a *ngIf=\"subscriptionAction === SubscriptionAction.PENDING_REQUEST\"\n (click)=\"denySubscriptionRequest($event)\"\n href=\"#\"\n >{{chatService.translations.denySubscriptionRequest}}</a>\n </li>\n </ul>\n <ul class=\"deny-actions\"\n *ngIf=\"(blockPlugin.supportsBlock$ | async) === true && subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\">\n <li>\n <a (click)=\"blockContact($event)\" href=\"#\">{{chatService.translations.block}}</a>\n </li>\n <li *ngIf=\"reportUserService\">\n <a (click)=\"blockContactAndReport($event)\" href=\"#\">{{chatService.translations.blockAndReport}}</a>\n </li>\n <li>\n <a (click)=\"dismissBlockOptions($event)\" href=\"#\">{{chatService.translations.dismiss}}</a>\n </li>\n </ul>\n </ngx-chat-message-simple>\n\n <div class=\"chat-messages-end\" (ngxChatIntersectionObserver)=\"onBottom($event)\"></div>\n\n</div>\n", styles: [":host.chat-message--out{align-self:flex-end}:host.chat-message--in{align-self:flex-start}.chat-messages{display:flex;flex-direction:column;min-height:10em;max-height:20em;overflow-y:scroll}.chat-messages-date-group{font-size:.7em;font-style:italic;margin:.5em 0;text-align:center}ngx-chat-message,ngx-chat-message-simple{max-width:76%;align-self:flex-start;animation-duration:1.5s;animation-timing-function:cubic-bezier(.16,1,.3,1)}ngx-chat-message.chat-message--in,ngx-chat-message-simple.chat-message--in{animation-name:ngx-chat-message-in}ngx-chat-message.chat-message--out,ngx-chat-message-simple.chat-message--out{animation-name:ngx-chat-message-out}.chat-messages-empty{text-align:center;font-size:1.5em;color:#999;margin-top:1em;margin-bottom:1em}.chat-presence-subscription-actions,.deny-actions{list-style:none;padding:0;margin:1em 0 0}.chat-presence-subscription-actions a,.deny-actions a,.chat-presence-subscription-actions a:visited,.deny-actions a:visited{color:#198cff}.deny-actions{margin-top:1em}.action-disabled{color:#999}.chat-messages-start{min-height:5px;height:5px;margin-top:5px}.chat-messages-end{min-height:5px;height:5px;margin-bottom:5px}\n"], dependencies: [{ kind: "directive", type: i4.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i4.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i5.ChatMessageComponent, selector: "ngx-chat-message", inputs: ["showAvatars", "avatar", "message", "nick", "contact", "showMessageReadState"] }, { kind: "component", type: i6.ChatMessageSimpleComponent, selector: "ngx-chat-message-simple", inputs: ["avatar", "avatarInteractive", "direction", "formattedDate", "footerHidden", "mediaLink", "isImage", "isAudio", "showImagePlaceholder", "messageState", "nick"], outputs: ["avatarClickHandler"] }, { kind: "directive", type: i7.IntersectionObserverDirective, selector: "[ngxChatIntersectionObserver]", outputs: ["ngxChatIntersectionObserver"] }, { kind: "pipe", type: i4.AsyncPipe, name: "async" }, { kind: "pipe", type: i4.DatePipe, name: "date" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: ChatMessageListComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-chat-message-list', template: "<div class=\"chat-messages\" #messageArea>\n\n <div class=\"chat-messages-start\" (ngxChatIntersectionObserver)=\"onTop$.next($event)\"></div>\n\n <ng-container *ngFor=\"let dateMessagesGroup of recipient.dateMessagesGroups\">\n <div class=\"chat-messages-date-group\">{{dateMessagesGroup.date | date:chatService.translations.dateFormat:undefined:chatService.translations.locale}}</div>\n <ngx-chat-message\n *ngFor=\"let message of dateMessagesGroup.messages\"\n [id]=\"'message-' + message.id\"\n [class.chat-message--in]=\"message.direction === Direction.in\"\n [class.chat-message--out]=\"message.direction === Direction.out\"\n [contact]=\"getOrCreateContactWithFullJid(message)\"\n [nick]=\"message.direction === Direction.in ? getOrCreateContactWithFullJid(message).name : ''\"\n [avatar]=\"getOrCreateContactWithFullJid(message).avatar\"\n [message]=\"message\"\n [showAvatars]=\"showAvatars\">\n </ngx-chat-message>\n </ng-container>\n\n <div class=\"chat-messages-empty\" *ngIf=\"recipient.messages.length === 0\">\n {{chatService.translations.noMessages}}\n </div>\n\n <ngx-chat-message-simple\n [direction]=\"Direction.in\"\n [avatar]=\"undefined\"\n [footerHidden]=\"true\"\n *ngIf=\"showPendingRoomInvite()\"\n >\n <ul class=\"chat-presence-subscription-actions\">\n <li>\n <a (click)=\"acceptRoomInvite($event)\" href=\"#\">Join</a>\n </li>\n <li>\n <a (click)=\"declineRoomInvite($event)\" href=\"#\">Decline</a>\n </li>\n </ul>\n </ngx-chat-message-simple>\n\n <ngx-chat-message-simple\n [direction]=\"Direction.in\"\n [avatar]=\"showAvatars ? recipient.avatar : undefined\"\n [footerHidden]=\"true\"\n *ngIf=\"subscriptionActionShown()\"\n >\n <span>\n {{chatService.translations.subscriptionRequestMessage}}\n </span>\n <ul class=\"chat-presence-subscription-actions\">\n <li>\n <span class=\"action-disabled\"\n *ngIf=\"subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\"\n >{{chatService.translations.acceptSubscriptionRequest}}</span>\n\n <a *ngIf=\"subscriptionAction === SubscriptionAction.PENDING_REQUEST\"\n (click)=\"acceptSubscriptionRequest($event)\"\n href=\"#\"\n >{{chatService.translations.acceptSubscriptionRequest}}</a>\n </li>\n\n <li>\n <span class=\"action-disabled\"\n *ngIf=\"subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\"\n >{{chatService.translations.denySubscriptionRequest}}</span>\n\n <a *ngIf=\"subscriptionAction === SubscriptionAction.PENDING_REQUEST\"\n (click)=\"denySubscriptionRequest($event)\"\n href=\"#\"\n >{{chatService.translations.denySubscriptionRequest}}</a>\n </li>\n </ul>\n <ul class=\"deny-actions\"\n *ngIf=\"(blockPlugin.supportsBlock$ | async) === true && subscriptionAction === SubscriptionAction.SHOW_BLOCK_ACTIONS\">\n <li>\n <a (click)=\"blockContact($event)\" href=\"#\">{{chatService.translations.block}}</a>\n </li>\n <li *ngIf=\"reportUserService\">\n <a (click)=\"blockContactAndReport($event)\" href=\"#\">{{chatService.translations.blockAndReport}}</a>\n </li>\n <li>\n <a (click)=\"dismissBlockOptions($event)\" href=\"#\">{{chatService.translations.dismiss}}</a>\n </li>\n </ul>\n </ngx-chat-message-simple>\n\n <div class=\"chat-messages-end\" (ngxChatIntersectionObserver)=\"onBottom($event)\"></div>\n\n</div>\n", styles: [":host.chat-message--out{align-self:flex-end}:host.chat-message--in{align-self:flex-start}.chat-messages{display:flex;flex-direction:column;min-height:10em;max-height:20em;overflow-y:scroll}.chat-messages-date-group{font-size:.7em;font-style:italic;margin:.5em 0;text-align:center}ngx-chat-message,ngx-chat-message-simple{max-width:76%;align-self:flex-start;animation-duration:1.5s;animation-timing-function:cubic-bezier(.16,1,.3,1)}ngx-chat-message.chat-message--in,ngx-chat-message-simple.chat-message--in{animation-name:ngx-chat-message-in}ngx-chat-message.chat-message--out,ngx-chat-message-simple.chat-message--out{animation-name:ngx-chat-message-out}.chat-messages-empty{text-align:center;font-size:1.5em;color:#999;margin-top:1em;margin-bottom:1em}.chat-presence-subscription-actions,.deny-actions{list-style:none;padding:0;margin:1em 0 0}.chat-presence-subscription-actions a,.deny-actions a,.chat-presence-subscription-actions a:visited,.deny-actions a:visited{color:#198cff}.deny-actions{margin-top:1em}.action-disabled{color:#999}.chat-messages-start{min-height:5px;height:5px;margin-top:5px}.chat-messages-end{min-height:5px;height:5px;margin-bottom:5px}\n"] }] }], ctorParameters: function () { return [{ type: i1.ChatListStateService }, { type: undefined, decorators: [{ type: Inject, args: [CHAT_SERVICE_TOKEN] }] }, { type: i2.ChatMessageListRegistryService }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [REPORT_USER_INJECTION_TOKEN] }] }, { type: i0.ChangeDetectorRef }, { type: i3.ContactFactoryService }]; }, propDecorators: { recipient: [{ type: Input }], showAvatars: [{ type: Input }], chatMessageAreaElement: [{ type: ViewChild, args: ['messageArea'] }], chatMessageViewChildrenList: [{ type: ViewChildren, args: [ChatMessageComponent] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2hhdC1tZXNzYWdlLWxpc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL2NoYXQtbWVzc2FnZS1saXN0L2NoYXQtbWVzc2FnZS1saXN0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9jaGF0LW1lc3NhZ2UtbGlzdC9jaGF0LW1lc3NhZ2UtbGlzdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBR0gsU0FBUyxFQUVULE1BQU0sRUFDTixLQUFLLEVBSUwsUUFBUSxFQUdSLFNBQVMsRUFDVCxZQUFZLEdBQ2YsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUFjLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMzQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxTQUFTLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUNqRSxPQUFPLEVBQUUsU0FBUyxFQUFXLE1BQU0sb0JBQW9CLENBQUM7QUFFeEQsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLG1EQUFtRCxDQUFDO0FBQ2hGLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLDZEQUE2RCxDQUFDO0FBR25HLE9BQU8sRUFBRSxrQkFBa0IsRUFBZSxNQUFNLDZCQUE2QixDQUFDO0FBRTlFLE9BQU8sRUFBRSwyQkFBMkIsRUFBcUIsTUFBTSxvQ0FBb0MsQ0FBQztBQUNwRyxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx3Q0FBd0MsQ0FBQztBQUU5RSxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSw2RUFBNkUsQ0FBQzs7Ozs7Ozs7O0FBR2xILElBQUssa0JBSUo7QUFKRCxXQUFLLGtCQUFrQjtJQUNuQixpRkFBZSxDQUFBO0lBQ2YsdUZBQWtCLENBQUE7SUFDbEIsdUZBQWtCLENBQUE7QUFDdEIsQ0FBQyxFQUpJLGtC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SU4sc0JBQXNCO3NCQURyQixTQUFTO3VCQUFDLGFBQWE7Z0JBSXhCLDJCQUEyQjtzQkFEMUIsWUFBWTt1QkFBQyxvQkFBb0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEVsZW1lbnRSZWYsXG4gICAgSW5qZWN0LFxuICAgIElucHV0LFxuICAgIE9uQ2hhbmdlcyxcbiAgICBPbkRlc3Ryb3ksXG4gICAgT25Jbml0LFxuICAgIE9wdGlvbmFsLFxuICAgIFF1ZXJ5TGlzdCxcbiAgICBTaW1wbGVDaGFuZ2VzLFxuICAgIFZpZXdDaGlsZCxcbiAgICBWaWV3Q2hpbGRyZW4sXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCBmaWx0ZXIsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IERpcmVjdGlvbiwgTWVzc2FnZSB9IGZyb20gJy4uLy4uL2NvcmUvbWVzc2FnZSc7XG5pbXBvcnQgeyBSZWNpcGllbnQgfSBmcm9tICcuLi8uLi9jb3JlL3JlY2lwaWVudCc7XG5pbXBvcnQgeyBCbG9ja1BsdWdpbiB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FkYXB0ZXJzL3htcHAvcGx1Z2lucy9ibG9jay5wbHVnaW4nO1xuaW1wb3J0IHsgTWVzc2FnZUFyY2hpdmVQbHVnaW4gfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hZGFwdGVycy94bXBwL3BsdWdpbnMvbWVzc2FnZS1hcmNoaXZlLnBsdWdpbic7XG5pbXBvcnQgeyBDaGF0TGlzdFN0YXRlU2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2NoYXQtbGlzdC1zdGF0ZS5zZXJ2aWNlJztcbmltcG9ydCB7IENoYXRNZXNzYWdlTGlzdFJlZ2lzdHJ5U2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2NoYXQtbWVzc2FnZS1saXN0LXJlZ2lzdHJ5LnNlcnZpY2UnO1xuaW1wb3J0IHsgQ0hBVF9TRVJWSUNFX1RPS0VOLCBDaGF0U2VydmljZSB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2NoYXQtc2VydmljZSc7XG5pbXBvcnQgeyBDb250YWN0RmFjdG9yeVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9jb250YWN0LWZhY3Rvcnkuc2VydmljZSc7XG5pbXBvcnQgeyBSRVBPUlRfVVNFUl9JTkpFQ1RJT05fVE9LRU4sIFJlcG9ydFVzZXJTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvcmVwb3J0LXVzZXItc2VydmljZSc7XG5pbXBvcnQgeyBDaGF0TWVzc2FnZUNvbXBvbmVudCB9IGZyb20gJy4uL2NoYXQtbWVzc2FnZS9jaGF0LW1lc3NhZ2UuY29tcG9uZW50JztcbmltcG9ydCB7IFJvb21NZXNzYWdlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvYWRhcHRlcnMveG1wcC9wbHVnaW5zL211bHRpLXVzZXItY2hhdC9yb29tLW1lc3NhZ2UnO1xuaW1wb3J0IHsgTXVsdGlVc2VyQ2hhdFBsdWdpbiB9IGZyb20gJy4uLy4uL3NlcnZpY2VzL2FkYXB0ZXJzL3htcHAvcGx1Z2lucy9tdWx0aS11c2VyLWNoYXQvbXVsdGktdXNlci1jaGF0LnBsdWdpbic7XG5pbXBvcnQgeyBDb250YWN0LCBJbnZpdGF0aW9uIH0gZnJvbSAnLi4vLi4vY29yZS9jb250YWN0JztcblxuZW51bSBTdWJzY3JpcHRpb25BY3Rpb24ge1xuICAgIFBFTkRJTkdfUkVRVUVTVCxcbiAgICBTSE9XX0JMT0NLX0FDVElPTlMsXG4gICAgTk9fUEVORElOR19SRVFVRVNULFxufVxuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ25neC1jaGF0LW1lc3NhZ2UtbGlzdCcsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2NoYXQtbWVzc2FnZS1saXN0LmNvbXBvbmVudC5odG1sJyxcbiAgICBzdHlsZVVybHM6IFsnLi9jaGF0LW1lc3NhZ2UtbGlzdC5jb21wb25lbnQubGVzcyddLFxufSlcbmV4cG9ydCBjbGFzcyBDaGF0TWVzc2FnZUxpc3RDb21wb25lbnQgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSwgT25DaGFuZ2VzLCBBZnRlclZpZXdJbml0IHtcblxuICAgIEBJbnB1dCgpXG4gICAgcmVjaXBpZW50OiBSZWNpcGllbnQ7XG5cbiAgICBASW5wdXQoKVxuICAgIHNob3dBdmF0YXJzOiBib29sZWFuO1xuXG4gICAgQFZpZXdDaGlsZCgnbWVzc2FnZUFyZWEnKVxuICAgIGNoYXRNZXNzYWdlQXJlYUVsZW1lbnQ6IEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+O1xuXG4gICAgQFZpZXdDaGlsZHJlbihDaGF0TWVzc2FnZUNvbXBvbmVudClcbiAgICBjaGF0TWVzc2FnZVZpZXdDaGlsZHJlbkxpc3Q6IFF1ZXJ5TGlzdDxDaGF0TWVzc2FnZUNvbXBvbmVudD47XG5cbiAgICBEaXJlY3Rpb24gPSBEaXJlY3Rpb247XG4gICAgU3Vic2NyaXB0aW9uQWN0aW9uID0gU3Vic2NyaXB0aW9uQWN0aW9uO1xuICAgIGJsb2NrUGx1Z2luOiBCbG9ja1BsdWdpbjtcbiAgICBzdWJzY3JpcHRpb25BY3Rpb24gPSBTdWJzY3JpcHRpb25BY3Rpb24uTk9fUEVORElOR19SRVFVRVNUO1xuICAgIG9uVG9wJCA9IG5ldyBTdWJqZWN0PEludGVyc2VjdGlvbk9ic2VydmVyRW50cnk+KCk7XG5cbiAgICBwcml2YXRlIG5nRGVzdHJveSA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gICAgcHJpdmF0ZSBpc0F0Qm90dG9tID0gdHJ1ZTtcbiAgICBwcml2YXRlIGJvdHRvbUxlZnRBdCA9IDA7XG4gICAgcHJpdmF0ZSBvbGRlc3RWaXNpYmxlTWVzc2FnZUJlZm9yZUxvYWRpbmc6IE1lc3NhZ2UgPSBudWxsO1xuICAgIHByaXZhdGUgcGVuZGluZ1Jvb21JbnZpdGU6IEludml0YXRpb24gfCBudWxsID0gbnVsbDtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgY2hhdExpc3RTZXJ2aWNlOiBDaGF0TGlzdFN0YXRlU2VydmljZSxcbiAgICAgICAgQEluamVjdChDSEFUX1NFUlZJQ0VfVE9LRU4pIHB1YmxpYyBjaGF0U2VydmljZTogQ2hhdFNlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgY2hhdE1lc3NhZ2VMaXN0UmVnaXN0cnk6IENoYXRNZXNzYWdlTGlzdFJlZ2lzdHJ5U2VydmljZSxcbiAgICAgICAgQE9wdGlvbmFsKCkgQEluamVjdChSRVBPUlRfVVNFUl9JTkpFQ1RJT05fVE9LRU4pIHB1YmxpYyByZXBvcnRVc2VyU2VydmljZTogUmVwb3J0VXNlclNlcnZpY2UsXG4gICAgICAgIHByaXZhdGUgY2hhbmdlRGV0ZWN0b3JSZWY6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICBwcml2YXRlIGNvbnRhY3RGYWN0b3J5OiBDb250YWN0RmFjdG9yeVNlcnZpY2UsXG4gICAgKSB7XG4gICAgICAgIHRoaXMuYmxvY2tQbHVnaW4gPSB0aGlzLmNoYXRTZXJ2aWNlLmdldFBsdWdpbihCbG9ja1BsdWdpbik7XG4gICAgfVxuXG4gICAgYXN5bmMgbmdPbkluaXQoKSB7XG5cbiAgICAgICAgdGhpcy5vblRvcCRcbiAgICAgICAgICAgIC5waXBlKGZpbHRlcihldmVudCA9PiBldmVudC5pc0ludGVyc2VjdGluZyksIGRlYm91bmNlVGltZSgxMDAwKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5sb2FkT2xkZXJNZXNzYWdlc0JlZm9yZVZpZXdwb3J0KCkpO1xuXG4gICAgICAgIGlmICh0aGlzLnJlY2lwaWVudC5yZWNpcGllbnRUeXBlID09PSAnY29udGFjdCcpIHtcbiAgICAgICAgICAgIHRoaXMucmVjaXBpZW50LnBlbmRpbmdJbiRcbiAgICAgICAgICAgICAgICAucGlwZShcbiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHBlbmRpbmdJbiA9PiBwZW5kaW5nSW4gPT09IHRydWUpLFxuICAgICAgICAgICAgICAgICAgICB0YWtlVW50aWwodGhpcy5uZ0Rlc3Ryb3kpLFxuICAgICAgICAgICAgICAgIClcbiAgICAgICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zdWJzY3JpcHRpb25BY3Rpb24gPSBTdWJzY3JpcHRpb25BY3Rpb24uUEVORElOR19SRVFVRVNUO1xuICAgICAgICAgICAgICAgICAgICB0aGlzLnNjaGVkdWxlU2Nyb2xsVG9MYXN0TWVzc2FnZSgpO1xuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0aGlzLnJlY2lwaWVudC5wZW5kaW5nUm9vbUludml0ZSRcbiAgICAgICAgICAgICAgICAucGlwZShcbiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKGludml0ZSA9PiBpbnZpdGUgIT0gbnVsbCksXG4gICAgICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLm5nRGVzdHJveSksXG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC5zdWJzY3JpYmUoKGludml0ZSkgPT4gdGhpcy5wZW5kaW5nUm9vbUludml0ZSA9IGludml0ZSk7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmNoYXRNZXNzYWdlTGlzdFJlZ2lzdHJ5LmluY3JlbWVudE9wZW5XaW5kb3dDb3VudCh0aGlzLnJlY2lwaWVudCk7XG4gICAgfVxuXG4gICAgYXN5bmMgbmdBZnRlclZpZXdJbml0KCkge1xuICAgICAgICB0aGlzLmNoYXRNZXNzYWdlVmlld0NoaWxkcmVuTGlzdC5jaGFuZ2VzXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5vbGRlc3RWaXNpYmxlTWVzc2FnZUJlZm9yZUxvYWRpbmcpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zY3JvbGxUb01lc3NhZ2UodGhpcy5vbGRlc3RWaXNpYmxlTWVzc2FnZUJlZm9yZUxvYWRpbmcpO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB0aGlzLm9sZGVzdFZpc2libGVNZXNzYWdlQmVmb3JlTG9hZGluZyA9IG51bGw7XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICBjb25zdCBtZXNzYWdlcyQ6IE9ic2VydmFibGU8TWVzc2FnZT4gPSB0aGlzLnJlY2lwaWVudC5tZXNzYWdlcyQ7XG4gICAgICAgIG1lc3NhZ2VzJFxuICAgICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICAgICAgZGVib3VuY2VUaW1lKDEwKSxcbiAgICAgICAgICAgICAgICBmaWx0ZXIoKCkgPT4gdGhpcy5pc05lYXJCb3R0b20oKSksXG4gICAgICAgICAgICAgICAgdGFrZVVudGlsKHRoaXMubmdEZXN0cm95KSxcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKF8pID0+IHRoaXMuc2NoZWR1bGVTY3JvbGxUb0xhc3RNZXNzYWdlKCkpO1xuXG4gICAgICAgIGlmICh0aGlzLnJlY2lwaWVudC5tZXNzYWdlcy5sZW5ndGggPCAxMCkge1xuICAgICAgICAgICAgYXdhaXQgdGhpcy5sb2FkTWVzc2FnZXMoKTsgLy8gaW4gY2FzZSBpbnN1ZmZpY2llbnQgb2xkIG1lc3NhZ2VzIGFyZSBkaXNwbGF5ZWRcbiAgICAgICAgfVxuICAgICAgICB0aGlzLnNjaGVkdWxlU2Nyb2xsVG9MYXN0TWVzc2FnZSgpO1xuICAgIH1cblxuICAgIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgY29udGFjdCA9IGNoYW5nZXMuY29udGFjdDtcblxuICAgICAgICBpZiAoY29udGFjdCAmJiBjb250YWN0LnByZXZpb3VzVmFsdWUgJiYgY29udGFjdC5jdXJyZW50VmFsdWUpIHtcbiAgICAgICAgICAgIHRoaXMuY2hhdE1lc3NhZ2VMaXN0UmVnaXN0cnkuZGVjcmVtZW50T3BlbldpbmRvd0NvdW50KGNvbnRhY3QucHJldmlvdXNWYWx1ZSk7XG4gICAgICAgICAgICB0aGlzLmNoYXRNZXNzYWdlTGlzdFJlZ2lzdHJ5LmluY3JlbWVudE9wZW5XaW5kb3dDb3VudChjb250YWN0LmN1cnJlbnRWYWx1ZSk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAoY29udGFjdCAmJiBjb250YWN0LmN1cnJlbnRWYWx1ZSkge1xuICAgICAgICAgICAgdGhpcy5zY2hlZHVsZVNjcm9sbFRvTGFzdE1lc3NhZ2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLm5nRGVzdHJveS5uZXh0KCk7XG4gICAgICAgIHRoaXMuY2hhdE1lc3NhZ2VMaXN0UmVnaXN0cnkuZGVjcmVtZW50T3BlbldpbmRvd0NvdW50KHRoaXMucmVjaXBpZW50KTtcbiAgICB9XG5cbiAgICBhY2NlcHRTdWJzY3JpcHRpb25SZXF1ZXN0KGV2ZW50OiBFdmVudCkge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBpZiAodGhpcy5zdWJzY3JpcHRpb25BY3Rpb24gPT09IFN1YnNjcmlwdGlvbkFjdGlvbi5QRU5ESU5HX1JFUVVFU1QpIHtcbiAgICAgICAgICAgIHRoaXMuY2hhdFNlcnZpY2UuYWRkQ29udGFjdCh0aGlzLnJlY2lwaWVudC5qaWRCYXJlLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgdGhpcy5zdWJzY3JpcHRpb25BY3Rpb24gPSBTdWJzY3JpcHRpb25BY3Rpb24uTk9fUEVORElOR19SRVFVRVNUO1xuICAgICAgICAgICAgdGhpcy5zY2hlZHVsZVNjcm9sbFRvTGFzdE1lc3NhZ2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGRlbnlTdWJzY3JpcHRpb25SZXF1ZXN0KGV2ZW50OiBFdmVudCkge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBpZiAodGhpcy5zdWJzY3JpcHRpb25BY3Rpb24gPT09IFN1YnNjcmlwdGlvbkFjdGlvbi5QRU5ESU5HX1JFUVVFU1QpIHtcbiAgICAgICAgICAgIHRoaXMuY2hhdFNlcnZpY2UucmVtb3ZlQ29udGFjdCh0aGlzLnJlY2lwaWVudC5qaWRCYXJlLnRvU3RyaW5nKCkpO1xuICAgICAgICAgICAgdGhpcy5zdWJzY3JpcHRpb25BY3Rpb24gPSBTdWJzY3JpcHRpb25BY3Rpb24uU0hPV19CTE9DS19BQ1RJT05TO1xuICAgICAgICAgICAgdGhpcy5zY2hlZHVsZVNjcm9sbFRvTGFzdE1lc3NhZ2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHNjaGVkdWxlU2Nyb2xsVG9MYXN0TWVzc2FnZSgpIHtcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnNjcm9sbFRvTGFzdE1lc3NhZ2UoKSwgMCk7XG4gICAgfVxuXG4gICAgYmxvY2tDb250YWN0KCRldmVudDogTW91c2VFdmVudCkge1xuICAgICAgICAkZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgdGhpcy5ibG9ja1BsdWdpbi5ibG9ja0ppZCh0aGlzLnJlY2lwaWVudC5qaWRCYXJlLnRvU3RyaW5nKCkpO1xuICAgICAgICB0aGlzLmNoYXRMaXN0U2VydmljZS5jbG9zZUNoYXQodGhpcy5yZWNpcGllbnQpO1xuICAgICAgICB0aGlzLnN1YnNjcmlwdGlvbkFjdGlvbiA9IFN1YnNjcmlwdGlvbkFjdGlvbi5OT19QRU5ESU5HX1JFUVVFU1Q7XG4gICAgfVxuXG4gICAgYmxvY2tDb250YWN0QW5kUmVwb3J0KCRldmVudDogTW91c2VFdmVudCkge1xuICAgICAgICBpZiAodGhpcy5yZWNpcGllbnQucmVjaXBpZW50VHlwZSAhPT0gJ2NvbnRhY3QnKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgJGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIHRoaXMucmVwb3J0VXNlclNlcnZpY2UucmVwb3J0VXNlcih0aGlzLnJlY2lwaWVudCk7XG4gICAgICAgIHRoaXMuYmxvY2tDb250YWN0KCRldmVudCk7XG4gICAgfVxuXG4gICAgZGlzbWlzc0Jsb2NrT3B0aW9ucygkZXZlbnQ6IE1vdXNlRXZlbnQpIHtcbiAgICAgICAgJGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIHRoaXMuc3Vic2NyaXB0aW9uQWN0aW9uID0gU3Vic2NyaXB0aW9uQWN0aW9uLk5PX1BFTkRJTkdfUkVRVUVTVDtcbiAgICB9XG5cbiAgICBzdWJzY3JpcHRpb25BY3Rpb25TaG93bigpIHtcbiAgICAgICAgaWYgKHRoaXMucmVjaXBpZW50LnJlY2lwaWVudFR5cGUgIT09ICdjb250YWN0Jykge1xuICAgICAgICAgICAgcmV0dXJuIGZhbHNlO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB0aGlzLnN1YnNjcmlwdGlvbkFjdGlvbiA9PT0gU3Vic2NyaXB0aW9uQWN0aW9uLlBFTkRJTkdfUkVRVUVTVFxuICAgICAgICAgICAgfHwgKHRoaXMuYmxvY2tQbHVnaW4uc3VwcG9ydHNCbG9jayQuZ2V0VmFsdWUoKSA9PT0gdHJ1ZSAmJiB0aGlzLnN1YnNjcmlwdGlvbkFjdGlvbiA9PT0gU3Vic2NyaXB0aW9uQWN0aW9uLlNIT1dfQkxPQ0tfQUNUSU9OUyk7XG4gICAgfVxuXG4gICAgYXN5bmMgbG9hZE9sZGVyTWVzc2FnZXNCZWZvcmVWaWV3cG9ydCgpIHtcbiAgICAgICAgaWYgKHRoaXMuaXNMb2FkaW5nSGlzdG9yeSgpIHx8IHRoaXMuaXNOZWFyQm90dG9tKCkpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICB0aGlzLm9sZGVzdFZpc2libGVNZXNzYWdlQmVmb3JlTG9hZGluZyA9IHRoaXMucmVjaXBpZW50Lm9sZGVzdE1lc3NhZ2U7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmxvYWRNZXNzYWdlcygpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgICB0aGlzLm9sZGVzdFZpc2libGVNZXNzYWdlQmVmb3JlTG9hZGluZyA9IG51bGw7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBvbkJvdHRvbShldmVudDogSW50ZXJzZWN0aW9uT2JzZXJ2ZXJFbnRyeSkge1xuICAgICAgICB0aGlzLmlzQXRCb3R0b20gPSBldmVudC5pc0ludGVyc2VjdGluZztcblxuICAgICAgICBpZiAoZXZlbnQuaXNJbnRlcnNlY3RpbmcpIHtcbiAgICAgICAgICAgIHRoaXMuaXNBdEJvdHRvbSA9IHRydWU7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLmlzQXRCb3R0b20gPSBmYWxzZTtcbiAgICAgICAgICAgIHRoaXMuYm90dG9tTGVmdEF0ID0gRGF0ZS5ub3coKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGdldE9yQ3JlYXRlQ29udGFjdFdpdGhGdWxsSmlkKG1lc3NhZ2U6IE1lc3NhZ2UgfCBSb29tTWVzc2FnZSk6IENvbnRhY3Qge1xuICAgICAgICBpZiAodGhpcy5yZWNpcGllbnQucmVjaXBpZW50VHlwZSA9PT0gJ2NvbnRhY3QnKSB7XG4gICAgICAgICAgICAvLyB0aGlzIGlzIG5vdCBhIG11bHRpIHVzZXIgY2hhdCwganVzdCB1c2UgcmVjaXBpZW50IGFzIGNvbnRhY3RcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnJlY2lwaWVudDtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHJvb21NZXNzYWdlID0gbWVzc2FnZSBhcyBSb29tTWVzc2FnZTtcblxuICAgICAgICBsZXQgbWF0Y2hpbmdDb250YWN0ID0gdGhpcy5jaGF0U2VydmljZS5jb250YWN0cyQuZ2V0VmFsdWUoKS5maW5kKFxuICAgICAgICAgICAgY29udGFjdCA9PiBjb250YWN0LmppZEZ1bGwuZXF1YWxzKHJvb21NZXNzYWdlLmZyb20pLFxuICAgICAgICApO1xuXG4gICAgICAgIGlmICghbWF0Y2hpbmdDb250YWN0KSB7XG4gICAgICAgICAgICBtYXRjaGluZ0NvbnRhY3QgPSB0aGlzLmNvbnRhY3RGYWN0b3J5LmNyZWF0ZUNvbnRhY3Qocm9vbU1lc3NhZ2UuZnJvbS50b1N0cmluZygpLCByb29tTWVzc2FnZS5mcm9tLnJlc291cmNlKTtcbiAgICAgICAgICAgIHRoaXMuY2hhdFNlcnZpY2UuY29udGFjdHMkLm5leHQoW21hdGNoaW5nQ29udGFjdF0uY29uY2F0KHRoaXMuY2hhdFNlcnZpY2UuY29udGFjdHMkLmdldFZhbHVlKCkpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiBtYXRjaGluZ0NvbnRhY3Q7XG4gICAgfVxuXG4gICAgc2hvd1BlbmRpbmdSb29tSW52aXRlKCkge1xuICAgICAgICBpZiAodGhpcy5yZWNpcGllbnQucmVjaXBpZW50VHlwZSAhPT0gJ2NvbnRhY3QnKSB7XG4gICAgICAgICAgICByZXR1cm4gZmFsc2U7XG4gICAgICAgIH1cbiAgICAgICAgcmV0dXJuIHRoaXMucGVuZGluZ1Jvb21JbnZpdGU7XG4gICAgfVxuXG4gICAgYXN5bmMgYWNjZXB0Um9vbUludml0ZShldmVudDogTW91c2VFdmVudCkge1xuICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICBhd2FpdCB0aGlzLmNoYXRTZXJ2aWNlLmdldFBsdWdpbihNdWx0aVVzZXJDaGF0UGx1Z2luKS5qb2luUm9vbSh0aGlzLnBlbmRpbmdSb29tSW52aXRlLnJvb21KaWQpO1xuICAgICAgICAodGhpcy5yZWNpcGllbnQgYXMgQ29udGFjdCkucGVuZGluZ1Jvb21JbnZpdGUkLm5leHQobnVsbCk7XG4gICAgICAgIHRoaXMucGVuZGluZ1Jvb21JbnZpdGUgPSBudWxsO1xuICAgIH1cblxuICAgIGFzeW5jIGRlY2xpbmVSb29tSW52aXRlKGV2ZW50OiBNb3VzZUV2ZW50KSB7XG4gICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY2hhdFNlcnZpY2UuZ2V0UGx1Z2luKE11bHRpVXNlckNoYXRQbHVnaW4pLmRlY2xpbmVSb29tSW52aXRlKHRoaXMucGVuZGluZ1Jvb21JbnZpdGUucm9vbUppZCk7XG4gICAgICAgICh0aGlzLnJlY2lwaWVudCBhcyBDb250YWN0KS5wZW5kaW5nUm9vbUludml0ZSQubmV4dChudWxsKTtcbiAgICAgICAgdGhpcy5wZW5kaW5nUm9vbUludml0ZSA9IG51bGw7XG4gICAgICAgIHRoaXMuY2hhdFNlcnZpY2UucmVtb3ZlQ29udGFjdCh0aGlzLnJlY2lwaWVudC5qaWRCYXJlLnRvU3RyaW5nKCkpO1xuICAgIH1cblxuICAgIHByaXZhdGUgc2Nyb2xsVG9MYXN0TWVzc2FnZSgpIHtcbiAgICAgICAgaWYgKHRoaXMuY2hhdE1lc3NhZ2VBcmVhRWxlbWVudCkge1xuICAgICAgICAgICAgdGhpcy5jaGF0TWVzc2FnZUFyZWFFbGVtZW50Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gdGhpcy5jaGF0TWVzc2FnZUFyZWFFbGVtZW50Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsSGVpZ2h0O1xuICAgICAgICAgICAgdGhpcy5pc0F0Qm90dG9tID0gdHJ1ZTsgLy8gaW4gc29tZSBicm93c2VycyB0aGUgaW50ZXJzZWN0aW9uIG9ic2VydmVyIGRvZXMgbm90IGVtaXQgd2hlbiBzY3JvbGxpbmcgcHJvZ3JhbW1hdGljYWxseVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzY3JvbGxUb01lc3NhZ2UobWVzc2FnZTogTWVzc2FnZSkge1xuICAgICAgICBpZiAodGhpcy5jaGF0TWVzc2FnZUFyZWFFbGVtZW50KSB7XG4gICAgICAgICAgICBjb25zdCBodG1sSWRBdHRyaWJ1dGUgPSAnbWVzc2FnZS0nICsgbWVzc2FnZS5pZDtcbiAgICAgICAgICAgIGNvbnN0IG1lc3NhZ2VFbGVtZW50ID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoaHRtbElkQXR0cmlidXRlKTtcbiAgICAgICAgICAgIG1lc3NhZ2VFbGVtZW50LnNjcm9sbEludG9WaWV3KGZhbHNlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgbG9hZE1lc3NhZ2VzKCkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgLy8gaW1wcm92ZSBwZXJmb3JtYW5jZSB3aGVuIGxvYWRpbmcgbG90cyBvZiBvbGQgbWVzc2FnZXNcbiAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYuZGV0YWNoKCk7XG4gICAgICAgICAgICBhd2FpdCB0aGlzLmNoYXRTZXJ2aWNlLmdldFBsdWdpbihNZXNzYWdlQXJjaGl2ZVBsdWdpbikubG9hZE1vc3RSZWNlbnRVbmxvYWRlZE1lc3NhZ2VzKHRoaXMucmVjaXBpZW50KTtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgIHRoaXMuY2hhbmdlRGV0ZWN0b3JSZWYucmVhdHRhY2goKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgaXNOZWFyQm90dG9tKCkge1xuICAgICAgICByZXR1cm4gdGhpcy5pc0F0Qm90dG9tIHx8IERhdGUubm93KCkgLSB0aGlzLmJvdHRvbUxlZnRBdCA8IDEwMDA7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBpc0xvYWRpbmdIaXN0b3J5KCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gISF0aGlzLm9sZGVzdFZpc2libGVNZXNzYWdlQmVmb3JlTG9hZGluZztcbiAgICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiY2hhdC1tZXNzYWdlc1wiICNtZXNzYWdlQXJlYT5cblxuICAgIDxkaXYgY2xhc3M9XCJjaGF0LW1lc3NhZ2VzLXN0YXJ0XCIgKG5neENoYXRJbnRlcnNlY3Rpb25PYnNlcnZlcik9XCJvblRvcCQubmV4dCgkZXZlbnQpXCI+PC9kaXY+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0Zvcj1cImxldCBkYXRlTWVzc2FnZXNHcm91cCBvZiByZWNpcGllbnQuZGF0ZU1lc3NhZ2VzR3JvdXBzXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjaGF0LW1lc3NhZ2VzLWRhdGUtZ3JvdXBcIj57e2RhdGVNZXNzYWdlc0dyb3VwLmRhdGUgfCBkYXRlOmNoYXRTZXJ2aWNlLnRyYW5zbGF0aW9ucy5kYXRlRm9ybWF0OnVuZGVmaW5lZDpjaGF0U2VydmljZS50cmFuc2xhdGlvbnMubG9jYWxlfX08L2Rpdj5cbiAgICAgICAgPG5neC1jaGF0LW1lc3NhZ2VcbiAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgbWVzc2FnZSBvZiBkYXRlTWVzc2FnZXNHcm91cC5t