@dotglitch/ngx-common
Version:
Angular components and utilities that are commonly used.
257 lines • 50.7 kB
JavaScript
import { ScrollingModule } from '@angular/cdk/scrolling';
import { Component, HostListener, Inject, Input, ViewChild, isDevMode } from '@angular/core';
import { MAT_DIALOG_DATA } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatInputModule } from '@angular/material/input';
import { NgScrollbar, NgScrollbarModule } from 'ngx-scrollbar';
import { BreadcrumbComponent } from './breadcrumb/breadcrumb.component';
import { ShortcutComponent } from './shortcut/shortcut.component';
import * as i0 from "@angular/core";
import * as i1 from "./command-palette.service";
import * as i2 from "@angular/material/dialog";
import * as i3 from "@angular/material/icon";
import * as i4 from "@angular/material/input";
import * as i5 from "@angular/material/form-field";
import * as i6 from "@angular/cdk/scrolling";
import * as i7 from "ngx-scrollbar";
export class CommandPaletteComponent {
get el() { return this.elementRef.nativeElement; }
constructor(commandPalette, dialog, elementRef, changeDetector, data) {
this.commandPalette = commandPalette;
this.dialog = dialog;
this.elementRef = elementRef;
this.changeDetector = changeDetector;
this.MAT_ICON_REGEX = /[:\/\.]/;
this.queryString = "";
this.activeIndex = 0;
this.rowHeight = 29;
this.padding = 6;
this.commands = [];
this.filteredCommands = [];
this.breadcrumbs = [];
this.contextElement = this.contextElement ?? data.contextElement;
}
ngOnInit() {
this.commands = this.commandPalette.getRegisteredCommands(this.contextElement);
this.setCommandList(this.commands);
// TODO: custom name
this.breadcrumbs.push({
action: { label: "/" },
commands: this.commands,
destroying: false,
selectedIndex: 0
});
}
onKeyDown(evt) {
switch (evt.key) {
case "Enter": {
// Fire the first command
if (this.filteredCommands.length > 0)
this.executeCommand(this.filteredCommands[this.activeIndex]);
evt.stopPropagation();
return;
}
case "ArrowUp": {
this.activeIndex = Math.max(this.activeIndex - 1, 0);
evt.stopPropagation();
this.focusRow();
return;
}
case "ArrowDown": {
this.activeIndex = Math.min(this.filteredCommands.length - 1, this.activeIndex + 1);
evt.stopPropagation();
this.focusRow();
return;
}
case "PageUp": {
// Fire the first command
this.activeIndex = Math.max(this.activeIndex - 12, 0);
evt.stopPropagation();
this.focusRow();
return;
}
case "PageDown": {
this.activeIndex = Math.min(this.filteredCommands.length - 1, this.activeIndex + 12);
evt.stopPropagation();
this.focusRow();
return;
}
case "Escape": {
this.dialog.close();
evt.stopPropagation();
return;
}
case "Backspace": {
// If we have no characters and we're hitting backspace, go back
// to the previous menu in the breadcrumb
if (this.queryString.length == 0 && this.breadcrumbs.length > 1) {
const layer = this.breadcrumbs.at(-1);
layer.destroying = true;
setTimeout(() => {
this.setCommandList(this.breadcrumbs.at(-2).commands);
this.breadcrumbs.pop();
this.activeIndex = layer.selectedIndex;
}, 190);
return;
}
else {
break;
}
}
case "Delete": {
}
}
this.activeIndex = 0;
this.commands.forEach(c => c['_renderedLabel'] = '');
// Check in the next tick to get the input's
// value so that it's updated
setTimeout(() => {
this.queryString = evt.target.value;
this.filterResults();
});
}
async filterResults() {
// Whitespace doesn't count.
if (this.queryString.trim().length == 0) {
this.filteredCommands = this.commands;
return;
}
const queryChars = this.queryString
.toLowerCase()
.split('');
const matchedCommands = [];
for (const command of this.commands) {
const { label } = command;
// Check the label
if (command.label) {
const commandChars = label
.toLowerCase()
.split('');
let renderedLabel = '';
let lastIndex = 0;
let isMatch = true;
for (const char of queryChars) {
const index = commandChars.indexOf(char, lastIndex);
if (index == -1) {
isMatch = false;
break;
}
else {
renderedLabel += label.slice(lastIndex, index) + `<b>${label.slice(index, index + 1)}</b>`;
lastIndex = index + 1;
}
}
renderedLabel += label.slice(lastIndex);
if (isMatch) {
command['_renderedLabel'] = renderedLabel;
matchedCommands.push(command);
}
}
// Check the hint
if (command.hint) {
const commandChars = label
.toLowerCase()
.split('');
let renderedHint = '';
let lastIndex = 0;
let isMatch = true;
for (const char of queryChars) {
const index = commandChars.indexOf(char, lastIndex);
if (index == -1) {
isMatch = false;
break;
}
else {
renderedHint += label.slice(lastIndex, index) + `<b>${label.slice(index, index + 1)}</b>`;
lastIndex = index + 1;
}
}
renderedHint += label.slice(lastIndex);
if (isMatch) {
command['_renderedHint'] = renderedHint;
matchedCommands.push(command);
}
}
}
this.filteredCommands = matchedCommands;
}
setCommandList(commands) {
this.commands = commands
.filter(c => c.visibleInList != false);
this.filteredCommands = this.commands;
this.queryString = '';
this.activeIndex = 0;
// Reset the filter labels
this.commands.forEach(command => command['_renderedLabel'] = '');
}
focusRow() {
const top = this.activeIndex * this.rowHeight;
const height = this.rowHeight;
const viewTop = this.scrollbar?.viewport?.scrollTop;
const viewHeight = this.scrollbar?.viewport?.clientHeight;
const viewBottom = viewTop + viewHeight;
if (top < viewTop) {
this.scrollbar.viewport.nativeElement.scrollTo({ top: top + this.padding });
}
else if ((top + height) > viewBottom) {
this.scrollbar.viewport.nativeElement.scrollTo({ top: ((top + this.rowHeight) - viewHeight) + this.padding });
}
// Immediately check for changes to update template
this.changeDetector.detectChanges();
}
executeCommand(command) {
// Open a sub menu of items
if (Array.isArray(command.subMenu)) {
this.breadcrumbs.push({
action: command,
commands: command.subMenu,
selectedIndex: this.activeIndex,
destroying: false
});
this.setCommandList(command.subMenu);
}
// Directly invoke the action and kill the dialog
else {
this.commandPalette.invokeAction(command);
this.dialog.close();
}
}
onBlur() {
if (!isDevMode()) {
this.dialog.close();
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CommandPaletteComponent, deps: [{ token: i1.CommandPaletteService }, { token: i2.MatDialogRef }, { token: i0.ElementRef }, { token: i0.ChangeDetectorRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: CommandPaletteComponent, isStandalone: true, selector: "ngx-command-palette", inputs: { contextElement: "contextElement" }, host: { listeners: { "click": "textInput.nativeElement.focus()", "window:blur": "onBlur()", "window:resize": "onBlur()" } }, viewQueries: [{ propertyName: "scrollbar", first: true, predicate: NgScrollbar, descendants: true }, { propertyName: "textInput", first: true, predicate: ["textinput"], descendants: true }], ngImport: i0, template: "<mat-form-field style=\"width: 100%; padding: 8px 8px 0 8px\">\n <ngx-commandpalette-breadcrumb\n [breadcrumbs]=\"breadcrumbs\"\n />\n <input\n #textinput\n matInput\n type=\"text\"\n [value]=\"queryString\"\n (keydown)=\"onKeyDown($event)\"\n >\n</mat-form-field>\n\n@if (filteredCommands.length == 0) {\n <div class=\"commands\">\n <div class=\"command selected\">\n <div class=\"label\">No matching results.</div>\n </div>\n </div>\n}\n@else {\n <div\n class=\"commands\"\n [style.flex]=\"(filteredCommands.length * rowHeight) + 'px'\"\n [class.shadow]=\"scrollbar?.viewport?.scrollTop > 2\"\n >\n <ng-scrollbar #scrollbar>\n <cdk-virtual-scroll-viewport [itemSize]=\"rowHeight\" scrollViewport [minBufferPx]=\"150\">\n <div\n *cdkVirtualFor=\"let command of filteredCommands; let index = index\"\n class=\"command\"\n [style.height]=\"rowHeight + 'px'\"\n [class.selected]=\"index==activeIndex\"\n [class.has-icon]=\"command.icon\"\n [attr.index]=\"index\"\n (click)=\"executeCommand(command)\"\n >\n @if (command.icon) {\n <div class=\"icon\">\n @if (!MAT_ICON_REGEX.test(command.icon)) {\n <mat-icon [fontIcon]=\"command.icon\"></mat-icon>\n }\n @else {\n <img [src]=\"command.icon\"/>\n }\n </div>\n }\n\n <div\n class=\"label\"\n [innerHTML]=\"command['_renderedLabel'] || command.label || command.description\"\n ></div>\n\n @if (command['_renderedHint'] || command.hint) {\n <div\n class=\"hint\"\n [innerHTML]=\"command['_renderedHint'] || command.hint\"\n ></div>\n }\n <div style=\"flex: 1\"></div>\n\n <div>\n @for (shortcut of (command.shortcutKey?.find ? command.shortcutKey : [command.shortcutKey]); track shortcut) {\n @if (shortcut) {\n <ngx-commandpalette-shortcut [shortcut]=\"shortcut\"/>\n }\n }\n </div>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-scrollbar>\n </div>\n}\n\n", styles: [":host{display:flex;flex-direction:column;width:860px;max-height:460px;border:1px solid #484848;border-radius:6px;background-color:#222;-webkit-user-select:none;user-select:none;overflow:hidden;--text-color: #ccc;--transition: .25s ease}.commands{max-height:100%;overflow:hidden;position:relative;padding:0 6px}.commands .command{display:flex;padding-left:16px;padding-right:32px;align-items:center;border-radius:3px;justify-content:space-between;color:var(--text-color);font-size:15.5px;background-color:#0000}.commands .command:hover{background-color:#2a2d2e}.commands .command.selected{background-color:#04395e}.commands .command.has-icon{padding-left:8px}.commands .command .label ::ng-deep b{color:#2196f3}.commands .command .icon{width:38px;height:100%;display:flex;align-items:center;justify-content:center}.commands .command img{max-height:100%;padding:4px}.commands .command .hint{margin-left:12px;opacity:.75}.commands.shadow:after{box-shadow:#000 0 6px 6px -6px inset}.commands:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:6px;box-shadow:#0000 0 6px 6px -6px inset;transition:box-shadow .3s ease}ng-scrollbar.ng-scrollbar{--scrollbar-padding: 0px;--scrollbar-size: 14px;--scrollbar-border-radius: 0;--scrollbar-thumb-color: #4440;--scrollbar-thumb-transition: height ease-out .15s, width ease-out .15s, background-color ease 1.2s;animation:fadeScrollbar 1.2s ease}ng-scrollbar.ng-scrollbar:hover{--scrollbar-thumb-color: #444f}@keyframes fadeScrollbar{0%{--scrollbar-thumb-color: #444f}to{--scrollbar-thumb-color: #4440}}cdk-virtual-scroll-viewport{height:100%;padding-bottom:6px}:host ::ng-deep .mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea) .mat-mdc-form-field-infix{padding:2px 0 4px}:host ::ng-deep .mat-mdc-form-field-infix{min-height:32px;display:flex}:host ::ng-deep .mat-mdc-text-field-wrapper{margin-bottom:8px;padding:0 12px}:host ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .light app-command-palette{border:1px solid #e5e5e5;background-color:#f8f8f8;--text-color: #262626}::ng-deep .light app-command-palette .commands .command:hover{background-color:#f2f2f2}::ng-deep .light app-command-palette .commands .command.selected{background-color:#e8e8e8}\n"], dependencies: [{ kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i3.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i4.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "ngmodule", type: ScrollingModule }, { kind: "directive", type: i6.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i6.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i6.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "ngmodule", type: NgScrollbarModule }, { kind: "component", type: i7.NgScrollbar, selector: "ng-scrollbar", inputs: ["disabled", "sensorDisabled", "pointerEventsDisabled", "viewportPropagateMouseMove", "autoHeightDisabled", "autoWidthDisabled", "viewClass", "trackClass", "thumbClass", "minThumbSize", "trackClickScrollDuration", "pointerEventsMethod", "track", "visibility", "appearance", "position", "sensorDebounce", "scrollAuditTime"], outputs: ["updated"], exportAs: ["ngScrollbar"] }, { kind: "directive", type: i7.ScrollViewport, selector: "[scrollViewport]" }, { kind: "component", type: ShortcutComponent, selector: "ngx-commandpalette-shortcut", inputs: ["shortcut"] }, { kind: "component", type: BreadcrumbComponent, selector: "ngx-commandpalette-breadcrumb", inputs: ["breadcrumbs"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: CommandPaletteComponent, decorators: [{
type: Component,
args: [{ selector: 'ngx-command-palette', host: {
"(click)": "textInput.nativeElement.focus()"
}, imports: [
MatIconModule,
MatInputModule,
ScrollingModule,
NgScrollbarModule,
ShortcutComponent,
BreadcrumbComponent
], standalone: true, template: "<mat-form-field style=\"width: 100%; padding: 8px 8px 0 8px\">\n <ngx-commandpalette-breadcrumb\n [breadcrumbs]=\"breadcrumbs\"\n />\n <input\n #textinput\n matInput\n type=\"text\"\n [value]=\"queryString\"\n (keydown)=\"onKeyDown($event)\"\n >\n</mat-form-field>\n\n@if (filteredCommands.length == 0) {\n <div class=\"commands\">\n <div class=\"command selected\">\n <div class=\"label\">No matching results.</div>\n </div>\n </div>\n}\n@else {\n <div\n class=\"commands\"\n [style.flex]=\"(filteredCommands.length * rowHeight) + 'px'\"\n [class.shadow]=\"scrollbar?.viewport?.scrollTop > 2\"\n >\n <ng-scrollbar #scrollbar>\n <cdk-virtual-scroll-viewport [itemSize]=\"rowHeight\" scrollViewport [minBufferPx]=\"150\">\n <div\n *cdkVirtualFor=\"let command of filteredCommands; let index = index\"\n class=\"command\"\n [style.height]=\"rowHeight + 'px'\"\n [class.selected]=\"index==activeIndex\"\n [class.has-icon]=\"command.icon\"\n [attr.index]=\"index\"\n (click)=\"executeCommand(command)\"\n >\n @if (command.icon) {\n <div class=\"icon\">\n @if (!MAT_ICON_REGEX.test(command.icon)) {\n <mat-icon [fontIcon]=\"command.icon\"></mat-icon>\n }\n @else {\n <img [src]=\"command.icon\"/>\n }\n </div>\n }\n\n <div\n class=\"label\"\n [innerHTML]=\"command['_renderedLabel'] || command.label || command.description\"\n ></div>\n\n @if (command['_renderedHint'] || command.hint) {\n <div\n class=\"hint\"\n [innerHTML]=\"command['_renderedHint'] || command.hint\"\n ></div>\n }\n <div style=\"flex: 1\"></div>\n\n <div>\n @for (shortcut of (command.shortcutKey?.find ? command.shortcutKey : [command.shortcutKey]); track shortcut) {\n @if (shortcut) {\n <ngx-commandpalette-shortcut [shortcut]=\"shortcut\"/>\n }\n }\n </div>\n </div>\n </cdk-virtual-scroll-viewport>\n </ng-scrollbar>\n </div>\n}\n\n", styles: [":host{display:flex;flex-direction:column;width:860px;max-height:460px;border:1px solid #484848;border-radius:6px;background-color:#222;-webkit-user-select:none;user-select:none;overflow:hidden;--text-color: #ccc;--transition: .25s ease}.commands{max-height:100%;overflow:hidden;position:relative;padding:0 6px}.commands .command{display:flex;padding-left:16px;padding-right:32px;align-items:center;border-radius:3px;justify-content:space-between;color:var(--text-color);font-size:15.5px;background-color:#0000}.commands .command:hover{background-color:#2a2d2e}.commands .command.selected{background-color:#04395e}.commands .command.has-icon{padding-left:8px}.commands .command .label ::ng-deep b{color:#2196f3}.commands .command .icon{width:38px;height:100%;display:flex;align-items:center;justify-content:center}.commands .command img{max-height:100%;padding:4px}.commands .command .hint{margin-left:12px;opacity:.75}.commands.shadow:after{box-shadow:#000 0 6px 6px -6px inset}.commands:after{content:\"\";position:absolute;top:0;left:0;width:100%;height:6px;box-shadow:#0000 0 6px 6px -6px inset;transition:box-shadow .3s ease}ng-scrollbar.ng-scrollbar{--scrollbar-padding: 0px;--scrollbar-size: 14px;--scrollbar-border-radius: 0;--scrollbar-thumb-color: #4440;--scrollbar-thumb-transition: height ease-out .15s, width ease-out .15s, background-color ease 1.2s;animation:fadeScrollbar 1.2s ease}ng-scrollbar.ng-scrollbar:hover{--scrollbar-thumb-color: #444f}@keyframes fadeScrollbar{0%{--scrollbar-thumb-color: #444f}to{--scrollbar-thumb-color: #4440}}cdk-virtual-scroll-viewport{height:100%;padding-bottom:6px}:host ::ng-deep .mdc-text-field--no-label:not(.mdc-text-field--outlined):not(.mdc-text-field--textarea) .mat-mdc-form-field-infix{padding:2px 0 4px}:host ::ng-deep .mat-mdc-form-field-infix{min-height:32px;display:flex}:host ::ng-deep .mat-mdc-text-field-wrapper{margin-bottom:8px;padding:0 12px}:host ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}::ng-deep .light app-command-palette{border:1px solid #e5e5e5;background-color:#f8f8f8;--text-color: #262626}::ng-deep .light app-command-palette .commands .command:hover{background-color:#f2f2f2}::ng-deep .light app-command-palette .commands .command.selected{background-color:#e8e8e8}\n"] }]
}], ctorParameters: () => [{ type: i1.CommandPaletteService }, { type: i2.MatDialogRef }, { type: i0.ElementRef }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
type: Inject,
args: [MAT_DIALOG_DATA]
}] }], propDecorators: { scrollbar: [{
type: ViewChild,
args: [NgScrollbar]
}], textInput: [{
type: ViewChild,
args: ['textinput']
}], contextElement: [{
type: Input
}], onBlur: [{
type: HostListener,
args: ["window:blur"]
}, {
type: HostListener,
args: ["window:resize"]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZC1wYWxldHRlLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3BhY2thZ2VzL2NvbW1vbi9jb21tYW5kLXBhbGV0dGUvY29tbWFuZC1wYWxldHRlLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uL3BhY2thZ2VzL2NvbW1vbi9jb21tYW5kLXBhbGV0dGUvY29tbWFuZC1wYWxldHRlLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxlQUFlLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQztBQUN6RCxPQUFPLEVBQXFCLFNBQVMsRUFBYyxZQUFZLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBVSxTQUFTLEVBQUUsU0FBUyxFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQ3BJLE9BQU8sRUFBRSxlQUFlLEVBQWdCLE1BQU0sMEJBQTBCLENBQUM7QUFDekUsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHdCQUF3QixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxjQUFjLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUN6RCxPQUFPLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sZUFBZSxDQUFDO0FBQy9ELE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLG1DQUFtQyxDQUFDO0FBRXhFLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLCtCQUErQixDQUFDOzs7Ozs7Ozs7QUFtQmxFLE1BQU0sT0FBTyx1QkFBdUI7SUFPaEMsSUFBSSxFQUFFLEtBQUssT0FBTyxJQUFJLENBQUMsVUFBVSxDQUFDLGFBQTRCLENBQUMsQ0FBQyxDQUFDO0lBbUJqRSxZQUNxQixjQUFxQyxFQUNyQyxNQUF5QixFQUN6QixVQUFzQixFQUN0QixjQUFpQyxFQUN6QixJQUFTO1FBSmpCLG1CQUFjLEdBQWQsY0FBYyxDQUF1QjtRQUNyQyxXQUFNLEdBQU4sTUFBTSxDQUFtQjtRQUN6QixlQUFVLEdBQVYsVUFBVSxDQUFZ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LENBQUMsQ0FBQztRQUN6QyxDQUFDO1FBQ0QsaURBQWlEO2FBQzVDLENBQUM7WUFDRixJQUFJLENBQUMsY0FBYyxDQUFDLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUMxQyxJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO1FBQ3hCLENBQUM7SUFDTCxDQUFDO0lBSUQsTUFBTTtRQUNGLElBQUksQ0FBQyxTQUFTLEVBQUUsRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLEVBQUUsQ0FBQztRQUN4QixDQUFDO0lBQ0wsQ0FBQzsrR0F2UVEsdUJBQXVCLDhJQStCcEIsZUFBZTttR0EvQmxCLHVCQUF1QixxU0FFckIsV0FBVyx5SUM3QjFCLDh1RkEwRUEsaXhFRHhEUSxhQUFhLG1MQUNiLGNBQWMscWpCQUNkLGVBQWUsMmpCQUNmLGlCQUFpQixpakJBQ2pCLGlCQUFpQiw4RkFDakIsbUJBQW1COzs0RkFJZCx1QkFBdUI7a0JBakJuQyxTQUFTOytCQUNJLHFCQUFxQixRQUd6Qjt3QkFDRixTQUFTLEVBQUUsaUNBQWlDO3FCQUMvQyxXQUNRO3dCQUNMLGFBQWE7d0JBQ2IsY0FBYzt3QkFDZCxlQUFlO3dCQUNmLGlCQUFpQjt3QkFDakIsaUJBQWlCO3dCQUNqQixtQkFBbUI7cUJBQ3RCLGNBQ1csSUFBSTs7MEJBaUNYLE1BQU07MkJBQUMsZUFBZTt5Q0E3QkgsU0FBUztzQkFBaEMsU0FBUzt1QkFBQyxXQUFXO2dCQUNFLFNBQVM7c0JBQWhDLFNBQVM7dUJBQUMsV0FBVztnQkFFYixjQUFjO3NCQUF0QixLQUFLO2dCQThQTixNQUFNO3NCQUZMLFlBQVk7dUJBQUMsYUFBYTs7c0JBQzFCLFlBQVk7dUJBQUMsZUFBZSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFNjcm9sbGluZ01vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL2Nkay9zY3JvbGxpbmcnO1xuaW1wb3J0IHsgQ2hhbmdlRGV0ZWN0b3JSZWYsIENvbXBvbmVudCwgRWxlbWVudFJlZiwgSG9zdExpc3RlbmVyLCBJbmplY3QsIElucHV0LCBPbkluaXQsIFZpZXdDaGlsZCwgaXNEZXZNb2RlIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBNQVRfRElBTE9HX0RBVEEsIE1hdERpYWxvZ1JlZiB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2RpYWxvZyc7XG5pbXBvcnQgeyBNYXRJY29uTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvbWF0ZXJpYWwvaWNvbic7XG5pbXBvcnQgeyBNYXRJbnB1dE1vZHVsZSB9IGZyb20gJ0Bhbmd1bGFyL21hdGVyaWFsL2lucHV0JztcbmltcG9ydCB7IE5nU2Nyb2xsYmFyLCBOZ1Njcm9sbGJhck1vZHVsZSB9IGZyb20gJ25neC1zY3JvbGxiYXInO1xuaW1wb3J0IHsgQnJlYWRjcnVtYkNvbXBvbmVudCB9IGZyb20gJy4vYnJlYWRjcnVtYi9icmVhZGNydW1iLmNvbXBvbmVudCc7XG5pbXBvcnQgeyBDb21tYW5kQWN0aW9uLCBDb21tYW5kUGFsZXR0ZVNlcnZpY2UgfSBmcm9tICcuL2NvbW1hbmQtcGFsZXR0ZS5zZXJ2aWNlJztcbmltcG9ydCB7IFNob3J0Y3V0Q29tcG9uZW50IH0gZnJvbSAnLi9zaG9ydGN1dC9zaG9ydGN1dC5jb21wb25lbnQnO1xuXG5AQ29tcG9uZW50KHtcbiAgICBzZWxlY3RvcjogJ25neC1jb21tYW5kLXBhbGV0dGUnLFxuICAgIHRlbXBsYXRlVXJsOiAnLi9jb21tYW5kLXBhbGV0dGUuY29tcG9uZW50Lmh0bWwnLFxuICAgIHN0eWxlVXJsczogWycuL2NvbW1hbmQtcGFsZXR0ZS5jb21wb25lbnQuc2NzcyddLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgXCIoY2xpY2spXCI6IFwidGV4dElucHV0Lm5hdGl2ZUVsZW1lbnQuZm9jdXMoKVwiXG4gICAgfSxcbiAgICBpbXBvcnRzOiBbXG4gICAgICAgIE1hdEljb25Nb2R1bGUsXG4gICAgICAgIE1hdElucHV0TW9kdWxlLFxuICAgICAgICBTY3JvbGxpbmdNb2R1bGUsXG4gICAgICAgIE5nU2Nyb2xsYmFyTW9kdWxlLFxuICAgICAgICBTaG9ydGN1dENvbXBvbmVudCxcbiAgICAgICAgQnJlYWRjcnVtYkNvbXBvbmVudFxuICAgIF0sXG4gICAgc3RhbmRhbG9uZTogdHJ1ZVxufSlcbmV4cG9ydCBjbGFzcyBDb21tYW5kUGFsZXR0ZUNvbXBvbmVudCBpbXBsZW1lbnRzIE9uSW5pdCB7XG5cbiAgICBAVmlld0NoaWxkKE5nU2Nyb2xsYmFyKSBzY3JvbGxiYXI6IE5nU2Nyb2xsYmFyO1xuICAgIEBWaWV3Q2hpbGQoJ3RleHRpbnB1dCcpIHRleHRJbnB1dDogRWxlbWVudFJlZjxIVE1MSW5wdXRFbGVtZW50PjtcblxuICAgIEBJbnB1dCgpIGNvbnRleHRFbGVtZW50OiBIVE1MRWxlbWVudDtcblxuICAgIGdldCBlbCgpIHsgcmV0dXJuIHRoaXMuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50OyB9XG5cbiAgICByZWFkb25seSBNQVRfSUNPTl9SRUdFWCA9IC9bOlxcL1xcLl0vO1xuXG4gICAgcXVlcnlTdHJpbmcgPSBcIlwiO1xuICAgIGFjdGl2ZUluZGV4ID0gMDtcbiAgICByZWFkb25seSByb3dIZWlnaHQgPSAyOTtcbiAgICByZWFkb25seSBwYWRkaW5nID0gNjtcblxuICAgIGNvbW1hbmRzOiBDb21tYW5kQWN0aW9uW10gPSBbXTtcbiAgICBmaWx0ZXJlZENvbW1hbmRzOiBDb21tYW5kQWN0aW9uW10gPSBbXTtcblxuICAgIGJyZWFkY3J1bWJzOiB7XG4gICAgICAgIGFjdGlvbjogQ29tbWFuZEFjdGlvbixcbiAgICAgICAgY29tbWFuZHM6IENvbW1hbmRBY3Rpb25bXSxcbiAgICAgICAgZGVzdHJveWluZzogYm9vbGVhbixcbiAgICAgICAgc2VsZWN0ZWRJbmRleDogbnVtYmVyO1xuICAgIH1bXSA9IFtdO1xuXG4gICAgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgY29tbWFuZFBhbGV0dGU6IENvbW1hbmRQYWxldHRlU2VydmljZSxcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBkaWFsb2c6IE1hdERpYWxvZ1JlZjxhbnk+LFxuICAgICAgICBwcml2YXRlIHJlYWRvbmx5IGVsZW1lbnRSZWY6IEVsZW1lbnRSZWYsXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgY2hhbmdlRGV0ZWN0b3I6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICBASW5qZWN0KE1BVF9ESUFMT0dfREFUQSkgZGF0YTogYW55XG4gICAgKSB7XG4gICAgICAgIHRoaXMuY29udGV4dEVsZW1lbnQgPSB0aGlzLmNvbnRleHRFbGVtZW50ID8/IGRhdGEuY29udGV4dEVsZW1lbnQ7XG4gICAgfVxuXG4gICAgbmdPbkluaXQoKSB7XG4gICAgICAgIHRoaXMuY29tbWFuZHMgPSB0aGlzLmNvbW1hbmRQYWxldHRlLmdldFJlZ2lzdGVyZWRDb21tYW5kcyh0aGlzLmNvbnRleHRFbGVtZW50KTtcbiAgICAgICAgdGhpcy5zZXRDb21tYW5kTGlzdCh0aGlzLmNvbW1hbmRzKTtcblxuICAgICAgICAvLyBUT0RPOiBjdXN0b20gbmFtZVxuICAgICAgICB0aGlzLmJyZWFkY3J1bWJzLnB1c2goe1xuICAgICAgICAgICAgYWN0aW9uOiB7IGxhYmVsOiBcIi9cIiB9LFxuICAgICAgICAgICAgY29tbWFuZHM6IHRoaXMuY29tbWFuZHMsXG4gICAgICAgICAgICBkZXN0cm95aW5nOiBmYWxzZSxcbiAgICAgICAgICAgIHNlbGVjdGVkSW5kZXg6IDBcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgb25LZXlEb3duKGV2dDogS2V5Ym9hcmRFdmVudCkge1xuICAgICAgICBzd2l0Y2ggKGV2dC5rZXkpIHtcbiAgICAgICAgICAgIGNhc2UgXCJFbnRlclwiOiB7XG4gICAgICAgICAgICAgICAgLy8gRmlyZSB0aGUgZmlyc3QgY29tbWFuZFxuICAgICAgICAgICAgICAgIGlmICh0aGlzLmZpbHRlcmVkQ29tbWFuZHMubGVuZ3RoID4gMClcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5leGVjdXRlQ29tbWFuZCh0aGlzLmZpbHRlcmVkQ29tbWFuZHNbdGhpcy5hY3RpdmVJbmRleF0pO1xuXG4gICAgICAgICAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGNhc2UgXCJBcnJvd1VwXCI6IHtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2ZUluZGV4ID0gTWF0aC5tYXgodGhpcy5hY3RpdmVJbmRleCAtIDEsIDApO1xuXG4gICAgICAgICAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHRoaXMuZm9jdXNSb3coKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlIFwiQXJyb3dEb3duXCI6IHtcbiAgICAgICAgICAgICAgICB0aGlzLmFjdGl2ZUluZGV4ID0gTWF0aC5taW4odGhpcy5maWx0ZXJlZENvbW1hbmRzLmxlbmd0aCAtIDEsIHRoaXMuYWN0aXZlSW5kZXggKyAxKTtcblxuICAgICAgICAgICAgICAgIGV2dC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZvY3VzUm93KCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2FzZSBcIlBhZ2VVcFwiOiB7XG4gICAgICAgICAgICAgICAgLy8gRmlyZSB0aGUgZmlyc3QgY29tbWFuZFxuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZlSW5kZXggPSBNYXRoLm1heCh0aGlzLmFjdGl2ZUluZGV4IC0gMTIsIDApO1xuXG4gICAgICAgICAgICAgICAgZXZ0LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgICAgICAgICAgICAgIHRoaXMuZm9jdXNSb3coKTtcbiAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlIFwiUGFnZURvd25cIjoge1xuICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZlSW5kZXggPSBNYXRoLm1pbih0aGlzLmZpbHRlcmVkQ29tbWFuZHMubGVuZ3RoIC0gMSwgdGhpcy5hY3RpdmVJbmRleCArIDEyKTtcblxuICAgICAgICAgICAgICAgIGV2dC5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICB0aGlzLmZvY3VzUm93KCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2FzZSBcIkVzY2FwZVwiOiB7XG4gICAgICAgICAgICAgICAgdGhpcy5kaWFsb2cuY2xvc2UoKTtcbiAgICAgICAgICAgICAgICBldnQuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2FzZSBcIkJhY2tzcGFjZVwiOiB7XG4gICAgICAgICAgICAgICAgLy8gSWYgd2UgaGF2ZSBubyBjaGFyYWN0ZXJzIGFuZCB3ZSdyZSBoaXR0aW5nIGJhY2tzcGFjZSwgZ28gYmFja1xuICAgICAgICAgICAgICAgIC8vIHRvIHRoZSBwcmV2aW91cyBtZW51IGluIHRoZSBicmVhZGNydW1iXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMucXVlcnlTdHJpbmcubGVuZ3RoID09IDAgJiYgdGhpcy5icmVhZGNydW1icy5sZW5ndGggPiAxKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGxheWVyID0gdGhpcy5icmVhZGNydW1icy5hdCgtMSk7XG4gICAgICAgICAgICAgICAgICAgIGxheWVyLmRlc3Ryb3lpbmcgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc2V0Q29tbWFuZExpc3QodGhpcy5icmVhZGNydW1icy5hdCgtMikuY29tbWFuZHMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5icmVhZGNydW1icy5wb3AoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuYWN0aXZlSW5kZXggPSBsYXllci5zZWxlY3RlZEluZGV4O1xuICAgICAgICAgICAgICAgICAgICB9LCAxOTApO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjYXNlIFwiRGVsZXRlXCI6IHtcblxuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5hY3RpdmVJbmRleCA9IDA7XG4gICAgICAgIHRoaXMuY29tbWFuZHMuZm9yRWFjaChjID0+IGNbJ19yZW5kZXJlZExhYmVsJ10gPSAnJyk7XG5cbiAgICAgICAgLy8gQ2hlY2sgaW4gdGhlIG5leHQgdGljayB0byBnZXQgdGhlIGlucHV0J3NcbiAgICAgICAgLy8gdmFsdWUgc28gdGhhdCBpdCdzIHVwZGF0ZWRcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgICAgICB0aGlzLnF1ZXJ5U3RyaW5nID0gKGV2dC50YXJnZXQgYXMgSFRNTElucHV0RWxlbWVudCkudmFsdWU7XG4gICAgICAgICAgICB0aGlzLmZpbHRlclJlc3VsdHMoKTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBhc3luYyBmaWx0ZXJSZXN1bHRzKCkge1xuICAgICAgICAvLyBXaGl0ZXNwYWNlIGRvZXNuJ3QgY291bnQuXG4gICAgICAgIGlmICh0aGlzLnF1ZXJ5U3RyaW5nLnRyaW0oKS5sZW5ndGggPT0gMCkge1xuICAgICAgICAgICAgdGhpcy5maWx0ZXJlZENvbW1hbmRzID0gdGhpcy5jb21tYW5kcztcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IHF1ZXJ5Q2hhcnMgPSB0aGlzLnF1ZXJ5U3RyaW5nXG4gICAgICAgICAgICAudG9Mb3dlckNhc2UoKVxuICAgICAgICAgICAgLnNwbGl0KCcnKTtcblxuICAgICAgICBjb25zdCBtYXRjaGVkQ29tbWFuZHM6IENvbW1hbmRBY3Rpb25bXSA9IFtdO1xuXG4gICAgICAgIGZvciAoY29uc3QgY29tbWFuZCBvZiB0aGlzLmNvbW1hbmRzKSB7XG4gICAgICAgICAgICBjb25zdCB7IGxhYmVsIH0gPSBjb21tYW5kO1xuXG4gICAgICAgICAgICAvLyBDaGVjayB0aGUgbGFiZWxcbiAgICAgICAgICAgIGlmIChjb21tYW5kLmxhYmVsKSB7XG4gICAgICAgICAgICAgICAgY29uc3QgY29tbWFuZENoYXJzID0gbGFiZWxcbiAgICAgICAgICAgICAgICAgICAgLnRvTG93ZXJDYXNlKClcbiAgICAgICAgICAgICAgICAgICAgLnNwbGl0KCcnKTtcblxuICAgICAgICAgICAgICAgIGxldCByZW5kZXJlZExhYmVsID0gJyc7XG4gICAgICAgICAgICAgICAgbGV0IGxhc3RJbmRleCA9IDA7XG4gICAgICAgICAgICAgICAgbGV0IGlzTWF0Y2ggPSB0cnVlO1xuXG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBjaGFyIG9mIHF1ZXJ5Q2hhcnMpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5kZXggPSBjb21tYW5kQ2hhcnMuaW5kZXhPZihjaGFyLCBsYXN0SW5kZXgpO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChpbmRleCA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaXNNYXRjaCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZW5kZXJlZExhYmVsICs9IGxhYmVsLnNsaWNlKGxhc3RJbmRleCwgaW5kZXgpICsgYDxiPiR7bGFiZWwuc2xpY2UoaW5kZXgsIGluZGV4ICsgMSl9PC9iPmA7XG4gICAgICAgICAgICAgICAgICAgICAgICBsYXN0SW5kZXggPSBpbmRleCArIDE7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICByZW5kZXJlZExhYmVsICs9IGxhYmVsLnNsaWNlKGxhc3RJbmRleCk7XG5cbiAgICAgICAgICAgICAgICBpZiAoaXNNYXRjaCkge1xuICAgICAgICAgICAgICAgICAgICBjb21tYW5kWydfcmVuZGVyZWRMYWJlbCddID0gcmVuZGVyZWRMYWJlbDtcbiAgICAgICAgICAgICAgICAgICAgbWF0Y2hlZENvbW1hbmRzLnB1c2goY29tbWFuZCk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICAvLyBDaGVjayB0aGUgaGludFxuICAgICAgICAgICAgaWYgKGNvbW1hbmQuaGludCkge1xuICAgICAgICAgICAgICAgIGNvbnN0IGNvbW1hbmRDaGFycyA9IGxhYmVsXG4gICAgICAgICAgICAgICAgICAgIC50b0xvd2VyQ2FzZSgpXG4gICAgICAgICAgICAgICAgICAgIC5zcGxpdCgnJyk7XG5cbiAgICAgICAgICAgICAgICBsZXQgcmVuZGVyZWRIaW50ID0gJyc7XG4gICAgICAgICAgICAgICAgbGV0IGxhc3RJbmRleCA9IDA7XG4gICAgICAgICAgICAgICAgbGV0IGlzTWF0Y2ggPSB0cnVlO1xuXG4gICAgICAgICAgICAgICAgZm9yIChjb25zdCBjaGFyIG9mIHF1ZXJ5Q2hhcnMpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgaW5kZXggPSBjb21tYW5kQ2hhcnMuaW5kZXhPZihjaGFyLCBsYXN0SW5kZXgpO1xuXG4gICAgICAgICAgICAgICAgICAgIGlmIChpbmRleCA9PSAtMSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgaXNNYXRjaCA9IGZhbHNlO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgICAgICByZW5kZXJlZEhpbnQgKz0gbGFiZWwuc2xpY2UobGFzdEluZGV4LCBpbmRleCkgKyBgPGI+JHtsYWJlbC5zbGljZShpbmRleCwgaW5kZXggKyAxKX08L2I+YDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxhc3RJbmRleCA9IGluZGV4ICsgMTtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgICAgIHJlbmRlcmVkSGludCArPSBsYWJlbC5zbGljZShsYXN0SW5kZXgpO1xuXG4gICAgICAgICAgICAgICAgaWYgKGlzTWF0Y2gpIHtcbiAgICAgICAgICAgICAgICAgICAgY29tbWFuZFsnX3JlbmRlcmVkSGludCddID0gcmVuZGVyZWRIaW50O1xuICAgICAgICAgICAgICAgICAgICBtYXRjaGVkQ29tbWFuZHMucHVzaChjb21tYW5kKTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLmZpbHRlcmVkQ29tbWFuZHMgPSBtYXRjaGVkQ29tbWFuZHM7XG4gICAgfVxuXG4gICAgcHVibGljIHNldENvbW1hbmRMaXN0KGNvbW1hbmRzOiBDb21tYW5kQWN0aW9uW10pIHtcbiAgICAgICAgdGhpcy5jb21tYW5kcyA9IGNvbW1hbmRzXG4gICAgICAgICAgICAuZmlsdGVyKGMgPT4gYy52aXNpYmxlSW5MaXN0ICE9IGZhbHNlKTtcbiAgICAgICAgdGhpcy5maWx0ZXJlZENvbW1hbmRzID0gdGhpcy5jb21tYW5kcztcbiAgICAgICAgdGhpcy5xdWVyeVN0cmluZyA9ICcnO1xuICAgICAgICB0aGlzLmFjdGl2ZUluZGV4ID0gMDtcblxuICAgICAgICAvLyBSZXNldCB0aGUgZmlsdGVyIGxhYmVsc1xuICAgICAgICB0aGlzLmNvbW1hbmRzLmZvckVhY2goY29tbWFuZCA9PiBjb21tYW5kWydfcmVuZGVyZWRMYWJlbCddID0gJycpO1xuICAgIH1cblxuICAgIHByaXZhdGUgZm9jdXNSb3coKSB7XG4gICAgICAgIGNvbnN0IHRvcCA9IHRoaXMuYWN0aXZlSW5kZXggKiB0aGlzLnJvd0hlaWdodDtcbiAgICAgICAgY29uc3QgaGVpZ2h0ID0gdGhpcy5yb3dIZWlnaHQ7XG5cbiAgICAgICAgY29uc3Qgdmlld1RvcCA9IHRoaXMuc2Nyb2xsYmFyPy52aWV3cG9ydD8uc2Nyb2xsVG9wO1xuICAgICAgICBjb25zdCB2aWV3SGVpZ2h0ID0gdGhpcy5zY3JvbGxiYXI/LnZpZXdwb3J0Py5jbGllbnRIZWlnaHQ7XG4gICAgICAgIGNvbnN0IHZpZXdCb3R0b20gPSB2aWV3VG9wICsgdmlld0hlaWdodDtcblxuICAgICAgICBpZiAodG9wIDwgdmlld1RvcCkge1xuICAgICAgICAgICAgdGhpcy5zY3JvbGxiYXIudmlld3BvcnQubmF0aXZlRWxlbWVudC5zY3JvbGxUbyh7IHRvcDogdG9wICsgdGhpcy5wYWRkaW5nIH0pO1xuICAgICAgICB9XG4gICAgICAgIGVsc2UgaWYgKCh0b3AgKyBoZWlnaHQpID4gdmlld0JvdHRvbSkge1xuICAgICAgICAgICAgdGhpcy5zY3JvbGxiYXIudmlld3BvcnQubmF0aXZlRWxlbWVudC5zY3JvbGxUbyh7IHRvcDogKCh0b3AgKyB0aGlzLnJvd0hlaWdodCkgLSB2aWV3SGVpZ2h0KSArIHRoaXMucGFkZGluZyB9KTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIEltbWVkaWF0ZWx5IGNoZWNrIGZvciBjaGFuZ2VzIHRvIHVwZGF0ZSB0ZW1wbGF0ZVxuICAgICAgICB0aGlzLmNoYW5nZURldGVjdG9yLmRldGVjdENoYW5nZXMoKTtcbiAgICB9XG5cbiAgICBleGVjdXRlQ29tbWFuZChjb21tYW5kOiBDb21tYW5kQWN0aW9uKSB7XG4gICAgICAgIC8vIE9wZW4gYSBzdWIgbWVudSBvZiBpdGVtc1xuICAgICAgICBpZiAoQXJyYXkuaXNBcnJheShjb21tYW5kLnN1Yk1lbnUpKSB7XG4gICAgICAgICAgICB0aGlzLmJyZWFkY3J1bWJzLnB1c2goe1xuICAgICAgICAgICAgICAgIGFjdGlvbjogY29tbWFuZCxcbiAgICAgICAgICAgICAgICBjb21tYW5kczogY29tbWFuZC5zdWJNZW51LFxuICAgICAgICAgICAgICAgIHNlbGVjdGVkSW5kZXg6IHRoaXMuYWN0aXZlSW5kZXgsXG4gICAgICAgICAgICAgICAgZGVzdHJveWluZzogZmFsc2VcbiAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICB0aGlzLnNldENvbW1hbmRMaXN0KGNvbW1hbmQuc3ViTWVudSk7XG4gICAgICAgIH1cbiAgICAgICAgLy8gRGlyZWN0bHkgaW52b2tlIHRoZSBhY3Rpb24gYW5kIGtpbGwgdGhlIGRpYWxvZ1xuICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMuY29tbWFuZFBhbGV0dGUuaW52b2tlQWN0aW9uKGNvbW1hbmQpO1xuICAgICAgICAgICAgdGhpcy5kaWFsb2cuY2xvc2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIEBIb3N0TGlzdGVuZXIoXCJ3aW5kb3c6Ymx1clwiKVxuICAgIEBIb3N0TGlzdGVuZXIoXCJ3aW5kb3c6cmVzaXplXCIpXG4gICAgb25CbHVyKCkge1xuICAgICAgICBpZiAoIWlzRGV2TW9kZSgpKSB7XG4gICAgICAgICAgICB0aGlzLmRpYWxvZy5jbG9zZSgpO1xuICAgICAgICB9XG4gICAgfVxufVxuIiwiPG1hdC1mb3JtLWZpZWxkIHN0eWxlPVwid2lkdGg6IDEwMCU7IHBhZGRpbmc6IDhweCA4cHggMCA4cHhcIj5cbiAgICA8bmd4LWNvbW1hbmRwYWxldHRlLWJyZWFkY3J1bWJcbiAgICAgICAgW2JyZWFkY3J1bWJzXT1cImJyZWFkY3J1bWJzXCJcbiAgICAvPlxuICAgIDxpbnB1dFxuICAgICAgICAjdGV4dGlucHV0XG4gICAgICAgIG1hdElucHV0XG4gICAgICAgIHR5cGU9XCJ0ZXh0XCJcbiAgICAgICAgW3ZhbHVlXT1cInF1ZXJ5U3RyaW5nXCJcbiAgICAgICAgKGtleWRvd24pPVwib25LZXlEb3duKCRldmVudClcIlxuICAgID5cbjwvbWF0LWZvcm0tZmllbGQ+XG5cbkBpZiAoZmlsdGVyZWRDb21tYW5kcy5sZW5ndGggPT0gMCkge1xuICAgIDxkaXYgY2xhc3M9XCJjb21tYW5kc1wiPlxuICAgICAgICA8ZGl2IGNsYXNzPVwiY29tbWFuZCBzZWxlY3RlZFwiPlxuICAgICAgICAgICAgPGRpdiBjbGFzcz1cImxhYmVsXCI+Tm8gbWF0Y2hpbmcgcmVzdWx0cy48L2Rpdj5cbiAgICAgICAgPC9kaXY+XG4gICAgPC9kaXY+XG59XG5AZWxzZSB7XG4gICAgPGRpdlxuICAgICAgICBjbGFzcz1cImNvbW1hbmRzXCJcbiAgICAgICAgW3N0eWxlLmZsZXhdPVwiKGZpbHRlcmVkQ29tbWFuZHMubGVuZ3RoICogcm93SGVpZ2h0KSArICdweCdcIlxuICAgICAgICBbY2xhc3Muc2hhZG93XT1cInNjcm9sbGJhcj8udmlld3BvcnQ/LnNjcm9sbFRvcCA+IDJcIlxuICAgID5cbiAgICAgICAgPG5nLXNjcm9sbGJhciAjc2Nyb2xsYmFyPlxuICAgICAgICAgICAgPGNkay12aXJ0dWFsLXNjcm9sbC12aWV3cG9ydCBbaXRlbVNpemVdPVwicm93SGVpZ2h0XCIgc2Nyb2xsVmlld3BvcnQgW21pbkJ1ZmZlclB4XT1cIjE1MFwiPlxuICAgICAgICAgICAgICAgIDxkaXZcbiAgICAgICAgICAgICAgICAgICAgKmNka1ZpcnR1YWxGb3I9XCJsZXQgY29tbWFuZCBvZiBmaWx0ZXJlZENvbW1hbmRzOyBsZXQgaW5kZXggPSBpbmRleFwiXG4gICAgICAgICAgICAgICAgICAgIGNsYXNzPVwiY29tbWFuZFwiXG4gICAgICAgICAgICAgICAgICAgIFtzdHlsZS5oZWlnaHRdPVwicm93SGVpZ2h0ICsgJ3B4J1wiXG4gICAgICAgICAgICAgICAgICAgIFtjbGFzcy5zZWxlY3RlZF09XCJpbmRleD09YWN0aXZlSW5kZXhcIlxuICAgICAgICAgICAgICAgICAgICBbY2xhc3MuaGFzLWljb25dPVwiY29tbWFuZC5pY29uXCJcbiAgICAgICAgICAgICAgICAgICAgW2F0dHIuaW5kZXhdPVwiaW5kZXhcIlxuICAgICAgICAgICAgICAgICAgICAoY2xpY2spPVwiZXhlY3V0ZUNvbW1hbmQoY29tbWFuZClcIlxuICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgQGlmIChjb21tYW5kLmljb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3M9XCJpY29uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQGlmICghTUFUX0lDT05fUkVHRVgudGVzdChjb21tYW5kLmljb24pKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxtYXQtaWNvbiBbZm9udEljb25dPVwiY29tbWFuZC5pY29uXCI+PC9tYXQtaWNvbj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgQGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8aW1nIFtzcmNdPVwiY29tbWFuZC5pY29uXCIvPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3M9XCJsYWJlbFwiXG4gICAgICAgICAgICAgICAgICAgICAgICBbaW5uZXJIVE1MXT1cImNvbW1hbmRbJ19yZW5kZXJlZExhYmVsJ10gfHwgY29tbWFuZC5sYWJlbCB8fCBjb21tYW5kLmRlc2NyaXB0aW9uXCJcbiAgICAgICAgICAgICAgICAgICAgPjwvZGl2PlxuXG4gICAgICAgICAgICAgICAgICAgIEBpZiAoY29tbWFuZFsnX3JlbmRlcmVkSGludCddIHx8IGNvbW1hbmQuaGludCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgPGRpdlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsYXNzPVwiaGludFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgW2lubmVySFRNTF09XCJjb21tYW5kWydfcmVuZGVyZWRIaW50J10gfHwgY29tbWFuZC5oaW50XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID48L2Rpdj5cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICA8ZGl2IHN0eWxl