UNPKG

truly-ui

Version:

Web Components for Desktop Applications.

286 lines (284 loc) 42.6 kB
/* MIT License Copyright (c) 2019 Temainfo Software Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ import { Component, EventEmitter, Input, Output, ViewChild, ViewChildren, } from '@angular/core'; import { ActiveDescendantKeyManager } from '@angular/cdk/a11y'; import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling'; import { ListBase } from './classes/list-base'; import { KeyEvent } from '../core/enums/key-events'; import { Subscription } from 'rxjs'; import { DataSourceList } from '../core/classes/datasource-list'; import { TlInput } from '../input/input'; import { TlItemSelectedDirective } from '../core/directives/itemSelected/item-selected.directive'; import { scrollIntoView } from '../core/helper/scrollIntoView'; import * as i0 from "@angular/core"; import * as i1 from "../i18n/i18n.service"; import * as i2 from "@angular/common"; import * as i3 from "@angular/cdk/scrolling"; import * as i4 from "../core/components/filter/filter-container"; import * as i5 from "../core/directives/itemSelected/item-selected.directive"; import * as i6 from "../loader/loader"; import * as i7 from "./components/listbox-template"; export class TlListBox extends ListBase { set data(value) { this._data = value; this.setUpData(value); } get data() { return this._data; } set inputElement(value) { this._inputElement = value; if (value instanceof TlInput) { this._inputElement = value.getNativeInput(); } } get inputElement() { return this._inputElement; } constructor(renderer, change, i18nService) { super(); this.renderer = renderer; this.change = change; this.i18nService = i18nService; this.rowHeight = 40; this.keyText = 'description'; this.color = 'basic'; this.loading = true; this.height = '200px'; this.debounceTime = 200; this.searchBy = null; this.nothingFoundMessage = this.i18nService.getLocale().Listbox.notFoundText; this.totalLength = 100; this.rowsPage = 10; this.selectOnInitialize = true; this.lazyMode = false; this.clickItem = new EventEmitter(); this.selectItem = new EventEmitter(); this.subscription = new Subscription(); this._data = []; this.nothingFound = false; } ngAfterViewInit() { this.listKeyManager = new ActiveDescendantKeyManager(this.listItems); this.listKeyManager.withTypeAhead(); this.initializeListBox(); this.handleListeners(); } handleListeners() { this.subscription.add(this.renderer.listen(this.getElementTarget(), 'keydown', ($event) => { const event = { [KeyEvent.ARROWDOWN]: () => this.handleKeyArrowDown($event), [KeyEvent.ARROWUP]: () => this.handleKeyArrowUp($event), [KeyEvent.ENTER]: () => this.onKeyEnter() }; if (event[$event.code]) { event[$event.code](); } })); } getElementTarget() { return this.inputElement ? this.inputElement : this.cdkVirtualScroll.elementRef.nativeElement; } initializeListBox() { if (this.selectOnInitialize) { this.setSelected(this.listItems.toArray()[0]); } this.setItemsByRowSet(); this.setItemsByScroll(); this.setContainer(); } setScrollTop() { this.scrollTop = this.cdkVirtualScroll.elementRef.nativeElement.scrollTop; } select(index) { this.listKeyManager.setActiveItem(index); if (this.listKeyManager.activeItem) { this.selectItem.emit(this.listKeyManager.activeItem.itemSelected); } } setSelected(item) { this.listKeyManager.setActiveItem(item); this.setInputFocus(); } onKeyEnter() { this.selectItem.emit(this.listKeyManager.activeItem.itemSelected); } onClickItem(item) { this.clickItem.emit(item); } setItemsByRowSet() { this.itemsByRowSet = Math.floor(this.cdkVirtualScroll.elementRef.nativeElement.offsetHeight / this.rowHeight); } setItemsByScroll() { this.itemsByScroll = Math.floor(this.cdkVirtualScroll.elementRef.nativeElement.scrollTop / this.rowHeight); } setContainer() { this.container = this.cdkVirtualScroll.elementRef.nativeElement; } setScrollVirtual() { this.cdkVirtualScroll.elementRef.nativeElement.scrollTop = 0; } setUpData(value) { if (value && value.length > 0) { this.dataSource = new DataSourceList({ dataSource: value || this.data, pageSize: this.rowsPage, totalLength: this.totalLength, lazyMode: this.lazyMode }); this.loading = false; this.nothingFound = false; this.dataSource.dataStream.next(value || this.data); } else { this.loading = false; this.nothingFound = true; if (this.dataSource) { this.dataSource.dataStream.next([]); } } } onScroll() { if (!this.scrollingByArrows) { clearTimeout(this.isScrolling); this.isScrolling = setTimeout(() => { this.setItemsByScroll(); this.setScrollTop(); this.isScrollDown() ? this.setScrollBottomItem() : this.setScrollTopItem(); this.setLastScrollTop(); }, 66); } } onWheel() { this.scrollingByArrows = false; } onFilter($event) { if ($event) { this.setScrollVirtual(); this.setUpData($event); return; } this.loading = false; this.nothingFound = true; this.dataSource.dataStream.next([]); } getItemByIndex(index) { return this.listItems.filter((item) => item.indexSelected === index)[0]; } setScrollTopItem() { this.setSelected(this.getItemByIndex(this.itemsByScroll)); } setLastScrollTop() { this.lastScrollTop = this.scrollTop; } setScrollBottomItem() { const index = Math.round(this.itemsByRowSet + this.itemsByScroll) - 1; this.setSelected(this.getItemByIndex(index)); } setInputFocus() { if (this.inputElement) { this.inputElement.focus(); } } setActiveItem() { this.activeItem = this.listKeyManager.activeItem; } handleKeyArrowUp($event) { this.scrollingByArrows = true; this.listKeyManager.onKeydown($event); this.setActiveItem(); scrollIntoView(this.activeItem.element.nativeElement); this.change.detectChanges(); } handleKeyArrowDown($event) { this.scrollingByArrows = true; this.listKeyManager.onKeydown($event); this.setActiveItem(); scrollIntoView(this.activeItem.element.nativeElement); this.change.detectChanges(); } isScrollDown() { return this.scrollTop > this.lastScrollTop; } ngOnChanges({ data }) { if (data && !data.firstChange && data.currentValue.length === 0) { this.loading = false; this.nothingFound = true; this.change.detectChanges(); } } ngOnDestroy() { this.subscription.unsubscribe(); this.change.detach(); } } /** @nocollapse */ TlListBox.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TlListBox, deps: [{ token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: i1.I18nService }], target: i0.ɵɵFactoryTarget.Component }); /** @nocollapse */ TlListBox.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "15.2.10", type: TlListBox, selector: "tl-listbox", inputs: { data: "data", rowHeight: "rowHeight", template: "template", templateNotFound: "templateNotFound", inputElement: "inputElement", keyText: "keyText", color: "color", loading: "loading", searchControl: "searchControl", height: "height", debounceTime: "debounceTime", searchBy: "searchBy", nothingFoundMessage: "nothingFoundMessage", totalLength: "totalLength", rowsPage: "rowsPage", selectOnInitialize: "selectOnInitialize", lazyMode: "lazyMode" }, outputs: { clickItem: "clickItem", selectItem: "selectItem" }, viewQueries: [{ propertyName: "cdkVirtualScroll", first: true, predicate: CdkVirtualScrollViewport, descendants: true, static: true }, { propertyName: "listItems", predicate: TlItemSelectedDirective, descendants: true }], usesInheritance: true, usesOnChanges: true, ngImport: i0, template: "<div class=\"ui-listbox-wrapper\" [style.height]=\"height\">\n <div class=\"ui-emptylist\" *ngIf=\"loading || nothingFound\">\n <div *ngIf=\"loading && !nothingFound\" class=\"ui-loading\" [style.width]=\"'50px'\" [style.height]=\"'50px'\">\n <tl-loader></tl-loader>\n </div>\n <div *ngIf=\"nothingFound && !loading\" class=\"ui-notfound\">\n <span class=\"message\">{{ nothingFoundMessage }}</span>\n <ng-container *ngTemplateOutlet=\"templateNotFound\"></ng-container>\n </div>\n </div>\n <cdk-virtual-scroll-viewport [itemSize]=\"rowHeight\" [class]=\"'ui-list-viewport ' + color\" tabindex=\"-1\"\n [style.height]=\"height\"\n (wheel)=\"onWheel()\"\n (scrolledIndexChange)=\"onScroll()\">\n <tl-filter-container [searchTerm]=\"searchControl?.value\"\n [debounceTime]=\"debounceTime\"\n [source]=\"data\"\n [searchBy]=\"searchBy\"\n (filter)=\"onFilter($event)\">\n <div [itemSelected]=\"item\" #select=\"selectItem\"\n [ngStyle]=\"{ height: rowHeight + 'px' }\"\n [indexSelected]=\"i\"\n [style.padding]=\"!template ? '0 10px' : '0'\"\n [attr.index]=\"i\"\n *cdkVirtualFor=\"let item of dataSource; let i = index\"\n (click)=\"setSelected(select); onClickItem(item)\"\n class=\"ui-list-viewport-item\">\n <div *ngIf=\"!template\">\n {{ item[keyText] }}\n </div>\n <tl-listbox-template\n [style.width]=\"'100%'\"\n *ngIf=\"template\"\n [template]=\"template\"\n [item]=\"item\"\n [index]=\"i\">\n </tl-listbox-template>\n </div>\n </tl-filter-container>\n </cdk-virtual-scroll-viewport>\n</div>\n", styles: [".ui-listbox-wrapper{position:relative;z-index:1}.ui-list-viewport{overflow-y:scroll;width:100%;border-radius:5px;outline:none}.ui-list-viewport::-webkit-scrollbar{border-radius:5px;width:8px}.ui-list-viewport-item{box-sizing:border-box;height:50px;scroll-snap-align:center;display:flex;align-items:center;cursor:pointer}.ui-emptylist{position:absolute;height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-flow:column;z-index:1}.ui-emptylist>.ui-notfound{text-align:center}.ui-emptylist>.ui-notfound .message{bottom:5px;position:relative;color:#7b7878}\n"], dependencies: [{ kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i3.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i3.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i3.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "component", type: i4.TlFilterContainer, selector: "tl-filter-container", inputs: ["searchTerm", "source", "searchBy", "lazyMode", "debounceTime"], outputs: ["filter"], exportAs: ["filterContainer"] }, { kind: "directive", type: i5.TlItemSelectedDirective, selector: "[itemSelected]", inputs: ["indexSelected", "itemSelected"], exportAs: ["selectItem"] }, { kind: "component", type: i6.TlLoader, selector: "tl-loader", inputs: ["color", "strokeWidth"] }, { kind: "component", type: i7.TlListBoxTemplate, selector: "tl-listbox-template", inputs: ["template", "item", "index"] }] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.2.10", ngImport: i0, type: TlListBox, decorators: [{ type: Component, args: [{ selector: 'tl-listbox', template: "<div class=\"ui-listbox-wrapper\" [style.height]=\"height\">\n <div class=\"ui-emptylist\" *ngIf=\"loading || nothingFound\">\n <div *ngIf=\"loading && !nothingFound\" class=\"ui-loading\" [style.width]=\"'50px'\" [style.height]=\"'50px'\">\n <tl-loader></tl-loader>\n </div>\n <div *ngIf=\"nothingFound && !loading\" class=\"ui-notfound\">\n <span class=\"message\">{{ nothingFoundMessage }}</span>\n <ng-container *ngTemplateOutlet=\"templateNotFound\"></ng-container>\n </div>\n </div>\n <cdk-virtual-scroll-viewport [itemSize]=\"rowHeight\" [class]=\"'ui-list-viewport ' + color\" tabindex=\"-1\"\n [style.height]=\"height\"\n (wheel)=\"onWheel()\"\n (scrolledIndexChange)=\"onScroll()\">\n <tl-filter-container [searchTerm]=\"searchControl?.value\"\n [debounceTime]=\"debounceTime\"\n [source]=\"data\"\n [searchBy]=\"searchBy\"\n (filter)=\"onFilter($event)\">\n <div [itemSelected]=\"item\" #select=\"selectItem\"\n [ngStyle]=\"{ height: rowHeight + 'px' }\"\n [indexSelected]=\"i\"\n [style.padding]=\"!template ? '0 10px' : '0'\"\n [attr.index]=\"i\"\n *cdkVirtualFor=\"let item of dataSource; let i = index\"\n (click)=\"setSelected(select); onClickItem(item)\"\n class=\"ui-list-viewport-item\">\n <div *ngIf=\"!template\">\n {{ item[keyText] }}\n </div>\n <tl-listbox-template\n [style.width]=\"'100%'\"\n *ngIf=\"template\"\n [template]=\"template\"\n [item]=\"item\"\n [index]=\"i\">\n </tl-listbox-template>\n </div>\n </tl-filter-container>\n </cdk-virtual-scroll-viewport>\n</div>\n", styles: [".ui-listbox-wrapper{position:relative;z-index:1}.ui-list-viewport{overflow-y:scroll;width:100%;border-radius:5px;outline:none}.ui-list-viewport::-webkit-scrollbar{border-radius:5px;width:8px}.ui-list-viewport-item{box-sizing:border-box;height:50px;scroll-snap-align:center;display:flex;align-items:center;cursor:pointer}.ui-emptylist{position:absolute;height:100%;width:100%;display:flex;align-items:center;justify-content:center;flex-flow:column;z-index:1}.ui-emptylist>.ui-notfound{text-align:center}.ui-emptylist>.ui-notfound .message{bottom:5px;position:relative;color:#7b7878}\n"] }] }], ctorParameters: function () { return [{ type: i0.Renderer2 }, { type: i0.ChangeDetectorRef }, { type: i1.I18nService }]; }, propDecorators: { data: [{ type: Input, args: ['data'] }], rowHeight: [{ type: Input }], template: [{ type: Input }], templateNotFound: [{ type: Input }], inputElement: [{ type: Input, args: ['inputElement'] }], keyText: [{ type: Input }], color: [{ type: Input }], loading: [{ type: Input }], searchControl: [{ type: Input }], height: [{ type: Input }], debounceTime: [{ type: Input }], searchBy: [{ type: Input }], nothingFoundMessage: [{ type: Input }], totalLength: [{ type: Input }], rowsPage: [{ type: Input }], selectOnInitialize: [{ type: Input }], lazyMode: [{ type: Input }], clickItem: [{ type: Output }], selectItem: [{ type: Output }], cdkVirtualScroll: [{ type: ViewChild, args: [CdkVirtualScrollViewport, { static: true }] }], listItems: [{ type: ViewChildren, args: [TlItemSelectedDirective] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlzdGJveC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL3RydWx5LXVpL3NyYy9jb21wb25lbnRzL2xpc3Rib3gvbGlzdGJveC50cyIsIi4uLy4uLy4uLy4uL3Byb2plY3RzL3RydWx5LXVpL3NyYy9jb21wb25lbnRzL2xpc3Rib3gvbGlzdGJveC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQW9CRztBQUVILE9BQU8sRUFHTCxTQUFTLEVBQ1QsWUFBWSxFQUNaLEtBQUssRUFHTCxNQUFNLEVBS04sU0FBUyxFQUNULFlBQVksR0FDYixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUMsMEJBQTBCLEVBQUMsTUFBTSxtQkFBbUIsQ0FBQztBQUM3RCxPQUFPLEVBQUMsd0JBQXdCLEVBQUMsTUFBTSx3QkFBd0IsQ0FBQztBQUNoRSxPQUFPLEVBQUMsUUFBUSxFQUFDLE1BQU0scUJBQXFCLENBQUM7QUFDN0MsT0FBTyxFQUFDLFFBQVEsRUFBQyxNQUFNLDBCQUEwQixDQUFDO0FBQ2xELE9BQU8sRUFBQyxZQUFZLEVBQUMsTUFBTSxNQUFNLENBQUM7QUFFbEMsT0FBTyxFQUFDLGNBQWMsRUFBQyxNQUFNLGlDQUFpQyxDQUFDO0FBQy9ELE9BQU8sRUFBQyxPQUFPLEVBQUMsTUFBTSxnQkFBZ0IsQ0FBQztBQUV2QyxPQUFPLEVBQUMsdUJBQXVCLEVBQUMsTUFBTSx5REFBeUQsQ0FBQztBQUNoRyxPQUFPLEVBQUMsY0FBYyxFQUFDLE1BQU0sK0JBQStCLENBQUM7Ozs7Ozs7OztBQU83RCxNQUFNLE9BQU8sU0FBVSxTQUFRLFFBQVE7SUFFckMsSUFDSSxJQUFJLENBQUMsS0FBSztRQUNaLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO1FBQ25CLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUM7SUFDeEIsQ0FBQztJQUVELElBQUksSUFBSTtRQUNO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TztzQkFBZixLQUFLO2dCQUVHLEtBQUs7c0JBQWIsS0FBSztnQkFFRyxPQUFPO3NCQUFmLEtBQUs7Z0JBRUcsYUFBYTtzQkFBckIsS0FBSztnQkFFRyxNQUFNO3NCQUFkLEtBQUs7Z0JBRUcsWUFBWTtzQkFBcEIsS0FBSztnQkFFRyxRQUFRO3NCQUFoQixLQUFLO2dCQUVHLG1CQUFtQjtzQkFBM0IsS0FBSztnQkFFRyxXQUFXO3NCQUFuQixLQUFLO2dCQUVHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBRUcsa0JBQWtCO3NCQUExQixLQUFLO2dCQUVHLFFBQVE7c0JBQWhCLEtBQUs7Z0JBRUksU0FBUztzQkFBbEIsTUFBTTtnQkFFRyxVQUFVO3NCQUFuQixNQUFNO2dCQUUrQyxnQkFBZ0I7c0JBQXJFLFNBQVM7dUJBQUMsd0JBQXdCLEVBQUUsRUFBQyxNQUFNLEVBQUUsSUFBSSxFQUFDO2dCQUVaLFNBQVM7c0JBQS9DLFlBQVk7dUJBQUMsdUJBQXVCIiwic291cmNlc0NvbnRlbnQiOlsiLypcbiBNSVQgTGljZW5zZVxuXG4gQ29weXJpZ2h0IChjKSAyMDE5IFRlbWFpbmZvIFNvZnR3YXJlXG5cbiBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4gb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG8gZGVhbFxuIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZSByaWdodHNcbiB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vciBzZWxsXG4gY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4gZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbiBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpbiBhbGxcbiBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1JcbiBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbiBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbiBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4gTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbiBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOIFRIRVxuIFNPRlRXQVJFLlxuICovXG5cbmltcG9ydCB7XG4gIEFmdGVyVmlld0luaXQsXG4gIENoYW5nZURldGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIEV2ZW50RW1pdHRlcixcbiAgSW5wdXQsXG4gIE9uQ2hhbmdlcyxcbiAgT25EZXN0cm95LFxuICBPdXRwdXQsXG4gIFF1ZXJ5TGlzdCxcbiAgUmVuZGVyZXIyLFxuICBTaW1wbGVDaGFuZ2VzLFxuICBUZW1wbGF0ZVJlZixcbiAgVmlld0NoaWxkLFxuICBWaWV3Q2hpbGRyZW4sXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHtBY3RpdmVEZXNjZW5kYW50S2V5TWFuYWdlcn0gZnJvbSAnQGFuZ3VsYXIvY2RrL2ExMXknO1xuaW1wb3J0IHtDZGtWaXJ0dWFsU2Nyb2xsVmlld3BvcnR9IGZyb20gJ0Bhbmd1bGFyL2Nkay9zY3JvbGxpbmcnO1xuaW1wb3J0IHtMaXN0QmFzZX0gZnJvbSAnLi9jbGFzc2VzL2xpc3QtYmFzZSc7XG5pbXBvcnQge0tleUV2ZW50fSBmcm9tICcuLi9jb3JlL2VudW1zL2tleS1ldmVudHMnO1xuaW1wb3J0IHtTdWJzY3JpcHRpb259IGZyb20gJ3J4anMnO1xuaW1wb3J0IHtVbnR5cGVkRm9ybUNvbnRyb2x9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcbmltcG9ydCB7RGF0YVNvdXJjZUxpc3R9IGZyb20gJy4uL2NvcmUvY2xhc3Nlcy9kYXRhc291cmNlLWxpc3QnO1xuaW1wb3J0IHtUbElucHV0fSBmcm9tICcuLi9pbnB1dC9pbnB1dCc7XG5pbXBvcnQge0kxOG5TZXJ2aWNlfSBmcm9tICcuLi9pMThuL2kxOG4uc2VydmljZSc7XG5pbXBvcnQge1RsSXRlbVNlbGVjdGVkRGlyZWN0aXZlfSBmcm9tICcuLi9jb3JlL2RpcmVjdGl2ZXMvaXRlbVNlbGVjdGVkL2l0ZW0tc2VsZWN0ZWQuZGlyZWN0aXZlJztcbmltcG9ydCB7c2Nyb2xsSW50b1ZpZXd9IGZyb20gJy4uL2NvcmUvaGVscGVyL3Njcm9sbEludG9WaWV3JztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAndGwtbGlzdGJveCcsXG4gIHRlbXBsYXRlVXJsOiAnLi9saXN0Ym94Lmh0bWwnLFxuICBzdHlsZVVybHM6IFsnLi9saXN0Ym94LnNjc3MnXSxcbn0pXG5leHBvcnQgY2xhc3MgVGxMaXN0Qm94IGV4dGVuZHMgTGlzdEJhc2UgaW1wbGVtZW50cyBBZnRlclZpZXdJbml0LCBPbkRlc3Ryb3ksIE9uQ2hhbmdlcyB7XG5cbiAgQElucHV0KCdkYXRhJylcbiAgc2V0IGRhdGEodmFsdWUpIHtcbiAgICB0aGlzLl9kYXRhID0gdmFsdWU7XG4gICAgdGhpcy5zZXRVcERhdGEodmFsdWUpO1xuICB9XG5cbiAgZ2V0IGRhdGEoKSB7XG4gICAgcmV0dXJuIHRoaXMuX2RhdGE7XG4gIH1cblxuICBASW5wdXQoKSByb3dIZWlnaHQgPSA0MDtcblxuICBASW5wdXQoKSB0ZW1wbGF0ZTogVGVtcGxhdGVSZWY8YW55PjtcblxuICBASW5wdXQoKSB0ZW1wbGF0ZU5vdEZvdW5kOiBUZW1wbGF0ZVJlZjxhbnk+O1xuXG4gIEBJbnB1dCgnaW5wdXRFbGVtZW50JylcbiAgc2V0IGlucHV0RWxlbWVudCh2YWx1ZSkge1xuICAgIHRoaXMuX2lucHV0RWxlbWVudCA9IHZhbHVlO1xuICAgIGlmICh2YWx1ZSBpbnN0YW5jZW9mIFRsSW5wdXQpIHtcbiAgICAgIHRoaXMuX2lucHV0RWxlbWVudCA9IHZhbHVlLmdldE5hdGl2ZUlucHV0KCk7XG4gICAgfVxuICB9XG5cbiAgZ2V0IGlucHV0RWxlbWVudCgpIHtcbiAgICByZXR1cm4gdGhpcy5faW5wdXRFbGVtZW50O1xuICB9XG5cbiAgQElucHV0KCkga2V5VGV4dCA9ICdkZXNjcmlwdGlvbic7XG5cbiAgQElucHV0KCkgY29sb3IgPSAnYmFzaWMnO1xuXG4gIEBJbnB1dCgpIGxvYWRpbmcgPSB0cnVlO1xuXG4gIEBJbnB1dCgpIHNlYXJjaENvbnRyb2w6IFVudHlwZWRGb3JtQ29udHJvbDtcblxuICBASW5wdXQoKSBoZWlnaHQgPSAnMjAwcHgnO1xuXG4gIEBJbnB1dCgpIGRlYm91bmNlVGltZSA9IDIwMDtcblxuICBASW5wdXQoKSBzZWFyY2hCeSA9IG51bGw7XG5cbiAgQElucHV0KCkgbm90aGluZ0ZvdW5kTWVzc2FnZSA9IHRoaXMuaTE4blNlcnZpY2UuZ2V0TG9jYWxlKCkuTGlzdGJveC5ub3RGb3VuZFRleHQ7XG5cbiAgQElucHV0KCkgdG90YWxMZW5ndGggPSAxMDA7XG5cbiAgQElucHV0KCkgcm93c1BhZ2UgPSAxMDtcblxuICBASW5wdXQoKSBzZWxlY3RPbkluaXRpYWxpemUgPSB0cnVlO1xuXG4gIEBJbnB1dCgpIGxhenlNb2RlID0gZmFsc2U7XG5cbiAgQE91dHB1dCgpIGNsaWNrSXRlbTogRXZlbnRFbWl0dGVyPGFueT4gPSBuZXcgRXZlbnRFbWl0dGVyKCk7XG5cbiAgQE91dHB1dCgpIHNlbGVjdEl0ZW06IEV2ZW50RW1pdHRlcjxhbnk+ID0gbmV3IEV2ZW50RW1pdHRlcigpO1xuXG4gIEBWaWV3Q2hpbGQoQ2RrVmlydHVhbFNjcm9sbFZpZXdwb3J0LCB7c3RhdGljOiB0cnVlfSApIGNka1ZpcnR1YWxTY3JvbGw6IENka1ZpcnR1YWxTY3JvbGxWaWV3cG9ydDtcblxuICBAVmlld0NoaWxkcmVuKFRsSXRlbVNlbGVjdGVkRGlyZWN0aXZlKSBsaXN0SXRlbXM6IFF1ZXJ5TGlzdDxUbEl0ZW1TZWxlY3RlZERpcmVjdGl2ZT47XG5cbiAgcHJpdmF0ZSBzdWJzY3JpcHRpb24gPSBuZXcgU3Vic2NyaXB0aW9uKCk7XG5cbiAgcHJpdmF0ZSBfZGF0YSA9IFtdO1xuXG4gIHByaXZhdGUgX2lucHV0RWxlbWVudDtcblxuICBwdWJsaWMgZGF0YVNvdXJjZTogRGF0YVNvdXJjZUxpc3Q7XG5cbiAgcHVibGljIG5vdGhpbmdGb3VuZCA9IGZhbHNlO1xuXG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVuZGVyZXI6IFJlbmRlcmVyMixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBjaGFuZ2U6IENoYW5nZURldGVjdG9yUmVmLFxuICAgICAgICAgICAgICBwcml2YXRlIGkxOG5TZXJ2aWNlOiBJMThuU2VydmljZSkge1xuICAgIHN1cGVyKCk7XG4gIH1cblxuICBuZ0FmdGVyVmlld0luaXQoKSB7XG4gICAgdGhpcy5saXN0S2V5TWFuYWdlciA9IG5ldyBBY3RpdmVEZXNjZW5kYW50S2V5TWFuYWdlcih0aGlzLmxpc3RJdGVtcyk7XG4gICAgdGhpcy5saXN0S2V5TWFuYWdlci53aXRoVHlwZUFoZWFkKCk7XG4gICAgdGhpcy5pbml0aWFsaXplTGlzdEJveCgpO1xuICAgIHRoaXMuaGFuZGxlTGlzdGVuZXJzKCk7XG4gIH1cblxuICBwcml2YXRlIGhhbmRsZUxpc3RlbmVycygpIHtcbiAgICB0aGlzLnN1YnNjcmlwdGlvbi5hZGQodGhpcy5yZW5kZXJlci5saXN0ZW4odGhpcy5nZXRFbGVtZW50VGFyZ2V0KCksICdrZXlkb3duJywgKCRldmVudCkgPT4ge1xuICAgICAgY29uc3QgZXZlbnQgPSB7XG4gICAgICAgIFtLZXlFdmVudC5BUlJPV0RPV05dOiAoKSA9PiB0aGlzLmhhbmRsZUtleUFycm93RG93bigkZXZlbnQpLFxuICAgICAgICBbS2V5RXZlbnQuQVJST1dVUF06ICgpID0+IHRoaXMuaGFuZGxlS2V5QXJyb3dVcCgkZXZlbnQpLFxuICAgICAgICBbS2V5RXZlbnQuRU5URVJdOiAoKSA9PiB0aGlzLm9uS2V5RW50ZXIoKVxuICAgICAgfTtcbiAgICAgIGlmIChldmVudFskZXZlbnQuY29kZV0pIHtcbiAgICAgICAgZXZlbnRbJGV2ZW50LmNvZGVdKCk7XG4gICAgICB9XG4gICAgfSkpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRFbGVtZW50VGFyZ2V0KCkge1xuICAgIHJldHVybiB0aGlzLmlucHV0RWxlbWVudCA/IHRoaXMuaW5wdXRFbGVtZW50IDogdGhpcy5jZGtWaXJ0dWFsU2Nyb2xsLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudDtcbiAgfVxuXG4gIHByaXZhdGUgaW5pdGlhbGl6ZUxpc3RCb3goKSB7XG4gICAgaWYgKCB0aGlzLnNlbGVjdE9uSW5pdGlhbGl6ZSApIHtcbiAgICAgIHRoaXMuc2V0U2VsZWN0ZWQodGhpcy5saXN0SXRlbXMudG9BcnJheSgpWzBdKTtcbiAgICB9XG4gICAgdGhpcy5zZXRJdGVtc0J5Um93U2V0KCk7XG4gICAgdGhpcy5zZXRJdGVtc0J5U2Nyb2xsKCk7XG4gICAgdGhpcy5zZXRDb250YWluZXIoKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0U2Nyb2xsVG9wKCkge1xuICAgIHRoaXMuc2Nyb2xsVG9wID0gdGhpcy5jZGtWaXJ0dWFsU2Nyb2xsLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5zY3JvbGxUb3A7XG4gIH1cblxuICBzZWxlY3QoaW5kZXg6IG51bWJlcikge1xuICAgIHRoaXMubGlzdEtleU1hbmFnZXIuc2V0QWN0aXZlSXRlbSggaW5kZXggKTtcbiAgICBpZiAodGhpcy5saXN0S2V5TWFuYWdlci5hY3RpdmVJdGVtKSB7XG4gICAgICB0aGlzLnNlbGVjdEl0ZW0uZW1pdCgodGhpcy5saXN0S2V5TWFuYWdlci5hY3RpdmVJdGVtIGFzIFRsSXRlbVNlbGVjdGVkRGlyZWN0aXZlKS5pdGVtU2VsZWN0ZWQpO1xuICAgIH1cbiAgfVxuXG4gIHNldFNlbGVjdGVkKGl0ZW06IFRsSXRlbVNlbGVjdGVkRGlyZWN0aXZlKSB7XG4gICAgdGhpcy5saXN0S2V5TWFuYWdlci5zZXRBY3RpdmVJdGVtKGl0ZW0pO1xuICAgIHRoaXMuc2V0SW5wdXRGb2N1cygpO1xuICB9XG5cbiAgb25LZXlFbnRlcigpIHtcbiAgICB0aGlzLnNlbGVjdEl0ZW0uZW1pdCgodGhpcy5saXN0S2V5TWFuYWdlci5hY3RpdmVJdGVtIGFzIFRsSXRlbVNlbGVjdGVkRGlyZWN0aXZlKS5pdGVtU2VsZWN0ZWQpO1xuICB9XG5cbiAgb25DbGlja0l0ZW0oaXRlbTogYW55KSB7XG4gICAgdGhpcy5jbGlja0l0ZW0uZW1pdChpdGVtKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0SXRlbXNCeVJvd1NldCgpIHtcbiAgICB0aGlzLml0ZW1zQnlSb3dTZXQgPSBNYXRoLmZsb29yKHRoaXMuY2RrVmlydHVhbFNjcm9sbC5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQub2Zmc2V0SGVpZ2h0IC8gdGhpcy5yb3dIZWlnaHQpO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRJdGVtc0J5U2Nyb2xsKCkge1xuICAgIHRoaXMuaXRlbXNCeVNjcm9sbCA9IE1hdGguZmxvb3IodGhpcy5jZGtWaXJ0dWFsU2Nyb2xsLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgLyB0aGlzLnJvd0hlaWdodCk7XG4gIH1cblxuICBwcml2YXRlIHNldENvbnRhaW5lcigpIHtcbiAgICB0aGlzLmNvbnRhaW5lciA9IHRoaXMuY2RrVmlydHVhbFNjcm9sbC5lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQ7XG4gIH1cblxuICBwcml2YXRlIHNldFNjcm9sbFZpcnR1YWwoKSB7XG4gICAgdGhpcy5jZGtWaXJ0dWFsU2Nyb2xsLmVsZW1lbnRSZWYubmF0aXZlRWxlbWVudC5zY3JvbGxUb3AgPSAwO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRVcERhdGEodmFsdWU/KSB7XG4gICAgaWYgKCB2YWx1ZSAmJiB2YWx1ZS5sZW5ndGggPiAwKSB7XG4gICAgICB0aGlzLmRhdGFTb3VyY2UgPSBuZXcgRGF0YVNvdXJjZUxpc3Qoe1xuICAgICAgICBkYXRhU291cmNlOiB2YWx1ZSB8fCB0aGlzLmRhdGEsXG4gICAgICAgIHBhZ2VTaXplOiB0aGlzLnJvd3NQYWdlLFxuICAgICAgICB0b3RhbExlbmd0aDogdGhpcy50b3RhbExlbmd0aCxcbiAgICAgICAgbGF6eU1vZGU6IHRoaXMubGF6eU1vZGVcbiAgICAgIH0pO1xuICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICB0aGlzLm5vdGhpbmdGb3VuZCA9IGZhbHNlO1xuICAgICAgdGhpcy5kYXRhU291cmNlLmRhdGFTdHJlYW0ubmV4dCh2YWx1ZSB8fCB0aGlzLmRhdGEpO1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvYWRpbmcgPSBmYWxzZTtcbiAgICAgIHRoaXMubm90aGluZ0ZvdW5kID0gdHJ1ZTtcbiAgICAgIGlmICh0aGlzLmRhdGFTb3VyY2UpIHtcbiAgICAgICAgdGhpcy5kYXRhU291cmNlLmRhdGFTdHJlYW0ubmV4dChbXSk7XG4gICAgICB9XG4gICAgfVxuICB9XG5cbiAgb25TY3JvbGwoKSB7XG4gICAgaWYgKCF0aGlzLnNjcm9sbGluZ0J5QXJyb3dzKSB7XG4gICAgICBjbGVhclRpbWVvdXQodGhpcy5pc1Njcm9sbGluZyk7XG4gICAgICB0aGlzLmlzU2Nyb2xsaW5nID0gc2V0VGltZW91dCgoKSA9PiB7XG4gICAgICAgIHRoaXMuc2V0SXRlbXNCeVNjcm9sbCgpO1xuICAgICAgICB0aGlzLnNldFNjcm9sbFRvcCgpO1xuICAgICAgICB0aGlzLmlzU2Nyb2xsRG93bigpID8gdGhpcy5zZXRTY3JvbGxCb3R0b21JdGVtKCkgOiB0aGlzLnNldFNjcm9sbFRvcEl0ZW0oKTtcbiAgICAgICAgdGhpcy5zZXRMYXN0U2Nyb2xsVG9wKCk7XG4gICAgICB9LCA2Nik7XG4gICAgfVxuICB9XG5cbiAgb25XaGVlbCgpIHtcbiAgICB0aGlzLnNjcm9sbGluZ0J5QXJyb3dzID0gZmFsc2U7XG4gIH1cblxuICBvbkZpbHRlcigkZXZlbnQpIHtcbiAgICBpZiAoJGV2ZW50KSB7XG4gICAgICB0aGlzLnNldFNjcm9sbFZpcnR1YWwoKTtcbiAgICAgIHRoaXMuc2V0VXBEYXRhKCRldmVudCk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRoaXMubG9hZGluZyA9IGZhbHNlO1xuICAgIHRoaXMubm90aGluZ0ZvdW5kID0gdHJ1ZTtcbiAgICB0aGlzLmRhdGFTb3VyY2UuZGF0YVN0cmVhbS5uZXh0KFtdKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0SXRlbUJ5SW5kZXgoaW5kZXg6IG51bWJlcikge1xuICAgIHJldHVybiB0aGlzLmxpc3RJdGVtcy5maWx0ZXIoKGl0ZW0pID0+IGl0ZW0uaW5kZXhTZWxlY3RlZCA9PT0gaW5kZXgpWzBdO1xuICB9XG5cbiAgcHJpdmF0ZSBzZXRTY3JvbGxUb3BJdGVtKCkge1xuICAgIHRoaXMuc2V0U2VsZWN0ZWQodGhpcy5nZXRJdGVtQnlJbmRleCh0aGlzLml0ZW1zQnlTY3JvbGwpKTtcbiAgfVxuXG4gIHByaXZhdGUgc2V0TGFzdFNjcm9sbFRvcCgpIHtcbiAgICB0aGlzLmxhc3RTY3JvbGxUb3AgPSB0aGlzLnNjcm9sbFRvcDtcbiAgfVxuXG4gIHByaXZhdGUgc2V0U2Nyb2xsQm90dG9tSXRlbSgpIHtcbiAgICBjb25zdCBpbmRleCA9IE1hdGgucm91bmQodGhpcy5pdGVtc0J5Um93U2V0ICsgdGhpcy5pdGVtc0J5U2Nyb2xsKSAtIDE7XG4gICAgdGhpcy5zZXRTZWxlY3RlZCh0aGlzLmdldEl0ZW1CeUluZGV4KGluZGV4KSk7XG4gIH1cblxuICBwcml2YXRlIHNldElucHV0Rm9jdXMoKSB7XG4gICAgaWYgKCB0aGlzLmlucHV0RWxlbWVudCApIHtcbiAgICAgIHRoaXMuaW5wdXRFbGVtZW50LmZvY3VzKCk7XG4gICAgfVxuICB9XG5cbiAgcHJpdmF0ZSBzZXRBY3RpdmVJdGVtKCkge1xuICAgIHRoaXMuYWN0aXZlSXRlbSA9IHRoaXMubGlzdEtleU1hbmFnZXIuYWN0aXZlSXRlbSBhcyBUbEl0ZW1TZWxlY3RlZERpcmVjdGl2ZTtcbiAgfVxuXG4gIGhhbmRsZUtleUFycm93VXAoJGV2ZW50KSB7XG4gICAgdGhpcy5zY3JvbGxpbmdCeUFycm93cyA9IHRydWU7XG4gICAgdGhpcy5saXN0S2V5TWFuYWdlci5vbktleWRvd24oJGV2ZW50KTtcbiAgICB0aGlzLnNldEFjdGl2ZUl0ZW0oKTtcbiAgICBzY3JvbGxJbnRvVmlldyh0aGlzLmFjdGl2ZUl0ZW0uZWxlbWVudC5uYXRpdmVFbGVtZW50KTtcbiAgICB0aGlzLmNoYW5nZS5kZXRlY3RDaGFuZ2VzKCk7XG4gIH1cblxuICBoYW5kbGVLZXlBcnJvd0Rvd24oJGV2ZW50KSB7XG4gICAgdGhpcy5zY3JvbGxpbmdCeUFycm93cyA9IHRydWU7XG4gICAgdGhpcy5saXN0S2V5TWFuYWdlci5vbktleWRvd24oJGV2ZW50KTtcbiAgICB0aGlzLnNldEFjdGl2ZUl0ZW0oKTtcbiAgICBzY3JvbGxJbnRvVmlldyh0aGlzLmFjdGl2ZUl0ZW0uZWxlbWVudC5uYXRpdmVFbGVtZW50KTtcbiAgICB0aGlzLmNoYW5nZS5kZXRlY3RDaGFuZ2VzKCk7XG4gIH1cblxuICBwcml2YXRlIGlzU2Nyb2xsRG93bigpIHtcbiAgICByZXR1cm4gdGhpcy5zY3JvbGxUb3AgPiB0aGlzLmxhc3RTY3JvbGxUb3A7XG4gIH1cblxuICBuZ09uQ2hhbmdlcyh7ZGF0YX06IFNpbXBsZUNoYW5nZXMpIHtcbiAgICBpZiAoIGRhdGEgJiYgIWRhdGEuZmlyc3RDaGFuZ2UgJiYgZGF0YS5jdXJyZW50VmFsdWUubGVuZ3RoID09PSAwICkge1xuICAgICAgdGhpcy5sb2FkaW5nID0gZmFsc2U7XG4gICAgICB0aGlzLm5vdGhpbmdGb3VuZCA9IHRydWU7XG4gICAgICB0aGlzLmNoYW5nZS5kZXRlY3RDaGFuZ2VzKCk7XG4gICAgfVxuICB9XG5cbiAgbmdPbkRlc3Ryb3koKSB7XG4gICAgdGhpcy5zdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTtcbiAgICB0aGlzLmNoYW5nZS5kZXRhY2goKTtcbiAgfVxuXG59XG4iLCI8ZGl2IGNsYXNzPVwidWktbGlzdGJveC13cmFwcGVyXCIgW3N0eWxlLmhlaWdodF09XCJoZWlnaHRcIj5cbiAgPGRpdiBjbGFzcz1cInVpLWVtcHR5bGlzdFwiICpuZ0lmPVwibG9hZGluZyB8fCBub3RoaW5nRm91bmRcIj5cbiAgICA8ZGl2ICpuZ0lmPVwibG9hZGluZyAmJiAhbm90aGluZ0ZvdW5kXCIgY2xhc3M9XCJ1aS1sb2FkaW5nXCIgW3N0eWxlLndpZHRoXT1cIic1MHB4J1wiIFtzdHlsZS5oZWlnaHRdPVwiJzUwcHgnXCI+XG4gICAgICA8dGwtbG9hZGVyPjwvdGwtbG9hZGVyPlxuICAgIDwvZGl2PlxuICAgIDxkaXYgKm5nSWY9XCJub3RoaW5nRm91bmQgJiYgIWxvYWRpbmdcIiBjbGFzcz1cInVpLW5vdGZvdW5kXCI+XG4gICAgICA8c3BhbiBjbGFzcz1cIm1lc3NhZ2VcIj57eyBub3RoaW5nRm91bmRNZXNzYWdlIH19PC9zcGFuPlxuICAgICAgPG5nLWNvbnRhaW5lciAqbmdUZW1wbGF0ZU91dGxldD1cInRlbXBsYXRlTm90Rm91bmRcIj48L25nLWNvbnRhaW5lcj5cbiAgICA8L2Rpdj5cbiAgPC9kaXY+XG4gIDxjZGstdmlydHVhbC1zY3JvbGwtdmlld3BvcnQgW2l0ZW1TaXplXT1cInJvd0hlaWdodFwiIFtjbGFzc109XCIndWktbGlzdC12aWV3cG9ydCAnICsgY29sb3JcIiB0YWJpbmRleD1cIi0xXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBbc3R5bGUuaGVpZ2h0XT1cImhlaWdodFwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHdoZWVsKT1cIm9uV2hlZWwoKVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKHNjcm9sbGVkSW5kZXhDaGFuZ2UpPVwib25TY3JvbGwoKVwiPlxuICAgIDx0bC1maWx0ZXItY29udGFpbmVyIFtzZWFyY2hUZXJtXT1cInNlYXJjaENvbnRyb2w/LnZhbHVlXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICBbZGVib3VuY2VUaW1lXT1cImRlYm91bmNlVGltZVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgW3NvdXJjZV09XCJkYXRhXCJcbiAgICAgICAgICAgICAgICAgICAgICAgICBbc2VhcmNoQnldPVwic2VhcmNoQnlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgIChmaWx0ZXIpPVwib25GaWx0ZXIoJGV2ZW50KVwiPlxuICAgICAgPGRpdiBbaXRlbVNlbGVjdGVkXT1cIml0ZW1cIiAjc2VsZWN0PVwic2VsZWN0SXRlbVwiXG4gICAgICAgICAgIFtuZ1N0eWxlXT1cInsgaGVpZ2h0OiByb3dIZWlnaHQgKyAncHgnIH1cIlxuICAgICAgICAgICBbaW5kZXhTZWxlY3RlZF09XCJpXCJcbiAgICAgICAgICAgW3N0eWxlLnBhZGRpbmddPVwiIXRlbXBsYXRlID8gJzAgMTBweCcgOiAnMCdcIlxuICAgICAgICAgICBbYXR0ci5pbmRleF09XCJpXCJcbiAgICAgICAgICAgKmNka1ZpcnR1YWxGb3I9XCJsZXQgaXRlbSBvZiBkYXRhU291cmNlOyBsZXQgaSA9IGluZGV4XCJcbiAgICAgICAgICAgKGNsaWNrKT1cInNldFNlbGVjdGVkKHNlbGVjdCk7IG9uQ2xpY2tJdGVtKGl0ZW0pXCJcbiAgICAgICAgICAgY2xhc3M9XCJ1aS1saXN0LXZpZXdwb3J0LWl0ZW1cIj5cbiAgICAgICAgPGRpdiAqbmdJZj1cIiF0ZW1wbGF0ZVwiPlxuICAgICAgICAgIHt7IGl0ZW1ba2V5VGV4dF0gfX1cbiAgICAgICAgPC9kaXY+XG4gICAgICAgIDx0bC1saXN0Ym94LXRlbXBsYXRlXG4gICAgICAgICAgW3N0eWxlLndpZHRoXT1cIicxMDAlJ1wiXG4gICAgICAgICAgKm5nSWY9XCJ0ZW1wbGF0ZVwiXG4gICAgICAgICAgW3RlbXBsYXRlXT1cInRlbXBsYXRlXCJcbiAgICAgICAgICBbaXRlbV09XCJpdGVtXCJcbiAgICAgICAgICBbaW5kZXhdPVwiaVwiPlxuICAgICAgICA8L3RsLWxpc3Rib3gtdGVtcGxhdGU+XG4gICAgICA8L2Rpdj5cbiAgICA8L3RsLWZpbHRlci1jb250YWluZXI+XG4gIDwvY2RrLXZpcnR1YWwtc2Nyb2xsLXZpZXdwb3J0PlxuPC9kaXY+XG4iXX0=