ng-hub-ui-portal
Version:
A flexible Angular portal library for dynamic content rendering with advanced positioning and interaction control
208 lines • 21.4 kB
JavaScript
import { DOCUMENT, NgIf } from '@angular/common';
import { Component, ElementRef, EventEmitter, inject, Input, NgZone, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { getFocusableBoundaryElements, hubRunTransition, reflow } from 'ng-hub-ui-utils';
import { Subject, zip } from 'rxjs';
import { take } from 'rxjs/operators';
import * as i0 from "@angular/core";
export class HubPortalWindow {
constructor() {
this._document = inject(DOCUMENT);
this._elRef = inject((ElementRef));
this._zone = inject(NgZone);
this._closed$ = new Subject();
this._elWithFocus = null; // element that is focused prior to portal opening
this.dismissEvent = new EventEmitter();
this.shown = new Subject();
this.hidden = new Subject();
}
dismiss(reason) {
this.dismissEvent.emit(reason);
}
ngOnInit() {
this._elWithFocus = this._document.activeElement;
this._zone.onStable
.asObservable()
.pipe(take(1))
.subscribe(() => {
this._show();
});
}
ngOnDestroy() {
this._disableEventHandling();
}
hide() {
const { nativeElement } = this._elRef;
const context = {
animation: this.animation,
runningTransition: 'stop'
};
const windowTransition$ = hubRunTransition(this._zone, nativeElement, () => nativeElement.classList.remove('show'), context);
const dialogTransition$ = hubRunTransition(this._zone, this._dialogEl.nativeElement, () => { }, context);
const transitions$ = zip(windowTransition$, dialogTransition$);
transitions$.subscribe(() => {
this.hidden.next();
this.hidden.complete();
});
this._disableEventHandling();
this._restoreFocus();
return transitions$;
}
_show() {
const context = {
animation: this.animation,
runningTransition: 'continue'
};
const windowTransition$ = hubRunTransition(this._zone, this._elRef.nativeElement, (element, animation) => {
if (animation) {
reflow(element);
}
element.classList.add('show');
}, context);
const dialogTransition$ = hubRunTransition(this._zone, this._dialogEl.nativeElement, () => { }, context);
zip(windowTransition$, dialogTransition$).subscribe(() => {
this.shown.next();
this.shown.complete();
});
this._setFocus();
}
_disableEventHandling() {
this._closed$.next();
}
_setFocus() {
const { nativeElement } = this._elRef;
if (!nativeElement.contains(document.activeElement)) {
const autoFocusable = nativeElement.querySelector(`[hubAutofocus]`);
const firstFocusable = getFocusableBoundaryElements(nativeElement)[0];
const elementToFocus = autoFocusable || firstFocusable || nativeElement;
elementToFocus.focus();
}
}
_restoreFocus() {
const body = this._document.body;
const elWithFocus = this._elWithFocus;
let elementToFocus;
if (elWithFocus && elWithFocus['focus'] && body.contains(elWithFocus)) {
elementToFocus = elWithFocus;
}
else {
elementToFocus = body;
}
this._zone.runOutsideAngular(() => {
setTimeout(() => elementToFocus.focus());
this._elWithFocus = null;
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubPortalWindow, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.5", type: HubPortalWindow, isStandalone: true, selector: "hub-portal-window", inputs: { animation: "animation", ariaLabelledBy: "ariaLabelledBy", ariaDescribedBy: "ariaDescribedBy", scrollable: "scrollable", windowClass: "windowClass", portalDialogClass: "portalDialogClass", portalContentClass: "portalContentClass" }, outputs: { dismissEvent: "dismiss" }, host: { attributes: { "role": "dialog", "tabindex": "-1" }, properties: { "class": "\"portal d-block\" + (windowClass ? \" \" + windowClass : \"\")", "class.fade": "animation", "attr.aria-portal": "true", "attr.aria-labelledby": "ariaLabelledBy", "attr.aria-describedby": "ariaDescribedBy" } }, viewQueries: [{ propertyName: "_dialogEl", first: true, predicate: ["dialog"], descendants: true, static: true }], ngImport: i0, template: `
<div
#dialog
[class]="
'portal-dialog' +
(scrollable ? ' portal-dialog-scrollable' : '') +
(portalDialogClass ? ' ' + portalDialogClass : '')
"
role="document"
>
<div
[class]="
'portal-content' +
(portalContentClass ? ' ' + portalContentClass : '')
"
>
<ng-container *ngIf="singleContent; else multipleContent">
<ng-content></ng-content>
</ng-container>
<ng-template #multipleContent>
<div class="portal-header">
<ng-content />
<button
type="button"
class="btn-close"
data-bs-dismiss="portal"
aria-label="Close"
(click)="dismiss(null)"
></button>
</div>
<div class="portal-body">
<ng-content />
</div>
<div class="portal-footer">
<ng-content />
</div>
</ng-template>
</div>
</div>
`, isInline: true, styles: ["hub-portal-window .component-host-scrollable{display:flex;flex-direction:column;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.5", ngImport: i0, type: HubPortalWindow, decorators: [{
type: Component,
args: [{ selector: 'hub-portal-window', standalone: true, imports: [NgIf], host: {
'[class]': '"portal d-block" + (windowClass ? " " + windowClass : "")',
'[class.fade]': 'animation',
role: 'dialog',
tabindex: '-1',
'[attr.aria-portal]': 'true',
'[attr.aria-labelledby]': 'ariaLabelledBy',
'[attr.aria-describedby]': 'ariaDescribedBy'
}, template: `
<div
#dialog
[class]="
'portal-dialog' +
(scrollable ? ' portal-dialog-scrollable' : '') +
(portalDialogClass ? ' ' + portalDialogClass : '')
"
role="document"
>
<div
[class]="
'portal-content' +
(portalContentClass ? ' ' + portalContentClass : '')
"
>
<ng-container *ngIf="singleContent; else multipleContent">
<ng-content></ng-content>
</ng-container>
<ng-template #multipleContent>
<div class="portal-header">
<ng-content />
<button
type="button"
class="btn-close"
data-bs-dismiss="portal"
aria-label="Close"
(click)="dismiss(null)"
></button>
</div>
<div class="portal-body">
<ng-content />
</div>
<div class="portal-footer">
<ng-content />
</div>
</ng-template>
</div>
</div>
`, encapsulation: ViewEncapsulation.None, styles: ["hub-portal-window .component-host-scrollable{display:flex;flex-direction:column;overflow:hidden}\n"] }]
}], propDecorators: { _dialogEl: [{
type: ViewChild,
args: ['dialog', { static: true }]
}], animation: [{
type: Input
}], ariaLabelledBy: [{
type: Input
}], ariaDescribedBy: [{
type: Input
}], scrollable: [{
type: Input
}], windowClass: [{
type: Input
}], portalDialogClass: [{
type: Input
}], portalContentClass: [{
type: Input
}], dismissEvent: [{
type: Output,
args: ['dismiss']
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicG9ydGFsLXdpbmRvdy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3BvcnRhbC9zcmMvbGliL3BvcnRhbC13aW5kb3cudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNqRCxPQUFPLEVBQ04sU0FBUyxFQUNULFVBQVUsRUFDVixZQUFZLEVBQ1osTUFBTSxFQUNOLEtBQUssRUFDTCxNQUFNLEVBR04sTUFBTSxFQUNOLFNBQVMsRUFDVCxpQkFBaUIsRUFDakIsTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxFQUNOLDRCQUE0QixFQUM1QixnQkFBZ0IsRUFDaEIsTUFBTSxFQUVOLE1BQU0saUJBQWlCLENBQUM7QUFDekIsT0FBTyxFQUFjLE9BQU8sRUFBRSxHQUFHLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDaEQsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDOztBQTBEdEMsTUFBTSxPQUFPLGVBQWU7SUF4RDVCO1FBeURTLGNBQVMsR0FBRyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDN0IsV0FBTSxHQUFHLE1BQU0sQ0FBQyxDQUFBLFVBQXVCLENBQUEsQ0FBQyxDQUFDO1FBQ3pDLFVBQUssR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUM7UUFFdkIsYUFBUSxHQUFHLElBQUksT0FBTyxFQUFRLENBQUM7UUFDL0IsaUJBQVksR0FBbUIsSUFBSSxDQUFDLENBQUMsa0RBQWtEO1FBZTVFLGlCQUFZLEdBQUcsSUFBSSxZQUFZLEVBQUUsQ0FBQztRQUVyRCxVQUFLLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztRQUM1QixXQUFNLEdBQUcsSUFBSSxPQUFPLEVBQVEsQ0FBQztLQXNIN0I7SUFwSEEsT0FBTyxDQUFDLE1BQU07UUFDYixJQUFJLENBQUMsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFN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QUNQLENBQUMsSUFBSSxDQUFDLFFBQ1Q7d0JBQ0wsU0FBUyxFQUFFLDJEQUEyRDt3QkFDdEUsY0FBYyxFQUFFLFdBQVc7d0JBQzNCLElBQUksRUFBRSxRQUFRO3dCQUNkLFFBQVEsRUFBRSxJQUFJO3dCQUNkLG9CQUFvQixFQUFFLE1BQU07d0JBQzVCLHdCQUF3QixFQUFFLGdCQUFnQjt3QkFDMUMseUJBQXlCLEVBQUUsaUJBQWlCO3FCQUM1QyxZQUNTOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7RUF1Q1QsaUJBQ2MsaUJBQWlCLENBQUMsSUFBSTs4QkFZN0IsU0FBUztzQkFEaEIsU0FBUzt1QkFBQyxRQUFRLEVBQUUsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFO2dCQUc1QixTQUFTO3NCQUFqQixLQUFLO2dCQUNHLGNBQWM7c0JBQXRCLEtBQUs7Z0JBQ0csZUFBZTtzQkFBdkIsS0FBSztnQkFDRyxVQUFVO3NCQUFsQixLQUFLO2dCQUNHLFdBQVc7c0JBQW5CLEtBQUs7Z0JBQ0csaUJBQWlCO3NCQUF6QixLQUFLO2dCQUNHLGtCQUFrQjtzQkFBMUIsS0FBSztnQkFJYSxZQUFZO3NCQUE5QixNQUFNO3VCQUFDLFNBQVMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBET0NVTUVOVCwgTmdJZiB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQge1xuXHRDb21wb25lbnQsXG5cdEVsZW1lbnRSZWYsXG5cdEV2ZW50RW1pdHRlcixcblx0aW5qZWN0LFxuXHRJbnB1dCxcblx0Tmdab25lLFxuXHRPbkRlc3Ryb3ksXG5cdE9uSW5pdCxcblx0T3V0cHV0LFxuXHRWaWV3Q2hpbGQsXG5cdFZpZXdFbmNhcHN1bGF0aW9uXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtcblx0Z2V0Rm9jdXNhYmxlQm91bmRhcnlFbGVtZW50cyxcblx0aHViUnVuVHJhbnNpdGlvbixcblx0cmVmbG93LFxuXHRUcmFuc2l0aW9uT3B0aW9uc1xufSBmcm9tICduZy1odWItdWktdXRpbHMnO1xuaW1wb3J0IHsgT2JzZXJ2YWJsZSwgU3ViamVjdCwgemlwIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyB0YWtlIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuXG5AQ29tcG9uZW50KHtcblx0c2VsZWN0b3I6ICdodWItcG9ydGFsLXdpbmRvdycsXG5cdHN0YW5kYWxvbmU6IHRydWUsXG5cdGltcG9ydHM6IFtOZ0lmXSxcblx0aG9zdDoge1xuXHRcdCdbY2xhc3NdJzogJ1wicG9ydGFsIGQtYmxvY2tcIiArICh3aW5kb3dDbGFzcyA/IFwiIFwiICsgd2luZG93Q2xhc3MgOiBcIlwiKScsXG5cdFx0J1tjbGFzcy5mYWRlXSc6ICdhbmltYXRpb24nLFxuXHRcdHJvbGU6ICdkaWFsb2cnLFxuXHRcdHRhYmluZGV4OiAnLTEnLFxuXHRcdCdbYXR0ci5hcmlhLXBvcnRhbF0nOiAndHJ1ZScsXG5cdFx0J1thdHRyLmFyaWEtbGFiZWxsZWRieV0nOiAnYXJpYUxhYmVsbGVkQnknLFxuXHRcdCdbYXR0ci5hcmlhLWRlc2NyaWJlZGJ5XSc6ICdhcmlhRGVzY3JpYmVkQnknXG5cdH0sXG5cdHRlbXBsYXRlOiBgXG5cdFx0PGRpdlxuXHRcdFx0I2RpYWxvZ1xuXHRcdFx0W2NsYXNzXT1cIlxuXHRcdFx0XHQncG9ydGFsLWRpYWxvZycgK1xuXHRcdFx0XHQoc2Nyb2xsYWJsZSA/ICcgcG9ydGFsLWRpYWxvZy1zY3JvbGxhYmxlJyA6ICcnKSArXG5cdFx0XHRcdChwb3J0YWxEaWFsb2dDbGFzcyA/ICcgJyArIHBvcnRhbERpYWxvZ0NsYXNzIDogJycpXG5cdFx0XHRcIlxuXHRcdFx0cm9sZT1cImRvY3VtZW50XCJcblx0XHQ+XG5cdFx0XHQ8ZGl2XG5cdFx0XHRcdFtjbGFzc109XCJcblx0XHRcdFx0XHQncG9ydGFsLWNvbnRlbnQnICtcblx0XHRcdFx0XHQocG9ydGFsQ29udGVudENsYXNzID8gJyAnICsgcG9ydGFsQ29udGVudENsYXNzIDogJycpXG5cdFx0XHRcdFwiXG5cdFx0XHQ+XG5cdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCJzaW5nbGVDb250ZW50OyBlbHNlIG11bHRpcGxlQ29udGVudFwiPlxuXHRcdFx0XHRcdDxuZy1jb250ZW50PjwvbmctY29udGVudD5cblx0XHRcdFx0PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdDxuZy10ZW1wbGF0ZSAjbXVsdGlwbGVDb250ZW50PlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJwb3J0YWwtaGVhZGVyXCI+XG5cdFx0XHRcdFx0XHQ8bmctY29udGVudCAvPlxuXHRcdFx0XHRcdFx0PGJ1dHRvblxuXHRcdFx0XHRcdFx0XHR0eXBlPVwiYnV0dG9uXCJcblx0XHRcdFx0XHRcdFx0Y2xhc3M9XCJidG4tY2xvc2VcIlxuXHRcdFx0XHRcdFx0XHRkYXRhLWJzLWRpc21pc3M9XCJwb3J0YWxcIlxuXHRcdFx0XHRcdFx0XHRhcmlhLWxhYmVsPVwiQ2xvc2VcIlxuXHRcdFx0XHRcdFx0XHQoY2xpY2spPVwiZGlzbWlzcyhudWxsKVwiXG5cdFx0XHRcdFx0XHQ+PC9idXR0b24+XG5cdFx0XHRcdFx0PC9kaXY+XG5cdFx0XHRcdFx0PGRpdiBjbGFzcz1cInBvcnRhbC1ib2R5XCI+XG5cdFx0XHRcdFx0XHQ8bmctY29udGVudCAvPlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHRcdDxkaXYgY2xhc3M9XCJwb3J0YWwtZm9vdGVyXCI+XG5cdFx0XHRcdFx0XHQ8bmctY29udGVudCAvPlxuXHRcdFx0XHRcdDwvZGl2PlxuXHRcdFx0XHQ8L25nLXRlbXBsYXRlPlxuXHRcdFx0PC9kaXY+XG5cdFx0PC9kaXY+XG5cdGAsXG5cdGVuY2Fwc3VsYXRpb246IFZpZXdFbmNhcHN1bGF0aW9uLk5vbmUsXG5cdHN0eWxlVXJsOiAnLi9wb3J0YWwuc2Nzcydcbn0pXG5leHBvcnQgY2xhc3MgSHViUG9ydGFsV2luZG93IGltcGxlbWVudHMgT25Jbml0LCBPbkRlc3Ryb3kge1xuXHRwcml2YXRlIF9kb2N1bWVudCA9IGluamVjdChET0NVTUVOVCk7XG5cdHByaXZhdGUgX2VsUmVmID0gaW5qZWN0KEVsZW1lbnRSZWY8SFRNTEVsZW1lbnQ+KTtcblx0cHJpdmF0ZSBfem9uZSA9IGluamVjdChOZ1pvbmUpO1xuXG5cdHByaXZhdGUgX2Nsb3NlZCQgPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXHRwcml2YXRlIF9lbFdpdGhGb2N1czogRWxlbWVudCB8IG51bGwgPSBudWxsOyAvLyBlbGVtZW50IHRoYXQgaXMgZm9jdXNlZCBwcmlvciB0byBwb3J0YWwgb3BlbmluZ1xuXG5cdEBWaWV3Q2hpbGQoJ2RpYWxvZycsIHsgc3RhdGljOiB0cnVlIH0pXG5cdHByaXZhdGUgX2RpYWxvZ0VsOiBFbGVtZW50UmVmPEhUTUxFbGVtZW50PjtcblxuXHRASW5wdXQoKSBhbmltYXRpb246IGJvb2xlYW47XG5cdEBJbnB1dCgpIGFyaWFMYWJlbGxlZEJ5OiBzdHJpbmc7XG5cdEBJbnB1dCgpIGFyaWFEZXNjcmliZWRCeTogc3RyaW5nO1xuXHRASW5wdXQoKSBzY3JvbGxhYmxlOiBzdHJpbmc7XG5cdEBJbnB1dCgpIHdpbmRvd0NsYXNzOiBzdHJpbmc7XG5cdEBJbnB1dCgpIHBvcnRhbERpYWxvZ0NsYXNzOiBzdHJpbmc7XG5cdEBJbnB1dCgpIHBvcnRhbENvbnRlbnRDbGFzczogc3RyaW5nO1xuXG5cdHNpbmdsZUNvbnRlbnQhOiBib29sZWFuO1xuXG5cdEBPdXRwdXQoJ2Rpc21pc3MnKSBkaXNtaXNzRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cblx0c2hvd24gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXHRoaWRkZW4gPSBuZXcgU3ViamVjdDx2b2lkPigpO1xuXG5cdGRpc21pc3MocmVhc29uKTogdm9pZCB7XG5cdFx0dGhpcy5kaXNtaXNzRXZlbnQuZW1pdChyZWFzb24pO1xuXHR9XG5cblx0bmdPbkluaXQoKSB7XG5cdFx0dGhpcy5fZWxXaXRoRm9jdXMgPSB0aGlzLl9kb2N1bWVudC5hY3RpdmVFbGVtZW50O1xuXHRcdHRoaXMuX3pvbmUub25TdGFibGVcblx0XHRcdC5hc09ic2VydmFibGUoKVxuXHRcdFx0LnBpcGUodGFrZSgxKSlcblx0XHRcdC5zdWJzY3JpYmUoKCkgPT4ge1xuXHRcdFx0XHR0aGlzLl9zaG93KCk7XG5cdFx0XHR9KTtcblx0fVxuXG5cdG5nT25EZXN0cm95KCkge1xuXHRcdHRoaXMuX2Rpc2FibGVFdmVudEhhbmRsaW5nKCk7XG5cdH1cblxuXHRoaWRlKCk6IE9ic2VydmFibGU8YW55PiB7XG5cdFx0Y29uc3QgeyBuYXRpdmVFbGVtZW50IH0gPSB0aGlzLl9lbFJlZjtcblx0XHRjb25zdCBjb250ZXh0OiBUcmFuc2l0aW9uT3B0aW9uczxhbnk+ID0ge1xuXHRcdFx0YW5pbWF0aW9uOiB0aGlzLmFuaW1hdGlvbixcblx0XHRcdHJ1bm5pbmdUcmFuc2l0aW9uOiAnc3RvcCdcblx0XHR9O1xuXG5cdFx0Y29uc3Qgd2luZG93VHJhbnNpdGlvbiQgPSBodWJSdW5UcmFuc2l0aW9uKFxuXHRcdFx0dGhpcy5fem9uZSxcblx0XHRcdG5hdGl2ZUVsZW1lbnQsXG5cdFx0XHQoKSA9PiBuYXRpdmVFbGVtZW50LmNsYXNzTGlzdC5yZW1vdmUoJ3Nob3cnKSxcblx0XHRcdGNvbnRleHRcblx0XHQpO1xuXHRcdGNvbnN0IGRpYWxvZ1RyYW5zaXRpb24kID0gaHViUnVuVHJhbnNpdGlvbihcblx0XHRcdHRoaXMuX3pvbmUsXG5cdFx0XHR0aGlzLl9kaWFsb2dFbC5uYXRpdmVFbGVtZW50LFxuXHRcdFx0KCkgPT4ge30sXG5cdFx0XHRjb250ZXh0XG5cdFx0KTtcblxuXHRcdGNvbnN0IHRyYW5zaXRpb25zJCA9IHppcCh3aW5kb3dUcmFuc2l0aW9uJCwgZGlhbG9nVHJhbnNpdGlvbiQpO1xuXHRcdHRyYW5zaXRpb25zJC5zdWJzY3JpYmUoKCkgPT4ge1xuXHRcdFx0dGhpcy5oaWRkZW4ubmV4dCgpO1xuXHRcdFx0dGhpcy5oaWRkZW4uY29tcGxldGUoKTtcblx0XHR9KTtcblxuXHRcdHRoaXMuX2Rpc2FibGVFdmVudEhhbmRsaW5nKCk7XG5cdFx0dGhpcy5fcmVzdG9yZUZvY3VzKCk7XG5cblx0XHRyZXR1cm4gdHJhbnNpdGlvbnMkO1xuXHR9XG5cblx0cHJpdmF0ZSBfc2hvdygpIHtcblx0XHRjb25zdCBjb250ZXh0OiBUcmFuc2l0aW9uT3B0aW9uczxhbnk+ID0ge1xuXHRcdFx0YW5pbWF0aW9uOiB0aGlzLmFuaW1hdGlvbixcblx0XHRcdHJ1bm5pbmdUcmFuc2l0aW9uOiAnY29udGludWUnXG5cdFx0fTtcblxuXHRcdGNvbnN0IHdpbmRvd1RyYW5zaXRpb24kID0gaHViUnVuVHJhbnNpdGlvbihcblx0XHRcdHRoaXMuX3pvbmUsXG5cdFx0XHR0aGlzLl9lbFJlZi5uYXRpdmVFbGVtZW50LFxuXHRcdFx0KGVsZW1lbnQ6IEhUTUxFbGVtZW50LCBhbmltYXRpb246IGJvb2xlYW4pID0+IHtcblx0XHRcdFx0aWYgKGFuaW1hdGlvbikge1xuXHRcdFx0XHRcdHJlZmxvdyhlbGVtZW50KTtcblx0XHRcdFx0fVxuXHRcdFx0XHRlbGVtZW50LmNsYXNzTGlzdC5hZGQoJ3Nob3cnKTtcblx0XHRcdH0sXG5cdFx0XHRjb250ZXh0XG5cdFx0KTtcblx0XHRjb25zdCBkaWFsb2dUcmFuc2l0aW9uJCA9IGh1YlJ1blRyYW5zaXRpb24oXG5cdFx0XHR0aGlzLl96b25lLFxuXHRcdFx0dGhpcy5fZGlhbG9nRWwubmF0aXZlRWxlbWVudCxcblx0XHRcdCgpID0+IHt9LFxuXHRcdFx0Y29udGV4dFxuXHRcdCk7XG5cblx0XHR6aXAod2luZG93VHJhbnNpdGlvbiQsIGRpYWxvZ1RyYW5zaXRpb24kKS5zdWJzY3JpYmUoKCkgPT4ge1xuXHRcdFx0dGhpcy5zaG93bi5uZXh0KCk7XG5cdFx0XHR0aGlzLnNob3duLmNvbXBsZXRlKCk7XG5cdFx0fSk7XG5cblx0XHR0aGlzLl9zZXRGb2N1cygpO1xuXHR9XG5cblx0cHJpdmF0ZSBfZGlzYWJsZUV2ZW50SGFuZGxpbmcoKSB7XG5cdFx0dGhpcy5fY2xvc2VkJC5uZXh0KCk7XG5cdH1cblxuXHRwcml2YXRlIF9zZXRGb2N1cygpIHtcblx0XHRjb25zdCB7IG5hdGl2ZUVsZW1lbnQgfSA9IHRoaXMuX2VsUmVmO1xuXHRcdGlmICghbmF0aXZlRWxlbWVudC5jb250YWlucyhkb2N1bWVudC5hY3RpdmVFbGVtZW50KSkge1xuXHRcdFx0Y29uc3QgYXV0b0ZvY3VzYWJsZSA9IG5hdGl2ZUVsZW1lbnQucXVlcnlTZWxlY3Rvcihcblx0XHRcdFx0YFtodWJBdXRvZm9jdXNdYFxuXHRcdFx0KSBhcyBIVE1MRWxlbWVudDtcblx0XHRcdGNvbnN0IGZpcnN0Rm9jdXNhYmxlID1cblx0XHRcdFx0Z2V0Rm9jdXNhYmxlQm91bmRhcnlFbGVtZW50cyhuYXRpdmVFbGVtZW50KVswXTtcblxuXHRcdFx0Y29uc3QgZWxlbWVudFRvRm9jdXMgPVxuXHRcdFx0XHRhdXRvRm9jdXNhYmxlIHx8IGZpcnN0Rm9jdXNhYmxlIHx8IG5hdGl2ZUVsZW1lbnQ7XG5cdFx0XHRlbGVtZW50VG9Gb2N1cy5mb2N1cygpO1xuXHRcdH1cblx0fVxuXG5cdHByaXZhdGUgX3Jlc3RvcmVGb2N1cygpIHtcblx0XHRjb25zdCBib2R5ID0gdGhpcy5fZG9jdW1lbnQuYm9keTtcblx0XHRjb25zdCBlbFdpdGhGb2N1cyA9IHRoaXMuX2VsV2l0aEZvY3VzO1xuXG5cdFx0bGV0IGVsZW1lbnRUb0ZvY3VzO1xuXHRcdGlmIChlbFdpdGhGb2N1cyAmJiBlbFdpdGhGb2N1c1snZm9jdXMnXSAmJiBib2R5LmNvbnRhaW5zKGVsV2l0aEZvY3VzKSkge1xuXHRcdFx0ZWxlbWVudFRvRm9jdXMgPSBlbFdpdGhGb2N1cztcblx0XHR9IGVsc2Uge1xuXHRcdFx0ZWxlbWVudFRvRm9jdXMgPSBib2R5O1xuXHRcdH1cblx0XHR0aGlzLl96b25lLnJ1bk91dHNpZGVBbmd1bGFyKCgpID0+IHtcblx0XHRcdHNldFRpbWVvdXQoKCkgPT4gZWxlbWVudFRvRm9jdXMuZm9jdXMoKSk7XG5cdFx0XHR0aGlzLl9lbFdpdGhGb2N1cyA9IG51bGw7XG5cdFx0fSk7XG5cdH1cbn1cbiJdfQ==