@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
407 lines • 61.6 kB
JavaScript
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