UNPKG

@seethelemurcricut/ngx-contextmenu

Version:

An Angular component to show a context menu on an arbitrary component

808 lines (796 loc) 84.5 kB
import { Directive, ElementRef, EventEmitter, Input, Output, TemplateRef, InjectionToken, ChangeDetectorRef, Component, Inject, Optional, Renderer2, ViewChild, ViewChildren, QueryList, HostListener, Injectable, ContentChildren, ViewEncapsulation, NgModule } from '@angular/core'; import { Overlay, ScrollStrategyOptions, OverlayModule } from '@angular/cdk/overlay'; import { Subscription, Subject } from 'rxjs'; import { ActiveDescendantKeyManager } from '@angular/cdk/a11y'; import { ComponentPortal } from '@angular/cdk/portal'; import { first } from 'rxjs/operators'; import { CommonModule } from '@angular/common'; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ContextMenuItemDirective { /** * @param {?} template * @param {?} elementRef */ constructor(template, elementRef) { this.template = template; this.elementRef = elementRef; this.divider = false; this.enabled = true; this.passive = false; this.visible = true; this.execute = new EventEmitter(); this.isActive = false; } /** * @return {?} */ get disabled() { return this.passive || this.divider || !this.evaluateIfFunction(this.enabled, this.currentItem); } /** * @param {?} value * @param {?} item * @return {?} */ evaluateIfFunction(value, item) { if (value instanceof Function) { return value(item); } return value; } /** * @return {?} */ setActiveStyles() { this.isActive = true; } /** * @return {?} */ setInactiveStyles() { this.isActive = false; } /** * @param {?} item * @param {?=} $event * @return {?} */ triggerExecute(item, $event) { if (!this.evaluateIfFunction(this.enabled, item)) { return; } this.execute.emit({ event: $event, item }); } } ContextMenuItemDirective.decorators = [ { type: Directive, args: [{ /* tslint:disable:directive-selector-type */ selector: '[contextMenuItem]', },] }, ]; /** @nocollapse */ ContextMenuItemDirective.ctorParameters = () => [ { type: TemplateRef }, { type: ElementRef } ]; ContextMenuItemDirective.propDecorators = { subMenu: [{ type: Input }], divider: [{ type: Input }], enabled: [{ type: Input }], passive: [{ type: Input }], visible: [{ type: Input }], execute: [{ type: Output }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const /** @type {?} */ CONTEXT_MENU_OPTIONS = new InjectionToken('CONTEXT_MENU_OPTIONS'); /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ const /** @type {?} */ ARROW_LEFT_KEYCODE = 37; class ContextMenuContentComponent { /** * @param {?} changeDetector * @param {?} elementRef * @param {?} options * @param {?} renderer */ constructor(changeDetector, elementRef, options, renderer) { this.changeDetector = changeDetector; this.elementRef = elementRef; this.options = options; this.renderer = renderer; this.menuItems = []; this.isLeaf = false; this.execute = new EventEmitter(); this.openSubMenu = new EventEmitter(); this.closeLeafMenu = new EventEmitter(); this.closeAllMenus = new EventEmitter(); this.autoFocus = false; this.useBootstrap4 = false; this.subscription = new Subscription(); if (options) { this.autoFocus = options.autoFocus; this.useBootstrap4 = options.useBootstrap4; } } /** * @return {?} */ ngOnInit() { this.menuItems.forEach((menuItem) => { menuItem.currentItem = this.item; this.subscription.add(menuItem.execute.subscribe((event) => this.execute.emit(Object.assign({}, event, { menuItem })))); }); const /** @type {?} */ queryList = new QueryList(); queryList.reset(this.menuItems); this._keyManager = new ActiveDescendantKeyManager(queryList).withWrap(); let /** @type {?} */ elementHeight = 14; this.menuItems.forEach((inMenuItem) => { elementHeight += inMenuItem.divider ? 19 : 26; }); (/** @type {?} */ (this.elementRef.nativeElement)).style.height = elementHeight + 'px'; } /** * @return {?} */ ngAfterViewInit() { if (this.autoFocus) { setTimeout(() => this.focus()); } this.overlay.updatePosition(); } /** * @return {?} */ ngOnDestroy() { this.subscription.unsubscribe(); } /** * @return {?} */ focus() { if (this.autoFocus) { this.menuElement.nativeElement.focus(); } } /** * @param {?} $event * @return {?} */ stopEvent($event) { $event.stopPropagation(); } /** * @param {?} menuItem * @return {?} */ isMenuItemEnabled(menuItem) { return this.evaluateIfFunction(menuItem && menuItem.enabled); } /** * @param {?} menuItem * @return {?} */ isMenuItemVisible(menuItem) { return this.evaluateIfFunction(menuItem && menuItem.visible); } /** * @param {?} value * @return {?} */ evaluateIfFunction(value) { if (value instanceof Function) { return value(this.item); } return value; } /** * @param {?} link * @return {?} */ isDisabled(link) { return link.enabled && !link.enabled(this.item); } /** * @param {?} event * @return {?} */ onKeyEvent(event) { if (!this.isLeaf) { return; } this._keyManager.onKeydown(event); } /** * @param {?=} event * @return {?} */ keyboardOpenSubMenu(event) { if (!this.isLeaf) { return; } this.cancelEvent(event); const /** @type {?} */ menuItem = this.menuItems[this._keyManager.activeItemIndex]; if (menuItem) { this.onOpenSubMenu(menuItem); } } /** * @param {?=} event * @return {?} */ keyboardMenuItemSelect(event) { if (!this.isLeaf) { return; } this.cancelEvent(event); const /** @type {?} */ menuItem = this.menuItems[this._keyManager.activeItemIndex]; if (menuItem) { this.onMenuItemSelect(menuItem, event); } } /** * @param {?} event * @return {?} */ onCloseLeafMenu(event) { if (!this.isLeaf) { return; } this.cancelEvent(event); this.closeLeafMenu.emit({ exceptRootMenu: event.keyCode === ARROW_LEFT_KEYCODE, event }); } /** * @param {?} event * @return {?} */ closeMenu(event) { if (event.type === 'click' && event.button === 2) { return; } this.closeAllMenus.emit({ event }); } /** * @param {?} menuItem * @param {?=} event * @return {?} */ onOpenSubMenu(menuItem, event) { const /** @type {?} */ anchorElementRef = this.menuItemElements.toArray()[this._keyManager.activeItemIndex]; const /** @type {?} */ anchorElement = anchorElementRef && anchorElementRef.nativeElement; this.openSubMenu.emit({ anchorElement, contextMenu: menuItem.subMenu, event, item: this.item, parentContextMenu: this, }); } /** * @param {?} menuItem * @param {?} event * @return {?} */ onMenuItemSelect(menuItem, event) { event.preventDefault(); event.stopPropagation(); this.onOpenSubMenu(menuItem, event); if (!menuItem.subMenu) { menuItem.triggerExecute(this.item, event); } } /** * @param {?} event * @return {?} */ cancelEvent(event) { if (!event) { return; } const /** @type {?} */ target = event.target; if (['INPUT', 'TEXTAREA', 'SELECT'].indexOf(target.tagName) > -1 || target.isContentEditable) { return; } event.preventDefault(); event.stopPropagation(); } } ContextMenuContentComponent.decorators = [ { type: Component, args: [{ selector: 'context-menu-content', styles: [ `.passive { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: @line-height-base; white-space: nowrap; } .hasSubMenu:before { content: "\u25B6"; float: right; }`, ], template: `<div class="dropdown open show ngx-contextmenu" [ngClass]="menuClass" tabindex="0"> <ul #menu class="dropdown-menu show" style="position: static; float: none;" tabindex="0"> <li #li *ngFor="let menuItem of menuItems; let i = index" [class.disabled]="!isMenuItemEnabled(menuItem)" [class.divider]="menuItem.divider" [class.dropdown-divider]="useBootstrap4 && menuItem.divider" [class.active]="menuItem.isActive && isMenuItemEnabled(menuItem)" [attr.role]="menuItem.divider ? 'separator' : undefined"> <a *ngIf="!menuItem.divider && !menuItem.passive" href [class.dropdown-item]="useBootstrap4" [class.active]="menuItem.isActive && isMenuItemEnabled(menuItem)" [class.disabled]="useBootstrap4 && !isMenuItemEnabled(menuItem)" [class.hasSubMenu]="!!menuItem.subMenu" (click)="onMenuItemSelect(menuItem, $event)" (mouseenter)="onOpenSubMenu(menuItem, $event)"> <ng-template [ngTemplateOutlet]="menuItem.template" [ngTemplateOutletContext]="{ $implicit: item }"></ng-template> </a> <span (click)="stopEvent($event)" (contextmenu)="stopEvent($event)" class="passive" *ngIf="!menuItem.divider && menuItem.passive" [class.dropdown-item]="useBootstrap4" [class.disabled]="useBootstrap4 && !isMenuItemEnabled(menuItem)"> <ng-template [ngTemplateOutlet]="menuItem.template" [ngTemplateOutletContext]="{ $implicit: item }"></ng-template> </span> </li> </ul> </div> `, },] }, ]; /** @nocollapse */ ContextMenuContentComponent.ctorParameters = () => [ { type: ChangeDetectorRef }, { type: ElementRef }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [CONTEXT_MENU_OPTIONS,] }] }, { type: Renderer2 } ]; ContextMenuContentComponent.propDecorators = { menuItems: [{ type: Input }], item: [{ type: Input }], event: [{ type: Input }], parentContextMenu: [{ type: Input }], menuClass: [{ type: Input }], overlay: [{ type: Input }], isLeaf: [{ type: Input }], execute: [{ type: Output }], openSubMenu: [{ type: Output }], closeLeafMenu: [{ type: Output }], closeAllMenus: [{ type: Output }], menuElement: [{ type: ViewChild, args: ['menu',] }], menuItemElements: [{ type: ViewChildren, args: ['li',] }], onKeyEvent: [{ type: HostListener, args: ['window:keydown.ArrowDown', ['$event'],] }, { type: HostListener, args: ['window:keydown.ArrowUp', ['$event'],] }], keyboardOpenSubMenu: [{ type: HostListener, args: ['window:keydown.ArrowRight', ['$event'],] }], keyboardMenuItemSelect: [{ type: HostListener, args: ['window:keydown.Enter', ['$event'],] }, { type: HostListener, args: ['window:keydown.Space', ['$event'],] }], onCloseLeafMenu: [{ type: HostListener, args: ['window:keydown.Escape', ['$event'],] }, { type: HostListener, args: ['window:keydown.ArrowLeft', ['$event'],] }], closeMenu: [{ type: HostListener, args: ['document:click', ['$event'],] }, { type: HostListener, args: ['document:contextmenu', ['$event'],] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ContextMenuService { /** * @param {?} overlay * @param {?} scrollStrategy */ constructor(overlay, scrollStrategy) { this.overlay = overlay; this.scrollStrategy = scrollStrategy; this.isDestroyingLeafMenu = false; this.show = new Subject(); this.triggerClose = new Subject(); this.close = new Subject(); this.overlays = []; this.fakeElement = { getBoundingClientRect: () => ({ bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }) }; } /** * @param {?} context * @return {?} */ openContextMenu(context) { const { anchorElement, event, parentContextMenu } = context; if (!parentContextMenu) { const /** @type {?} */ mouseEvent = /** @type {?} */ (event); this.fakeElement.getBoundingClientRect = () => ({ bottom: mouseEvent.clientY, height: 0, left: mouseEvent.clientX, right: mouseEvent.clientX, top: mouseEvent.clientY, width: 0 }); this.closeAllContextMenus({ eventType: 'cancel', event }); const /** @type {?} */ positionStrategy = this.overlay .position() .connectedTo(new ElementRef(anchorElement || this.fakeElement), { originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'top' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'center' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'center' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }); this.overlays = [ this.overlay.create({ positionStrategy, panelClass: 'ngx-contextmenu', scrollStrategy: this.scrollStrategy.close() }) ]; this.attachContextMenu(this.overlays[0], context); } else { const /** @type {?} */ positionStrategy = this.overlay .position() .connectedTo(new ElementRef(event ? event.target : anchorElement), { originX: context.menuClass === 'sub-to-side' ? 'end' : 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'top' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'top' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'center' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'center' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'start', overlayY: 'bottom' }) .withFallbackPosition({ originX: 'start', originY: 'top' }, { overlayX: 'end', overlayY: 'bottom' }); const /** @type {?} */ newOverlay = this.overlay.create({ positionStrategy, panelClass: 'ngx-contextmenu', scrollStrategy: this.scrollStrategy.close() }); this.destroySubMenus(parentContextMenu); this.overlays = this.overlays.concat(newOverlay); this.attachContextMenu(newOverlay, context); } } /** * @param {?} overlay * @param {?} context * @return {?} */ attachContextMenu(overlay, context) { const { event, item, menuItems, menuClass } = context; const /** @type {?} */ contextMenuContent = overlay.attach(new ComponentPortal(ContextMenuContentComponent)); contextMenuContent.instance.event = event; contextMenuContent.instance.item = item; contextMenuContent.instance.menuItems = menuItems; contextMenuContent.instance.overlay = overlay; contextMenuContent.instance.isLeaf = true; contextMenuContent.instance.menuClass = menuClass; (/** @type {?} */ (overlay)).contextMenu = contextMenuContent.instance; const /** @type {?} */ subscriptions = new Subscription(); subscriptions.add(contextMenuContent.instance.execute .asObservable() .subscribe((executeEvent) => this.closeAllContextMenus(Object.assign({ eventType: 'execute' }, executeEvent)))); subscriptions.add(contextMenuContent.instance.closeAllMenus .asObservable() .subscribe((closeAllEvent) => this.closeAllContextMenus(Object.assign({ eventType: 'cancel' }, closeAllEvent)))); subscriptions.add(contextMenuContent.instance.closeLeafMenu .asObservable() .subscribe((closeLeafMenuEvent) => this.destroyLeafMenu(closeLeafMenuEvent))); subscriptions.add(contextMenuContent.instance.openSubMenu .asObservable() .subscribe((subMenuEvent) => { this.destroySubMenus(contextMenuContent.instance); if (!subMenuEvent.contextMenu) { contextMenuContent.instance.isLeaf = true; return; } contextMenuContent.instance.isLeaf = false; this.show.next(subMenuEvent); })); contextMenuContent.onDestroy(() => { menuItems.forEach((menuItem) => (menuItem.isActive = false)); subscriptions.unsubscribe(); }); contextMenuContent.changeDetectorRef.detectChanges(); } /** * @param {?} closeEvent * @return {?} */ closeAllContextMenus(closeEvent) { if (this.overlays) { this.close.next(closeEvent); this.overlays.forEach((overlay, index) => { overlay.detach(); overlay.dispose(); }); } this.overlays = []; } /** * @return {?} */ getLastAttachedOverlay() { let /** @type {?} */ overlay = this.overlays[this.overlays.length - 1]; while (this.overlays.length > 1 && overlay && !overlay.hasAttached()) { overlay.detach(); overlay.dispose(); this.overlays = this.overlays.slice(0, -1); overlay = this.overlays[this.overlays.length - 1]; } return overlay; } /** * @param {?=} __0 * @return {?} */ destroyLeafMenu({ exceptRootMenu, event } = {}) { if (this.isDestroyingLeafMenu) { return; } this.isDestroyingLeafMenu = true; setTimeout(() => { const /** @type {?} */ overlay = this.getLastAttachedOverlay(); if (this.overlays.length > 1 && overlay) { overlay.detach(); overlay.dispose(); } if (!exceptRootMenu && this.overlays.length > 0 && overlay) { this.close.next({ eventType: 'cancel', event }); overlay.detach(); overlay.dispose(); } const /** @type {?} */ newLeaf = this.getLastAttachedOverlay(); if (newLeaf) { newLeaf.contextMenu.isLeaf = true; } this.isDestroyingLeafMenu = false; }); } /** * @param {?} contextMenu * @return {?} */ destroySubMenus(contextMenu) { const /** @type {?} */ overlay = contextMenu.overlay; const /** @type {?} */ index = this.overlays.indexOf(overlay); this.overlays.slice(index + 1).forEach((subMenuOverlay) => { subMenuOverlay.detach(); subMenuOverlay.dispose(); }); } /** * @param {?} contextMenuContent * @return {?} */ isLeafMenu(contextMenuContent) { const /** @type {?} */ overlay = this.getLastAttachedOverlay(); return contextMenuContent.overlay === overlay; } } ContextMenuService.decorators = [ { type: Injectable }, ]; /** @nocollapse */ ContextMenuService.ctorParameters = () => [ { type: Overlay }, { type: ScrollStrategyOptions } ]; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ContextMenuComponent { /** * @param {?} _contextMenuService * @param {?} changeDetector * @param {?} elementRef * @param {?} options */ constructor(_contextMenuService, changeDetector, elementRef, options) { this._contextMenuService = _contextMenuService; this.changeDetector = changeDetector; this.elementRef = elementRef; this.options = options; this.menuClass = ""; this.autoFocus = false; this.useBootstrap4 = false; this.disabled = false; this.close = new EventEmitter(); this.open = new EventEmitter(); this.visibleMenuItems = []; this.links = []; this.subscription = new Subscription(); if (options) { this.autoFocus = options.autoFocus; this.useBootstrap4 = options.useBootstrap4; } this.subscription.add(_contextMenuService.show.subscribe(menuEvent => { this.onMenuEvent(menuEvent); })); } /** * @return {?} */ ngOnDestroy() { this.subscription.unsubscribe(); } /** * @param {?} menuEvent * @return {?} */ onMenuEvent(menuEvent) { if (this.disabled) { return; } const { contextMenu, event, item } = menuEvent; if (contextMenu && contextMenu !== this) { return; } this.event = event; this.item = item; this.setVisibleMenuItems(); this._contextMenuService.openContextMenu(Object.assign({}, menuEvent, { menuItems: this.visibleMenuItems, menuClass: this.menuClass })); this._contextMenuService.close.asObservable().pipe(first()).subscribe(closeEvent => this.close.emit(closeEvent)); this.open.next(menuEvent); } /** * @param {?} menuItem * @return {?} */ isMenuItemVisible(menuItem) { return this.evaluateIfFunction(menuItem.visible); } /** * @return {?} */ setVisibleMenuItems() { this.visibleMenuItems = this.menuItems.filter(menuItem => this.isMenuItemVisible(menuItem)); } /** * @param {?} value * @return {?} */ evaluateIfFunction(value) { if (value instanceof Function) { return value(this.item); } return value; } } ContextMenuComponent.decorators = [ { type: Component, args: [{ encapsulation: ViewEncapsulation.None, selector: 'context-menu', styles: [` .cdk-overlay-container { position: fixed; z-index: 1000; pointer-events: none; top: 0; left: 0; width: 100%; height: 100%; } .ngx-contextmenu.cdk-overlay-pane { position: absolute; pointer-events: auto; box-sizing: border-box; } `], template: ` `, },] }, ]; /** @nocollapse */ ContextMenuComponent.ctorParameters = () => [ { type: ContextMenuService }, { type: ChangeDetectorRef }, { type: ElementRef }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [CONTEXT_MENU_OPTIONS,] }] } ]; ContextMenuComponent.propDecorators = { menuClass: [{ type: Input }], autoFocus: [{ type: Input }], useBootstrap4: [{ type: Input }], disabled: [{ type: Input }], close: [{ type: Output }], open: [{ type: Output }], menuItems: [{ type: ContentChildren, args: [ContextMenuItemDirective,] }], menuElement: [{ type: ViewChild, args: ['menu',] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ContextMenuAttachDirective { /** * @param {?} contextMenuService */ constructor(contextMenuService) { this.contextMenuService = contextMenuService; } /** * @param {?} event * @return {?} */ onContextMenu(event) { if (!this.contextMenu.disabled) { this.contextMenuService.show.next({ contextMenu: this.contextMenu, event, item: this.contextMenuSubject, }); event.preventDefault(); event.stopPropagation(); } } } ContextMenuAttachDirective.decorators = [ { type: Directive, args: [{ selector: '[contextMenu]', },] }, ]; /** @nocollapse */ ContextMenuAttachDirective.ctorParameters = () => [ { type: ContextMenuService } ]; ContextMenuAttachDirective.propDecorators = { contextMenuSubject: [{ type: Input }], contextMenu: [{ type: Input }], onContextMenu: [{ type: HostListener, args: ['contextmenu', ['$event'],] }] }; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ class ContextMenuModule { /** * @param {?=} options * @return {?} */ static forRoot(options) { return { ngModule: ContextMenuModule, providers: [ ContextMenuService, { provide: CONTEXT_MENU_OPTIONS, useValue: options, }, ], }; } } ContextMenuModule.decorators = [ { type: NgModule, args: [{ declarations: [ ContextMenuAttachDirective, ContextMenuComponent, ContextMenuContentComponent, ContextMenuItemDirective, ], entryComponents: [ ContextMenuContentComponent, ], exports: [ ContextMenuAttachDirective, ContextMenuComponent, ContextMenuItemDirective, ], imports: [ CommonModule, OverlayModule, ], },] }, ]; /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ /** * @fileoverview added by tsickle * @suppress {checkTypes} checked by tsc */ export { ContextMenuModule, ContextMenuComponent, ContextMenuService, ContextMenuAttachDirective as ɵa, ContextMenuItemDirective as ɵb, CONTEXT_MENU_OPTIONS as ɵc, ContextMenuContentComponent as ɵd }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VldGhlbGVtdXJjcmljdXQtbmd4LWNvbnRleHRtZW51LmpzLm1hcCIsInNvdXJjZXMiOlsibmc6Ly9Ac2VldGhlbGVtdXJjcmljdXQvbmd4LWNvbnRleHRtZW51L2xpYi9jb250ZXh0TWVudS5pdGVtLmRpcmVjdGl2ZS50cyIsIm5nOi8vQHNlZXRoZWxlbXVyY3JpY3V0L25neC1jb250ZXh0bWVudS9saWIvY29udGV4dE1lbnUudG9rZW5zLnRzIiwibmc6Ly9Ac2VldGhlbGVtdXJjcmljdXQvbmd4LWNvbnRleHRtZW51L2xpYi9jb250ZXh0TWVudUNvbnRlbnQuY29tcG9uZW50LnRzIiwibmc6Ly9Ac2VldGhlbGVtdXJjcmljdXQvbmd4LWNvbnRleHRtZW51L2xpYi9jb250ZXh0TWVudS5zZXJ2aWNlLnRzIiwibmc6Ly9Ac2VldGhlbGVtdXJjcmljdXQvbmd4LWNvbnRleHRtZW51L2xpYi9jb250ZXh0TWVudS5jb21wb25lbnQudHMiLCJuZzovL0BzZWV0aGVsZW11cmNyaWN1dC9uZ3gtY29udGV4dG1lbnUvbGliL2NvbnRleHRNZW51LmF0dGFjaC5kaXJlY3RpdmUudHMiLCJuZzovL0BzZWV0aGVsZW11cmNyaWN1dC9uZ3gtY29udGV4dG1lbnUvbGliL25neC1jb250ZXh0bWVudS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBIaWdobGlnaHRhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHsgRGlyZWN0aXZlLCBFbGVtZW50UmVmLCBFdmVudEVtaXR0ZXIsIElucHV0LCBPdXRwdXQsIFRlbXBsYXRlUmVmIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbkBEaXJlY3RpdmUoe1xuICAvKiB0c2xpbnQ6ZGlzYWJsZTpkaXJlY3RpdmUtc2VsZWN0b3ItdHlwZSAqL1xuICBzZWxlY3RvcjogJ1tjb250ZXh0TWVudUl0ZW1dJyxcbiAgLyogdHNsaW50OmVuYWJsZTpkaXJlY3RpdmUtc2VsZWN0b3ItdHlwZSAqL1xufSlcbmV4cG9ydCBjbGFzcyBDb250ZXh0TWVudUl0ZW1EaXJlY3RpdmUgaW1wbGVtZW50cyBIaWdobGlnaHRhYmxlIHtcbiAgQElucHV0KCkgcHVibGljIHN1Yk1lbnU6IGFueTtcbiAgQElucHV0KCkgcHVibGljIGRpdmlkZXIgPSBmYWxzZTtcbiAgQElucHV0KCkgcHVibGljIGVuYWJsZWQ6IGJvb2xlYW4gfCAoKGl0ZW06IGFueSkgPT4gYm9vbGVhbikgPSB0cnVlO1xuICBASW5wdXQoKSBwdWJsaWMgcGFzc2l2ZSA9IGZhbHNlO1xuICBASW5wdXQoKSBwdWJsaWMgdmlzaWJsZTogYm9vbGVhbiB8ICgoaXRlbTogYW55KSA9PiBib29sZWFuKSA9IHRydWU7XG4gIEBPdXRwdXQoKSBwdWJsaWMgZXhlY3V0ZTogRXZlbnRFbWl0dGVyPHsgZXZlbnQ6IEV2ZW50LCBpdGVtOiBhbnkgfT4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgcHVibGljIGN1cnJlbnRJdGVtOiBhbnk7XG4gIHB1YmxpYyBpc0FjdGl2ZSA9IGZhbHNlO1xuICBwdWJsaWMgZ2V0IGRpc2FibGVkKCkge1xuICAgIHJldHVybiB0aGlzLnBhc3NpdmUgfHxcbiAgICAgIHRoaXMuZGl2aWRlciB8fFxuICAgICAgIXRoaXMuZXZhbHVhdGVJZkZ1bmN0aW9uKHRoaXMuZW5hYmxlZCwgdGhpcy5jdXJyZW50SXRlbSk7XG4gIH1cblxuICBjb25zdHJ1Y3RvcihwdWJsaWMgdGVtcGxhdGU6IFRlbXBsYXRlUmVmPHsgaXRlbTogYW55IH0+LCBwdWJsaWMgZWxlbWVudFJlZjogRWxlbWVudFJlZikgeyB9XG5cbiAgcHVibGljIGV2YWx1YXRlSWZGdW5jdGlvbih2YWx1ZTogYW55LCBpdGVtOiBhbnkpOiBhbnkge1xuICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIEZ1bmN0aW9uKSB7XG4gICAgICByZXR1cm4gdmFsdWUoaXRlbSk7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuXG4gIHB1YmxpYyBzZXRBY3RpdmVTdHlsZXMoKTogdm9pZCB7XG4gICAgdGhpcy5pc0FjdGl2ZSA9IHRydWU7XG4gIH1cbiAgcHVibGljIHNldEluYWN0aXZlU3R5bGVzKCk6IHZvaWQge1xuICAgIHRoaXMuaXNBY3RpdmUgPSBmYWxzZTtcbiAgfVxuXG4gIHB1YmxpYyB0cmlnZ2VyRXhlY3V0ZShpdGVtOiBhbnksICRldmVudD86IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmV2YWx1YXRlSWZGdW5jdGlvbih0aGlzLmVuYWJsZWQsIGl0ZW0pKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuZXhlY3V0ZS5lbWl0KHsgZXZlbnQ6ICRldmVudCwgaXRlbSB9KTtcbiAgfVxufVxuIiwiaW1wb3J0IHsgSW5qZWN0aW9uVG9rZW4gfSBmcm9tICdAYW5ndWxhci9jb3JlJztcblxuZXhwb3J0IGNvbnN0IENPTlRFWFRfTUVOVV9PUFRJT05TID0gbmV3IEluamVjdGlvblRva2VuKCdDT05URVhUX01FTlVfT1BUSU9OUycpO1xuIiwiaW1wb3J0IHsgQ2xvc2VMZWFmTWVudUV2ZW50LCBJQ29udGV4dE1lbnVDbGlja0V2ZW50IH0gZnJvbSAnLi9jb250ZXh0TWVudS5zZXJ2aWNlJztcbmltcG9ydCB7IE92ZXJsYXlSZWYgfSBmcm9tICdAYW5ndWxhci9jZGsvb3ZlcmxheSc7XG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgQ29tcG9uZW50LFxuICAgIEVsZW1lbnRSZWYsXG4gICAgSW5qZWN0LFxuICAgIElucHV0LFxuICAgIE9wdGlvbmFsLFxuICAgIFJlbmRlcmVyMixcbiAgICBWaWV3Q2hpbGQsXG4gICAgVmlld0NoaWxkcmVuLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEV2ZW50RW1pdHRlciwgT25EZXN0cm95LCBPbkluaXQsIE91dHB1dCwgUXVlcnlMaXN0LCBIb3N0TGlzdGVuZXIgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IFN1YnNjcmlwdGlvbiB9IGZyb20gJ3J4anMnO1xuXG5pbXBvcnQgeyBDb250ZXh0TWVudUl0ZW1EaXJlY3RpdmUgfSBmcm9tICcuL2NvbnRleHRNZW51Lml0ZW0uZGlyZWN0aXZlJztcbmltcG9ydCB7IElDb250ZXh0TWVudU9wdGlvbnMgfSBmcm9tICcuL2NvbnRleHRNZW51Lm9wdGlvbnMnO1xuaW1wb3J0IHsgQ09OVEVYVF9NRU5VX09QVElPTlMgfSBmcm9tICcuL2NvbnRleHRNZW51LnRva2Vucyc7XG5pbXBvcnQgeyBBY3RpdmVEZXNjZW5kYW50S2V5TWFuYWdlciB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9hMTF5JztcblxuZXhwb3J0IGludGVyZmFjZSBJTGlua0NvbmZpZyB7XG4gIGNsaWNrOiAoaXRlbTogYW55LCAkZXZlbnQ/OiBNb3VzZUV2ZW50KSA9PiB2b2lkO1xuICBlbmFibGVkPzogKGl0ZW06IGFueSkgPT4gYm9vbGVhbjtcbiAgaHRtbDogKGl0ZW06IGFueSkgPT4gc3RyaW5nO1xufVxuXG5jb25zdCBBUlJPV19MRUZUX0tFWUNPREUgPSAzNztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnY29udGV4dC1tZW51LWNvbnRlbnQnLFxuICBzdHlsZXM6IFtcbiAgICBgLnBhc3NpdmUge1xuICAgICAgIGRpc3BsYXk6IGJsb2NrO1xuICAgICAgIHBhZGRpbmc6IDNweCAyMHB4O1xuICAgICAgIGNsZWFyOiBib3RoO1xuICAgICAgIGZvbnQtd2VpZ2h0OiBub3JtYWw7XG4gICAgICAgbGluZS1oZWlnaHQ6IEBsaW5lLWhlaWdodC1iYXNlO1xuICAgICAgIHdoaXRlLXNwYWNlOiBub3dyYXA7XG4gICAgIH1cbiAgICAuaGFzU3ViTWVudTpiZWZvcmUge1xuICAgICAgY29udGVudDogXCJcXHUyNUI2XCI7XG4gICAgICBmbG9hdDogcmlnaHQ7XG4gICAgfWAsXG4gIF0sXG4gIHRlbXBsYXRlOlxuICBgPGRpdiBjbGFzcz1cImRyb3Bkb3duIG9wZW4gc2hvdyBuZ3gtY29udGV4dG1lbnVcIiBbbmdDbGFzc109XCJtZW51Q2xhc3NcIiB0YWJpbmRleD1cIjBcIj5cbiAgICAgIDx1bCAjbWVudSBjbGFzcz1cImRyb3Bkb3duLW1lbnUgc2hvd1wiIHN0eWxlPVwicG9zaXRpb246IHN0YXRpYzsgZmxvYXQ6IG5vbmU7XCIgdGFiaW5kZXg9XCIwXCI+XG4gICAgICAgIDxsaSAjbGkgKm5nRm9yPVwibGV0IG1lbnVJdGVtIG9mIG1lbnVJdGVtczsgbGV0IGkgPSBpbmRleFwiIFtjbGFzcy5kaXNhYmxlZF09XCIhaXNNZW51SXRlbUVuYWJsZWQobWVudUl0ZW0pXCJcbiAgICAgICAgICAgIFtjbGFzcy5kaXZpZGVyXT1cIm1lbnVJdGVtLmRpdmlkZXJcIiBbY2xhc3MuZHJvcGRvd24tZGl2aWRlcl09XCJ1c2VCb290c3RyYXA0ICYmIG1lbnVJdGVtLmRpdmlkZXJcIlxuICAgICAgICAgICAgW2NsYXNzLmFjdGl2ZV09XCJtZW51SXRlbS5pc0FjdGl2ZSAmJiBpc01lbnVJdGVtRW5hYmxlZChtZW51SXRlbSlcIlxuICAgICAgICAgICAgW2F0dHIucm9sZV09XCJtZW51SXRlbS5kaXZpZGVyID8gJ3NlcGFyYXRvcicgOiB1bmRlZmluZWRcIj5cbiAgICAgICAgICA8YSAqbmdJZj1cIiFtZW51SXRlbS5kaXZpZGVyICYmICFtZW51SXRlbS5wYXNzaXZlXCIgaHJlZiBbY2xhc3MuZHJvcGRvd24taXRlbV09XCJ1c2VCb290c3RyYXA0XCJcbiAgICAgICAgICAgIFtjbGFzcy5hY3RpdmVdPVwibWVudUl0ZW0uaXNBY3RpdmUgJiYgaXNNZW51SXRlbUVuYWJsZWQobWVudUl0ZW0pXCJcbiAgICAgICAgICAgIFtjbGFzcy5kaXNhYmxlZF09XCJ1c2VCb290c3RyYXA0ICYmICFpc01lbnVJdGVtRW5hYmxlZChtZW51SXRlbSlcIiBbY2xhc3MuaGFzU3ViTWVudV09XCIhIW1lbnVJdGVtLnN1Yk1lbnVcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm9uTWVudUl0ZW1TZWxlY3QobWVudUl0ZW0sICRldmVudClcIiAobW91c2VlbnRlcik9XCJvbk9wZW5TdWJNZW51KG1lbnVJdGVtLCAkZXZlbnQpXCI+XG4gICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nVGVtcGxhdGVPdXRsZXRdPVwibWVudUl0ZW0udGVtcGxhdGVcIiBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwieyAkaW1wbGljaXQ6IGl0ZW0gfVwiPjwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgPC9hPlxuXG4gICAgICAgICAgPHNwYW4gKGNsaWNrKT1cInN0b3BFdmVudCgkZXZlbnQpXCIgKGNvbnRleHRtZW51KT1cInN0b3BFdmVudCgkZXZlbnQpXCIgY2xhc3M9XCJwYXNzaXZlXCJcbiAgICAgICAgICAgICAgICAqbmdJZj1cIiFtZW51SXRlbS5kaXZpZGVyICYmIG1lbnVJdGVtLnBhc3NpdmVcIiBbY2xhc3MuZHJvcGRvd24taXRlbV09XCJ1c2VCb290c3RyYXA0XCJcbiAgICAgICAgICAgICAgICBbY2xhc3MuZGlzYWJsZWRdPVwidXNlQm9vdHN0cmFwNCAmJiAhaXNNZW51SXRlbUVuYWJsZWQobWVudUl0ZW0pXCI+XG4gICAgICAgICAgICA8bmctdGVtcGxhdGUgW25nVGVtcGxhdGVPdXRsZXRdPVwibWVudUl0ZW0udGVtcGxhdGVcIiBbbmdUZW1wbGF0ZU91dGxldENvbnRleHRdPVwieyAkaW1wbGljaXQ6IGl0ZW0gfVwiPjwvbmctdGVtcGxhdGU+XG4gICAgICAgICAgPC9zcGFuPlxuICAgICAgICA8L2xpPlxuICAgICAgPC91bD5cbiAgICA8L2Rpdj5cbiAgYCxcbn0pXG5leHBvcnQgY2xhc3MgQ29udGV4dE1lbnVDb250ZW50Q29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3ksIEFmdGVyVmlld0luaXQge1xuICBASW5wdXQoKSBwdWJsaWMgbWVudUl0ZW1zOiBDb250ZXh0TWVudUl0ZW1EaXJlY3RpdmVbXSA9IFtdO1xuICBASW5wdXQoKSBwdWJsaWMgaXRlbTogYW55O1xuICBASW5wdXQoKSBwdWJsaWMgZXZlbnQ6IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50O1xuICBASW5wdXQoKSBwdWJsaWMgcGFyZW50Q29udGV4dE1lbnU6IENvbnRleHRNZW51Q29udGVudENvbXBvbmVudDtcbiAgQElucHV0KCkgcHVibGljIG1lbnVDbGFzczogc3RyaW5nO1xuICBASW5wdXQoKSBwdWJsaWMgb3ZlcmxheTogT3ZlcmxheVJlZjtcbiAgQElucHV0KCkgcHVibGljIGlzTGVhZiA9IGZhbHNlO1xuICBAT3V0cHV0KCkgcHVibGljIGV4ZWN1dGU6IEV2ZW50RW1pdHRlcjx7IGV2ZW50OiBNb3VzZUV2ZW50IHwgS2V5Ym9hcmRFdmVudCwgaXRlbTogYW55LCBtZW51SXRlbTogQ29udGV4dE1lbnVJdGVtRGlyZWN0aXZlIH0+XG4gICAgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG4gIEBPdXRwdXQoKSBwdWJsaWMgb3BlblN1Yk1lbnU6IEV2ZW50RW1pdHRlcjxJQ29udGV4dE1lbnVDbGlja0V2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgpIHB1YmxpYyBjbG9zZUxlYWZNZW51OiBFdmVudEVtaXR0ZXI8Q2xvc2VMZWFmTWVudUV2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQE91dHB1dCgpIHB1YmxpYyBjbG9zZUFsbE1lbnVzOiBFdmVudEVtaXR0ZXI8eyBldmVudDogTW91c2VFdmVudCB9PiA9IG5ldyBFdmVudEVtaXR0ZXIoKTtcbiAgQFZpZXdDaGlsZCgnbWVudScpIHB1YmxpYyBtZW51RWxlbWVudDogRWxlbWVudFJlZjtcbiAgQFZpZXdDaGlsZHJlbignbGknKSBwdWJsaWMgbWVudUl0ZW1FbGVtZW50czogUXVlcnlMaXN0PEVsZW1lbnRSZWY+O1xuXG4gIHB1YmxpYyBhdXRvRm9jdXMgPSBmYWxzZTtcbiAgcHVibGljIHVzZUJvb3RzdHJhcDQgPSBmYWxzZTtcbiAgcHJpdmF0ZSBfa2V5TWFuYWdlcjogQWN0aXZlRGVzY2VuZGFudEtleU1hbmFnZXI8Q29udGV4dE1lbnVJdGVtRGlyZWN0aXZlPjtcbiAgcHJpdmF0ZSBzdWJzY3JpcHRpb246IFN1YnNjcmlwdGlvbiA9IG5ldyBTdWJzY3JpcHRpb24oKTtcbiAgY29uc3RydWN0b3IoXG4gICAgcHJpdmF0ZSBjaGFuZ2VEZXRlY3RvcjogQ2hhbmdlRGV0ZWN0b3JSZWYsXG4gICAgcHJpdmF0ZSBlbGVtZW50UmVmOiBFbGVtZW50UmVmLFxuICAgIEBPcHRpb25hbCgpXG4gICAgQEluamVjdChDT05URVhUX01FTlVfT1BUSU9OUykgcHJpdmF0ZSBvcHRpb25zOiBJQ29udGV4dE1lbnVPcHRpb25zLFxuICAgIHB1YmxpYyByZW5kZXJlcjogUmVuZGVyZXIyLFxuICApIHtcbiAgICBpZiAob3B0aW9ucykge1xuICAgICAgdGhpcy5hdXRvRm9jdXMgPSBvcHRpb25zLmF1dG9Gb2N1cztcbiAgICAgIHRoaXMudXNlQm9vdHN0cmFwNCA9IG9wdGlvbnMudXNlQm9vdHN0cmFwNDtcbiAgICB9XG4gIH1cblxuICBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgIHRoaXMubWVudUl0ZW1zLmZvckVhY2goKG1lbnVJdGVtKSA9PiB7XG4gICAgICAgICAgbWVudUl0ZW0uY3VycmVudEl0ZW0gPSB0aGlzLml0ZW07XG4gICAgICAgICAgdGhpcy5zdWJzY3JpcHRpb24uYWRkKFxuICAgICAgICAgICAgICBtZW51SXRlbS5leGVjdXRlLnN1YnNjcmliZSgoZXZlbnQpID0+IHRoaXMuZXhlY3V0ZS5lbWl0KHsgLi4uZXZlbnQsIG1lbnVJdGVtIH0pKVxuICAgICAgICAgICk7XG4gICAgICB9KTtcbiAgICAgIGNvbnN0IHF1ZXJ5TGlzdCA9IG5ldyBRdWVyeUxpc3Q8Q29udGV4dE1lbnVJdGVtRGlyZWN0aXZlPigpO1xuICAgICAgcXVlcnlMaXN0LnJlc2V0KHRoaXMubWVudUl0ZW1zKTtcbiAgICAgIHRoaXMuX2tleU1hbmFnZXIgPSBuZXcgQWN0aXZlRGVzY2VuZGFudEtleU1hbmFnZXI8Q29udGV4dE1lbnVJdGVtRGlyZWN0aXZlPihcbiAgICAgICAgICBxdWVyeUxpc3RcbiAgICAgICkud2l0aFdyYXAoKTtcblxuICAgICAgbGV0IGVsZW1lbnRIZWlnaHQgPSAxNDtcblxuICAgICAgdGhpcy5tZW51SXRlbXMuZm9yRWFjaCgoaW5NZW51SXRlbSkgPT4ge1xuICAgICAgICAgIGVsZW1lbnRIZWlnaHQgKz0gaW5NZW51SXRlbS5kaXZpZGVyID8gMTkgOiAyNjtcbiAgICAgIH0pO1xuXG4gICAgICAodGhpcy5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQpLnN0eWxlLmhlaWdodCA9IGVsZW1lbnRIZWlnaHQgKyAncHgnO1xuICB9XG5cbiAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgIGlmICh0aGlzLmF1dG9Gb2N1cykge1xuICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLmZvY3VzKCkpO1xuICAgIH1cbiAgICB0aGlzLm92ZXJsYXkudXBkYXRlUG9zaXRpb24oKTtcbiAgfVxuXG4gIG5nT25EZXN0cm95KCkge1xuICAgIHRoaXMuc3Vic2NyaXB0aW9uLnVuc3Vic2NyaWJlKCk7XG4gIH1cblxuICBmb2N1cygpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5hdXRvRm9jdXMpIHtcbiAgICAgIHRoaXMubWVudUVsZW1lbnQubmF0aXZlRWxlbWVudC5mb2N1cygpO1xuICAgIH1cbiAgfVxuXG4gIHN0b3BFdmVudCgkZXZlbnQ6IE1vdXNlRXZlbnQpIHtcbiAgICAkZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cblxuICBwdWJsaWMgaXNNZW51SXRlbUVuYWJsZWQobWVudUl0ZW06IENvbnRleHRNZW51SXRlbURpcmVjdGl2ZSk6IGJvb2xlYW4ge1xuICAgIHJldHVybiB0aGlzLmV2YWx1YXRlSWZGdW5jdGlvbihtZW51SXRlbSAmJiBtZW51SXRlbS5lbmFibGVkKTtcbiAgfVxuXG4gIHB1YmxpYyBpc01lbnVJdGVtVmlzaWJsZShtZW51SXRlbTogQ29udGV4dE1lbnVJdGVtRGlyZWN0aXZlKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHRoaXMuZXZhbHVhdGVJZkZ1bmN0aW9uKG1lbnVJdGVtICYmIG1lbnVJdGVtLnZpc2libGUpO1xuICB9XG5cbiAgcHVibGljIGV2YWx1YXRlSWZGdW5jdGlvbih2YWx1ZTogYW55KTogYW55IHtcbiAgICBpZiAodmFsdWUgaW5zdGFuY2VvZiBGdW5jdGlvbikge1xuICAgICAgcmV0dXJuIHZhbHVlKHRoaXMuaXRlbSk7XG4gICAgfVxuICAgIHJldHVybiB2YWx1ZTtcbiAgfVxuXG4gIHB1YmxpYyBpc0Rpc2FibGVkKGxpbms6IElMaW5rQ29uZmlnKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIGxpbmsuZW5hYmxlZCAmJiAhbGluay5lbmFibGVkKHRoaXMuaXRlbSk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6a2V5ZG93bi5BcnJvd0Rvd24nLCBbJyRldmVudCddKVxuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6a2V5ZG93bi5BcnJvd1VwJywgWyckZXZlbnQnXSlcbiAgcHVibGljIG9uS2V5RXZlbnQoZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNMZWFmKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuX2tleU1hbmFnZXIub25LZXlkb3duKGV2ZW50KTtcbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duLkFycm93UmlnaHQnLCBbJyRldmVudCddKVxuICBwdWJsaWMga2V5Ym9hcmRPcGVuU3ViTWVudShldmVudD86IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNMZWFmKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuY2FuY2VsRXZlbnQoZXZlbnQpO1xuICAgIGNvbnN0IG1lbnVJdGVtID0gdGhpcy5tZW51SXRlbXNbdGhpcy5fa2V5TWFuYWdlci5hY3RpdmVJdGVtSW5kZXhdO1xuICAgIGlmIChtZW51SXRlbSkge1xuICAgICAgdGhpcy5vbk9wZW5TdWJNZW51KG1lbnVJdGVtKTtcbiAgICB9XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCd3aW5kb3c6a2V5ZG93bi5FbnRlcicsIFsnJGV2ZW50J10pXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duLlNwYWNlJywgWyckZXZlbnQnXSlcbiAgcHVibGljIGtleWJvYXJkTWVudUl0ZW1TZWxlY3QoZXZlbnQ/OiBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgaWYgKCF0aGlzLmlzTGVhZikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNhbmNlbEV2ZW50KGV2ZW50KTtcbiAgICBjb25zdCBtZW51SXRlbSA9IHRoaXMubWVudUl0ZW1zW3RoaXMuX2tleU1hbmFnZXIuYWN0aXZlSXRlbUluZGV4XTtcbiAgICBpZiAobWVudUl0ZW0pIHtcbiAgICAgIHRoaXMub25NZW51SXRlbVNlbGVjdChtZW51SXRlbSwgZXZlbnQpO1xuICAgIH1cbiAgfVxuXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duLkVzY2FwZScsIFsnJGV2ZW50J10pXG4gIEBIb3N0TGlzdGVuZXIoJ3dpbmRvdzprZXlkb3duLkFycm93TGVmdCcsIFsnJGV2ZW50J10pXG4gIHB1YmxpYyBvbkNsb3NlTGVhZk1lbnUoZXZlbnQ6IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIXRoaXMuaXNMZWFmKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMuY2FuY2VsRXZlbnQoZXZlbnQpO1xuICAgIHRoaXMuY2xvc2VMZWFmTWVudS5lbWl0KHsgZXhjZXB0Um9vdE1lbnU6IGV2ZW50LmtleUNvZGUgPT09IEFSUk9XX0xFRlRfS0VZQ09ERSwgZXZlbnQgfSk7XG4gIH1cblxuICBASG9zdExpc3RlbmVyKCdkb2N1bWVudDpjbGljaycsIFsnJGV2ZW50J10pXG4gIEBIb3N0TGlzdGVuZXIoJ2RvY3VtZW50OmNvbnRleHRtZW51JywgWyckZXZlbnQnXSlcbiAgcHVibGljIGNsb3NlTWVudShldmVudDogTW91c2VFdmVudCk6IHZvaWQge1xuICAgIGlmIChldmVudC50eXBlID09PSAnY2xpY2snICYmIGV2ZW50LmJ1dHRvbiA9PT0gMikge1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICB0aGlzLmNsb3NlQWxsTWVudXMuZW1pdCh7ZXZlbnR9KTtcbiAgfVxuXG4gIHB1YmxpYyBvbk9wZW5TdWJNZW51KG1lbnVJdGVtOiBDb250ZXh0TWVudUl0ZW1EaXJlY3RpdmUsIGV2ZW50PzogTW91c2VFdmVudCB8IEtleWJvYXJkRXZlbnQpOiB2b2lkIHtcbiAgICBjb25zdCBhbmNob3JFbGVtZW50UmVmID0gdGhpcy5tZW51SXRlbUVsZW1lbnRzLnRvQXJyYXkoKVt0aGlzLl9rZXlNYW5hZ2VyLmFjdGl2ZUl0ZW1JbmRleF07XG4gICAgY29uc3QgYW5jaG9yRWxlbWVudCA9IGFuY2hvckVsZW1lbnRSZWYgJiYgYW5jaG9yRWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xuICAgIHRoaXMub3BlblN1Yk1lbnUuZW1pdCh7XG4gICAgICBhbmNob3JFbGVtZW50LFxuICAgICAgY29udGV4dE1lbnU6IG1lbnVJdGVtLnN1Yk1lbnUsXG4gICAgICBldmVudCxcbiAgICAgIGl0ZW06IHRoaXMuaXRlbSxcbiAgICAgIHBhcmVudENvbnRleHRNZW51OiB0aGlzLFxuICAgIH0pO1xuICB9XG5cbiAgcHVibGljIG9uTWVudUl0ZW1TZWxlY3QobWVudUl0ZW06IENvbnRleHRNZW51SXRlbURpcmVjdGl2ZSwgZXZlbnQ6IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50KTogdm9pZCB7XG4gICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICBldmVudC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICB0aGlzLm9uT3BlblN1Yk1lbnUobWVudUl0ZW0sIGV2ZW50KTtcbiAgICBpZiAoIW1lbnVJdGVtLnN1Yk1lbnUpIHtcbiAgICAgIG1lbnVJdGVtLnRyaWdnZXJFeGVjdXRlKHRoaXMuaXRlbSwgZXZlbnQpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY2FuY2VsRXZlbnQoZXZlbnQpOiB2b2lkIHtcbiAgICBpZiAoIWV2ZW50KSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgY29uc3QgdGFyZ2V0OiBIVE1MRWxlbWVudCA9IGV2ZW50LnRhcmdldDtcbiAgICBpZiAoWydJTlBVVCcsICdURVhUQVJFQScsICdTRUxFQ1QnXS5pbmRleE9mKHRhcmdldC50YWdOYW1lKSA+IC0xIHx8IHRhcmdldC5pc0NvbnRlbnRFZGl0YWJsZSkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgZXZlbnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gIH1cbn1cbiIsImltcG9ydCB7IE92ZXJsYXksIE92ZXJsYXlSZWYsIFNjcm9sbFN0cmF0ZWd5T3B0aW9ucyB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9vdmVybGF5JztcbmltcG9ydCB7IENvbXBvbmVudFBvcnRhbCB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9wb3J0YWwnO1xuaW1wb3J0IHsgQ29tcG9uZW50UmVmLCBFbGVtZW50UmVmLCBJbmplY3RhYmxlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBTdWJqZWN0LCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcblxuaW1wb3J0IHsgQ29udGV4dE1lbnVDb21wb25lbnQgfSBmcm9tICcuL2NvbnRleHRNZW51LmNvbXBvbmVudCc7XG5pbXBvcnQgeyBDb250ZXh0TWVudUl0ZW1EaXJlY3RpdmUgfSBmcm9tICcuL2NvbnRleHRNZW51Lml0ZW0uZGlyZWN0aXZlJztcbmltcG9ydCB7IENvbnRleHRNZW51Q29udGVudENvbXBvbmVudCB9IGZyb20gJy4vY29udGV4dE1lbnVDb250ZW50LmNvbXBvbmVudCc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgSUNvbnRleHRNZW51Q2xpY2tFdmVudCB7XG4gICAgYW5jaG9yRWxlbWVudD86IEVsZW1lbnQgfCBFdmVudFRhcmdldDtcbiAgICBjb250ZXh0TWVudT86IENvbnRleHRNZW51Q29tcG9uZW50O1xuICAgIGV2ZW50PzogTW91c2VFdmVudCB8IEtleWJvYXJkRXZlbnQ7XG4gICAgcGFyZW50Q29udGV4dE1lbnU/OiBDb250ZXh0TWVudUNvbnRlbnRDb21wb25lbnQ7XG4gICAgaXRlbTogYW55O1xuICAgIGFjdGl2ZU1lbnVJdGVtSW5kZXg/OiBudW1iZXI7XG59XG5leHBvcnQgaW50ZXJmYWNlIElDb250ZXh0TWVudUNvbnRleHQgZXh0ZW5kcyBJQ29udGV4dE1lbnVDbGlja0V2ZW50IHtcbiAgICBtZW51SXRlbXM6IENvbnRleHRNZW51SXRlbURpcmVjdGl2ZVtdO1xuICAgIG1lbnVDbGFzczogc3RyaW5nO1xufVxuZXhwb3J0IGludGVyZmFjZSBDbG9zZUxlYWZNZW51RXZlbnQge1xuICAgIGV4Y2VwdFJvb3RNZW51PzogYm9vbGVhbjtcbiAgICBldmVudD86IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50O1xufVxuZXhwb3J0IGludGVyZmFjZSBPdmVybGF5UmVmV2l0aENvbnRleHRNZW51IGV4dGVuZHMgT3ZlcmxheVJlZiB7XG4gICAgY29udGV4dE1lbnU/OiBDb250ZXh0TWVudUNvbnRlbnRDb21wb25lbnQ7XG59XG5cbmV4cG9ydCBpbnRlcmZhY2UgQ2FuY2VsQ29udGV4dE1lbnVFdmVudCB7XG4gICAgZXZlbnRUeXBlOiAnY2FuY2VsJztcbiAgICBldmVudD86IE1vdXNlRXZlbnQgfCBLZXlib2FyZEV2ZW50O1xufVxuZXhwb3J0IGludGVyZmFjZSBFeGVjdXRlQ29udGV4dE1lbnVFdmVudCB7XG4gICAgZXZlbnRUeXBlOiAnZXhlY3V0ZSc7XG4gICAgZXZlbnQ/OiBNb3VzZUV2ZW50IHwgS2V5Ym9hcmRFdmVudDtcbiAgICBpdGVtOiBhbnk7XG4gICAgbWVudUl0ZW06IENvbnRleHRNZW51SXRlbURpcmVjdGl2ZTtcbn1cbmV4cG9ydCB0eXBlIENsb3NlQ29udGV4dE1lbnVFdmVudCA9IEV4ZWN1dGVDb250ZXh0TWVudUV2ZW50IHwgQ2FuY2VsQ29udGV4dE1lbnVFdmVudDtcblxuQEluamVjdGFibGUoKVxuZXhwb3J0IGNsYXNzIENvbnRleHRNZW51U2VydmljZSB7XG4gICAgcHVibGljIGlzRGVzdHJveWluZ0xlYWZNZW51ID0gZmFsc2U7XG5cbiAgICBwdWJsaWMgc2hvdzogU3ViamVjdDxJQ29udGV4dE1lbnVDbGlja0V2ZW50PiA9IG5ldyBTdWJqZWN0PElDb250ZXh0TWVudUNsaWNrRXZlbnQ+KCk7XG4gICAgcHVibGljIHRyaWdnZXJDbG9zZTogU3ViamVjdDxDb250ZXh0TWVudUNvbnRlbnRDb21wb25lbnQ+ID0gbmV3IFN1YmplY3QoKTtcbiAgICBwdWJsaWMgY2xvc2U6IFN1YmplY3Q8Q2xvc2VDb250ZXh0TWVudUV2ZW50PiA9IG5ldyBTdWJqZWN0KCk7XG5cbiAgICBwcml2YXRlIGNvbnRleHRNZW51Q29udGVudDogQ29tcG9uZW50UmVmPENvbnRleHRNZW51Q29udGVudENvbXBvbmVudD47XG4gICAgcHJpdmF0ZSBvdmVybGF5czogT3ZlcmxheVJlZltdID0gW107XG4gICAgcHJpdmF0ZSBmYWtlRWxlbWVudDogYW55ID0ge1xuICAgICAgICBnZXRCb3VuZGluZ0NsaWVudFJlY3Q6ICgpOiBDbGllbnRSZWN0ID0+ICh7XG4gICAgICAgICAgICBib3R0b206IDAsXG4gICAgICAgICAgICBoZWlnaHQ6IDAsXG4gICAgICAgICAgICBsZWZ0OiAwLFxuICAgICAgICAgICAgcmlnaHQ6IDAsXG4gICAgICAgICAgICB0b3A6IDAsXG4gICAgICAgICAgICB3aWR0aDogMFxuICAgICAgICB9KVxuICAgIH07XG5cbiAgICBjb25zdHJ1Y3Rvcihwcml2YXRlIG92ZXJsYXk6IE92ZXJsYXksIHByaXZhdGUgc2Nyb2xsU3RyYXRlZ3k6IFNjcm9sbFN0cmF0ZWd5T3B0aW9ucykge31cblxuICAgIHB1YmxpYyBvcGVuQ29udGV4dE1lbnUoY29udGV4dDogSUNvbnRleHRNZW51Q29udGV4dCkge1xuICAgICAgICBjb25zdCB7IGFuY2hvckVsZW1lbnQsIGV2ZW50LCBwYXJlbnRDb250ZXh0TWVudSB9ID0gY29udGV4dDtcblxuICAgICAgICBpZiAoIXBhcmVudENvbnRleHRNZW51KSB7XG4gICAgICAgICAgICBjb25zdCBtb3VzZUV2ZW50ID0gZXZlbnQgYXMgTW91c2VFdmVudDtcbiAgICAgICAgICAgIHRoaXMuZmFrZUVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0ID0gKCk6IENsaWVudFJlY3QgPT4gKHtcbiAgICAgICAgICAgICAgICBib3R0b206IG1vdXNlRXZlbnQuY2xpZW50WSxcbiAgICAgICAgICAgICAgICBoZWlnaHQ6IDAsXG4gICAgICAgICAgICAgICAgbGVmdDogbW91c2VFdmVudC5jbGllbnRYLFxuICAgICAgICAgICAgICAgIHJpZ2h0OiBtb3VzZUV2ZW50LmNsaWVudFgsXG4gICAgICAgICAgICAgICAgdG9wOiBtb3VzZUV2ZW50LmNsaWVudFksXG4gICAgICAgICAgICAgICAgd2lkdGg6IDBcbiAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgdGhpcy5jbG9zZUFsbENvbnRleHRNZW51cyh7IGV2ZW50VHlwZTogJ2NhbmNlbCcsIGV2ZW50IH0pO1xuICAgICAgICAgICAgY29uc3QgcG9zaXRpb25TdHJhdGVneSA9IHRoaXMub3ZlcmxheVxuICAgICAgICAgICAgICAgIC5wb3NpdGlvbigpXG4gICAgICAgICAgICAgICAgLmNvbm5lY3RlZFRvKFxuICAgICAgICAgICAgICAgICAgICBuZXcgRWxlbWVudFJlZihhbmNob3JFbGVtZW50IHx8IHRoaXMuZmFrZUVsZW1lbnQpLFxuICAgICAgICAgICAgICAgICAgICB7IG9yaWdpblg6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAndG9wJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICd0b3AnIH1cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLndpdGhGYWxsYmFja1Bvc2l0aW9uKFxuICAgICAgICAgICAgICAgICAgICB7IG9yaWdpblg6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAnY2VudGVyJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICdjZW50ZXInIH1cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLndpdGhGYWxsYmFja1Bvc2l0aW9uKFxuICAgICAgICAgICAgICAgICAgICB7IG9yaWdpblg6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAnYm90dG9tJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICdib3R0b20nIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgdGhpcy5vdmVybGF5cyA9IFtcbiAgICAgICAgICAgICAgICB0aGlzLm92ZXJsYXkuY3JlYXRlKHtcbiAgICAgICAgICAgICAgICAgICAgcG9zaXRpb25TdHJhdGVneSxcbiAgICAgICAgICAgICAgICAgICAgcGFuZWxDbGFzczogJ25neC1jb250ZXh0bWVudScsXG4gICAgICAgICAgICAgICAgICAgIHNjcm9sbFN0cmF0ZWd5OiB0aGlzLnNjcm9sbFN0cmF0ZWd5LmNsb3NlKClcbiAgICAgICAgICAgICAgICB9KVxuICAgICAgICAgICAgXTtcbiAgICAgICAgICAgIHRoaXMuYXR0YWNoQ29udGV4dE1lbnUodGhpcy5vdmVybGF5c1swXSwgY29udGV4dCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBjb25zdCBwb3NpdGlvblN0cmF0ZWd5ID0gdGhpcy5vdmVybGF5XG4gICAgICAgICAgICAgICAgLnBvc2l0aW9uKClcbiAgICAgICAgICAgICAgICAuY29ubmVjdGVkVG8oXG4gICAgICAgICAgICAgICAgICAgIG5ldyBFbGVtZW50UmVmKGV2ZW50ID8gZXZlbnQudGFyZ2V0IDogYW5jaG9yRWxlbWVudCksXG4gICAgICAgICAgICAgICAgICAgIHsgb3JpZ2luWDogY29udGV4dC5tZW51Q2xhc3MgPT09ICdzdWItdG8tc2lkZScgPyAnZW5kJyA6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAndG9wJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICd0b3AnIH1cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLndpdGhGYWxsYmFja1Bvc2l0aW9uKFxuICAgICAgICAgICAgICAgICAgICB7IG9yaWdpblg6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAnY2VudGVyJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICdjZW50ZXInIH1cbiAgICAgICAgICAgICAgICApXG4gICAgICAgICAgICAgICAgLndpdGhGYWxsYmFja1Bvc2l0aW9uKFxuICAgICAgICAgICAgICAgICAgICB7IG9yaWdpblg6ICdzdGFydCcsIG9yaWdpblk6ICd0b3AnIH0sXG4gICAgICAgICAgICAgICAgICAgIHsgb3ZlcmxheVg6ICdzdGFydCcsIG92ZXJsYXlZOiAnYm90dG9tJyB9XG4gICAgICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgIC53aXRoRmFsbGJhY2tQb3NpdGlvbihcbiAgICAgICAgICAgICAgICAgICAgeyBvcmlnaW5YOiAnc3RhcnQnLCBvcmlnaW5ZOiAndG9wJyB9LFxuICAgICAgICAgICAgICAgICAgICB7IG92ZXJsYXlYOiAnZW5kJywgb3ZlcmxheVk6ICdib3R0b20nIH1cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgY29uc3QgbmV3T3ZlcmxheSA9IHRoaXMub3ZlcmxheS5jcmVhdGUoe1xuICAgICAgICAgICAgICAgIHBvc2l0aW9uU3RyYXRlZ3ksXG4gICAgICAgICAgICAgICAgcGFuZWxDbGFzczogJ25neC1jb250ZXh0bWVudScsXG4gICAgICAgICAgICAgICAgc2Nyb2xsU3RyYXRlZ3k6IHRoaXMuc2Nyb2xsU3RyYXRlZ3kuY2xvc2UoKVxuICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB0aGlzLmRlc3Ryb3lTdWJNZW51cyhwYXJlbnRDb250ZXh0TWVudSk7XG4gICAgICAgICAgICB0aGlzLm92ZXJsYXlzID0gdGhpcy5vdmVybGF5cy5jb25jYXQobmV3T3ZlcmxheSk7XG4gICAgICAgICAgICB0aGlzLmF0dGFjaENvbnRleHRNZW51KG5ld092ZXJsYXksI