@dotglitch/ngx-common
Version:
Angular components and utilities that are commonly used.
205 lines • 40.4 kB
JavaScript
import { NgComponentOutlet, NgTemplateOutlet } from '@angular/common';
import { Component, HostListener, Inject, Input, TemplateRef } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Optional } from '@angular/core';
import { createApplication } from '@angular/platform-browser';
import { firstValueFrom } from 'rxjs';
import { MenuComponent } from '../menu/menu.component';
import * as i0 from "@angular/core";
import * as i1 from "@angular/material/dialog";
const zone = new Zone(Zone.current, { name: "@dotglitch_menu", properties: {} });
export const calcTooltipBounds = async (template, data, matDialogConfig) => {
const args = {
data: data || {},
template,
config: {},
selfCords: { left: "0px", top: "0px" },
ownerCords: { x: 0, y: 0, width: 0, height: 0 },
id: null
};
// dimensions should be in px... Might need to handle vw/v
if (matDialogConfig?.width && matDialogConfig?.height) {
return {
width: parseInt(matDialogConfig.width),
height: parseInt(matDialogConfig.height),
top: 0,
left: 0,
right: 0,
bottom: 0
};
}
return new Promise((res, rej) => {
zone.run(async () => {
// Forcibly bootstrap the ctx menu outside of the client application's zone.
const app = await createApplication({
providers: [
{ provide: MAT_DIALOG_DATA, useValue: args }
]
});
const del = document.createElement("div");
del.style.position = "absolute";
del.style.left = '-1000vw';
document.body.append(del);
const base = app.bootstrap(TooltipComponent, del);
const { instance } = base;
await firstValueFrom(app.isStable);
const el = instance.viewContainer?.element?.nativeElement;
const rect = el.getBoundingClientRect();
app.destroy();
del.remove();
res(rect);
});
});
};
export class TooltipComponent {
constructor(viewContainer, _data, dialog, // optional only for the purpose of estimating dimensions
dialogRef) {
this.viewContainer = viewContainer;
this._data = _data;
this.dialog = dialog;
this.dialogRef = dialogRef;
this.isTemplate = false;
this.isMenu = false;
this.hasBootstrapped = false;
this.pointerIsOnVoid = false;
this.isLockedOpen = false;
this.clientWidth = window.innerWidth;
this.clientHeight = window.innerHeight;
this.coverRectCords = {
top: 0,
left: 0,
height: 0,
width: 0
};
// Defaults are set before @Input() hooks evaluate
this.data = this.data || this._data?.data || {};
this.config = this.config || this._data?.config;
this.dialog = this.dialog || this._data?.dialog;
this.template = this.template || this._data?.template;
this.ownerCords = this.ownerCords || this._data?.ownerCords;
this.selfCords = this.selfCords || this._data?.selfCords;
this.isLockedOpen = this._data?.isLockedOpen || this.config?.stayOpen;
}
ngOnInit() {
const selfY = parseInt(this.selfCords.top.replace('px', ''));
const selfX = parseInt(this.selfCords.left.replace('px', ''));
this.coverRectCords = {
top: this.ownerCords.y - selfY - 16,
left: this.ownerCords.x - selfX - 16,
height: this.ownerCords.height + 32,
width: this.ownerCords.width + 32
};
if (Array.isArray(this.template))
this.isMenu = true;
else if (this.template instanceof TemplateRef)
this.isTemplate = true;
else if (typeof this.template == "function")
this.isTemplate = false;
else
throw new Error("Unrecognized template object provided.");
// TODO: resolve the event hook with the .void element
setTimeout(() => {
this.hasBootstrapped = true;
if (this.pointerIsOnVoid && !this.isLockedOpen)
this.dialogRef.close();
}, 200);
}
ngAfterViewInit() {
const el = this.viewContainer.element.nativeElement;
el.addEventListener("keydown", evt => {
this.isLockedOpen = true;
});
el.addEventListener("pointerdown", evt => {
this.isLockedOpen = true;
});
el.addEventListener("touch", evt => {
this.isLockedOpen = true;
});
}
onKeyDown(evt) {
if (this.config?.freezeOnKeyCode) {
if (evt.code == this.config.freezeOnKeyCode)
this.isLockedOpen = true;
}
}
onVoidPointerDown(evt) {
if (!this.isLockedOpen) {
const el = this.viewContainer.element.nativeElement;
el.querySelector(".void").remove();
setTimeout(() => {
const clonedEvt = new PointerEvent("pointerdown", evt);
const target = document.elementFromPoint(evt.clientX, evt.clientY);
console.log("DEBUG EVENTS", { evt, clonedEvt });
target.dispatchEvent(clonedEvt);
}, 15);
}
this.closeOnVoid(true);
}
// If the void element gets stuck open, make wheel events pass through.
onWheel(evt) {
const el = this.viewContainer.element.nativeElement;
el.style.display = "none";
const target = document.elementFromPoint(evt.clientX, evt.clientY);
el.style.display = "block";
target.scroll({
top: evt.deltaY + target.scrollTop,
left: evt.deltaX + target.scrollLeft,
behavior: "smooth"
});
}
/**
* Close the tooltip if these actions occur
*/
onClose() {
if (!this.isLockedOpen)
this.dialogRef?.close();
this.clientWidth = window.innerWidth;
this.clientHeight = window.innerHeight;
}
closeOnVoid(force = false) {
if (!this.isLockedOpen || force)
this.dialogRef.close();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TooltipComponent, deps: [{ token: i0.ViewContainerRef }, { token: MAT_DIALOG_DATA, optional: true }, { token: i1.MatDialog, optional: true }, { token: i1.MatDialogRef, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.1.2", type: TooltipComponent, isStandalone: true, selector: "ngx-tooltip", inputs: { data: "data", config: "config", ownerCords: "ownerCords", selfCords: "selfCords", template: "template" }, host: { listeners: { "window:keydown": "onKeyDown($event)", "window:resize": "onClose()", "window:blur": "onClose()", "pointerleave": "onClose()" } }, ngImport: i0, template: "<!-- Mouse event blocker for pointer leave -->\n@if (coverRectCords) {\n <!-- <div\n class=\"owner-mask\"\n [style.top]=\"coverRectCords.top + 'px'\"\n [style.left]=\"coverRectCords.left + 'px'\"\n [style.height]=\"coverRectCords.height + 'px'\"\n [style.width]=\"coverRectCords.width + 'px'\"\n style=\"z-index: -1;\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n ></div> -->\n\n <div class=\"void left\"\n [style.top]=\"'0px'\"\n [style.left]=\"'0px'\"\n [style.height]=\"'100%'\"\n [style.width]=\"(ownerCords.left) + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void top\"\n [style.top]=\"'0px'\"\n [style.left]=\"ownerCords.left + 'px'\"\n [style.height]=\"ownerCords.top + 'px'\"\n [style.width]=\"ownerCords.width + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void right\"\n [style.top]=\"'0px'\"\n [style.left]=\"(ownerCords.left + ownerCords.width) + 'px'\"\n [style.height]=\"'100%'\"\n [style.width]=\"(clientWidth - (ownerCords.left + ownerCords.width)) + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void\"\n [style.top]=\"(ownerCords.top + ownerCords.height) + 'px'\"\n [style.left]=\"ownerCords.left + 'px'\"\n [style.height]=\"(clientHeight - (ownerCords.top + ownerCords.height)) + 'px'\"\n [style.width]=\"ownerCords.width + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n}\n\n\n<div\n #container\n class=\"container\"\n>\n @if (isMenu) {\n <ngx-menu\n [config]=\"config\"\n [data]=\"data\"\n [ownerCords]=\"ownerCords\"\n [selfCords]=\"selfCords\"\n [items]=\"$any(template)\"\n [isLockedOpen]=\"config.stayOpen\"\n />\n }\n @else if (isTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"$any(template)\"\n [ngTemplateOutletContext]=\"{\n '$implicit': data,\n 'dialog': dialogRef,\n 'element': container,\n 'tooltip': this\n }\"\n ></ng-container>\n }\n @else {\n <ng-container\n [ngComponentOutlet]=\"$any(template)\"\n >\n </ng-container>\n }\n</div>\n", styles: ["::ng-deep .cdk-overlay-container .ngx-tooltip{--mdc-dialog-container-color: var(--ngx-tooltip-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__surface{overflow:visible;background-color:#0000}::ng-deep .cdk-overlay-container .context-menu-backdrop.cdk-overlay-backdrop-showing{opacity:0}::ng-deep .cdk-overlay-pane.ngx-tooltip .mat-dialog-container{padding:0}:host{min-width:2px;min-height:2px;display:block}.void,.owner-mask{position:absolute}.void{top:-100vh;right:-100vw;bottom:-100vh;left:-100vw;z-index:-2;position:fixed}.container{width:100%;height:100%;background:var(--ngx-tooltip-background-color, #333);border-radius:6px;overflow:hidden}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletContent", "ngComponentOutletNgModule", "ngComponentOutletNgModuleFactory"] }, { kind: "component", type: MenuComponent, selector: "ngx-menu", inputs: ["data", "items", "config", "id", "overlayOverlap", "hoverDelay", "showDebugOverlay", "targetBounds", "ownerCords", "selfCords", "parentItem", "parentContext", "isLockedOpen"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.1.2", ngImport: i0, type: TooltipComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-tooltip', imports: [
NgTemplateOutlet,
NgComponentOutlet,
MenuComponent
], standalone: true, template: "<!-- Mouse event blocker for pointer leave -->\n@if (coverRectCords) {\n <!-- <div\n class=\"owner-mask\"\n [style.top]=\"coverRectCords.top + 'px'\"\n [style.left]=\"coverRectCords.left + 'px'\"\n [style.height]=\"coverRectCords.height + 'px'\"\n [style.width]=\"coverRectCords.width + 'px'\"\n style=\"z-index: -1;\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n ></div> -->\n\n <div class=\"void left\"\n [style.top]=\"'0px'\"\n [style.left]=\"'0px'\"\n [style.height]=\"'100%'\"\n [style.width]=\"(ownerCords.left) + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void top\"\n [style.top]=\"'0px'\"\n [style.left]=\"ownerCords.left + 'px'\"\n [style.height]=\"ownerCords.top + 'px'\"\n [style.width]=\"ownerCords.width + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void right\"\n [style.top]=\"'0px'\"\n [style.left]=\"(ownerCords.left + ownerCords.width) + 'px'\"\n [style.height]=\"'100%'\"\n [style.width]=\"(clientWidth - (ownerCords.left + ownerCords.width)) + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n <div class=\"void\"\n [style.top]=\"(ownerCords.top + ownerCords.height) + 'px'\"\n [style.left]=\"ownerCords.left + 'px'\"\n [style.height]=\"(clientHeight - (ownerCords.top + ownerCords.height)) + 'px'\"\n [style.width]=\"ownerCords.width + 'px'\"\n (pointerenter)=\"pointerIsOnVoid = true; hasBootstrapped && closeOnVoid()\"\n (pointerleave)=\"pointerIsOnVoid = false\"\n (pointerdown)=\"onVoidPointerDown($event)\"\n (wheel)=\"onWheel($event)\"\n ></div>\n}\n\n\n<div\n #container\n class=\"container\"\n>\n @if (isMenu) {\n <ngx-menu\n [config]=\"config\"\n [data]=\"data\"\n [ownerCords]=\"ownerCords\"\n [selfCords]=\"selfCords\"\n [items]=\"$any(template)\"\n [isLockedOpen]=\"config.stayOpen\"\n />\n }\n @else if (isTemplate) {\n <ng-container\n [ngTemplateOutlet]=\"$any(template)\"\n [ngTemplateOutletContext]=\"{\n '$implicit': data,\n 'dialog': dialogRef,\n 'element': container,\n 'tooltip': this\n }\"\n ></ng-container>\n }\n @else {\n <ng-container\n [ngComponentOutlet]=\"$any(template)\"\n >\n </ng-container>\n }\n</div>\n", styles: ["::ng-deep .cdk-overlay-container .ngx-tooltip{--mdc-dialog-container-color: var(--ngx-tooltip-background-color, #2f2f2f)}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__container{transform-origin:top left}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog--open .mdc-dialog__container{transform:none}::ng-deep .cdk-overlay-container .ngx-tooltip .mdc-dialog__surface{overflow:visible;background-color:#0000}::ng-deep .cdk-overlay-container .context-menu-backdrop.cdk-overlay-backdrop-showing{opacity:0}::ng-deep .cdk-overlay-pane.ngx-tooltip .mat-dialog-container{padding:0}:host{min-width:2px;min-height:2px;display:block}.void,.owner-mask{position:absolute}.void{top:-100vh;right:-100vw;bottom:-100vh;left:-100vw;z-index:-2;position:fixed}.container{width:100%;height:100%;background:var(--ngx-tooltip-background-color, #333);border-radius:6px;overflow:hidden}\n"] }]
}], ctorParameters: () => [{ type: i0.ViewContainerRef }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [MAT_DIALOG_DATA]
}] }, { type: i1.MatDialog, decorators: [{
type: Optional
}] }, { type: i1.MatDialogRef, decorators: [{
type: Optional
}] }], propDecorators: { data: [{
type: Input
}], config: [{
type: Input
}], ownerCords: [{
type: Input
}], selfCords: [{
type: Input
}], template: [{
type: Input
}], onKeyDown: [{
type: HostListener,
args: ["window:keydown", ['$event']]
}], onClose: [{
type: HostListener,
args: ["window:resize"]
}, {
type: HostListener,
args: ["window:blur"]
}, {
type: HostListener,
args: ["pointerleave"]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG9vbHRpcC5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9wYWNrYWdlcy9jb21tb24vc3JjL2NvbXBvbmVudHMvdG9vbHRpcC90b29sdGlwLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL3BhY2thZ2VzL2NvbW1vbi9zcmMvY29tcG9uZW50cy90b29sdGlwL3Rvb2x0aXAuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGlCQUFpQixFQUFFLGdCQUFnQixFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDdEUsT0FBTyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRSxXQUFXLEVBQTBCLE1BQU0sZUFBZSxDQUFDO0FBQzVHLE9BQU8sRUFBMkIsZUFBZSxFQUFtQixNQUFNLDBCQUEwQixDQUFDO0FBQ3JHLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxlQUFlLENBQUM7QUFDekMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUd0QyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sd0JBQXdCLENBQUM7OztBQUd2RCxNQUFNLElBQUksR0FBRyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixFQUFFLFVBQVUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0FBRWpGLE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLEtBQUssRUFBRSxRQUFzQyxFQUFFLElBQVMsRUFBRSxlQUFnQyxFQUFFLEVBQUU7SUFFM0gsTUFBTSxJQUFJLEdBQUc7UUFDVCxJQUFJLEVBQUUsSUFBSSxJQUFJLEVBQUU7UUFDaEIsUUFBUTtRQUNSLE1BQU0sRUFBRSxFQUFFO1FBQ1YsU0FBUyxFQUFFLEVBQUUs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QUFnQjt3QkFDaEIsaUJBQWlCO3dCQUNqQixhQUFhO3FCQUNoQixjQUNXLElBQUk7OzBCQTJCWCxRQUFROzswQkFBSSxNQUFNOzJCQUFDLGVBQWU7OzBCQUNsQyxRQUFROzswQkFDUixRQUFRO3lDQTFCSixJQUFJO3NCQUFaLEtBQUs7Z0JBQ0csTUFBTTtzQkFBZCxLQUFLO2dCQUNHLFVBQVU7c0JBQWxCLEtBQUs7Z0JBQ0csU0FBUztzQkFBakIsS0FBSztnQkFDRyxRQUFRO3NCQUFoQixLQUFLO2dCQWdGTixTQUFTO3NCQURSLFlBQVk7dUJBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxRQUFRLENBQUM7Z0JBNkNsQyxPQUFPO3NCQUhkLFlBQVk7dUJBQUMsZUFBZTs7c0JBQzVCLFlBQVk7dUJBQUMsYUFBYTs7c0JBQzFCLFlBQVk7dUJBQUMsY0FBYyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IE5nQ29tcG9uZW50T3V0bGV0LCBOZ1RlbXBsYXRlT3V0bGV0IH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbmltcG9ydCB7IENvbXBvbmVudCwgSG9zdExpc3RlbmVyLCBJbmplY3QsIElucHV0LCBUZW1wbGF0ZVJlZiwgVHlwZSwgVmlld0NvbnRhaW5lclJlZiB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgTWF0RGlhbG9nLCBNYXREaWFsb2dSZWYsIE1BVF9ESUFMT0dfREFUQSwgTWF0RGlhbG9nQ29uZmlnIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvZGlhbG9nJztcbmltcG9ydCB7IE9wdGlvbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBjcmVhdGVBcHBsaWNhdGlvbiB9IGZyb20gJ0Bhbmd1bGFyL3BsYXRmb3JtLWJyb3dzZXInO1xuaW1wb3J0IHsgZmlyc3RWYWx1ZUZyb20gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IFRvb2x0aXBPcHRpb25zIH0gZnJvbSAnLi4vLi4vdHlwZXMvdG9vbHRpcCc7XG5pbXBvcnQgeyBNZW51SXRlbSB9IGZyb20gJy4uLy4uL3R5cGVzL21lbnUnO1xuaW1wb3J0IHsgTWVudUNvbXBvbmVudCB9IGZyb20gJy4uL21lbnUvbWVudS5jb21wb25lbnQnO1xuXG5kZWNsYXJlIGNvbnN0IFpvbmU7XG5jb25zdCB6b25lID0gbmV3IFpvbmUoWm9uZS5jdXJyZW50LCB7IG5hbWU6IFwiQGRvdGdsaXRjaF9tZW51XCIsIHByb3BlcnRpZXM6IHt9IH0pO1xuXG5leHBvcnQgY29uc3QgY2FsY1Rvb2x0aXBCb3VuZHMgPSBhc3luYyAodGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT4gfCBUeXBlPGFueT4sIGRhdGE6IGFueSwgbWF0RGlhbG9nQ29uZmlnOiBNYXREaWFsb2dDb25maWcpID0+IHtcblxuICAgIGNvbnN0IGFyZ3MgPSB7XG4gICAgICAgIGRhdGE6IGRhdGEgfHwge30sXG4gICAgICAgIHRlbXBsYXRlLFxuICAgICAgICBjb25maWc6IHt9LFxuICAgICAgICBzZWxmQ29yZHM6IHsgbGVmdDogXCIwcHhcIiwgdG9wOiBcIjBweFwiIH0sXG4gICAgICAgIG93bmVyQ29yZHM6IHsgeDogMCwgeTogMCwgd2lkdGg6IDAsIGhlaWdodDogMCB9LFxuICAgICAgICBpZDogbnVsbFxuICAgIH1cblxuICAgIC8vIGRpbWVuc2lvbnMgc2hvdWxkIGJlIGluIHB4Li4uIE1pZ2h0IG5lZWQgdG8gaGFuZGxlIHZ3L3ZcbiAgICBpZiAobWF0RGlhbG9nQ29uZmlnPy53aWR0aCAmJiBtYXREaWFsb2dDb25maWc/LmhlaWdodCkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgICAgd2lkdGg6IHBhcnNlSW50KG1hdERpYWxvZ0NvbmZpZy53aWR0aCksXG4gICAgICAgICAgICBoZWlnaHQ6IHBhcnNlSW50KG1hdERpYWxvZ0NvbmZpZy5oZWlnaHQpLFxuICAgICAgICAgICAgdG9wOiAwLFxuICAgICAgICAgICAgbGVmdDogMCxcbiAgICAgICAgICAgIHJpZ2h0OiAwLFxuICAgICAgICAgICAgYm90dG9tOiAwXG4gICAgICAgIH0gYXMgRE9NUmVjdDtcbiAgICB9XG5cbiAgICByZXR1cm4gbmV3IFByb21pc2U8RE9NUmVjdD4oKHJlcywgcmVqKSA9PiB7XG4gICAgICAgIHpvbmUucnVuKGFzeW5jICgpID0+IHtcbiAgICAgICAgICAgIC8vIEZvcmNpYmx5IGJvb3RzdHJhcCB0aGUgY3R4IG1lbnUgb3V0c2lkZSBvZiB0aGUgY2xpZW50IGFwcGxpY2F0aW9uJ3Mgem9uZS5cbiAgICAgICAgICAgIGNvbnN0IGFwcCA9IGF3YWl0IGNyZWF0ZUFwcGxpY2F0aW9uKHtcbiAgICAgICAgICAgICAgICBwcm92aWRlcnM6IFtcbiAgICAgICAgICAgICAgICAgICAgeyBwcm92aWRlOiBNQVRfRElBTE9HX0RBVEEsIHVzZVZhbHVlOiBhcmdzIH1cbiAgICAgICAgICAgICAgICBdXG4gICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgY29uc3QgZGVsID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImRpdlwiKTtcbiAgICAgICAgICAgIGRlbC5zdHlsZS5wb3NpdGlvbiA9IFwiYWJzb2x1dGVcIjtcbiAgICAgICAgICAgIGRlbC5zdHlsZS5sZWZ0ID0gJy0xMDAwdncnO1xuICAgICAgICAgICAgZG9jdW1lbnQuYm9keS5hcHBlbmQoZGVsKTtcblxuICAgICAgICAgICAgY29uc3QgYmFzZSA9IGFwcC5ib290c3RyYXAoVG9vbHRpcENvbXBvbmVudCwgZGVsKTtcbiAgICAgICAgICAgIGNvbnN0IHsgaW5zdGFuY2UgfSA9IGJhc2U7XG5cbiAgICAgICAgICAgIGF3YWl0IGZpcnN0VmFsdWVGcm9tKGFwcC5pc1N0YWJsZSk7XG5cbiAgICAgICAgICAgIGNvbnN0IGVsOiBIVE1MRWxlbWVudCA9IGluc3RhbmNlLnZpZXdDb250YWluZXI/LmVsZW1lbnQ/Lm5hdGl2ZUVsZW1lbnQ7XG5cbiAgICAgICAgICAgIGNvbnN0IHJlY3QgPSBlbC5nZXRCb3VuZGluZ0NsaWVudFJlY3QoKTtcbiAgICAgICAgICAgIGFwcC5kZXN0cm95KCk7XG4gICAgICAgICAgICBkZWwucmVtb3ZlKCk7XG5cbiAgICAgICAgICAgIHJlcyhyZWN0KVxuICAgICAgICB9KTtcbiAgICB9KVxufVxuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ25neC10b29sdGlwJyxcbiAgICB0ZW1wbGF0ZVVybDogJy4vdG9vbHRpcC5jb21wb25lbnQuaHRtbCcsXG4gICAgc3R5bGVVcmxzOiBbJy4vdG9vbHRpcC5jb21wb25lbnQuc2NzcyddLFxuICAgIGltcG9ydHM6IFtcbiAgICAgICAgTmdUZW1wbGF0ZU91dGxldCxcbiAgICAgICAgTmdDb21wb25lbnRPdXRsZXQsXG4gICAgICAgIE1lbnVDb21wb25lbnRcbiAgICBdLFxuICAgIHN0YW5kYWxvbmU6IHRydWVcbn0pXG5leHBvcnQgY2xhc3MgVG9vbHRpcENvbXBvbmVudCB7XG4gICAgQElucHV0KCkgZGF0YTogYW55O1xuICAgIEBJbnB1dCgpIGNvbmZpZzogVG9vbHRpcE9wdGlvbnM7XG4gICAgQElucHV0KCkgb3duZXJDb3JkczogRE9NUmVjdDtcbiAgICBASW5wdXQoKSBzZWxmQ29yZHM7XG4gICAgQElucHV0KCkgdGVtcGxhdGU6IFRlbXBsYXRlUmVmPGFueT4gfCBUeXBlPGFueT4gfCBNZW51SXRlbVtdO1xuXG4gICAgcHVibGljIGlzVGVtcGxhdGUgPSBmYWxzZTtcbiAgICBwdWJsaWMgaXNNZW51ID0gZmFsc2U7XG4gICAgcHVibGljIGhhc0Jvb3RzdHJhcHBlZCA9IGZhbHNlO1xuICAgIHB1YmxpYyBwb2ludGVySXNPblZvaWQgPSBmYWxzZTtcbiAgICBwdWJsaWMgaXNMb2NrZWRPcGVuID0gZmFsc2U7XG5cbiAgICBjbGllbnRXaWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuICAgIGNsaWVudEhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcblxuICAgIGNvdmVyUmVjdENvcmRzID0ge1xuICAgICAgICB0b3A6IDAsXG4gICAgICAgIGxlZnQ6IDAsXG4gICAgICAgIGhlaWdodDogMCxcbiAgICAgICAgd2lkdGg6IDBcbiAgICB9XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHVibGljIHZpZXdDb250YWluZXI6IFZpZXdDb250YWluZXJSZWYsXG4gICAgICAgIEBPcHRpb25hbCgpIEBJbmplY3QoTUFUX0RJQUxPR19EQVRBKSBwcml2YXRlIF9kYXRhOiBhbnksXG4gICAgICAgIEBPcHRpb25hbCgpIHB1YmxpYyBkaWFsb2c6IE1hdERpYWxvZywgLy8gb3B0aW9uYWwgb25seSBmb3IgdGhlIHB1cnBvc2Ugb2YgZXN0aW1hdGluZyBkaW1lbnNpb25zXG4gICAgICAgIEBPcHRpb25hbCgpIHB1YmxpYyBkaWFsb2dSZWY6IE1hdERpYWxvZ1JlZjxhbnk+LFxuICAgICkge1xuICAgICAgICAvLyBEZWZhdWx0cyBhcmUgc2V0IGJlZm9yZSBASW5wdXQoKSBob29rcyBldmFsdWF0ZVxuICAgICAgICB0aGlzLmRhdGEgPSB0aGlzLmRhdGEgfHwgdGhpcy5fZGF0YT8uZGF0YSB8fCB7fTtcbiAgICAgICAgdGhpcy5jb25maWcgPSB0aGlzLmNvbmZpZyB8fCB0aGlzLl9kYXRhPy5jb25maWc7XG4gICAgICAgIHRoaXMuZGlhbG9nID0gdGhpcy5kaWFsb2cgfHwgdGhpcy5fZGF0YT8uZGlhbG9nO1xuICAgICAgICB0aGlzLnRlbXBsYXRlID0gdGhpcy50ZW1wbGF0ZSB8fCB0aGlzLl9kYXRhPy50ZW1wbGF0ZTtcbiAgICAgICAgdGhpcy5vd25lckNvcmRzID0gdGhpcy5vd25lckNvcmRzIHx8IHRoaXMuX2RhdGE/Lm93bmVyQ29yZHM7XG4gICAgICAgIHRoaXMuc2VsZkNvcmRzID0gdGhpcy5zZWxmQ29yZHMgfHwgdGhpcy5fZGF0YT8uc2VsZkNvcmRzO1xuICAgICAgICB0aGlzLmlzTG9ja2VkT3BlbiA9IHRoaXMuX2RhdGE/LmlzTG9ja2VkT3BlbiB8fCB0aGlzLmNvbmZpZz8uc3RheU9wZW47XG4gICAgfVxuXG4gICAgbmdPbkluaXQoKSB7XG5cbiAgICAgICAgY29uc3Qgc2VsZlkgPSBwYXJzZUludCh0aGlzLnNlbGZDb3Jkcy50b3AucmVwbGFjZSgncHgnLCAnJykpO1xuICAgICAgICBjb25zdCBzZWxmWCA9IHBhcnNlSW50KHRoaXMuc2VsZkNvcmRzLmxlZnQucmVwbGFjZSgncHgnLCAnJykpO1xuXG4gICAgICAgIHRoaXMuY292ZXJSZWN0Q29yZHMgPSB7XG4gICAgICAgICAgICB0b3A6IHRoaXMub3duZXJDb3Jkcy55IC0gc2VsZlkgLSAxNixcbiAgICAgICAgICAgIGxlZnQ6IHRoaXMub3duZXJDb3Jkcy54IC0gc2VsZlggLSAxNixcbiAgICAgICAgICAgIGhlaWdodDogdGhpcy5vd25lckNvcmRzLmhlaWdodCArIDMyLFxuICAgICAgICAgICAgd2lkdGg6IHRoaXMub3duZXJDb3Jkcy53aWR0aCArIDMyXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheSh0aGlzLnRlbXBsYXRlKSlcbiAgICAgICAgICAgIHRoaXMuaXNNZW51ID0gIHRydWU7XG4gICAgICAgIGVsc2UgaWYgKHRoaXMudGVtcGxhdGUgaW5zdGFuY2VvZiBUZW1wbGF0ZVJlZilcbiAgICAgICAgICAgIHRoaXMuaXNUZW1wbGF0ZSA9IHRydWU7XG4gICAgICAgIGVsc2UgaWYgKHR5cGVvZiB0aGlzLnRlbXBsYXRlID09IFwiZnVuY3Rpb25cIilcbiAgICAgICAgICAgIHRoaXMuaXNUZW1wbGF0ZSA9IGZhbHNlO1xuICAgICAgICBlbHNlXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJVbnJlY29nbml6ZWQgdGVtcGxhdGUgb2JqZWN0IHByb3ZpZGVkLlwiKTtcblxuICAgICAgICAvLyBUT0RPOiByZXNvbHZlIHRoZSBldmVudCBob29rIHdpdGggdGhlIC52b2lkIGVsZW1lbnRcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLmhhc0Jvb3RzdHJhcHBlZCA9IHRydWU7XG4gICAgICAgICAgICBpZiAodGhpcy5wb2ludGVySXNPblZvaWQgJiYgIXRoaXMuaXNMb2NrZWRPcGVuKVxuICAgICAgICAgICAgICAgIHRoaXMuZGlhbG9nUmVmLmNsb3NlKCk7XG4gICAgICAgIH0sIDIwMCk7XG4gICAgfVxuXG4gICAgbmdBZnRlclZpZXdJbml0KCkge1xuICAgICAgICBjb25zdCBlbCA9IHRoaXMudmlld0NvbnRhaW5lci5lbGVtZW50Lm5hdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQ7XG5cbiAgICAgICAgZWwuYWRkRXZlbnRMaXN0ZW5lcihcImtleWRvd25cIiwgZXZ0ID0+IHtcbiAgICAgICAgICAgIHRoaXMuaXNMb2NrZWRPcGVuID0gdHJ1ZTtcbiAgICAgICAgfSk7XG5cbiAgICAgICAgZWwuYWRkRXZlbnRMaXN0ZW5lcihcInBvaW50ZXJkb3duXCIsIGV2dCA9PiB7XG4gICAgICAgICAgICB0aGlzLmlzTG9ja2VkT3BlbiA9IHRydWU7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIGVsLmFkZEV2ZW50TGlzdGVuZXIoXCJ0b3VjaFwiLCBldnQgPT4ge1xuICAgICAgICAgICAgdGhpcy5pc0xvY2tlZE9wZW4gPSB0cnVlO1xuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICBASG9zdExpc3RlbmVyKFwid2luZG93OmtleWRvd25cIiwgWyckZXZlbnQnXSlcbiAgICBvbktleURvd24oZXZ0OiBLZXlib2FyZEV2ZW50KSB7XG4gICAgICAgIGlmICh0aGlzLmNvbmZpZz8uZnJlZXplT25LZXlDb2RlKSB7XG4gICAgICAgICAgICBpZiAoZXZ0LmNvZGUgPT0gdGhpcy5jb25maWcuZnJlZXplT25LZXlDb2RlKVxuICAgICAgICAgICAgICAgIHRoaXMuaXNMb2NrZWRPcGVuID0gdHJ1ZTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG9uVm9pZFBvaW50ZXJEb3duKGV2dDogUG9pbnRlckV2ZW50KSB7XG4gICAgICAgIGlmICghdGhpcy5pc0xvY2tlZE9wZW4pIHtcbiAgICAgICAgICAgIGNvbnN0IGVsID0gdGhpcy52aWV3Q29udGFpbmVyLmVsZW1lbnQubmF0aXZlRWxlbWVudCBhcyBIVE1MRWxlbWVudDtcbiAgICAgICAgICAgIGVsLnF1ZXJ5U2VsZWN0b3IoXCIudm9pZFwiKS5yZW1vdmUoKTtcblxuICAgICAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc3QgY2xvbmVkRXZ0ID0gbmV3IFBvaW50ZXJFdmVudChcInBvaW50ZXJkb3duXCIsIGV2dCk7XG4gICAgICAgICAgICAgICAgY29uc3QgdGFyZ2V0ID0gZG9jdW1lbnQuZWxlbWVudEZyb21Qb2ludChldnQuY2xpZW50WCwgZXZ0LmNsaWVudFkpIGFzIEhUTUxFbGVtZW50O1xuXG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJERUJVRyBFVkVOVFNcIiwge2V2dCwgY2xvbmVkRXZ0fSk7XG4gICAgICAgICAgICAgICAgdGFyZ2V0LmRpc3BhdGNoRXZlbnQoY2xvbmVkRXZ0KTtcbiAgICAgICAgICAgIH0sIDE1KVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5jbG9zZU9uVm9pZCh0cnVlKVxuICAgIH1cblxuICAgIC8vIElmIHRoZSB2b2lkIGVsZW1lbnQgZ2V0cyBzdHVjayBvcGVuLCBtYWtlIHdoZWVsIGV2ZW50cyBwYXNzIHRocm91Z2guXG4gICAgb25XaGVlbChldnQ6IFdoZWVsRXZlbnQpIHtcbiAgICAgICAgY29uc3QgZWwgPSB0aGlzLnZpZXdDb250YWluZXIuZWxlbWVudC5uYXRpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50O1xuICAgICAgICBlbC5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XG4gICAgICAgIGNvbnN0IHRhcmdldCA9IGRvY3VtZW50LmVsZW1lbnRGcm9tUG9pbnQoZXZ0LmNsaWVudFgsIGV2dC5jbGllbnRZKTtcbiAgICAgICAgZWwuc3R5bGUuZGlzcGxheSA9IFwiYmxvY2tcIjtcblxuICAgICAgICB0YXJnZXQuc2Nyb2xsKHtcbiAgICAgICAgICAgIHRvcDogZXZ0LmRlbHRhWSArIHRhcmdldC5zY3JvbGxUb3AsXG4gICAgICAgICAgICBsZWZ0OiBldnQuZGVsdGFYICsgdGFyZ2V0LnNjcm9sbExlZnQsXG4gICAgICAgICAgICBiZWhhdmlvcjogXCJzbW9vdGhcIlxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbG9zZSB0aGUgdG9vbHRpcCBpZiB0aGVzZSBhY3Rpb25zIG9jY3VyXG4gICAgICovXG4gICAgQEhvc3RMaXN0ZW5lcihcIndpbmRvdzpyZXNpemVcIilcbiAgICBASG9zdExpc3RlbmVyKFwid2luZG93OmJsdXJcIilcbiAgICBASG9zdExpc3RlbmVyKFwicG9pbnRlcmxlYXZlXCIpXG4gICAgcHJpdmF0ZSBvbkNsb3NlKCkge1xuICAgICAgICBpZiAoIXRoaXMuaXNMb2NrZWRPcGVuKVxuICAgICAgICAgICAgdGhpcy5kaWFsb2dSZWY/LmNsb3NlKCk7XG5cbiAgICAgICAgdGhpcy5jbGllbnRXaWR0aCA9IHdpbmRvdy5pbm5lcldpZHRoO1xuICAgICAgICB0aGlzLmNsaWVudEhlaWdodCA9IHdpbmRvdy5pbm5lckhlaWdodDtcbiAgICB9XG5cbiAgICBjbG9zZU9uVm9pZChmb3JjZSA9IGZhbHNlKSB7XG4gICAgICAgIGlmICghdGhpcy5pc0xvY2tlZE9wZW4gfHwgZm9yY2UpXG4gICAgICAgICAgICB0aGlzLmRpYWxvZ1JlZi5jbG9zZSgpO1xuICAgIH1cbn1cbiIsIjwhLS0gTW91c2UgZXZlbnQgYmxvY2tlciBmb3IgcG9pbnRlciBsZWF2ZSAtLT5cbkBpZiAoY292ZXJSZWN0Q29yZHMpIHtcbiAgICA8IS0tIDxkaXZcbiAgICAgICAgY2xhc3M9XCJvd25lci1tYXNrXCJcbiAgICAgICAgW3N0eWxlLnRvcF09XCJjb3ZlclJlY3RDb3Jkcy50b3AgKyAncHgnXCJcbiAgICAgICAgW3N0eWxlLmxlZnRdPVwiY292ZXJSZWN0Q29yZHMubGVmdCArICdweCdcIlxuICAgICAgICBbc3R5bGUuaGVpZ2h0XT1cImNvdmVyUmVjdENvcmRzLmhlaWdodCArICdweCdcIlxuICAgICAgICBbc3R5bGUud2lkdGhdPVwiY292ZXJSZWN0Q29yZHMud2lkdGggKyAncHgnXCJcbiAgICAgICAgc3R5bGU9XCJ6LWluZGV4OiAtMTtcIlxuICAgICAgICAocG9pbnRlcmRvd24pPVwib25Wb2lkUG9pbnRlckRvd24oJGV2ZW50KVwiXG4gICAgPjwvZGl2PiAtLT5cblxuICAgIDxkaXYgY2xhc3M9XCJ2b2lkIGxlZnRcIlxuICAgICAgICBbc3R5bGUudG9wXT1cIicwcHgnXCJcbiAgICAgICAgW3N0eWxlLmxlZnRdPVwiJzBweCdcIlxuICAgICAgICBbc3R5bGUuaGVpZ2h0XT1cIicxMDAlJ1wiXG4gICAgICAgIFtzdHlsZS53aWR0aF09XCIob3duZXJDb3Jkcy5sZWZ0KSArICdweCdcIlxuICAgICAgICAocG9pbnRlcmVudGVyKT1cInBvaW50ZXJJc09uVm9pZCA9IHRydWU7IGhhc0Jvb3RzdHJhcHBlZCAmJiBjbG9zZU9uVm9pZCgpXCJcbiAgICAgICAgKHBvaW50ZXJsZWF2ZSk9XCJwb2ludGVySXNPblZvaWQgPSBmYWxzZVwiXG4gICAgICAgIChwb2ludGVyZG93bik9XCJvblZvaWRQb2ludGVyRG93bigkZXZlbnQpXCJcbiAgICAgICAgKHdoZWVsKT1cIm9uV2hlZWwoJGV2ZW50KVwiXG4gICAgPjwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJ2b2lkIHRvcFwiXG4gICAgICAgIFtzdHlsZS50b3BdPVwiJzBweCdcIlxuICAgICAgICBbc3R5bGUubGVmdF09XCJvd25lckNvcmRzLmxlZnQgKyAncHgnXCJcbiAgICAgICAgW3N0eWxlLmhlaWdodF09XCJvd25lckNvcmRzLnRvcCArICdweCdcIlxuICAgICAgICBbc3R5bGUud2lkdGhdPVwib3duZXJDb3Jkcy53aWR0aCArICdweCdcIlxuICAgICAgICAocG9pbnRlcmVudGVyKT1cInBvaW50ZXJJc09uVm9pZCA9IHRydWU7IGhhc0Jvb3RzdHJhcHBlZCAmJiBjbG9zZU9uVm9pZCgpXCJcbiAgICAgICAgKHBvaW50ZXJsZWF2ZSk9XCJwb2ludGVySXNPblZvaWQgPSBmYWxzZVwiXG4gICAgICAgIChwb2ludGVyZG93bik9XCJvblZvaWRQb2ludGVyRG93bigkZXZlbnQpXCJcbiAgICAgICAgKHdoZWVsKT1cIm9uV2hlZWwoJGV2ZW50KVwiXG4gICAgPjwvZGl2PlxuICAgIDxkaXYgY2xhc3M9XCJ2b2lkIHJpZ2h0XCJcbiAgICAgICAgW3N0eWxlLnRvcF09XCInMHB4J1wiXG4gICAgICAgIFtzdHlsZS5sZWZ0XT1cIihvd25lckNvcmRzLmxlZnQgKyBvd25lckNvcmRzLndpZHRoKSArICdweCdcIlxuICAgICAgICBbc3R5bGUuaGVpZ2h0XT1cIicxMDAlJ1wiXG4gICAgICAgIFtzdHlsZS53aWR0aF09XCIoY2xpZW50V2lkdGggLSAob3duZXJDb3Jkcy5sZWZ0ICsgb3duZXJDb3Jkcy53aWR0aCkpICsgJ3B4J1wiXG4gICAgICAgIChwb2ludGVyZW50ZXIpPVwicG9pbnRlcklzT25Wb2lkID0gdHJ1ZTsgaGFzQm9vdHN0cmFwcGVkICYmIGNsb3NlT25Wb2lkKClcIlxuICAgICAgICAocG9pbnRlcmxlYXZlKT1cInBvaW50ZXJJc09uVm9pZCA9IGZhbHNlXCJcbiAgICAgICAgKHBvaW50ZXJkb3duKT1cIm9uVm9pZFBvaW50ZXJEb3duKCRldmVudClcIlxuICAgICAgICAod2hlZWwpPVwib25XaGVlbCgkZXZlbnQpXCJcbiAgICA+PC9kaXY+XG4gICAgPGRpdiBjbGFzcz1cInZvaWRcIlxuICAgICAgICBbc3R5bGUudG9wXT1cIihvd25lckNvcmRzLnRvcCArIG93bmVyQ29yZHMuaGVpZ2h0KSArICdweCdcIlxuICAgICAgICBbc3R5bGUubGVmdF09XCJvd25lckNvcmRzLmxlZnQgKyAncHgnXCJcbiAgICAgICAgW3N0eWxlLmhlaWdodF09XCIoY2xpZW50SGVpZ2h0IC0gKG93bmVyQ29yZHMudG9wICsgb3duZXJDb3Jkcy5oZWlnaHQpKSArICdweCdcIlxuICAgICAgICBbc3R5bGUud2lkdGhdPVwib3duZXJDb3Jkcy53aWR0aCArICdweCdcIlxuICAgICAgICAocG9pbnRlcmVudGVyKT1cInBvaW50ZXJJc09uVm9pZCA9IHRydWU7IGhhc0Jvb3RzdHJhcHBlZCAmJiBjbG9zZU9uVm9pZCgpXCJcbiAgICAgICAgKHBvaW50ZXJsZWF2ZSk9XCJwb2ludGVySXNPblZvaWQgPSBmYWxzZVwiXG4gICAgICAgIChwb2ludGVyZG93bik9XCJvblZvaWRQb2ludGVyRG93bigkZXZlbnQpXCJcbiAgICAgICAgKHdoZWVsKT1cIm9uV2hlZWwoJGV2ZW50KVwiXG4gICAgPjwvZGl2PlxufVxuXG5cbjxkaXZcbiAgICAjY29udGFpbmVyXG4gICAgY2xhc3M9XCJjb250YWluZXJcIlxuPlxuICAgIEBpZiAoaXNNZW51KSB7XG4gICAgICAgIDxuZ3gtbWVudVxuICAgICAgICAgICAgW2NvbmZpZ109XCJjb25maWdcIlxuICAgICAgICAgICAgW2RhdGFdPVwiZGF0YVwiXG4gICAgICAgICAgICBbb3duZXJDb3Jkc109XCJvd25lckNvcmRzXCJcbiAgICAgICAgICAgIFtzZWxmQ29yZHNdPVwic2VsZkNvcmRzXCJcbiAgICAgICAgICAgIFtpdGVtc109XCIkYW55KHRlbXBsYXRlKVwiXG4gICAgICAgICAgICBbaXNMb2NrZWRPcGVuXT1cImNvbmZpZy5zdGF5T3BlblwiXG4gICAgICAgIC8+XG4gICAgfVxuICAgIEBlbHNlIGlmIChpc1RlbXBsYXRlKSB7XG4gICAgICAgIDxuZy1jb250YWluZXJcbiAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIiRhbnkodGVtcGxhdGUpXCJcbiAgICAgICAgICAgIFtuZ1RlbXBsYXRlT3V0bGV0Q29udGV4dF09XCJ7XG4gICAgICAgICAgICAgICAgJyRpbXBsaWNpdCc6IGRhdGEsXG4gICAgICAgICAgICAgICAgJ2RpYWxvZyc6IGRpYWxvZ1JlZixcbiAgICAgICAgICAgICAgICAnZWxlbWVudCc6IGNvbnRhaW5lcixcbiAgICAgICAgICAgICAgICAndG9vbHRpcCc6IHRoaXNcbiAgICAgICAgICAgIH1cIlxuICAgICAgICA+PC9uZy1jb250YWluZXI+XG4gICAgfVxuICAgIEBlbHNlIHtcbiAgICAgICAgPG5nLWNvbnRhaW5lclxuICAgICAgICAgICAgW25nQ29tcG9uZW50T3V0bGV0XT1cIiRhbnkodGVtcGxhdGUpXCJcbiAgICAgICAgPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICB9XG48L2Rpdj5cbiJdfQ==