@pazznetwork/ngx-chat
Version:
[](https://travis-ci.com/pazznetwork/ngx-chat) [](https://coveralls.io/github/pazzn
109 lines • 34 kB
JavaScript
import { animate, state, style, transition, trigger } from '@angular/animations';
import { Component, EventEmitter, Inject, Input, Output } from '@angular/core';
import { combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
import { MultiUserChatPlugin } from '../../services/adapters/xmpp/plugins/multi-user-chat/multi-user-chat.plugin';
import { CHAT_SERVICE_TOKEN } from '../../services/chat-service';
import * as i0 from "@angular/core";
import * as i1 from "../../services/chat-list-state.service";
import * as i2 from "@angular/common";
import * as i3 from "../roster-recipient/roster-recipient.component";
export class RosterListComponent {
constructor(chatService, chatListService) {
this.chatService = chatService;
this.chatListService = chatListService;
this.rosterStateChanged = new EventEmitter();
this.multiUserChatPlugin = this.chatService.getPlugin(MultiUserChatPlugin);
}
ngOnInit() {
if (!this.contacts) {
this.contacts = this.chatService.contactsSubscribed$;
}
if (!this.contactRequestsReceived$) {
this.contactRequestsReceived$ = this.chatService.contactRequestsReceived$;
}
if (!this.contactRequestsSent$) {
this.contactRequestsSent$ = this.chatService.contactRequestsSent$;
}
if (!this.contactsUnaffiliated$) {
this.contactsUnaffiliated$ = this.chatService.contactsUnaffiliated$;
}
this.hasNoContacts$ = combineLatest([
this.contacts,
this.contactRequestsReceived$,
this.contactRequestsSent$,
this.contactsUnaffiliated$,
]).pipe(map(([contacts, received, sent, unaffiliated]) => contacts.length + received.length + sent.length + unaffiliated.length === 0));
}
onClickRecipient(recipient) {
this.chatListService.openChat(recipient);
}
toggleVisibility() {
const newState = this.rosterState === 'shown' ? 'hidden' : 'shown';
this.rosterStateChanged.emit(newState);
}
}
RosterListComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RosterListComponent, deps: [{ token: CHAT_SERVICE_TOKEN }, { token: i1.ChatListStateService }], target: i0.ɵɵFactoryTarget.Component });
RosterListComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.9", type: RosterListComponent, selector: "ngx-chat-roster-list", inputs: { rosterState: "rosterState", contacts: "contacts", contactRequestsReceived$: "contactRequestsReceived$", contactRequestsSent$: "contactRequestsSent$", contactsUnaffiliated$: "contactsUnaffiliated$" }, outputs: { rosterStateChanged: "rosterStateChanged" }, ngImport: i0, template: "<div class=\"roster-drawer\" (click)=\"toggleVisibility()\" [@drawerVisibility]=\"rosterState\">\n <div class=\"roster-drawer__button\" *ngIf=\"rosterState === 'shown'\">»</div>\n <div class=\"roster-drawer__button\" *ngIf=\"rosterState === 'hidden'\">«</div>\n</div>\n\n<div class=\"roster-list\" [@rosterVisibility]=\"rosterState\" [attr.data-ngx-chat-state]=\"chatService.state$ | async\">\n\n <div class=\"roster-header\">\n {{ chatService.translations.chat }}\n </div>\n\n <ng-container *ngIf=\"(multiUserChatPlugin.rooms$ | async) as rooms\">\n <ng-container *ngIf=\"rooms.length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.rooms}}</div>\n\n <div class=\"contact-list-wrapper\">\n <ngx-chat-roster-recipient\n *ngFor=\"let room of rooms\"\n [recipient]=\"room\"\n (click)=\"onClickRecipient(room)\">\n </ngx-chat-roster-recipient>\n </div>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"(contacts | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contacts}}</div>\n\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contacts | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactRequestsReceived$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactRequestIn}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contactRequestsReceived$ | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactRequestsSent$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactRequestOut}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contactRequestsSent$ | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactsUnaffiliated$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactsUnaffiliated}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ng-container *ngFor=\"let contact of (contactsUnaffiliated$ | async)\">\n <ngx-chat-roster-recipient\n *ngIf=\"contact.messages.length > 0\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n </ng-container>\n\n </div>\n </ng-container>\n\n <div class=\"roster-list__empty\" *ngIf=\"hasNoContacts$ | async\">\n {{chatService.translations.noContacts}}\n </div>\n\n <ng-container *ngIf=\"chatService.enableDebugging\">\n <hr/>\n\n <div class=\"contact-pending-request-received-wrapper\">\n debug contact requests in:\n <div class=\"contact-pending-request-received\"\n *ngFor=\"let request of (chatService.contactRequestsReceived$ | async)\">\n {{request.name}}\n </div>\n </div>\n\n <hr/>\n\n <div class=\"contact-pending-request-sent-wrapper\">\n debug contact requests out:\n <div class=\"contact-pending-request-sent\"\n *ngFor=\"let request of (chatService.contactRequestsSent$ | async)\">\n {{request.name}}\n </div>\n </div>\n\n <hr/>\n\n <p>\n debug contacts (count: {{ (chatService.contacts$ | async).length }})<br/>\n </p>\n <p *ngFor=\"let contact of (chatService.contacts$|async)\" style=\"margin-bottom: 1em\">\n <em>{{contact.name}}:</em><br/>\n subscription={{contact.subscription$ | async}}<br/>\n presence={{contact.presence$ | async}}<br/>\n pendingIn={{contact.pendingIn$ | async}}<br/>\n pendingOut={{contact.pendingOut$ | async}}\n </p>\n <p class=\"roster-debug-state\">state: {{chatService.state$ | async}}</p>\n </ng-container>\n\n <!--\n <div class=\"roster-footer\">\n </div>\n -->\n</div>\n", styles: ["@keyframes ngx-chat-message-in{0%{transform:translate(50px);opacity:0}to{transform:none;opacity:1}}@keyframes ngx-chat-message-out{0%{transform:translate(-50px);opacity:0}to{transform:none;opacity:1}}*{box-sizing:border-box;margin:0;padding:0;font-family:Helvetica,Arial,serif}.roster-list{position:fixed;top:0;bottom:0;right:0;width:14em;overflow-y:auto;border-left:1px solid #e1e1e1;z-index:80;margin-left:10px;background-color:#f5f5f5;text-align:left;padding:.5em .5em 0;display:flex;flex-direction:column}.roster-list .roster-header{text-align:center;margin-bottom:1em}.roster-list .roster-group-header{border-bottom:1px solid #e1e1e1;padding-bottom:.5em}.roster-list .roster-footer{border-top:1px solid #e1e1e1;width:14em;padding-top:.5em}.roster-list .contact-list-wrapper{margin-bottom:1em}.roster-list ngx-chat-roster-recipient{display:block;padding:.5em}.roster-list ngx-chat-roster-recipient:hover{background-color:#e8e8e8;cursor:pointer}.roster-list__empty{color:#999;text-align:center;margin-top:.5em;font-size:1.5em}.roster-drawer{top:0;bottom:0;right:14em;width:1em;position:fixed;z-index:80;cursor:pointer}.roster-drawer:hover{background-color:#1e1e1e33}.roster-drawer__button{top:50%;color:transparent;background-color:transparent;position:fixed;margin-left:.4rem}.roster-drawer:hover .roster-drawer__button{color:#fff}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.RosterRecipientComponent, selector: "ngx-chat-roster-recipient", inputs: ["recipient"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }], animations: [
trigger('rosterVisibility', [
state('hidden', style({
right: '-14em',
})),
state('shown', style({
right: '0em',
})),
transition('hidden => shown', animate('400ms ease')),
transition('shown => hidden', animate('400ms ease')),
]),
trigger('drawerVisibility', [
state('hidden', style({
right: '0em',
})),
state('shown', style({
right: '14em',
})),
transition('hidden => shown', animate('400ms ease')),
transition('shown => hidden', animate('400ms ease')),
]),
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.9", ngImport: i0, type: RosterListComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-chat-roster-list', animations: [
trigger('rosterVisibility', [
state('hidden', style({
right: '-14em',
})),
state('shown', style({
right: '0em',
})),
transition('hidden => shown', animate('400ms ease')),
transition('shown => hidden', animate('400ms ease')),
]),
trigger('drawerVisibility', [
state('hidden', style({
right: '0em',
})),
state('shown', style({
right: '14em',
})),
transition('hidden => shown', animate('400ms ease')),
transition('shown => hidden', animate('400ms ease')),
]),
], template: "<div class=\"roster-drawer\" (click)=\"toggleVisibility()\" [@drawerVisibility]=\"rosterState\">\n <div class=\"roster-drawer__button\" *ngIf=\"rosterState === 'shown'\">»</div>\n <div class=\"roster-drawer__button\" *ngIf=\"rosterState === 'hidden'\">«</div>\n</div>\n\n<div class=\"roster-list\" [@rosterVisibility]=\"rosterState\" [attr.data-ngx-chat-state]=\"chatService.state$ | async\">\n\n <div class=\"roster-header\">\n {{ chatService.translations.chat }}\n </div>\n\n <ng-container *ngIf=\"(multiUserChatPlugin.rooms$ | async) as rooms\">\n <ng-container *ngIf=\"rooms.length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.rooms}}</div>\n\n <div class=\"contact-list-wrapper\">\n <ngx-chat-roster-recipient\n *ngFor=\"let room of rooms\"\n [recipient]=\"room\"\n (click)=\"onClickRecipient(room)\">\n </ngx-chat-roster-recipient>\n </div>\n </ng-container>\n </ng-container>\n\n <ng-container *ngIf=\"(contacts | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contacts}}</div>\n\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contacts | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactRequestsReceived$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactRequestIn}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contactRequestsReceived$ | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactRequestsSent$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactRequestOut}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ngx-chat-roster-recipient\n *ngFor=\"let contact of (contactRequestsSent$ | async)\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n\n </div>\n </ng-container>\n\n <ng-container *ngIf=\"(contactsUnaffiliated$ | async).length > 0\">\n <div class=\"roster-group-header\">{{chatService.translations.contactsUnaffiliated}}</div>\n <div class=\"contact-list-wrapper\">\n\n <ng-container *ngFor=\"let contact of (contactsUnaffiliated$ | async)\">\n <ngx-chat-roster-recipient\n *ngIf=\"contact.messages.length > 0\"\n [recipient]=\"contact\"\n (click)=\"onClickRecipient(contact)\">\n </ngx-chat-roster-recipient>\n </ng-container>\n\n </div>\n </ng-container>\n\n <div class=\"roster-list__empty\" *ngIf=\"hasNoContacts$ | async\">\n {{chatService.translations.noContacts}}\n </div>\n\n <ng-container *ngIf=\"chatService.enableDebugging\">\n <hr/>\n\n <div class=\"contact-pending-request-received-wrapper\">\n debug contact requests in:\n <div class=\"contact-pending-request-received\"\n *ngFor=\"let request of (chatService.contactRequestsReceived$ | async)\">\n {{request.name}}\n </div>\n </div>\n\n <hr/>\n\n <div class=\"contact-pending-request-sent-wrapper\">\n debug contact requests out:\n <div class=\"contact-pending-request-sent\"\n *ngFor=\"let request of (chatService.contactRequestsSent$ | async)\">\n {{request.name}}\n </div>\n </div>\n\n <hr/>\n\n <p>\n debug contacts (count: {{ (chatService.contacts$ | async).length }})<br/>\n </p>\n <p *ngFor=\"let contact of (chatService.contacts$|async)\" style=\"margin-bottom: 1em\">\n <em>{{contact.name}}:</em><br/>\n subscription={{contact.subscription$ | async}}<br/>\n presence={{contact.presence$ | async}}<br/>\n pendingIn={{contact.pendingIn$ | async}}<br/>\n pendingOut={{contact.pendingOut$ | async}}\n </p>\n <p class=\"roster-debug-state\">state: {{chatService.state$ | async}}</p>\n </ng-container>\n\n <!--\n <div class=\"roster-footer\">\n </div>\n -->\n</div>\n", styles: ["@keyframes ngx-chat-message-in{0%{transform:translate(50px);opacity:0}to{transform:none;opacity:1}}@keyframes ngx-chat-message-out{0%{transform:translate(-50px);opacity:0}to{transform:none;opacity:1}}*{box-sizing:border-box;margin:0;padding:0;font-family:Helvetica,Arial,serif}.roster-list{position:fixed;top:0;bottom:0;right:0;width:14em;overflow-y:auto;border-left:1px solid #e1e1e1;z-index:80;margin-left:10px;background-color:#f5f5f5;text-align:left;padding:.5em .5em 0;display:flex;flex-direction:column}.roster-list .roster-header{text-align:center;margin-bottom:1em}.roster-list .roster-group-header{border-bottom:1px solid #e1e1e1;padding-bottom:.5em}.roster-list .roster-footer{border-top:1px solid #e1e1e1;width:14em;padding-top:.5em}.roster-list .contact-list-wrapper{margin-bottom:1em}.roster-list ngx-chat-roster-recipient{display:block;padding:.5em}.roster-list ngx-chat-roster-recipient:hover{background-color:#e8e8e8;cursor:pointer}.roster-list__empty{color:#999;text-align:center;margin-top:.5em;font-size:1.5em}.roster-drawer{top:0;bottom:0;right:14em;width:1em;position:fixed;z-index:80;cursor:pointer}.roster-drawer:hover{background-color:#1e1e1e33}.roster-drawer__button{top:50%;color:transparent;background-color:transparent;position:fixed;margin-left:.4rem}.roster-drawer:hover .roster-drawer__button{color:#fff}\n"] }]
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [CHAT_SERVICE_TOKEN]
}] }, { type: i1.ChatListStateService }]; }, propDecorators: { rosterState: [{
type: Input
}], contacts: [{
type: Input
}], contactRequestsReceived$: [{
type: Input
}], contactRequestsSent$: [{
type: Input
}], contactsUnaffiliated$: [{
type: Input
}], rosterStateChanged: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9zdGVyLWxpc3QuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2xpYi9jb21wb25lbnRzL3Jvc3Rlci1saXN0L3Jvc3Rlci1saXN0LmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9saWIvY29tcG9uZW50cy9yb3N0ZXItbGlzdC9yb3N0ZXItbGlzdC5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxLQUFLLEVBQUUsVUFBVSxFQUFFLE9BQU8sRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ2pGLE9BQU8sRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQVUsTUFBTSxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3ZGLE9BQU8sRUFBRSxhQUFhLEVBQWMsTUFBTSxNQUFNLENBQUM7QUFDakQsT0FBTyxFQUFFLEdBQUcsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBR3JDLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLDZFQUE2RSxDQUFDO0FBRWxILE9BQU8sRUFBRSxrQkFBa0IsRUFBZSxNQUFNLDZCQUE2QixDQUFDOzs7OztBQTZCOUUsTUFBTSxPQUFPLG1CQUFtQjtJQXdCNUIsWUFBK0MsV0FBd0IsRUFDbkQsZUFBcUM7UUFEVixnQkFBVyxHQUFYLFdBQVcsQ0FBYTtRQUNuRCxvQkFBZSxHQUFmLGVBQWUsQ0FBc0I7UUFMekQsdUJBQWtCLEdBQUcsSUFBSSxZQUFZLEVBQXNCLENBQUM7UUFNeEQsSUFBSSxDQUFDLG1CQUFtQixHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsU0FBUyxDQUFDLG1CQUFtQixDQUFDLENBQUM7SUFDL0UsQ0FBQztJQUVELFFBQVE7UUFDSixJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRTtZQUNoQixJQUFJLENBQUMsUUFBUSxHQUFHLElB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QUMsQ0FBQzs0QkFDSCxVQUFVLENBQUMsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDOzRCQUNwRCxVQUFVLENBQUMsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO3lCQUN2RCxDQUFDO3dCQUNGLE9BQU8sQ0FBQyxrQkFBa0IsRUFBRTs0QkFDeEIsS0FBSyxDQUFDLFFBQVEsRUFBRSxLQUFLLENBQUM7Z0NBQ2xCLEtBQUssRUFBRSxLQUFLOzZCQUNmLENBQUMsQ0FBQzs0QkFDSCxLQUFLLENBQUMsT0FBTyxFQUFFLEtBQUssQ0FBQztnQ0FDakIsS0FBSyxFQUFFLE1BQU07NkJBQ2hCLENBQUMsQ0FBQzs0QkFDSCxVQUFVLENBQUMsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDOzRCQUNwRCxVQUFVLENBQUMsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLFlBQVksQ0FBQyxDQUFDO3lCQUN2RCxDQUFDO3FCQUNMOzswQkEwQlksTUFBTTsyQkFBQyxrQkFBa0I7K0VBckJ0QyxXQUFXO3NCQURWLEtBQUs7Z0JBSU4sUUFBUTtzQkFEUCxLQUFLO2dCQUlOLHdCQUF3QjtzQkFEdkIsS0FBSztnQkFJTixvQkFBb0I7c0JBRG5CLEtBQUs7Z0JBSU4scUJBQXFCO3NCQURwQixLQUFLO2dCQU1OLGtCQUFrQjtzQkFEakIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGFuaW1hdGUsIHN0YXRlLCBzdHlsZSwgdHJhbnNpdGlvbiwgdHJpZ2dlciB9IGZyb20gJ0Bhbmd1bGFyL2FuaW1hdGlvbnMnO1xuaW1wb3J0IHsgQ29tcG9uZW50LCBFdmVudEVtaXR0ZXIsIEluamVjdCwgSW5wdXQsIE9uSW5pdCwgT3V0cHV0IH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBjb21iaW5lTGF0ZXN0LCBPYnNlcnZhYmxlIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBtYXAgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBDb250YWN0IH0gZnJvbSAnLi4vLi4vY29yZS9jb250YWN0JztcbmltcG9ydCB7IFJlY2lwaWVudCB9IGZyb20gJy4uLy4uL2NvcmUvcmVjaXBpZW50JztcbmltcG9ydCB7IE11bHRpVXNlckNoYXRQbHVnaW4gfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9hZGFwdGVycy94bXBwL3BsdWdpbnMvbXVsdGktdXNlci1jaGF0L211bHRpLXVzZXItY2hhdC5wbHVnaW4nO1xuaW1wb3J0IHsgQ2hhdExpc3RTdGF0ZVNlcnZpY2UgfSBmcm9tICcuLi8uLi9zZXJ2aWNlcy9jaGF0LWxpc3Qtc3RhdGUuc2VydmljZSc7XG5pbXBvcnQgeyBDSEFUX1NFUlZJQ0VfVE9LRU4sIENoYXRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vc2VydmljZXMvY2hhdC1zZXJ2aWNlJztcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6ICduZ3gtY2hhdC1yb3N0ZXItbGlzdCcsXG4gICAgdGVtcGxhdGVVcmw6ICcuL3Jvc3Rlci1saXN0LmNvbXBvbmVudC5odG1sJyxcbiAgICBzdHlsZVVybHM6IFsnLi9yb3N0ZXItbGlzdC5jb21wb25lbnQubGVzcyddLFxuICAgIGFuaW1hdGlvbnM6IFtcbiAgICAgICAgdHJpZ2dlcigncm9zdGVyVmlzaWJpbGl0eScsIFtcbiAgICAgICAgICAgIHN0YXRlKCdoaWRkZW4nLCBzdHlsZSh7XG4gICAgICAgICAgICAgICAgcmlnaHQ6ICctMTRlbScsXG4gICAgICAgICAgICB9KSksXG4gICAgICAgICAgICBzdGF0ZSgnc2hvd24nLCBzdHlsZSh7XG4gICAgICAgICAgICAgICAgcmlnaHQ6ICcwZW0nLFxuICAgICAgICAgICAgfSkpLFxuICAgICAgICAgICAgdHJhbnNpdGlvbignaGlkZGVuID0+IHNob3duJywgYW5pbWF0ZSgnNDAwbXMgZWFzZScpKSxcbiAgICAgICAgICAgIHRyYW5zaXRpb24oJ3Nob3duID0+IGhpZGRlbicsIGFuaW1hdGUoJzQwMG1zIGVhc2UnKSksXG4gICAgICAgIF0pLFxuICAgICAgICB0cmlnZ2VyKCdkcmF3ZXJWaXNpYmlsaXR5JywgW1xuICAgICAgICAgICAgc3RhdGUoJ2hpZGRlbicsIHN0eWxlKHtcbiAgICAgICAgICAgICAgICByaWdodDogJzBlbScsXG4gICAgICAgICAgICB9KSksXG4gICAgICAgICAgICBzdGF0ZSgnc2hvd24nLCBzdHlsZSh7XG4gICAgICAgICAgICAgICAgcmlnaHQ6ICcxNGVtJyxcbiAgICAgICAgICAgIH0pKSxcbiAgICAgICAgICAgIHRyYW5zaXRpb24oJ2hpZGRlbiA9PiBzaG93bicsIGFuaW1hdGUoJzQwMG1zIGVhc2UnKSksXG4gICAgICAgICAgICB0cmFuc2l0aW9uKCdzaG93biA9PiBoaWRkZW4nLCBhbmltYXRlKCc0MDBtcyBlYXNlJykpLFxuICAgICAgICBdKSxcbiAgICBdLFxufSlcbmV4cG9ydCBjbGFzcyBSb3N0ZXJMaXN0Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0IHtcblxuICAgIEBJbnB1dCgpXG4gICAgcm9zdGVyU3RhdGU6ICdoaWRkZW4nIHwgJ3Nob3duJztcblxuICAgIEBJbnB1dCgpXG4gICAgY29udGFjdHM6IE9ic2VydmFibGU8Q29udGFjdFtdPjtcblxuICAgIEBJbnB1dCgpXG4gICAgY29udGFjdFJlcXVlc3RzUmVjZWl2ZWQkOiBPYnNlcnZhYmxlPENvbnRhY3RbXT47XG5cbiAgICBASW5wdXQoKVxuICAgIGNvbnRhY3RSZXF1ZXN0c1NlbnQkOiBPYnNlcnZhYmxlPENvbnRhY3RbXT47XG5cbiAgICBASW5wdXQoKVxuICAgIGNvbnRhY3RzVW5hZmZpbGlhdGVkJDogT2JzZXJ2YWJsZTxDb250YWN0W10+O1xuXG4gICAgaGFzTm9Db250YWN0cyQ6IE9ic2VydmFibGU8Ym9vbGVhbj47XG5cbiAgICBAT3V0cHV0KClcbiAgICByb3N0ZXJTdGF0ZUNoYW5nZWQgPSBuZXcgRXZlbnRFbWl0dGVyPCdoaWRkZW4nIHwgJ3Nob3duJz4oKTtcblxuICAgIG11bHRpVXNlckNoYXRQbHVnaW46IE11bHRpVXNlckNoYXRQbHVnaW47XG5cbiAgICBjb25zdHJ1Y3RvcihASW5qZWN0KENIQVRfU0VSVklDRV9UT0tFTikgcHVibGljIGNoYXRTZXJ2aWNlOiBDaGF0U2VydmljZSxcbiAgICAgICAgICAgICAgICBwcml2YXRlIGNoYXRMaXN0U2VydmljZTogQ2hhdExpc3RTdGF0ZVNlcnZpY2UpIHtcbiAgICAgICAgdGhpcy5tdWx0aVVzZXJDaGF0UGx1Z2luID0gdGhpcy5jaGF0U2VydmljZS5nZXRQbHVnaW4oTXVsdGlVc2VyQ2hhdFBsdWdpbik7XG4gICAgfVxuXG4gICAgbmdPbkluaXQoKSB7XG4gICAgICAgIGlmICghdGhpcy5jb250YWN0cykge1xuICAgICAgICAgICAgdGhpcy5jb250YWN0cyA9IHRoaXMuY2hhdFNlcnZpY2UuY29udGFjdHNTdWJzY3JpYmVkJDtcbiAgICAgICAgfVxuICAgICAgICBpZiAoIXRoaXMuY29udGFjdFJlcXVlc3RzUmVjZWl2ZWQkKSB7XG4gICAgICAgICAgICB0aGlzLmNvbnRhY3RSZXF1ZXN0c1JlY2VpdmVkJCA9IHRoaXMuY2hhdFNlcnZpY2UuY29udGFjdFJlcXVlc3RzUmVjZWl2ZWQkO1xuICAgICAgICB9XG4gICAgICAgIGlmICghdGhpcy5jb250YWN0UmVxdWVzdHNTZW50JCkge1xuICAgICAgICAgICAgdGhpcy5jb250YWN0UmVxdWVzdHNTZW50JCA9IHRoaXMuY2hhdFNlcnZpY2UuY29udGFjdFJlcXVlc3RzU2VudCQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKCF0aGlzLmNvbnRhY3RzVW5hZmZpbGlhdGVkJCkge1xuICAgICAgICAgICAgdGhpcy5jb250YWN0c1VuYWZmaWxpYXRlZCQgPSB0aGlzLmNoYXRTZXJ2aWNlLmNvbnRhY3RzVW5hZmZpbGlhdGVkJDtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmhhc05vQ29udGFjdHMkID0gY29tYmluZUxhdGVzdChbXG4gICAgICAgICAgICB0aGlzLmNvbnRhY3RzLFxuICAgICAgICAgICAgdGhpcy5jb250YWN0UmVxdWVzdHNSZWNlaXZlZCQsXG4gICAgICAgICAgICB0aGlzLmNvbnRhY3RSZXF1ZXN0c1NlbnQkLFxuICAgICAgICAgICAgdGhpcy5jb250YWN0c1VuYWZmaWxpYXRlZCQsXG4gICAgICAgIF0pLnBpcGUoXG4gICAgICAgICAgICBtYXAoKFtjb250YWN0cywgcmVjZWl2ZWQsIHNlbnQsIHVuYWZmaWxpYXRlZF0pID0+XG4gICAgICAgICAgICAgICAgY29udGFjdHMubGVuZ3RoICsgcmVjZWl2ZWQubGVuZ3RoICsgc2VudC5sZW5ndGggKyB1bmFmZmlsaWF0ZWQubGVuZ3RoID09PSAwKSxcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBvbkNsaWNrUmVjaXBpZW50KHJlY2lwaWVudDogUmVjaXBpZW50KSB7XG4gICAgICAgIHRoaXMuY2hhdExpc3RTZXJ2aWNlLm9wZW5DaGF0KHJlY2lwaWVudCk7XG4gICAgfVxuXG4gICAgdG9nZ2xlVmlzaWJpbGl0eSgpIHtcbiAgICAgICAgY29uc3QgbmV3U3RhdGUgPSB0aGlzLnJvc3RlclN0YXRlID09PSAnc2hvd24nID8gJ2hpZGRlbicgOiAnc2hvd24nO1xuICAgICAgICB0aGlzLnJvc3RlclN0YXRlQ2hhbmdlZC5lbWl0KG5ld1N0YXRlKTtcbiAgICB9XG5cbn1cbiIsIjxkaXYgY2xhc3M9XCJyb3N0ZXItZHJhd2VyXCIgKGNsaWNrKT1cInRvZ2dsZVZpc2liaWxpdHkoKVwiIFtAZHJhd2VyVmlzaWJpbGl0eV09XCJyb3N0ZXJTdGF0ZVwiPlxuICAgIDxkaXYgY2xhc3M9XCJyb3N0ZXItZHJhd2VyX19idXR0b25cIiAqbmdJZj1cInJvc3RlclN0YXRlID09PSAnc2hvd24nXCI+JnJhcXVvOzwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJyb3N0ZXItZHJhd2VyX19idXR0b25cIiAqbmdJZj1cInJvc3RlclN0YXRlID09PSAnaGlkZGVuJ1wiPiZsYXF1bzs8L2Rpdj5cbjwvZGl2PlxuXG48ZGl2IGNsYXNzPVwicm9zdGVyLWxpc3RcIiBbQHJvc3RlclZpc2liaWxpdHldPVwicm9zdGVyU3RhdGVcIiBbYXR0ci5kYXRhLW5neC1jaGF0LXN0YXRlXT1cImNoYXRTZXJ2aWNlLnN0YXRlJCB8IGFzeW5jXCI+XG5cbiAgICA8ZGl2IGNsYXNzPVwicm9zdGVyLWhlYWRlclwiPlxuICAgICAgICB7eyBjaGF0U2VydmljZS50cmFuc2xhdGlvbnMuY2hhdCB9fVxuICAgIDwvZGl2PlxuXG4gICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIihtdWx0aVVzZXJDaGF0UGx1Z2luLnJvb21zJCB8IGFzeW5jKSBhcyByb29tc1wiPlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwicm9vbXMubGVuZ3RoID4gMFwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cInJvc3Rlci1ncm91cC1oZWFkZXJcIj57e2NoYXRTZXJ2aWNlLnRyYW5zbGF0aW9ucy5yb29tc319PC9kaXY+XG5cbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb250YWN0LWxpc3Qtd3JhcHBlclwiPlxuICAgICAgICAgICAgICAgIDxuZ3gtY2hhdC1yb3N0ZXItcmVjaXBpZW50XG4gICAgICAgICAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgcm9vbSBvZiByb29tc1wiXG4gICAgICAgICAgICAgICAgICAgICAgICBbcmVjaXBpZW50XT1cInJvb21cIlxuICAgICAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQ2xpY2tSZWNpcGllbnQocm9vbSlcIj5cbiAgICAgICAgICAgICAgICA8L25neC1jaGF0LXJvc3Rlci1yZWNpcGllbnQ+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiKGNvbnRhY3RzIHwgYXN5bmMpLmxlbmd0aCA+IDBcIj5cbiAgICAgICAgPGRpdiBjbGFzcz1cInJvc3Rlci1ncm91cC1oZWFkZXJcIj57e2NoYXRTZXJ2aWNlLnRyYW5zbGF0aW9ucy5jb250YWN0c319PC9kaXY+XG5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbnRhY3QtbGlzdC13cmFwcGVyXCI+XG5cbiAgICAgICAgICAgIDxuZ3gtY2hhdC1yb3N0ZXItcmVjaXBpZW50XG4gICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBjb250YWN0IG9mIChjb250YWN0cyB8IGFzeW5jKVwiXG4gICAgICAgICAgICAgICAgICAgIFtyZWNpcGllbnRdPVwiY29udGFjdFwiXG4gICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJvbkNsaWNrUmVjaXBpZW50KGNvbnRhY3QpXCI+XG4gICAgICAgICAgICA8L25neC1jaGF0LXJvc3Rlci1yZWNpcGllbnQ+XG5cbiAgICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiKGNvbnRhY3RSZXF1ZXN0c1JlY2VpdmVkJCB8IGFzeW5jKS5sZW5ndGggPiAwXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJyb3N0ZXItZ3JvdXAtaGVhZGVyXCI+e3tjaGF0U2VydmljZS50cmFuc2xhdGlvbnMuY29udGFjdFJlcXVlc3RJbn19PC9kaXY+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJjb250YWN0LWxpc3Qtd3JhcHBlclwiPlxuXG4gICAgICAgICAgICA8bmd4LWNoYXQtcm9zdGVyLXJlY2lwaWVudFxuICAgICAgICAgICAgICAgICAgICAqbmdGb3I9XCJsZXQgY29udGFjdCBvZiAoY29udGFjdFJlcXVlc3RzUmVjZWl2ZWQkIHwgYXN5bmMpXCJcbiAgICAgICAgICAgICAgICAgICAgW3JlY2lwaWVudF09XCJjb250YWN0XCJcbiAgICAgICAgICAgICAgICAgICAgKGNsaWNrKT1cIm9uQ2xpY2tSZWNpcGllbnQoY29udGFjdClcIj5cbiAgICAgICAgICAgIDwvbmd4LWNoYXQtcm9zdGVyLXJlY2lwaWVudD5cblxuICAgICAgICA8L2Rpdj5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIoY29udGFjdFJlcXVlc3RzU2VudCQgfCBhc3luYykubGVuZ3RoID4gMFwiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwicm9zdGVyLWdyb3VwLWhlYWRlclwiPnt7Y2hhdFNlcnZpY2UudHJhbnNsYXRpb25zLmNvbnRhY3RSZXF1ZXN0T3V0fX08L2Rpdj5cbiAgICAgICAgPGRpdiBjbGFzcz1cImNvbnRhY3QtbGlzdC13cmFwcGVyXCI+XG5cbiAgICAgICAgICAgIDxuZ3gtY2hhdC1yb3N0ZXItcmVjaXBpZW50XG4gICAgICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCBjb250YWN0IG9mIChjb250YWN0UmVxdWVzdHNTZW50JCB8IGFzeW5jKVwiXG4gICAgICAgICAgICAgICAgICAgIFtyZWNpcGllbnRdPVwiY29udGFjdFwiXG4gICAgICAgICAgICAgICAgICAgIChjbGljayk9XCJvbkNsaWNrUmVjaXBpZW50KGNvbnRhY3QpXCI+XG4gICAgICAgICAgICA8L25neC1jaGF0LXJvc3Rlci1yZWNpcGllbnQ+XG5cbiAgICAgICAgPC9kaXY+XG4gICAgPC9uZy1jb250YWluZXI+XG5cbiAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiKGNvbnRhY3RzVW5hZmZpbGlhdGVkJCB8IGFzeW5jKS5sZW5ndGggPiAwXCI+XG4gICAgICAgIDxkaXYgY2xhc3M9XCJyb3N0ZXItZ3JvdXAtaGVhZGVyXCI+e3tjaGF0U2VydmljZS50cmFuc2xhdGlvbnMuY29udGFjdHNVbmFmZmlsaWF0ZWR9fTwvZGl2PlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29udGFjdC1saXN0LXdyYXBwZXJcIj5cblxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgY29udGFjdCBvZiAoY29udGFjdHNVbmFmZmlsaWF0ZWQkIHwgYXN5bmMpXCI+XG4gICAgICAgICAgICAgICAgPG5neC1jaGF0LXJvc3Rlci1yZWNpcGllbnRcbiAgICAgICAgICAgICAgICAgICAgICAgICpuZ0lmPVwiY29udGFjdC5tZXNzYWdlcy5sZW5ndGggPiAwXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIFtyZWNpcGllbnRdPVwiY29udGFjdFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwib25DbGlja1JlY2lwaWVudChjb250YWN0KVwiPlxuICAgICAgICAgICAgICAgIDwvbmd4LWNoYXQtcm9zdGVyLXJlY2lwaWVudD5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgICAgIDwvZGl2PlxuICAgIDwvbmctY29udGFpbmVyPlxuXG4gICAgPGRpdiBjbGFzcz1cInJvc3Rlci1saXN0X19lbXB0eVwiICpuZ0lmPVwiaGFzTm9Db250YWN0cyQgfCBhc3luY1wiPlxuICAgICAgICB7e2NoYXRTZXJ2aWNlLnRyYW5zbGF0aW9ucy5ub0NvbnRhY3RzfX1cbiAgICA8L2Rpdj5cblxuICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCJjaGF0U2VydmljZS5lbmFibGVEZWJ1Z2dpbmdcIj5cbiAgICAgICAgPGhyLz5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29udGFjdC1wZW5kaW5nLXJlcXVlc3QtcmVjZWl2ZWQtd3JhcHBlclwiPlxuICAgICAgICAgICAgZGVidWcgY29udGFjdCByZXF1ZXN0cyBpbjpcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb250YWN0LXBlbmRpbmctcmVxdWVzdC1yZWNlaXZlZFwiXG4gICAgICAgICAgICAgICAgICpuZ0Zvcj1cImxldCByZXF1ZXN0IG9mIChjaGF0U2VydmljZS5jb250YWN0UmVxdWVzdHNSZWNlaXZlZCQgfCBhc3luYylcIj5cbiAgICAgICAgICAgICAgICB7e3JlcXVlc3QubmFtZX19XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgPC9kaXY+XG5cbiAgICAgICAgPGhyLz5cblxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29udGFjdC1wZW5kaW5nLXJlcXVlc3Qtc2VudC13cmFwcGVyXCI+XG4gICAgICAgICAgICBkZWJ1ZyBjb250YWN0IHJlcXVlc3RzIG91dDpcbiAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJjb250YWN0LXBlbmRpbmctcmVxdWVzdC1zZW50XCJcbiAgICAgICAgICAgICAgICAgKm5nRm9yPVwibGV0IHJlcXVlc3Qgb2YgKGNoYXRTZXJ2aWNlLmNvbnRhY3RSZXF1ZXN0c1NlbnQkIHwgYXN5bmMpXCI+XG4gICAgICAgICAgICAgICAge3tyZXF1ZXN0Lm5hbWV9fVxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgIDwvZGl2PlxuXG4gICAgICAgIDxoci8+XG5cbiAgICAgICAgPHA+XG4gICAgICAgICAgICBkZWJ1ZyBjb250YWN0cyAoY291bnQ6IHt7IChjaGF0U2VydmljZS5jb250YWN0cyQgfCBhc3luYykubGVuZ3RoIH19KTxici8+XG4gICAgICAgIDwvcD5cbiAgICAgICAgPHAgKm5nRm9yPVwibGV0IGNvbnRhY3Qgb2YgKGNoYXRTZXJ2aWNlLmNvbnRhY3RzJHxhc3luYylcIiBzdHlsZT1cIm1hcmdpbi1ib3R0b206IDFlbVwiPlxuICAgICAgICAgICAgPGVtPnt7Y29udGFjdC5uYW1lfX06PC9lbT48YnIvPlxuICAgICAgICAgICAgc3Vic2NyaXB0aW9uPXt7Y29udGFjdC5zdWJzY3JpcHRpb24kIHwgYXN5bmN9fTxici8+XG4gICAgICAgICAgICBwcmVzZW5jZT17e2NvbnRhY3QucHJlc2VuY2UkIHwgYXN5bmN9fTxici8+XG4gICAgICAgICAgICBwZW5kaW5nSW49e3tjb250YWN0LnBlbmRpbmdJbiQgfCBhc3luY319PGJyLz5cbiAgICAgICAgICAgIHBlbmRpbmdPdXQ9e3tjb250YWN0LnBlbmRpbmdPdXQkIHwgYXN5bmN9fVxuICAgICAgICA8L3A+XG4gICAgICAgIDxwIGNsYXNzPVwicm9zdGVyLWRlYnVnLXN0YXRlXCI+c3RhdGU6IHt7Y2hhdFNlcnZpY2Uuc3RhdGUkIHwgYXN5bmN9fTwvcD5cbiAgICA8L25nLWNvbnRhaW5lcj5cblxuICAgIDwhLS1cbiAgICA8ZGl2IGNsYXNzPVwicm9zdGVyLWZvb3RlclwiPlxuICAgIDwvZGl2PlxuICAgIC0tPlxuPC9kaXY+XG4iXX0=