UNPKG

ngx-slice-kit

Version:

[![npm version](https://badge.fury.io/js/ngx-slice-kit.svg)](https://badge.fury.io/js/ngx-slice-kit)

222 lines 41.5 kB
import { Component, EventEmitter, Inject, Input, Output, ViewChild, } from '@angular/core'; import { fromEvent } from 'rxjs'; import { take } from 'rxjs/operators'; import { DOCUMENT } from '@angular/common'; import * as i0 from "@angular/core"; import * as i1 from "./options.service"; import * as i2 from "@angular/common"; import * as i3 from "../buttons/icon/icon.component"; const DEFAULT_DROPDOWN_SIZE = 240; // I'm taking additional 16 pixels here, to prevent element sticking to bottom panel const DEFAULT_DROPDOWN_OFFSET = 256; export class DropdownComponent { constructor(document, elem, renderer, optionsService) { this.document = document; this.elem = elem; this.renderer = renderer; this.optionsService = optionsService; this.resultEvent = new EventEmitter(); this.rects = {}; } onOptionMouseEnter(o, index) { this.currentOption = o; this.highlightedIndex = index; } onOptionMouseLeave() { this.currentOption = undefined; this.highlightedIndex = undefined; } select(ev, option) { this.onResult(option); } onResult(res) { this.resultEvent.emit(res); this.resultEvent.complete(); this.sub?.unsubscribe(); } nextOption(direction) { if (!this.highlightedIndex && this.highlightedIndex !== 0) { const selected = this.optionsService.options.find(o => o.selected); if (!!selected) { this.highlightedIndex = this.optionsService.options.indexOf(selected); } else { this.highlightedIndex = direction === 'up' ? 0 : this.optionsService.options.length - 1; } } if (this.optionsService.options?.length === 1) { this.currentOption = this.optionsService.options[0]; this.highlightedIndex = 0; } else { const isFirstOption = this.highlightedIndex === 0; const optionsLength = this.optionsService.options.length; const isLastOption = this.highlightedIndex === optionsLength - 1; const index = direction === 'down' ? (isLastOption ? 0 : (this.highlightedIndex + 1)) : (isFirstOption ? (optionsLength - 1) : this.highlightedIndex - 1); this.currentOption = this.optionsService.options[index]; this.highlightedIndex = index; } setTimeout(() => this.autoScroll(direction)); } autoScroll(direction) { const dropdownPaddingTop = 8; /** * okay that padding is constant at the top and the bottom of the element, * so it should be used in calculations when we are counting top offset */ const { scrollHeight, scrollTop, offsetHeight } = this.dropdownElement.nativeElement; const currentItem = this.elem.nativeElement.querySelector(`.highlighted`); if (!currentItem) { return; } const itemHeight = currentItem.offsetHeight; switch (direction) { case 'up': if (currentItem.offsetTop - dropdownPaddingTop < scrollTop) { this.dropdownElement.nativeElement.scrollTop = scrollTop - offsetHeight; } if (currentItem.offsetTop + itemHeight > scrollTop + offsetHeight) { this.dropdownElement.nativeElement.scrollTop = scrollHeight - offsetHeight; } break; default: if (currentItem.offsetTop + itemHeight > offsetHeight + scrollTop) { this.dropdownElement.nativeElement.scrollTop = scrollTop + itemHeight; } else if (currentItem.offsetTop === dropdownPaddingTop) { this.dropdownElement.nativeElement.scrollTop = 0; } } } /** * click outside subscription if backdrop disabled. also dropdown must be attached during input */ initClickOutsideSub() { this.renderer.listen('window', 'click', event => { const isParent = this.config.parentElem?.contains(event.target); const isDropdown = this.elem.nativeElement.contains(event.target); if (!isParent && !isDropdown) { this.onResult(); } }); } /** * detect relative position to prevent dropdown being hidden over `overflow: none` */ getDropdownRects() { const windowHeight = window.innerHeight; const pixelsLeft = windowHeight - this.config.triggerRect.height - this.config.triggerRect.top; const rects = this.dropdownElement.nativeElement.getBoundingClientRect(); this.inverted = pixelsLeft <= DEFAULT_DROPDOWN_OFFSET; if (this.inverted) { this.rects.bottom = windowHeight - this.config.triggerRect.top; } else { this.rects.top = this.config.triggerRect.bottom; } if (this.config.fitWidth) { this.rects.width = this.config.triggerRect.width; } if (rects.width > this.config.triggerRect.width) { const rightOffset = window.innerWidth - this.config.triggerRect.width - this.config.triggerRect.left; if (rightOffset <= rects.width + 16) { this.rects.left = (this.config.triggerRect.left + this.config.triggerRect.width) - rects.width; } } } /** * - if `fitWidth` config options is true * there are width declared depending on its parent element * - if `rects.top` calculated there is enough place to drop it down, * if it hits `rects.bottom` – show it above the element */ setDropdownPosition() { if (this.rects.width) { this.renderer.setStyle(this.dropdownElement.nativeElement, `width`, `${this.rects.width}px`); } if (this.rects.top) { this.renderer.setStyle(this.dropdownElement.nativeElement, `top`, `${this.rects.top}px`); } else { this.renderer.setStyle(this.dropdownElement.nativeElement, `bottom`, `${this.rects.bottom}px`); } this.renderer.setStyle(this.dropdownElement.nativeElement, `left`, `${this.rects.left || this.config.triggerRect.left}px`); this.renderer.setStyle(this.dropdownElement.nativeElement, `opacity`, 1); } /** * detect window resize and scroll to prevent failed dropdown position */ initClosingSubscriptions() { this.sub = fromEvent(window, 'scroll').pipe(take(1)).subscribe(() => { this.onResult(); }); this.sub.add(fromEvent(window, 'resize').pipe(take(1)).subscribe(() => { this.onResult(); })); } /** * keyboard events */ initKeydownSubscription() { this.sub.add(fromEvent(this.document, 'keydown').subscribe((e) => { switch (e.key || e.code) { case 'ArrowDown': e.preventDefault(); e.stopPropagation(); this.nextOption('down'); break; case 'ArrowUp': e.preventDefault(); e.stopPropagation(); this.nextOption('up'); break; case 'Enter': e.preventDefault(); e.stopPropagation(); if (this.currentOption) { this.onResult(this.currentOption); } break; case 'Escape': e.preventDefault(); e.stopPropagation(); this.onResult(); break; } })); } ngOnInit() { this.initClosingSubscriptions(); this.initKeydownSubscription(); if (this.config && this.config.hideBackdrop) { this.initClickOutsideSub(); } } ngAfterViewInit() { this.getDropdownRects(); this.setDropdownPosition(); } ngOnDestroy() { this.sub?.unsubscribe(); this.currentOption = undefined; this.highlightedIndex = undefined; } } DropdownComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: DropdownComponent, deps: [{ token: DOCUMENT }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i1.OptionsService }], target: i0.ɵɵFactoryTarget.Component }); DropdownComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.3", type: DropdownComponent, selector: "sdk-dropdown-menu", inputs: { config: "config" }, outputs: { resultEvent: "resultEvent" }, viewQueries: [{ propertyName: "dropdownElement", first: true, predicate: ["dropdown"], descendants: true }], ngImport: i0, template: "<div class=\"sdk-dropdown-wrap\" [class.no-backdrop]=\"config?.hideBackdrop\">\n <div class=\"sdk-dropdown-backdrop\" *ngIf=\"!config?.hideBackdrop\" (click)=\"onResult()\"></div>\n <ul class=\"sdk-dropdown\" #dropdown [class.multi]=\"config?.multi\">\n <ng-container *ngIf=\"!optionsService.hasOptions\">\n <li class=\"sdk-dropdown-item\">\n <a>No options.</a>\n </li>\n </ng-container>\n <ng-container *ngIf=\"optionsService.hasOptions\">\n <ng-container *ngFor=\"let o of optionsService.optionsObservable | async; index as i\">\n <li class=\"sdk-dropdown-item\" (click)=\"select($event, o)\"\n [ngClass]=\"{\n highlighted: o.value === currentOption?.value,\n disabled: o.disabled,\n active: o.selected && !config?.multi\n }\"\n (mouseenter)=\"onOptionMouseEnter(o, i)\"\n (mouseleave)=\"onOptionMouseLeave()\">\n <span class=\"sdk-dropdown-item-checked\" *ngIf=\"config?.multi\">\n <ng-container *ngIf=\"o.selected\">\n <sdk-icon icon=\"check\" size=\"16\"></sdk-icon>\n </ng-container>\n </span>\n <ng-container *ngIf=\"o.image\">\n <img alt=\"{{o.label}}\" class=\"sdk-dropdown-item-image\" [src]=\"o.image\"/>\n </ng-container>\n <a>{{o.label}}</a>\n </li>\n </ng-container>\n </ng-container>\n </ul>\n</div>\n\n\n", styles: [".sdk-dropdown-wrap{position:fixed;top:0;left:0;bottom:0;width:100vw;height:100vh;z-index:200;cursor:default}.sdk-dropdown-wrap.no-backdrop{width:auto;height:auto}.sdk-dropdown-backdrop{position:absolute;top:0;left:0;bottom:0;right:0;z-index:1}.sdk-dropdown{position:absolute;max-height:240px;min-width:64px;padding:8px 0;z-index:3;animation:sdk-dropdown .2s;transition:transform .2s,opacity .2s;background-color:var(--regular-a20);box-shadow:0 1px #1515150a,0 1px 2px #15151514;opacity:0;border-radius:4px;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scroll-behavior:smooth}.sdk-dropdown.multi .sdk-dropdown-item{padding:8px 16px 8px 4px}.sdk-dropdown .sdk-dropdown-item{position:relative;display:flex;align-items:center;align-content:center;flex-direction:row;flex-wrap:nowrap;white-space:nowrap;width:100%;padding:8px 16px;transition:all .2s ease;cursor:pointer}.sdk-dropdown .sdk-dropdown-item.highlighted{background-color:var(--regular-a50)}.sdk-dropdown .sdk-dropdown-item.active{background-color:var(--regular-a80)}.sdk-dropdown .sdk-dropdown-item.disabled{cursor:default;pointer-events:none;opacity:.5}.sdk-dropdown .sdk-dropdown-item a{font-size:17px;line-height:24px}.sdk-dropdown .sdk-dropdown-item .sdk-dropdown-item-checked{display:flex;height:16px;width:16px;margin:0 4px}.sdk-dropdown .sdk-dropdown-item .sdk-dropdown-item-image{height:24px;width:24px;border-radius:100%}@keyframes sdk-dropdown{0%{opacity:0;transform:scale(.9)}}\n"], dependencies: [{ kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.IconComponent, selector: "sdk-icon", inputs: ["icon", "image", "inline", "size", "color"] }, { kind: "pipe", type: i2.AsyncPipe, name: "async" }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.3", ngImport: i0, type: DropdownComponent, decorators: [{ type: Component, args: [{ selector: 'sdk-dropdown-menu', template: "<div class=\"sdk-dropdown-wrap\" [class.no-backdrop]=\"config?.hideBackdrop\">\n <div class=\"sdk-dropdown-backdrop\" *ngIf=\"!config?.hideBackdrop\" (click)=\"onResult()\"></div>\n <ul class=\"sdk-dropdown\" #dropdown [class.multi]=\"config?.multi\">\n <ng-container *ngIf=\"!optionsService.hasOptions\">\n <li class=\"sdk-dropdown-item\">\n <a>No options.</a>\n </li>\n </ng-container>\n <ng-container *ngIf=\"optionsService.hasOptions\">\n <ng-container *ngFor=\"let o of optionsService.optionsObservable | async; index as i\">\n <li class=\"sdk-dropdown-item\" (click)=\"select($event, o)\"\n [ngClass]=\"{\n highlighted: o.value === currentOption?.value,\n disabled: o.disabled,\n active: o.selected && !config?.multi\n }\"\n (mouseenter)=\"onOptionMouseEnter(o, i)\"\n (mouseleave)=\"onOptionMouseLeave()\">\n <span class=\"sdk-dropdown-item-checked\" *ngIf=\"config?.multi\">\n <ng-container *ngIf=\"o.selected\">\n <sdk-icon icon=\"check\" size=\"16\"></sdk-icon>\n </ng-container>\n </span>\n <ng-container *ngIf=\"o.image\">\n <img alt=\"{{o.label}}\" class=\"sdk-dropdown-item-image\" [src]=\"o.image\"/>\n </ng-container>\n <a>{{o.label}}</a>\n </li>\n </ng-container>\n </ng-container>\n </ul>\n</div>\n\n\n", styles: [".sdk-dropdown-wrap{position:fixed;top:0;left:0;bottom:0;width:100vw;height:100vh;z-index:200;cursor:default}.sdk-dropdown-wrap.no-backdrop{width:auto;height:auto}.sdk-dropdown-backdrop{position:absolute;top:0;left:0;bottom:0;right:0;z-index:1}.sdk-dropdown{position:absolute;max-height:240px;min-width:64px;padding:8px 0;z-index:3;animation:sdk-dropdown .2s;transition:transform .2s,opacity .2s;background-color:var(--regular-a20);box-shadow:0 1px #1515150a,0 1px 2px #15151514;opacity:0;border-radius:4px;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar;scroll-behavior:smooth}.sdk-dropdown.multi .sdk-dropdown-item{padding:8px 16px 8px 4px}.sdk-dropdown .sdk-dropdown-item{position:relative;display:flex;align-items:center;align-content:center;flex-direction:row;flex-wrap:nowrap;white-space:nowrap;width:100%;padding:8px 16px;transition:all .2s ease;cursor:pointer}.sdk-dropdown .sdk-dropdown-item.highlighted{background-color:var(--regular-a50)}.sdk-dropdown .sdk-dropdown-item.active{background-color:var(--regular-a80)}.sdk-dropdown .sdk-dropdown-item.disabled{cursor:default;pointer-events:none;opacity:.5}.sdk-dropdown .sdk-dropdown-item a{font-size:17px;line-height:24px}.sdk-dropdown .sdk-dropdown-item .sdk-dropdown-item-checked{display:flex;height:16px;width:16px;margin:0 4px}.sdk-dropdown .sdk-dropdown-item .sdk-dropdown-item-image{height:24px;width:24px;border-radius:100%}@keyframes sdk-dropdown{0%{opacity:0;transform:scale(.9)}}\n"] }] }], ctorParameters: function () { return [{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: i0.ElementRef }, { type: i0.Renderer2 }, { type: i1.OptionsService }]; }, propDecorators: { dropdownElement: [{ type: ViewChild, args: ['dropdown', { static: false }] }], resultEvent: [{ type: Output }], config: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcGRvd24uY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vbGlicy9uZ3gtc2xpY2Uta2l0L3NyYy9saWIvZHJvcGRvd25zL2Ryb3Bkb3duLmNvbXBvbmVudC50cyIsIi4uLy4uLy4uLy4uLy4uL2xpYnMvbmd4LXNsaWNlLWtpdC9zcmMvbGliL2Ryb3Bkb3ducy9kcm9wZG93bi5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBRUgsU0FBUyxFQUVULFlBQVksRUFDWixNQUFNLEVBQ04sS0FBSyxFQUdMLE1BQU0sRUFFTixTQUFTLEdBQ1osTUFBTSxlQUFlLENBQUM7QUFHdkIsT0FBTyxFQUFFLFNBQVMsRUFBZ0IsTUFBTSxNQUFNLENBQUM7QUFDL0MsT0FBTyxFQUFFLElBQUksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RDLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQzs7Ozs7QUFHM0MsTUFBTSxxQkFBcUIsR0FBRyxHQUFHLENBQUM7QUFDbEMsb0ZBQW9GO0FBQ3BGLE1BQU0sdUJBQXVCLEdBQUcsR0FBRyxDQUFDO0FBT3BDLE1BQU0sT0FBTyxpQkFBaUI7SUFZMUIsWUFDOEIsUUFBYSxFQUMvQixJQUFnQixFQUNoQixRQUFtQixFQUNwQixjQUE4QjtRQUhYLGFBQVEsR0FBUixRQUFRLENBQUs7UUFDL0IsU0FBSSxHQUFKLElBQUksQ0FBWTtRQUNoQixhQUFRLEdBQVIsUUFBUSxDQUFXO1FBQ3BCLG1CQUFjLEdBQWQsY0FBYyxDQUFnQjtRQWJ4QixnQkFBVyxHQUFzQixJQUFJLFlBQVksRUFBTyxDQUFDO1FBTW5FLFVBQUssR0FBcUMsRUFBRSxDQUFDO0lBU3BELENBQUM7SUFFTSxrQkFBa0IsQ0FBQyxDQUFjLEVBQUUsS0FBYTtRQUNuRCxJQUFJLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQztRQUN2QixJQUFJLENBQUMsZ0JBQWdCLEdB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QWJYLGlCQUFpQiw2T0M3QjlCLG9wREFrQ0E7MkZETGEsaUJBQWlCO2tCQUw3QixTQUFTOytCQUNJLG1CQUFtQjs7MEJBaUJ4QixNQUFNOzJCQUFDLFFBQVE7MEhBWDJCLGVBQWU7c0JBQTdELFNBQVM7dUJBQUMsVUFBVSxFQUFFLEVBQUMsTUFBTSxFQUFFLEtBQUssRUFBQztnQkFDckIsV0FBVztzQkFBM0IsTUFBTTtnQkFDUyxNQUFNO3NCQUFyQixLQUFLIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHtcbiAgICBBZnRlclZpZXdJbml0LFxuICAgIENvbXBvbmVudCxcbiAgICBFbGVtZW50UmVmLFxuICAgIEV2ZW50RW1pdHRlcixcbiAgICBJbmplY3QsXG4gICAgSW5wdXQsXG4gICAgT25EZXN0cm95LFxuICAgIE9uSW5pdCxcbiAgICBPdXRwdXQsXG4gICAgUmVuZGVyZXIyLFxuICAgIFZpZXdDaGlsZCxcbn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBPcHRpb25Nb2RlbCB9IGZyb20gJy4vZHJvcGRvd24tb3B0aW9uLm1vZGVsJztcbmltcG9ydCB7IERyb3Bkb3duT3B0aW9ucyB9IGZyb20gJy4vZHJvcGRvd24ubW9kZWwnO1xuaW1wb3J0IHsgZnJvbUV2ZW50LCBTdWJzY3JpcHRpb24gfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IHRha2UgfSBmcm9tICdyeGpzL29wZXJhdG9ycyc7XG5pbXBvcnQgeyBET0NVTUVOVCB9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbic7XG5pbXBvcnQgeyBPcHRpb25zU2VydmljZSB9IGZyb20gJy4vb3B0aW9ucy5zZXJ2aWNlJztcblxuY29uc3QgREVGQVVMVF9EUk9QRE9XTl9TSVpFID0gMjQwO1xuLy8gSSdtIHRha2luZyBhZGRpdGlvbmFsIDE2IHBpeGVscyBoZXJlLCB0byBwcmV2ZW50IGVsZW1lbnQgc3RpY2tpbmcgdG8gYm90dG9tIHBhbmVsXG5jb25zdCBERUZBVUxUX0RST1BET1dOX09GRlNFVCA9IDI1NjtcblxuQENvbXBvbmVudCh7XG4gICAgc2VsZWN0b3I6ICdzZGstZHJvcGRvd24tbWVudScsXG4gICAgdGVtcGxhdGVVcmw6ICcuL2Ryb3Bkb3duLmNvbXBvbmVudC5odG1sJyxcbiAgICBzdHlsZVVybHM6IFsnLi9kcm9wZG93bi5jb21wb25lbnQuc2NzcyddXG59KVxuZXhwb3J0IGNsYXNzIERyb3Bkb3duQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3kge1xuXG4gICAgQFZpZXdDaGlsZCgnZHJvcGRvd24nLCB7c3RhdGljOiBmYWxzZX0pIHB1YmxpYyBkcm9wZG93bkVsZW1lbnQ6IEVsZW1lbnRSZWY7XG4gICAgQE91dHB1dCgpIHB1YmxpYyByZXN1bHRFdmVudDogRXZlbnRFbWl0dGVyPGFueT4gPSBuZXcgRXZlbnRFbWl0dGVyPGFueT4oKTtcbiAgICBASW5wdXQoKSBwdWJsaWMgY29uZmlnOiBEcm9wZG93bk9wdGlvbnM7XG5cbiAgICBwdWJsaWMgc3ViOiBTdWJzY3JpcHRpb247XG4gICAgcHVibGljIGN1cnJlbnRPcHRpb246IE9wdGlvbk1vZGVsO1xuICAgIHB1YmxpYyBpbnZlcnRlZDogYm9vbGVhbjtcbiAgICBwdWJsaWMgcmVjdHM6IHsgYm90dG9tPywgdG9wPywgbGVmdD8sIHdpZHRoPyB9ID0ge307XG4gICAgcHVibGljIGhpZ2hsaWdodGVkSW5kZXg6IG51bWJlcjtcblxuICAgIGNvbnN0cnVjdG9yKFxuICAgICAgICBASW5qZWN0KERPQ1VNRU5UKSBwcml2YXRlIGRvY3VtZW50OiBhbnksXG4gICAgICAgIHByaXZhdGUgZWxlbTogRWxlbWVudFJlZixcbiAgICAgICAgcHJpdmF0ZSByZW5kZXJlcjogUmVuZGVyZXIyLFxuICAgICAgICBwdWJsaWMgb3B0aW9uc1NlcnZpY2U6IE9wdGlvbnNTZXJ2aWNlXG4gICAgKSB7XG4gICAgfVxuXG4gICAgcHVibGljIG9uT3B0aW9uTW91c2VFbnRlcihvOiBPcHRpb25Nb2RlbCwgaW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgICAgICB0aGlzLmN1cnJlbnRPcHRpb24gPSBvO1xuICAgICAgICB0aGlzLmhpZ2hsaWdodGVkSW5kZXggPSBpbmRleDtcbiAgICB9XG5cbiAgICBwdWJsaWMgb25PcHRpb25Nb3VzZUxlYXZlKCk6IHZvaWQge1xuICAgICAgICB0aGlzLmN1cnJlbnRPcHRpb24gPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuaGlnaGxpZ2h0ZWRJbmRleCA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbiAgICBwdWJsaWMgc2VsZWN0KGV2OiBhbnksIG9wdGlvbjogT3B0aW9uTW9kZWwpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5vblJlc3VsdChvcHRpb24pO1xuICAgIH1cblxuICAgIHB1YmxpYyBvblJlc3VsdChyZXM/OiBPcHRpb25Nb2RlbCk6IHZvaWQge1xuICAgICAgICB0aGlzLnJlc3VsdEV2ZW50LmVtaXQocmVzKTtcbiAgICAgICAgdGhpcy5yZXN1bHRFdmVudC5jb21wbGV0ZSgpO1xuICAgICAgICB0aGlzLnN1Yj8udW5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmV4dE9wdGlvbihkaXJlY3Rpb246ICd1cCcgfCAnZG93bicpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmhpZ2hsaWdodGVkSW5kZXggJiYgdGhpcy5oaWdobGlnaHRlZEluZGV4ICE9PSAwKSB7XG4gICAgICAgICAgICBjb25zdCBzZWxlY3RlZCA9IHRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5maW5kKG8gPT4gby5zZWxlY3RlZCk7XG4gICAgICAgICAgICBpZiAoISFzZWxlY3RlZCkge1xuICAgICAgICAgICAgICAgIHRoaXMuaGlnaGxpZ2h0ZWRJbmRleCA9IHRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5pbmRleE9mKHNlbGVjdGVkKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgdGhpcy5oaWdobGlnaHRlZEluZGV4ID0gZGlyZWN0aW9uID09PSAndXAnID8gMCA6IHRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucy5sZW5ndGggLSAxO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMub3B0aW9uc1NlcnZpY2Uub3B0aW9ucz8ubGVuZ3RoID09PSAxKSB7XG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRPcHRpb24gPSB0aGlzLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnNbMF07XG4gICAgICAgICAgICB0aGlzLmhpZ2hsaWdodGVkSW5kZXggPSAwO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgY29uc3QgaXNGaXJzdE9wdGlvbiA9IHRoaXMuaGlnaGxpZ2h0ZWRJbmRleCA9PT0gMDtcbiAgICAgICAgICAgIGNvbnN0IG9wdGlvbnNMZW5ndGggPSB0aGlzLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnMubGVuZ3RoO1xuICAgICAgICAgICAgY29uc3QgaXNMYXN0T3B0aW9uID0gdGhpcy5oaWdobGlnaHRlZEluZGV4ID09PSBvcHRpb25zTGVuZ3RoIC0gMTtcbiAgICAgICAgICAgIGNvbnN0IGluZGV4ID0gZGlyZWN0aW9uID09PSAnZG93bicgPyAoaXNMYXN0T3B0aW9uID8gMCA6ICh0aGlzLmhpZ2hsaWdodGVkSW5kZXggKyAxKSkgOlxuICAgICAgICAgICAgICAgIChpc0ZpcnN0T3B0aW9uID8gKG9wdGlvbnNMZW5ndGggLSAxKSA6IHRoaXMuaGlnaGxpZ2h0ZWRJbmRleCAtIDEpO1xuXG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRPcHRpb24gPSB0aGlzLm9wdGlvbnNTZXJ2aWNlLm9wdGlvbnNbaW5kZXhdO1xuICAgICAgICAgICAgdGhpcy5oaWdobGlnaHRlZEluZGV4ID0gaW5kZXg7XG4gICAgICAgIH1cblxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMuYXV0b1Njcm9sbChkaXJlY3Rpb24pKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYXV0b1Njcm9sbChkaXJlY3Rpb246ICd1cCcgfCAnZG93bicpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgZHJvcGRvd25QYWRkaW5nVG9wID0gODtcbiAgICAgICAgLyoqXG4gICAgICAgICAqIG9rYXkgdGhhdCBwYWRkaW5nIGlzIGNvbnN0YW50IGF0IHRoZSB0b3AgYW5kIHRoZSBib3R0b20gb2YgdGhlIGVsZW1lbnQsXG4gICAgICAgICAqIHNvIGl0IHNob3VsZCBiZSB1c2VkIGluIGNhbGN1bGF0aW9ucyB3aGVuIHdlIGFyZSBjb3VudGluZyB0b3Agb2Zmc2V0XG4gICAgICAgICAqL1xuICAgICAgICBjb25zdCB7c2Nyb2xsSGVpZ2h0LCBzY3JvbGxUb3AsIG9mZnNldEhlaWdodH0gPSB0aGlzLmRyb3Bkb3duRWxlbWVudC5uYXRpdmVFbGVtZW50O1xuICAgICAgICBjb25zdCBjdXJyZW50SXRlbSA9IHRoaXMuZWxlbS5uYXRpdmVFbGVtZW50LnF1ZXJ5U2VsZWN0b3IoYC5oaWdobGlnaHRlZGApO1xuICAgICAgICBpZiAoIWN1cnJlbnRJdGVtKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cbiAgICAgICAgY29uc3QgaXRlbUhlaWdodCA9IGN1cnJlbnRJdGVtLm9mZnNldEhlaWdodDtcblxuICAgICAgICBzd2l0Y2ggKGRpcmVjdGlvbikge1xuICAgICAgICAgICAgY2FzZSAndXAnOlxuICAgICAgICAgICAgICAgIGlmIChjdXJyZW50SXRlbS5vZmZzZXRUb3AgLSBkcm9wZG93blBhZGRpbmdUb3AgPCBzY3JvbGxUb3ApIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kcm9wZG93bkVsZW1lbnQubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgPSBzY3JvbGxUb3AgLSBvZmZzZXRIZWlnaHQ7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChjdXJyZW50SXRlbS5vZmZzZXRUb3AgKyBpdGVtSGVpZ2h0ID4gc2Nyb2xsVG9wICsgb2Zmc2V0SGVpZ2h0KSB7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuZHJvcGRvd25FbGVtZW50Lm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gc2Nyb2xsSGVpZ2h0IC0gb2Zmc2V0SGVpZ2h0O1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgIGRlZmF1bHQ6XG4gICAgICAgICAgICAgICAgaWYgKGN1cnJlbnRJdGVtLm9mZnNldFRvcCArIGl0ZW1IZWlnaHQgPiBvZmZzZXRIZWlnaHQgKyBzY3JvbGxUb3ApIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kcm9wZG93bkVsZW1lbnQubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgPSBzY3JvbGxUb3AgKyBpdGVtSGVpZ2h0O1xuICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoY3VycmVudEl0ZW0ub2Zmc2V0VG9wID09PSBkcm9wZG93blBhZGRpbmdUb3ApIHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5kcm9wZG93bkVsZW1lbnQubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgPSAwO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIGNsaWNrIG91dHNpZGUgc3Vic2NyaXB0aW9uIGlmIGJhY2tkcm9wIGRpc2FibGVkLiBhbHNvIGRyb3Bkb3duIG11c3QgYmUgYXR0YWNoZWQgZHVyaW5nIGlucHV0XG4gICAgICovXG4gICAgcHVibGljIGluaXRDbGlja091dHNpZGVTdWIoKTogdm9pZCB7XG4gICAgICAgIHRoaXMucmVuZGVyZXIubGlzdGVuKCd3aW5kb3cnLCAnY2xpY2snLCBldmVudCA9PiB7XG4gICAgICAgICAgICBjb25zdCBpc1BhcmVudCA9IHRoaXMuY29uZmlnLnBhcmVudEVsZW0/LmNvbnRhaW5zKGV2ZW50LnRhcmdldCk7XG4gICAgICAgICAgICBjb25zdCBpc0Ryb3Bkb3duID0gdGhpcy5lbGVtLm5hdGl2ZUVsZW1lbnQuY29udGFpbnMoZXZlbnQudGFyZ2V0KTtcbiAgICAgICAgICAgIGlmICghaXNQYXJlbnQgJiYgIWlzRHJvcGRvd24pIHtcbiAgICAgICAgICAgICAgICB0aGlzLm9uUmVzdWx0KCk7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIGRldGVjdCByZWxhdGl2ZSBwb3NpdGlvbiB0byBwcmV2ZW50IGRyb3Bkb3duIGJlaW5nIGhpZGRlbiBvdmVyIGBvdmVyZmxvdzogbm9uZWBcbiAgICAgKi9cbiAgICBwdWJsaWMgZ2V0RHJvcGRvd25SZWN0cygpOiB2b2lkIHtcbiAgICAgICAgY29uc3Qgd2luZG93SGVpZ2h0ID0gd2luZG93LmlubmVySGVpZ2h0O1xuICAgICAgICBjb25zdCBwaXhlbHNMZWZ0ID0gd2luZG93SGVpZ2h0IC0gdGhpcy5jb25maWcudHJpZ2dlclJlY3QuaGVpZ2h0IC0gdGhpcy5jb25maWcudHJpZ2dlclJlY3QudG9wO1xuICAgICAgICBjb25zdCByZWN0cyA9IHRoaXMuZHJvcGRvd25FbGVtZW50Lm5hdGl2ZUVsZW1lbnQuZ2V0Qm91bmRpbmdDbGllbnRSZWN0KCk7XG5cbiAgICAgICAgdGhpcy5pbnZlcnRlZCA9IHBpeGVsc0xlZnQgPD0gREVGQVVMVF9EUk9QRE9XTl9PRkZTRVQ7XG4gICAgICAgIGlmICh0aGlzLmludmVydGVkKSB7XG4gICAgICAgICAgICB0aGlzLnJlY3RzLmJvdHRvbSA9IHdpbmRvd0hlaWdodCAtIHRoaXMuY29uZmlnLnRyaWdnZXJSZWN0LnRvcDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRoaXMucmVjdHMudG9wID0gdGhpcy5jb25maWcudHJpZ2dlclJlY3QuYm90dG9tO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuY29uZmlnLmZpdFdpZHRoKSB7XG4gICAgICAgICAgICB0aGlzLnJlY3RzLndpZHRoID0gdGhpcy5jb25maWcudHJpZ2dlclJlY3Qud2lkdGg7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAocmVjdHMud2lkdGggPiB0aGlzLmNvbmZpZy50cmlnZ2VyUmVjdC53aWR0aCkge1xuICAgICAgICAgICAgY29uc3QgcmlnaHRPZmZzZXQgPSB3aW5kb3cuaW5uZXJXaWR0aCAtIHRoaXMuY29uZmlnLnRyaWdnZXJSZWN0LndpZHRoIC0gdGhpcy5jb25maWcudHJpZ2dlclJlY3QubGVmdDtcbiAgICAgICAgICAgIGlmIChyaWdodE9mZnNldCA8PSByZWN0cy53aWR0aCArIDE2KSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWN0cy5sZWZ0ID0gKHRoaXMuY29uZmlnLnRyaWdnZXJSZWN0LmxlZnQgKyB0aGlzLmNvbmZpZy50cmlnZ2VyUmVjdC53aWR0aCkgLSByZWN0cy53aWR0aDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIC0gaWYgYGZpdFdpZHRoYCBjb25maWcgb3B0aW9ucyBpcyB0cnVlXG4gICAgICogdGhlcmUgYXJlIHdpZHRoIGRlY2xhcmVkIGRlcGVuZGluZyBvbiBpdHMgcGFyZW50IGVsZW1lbnRcbiAgICAgKiAtIGlmIGByZWN0cy50b3BgIGNhbGN1bGF0ZWQgdGhlcmUgaXMgZW5vdWdoIHBsYWNlIHRvIGRyb3AgaXQgZG93bixcbiAgICAgKiAgaWYgaXQgaGl0cyBgcmVjdHMuYm90dG9tYCDigJMgc2hvdyBpdCBhYm92ZSB0aGUgZWxlbWVudFxuICAgICAqL1xuICAgIHB1YmxpYyBzZXREcm9wZG93blBvc2l0aW9uKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5yZWN0cy53aWR0aCkge1xuICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmRyb3Bkb3duRWxlbWVudC5uYXRpdmVFbGVtZW50LCBgd2lkdGhgLCBgJHt0aGlzLnJlY3RzLndpZHRofXB4YCk7XG4gICAgICAgIH1cblxuICAgICAgICBpZiAodGhpcy5yZWN0cy50b3ApIHtcbiAgICAgICAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5kcm9wZG93bkVsZW1lbnQubmF0aXZlRWxlbWVudCwgYHRvcGAsIGAke3RoaXMucmVjdHMudG9wfXB4YCk7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoaXMuZHJvcGRvd25FbGVtZW50Lm5hdGl2ZUVsZW1lbnQsIGBib3R0b21gLCBgJHt0aGlzLnJlY3RzLmJvdHRvbX1weGApO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh0aGlzLmRyb3Bkb3duRWxlbWVudC5uYXRpdmVFbGVtZW50LCBgbGVmdGAsIGAke3RoaXMucmVjdHMubGVmdCB8fCB0aGlzLmNvbmZpZy50cmlnZ2VyUmVjdC5sZWZ0fXB4YCk7XG4gICAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUodGhpcy5kcm9wZG93bkVsZW1lbnQubmF0aXZlRWxlbWVudCwgYG9wYWNpdHlgLCAxKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBkZXRlY3Qgd2luZG93IHJlc2l6ZSBhbmQgc2Nyb2xsIHRvIHByZXZlbnQgZmFpbGVkIGRyb3Bkb3duIHBvc2l0aW9uXG4gICAgICovXG4gICAgcHVibGljIGluaXRDbG9zaW5nU3Vic2NyaXB0aW9ucygpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zdWIgPSBmcm9tRXZlbnQod2luZG93LCAnc2Nyb2xsJykucGlwZShcbiAgICAgICAgICAgIHRha2UoMSlcbiAgICAgICAgKS5zdWJzY3JpYmUoKCkgPT4ge1xuICAgICAgICAgICAgdGhpcy5vblJlc3VsdCgpO1xuICAgICAgICB9KTtcblxuICAgICAgICB0aGlzLnN1Yi5hZGQoZnJvbUV2ZW50KHdpbmRvdywgJ3Jlc2l6ZScpLnBpcGUoXG4gICAgICAgICAgICB0YWtlKDEpXG4gICAgICAgICkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMub25SZXN1bHQoKTtcbiAgICAgICAgfSkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIGtleWJvYXJkIGV2ZW50c1xuICAgICAqL1xuICAgIHB1YmxpYyBpbml0S2V5ZG93blN1YnNjcmlwdGlvbigpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zdWIuYWRkKFxuICAgICAgICAgICAgZnJvbUV2ZW50KHRoaXMuZG9jdW1lbnQsICdrZXlkb3duJykuc3Vic2NyaWJlKChlOiBLZXlib2FyZEV2ZW50KSA9PiB7XG4gICAgICAgICAgICAgICAgc3dpdGNoIChlLmtleSB8fCBlLmNvZGUpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnQXJyb3dEb3duJzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm5leHRPcHRpb24oJ2Rvd24nKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICBjYXNlICdBcnJvd1VwJzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm5leHRPcHRpb24oJ3VwJyk7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnRW50ZXInOlxuICAgICAgICAgICAgICAgICAgICAgICAgZS5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgICAgICAgICAgZS5zdG9wUHJvcGFnYXRpb24oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh0aGlzLmN1cnJlbnRPcHRpb24pIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uUmVzdWx0KHRoaXMuY3VycmVudE9wdGlvbik7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgY2FzZSAnRXNjYXBlJzpcbiAgICAgICAgICAgICAgICAgICAgICAgIGUucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGUuc3RvcFByb3BhZ2F0aW9uKCk7XG4gICAgICAgICAgICAgICAgICAgICAgICB0aGlzLm9uUmVzdWx0KCk7XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICB9KVxuICAgICAgICApO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uSW5pdCgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5pbml0Q2xvc2luZ1N1YnNjcmlwdGlvbnMoKTtcbiAgICAgICAgdGhpcy5pbml0S2V5ZG93blN1YnNjcmlwdGlvbigpO1xuICAgICAgICBpZiAodGhpcy5jb25maWcgJiYgdGhpcy5jb25maWcuaGlkZUJhY2tkcm9wKSB7XG4gICAgICAgICAgICB0aGlzLmluaXRDbGlja091dHNpZGVTdWIoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHB1YmxpYyBuZ0FmdGVyVmlld0luaXQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZ2V0RHJvcGRvd25SZWN0cygpO1xuICAgICAgICB0aGlzLnNldERyb3Bkb3duUG9zaXRpb24oKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuc3ViPy51bnN1YnNjcmliZSgpO1xuICAgICAgICB0aGlzLmN1cnJlbnRPcHRpb24gPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuaGlnaGxpZ2h0ZWRJbmRleCA9IHVuZGVmaW5lZDtcbiAgICB9XG5cbn1cbiIsIjxkaXYgY2xhc3M9XCJzZGstZHJvcGRvd24td3JhcFwiIFtjbGFzcy5uby1iYWNrZHJvcF09XCJjb25maWc/LmhpZGVCYWNrZHJvcFwiPlxuICAgIDxkaXYgY2xhc3M9XCJzZGstZHJvcGRvd24tYmFja2Ryb3BcIiAqbmdJZj1cIiFjb25maWc/LmhpZGVCYWNrZHJvcFwiIChjbGljayk9XCJvblJlc3VsdCgpXCI+PC9kaXY+XG4gICAgPHVsIGNsYXNzPVwic2RrLWRyb3Bkb3duXCIgI2Ryb3Bkb3duIFtjbGFzcy5tdWx0aV09XCJjb25maWc/Lm11bHRpXCI+XG4gICAgICAgIDxuZy1jb250YWluZXIgKm5nSWY9XCIhb3B0aW9uc1NlcnZpY2UuaGFzT3B0aW9uc1wiPlxuICAgICAgICAgICAgPGxpIGNsYXNzPVwic2RrLWRyb3Bkb3duLWl0ZW1cIj5cbiAgICAgICAgICAgICAgICA8YT5ObyBvcHRpb25zLjwvYT5cbiAgICAgICAgICAgIDwvbGk+XG4gICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwib3B0aW9uc1NlcnZpY2UuaGFzT3B0aW9uc1wiPlxuICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdGb3I9XCJsZXQgbyBvZiBvcHRpb25zU2VydmljZS5vcHRpb25zT2JzZXJ2YWJsZSB8IGFzeW5jOyBpbmRleCBhcyBpXCI+XG4gICAgICAgICAgICAgICAgPGxpIGNsYXNzPVwic2RrLWRyb3Bkb3duLWl0ZW1cIiAoY2xpY2spPVwic2VsZWN0KCRldmVudCwgbylcIlxuICAgICAgICAgICAgICAgICAgICBbbmdDbGFzc109XCJ7XG4gICAgICAgICAgICAgICAgICAgICAgICBoaWdobGlnaHRlZDogby52YWx1ZSA9PT0gY3VycmVudE9wdGlvbj8udmFsdWUsXG4gICAgICAgICAgICAgICAgICAgICAgICBkaXNhYmxlZDogby5kaXNhYmxlZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIGFjdGl2ZTogby5zZWxlY3RlZCAmJiAhY29uZmlnPy5tdWx0aVxuICAgICAgICAgICAgICAgICAgICB9XCJcbiAgICAgICAgICAgICAgICAgICAgKG1vdXNlZW50ZXIpPVwib25PcHRpb25Nb3VzZUVudGVyKG8sIGkpXCJcbiAgICAgICAgICAgICAgICAgICAgKG1vdXNlbGVhdmUpPVwib25PcHRpb25Nb3VzZUxlYXZlKClcIj5cbiAgICAgICAgICAgICAgICAgICAgPHNwYW4gY2xhc3M9XCJzZGstZHJvcGRvd24taXRlbS1jaGVja2VkXCIgKm5nSWY9XCJjb25maWc/Lm11bHRpXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8bmctY29udGFpbmVyICpuZ0lmPVwiby5zZWxlY3RlZFwiPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxzZGstaWNvbiBpY29uPVwiY2hlY2tcIiBzaXplPVwiMTZcIj48L3Nkay1pY29uPlxuICAgICAgICAgICAgICAgICAgICAgICAgPC9uZy1jb250YWluZXI+XG4gICAgICAgICAgICAgICAgICAgIDwvc3Bhbj5cbiAgICAgICAgICAgICAgICAgICAgPG5nLWNvbnRhaW5lciAqbmdJZj1cIm8uaW1hZ2VcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxpbWcgYWx0PVwie3tvLmxhYmVsfX1cIiBjbGFzcz1cInNkay1kcm9wZG93bi1pdGVtLWltYWdlXCIgW3NyY109XCJvLmltYWdlXCIvPlxuICAgICAgICAgICAgICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICAgICAgICAgICAgICAgICAgPGE+e3tvLmxhYmVsfX08L2E+XG4gICAgICAgICAgICAgICAgPC9saT5cbiAgICAgICAgICAgIDwvbmctY29udGFpbmVyPlxuICAgICAgICA8L25nLWNvbnRhaW5lcj5cbiAgICA8L3VsPlxuPC9kaXY+XG5cblxuIl19