UNPKG

@dotglitch/ngx-common

Version:

Angular components and utilities that are commonly used.

257 lines 50.7 kB
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