UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

407 lines 61.6 kB
import { coerceBooleanProperty } from '@angular/cdk/coercion'; import { DOWN_ARROW, LEFT_ARROW, RIGHT_ARROW, UP_ARROW } from '@angular/cdk/keycodes'; import { Overlay } from '@angular/cdk/overlay'; import { TemplatePortal } from '@angular/cdk/portal'; import { ContentChildren, Directive, ElementRef, EventEmitter, HostListener, Input, Output, ViewContainerRef, forwardRef, inject } from '@angular/core'; import { Subject, combineLatest, merge, of, timer } from 'rxjs'; import { debounceTime, filter, take, takeUntil } from 'rxjs/operators'; import { FocusIndicatorOriginService, FocusIndicatorService } from '../../../directives/accessibility/index'; import { OverlayPlacementService } from '../../../services/overlay-placement/index'; import { MenuItemComponent } from '../menu-item/menu-item.component'; import { MenuComponent } from '../menu/menu.component'; import * as i0 from "@angular/core"; export class MenuTriggerDirective { constructor() { this._overlay = inject(Overlay); this._elementRef = inject(ElementRef); this._viewContainerRef = inject(ViewContainerRef); this._focusOrigin = inject(FocusIndicatorOriginService); this._focusIndicatorService = inject(FocusIndicatorService); this._overlayPlacement = inject(OverlayPlacementService); this._parentMenu = inject(MenuComponent, { optional: true }); this._menuItem = inject(MenuItemComponent, { optional: true, self: true }); /** Determine if we should disable the trigger */ this.disabled = false; /** Emit when the menu is closed */ this.closed = new EventEmitter(); /** Automatically unsubscribe on directive destroy */ this._onDestroy$ = new Subject(); /** Reference to the menu should close when it loses focus */ this._closeOnBlur = false; this._debounceTime = 50; } /** Determine if the menu should close when it loses focus */ set closeOnBlur(value) { this._closeOnBlur = coerceBooleanProperty(value); } get closeOnBlur() { return this._closeOnBlur; } /** Get the aria controls for accessibility */ get ariaControls() { return this.menu?.isMenuOpen ? this.menu?.innerId : null; } /** Determine if this triggers a submenu */ get _isSubmenuTrigger() { return !!this._parentMenu; } /** Determine if this is the root trigger */ get _isRootTrigger() { return !this._isSubmenuTrigger; } /** If this is a submenu we want to know when the mouse leaves the items or parent item */ get _menuShouldClose() { if (!this._isSubmenuTrigger) { return of(); } // This combined observable will essentially check for all of the combinations of events that can cause a menu // to remain open, for example: // // 1. Hovering over any item in the menu should keep the menu open // 2. Having any item in the menu focused should keep the menu open // 3. Having the parent menu item hovered should keep a submenu open // 4. Having the parent menu item focused should keep a submenu open // 5. Having a submenu open should keep the parent open (if the submenu meets one of the above conditions) // // We also debounce this because there is often a delay between a blur and a focus event or moving the mouse // from a menu item to a sub menu item, so we add this buffer time to prevent the menu from closing unexpectedly return combineLatest([this.menu._isHovering$, this.menu._isFocused$, this._menuItem.isHovered$, this.menu._isExpanded, this._menuItem.isFocused$]) .pipe(debounceTime(50), filter(([isHovered, isFocused, isItemHovered, isExpanded, isItemFocused]) => !isHovered && !isFocused && !isItemHovered && !isExpanded && !isItemFocused)); } ngOnInit() { // set up focus indicator handling this._focusIndicator = this._focusIndicatorService.monitor(this._elementRef.nativeElement); // if there is a parent menu then we should override the default initial // position to be to the right rather than beneath. Note this gets called // before ngOnInit in the MenuComponent so if the user specifies an explicit // position then it will still take precendence if (this._isSubmenuTrigger) { this.menu._isSubMenu = true; this.menu.placement = this.getSubMenuPlacement(this.menu.placement); } // listen for the menu to open (after animation so we can focus the first item) this.menu.opened.pipe(takeUntil(this._onDestroy$)) .subscribe(() => this.menuDidOpen()); // propagate the close event if it is triggered this.menu._closeAll$.pipe(takeUntil(this._onDestroy$)) .subscribe(origin => { if (origin === 'tabout' && this._isRootTrigger) { this.closeMenu('keyboard', true, true, true); } else { this.closeMenu(origin, true); } }); // handle keyboard events in the menu this.menu._onKeydown$.pipe(takeUntil(this._onDestroy$)) .subscribe(event => this.onMenuKeydown(event)); combineLatest([this.menu._placement$, this.menu._alignment$]) .pipe(takeUntil(this._onDestroy$)) .subscribe(() => { if (this._isSubmenuTrigger) { this.menu.placement = this.getSubMenuPlacement(this.menu.placement); } this.getOverlay(true); }); } ngOnDestroy() { this.destroyMenu(); this._onDestroy$.next(); this._onDestroy$.complete(); } /** Focus the next focusable element */ focusNextElement() { // add elements we want to include in our selection const focusableElements = 'a:not([disabled]), button:not([disabled]), input:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'; if (document.activeElement) { const focusable = document.querySelectorAll(focusableElements); const suitableElements = []; focusable.forEach((element) => { if ((element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement) && !element.classList.contains('cdk-visually-hidden')) { suitableElements.push(element); } }); const index = suitableElements.indexOf(document.activeElement); if (index > -1) { const nextElement = suitableElements[index + 1] || focusable[0]; nextElement.focus(); } } } /** Open the menu */ openMenu() { // if the menu is already open then do nothing if (this.menu.isMenuOpen || this.disabled) { return; } this.menu._closeOnBlur = this.closeOnBlur; // get or create an overlayRef const overlayRef = this.getOverlay(); const portal = this.getPortal(); // if the overlay is already attached do nothing if (overlayRef.hasAttached()) { return; } // attach the menu to the DOM overlayRef.attach(portal); // mark the menu as open this.menu._setMenuOpen(true); if (this._menuItem) { // timer is needed because isExpanded$ will get set to false // prematurely due to the debounceTime on the menuShouldClose. timer(this._debounceTime).pipe(takeUntil(this._onDestroy$)) .subscribe(() => this._menuItem.isExpanded$.next(true)); } // listen for a menu item to be selected this.menu._menuItemClick.pipe(take(1), takeUntil(this._onDestroy$)) .subscribe(origin => this.closeMenu(origin, true)); // subscribe to any close events this.didMenuClose().pipe(take(1), takeUntil(this._onDestroy$)) .subscribe(() => this.closeMenu()); // listen for the menu to animate closed then destroy it, if submenu wait for it to start closing to destroy. if (this._isSubmenuTrigger) { this.menu.closing.pipe(take(1), takeUntil(this._onDestroy$)) .subscribe(() => this.destroyMenu()); } else { this.menu.closed.pipe(take(1), takeUntil(this._onDestroy$)) .subscribe(() => this.destroyMenu()); } } /** Close a menu or submenu */ closeMenu(origin, closeParents = false, focusTrigger = true, focusNextElement = false) { if (!this._overlayRef.hasAttached()) { return; } // update the menu state this.menu._setMenuOpen(false); if (this._menuItem) { this._menuItem.isExpanded$.next(false); } // if we should close parents then propagate the event if (closeParents && this._parentMenu) { this._parentMenu._closeAll$.next(origin); } // we should focus the trigger element if this is the root trigger unless otherwise specified if (this._isRootTrigger && focusTrigger) { this._focusIndicator.focus(origin); } if (focusNextElement) { this.focusNextElement(); } this.closed.emit(); return this.menu.closed; } /** Toggle the open state of a menu */ toggleMenu(event) { // if this occurs on a submenu trigger then we can skip if (this._isSubmenuTrigger) { return; } if (!this.menu._isAnimating) { // determine the focus origin based on whether or not a keyboard was used const origin = event instanceof KeyboardEvent ? 'keyboard' : 'mouse'; // set the correct focus origin - if triggered by an event then use the source otherwise it was programmatic this._focusOrigin.setOrigin(event ? origin : 'program'); // toggle the menu open state this.menu.isMenuOpen ? this.closeMenu(origin, true) : this.openMenu(); } // the enter key will trigger the click event and therefore set the wrong focus origin // so we nee to ensure this doesn't happen if (event) { event.preventDefault(); } } /** Submenus should be opened by hovering on the menu item */ _onMouseEnter() { if (this._isSubmenuTrigger && !this._parentMenu._isAnimating) { this.openMenu(); } } _onMouseMove() { if (this._isSubmenuTrigger && !this._parentMenu._isAnimating) { setTimeout(() => { this.openMenu(); }, this._debounceTime); } } /** Pressing the escape key should close all menus */ _onEscape() { if (this.menu.isMenuOpen) { this.closeMenu(); // refocus the root trigger and show the focus ring if (this._isRootTrigger) { this._focusIndicator.focus('keyboard'); } } } /** Handle keyboard events for opening submenus */ _onKeydown(event) { // arrow key in the correct direction should open the menu if (this.menu.placement === 'right' && event.keyCode === RIGHT_ARROW || this.menu.placement === 'left' && event.keyCode === LEFT_ARROW || this.menu.placement === 'top' && event.keyCode === UP_ARROW || this.menu.placement === 'bottom' && event.keyCode === DOWN_ARROW) { this._focusOrigin.setOrigin('keyboard'); // if the menu was opened by a click but we subsequently use the arrow keys focus the first item if (this.menu.isMenuOpen) { this.menu._keyManager.setFocusOrigin('keyboard').setFirstItemActive(); } else { // otherwise open the menu this.openMenu(); } // prevent the browser from scrolling event.preventDefault(); } } /** Blurring the trigger should check if the menu has focus and close it if not */ _onBlur() { if (this.closeOnBlur) { this.closeOnFocusout(); } } /** Remove the menu from the DOM */ destroyMenu() { // if the menu has been destroyed already then do nothing if (!this._overlayRef) { return; } // remove the overlay this._overlayRef.detach(); } /** Create an overlay or return an existing instance */ getOverlay(recreateOverlay = false) { // if we have already created the overlay then reuse it if (this._overlayRef && !recreateOverlay) { return this._overlayRef; } const strategy = this._overlay.position() .flexibleConnectedTo(this.parent ?? this._elementRef) .withFlexibleDimensions(false) .withPush(false) .withTransformOriginOn('.ux-menu'); // otherwise create a new one this._overlayRef = this._overlay.create({ hasBackdrop: !this._isSubmenuTrigger, backdropClass: 'cdk-overlay-transparent-backdrop', scrollStrategy: this._overlay.scrollStrategies.reposition({ scrollThrottle: 0 }), positionStrategy: strategy }); this._overlayPlacement.updatePosition(this._overlayRef, this.menu.placement, this.menu.alignment, undefined); return this._overlayRef; } /** Create a Template portal if one does not already exist (or the template has changed) */ getPortal() { // if there is no portal or the templateRef has changed then create a new one if (!this._portal || this.menu.templateRef !== this._portal.templateRef) { this._portal = new TemplatePortal(this.menu.templateRef, this._viewContainerRef); } return this._portal; } /** Get an observable that emits on any of the triggers that close a menu */ didMenuClose() { return merge(this._overlayRef.backdropClick(), this._parentMenu ? this._parentMenu.closing : of(), this._menuShouldClose); } /** When the menu opens we want to focus the first item in the list */ menuDidOpen() { // if the keyboard is used we should always focus and show the indicator // regardless of it this is the root menu or not if (this._focusOrigin.getOrigin() === 'keyboard') { this.menu._keyManager.setFocusOrigin('keyboard').setFirstItemActive(); } } /** Handle keypresses in submenus where we may want to close them */ onMenuKeydown(event) { // if we are a submenu and the user presses an arrow key in the opposite // direction than it is positioned from its parents then we should close the menu if (this._parentMenu) { if (this.menu.placement === 'right' && event.keyCode === LEFT_ARROW || this.menu.placement === 'left' && event.keyCode === RIGHT_ARROW || this.menu.placement === 'top' && event.keyCode === DOWN_ARROW || this.menu.placement === 'bottom' && event.keyCode === UP_ARROW) { this.closeMenu(); // refocus the parent menu item this._menuItem.focus('keyboard'); } } } /** Check whether the overlay has focus */ hasFocus() { let check = false; document.querySelectorAll('.cdk-overlay-container .ux-menu').forEach(el => { if (el.contains(document.activeElement)) { check = true; } }); return check; } /** Close the menu if there is no element focused */ closeOnFocusout() { if (this.menu.isMenuOpen) { setTimeout(() => { if (!this.hasFocus()) { this.closeMenu(undefined, true, false); } }, this._debounceTime); } } getSubMenuPlacement(placement) { return placement === 'left' ? 'left' : 'right'; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MenuTriggerDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: MenuTriggerDirective, selector: "[uxMenuTriggerFor]", inputs: { menu: ["uxMenuTriggerFor", "menu"], disabled: "disabled", parent: ["uxMenuParent", "parent"], closeOnBlur: "closeOnBlur" }, outputs: { closed: "closed" }, host: { listeners: { "click": "toggleMenu($event)", "keydown.enter": "toggleMenu($event)", "keydown.space": "toggleMenu($event)", "mouseenter": "_onMouseEnter()", "mousemove": "_onMouseMove()", "document:keydown.escape": "_onEscape()", "keydown": "_onKeydown($event)", "blur": "_onBlur()" }, properties: { "attr.disabled": "disabled ? true : null", "attr.aria-haspopup": "!!menu", "attr.aria-expanded": "menu?.isMenuOpen", "attr.aria-controls": "ariaControls" } }, queries: [{ propertyName: "menuTriggers", predicate: i0.forwardRef(() => MenuTriggerDirective) }], exportAs: ["ux-menu-trigger"], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: MenuTriggerDirective, decorators: [{ type: Directive, args: [{ selector: '[uxMenuTriggerFor]', exportAs: 'ux-menu-trigger', host: { '[attr.disabled]': 'disabled ? true : null', '[attr.aria-haspopup]': '!!menu', '[attr.aria-expanded]': 'menu?.isMenuOpen', '[attr.aria-controls]': 'ariaControls' } }] }], propDecorators: { menu: [{ type: Input, args: ['uxMenuTriggerFor'] }], disabled: [{ type: Input }], parent: [{ type: Input, args: ['uxMenuParent'] }], closeOnBlur: [{ type: Input }], closed: [{ type: Output }], menuTriggers: [{ type: ContentChildren, args: [forwardRef(() => MenuTriggerDirective)] }], toggleMenu: [{ type: HostListener, args: ['click', ['$event']] }, { type: HostListener, args: ['keydown.enter', ['$event']] }, { type: HostListener, args: ['keydown.space', ['$event']] }], _onMouseEnter: [{ type: HostListener, args: ['mouseenter'] }], _onMouseMove: [{ type: HostListener, args: ['mousemove'] }], _onEscape: [{ type: HostListener, args: ['document:keydown.escape'] }], _onKeydown: [{ type: HostListener, args: ['keydown', ['$event']] }], _onBlur: [{ type: HostListener, args: ['blur'] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVudS10cmlnZ2VyLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL21lbnUvbWVudS10cmlnZ2VyL21lbnUtdHJpZ2dlci5kaXJlY3RpdmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFnQixxQkFBcUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzVFLE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLFdBQVcsRUFBRSxRQUFRLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUN0RixPQUFPLEVBQUUsT0FBTyxFQUFjLE1BQU0sc0JBQXNCLENBQUM7QUFDM0QsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLHFCQUFxQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxlQUFlLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsWUFBWSxFQUFFLEtBQUssRUFBcUIsTUFBTSxFQUFhLGdCQUFnQixFQUFFLFVBQVUsRUFBRSxNQUFNLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDdEwsT0FBTyxFQUFjLE9BQU8sRUFBRSxhQUFhLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDNUUsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRXZFLE9BQU8sRUFBa0IsMkJBQTJCLEVBQUUscUJBQXFCLEVBQUUsTUFBTSx5Q0FBeUMsQ0FBQztBQUM3SCxPQUFPLEVBQUUsdUJBQXVCLEVBQUUsTUFBTSwyQ0FBMkMsQ0FBQztBQUNwRixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUNyRSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7O0FBWXZELE1BQU0sT0FBTyxvQkFBb0I7SUFWakM7UUFXcUIsYUFB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QkE2QnpDLGFBQWE7c0JBRFosWUFBWTt1QkFBQyxZQUFZO2dCQVExQixZQUFZO3NCQURYLFlBQVk7dUJBQUMsV0FBVztnQkFXekIsU0FBUztzQkFEUixZQUFZO3VCQUFDLHlCQUF5QjtnQkFjdkMsVUFBVTtzQkFEVCxZQUFZO3VCQUFDLFNBQVMsRUFBRSxDQUFDLFFBQVEsQ0FBQztnQkEyQm5DLE9BQU87c0JBRE4sWUFBWTt1QkFBQyxNQUFNIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRm9jdXNPcmlnaW4gfSBmcm9tICdAYW5ndWxhci9jZGsvYTExeSc7XG5pbXBvcnQgeyBCb29sZWFuSW5wdXQsIGNvZXJjZUJvb2xlYW5Qcm9wZXJ0eSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9jb2VyY2lvbic7XG5pbXBvcnQgeyBET1dOX0FSUk9XLCBMRUZUX0FSUk9XLCBSSUdIVF9BUlJPVywgVVBfQVJST1cgfSBmcm9tICdAYW5ndWxhci9jZGsva2V5Y29kZXMnO1xuaW1wb3J0IHsgT3ZlcmxheSwgT3ZlcmxheVJlZiB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9vdmVybGF5JztcbmltcG9ydCB7IFRlbXBsYXRlUG9ydGFsIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL3BvcnRhbCc7XG5pbXBvcnQgeyBDb250ZW50Q2hpbGRyZW4sIERpcmVjdGl2ZSwgRWxlbWVudFJlZiwgRXZlbnRFbWl0dGVyLCBIb3N0TGlzdGVuZXIsIElucHV0LCBPbkRlc3Ryb3ksIE9uSW5pdCwgT3V0cHV0LCBRdWVyeUxpc3QsIFZpZXdDb250YWluZXJSZWYsIGZvcndhcmRSZWYsIGluamVjdCB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgU3ViamVjdCwgY29tYmluZUxhdGVzdCwgbWVyZ2UsIG9mLCB0aW1lciB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgZGVib3VuY2VUaW1lLCBmaWx0ZXIsIHRha2UsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEFuY2hvclBsYWNlbWVudCB9IGZyb20gJy4uLy4uLy4uL2NvbW1vbi9vdmVybGF5L2FuY2hvci1wbGFjZW1lbnQnO1xuaW1wb3J0IHsgRm9jdXNJbmRpY2F0b3IsIEZvY3VzSW5kaWNhdG9yT3JpZ2luU2VydmljZSwgRm9jdXNJbmRpY2F0b3JTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vZGlyZWN0aXZlcy9hY2Nlc3NpYmlsaXR5L2luZGV4JztcbmltcG9ydCB7IE92ZXJsYXlQbGFjZW1lbnRTZXJ2aWNlIH0gZnJvbSAnLi4vLi4vLi4vc2VydmljZXMvb3ZlcmxheS1wbGFjZW1lbnQvaW5kZXgnO1xuaW1wb3J0IHsgTWVudUl0ZW1Db21wb25lbnQgfSBmcm9tICcuLi9tZW51LWl0ZW0vbWVudS1pdGVtLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBNZW51Q29tcG9uZW50IH0gZnJvbSAnLi4vbWVudS9tZW51LmNvbXBvbmVudCc7XG5cbkBEaXJlY3RpdmUoe1xuICAgIHNlbGVjdG9yOiAnW3V4TWVudVRyaWdnZXJGb3JdJyxcbiAgICBleHBvcnRBczogJ3V4LW1lbnUtdHJpZ2dlcicsXG4gICAgaG9zdDoge1xuICAgICAgICAnW2F0dHIuZGlzYWJsZWRdJzogJ2Rpc2FibGVkID8gdHJ1ZSA6IG51bGwnLFxuICAgICAgICAnW2F0dHIuYXJpYS1oYXNwb3B1cF0nOiAnISFtZW51JyxcbiAgICAgICAgJ1thdHRyLmFyaWEtZXhwYW5kZWRdJzogJ21lbnU/LmlzTWVudU9wZW4nLFxuICAgICAgICAnW2F0dHIuYXJpYS1jb250cm9sc10nOiAnYXJpYUNvbnRyb2xzJ1xuICAgIH1cbn0pXG5leHBvcnQgY2xhc3MgTWVudVRyaWdnZXJEaXJlY3RpdmUgaW1wbGVtZW50cyBPbkluaXQsIE9uRGVzdHJveSB7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfb3ZlcmxheSA9IGluamVjdChPdmVybGF5KTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2VsZW1lbnRSZWYgPSBpbmplY3QoRWxlbWVudFJlZik7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF92aWV3Q29udGFpbmVyUmVmID0gaW5qZWN0KFZpZXdDb250YWluZXJSZWYpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfZm9jdXNPcmlnaW4gPSBpbmplY3QoRm9jdXNJbmRpY2F0b3JPcmlnaW5TZXJ2aWNlKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2ZvY3VzSW5kaWNhdG9yU2VydmljZSA9IGluamVjdChGb2N1c0luZGljYXRvclNlcnZpY2UpO1xuXG4gICAgcHJpdmF0ZSByZWFkb25seSBfb3ZlcmxheVBsYWNlbWVudCA9IGluamVjdChPdmVybGF5UGxhY2VtZW50U2VydmljZSk7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9wYXJlbnRNZW51ID0gaW5qZWN0KE1lbnVDb21wb25lbnQsIHsgb3B0aW9uYWw6IHRydWUgfSk7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9tZW51SXRlbSA9IGluamVjdChNZW51SXRlbUNvbXBvbmVudCwgeyBvcHRpb25hbDogdHJ1ZSwgc2VsZjogdHJ1ZSB9KTtcblxuICAgIC8qKiBBY2Nlc3MgdGhlIG1lbnUgd2Ugc2hvdWxkIHNob3cgKi9cbiAgICBASW5wdXQoJ3V4TWVudVRyaWdnZXJGb3InKSBtZW51OiBNZW51Q29tcG9uZW50O1xuXG4gICAgLyoqIERldGVybWluZSBpZiB3ZSBzaG91bGQgZGlzYWJsZSB0aGUgdHJpZ2dlciAqL1xuICAgIEBJbnB1dCgpIGRpc2FibGVkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogT3B0aW9uYWxseSBzcGVjaWZ5IHRoZSBtZW51J3MgcGFyZW50IGVsZW1lbnQgKi9cbiAgICBASW5wdXQoJ3V4TWVudVBhcmVudCcpIHBhcmVudDogRWxlbWVudFJlZjtcblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhlIG1lbnUgc2hvdWxkIGNsb3NlIHdoZW4gaXQgbG9zZXMgZm9jdXMgKi9cbiAgICBASW5wdXQoKSBzZXQgY2xvc2VPbkJsdXIodmFsdWU6IGJvb2xlYW4pIHtcbiAgICAgICAgdGhpcy5fY2xvc2VPbkJsdXIgPSBjb2VyY2VCb29sZWFuUHJvcGVydHkodmFsdWUpO1xuICAgIH1cblxuICAgIGdldCBjbG9zZU9uQmx1cigpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Nsb3NlT25CbHVyO1xuICAgIH1cblxuICAgIC8qKiBFbWl0IHdoZW4gdGhlIG1lbnUgaXMgY2xvc2VkICovXG4gICAgQE91dHB1dCgpIHJlYWRvbmx5IGNsb3NlZCA9IG5ldyBFdmVudEVtaXR0ZXI8dm9pZD4oKTtcblxuICAgIC8qKiBSZWZlcmVuY2UgdG8gdGhlIHBvcnRhbCBiYXNlZCBvZmYgdGhlIE1lbnVDb21wb250IHRlbXBsYXRlUmVmICovXG4gICAgcHJpdmF0ZSBfcG9ydGFsOiBUZW1wbGF0ZVBvcnRhbDtcblxuICAgIC8qKiBTdG9yZSB0aGUgcmVmZXJlbmNlIHRvIHRoZSBvdmVybGF5ICovXG4gICAgcHJpdmF0ZSBfb3ZlcmxheVJlZj86IE92ZXJsYXlSZWY7XG5cbiAgICAvKiogR2V0IHRoZSBhcmlhIGNvbnRyb2xzIGZvciBhY2Nlc3NpYmlsaXR5ICovXG4gICAgZ2V0IGFyaWFDb250cm9scygpOiBzdHJpbmcgfCBudWxsIHtcbiAgICAgICAgcmV0dXJuIHRoaXMubWVudT8uaXNNZW51T3BlbiA/IHRoaXMubWVudT8uaW5uZXJJZCA6IG51bGw7XG4gICAgfVxuXG4gICAgLyoqIFN0b3JlIHRoZSBpbnN0YW5jZSBvZiB0aGUgZm9jdXMgaW5kaWNhdG9yICovXG4gICAgcHJpdmF0ZSBfZm9jdXNJbmRpY2F0b3I6IEZvY3VzSW5kaWNhdG9yO1xuXG4gICAgLyoqIEF1dG9tYXRpY2FsbHkgdW5zdWJzY3JpYmUgb24gZGlyZWN0aXZlIGRlc3Ryb3kgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9vbkRlc3Ryb3kkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIC8qKiBSZWZlcmVuY2UgdG8gdGhlIG1lbnUgc2hvdWxkIGNsb3NlIHdoZW4gaXQgbG9zZXMgZm9jdXMgKi9cbiAgICBwcml2YXRlIF9jbG9zZU9uQmx1cjogYm9vbGVhbiA9IGZhbHNlO1xuXG4gICAgLyoqIERldGVybWluZSBpZiB0aGlzIHRyaWdnZXJzIGEgc3VibWVudSAqL1xuICAgIHByaXZhdGUgZ2V0IF9pc1N1Ym1lbnVUcmlnZ2VyKCk6IGJvb2xlYW4ge1xuICAgICAgICByZXR1cm4gISF0aGlzLl9wYXJlbnRNZW51O1xuICAgIH1cblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhpcyBpcyB0aGUgcm9vdCB0cmlnZ2VyICovXG4gICAgcHJpdmF0ZSBnZXQgX2lzUm9vdFRyaWdnZXIoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiAhdGhpcy5faXNTdWJtZW51VHJpZ2dlcjtcbiAgICB9XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9kZWJvdW5jZVRpbWU6IG51bWJlciA9IDUwO1xuXG4gICAgQENvbnRlbnRDaGlsZHJlbihmb3J3YXJkUmVmKCgpID0+IE1lbnVUcmlnZ2VyRGlyZWN0aXZlKSkgbWVudVRyaWdnZXJzOiBRdWVyeUxpc3Q8TWVudVRyaWdnZXJEaXJlY3RpdmU+O1xuXG4gICAgLyoqIElmIHRoaXMgaXMgYSBzdWJtZW51IHdlIHdhbnQgdG8ga25vdyB3aGVuIHRoZSBtb3VzZSBsZWF2ZXMgdGhlIGl0ZW1zIG9yIHBhcmVudCBpdGVtICovXG4gICAgcHJpdmF0ZSBnZXQgX21lbnVTaG91bGRDbG9zZSgpOiBPYnNlcnZhYmxlPGJvb2xlYW5bXT4ge1xuICAgICAgICBpZiAoIXRoaXMuX2lzU3VibWVudVRyaWdnZXIpIHtcbiAgICAgICAgICAgIHJldHVybiBvZigpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gVGhpcyBjb21iaW5lZCBvYnNlcnZhYmxlIHdpbGwgZXNzZW50aWFsbHkgY2hlY2sgZm9yIGFsbCBvZiB0aGUgY29tYmluYXRpb25zIG9mIGV2ZW50cyB0aGF0IGNhbiBjYXVzZSBhIG1lbnVcbiAgICAgICAgLy8gdG8gcmVtYWluIG9wZW4sIGZvciBleGFtcGxlOlxuICAgICAgICAvL1xuICAgICAgICAvLyAxLiBIb3ZlcmluZyBvdmVyIGFueSBpdGVtIGluIHRoZSBtZW51IHNob3VsZCBrZWVwIHRoZSBtZW51IG9wZW5cbiAgICAgICAgLy8gMi4gSGF2aW5nIGFueSBpdGVtIGluIHRoZSBtZW51IGZvY3VzZWQgc2hvdWxkIGtlZXAgdGhlIG1lbnUgb3BlblxuICAgICAgICAvLyAzLiBIYXZpbmcgdGhlIHBhcmVudCBtZW51IGl0ZW0gaG92ZXJlZCBzaG91bGQga2VlcCBhIHN1Ym1lbnUgb3BlblxuICAgICAgICAvLyA0LiBIYXZpbmcgdGhlIHBhcmVudCBtZW51IGl0ZW0gZm9jdXNlZCBzaG91bGQga2VlcCBhIHN1Ym1lbnUgb3BlblxuICAgICAgICAvLyA1LiBIYXZpbmcgYSBzdWJtZW51IG9wZW4gc2hvdWxkIGtlZXAgdGhlIHBhcmVudCBvcGVuIChpZiB0aGUgc3VibWVudSBtZWV0cyBvbmUgb2YgdGhlIGFib3ZlIGNvbmRpdGlvbnMpXG4gICAgICAgIC8vXG4gICAgICAgIC8vIFdlIGFsc28gZGVib3VuY2UgdGhpcyBiZWNhdXNlIHRoZXJlIGlzIG9mdGVuIGEgZGVsYXkgYmV0d2VlbiBhIGJsdXIgYW5kIGEgZm9jdXMgZXZlbnQgb3IgbW92aW5nIHRoZSBtb3VzZVxuICAgICAgICAvLyBmcm9tIGEgbWVudSBpdGVtIHRvIGEgc3ViIG1lbnUgaXRlbSwgc28gd2UgYWRkIHRoaXMgYnVmZmVyIHRpbWUgdG8gcHJldmVudCB0aGUgbWVudSBmcm9tIGNsb3NpbmcgdW5leHBlY3RlZGx5XG4gICAgICAgIHJldHVybiBjb21iaW5lTGF0ZXN0KFsgdGhpcy5tZW51Ll9pc0hvdmVyaW5nJCwgdGhpcy5tZW51Ll9pc0ZvY3VzZWQkLCB0aGlzLl9tZW51SXRlbS5pc0hvdmVyZWQkLCB0aGlzLm1lbnUuX2lzRXhwYW5kZWQsIHRoaXMuX21lbnVJdGVtLmlzRm9jdXNlZCQgXSlcbiAgICAgICAgICAgIC5waXBlKGRlYm91bmNlVGltZSg1MCksIGZpbHRlcigoW2lzSG92ZXJlZCwgaXNGb2N1c2VkLCBpc0l0ZW1Ib3ZlcmVkLCBpc0V4cGFuZGVkLCBpc0l0ZW1Gb2N1c2VkXSkgPT5cbiAgICAgICAgICAgICAgICAhaXNIb3ZlcmVkICYmICFpc0ZvY3VzZWQgJiYgIWlzSXRlbUhvdmVyZWQgJiYgIWlzRXhwYW5kZWQgJiYgIWlzSXRlbUZvY3VzZWQpKTtcbiAgICB9XG5cbiAgICBuZ09uSW5pdCgpOiB2b2lkIHtcblxuICAgICAgICAvLyBzZXQgdXAgZm9jdXMgaW5kaWNhdG9yIGhhbmRsaW5nXG4gICAgICAgIHRoaXMuX2ZvY3VzSW5kaWNhdG9yID0gdGhpcy5fZm9jdXNJbmRpY2F0b3JTZXJ2aWNlLm1vbml0b3IodGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50KTtcblxuICAgICAgICAvLyBpZiB0aGVyZSBpcyBhIHBhcmVudCBtZW51IHRoZW4gd2Ugc2hvdWxkIG92ZXJyaWRlIHRoZSBkZWZhdWx0IGluaXRpYWxcbiAgICAgICAgLy8gcG9zaXRpb24gdG8gYmUgdG8gdGhlIHJpZ2h0IHJhdGhlciB0aGFuIGJlbmVhdGguIE5vdGUgdGhpcyBnZXRzIGNhbGxlZFxuICAgICAgICAvLyBiZWZvcmUgbmdPbkluaXQgaW4gdGhlIE1lbnVDb21wb25lbnQgc28gaWYgdGhlIHVzZXIgc3BlY2lmaWVzIGFuIGV4cGxpY2l0XG4gICAgICAgIC8vIHBvc2l0aW9uIHRoZW4gaXQgd2lsbCBzdGlsbCB0YWtlIHByZWNlbmRlbmNlXG4gICAgICAgIGlmICh0aGlzLl9pc1N1Ym1lbnVUcmlnZ2VyKSB7XG4gICAgICAgICAgICB0aGlzLm1lbnUuX2lzU3ViTWVudSA9IHRydWU7XG4gICAgICAgICAgICB0aGlzLm1lbnUucGxhY2VtZW50ID0gdGhpcy5nZXRTdWJNZW51UGxhY2VtZW50KHRoaXMubWVudS5wbGFjZW1lbnQpO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gbGlzdGVuIGZvciB0aGUgbWVudSB0byBvcGVuIChhZnRlciBhbmltYXRpb24gc28gd2UgY2FuIGZvY3VzIHRoZSBmaXJzdCBpdGVtKVxuICAgICAgICB0aGlzLm1lbnUub3BlbmVkLnBpcGUodGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLm1lbnVEaWRPcGVuKCkpO1xuXG4gICAgICAgIC8vIHByb3BhZ2F0ZSB0aGUgY2xvc2UgZXZlbnQgaWYgaXQgaXMgdHJpZ2dlcmVkXG4gICAgICAgIHRoaXMubWVudS5fY2xvc2VBbGwkLnBpcGUodGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZShvcmlnaW4gPT4ge1xuICAgICAgICAgICAgICAgIGlmIChvcmlnaW4gPT09ICd0YWJvdXQnICYmIHRoaXMuX2lzUm9vdFRyaWdnZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jbG9zZU1lbnUoJ2tleWJvYXJkJywgdHJ1ZSwgdHJ1ZSwgdHJ1ZSk7XG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5jbG9zZU1lbnUob3JpZ2luIGFzIEZvY3VzT3JpZ2luLCB0cnVlKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAvLyBoYW5kbGUga2V5Ym9hcmQgZXZlbnRzIGluIHRoZSBtZW51XG4gICAgICAgIHRoaXMubWVudS5fb25LZXlkb3duJC5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kkKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoZXZlbnQgPT4gdGhpcy5vbk1lbnVLZXlkb3duKGV2ZW50KSk7XG5cbiAgICAgICAgY29tYmluZUxhdGVzdChbdGhpcy5tZW51Ll9wbGFjZW1lbnQkLCB0aGlzLm1lbnUuX2FsaWdubWVudCRdKVxuICAgICAgICAgICAgLnBpcGUodGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuX2lzU3VibWVudVRyaWdnZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5tZW51LnBsYWNlbWVudCA9IHRoaXMuZ2V0U3ViTWVudVBsYWNlbWVudCh0aGlzLm1lbnUucGxhY2VtZW50KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgdGhpcy5nZXRPdmVybGF5KHRydWUpO1xuICAgICAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGVzdHJveU1lbnUoKTtcbiAgICAgICAgdGhpcy5fb25EZXN0cm95JC5uZXh0KCk7XG4gICAgICAgIHRoaXMuX29uRGVzdHJveSQuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICAvKiogRm9jdXMgdGhlIG5leHQgZm9jdXNhYmxlIGVsZW1lbnQgKi9cbiAgICBmb2N1c05leHRFbGVtZW50KCkge1xuICAgICAgICAvLyBhZGQgZWxlbWVudHMgd2Ugd2FudCB0byBpbmNsdWRlIGluIG91ciBzZWxlY3Rpb25cbiAgICAgICAgY29uc3QgZm9jdXNhYmxlRWxlbWVudHMgPSAnYTpub3QoW2Rpc2FibGVkXSksIGJ1dHRvbjpub3QoW2Rpc2FibGVkXSksIGlucHV0Om5vdChbZGlzYWJsZWRdKSwgW3RhYmluZGV4XTpub3QoW2Rpc2FibGVkXSk6bm90KFt0YWJpbmRleD1cIi0xXCJdKSc7XG5cbiAgICAgICAgaWYgKGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQpIHtcblxuICAgICAgICAgICAgY29uc3QgZm9jdXNhYmxlOiBOb2RlTGlzdE9mPEhUTUxFbGVtZW50PiA9IGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoZm9jdXNhYmxlRWxlbWVudHMpO1xuICAgICAgICAgICAgY29uc3Qgc3VpdGFibGVFbGVtZW50cyA9IFtdO1xuXG4gICAgICAgICAgICBmb2N1c2FibGUuZm9yRWFjaCgoZWxlbWVudCkgPT4ge1xuICAgICAgICAgICAgICAgIGlmICgoZWxlbWVudC5vZmZzZXRXaWR0aCA+IDAgfHwgZWxlbWVudC5vZmZzZXRIZWlnaHQgPiAwIHx8IGVsZW1lbnQgPT09IGRvY3VtZW50LmFjdGl2ZUVsZW1lbnQpICYmICFlbGVtZW50LmNsYXNzTGlzdC5jb250YWlucygnY2RrLXZpc3VhbGx5LWhpZGRlbicpKSB7XG4gICAgICAgICAgICAgICAgICAgIHN1aXRhYmxlRWxlbWVudHMucHVzaChlbGVtZW50KTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgY29uc3QgaW5kZXggPSBzdWl0YWJsZUVsZW1lbnRzLmluZGV4T2YoZG9jdW1lbnQuYWN0aXZlRWxlbWVudCk7XG5cbiAgICAgICAgICAgIGlmIChpbmRleCA+IC0xKSB7XG4gICAgICAgICAgICAgICBjb25zdCBuZXh0RWxlbWVudCA9IHN1aXRhYmxlRWxlbWVudHNbaW5kZXggKyAxXSB8fCBmb2N1c2FibGVbMF07XG4gICAgICAgICAgICAgICBuZXh0RWxlbWVudC5mb2N1cygpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqIE9wZW4gdGhlIG1lbnUgKi9cbiAgICBvcGVuTWVudSgpOiB2b2lkIHtcbiAgICAgICAgLy8gaWYgdGhlIG1lbnUgaXMgYWxyZWFkeSBvcGVuIHRoZW4gZG8gbm90aGluZ1xuICAgICAgICBpZiAodGhpcy5tZW51LmlzTWVudU9wZW4gfHwgdGhpcy5kaXNhYmxlZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5tZW51Ll9jbG9zZU9uQmx1ciA9IHRoaXMuY2xvc2VPbkJsdXI7XG5cbiAgICAgICAgLy8gZ2V0IG9yIGNyZWF0ZSBhbiBvdmVybGF5UmVmXG4gICAgICAgIGNvbnN0IG92ZXJsYXlSZWYgPSB0aGlzLmdldE92ZXJsYXkoKTtcbiAgICAgICAgY29uc3QgcG9ydGFsID0gdGhpcy5nZXRQb3J0YWwoKTtcblxuICAgICAgICAvLyBpZiB0aGUgb3ZlcmxheSBpcyBhbHJlYWR5IGF0dGFjaGVkIGRvIG5vdGhpbmdcbiAgICAgICAgaWYgKG92ZXJsYXlSZWYuaGFzQXR0YWNoZWQoKSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gYXR0YWNoIHRoZSBtZW51IHRvIHRoZSBET01cbiAgICAgICAgb3ZlcmxheVJlZi5hdHRhY2gocG9ydGFsKTtcblxuICAgICAgICAvLyBtYXJrIHRoZSBtZW51IGFzIG9wZW5cbiAgICAgICAgdGhpcy5tZW51Ll9zZXRNZW51T3Blbih0cnVlKTtcblxuICAgICAgICBpZiAodGhpcy5fbWVudUl0ZW0pIHtcbiAgICAgICAgICAgIC8vIHRpbWVyIGlzIG5lZWRlZCBiZWNhdXNlIGlzRXhwYW5kZWQkIHdpbGwgZ2V0IHNldCB0byBmYWxzZVxuICAgICAgICAgICAgLy8gcHJlbWF0dXJlbHkgZHVlIHRvIHRoZSBkZWJvdW5jZVRpbWUgb24gdGhlIG1lbnVTaG91bGRDbG9zZS5cbiAgICAgICAgICAgIHRpbWVyKHRoaXMuX2RlYm91bmNlVGltZSkucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95JCkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLl9tZW51SXRlbS5pc0V4cGFuZGVkJC5uZXh0KHRydWUpKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGxpc3RlbiBmb3IgYSBtZW51IGl0ZW0gdG8gYmUgc2VsZWN0ZWRcbiAgICAgICAgdGhpcy5tZW51Ll9tZW51SXRlbUNsaWNrLnBpcGUodGFrZSgxKSwgdGFrZVVudGlsKHRoaXMuX29uRGVzdHJveSQpKVxuICAgICAgICAgICAgLnN1YnNjcmliZShvcmlnaW4gPT4gdGhpcy5jbG9zZU1lbnUob3JpZ2luLCB0cnVlKSk7XG5cbiAgICAgICAgLy8gc3Vic2NyaWJlIHRvIGFueSBjbG9zZSBldmVudHNcbiAgICAgICAgdGhpcy5kaWRNZW51Q2xvc2UoKS5waXBlKHRha2UoMSksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kkKSlcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKCkgPT4gdGhpcy5jbG9zZU1lbnUoKSk7XG5cbiAgICAgICAgLy8gbGlzdGVuIGZvciB0aGUgbWVudSB0byBhbmltYXRlIGNsb3NlZCB0aGVuIGRlc3Ryb3kgaXQsIGlmIHN1Ym1lbnUgd2FpdCBmb3IgaXQgdG8gc3RhcnQgY2xvc2luZyB0byBkZXN0cm95LlxuICAgICAgICBpZiAodGhpcy5faXNTdWJtZW51VHJpZ2dlcikge1xuICAgICAgICAgICAgdGhpcy5tZW51LmNsb3NpbmcucGlwZSh0YWtlKDEpLCB0YWtlVW50aWwodGhpcy5fb25EZXN0cm95JCkpXG4gICAgICAgICAgICAgICAgLnN1YnNjcmliZSgoKSA9PiB0aGlzLmRlc3Ryb3lNZW51KCkpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5tZW51LmNsb3NlZC5waXBlKHRha2UoMSksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kkKSlcbiAgICAgICAgICAgICAgICAuc3Vic2NyaWJlKCgpID0+IHRoaXMuZGVzdHJveU1lbnUoKSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKiogQ2xvc2UgYSBtZW51IG9yIHN1Ym1lbnUgKi9cbiAgICBjbG9zZU1lbnUob3JpZ2luPzogRm9jdXNPcmlnaW4sIGNsb3NlUGFyZW50czogYm9vbGV