UNPKG

@nova-ui/bits

Version:

SolarWinds Nova Framework

284 lines 48.8 kB
// © 2022 SolarWinds Worldwide, LLC. All rights reserved. // // 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 { CdkVirtualForOf, CdkVirtualScrollViewport, } from "@angular/cdk/scrolling"; import { ContentChild, Directive, Input, Renderer2, } from "@angular/core"; import isBoolean from "lodash/isBoolean"; import isEmpty from "lodash/isEmpty"; import { asyncScheduler, EMPTY, merge, Subject } from "rxjs"; import { delay, exhaustMap, filter, finalize, map, take, takeUntil, tap, throttleTime, } from "rxjs/operators"; import { FIXED_WIDTH_CLASS } from "../constants"; import { TableComponent } from "../table.component"; import * as i0 from "@angular/core"; import * as i1 from "@angular/cdk/scrolling"; export var TableVirtualScrollHeaderPosition; (function (TableVirtualScrollHeaderPosition) { TableVirtualScrollHeaderPosition[TableVirtualScrollHeaderPosition["Native"] = 0] = "Native"; TableVirtualScrollHeaderPosition[TableVirtualScrollHeaderPosition["Sticky"] = 1] = "Sticky"; })(TableVirtualScrollHeaderPosition || (TableVirtualScrollHeaderPosition = {})); export class TableStickyHeaderDirective { set tableStickyHeader(isSticky) { if (!isBoolean(isSticky)) { return; } this._sticky = isSticky; // Note: We need to have all properties set // before proceeding with table head movements. if (!this.isInitialized) { return; } this.updateHeadPosition(isSticky); } get viewportEl() { return this.viewport.elementRef.nativeElement; } get isInitialized() { return !!this.tableElRef; } constructor(renderer, viewport) { this.renderer = renderer; this.viewport = viewport; this._sticky = true; this.unsubscribe$ = new Subject(); this.updateContainerToFitHead = () => { if (this._sticky) { this.origViewportHeight = this.origViewportHeight || this.viewportEl?.offsetHeight; const viewportComputedHeight = isEmpty(this.userProvidedHeight) ? this.origViewportHeight + "px" : this.userProvidedHeight; this.viewportEl.style.setProperty("height", `calc(${viewportComputedHeight} - ${this.headRef?.rows.item(0)?.offsetHeight ?? 0}px)`, "important"); } }; this.handleColumnsUpdate$ = () => { if (this.table.resizable) { return EMPTY; } // TODO: Perform a dirty check before starting assigning new values // Note: Setting the width of stickyHeadContainer container to be able to simulate horizontal scroll of the sticky header this.renderer.setStyle(this.stickyHeadContainer, "width", `${this.viewport._contentWrapper.nativeElement.scrollWidth}px`); const headColumns = Array.from(this.stickyHeadContainer?.getElementsByTagName("th") || []); const firstDataRowCells = Array.from(this.bodyRef?.rows.item(0)?.cells || []); // Note: If head columns are not in sync with data columns skip if (headColumns.length !== firstDataRowCells.length) { return EMPTY; } // TODO: Find a better way to pair placeholderHeader columns with header columns firstDataRowCells.forEach((cell, index) => { const fixedWidth = headColumns[index].classList.contains(FIXED_WIDTH_CLASS); if (!fixedWidth) { // Note: Assigning data cell width to the corresponding header column // (using the style width if specified; otherwise, falling back to the offsetWidth) headColumns[index].style.width = cell.style.width || `${cell.offsetWidth}px`; } }); // update the header placeholder to match the updated column widths this.updateNativeHeaderPlaceholder(); // Note: Returning empty observable to be able to create an execution queue return EMPTY; }; } ngAfterViewInit() { this.assignRequiredProperties(); // TODO: Find a better way to identify when the table header are rendered properly // Waiting for the next tick to let cdk table properly draw the table header setTimeout(() => this.updateNativeHeaderPlaceholder()); this.updateHeadPosition(this._sticky); } ngOnDestroy() { this.unsubscribe$.next(); this.unsubscribe$.complete(); if (this.headResizeObserver) { this.headResizeObserver.disconnect(); } } setNative() { if (this.headPosition === TableVirtualScrollHeaderPosition.Native) { console.warn("Already in native mode"); return; } // Note: Moving the thead back to the table this.renderer.insertBefore(this.tableElRef, this.headRef, this.bodyRef); // Note: Unsubscribing from potential subscriptions generated by stickyMode. this.unsubscribe$.next(); // Note: Restoring user provided height if (isEmpty(this.userProvidedHeight)) { this.viewportEl.style.removeProperty("height"); } else { this.viewportEl.style.setProperty("height", this.userProvidedHeight); } this.headPosition = TableVirtualScrollHeaderPosition.Native; } setSticky() { if (this.headPosition === TableVirtualScrollHeaderPosition.Sticky) { console.warn("Already in sticky mode"); return; } if (!this.stickyHeadContainer) { this.createStickyHeaderContainer(); } // Note: Moving the table head into sticky container this.renderer.appendChild(this.stickyHeadContainer, this.headRef); this.syncHorizontalScroll(); this.syncColumnWidths(); // Note: While we're detaching header from CDK Viewport we have // to recalculate viewport height to keep the same total height. // The setTimeout is for skipping one tick to let the header get his height. setTimeout(() => this.updateContainerToFitHead()); this.updateViewportHeightOnHeadResize(); this.headPosition = TableVirtualScrollHeaderPosition.Sticky; } updateViewportHeightOnHeadResize() { if (this.headResizeObserver) { return; } // This resize observer is needed in case a parent element has a height of zero upon instantiation // thereby prohibiting the header from having its intended height when its initially rendered. if (this.headRef) { this.headResizeObserver = new ResizeObserver((entries) => // We wrap this in requestAnimationFrame to avoid "ResizeObserver loop limit exceeded" error in unit tests // https://stackoverflow.com/questions/49384120/resizeobserver-loop-limit-exceeded window.requestAnimationFrame(() => { if (!Array.isArray(entries) || !entries.length) { return; } this.updateContainerToFitHead(); })); this.headResizeObserver.observe(this.headRef); } } assignRequiredProperties() { this.tableElRef = this.viewportEl.getElementsByTagName("table").item(0) || undefined; this.headRef = this.viewportEl.getElementsByTagName("thead").item(0) || undefined; this.userProvidedHeight = this.viewportEl.style.height; // Disable animation for resizable sticky header cells to prevent a lagging effect during width changes. if (!this.table.resizable) { Array.from(this.headRef?.getElementsByTagName("th") || []).forEach((th) => th.classList.add("virtual-sticky")); } this.bodyRef = this.viewportEl.getElementsByTagName("tbody").item(0) || undefined; } syncColumnWidths() { const resize$ = new Subject(); // Note: Passing the resize event to resize$ subject to be able // to handle all the columnWidth update trigger in a single stream const resizeObserver = new ResizeObserver(() => { resize$.next(); }); resizeObserver.observe(this.viewportEl); this.unsubscribe$ .pipe(take(1), tap(() => resize$.complete())) .subscribe(); const onResize$ = resize$.pipe( // Note: Performing the resizeObserver cleanup finalize(() => resizeObserver.disconnect())); const onScroll$ = this.viewport.elementScrolled().pipe( // Note: Reducing the number of times function is invoked // by scheduling the scroll events via trailing throttling throttleTime(50, asyncScheduler, { trailing: true })); const tableColumnsUpdate$ = merge(this.table.columnsOrderChange, this.table.columnsWidthChange, this.table._contentColumnDefs.changes).pipe( // Note: Using delay(0) to grant some time to the table // to update the rows and then proceed with the event delay(0), // Note: Reattaching native header on every columns changes tap(() => this.updateNativeHeaderPlaceholder())); if (!this.virtualFor) { throw new Error("Unable to find CdkVirtualForOf"); } merge(onScroll$, onResize$, tableColumnsUpdate$, this.virtualFor.dataStream) .pipe( // Note: Preventing function to be invoked multiple times // by merging new observable only if the previous one was completed exhaustMap(this.handleColumnsUpdate$), takeUntil(this.unsubscribe$)) .subscribe(); } syncHorizontalScroll() { let previousScrollLeft = 0; this.viewport .elementScrolled() .pipe(map(() => this.viewportEl.scrollLeft), // Note: Filtering out vertical scroll events filter((scrollLeft) => scrollLeft !== previousScrollLeft), tap((scrollLeft) => { previousScrollLeft = scrollLeft; // Note: Simulating horizontal scroll by assigning margin-left to be equal to scrolled distance this.renderer.setStyle(this.stickyHeadContainer, "margin-left", `-${scrollLeft}px`); }), takeUntil(this.unsubscribe$)) .subscribe(); } createStickyHeaderContainer() { this.stickyHeadContainer = this.renderer.createElement("div"); const wrapper = this.renderer.createElement("div"); this.renderer.appendChild(wrapper, this.stickyHeadContainer); this.renderer.setStyle(wrapper, "overflow-x", `hidden`); this.renderer.setStyle(wrapper, "width", `100%`); // Assigning original table classes const originalTableClasses = Array.from(this.tableElRef?.classList || []); originalTableClasses.push("sticky-table-header-container"); originalTableClasses.forEach((cssClass) => this.renderer.addClass(this.stickyHeadContainer, cssClass)); this.renderer.insertBefore(this.viewportEl.parentElement, wrapper, this.viewportEl); } updateNativeHeaderPlaceholder() { if (!this.headRef || !this.tableElRef || !this.bodyRef) { throw new Error("Can't append thead placeholder. TableRef, BodyRef or HeaderRef is undefined"); } const headPlaceholder = this.tableElRef.getElementsByTagName("thead")[0]; if (headPlaceholder) { headPlaceholder.remove(); } const theadPlaceholder = this.headRef.cloneNode(true); // Note: making header invisible this.renderer.setStyle(theadPlaceholder, "visibility", "collapse"); // Note: Adding an identifier for the header placeholder to avoid confusion this.renderer.addClass(theadPlaceholder, "sticky-header-placeholder"); // Note: Appending head placeholder to the table this.renderer.insertBefore(this.tableElRef, theadPlaceholder, this.bodyRef); } updateHeadPosition(isSticky) { if (!isSticky) { this.setNative(); return; } this.setSticky(); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TableStickyHeaderDirective, deps: [{ token: i0.Renderer2 }, { token: i1.CdkVirtualScrollViewport }], target: i0.ɵɵFactoryTarget.Directive }); } static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "17.3.12", type: TableStickyHeaderDirective, selector: "cdk-virtual-scroll-viewport[tableStickyHeader]", inputs: { tableStickyHeader: "tableStickyHeader" }, host: { properties: { "class.sticky-table-header": "true" }, styleAttribute: "overflow-y:overlay" }, queries: [{ propertyName: "table", first: true, predicate: TableComponent, descendants: true }, { propertyName: "virtualFor", first: true, predicate: CdkVirtualForOf, descendants: true }], ngImport: i0 }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: TableStickyHeaderDirective, decorators: [{ type: Directive, args: [{ selector: "cdk-virtual-scroll-viewport[tableStickyHeader]", host: { "[class.sticky-table-header]": "true", style: "overflow-y:overlay", }, }] }], ctorParameters: () => [{ type: i0.Renderer2 }, { type: i1.CdkVirtualScrollViewport }], propDecorators: { table: [{ type: ContentChild, args: [TableComponent] }], virtualFor: [{ type: ContentChild, args: [CdkVirtualForOf] }], tableStickyHeader: [{ type: Input }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFibGUtc3RpY2t5LWhlYWRlci5kaXJlY3RpdmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvbGliL3RhYmxlL3RhYmxlLXZpcnR1YWwtc2Nyb2xsL3RhYmxlLXN0aWNreS1oZWFkZXIuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLHlEQUF5RDtBQUN6RCxFQUFFO0FBQ0YsK0VBQStFO0FBQy9FLDRFQUE0RTtBQUM1RSw4RUFBOEU7QUFDOUUsK0VBQStFO0FBQy9FLDhFQUE4RTtBQUM5RSw0REFBNEQ7QUFDNUQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSx1REFBdUQ7QUFDdkQsRUFBRTtBQUNGLDZFQUE2RTtBQUM3RSw0RUFBNEU7QUFDNUUsK0VBQStFO0FBQy9FLDBFQUEwRTtBQUMxRSxpRkFBaUY7QUFDakYsNkVBQTZFO0FBQzdFLGlCQUFpQjtBQUVqQixPQUFPLEVBQ0gsZUFBZSxFQUNmLHdCQUF3QixHQUMzQixNQUFNLHdCQUF3QixDQUFDO0FBQ2hDLE9BQU8sRUFFSCxZQUFZLEVBQ1osU0FBUyxFQUNULEtBQUssRUFFTCxTQUFTLEdBQ1osTUFBTSxlQUFlLENBQUM7QUFDdkIsT0FBTyxTQUFTLE1BQU0sa0JBQWtCLENBQUM7QUFDekMsT0FBTyxPQUFPLE1BQU0sZ0JBQWdCLENBQUM7QUFDckMsT0FBTyxFQUFFLGNBQWMsRUFBRSxLQUFLLEVBQUUsS0FBSyxFQUFjLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUN6RSxPQUFPLEVBQ0gsS0FBSyxFQUNMLFVBQVUsRUFDVixNQUFNLEVBQ04sUUFBUSxFQUNSLEdBQUcsRUFDSCxJQUFJLEVBQ0osU0FBUyxFQUNULEdBQUcsRUFDSCxZQUFZLEdBQ2YsTUFBTSxnQkFBZ0IsQ0FBQztBQUV4QixPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFFakQsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLG9CQUFvQixDQUFD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O21HQUExQiwwQkFBMEIsa1JBQ3JCLGNBQWMsNkVBQ2QsZUFBZTs7NEZBRnBCLDBCQUEwQjtrQkFQdEMsU0FBUzttQkFBQztvQkFDUCxRQUFRLEVBQUUsZ0RBQWdEO29CQUMxRCxJQUFJLEVBQUU7d0JBQ0YsNkJBQTZCLEVBQUUsTUFBTTt3QkFDckMsS0FBSyxFQUFFLG9CQUFvQjtxQkFDOUI7aUJBQ0o7cUhBRXdDLEtBQUs7c0JBQXpDLFlBQVk7dUJBQUMsY0FBYztnQkFDVSxVQUFVO3NCQUEvQyxZQUFZO3VCQUFDLGVBQWU7Z0JBR2xCLGlCQUFpQjtzQkFEM0IsS0FBSyIsInNvdXJjZXNDb250ZW50IjpbIi8vIMKpIDIwMjIgU29sYXJXaW5kcyBXb3JsZHdpZGUsIExMQy4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cbi8vXG4vLyBQZXJtaXNzaW9uIGlzIGhlcmVieSBncmFudGVkLCBmcmVlIG9mIGNoYXJnZSwgdG8gYW55IHBlcnNvbiBvYnRhaW5pbmcgYSBjb3B5XG4vLyAgb2YgdGhpcyBzb2Z0d2FyZSBhbmQgYXNzb2NpYXRlZCBkb2N1bWVudGF0aW9uIGZpbGVzICh0aGUgXCJTb2Z0d2FyZVwiKSwgdG9cbi8vICBkZWFsIGluIHRoZSBTb2Z0d2FyZSB3aXRob3V0IHJlc3RyaWN0aW9uLCBpbmNsdWRpbmcgd2l0aG91dCBsaW1pdGF0aW9uIHRoZVxuLy8gIHJpZ2h0cyB0byB1c2UsIGNvcHksIG1vZGlmeSwgbWVyZ2UsIHB1Ymxpc2gsIGRpc3RyaWJ1dGUsIHN1YmxpY2Vuc2UsIGFuZC9vclxuLy8gIHNlbGwgY29waWVzIG9mIHRoZSBTb2Z0d2FyZSwgYW5kIHRvIHBlcm1pdCBwZXJzb25zIHRvIHdob20gdGhlIFNvZnR3YXJlIGlzXG4vLyAgZnVybmlzaGVkIHRvIGRvIHNvLCBzdWJqZWN0IHRvIHRoZSBmb2xsb3dpbmcgY29uZGl0aW9uczpcbi8vXG4vLyBUaGUgYWJvdmUgY29weXJpZ2h0IG5vdGljZSBhbmQgdGhpcyBwZXJtaXNzaW9uIG5vdGljZSBzaGFsbCBiZSBpbmNsdWRlZCBpblxuLy8gIGFsbCBjb3BpZXMgb3Igc3Vic3RhbnRpYWwgcG9ydGlvbnMgb2YgdGhlIFNvZnR3YXJlLlxuLy9cbi8vIFRIRSBTT0ZUV0FSRSBJUyBQUk9WSURFRCBcIkFTIElTXCIsIFdJVEhPVVQgV0FSUkFOVFkgT0YgQU5ZIEtJTkQsIEVYUFJFU1MgT1Jcbi8vICBJTVBMSUVELCBJTkNMVURJTkcgQlVUIE5PVCBMSU1JVEVEIFRPIFRIRSBXQVJSQU5USUVTIE9GIE1FUkNIQU5UQUJJTElUWSxcbi8vICBGSVRORVNTIEZPUiBBIFBBUlRJQ1VMQVIgUFVSUE9TRSBBTkQgTk9OSU5GUklOR0VNRU5ULiBJTiBOTyBFVkVOVCBTSEFMTCBUSEVcbi8vICBBVVRIT1JTIE9SIENPUFlSSUdIVCBIT0xERVJTIEJFIExJQUJMRSBGT1IgQU5ZIENMQUlNLCBEQU1BR0VTIE9SIE9USEVSXG4vLyAgTElBQklMSVRZLCBXSEVUSEVSIElOIEFOIEFDVElPTiBPRiBDT05UUkFDVCwgVE9SVCBPUiBPVEhFUldJU0UsIEFSSVNJTkcgRlJPTSxcbi8vICBPVVQgT0YgT1IgSU4gQ09OTkVDVElPTiBXSVRIIFRIRSBTT0ZUV0FSRSBPUiBUSEUgVVNFIE9SIE9USEVSIERFQUxJTkdTIElOXG4vLyAgVEhFIFNPRlRXQVJFLlxuXG5pbXBvcnQge1xuICAgIENka1ZpcnR1YWxGb3JPZixcbiAgICBDZGtWaXJ0dWFsU2Nyb2xsVmlld3BvcnQsXG59IGZyb20gXCJAYW5ndWxhci9jZGsvc2Nyb2xsaW5nXCI7XG5pbXBvcnQge1xuICAgIEFmdGVyVmlld0luaXQsXG4gICAgQ29udGVudENoaWxkLFxuICAgIERpcmVjdGl2ZSxcbiAgICBJbnB1dCxcbiAgICBPbkRlc3Ryb3ksXG4gICAgUmVuZGVyZXIyLFxufSBmcm9tIFwiQGFuZ3VsYXIvY29yZVwiO1xuaW1wb3J0IGlzQm9vbGVhbiBmcm9tIFwibG9kYXNoL2lzQm9vbGVhblwiO1xuaW1wb3J0IGlzRW1wdHkgZnJvbSBcImxvZGFzaC9pc0VtcHR5XCI7XG5pbXBvcnQgeyBhc3luY1NjaGVkdWxlciwgRU1QVFksIG1lcmdlLCBPYnNlcnZhYmxlLCBTdWJqZWN0IH0gZnJvbSBcInJ4anNcIjtcbmltcG9ydCB7XG4gICAgZGVsYXksXG4gICAgZXhoYXVzdE1hcCxcbiAgICBmaWx0ZXIsXG4gICAgZmluYWxpemUsXG4gICAgbWFwLFxuICAgIHRha2UsXG4gICAgdGFrZVVudGlsLFxuICAgIHRhcCxcbiAgICB0aHJvdHRsZVRpbWUsXG59IGZyb20gXCJyeGpzL29wZXJhdG9yc1wiO1xuXG5pbXBvcnQgeyBGSVhFRF9XSURUSF9DTEFTUyB9IGZyb20gXCIuLi9jb25zdGFudHNcIjtcbmltcG9ydCB7IFRhYmxlU3RhdGVIYW5kbGVyU2VydmljZSB9IGZyb20gXCIuLi90YWJsZS1zdGF0ZS1oYW5kbGVyLnNlcnZpY2VcIjtcbmltcG9ydCB7IFRhYmxlQ29tcG9uZW50IH0gZnJvbSBcIi4uL3RhYmxlLmNvbXBvbmVudFwiO1xuXG5leHBvcnQgZW51bSBUYWJsZVZpcnR1YWxTY3JvbGxIZWFkZXJQb3NpdGlvbiB7XG4gICAgTmF0aXZlLFxuICAgIFN0aWNreSxcbn1cblxuQERpcmVjdGl2ZSh7XG4gICAgc2VsZWN0b3I6IFwiY2RrLXZpcnR1YWwtc2Nyb2xsLXZpZXdwb3J0W3RhYmxlU3RpY2t5SGVhZGVyXVwiLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgXCJbY2xhc3Muc3RpY2t5LXRhYmxlLWhlYWRlcl1cIjogXCJ0cnVlXCIsXG4gICAgICAgIHN0eWxlOiBcIm92ZXJmbG93LXk6b3ZlcmxheVwiLFxuICAgIH0sXG59KVxuZXhwb3J0IGNsYXNzIFRhYmxlU3RpY2t5SGVhZGVyRGlyZWN0aXZlIGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgT25EZXN0cm95IHtcbiAgICBAQ29udGVudENoaWxkKFRhYmxlQ29tcG9uZW50KSBwdWJsaWMgdGFibGU6IFRhYmxlQ29tcG9uZW50PHVua25vd24+O1xuICAgIEBDb250ZW50Q2hpbGQoQ2RrVmlydHVhbEZvck9mKSBwdWJsaWMgdmlydHVhbEZvcjogQ2RrVmlydHVhbEZvck9mPHVua25vd24+O1xuXG4gICAgQElucHV0KClcbiAgICBwdWJsaWMgc2V0IHRhYmxlU3RpY2t5SGVhZGVyKGlzU3RpY2t5OiBib29sZWFuKSB7XG4gICAgICAgIGlmICghaXNCb29sZWFuKGlzU3RpY2t5KSkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy5fc3RpY2t5ID0gaXNTdGlja3k7XG5cbiAgICAgICAgLy8gTm90ZTogV2UgbmVlZCB0byBoYXZlIGFsbCBwcm9wZXJ0aWVzIHNldFxuICAgICAgICAvLyBiZWZvcmUgcHJvY2VlZGluZyB3aXRoIHRhYmxlIGhlYWQgbW92ZW1lbnRzLlxuICAgICAgICBpZiAoIXRoaXMuaXNJbml0aWFsaXplZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgdGhpcy51cGRhdGVIZWFkUG9zaXRpb24oaXNTdGlja3kpO1xuICAgIH1cblxuICAgIHByaXZhdGUgX3N0aWNreTogYm9vbGVhbiA9IHRydWU7XG4gICAgcHJpdmF0ZSBoZWFkUG9zaXRpb246IFRhYmxlVmlydHVhbFNjcm9sbEhlYWRlclBvc2l0aW9uO1xuXG4gICAgcHJpdmF0ZSBzdGlja3lIZWFkQ29udGFpbmVyPzogSFRNTEVsZW1lbnQ7IC8vIEFjdHVhbCB0aGVhZCBjb250YWluZXJcbiAgICBwcml2YXRlIGhlYWRSZWY/OiBIVE1MVGFibGVTZWN0aW9uRWxlbWVudDtcbiAgICBwcml2YXRlIGJvZHlSZWY/OiBIVE1MVGFibGVTZWN0aW9uRWxlbWVudDtcbiAgICBwcml2YXRlIHRhYmxlRWxSZWY/OiBIVE1MVGFibGVFbGVtZW50O1xuICAgIHByaXZhdGUgdXNlclByb3ZpZGVkSGVpZ2h0OiBzdHJpbmc7XG5cbiAgICBwcml2YXRlIHJlYWRvbmx5IHVuc3Vic2NyaWJlJCA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG4gICAgcHJpdmF0ZSBoZWFkUmVzaXplT2JzZXJ2ZXI6IFJlc2l6ZU9ic2VydmVyO1xuICAgIC8vIHRoaXMgaXMgZm9yIGtlZXBpbmcgdHJhY2sgb2YgdGhlIG9yaWdpbmFsIHZpZXdwb3J0IGhlaWdodCBvbiBoZWFkIHJlc2l6ZVxuICAgIHByaXZhdGUgb3JpZ1ZpZXdwb3J0SGVpZ2h0OiBudW1iZXI7XG5cbiAgICBwcml2YXRlIGdldCB2aWV3cG9ydEVsKCk6IEhUTUxFbGVtZW50IHtcbiAgICAgICAgcmV0dXJuIHRoaXMudmlld3BvcnQuZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50O1xuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0IGlzSW5pdGlhbGl6ZWQoKTogYm9vbGVhbiB7XG4gICAgICAgIHJldHVybiAhIXRoaXMudGFibGVFbFJlZjtcbiAgICB9XG5cbiAgICBjb25zdHJ1Y3RvcihcbiAgICAgICAgcHJpdmF0ZSByZW5kZXJlcjogUmVuZGVyZXIyLFxuICAgICAgICBwcml2YXRlIHZpZXdwb3J0OiBDZGtWaXJ0dWFsU2Nyb2xsVmlld3BvcnRcbiAgICApIHt9XG5cbiAgICBwdWJsaWMgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICB0aGlzLmFzc2lnblJlcXVpcmVkUHJvcGVydGllcygpO1xuICAgICAgICAvLyBUT0RPOiBGaW5kIGEgYmV0dGVyIHdheSB0byBpZGVudGlmeSB3aGVuIHRoZSB0YWJsZSBoZWFkZXIgYXJlIHJlbmRlcmVkIHByb3Blcmx5XG4gICAgICAgIC8vIFdhaXRpbmcgZm9yIHRoZSBuZXh0IHRpY2sgdG8gbGV0IGNkayB0YWJsZSBwcm9wZXJseSBkcmF3IHRoZSB0YWJsZSBoZWFkZXJcbiAgICAgICAgc2V0VGltZW91dCgoKSA9PiB0aGlzLnVwZGF0ZU5hdGl2ZUhlYWRlclBsYWNlaG9sZGVyKCkpO1xuICAgICAgICB0aGlzLnVwZGF0ZUhlYWRQb3NpdGlvbih0aGlzLl9zdGlja3kpO1xuICAgIH1cblxuICAgIHB1YmxpYyBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICAgICAgdGhpcy51bnN1YnNjcmliZSQubmV4dCgpO1xuICAgICAgICB0aGlzLnVuc3Vic2NyaWJlJC5jb21wbGV0ZSgpO1xuXG4gICAgICAgIGlmICh0aGlzLmhlYWRSZXNpemVPYnNlcnZlcikge1xuICAgICAgICAgICAgdGhpcy5oZWFkUmVzaXplT2JzZXJ2ZXIuZGlzY29ubmVjdCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHNldE5hdGl2ZSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuaGVhZFBvc2l0aW9uID09PSBUYWJsZVZpcnR1YWxTY3JvbGxIZWFkZXJQb3NpdGlvbi5OYXRpdmUpIHtcbiAgICAgICAgICAgIGNvbnNvbGUud2FybihcIkFscmVhZHkgaW4gbmF0aXZlIG1vZGVcIik7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBOb3RlOiBNb3ZpbmcgdGhlIHRoZWFkIGJhY2sgdG8gdGhlIHRhYmxlXG4gICAgICAgIHRoaXMucmVuZGVyZXIuaW5zZXJ0QmVmb3JlKHRoaXMudGFibGVFbFJlZiwgdGhpcy5oZWFkUmVmLCB0aGlzLmJvZHlSZWYpO1xuICAgICAgICAvLyBOb3RlOiBVbnN1YnNjcmliaW5nIGZyb20gcG90ZW50aWFsIHN1YnNjcmlwdGlvbnMgZ2VuZXJhdGVkIGJ5IHN0aWNreU1vZGUuXG4gICAgICAgIHRoaXMudW5zdWJzY3JpYmUkLm5leHQoKTtcblxuICAgICAgICAvLyBOb3RlOiBSZXN0b3JpbmcgdXNlciBwcm92aWRlZCBoZWlnaHRcbiAgICAgICAgaWYgKGlzRW1wdHkodGhpcy51c2VyUHJvdmlkZWRIZWlnaHQpKSB7XG4gICAgICAgICAgICB0aGlzLnZpZXdwb3J0RWwuc3R5bGUucmVtb3ZlUHJvcGVydHkoXCJoZWlnaHRcIik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICB0aGlzLnZpZXdwb3J0RWwuc3R5bGUuc2V0UHJvcGVydHkoXG4gICAgICAgICAgICAgICAgXCJoZWlnaHRcIixcbiAgICAgICAgICAgICAgICB0aGlzLnVzZXJQcm92aWRlZEhlaWdodFxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuaGVhZFBvc2l0aW9uID0gVGFibGVWaXJ0dWFsU2Nyb2xsSGVhZGVyUG9zaXRpb24uTmF0aXZlO1xuICAgIH1cblxuICAgIHB1YmxpYyBzZXRTdGlja3koKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLmhlYWRQb3NpdGlvbiA9PT0gVGFibGVWaXJ0dWFsU2Nyb2xsSGVhZGVyUG9zaXRpb24uU3RpY2t5KSB7XG4gICAgICAgICAgICBjb25zb2xlLndhcm4oXCJBbHJlYWR5IGluIHN0aWNreSBtb2RlXCIpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKCF0aGlzLnN0aWNreUhlYWRDb250YWluZXIpIHtcbiAgICAgICAgICAgIHRoaXMuY3JlYXRlU3RpY2t5SGVhZGVyQ29udGFpbmVyKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBOb3RlOiBNb3ZpbmcgdGhlIHRhYmxlIGhlYWQgaW50byBzdGlja3kgY29udGFpbmVyXG4gICAgICAgIHRoaXMucmVuZGVyZXIuYXBwZW5kQ2hpbGQodGhpcy5zdGlja3lIZWFkQ29udGFpbmVyLCB0aGlzLmhlYWRSZWYpO1xuXG4gICAgICAgIHRoaXMuc3luY0hvcml6b250YWxTY3JvbGwoKTtcbiAgICAgICAgdGhpcy5zeW5jQ29sdW1uV2lkdGhzKCk7XG5cbiAgICAgICAgLy8gTm90ZTogV2hpbGUgd2UncmUgZGV0YWNoaW5nIGhlYWRlciBmcm9tIENESyBWaWV3cG9ydCB3ZSBoYXZlXG4gICAgICAgIC8vIHRvIHJlY2FsY3VsYXRlIHZpZXdwb3J0IGhlaWdodCB0byBrZWVwIHRoZSBzYW1lIHRvdGFsIGhlaWdodC5cbiAgICAgICAgLy8gVGhlIHNldFRpbWVvdXQgaXMgZm9yIHNraXBwaW5nIG9uZSB0aWNrIHRvIGxldCB0aGUgaGVhZGVyIGdldCBoaXMgaGVpZ2h0LlxuICAgICAgICBzZXRUaW1lb3V0KCgpID0+IHRoaXMudXBkYXRlQ29udGFpbmVyVG9GaXRIZWFkKCkpO1xuICAgICAgICB0aGlzLnVwZGF0ZVZpZXdwb3J0SGVpZ2h0T25IZWFkUmVzaXplKCk7XG5cbiAgICAgICAgdGhpcy5oZWFkUG9zaXRpb24gPSBUYWJsZVZpcnR1YWxTY3JvbGxIZWFkZXJQb3NpdGlvbi5TdGlja3k7XG4gICAgfVxuXG4gICAgcHVibGljIHVwZGF0ZUNvbnRhaW5lclRvRml0SGVhZCA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKHRoaXMuX3N0aWNreSkge1xuICAgICAgICAgICAgdGhpcy5vcmlnVmlld3BvcnRIZWlnaHQgPVxuICAgICAgICAgICAgICAgIHRoaXMub3JpZ1ZpZXdwb3J0SGVpZ2h0IHx8IHRoaXMudmlld3BvcnRFbD8ub2Zmc2V0SGVpZ2h0O1xuICAgICAgICAgICAgY29uc3Qgdmlld3BvcnRDb21wdXRlZEhlaWdodDogc3RyaW5nID0gaXNFbXB0eShcbiAgICAgICAgICAgICAgICB0aGlzLnVzZXJQcm92aWRlZEhlaWdodFxuICAgICAgICAgICAgKVxuICAgICAgICAgICAgICAgID8gdGhpcy5vcmlnVmlld3BvcnRIZWlnaHQgKyBcInB4XCJcbiAgICAgICAgICAgICAgICA6IHRoaXMudXNlclByb3ZpZGVkSGVpZ2h0O1xuICAgICAgICAgICAgdGhpcy52aWV3cG9ydEVsLnN0eWxlLnNldFByb3BlcnR5KFxuICAgICAgICAgICAgICAgIFwiaGVpZ2h0XCIsXG4gICAgICAgICAgICAgICAgYGNhbGMoJHt2aWV3cG9ydENvbXB1dGVkSGVpZ2h0fSAtICR7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuaGVhZFJlZj8ucm93cy5pdGVtKDApPy5vZmZzZXRIZWlnaHQgPz8gMFxuICAgICAgICAgICAgICAgIH1weClgLFxuICAgICAgICAgICAgICAgIFwiaW1wb3J0YW50XCJcbiAgICAgICAgICAgICk7XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHVibGljIHVwZGF0ZVZpZXdwb3J0SGVpZ2h0T25IZWFkUmVzaXplKCk6IHZvaWQge1xuICAgICAgICBpZiAodGhpcy5oZWFkUmVzaXplT2JzZXJ2ZXIpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIFRoaXMgcmVzaXplIG9ic2VydmVyIGlzIG5lZWRlZCBpbiBjYXNlIGEgcGFyZW50IGVsZW1lbnQgaGFzIGEgaGVpZ2h0IG9mIHplcm8gdXBvbiBpbnN0YW50aWF0aW9uXG4gICAgICAgIC8vIHRoZXJlYnkgcHJvaGliaXRpbmcgdGhlIGhlYWRlciBmcm9tIGhhdmluZyBpdHMgaW50ZW5kZWQgaGVpZ2h0IHdoZW4gaXRzIGluaXRpYWxseSByZW5kZXJlZC5cbiAgICAgICAgaWYgKHRoaXMuaGVhZFJlZikge1xuICAgICAgICAgICAgdGhpcy5oZWFkUmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoXG4gICAgICAgICAgICAgICAgKGVudHJpZXM6IFJlc2l6ZU9ic2VydmVyRW50cnlbXSkgPT5cbiAgICAgICAgICAgICAgICAgICAgLy8gV2Ugd3JhcCB0aGlzIGluIHJlcXVlc3RBbmltYXRpb25GcmFtZSB0byBhdm9pZCBcIlJlc2l6ZU9ic2VydmVyIGxvb3AgbGltaXQgZXhjZWVkZWRcIiBlcnJvciBpbiB1bml0IHRlc3RzXG4gICAgICAgICAgICAgICAgICAgIC8vIGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzQ5Mzg0MTIwL3Jlc2l6ZW9ic2VydmVyLWxvb3AtbGltaXQtZXhjZWVkZWRcbiAgICAgICAgICAgICAgICAgICAgd2luZG93LnJlcXVlc3RBbmltYXRpb25GcmFtZSgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBpZiAoIUFycmF5LmlzQXJyYXkoZW50cmllcykgfHwgIWVudHJpZXMubGVuZ3RoKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy51cGRhdGVDb250YWluZXJUb0ZpdEhlYWQoKTtcbiAgICAgICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgICB0aGlzLmhlYWRSZXNpemVPYnNlcnZlci5vYnNlcnZlKHRoaXMuaGVhZFJlZik7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwdWJsaWMgaGFuZGxlQ29sdW1uc1VwZGF0ZSQ6ICgpID0+IE9ic2VydmFibGU8dW5rbm93bj4gPVxuICAgICAgICAoKTogT2JzZXJ2YWJsZTx1bmtub3duPiA9PiB7XG4gICAgICAgICAgICBpZiAodGhpcy50YWJsZS5yZXNpemFibGUpIHtcbiAgICAgICAgICAgICAgICByZXR1cm4gRU1QVFk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIC8vIFRPRE86IFBlcmZvcm0gYSBkaXJ0eSBjaGVjayBiZWZvcmUgc3RhcnRpbmcgYXNzaWduaW5nIG5ldyB2YWx1ZXNcbiAgICAgICAgICAgIC8vIE5vdGU6IFNldHRpbmcgdGhlIHdpZHRoIG9mIHN0aWNreUhlYWRDb250YWluZXIgY29udGFpbmVyIHRvIGJlIGFibGUgdG8gc2ltdWxhdGUgaG9yaXpvbnRhbCBzY3JvbGwgb2YgdGhlIHN0aWNreSBoZWFkZXJcbiAgICAgICAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUoXG4gICAgICAgICAgICAgICAgdGhpcy5zdGlja3lIZWFkQ29udGFpbmVyLFxuICAgICAgICAgICAgICAgIFwid2lkdGhcIixcbiAgICAgICAgICAgICAgICBgJHt0aGlzLnZpZXdwb3J0Ll9jb250ZW50V3JhcHBlci5uYXRpdmVFbGVtZW50LnNjcm9sbFdpZHRofXB4YFxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgY29uc3QgaGVhZENvbHVtbnM6IEhUTUxUYWJsZUhlYWRlckNlbGxFbGVtZW50W10gPSBBcnJheS5mcm9tKFxuICAgICAgICAgICAgICAgIHRoaXMuc3RpY2t5SGVhZENvbnRhaW5lcj8uZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJ0aFwiKSB8fCBbXVxuICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIGNvbnN0IGZpcnN0RGF0YVJvd0NlbGxzOiBIVE1MVGFibGVEYXRhQ2VsbEVsZW1lbnRbXSA9IEFycmF5LmZyb20oXG4gICAgICAgICAgICAgICAgdGhpcy5ib2R5UmVmPy5yb3dzLml0ZW0oMCk/LmNlbGxzIHx8IFtdXG4gICAgICAgICAgICApO1xuXG4gICAgICAgICAgICAvLyBOb3RlOiBJZiBoZWFkIGNvbHVtbnMgYXJlIG5vdCBpbiBzeW5jIHdpdGggZGF0YSBjb2x1bW5zIHNraXBcbiAgICAgICAgICAgIGlmIChoZWFkQ29sdW1ucy5sZW5ndGggIT09IGZpcnN0RGF0YVJvd0NlbGxzLmxlbmd0aCkge1xuICAgICAgICAgICAgICAgIHJldHVybiBFTVBUWTtcbiAgICAgICAgICAgIH1cblxuICAgICAgICAgICAgLy8gVE9ETzogRmluZCBhIGJldHRlciB3YXkgdG8gcGFpciBwbGFjZWhvbGRlckhlYWRlciBjb2x1bW5zIHdpdGggaGVhZGVyIGNvbHVtbnNcbiAgICAgICAgICAgIGZpcnN0RGF0YVJvd0NlbGxzLmZvckVhY2goXG4gICAgICAgICAgICAgICAgKGNlbGw6IEhUTUxUYWJsZURhdGFDZWxsRWxlbWVudCwgaW5kZXg6IG51bWJlcikgPT4ge1xuICAgICAgICAgICAgICAgICAgICBjb25zdCBmaXhlZFdpZHRoID1cbiAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRDb2x1bW5zW2luZGV4XS5jbGFzc0xpc3QuY29udGFpbnMoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgRklYRURfV0lEVEhfQ0xBU1NcbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgIGlmICghZml4ZWRXaWR0aCkge1xuICAgICAgICAgICAgICAgICAgICAgICAgLy8gTm90ZTogQXNzaWduaW5nIGRhdGEgY2VsbCB3aWR0aCB0byB0aGUgY29ycmVzcG9uZGluZyBoZWFkZXIgY29sdW1uXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyAodXNpbmcgdGhlIHN0eWxlIHdpZHRoIGlmIHNwZWNpZmllZDsgb3RoZXJ3aXNlLCBmYWxsaW5nIGJhY2sgdG8gdGhlIG9mZnNldFdpZHRoKVxuICAgICAgICAgICAgICAgICAgICAgICAgaGVhZENvbHVtbnNbaW5kZXhdLnN0eWxlLndpZHRoID1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjZWxsLnN0eWxlLndpZHRoIHx8IGAke2NlbGwub2Zmc2V0V2lkdGh9cHhgO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgKTtcblxuICAgICAgICAgICAgLy8gdXBkYXRlIHRoZSBoZWFkZXIgcGxhY2Vob2xkZXIgdG8gbWF0Y2ggdGhlIHVwZGF0ZWQgY29sdW1uIHdpZHRoc1xuICAgICAgICAgICAgdGhpcy51cGRhdGVOYXRpdmVIZWFkZXJQbGFjZWhvbGRlcigpO1xuXG4gICAgICAgICAgICAvLyBOb3RlOiBSZXR1cm5pbmcgZW1wdHkgb2JzZXJ2YWJsZSB0byBiZSBhYmxlIHRvIGNyZWF0ZSBhbiBleGVjdXRpb24gcXVldWVcbiAgICAgICAgICAgIHJldHVybiBFTVBUWTtcbiAgICAgICAgfTtcblxuICAgIHByaXZhdGUgYXNzaWduUmVxdWlyZWRQcm9wZXJ0aWVzKCk6IHZvaWQge1xuICAgICAgICB0aGlzLnRhYmxlRWxSZWYgPVxuICAgICAgICAgICAgdGhpcy52aWV3cG9ydEVsLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwidGFibGVcIikuaXRlbSgwKSB8fCB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuaGVhZFJlZiA9XG4gICAgICAgICAgICB0aGlzLnZpZXdwb3J0RWwuZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJ0aGVhZFwiKS5pdGVtKDApIHx8IHVuZGVmaW5lZDtcbiAgICAgICAgdGhpcy51c2VyUHJvdmlkZWRIZWlnaHQgPSB0aGlzLnZpZXdwb3J0RWwuc3R5bGUuaGVpZ2h0O1xuICAgICAgICAvLyBEaXNhYmxlIGFuaW1hdGlvbiBmb3IgcmVzaXphYmxlIHN0aWNreSBoZWFkZXIgY2VsbHMgdG8gcHJldmVudCBhIGxhZ2dpbmcgZWZmZWN0IGR1cmluZyB3aWR0aCBjaGFuZ2VzLlxuICAgICAgICBpZiAoIXRoaXMudGFibGUucmVzaXphYmxlKSB7XG4gICAgICAgICAgICBBcnJheS5mcm9tKHRoaXMuaGVhZFJlZj8uZ2V0RWxlbWVudHNCeVRhZ05hbWUoXCJ0aFwiKSB8fCBbXSkuZm9yRWFjaChcbiAgICAgICAgICAgICAgICAodGgpID0+IHRoLmNsYXNzTGlzdC5hZGQoXCJ2aXJ0dWFsLXN0aWNreVwiKVxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuICAgICAgICB0aGlzLmJvZHlSZWYgPVxuICAgICAgICAgICAgdGhpcy52aWV3cG9ydEVsLmdldEVsZW1lbnRzQnlUYWdOYW1lKFwidGJvZHlcIikuaXRlbSgwKSB8fCB1bmRlZmluZWQ7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBzeW5jQ29sdW1uV2lkdGhzKCk6IHZvaWQge1xuICAgICAgICBjb25zdCByZXNpemUkID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcbiAgICAgICAgLy8gTm90ZTogUGFzc2luZyB0aGUgcmVzaXplIGV2ZW50IHRvIHJlc2l6ZSQgc3ViamVjdCB0byBiZSBhYmxlXG4gICAgICAgIC8vIHRvIGhhbmRsZSBhbGwgdGhlIGNvbHVtbldpZHRoIHVwZGF0ZSB0cmlnZ2VyIGluIGEgc2luZ2xlIHN0cmVhbVxuICAgICAgICBjb25zdCByZXNpemVPYnNlcnZlcjogUmVzaXplT2JzZXJ2ZXIgPSBuZXcgUmVzaXplT2JzZXJ2ZXIoKCkgPT4ge1xuICAgICAgICAgICAgcmVzaXplJC5uZXh0KCk7XG4gICAgICAgIH0pO1xuICAgICAgICByZXNpemVPYnNlcnZlci5vYnNlcnZlKHRoaXMudmlld3BvcnRFbCk7XG4gICAgICAgIHRoaXMudW5zdWJzY3JpYmUkXG4gICAgICAgICAgICAucGlwZShcbiAgICAgICAgICAgICAgICB0YWtlKDEpLFxuICAgICAgICAgICAgICAgIHRhcCgoKSA9PiByZXNpemUkLmNvbXBsZXRlKCkpXG4gICAgICAgICAgICApXG4gICAgICAgICAgICAuc3Vic2NyaWJlKCk7XG5cbiAgICAgICAgY29uc3Qgb25SZXNpemUkID0gcmVzaXplJC5waXBlKFxuICAgICAgICAgICAgLy8gTm90ZTogUGVyZm9ybWluZyB0aGUgcmVzaXplT2JzZXJ2ZXIgY2xlYW51cFxuICAgICAgICAgICAgZmluYWxpemUoKCkgPT4gcmVzaXplT2JzZXJ2ZXIuZGlzY29ubmVjdCgpKVxuICAgICAgICApO1xuICAgICAgICBjb25zdCBvblNjcm9sbCQgPSB0aGlzLnZpZXdwb3J0LmVsZW1lbnRTY3JvbGxlZCgpLnBpcGUoXG4gICAgICAgICAgICAvLyBOb3RlOiBSZWR1Y2luZyB0aGUgbnVtYmVyIG9mIHRpbWVzIGZ1bmN0aW9uIGlzIGludm9rZWRcbiAgICAgICAgICAgIC8vIGJ5IHNjaGVkdWxpbmcgdGhlIHNjcm9sbCBldmVudHMgdmlhIHRyYWlsaW5nIHRocm90dGxpbmdcbiAgICAgICAgICAgIHRocm90dGxlVGltZSg1MCwgYXN5bmNTY2hlZHVsZXIsIHsgdHJhaWxpbmc6IHRydWUgfSlcbiAgICAgICAgKTtcbiAgICAgICAgY29uc3QgdGFibGVDb2x1bW5zVXBkYXRlJCA9IG1lcmdlKFxuICAgICAgICAgICAgdGhpcy50YWJsZS5jb2x1bW5zT3JkZXJDaGFuZ2UsXG4gICAgICAgICAgICB0aGlzLnRhYmxlLmNvbHVtbnNXaWR0aENoYW5nZSxcbiAgICAgICAgICAgIHRoaXMudGFibGUuX2NvbnRlbnRDb2x1bW5EZWZzLmNoYW5nZXNcbiAgICAgICAgKS5waXBlKFxuICAgICAgICAgICAgLy8gTm90ZTogVXNpbmcgZGVsYXkoMCkgdG8gZ3JhbnQgc29tZSB0aW1lIHRvIHRoZSB0YWJsZVxuICAgICAgICAgICAgLy8gdG8gdXBkYXRlIHRoZSByb3dzIGFuZCB0aGVuIHByb2NlZWQgd2l0aCB0aGUgZXZlbnRcbiAgICAgICAgICAgIGRlbGF5KDApLFxuICAgICAgICAgICAgLy8gTm90ZTogUmVhdHRhY2hpbmcgbmF0aXZlIGhlYWRlciBvbiBldmVyeSBjb2x1bW5zIGNoYW5nZXNcbiAgICAgICAgICAgIHRhcCgoKSA9PiB0aGlzLnVwZGF0ZU5hdGl2ZUhlYWRlclBsYWNlaG9sZGVyKCkpXG4gICAgICAgICk7XG5cbiAgICAgICAgaWYgKCF0aGlzLnZpcnR1YWxGb3IpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIlVuYWJsZSB0byBmaW5kIENka1ZpcnR1YWxGb3JPZlwiKTtcbiAgICAgICAgfVxuXG4gICAgICAgIG1lcmdlKFxuICAgICAgICAgICAgb25TY3JvbGwkLFxuICAgICAgICAgICAgb25SZXNpemUkLFxuICAgICAgICAgICAgdGFibGVDb2x1bW5zVXBkYXRlJCxcbiAgICAgICAgICAgIHRoaXMudmlydHVhbEZvci5kYXRhU3RyZWFtXG4gICAgICAgIClcbiAgICAgICAgICAgIC5waXBlKFxuICAgICAgICAgICAgICAgIC8vIE5vdGU6IFByZXZlbnRpbmcgZnVuY3Rpb24gdG8gYmUgaW52b2tlZCBtdWx0aXBsZSB0aW1lc1xuICAgICAgICAgICAgICAgIC8vIGJ5IG1lcmdpbmcgbmV3IG9ic2VydmFibGUgb25seSBpZiB0aGUgcHJldmlvdXMgb25lIHdhcyBjb21wbGV0ZWRcbiAgICAgICAgICAgICAgICBleGhhdXN0TWFwKHRoaXMuaGFuZGxlQ29sdW1uc1VwZGF0ZSQpLFxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLnVuc3Vic2NyaWJlJClcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHN5bmNIb3Jpem9udGFsU2Nyb2xsKCk6IHZvaWQge1xuICAgICAgICBsZXQgcHJldmlvdXNTY3JvbGxMZWZ0OiBudW1iZXIgPSAwO1xuXG4gICAgICAgIHRoaXMudmlld3BvcnRcbiAgICAgICAgICAgIC5lbGVtZW50U2Nyb2xsZWQoKVxuICAgICAgICAgICAgLnBpcGUoXG4gICAgICAgICAgICAgICAgbWFwKCgpID0+IHRoaXMudmlld3BvcnRFbC5zY3JvbGxMZWZ0KSxcbiAgICAgICAgICAgICAgICAvLyBOb3RlOiBGaWx0ZXJpbmcgb3V0IHZlcnRpY2FsIHNjcm9sbCBldmVudHNcbiAgICAgICAgICAgICAgICBmaWx0ZXIoKHNjcm9sbExlZnQpID0+IHNjcm9sbExlZnQgIT09IHByZXZpb3VzU2Nyb2xsTGVmdCksXG4gICAgICAgICAgICAgICAgdGFwKChzY3JvbGxMZWZ0OiBudW1iZXIpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgcHJldmlvdXNTY3JvbGxMZWZ0ID0gc2Nyb2xsTGVmdDtcbiAgICAgICAgICAgICAgICAgICAgLy8gTm90ZTogU2ltdWxhdGluZyBob3Jpem9udGFsIHNjcm9sbCBieSBhc3NpZ25pbmcgbWFyZ2luLWxlZnQgdG8gYmUgZXF1YWwgdG8gc2Nyb2xsZWQgZGlzdGFuY2VcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZShcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuc3RpY2t5SGVhZENvbnRhaW5lcixcbiAgICAgICAgICAgICAgICAgICAgICAgIFwibWFyZ2luLWxlZnRcIixcbiAgICAgICAgICAgICAgICAgICAgICAgIGAtJHtzY3JvbGxMZWZ0fXB4YFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH0pLFxuICAgICAgICAgICAgICAgIHRha2VVbnRpbCh0aGlzLnVuc3Vic2NyaWJlJClcbiAgICAgICAgICAgIClcbiAgICAgICAgICAgIC5zdWJzY3JpYmUoKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIGNyZWF0ZVN0aWNreUhlYWRlckNvbnRhaW5lcigpOiB2b2lkIHtcbiAgICAgICAgdGhpcy5zdGlja3lIZWFkQ29udGFpbmVyID0gdGhpcy5yZW5kZXJlci5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuXG4gICAgICAgIGNvbnN0IHdyYXBwZXI6IEhUTUxFbGVtZW50ID0gdGhpcy5yZW5kZXJlci5jcmVhdGVFbGVtZW50KFwiZGl2XCIpO1xuICAgICAgICB0aGlzLnJlbmRlcmVyLmFwcGVuZENoaWxkKHdyYXBwZXIsIHRoaXMuc3RpY2t5SGVhZENvbnRhaW5lcik7XG4gICAgICAgIHRoaXMucmVuZGVyZXIuc2V0U3R5bGUod3JhcHBlciwgXCJvdmVyZmxvdy14XCIsIGBoaWRkZW5gKTtcbiAgICAgICAgdGhpcy5yZW5kZXJlci5zZXRTdHlsZSh3cmFwcGVyLCBcIndpZHRoXCIsIGAxMDAlYCk7XG5cbiAgICAgICAgLy8gQXNzaWduaW5nIG9yaWdpbmFsIHRhYmxlIGNsYXNzZXNcbiAgICAgICAgY29uc3Qgb3JpZ2luYWxUYWJsZUNsYXNzZXM6IHN0cmluZ1tdID0gQXJyYXkuZnJvbShcbiAgICAgICAgICAgIHRoaXMudGFibGVFbFJlZj8uY2xhc3NMaXN0IHx8IFtdXG4gICAgICAgICk7XG4gICAgICAgIG9yaWdpbmFsVGFibGVDbGFzc2VzLnB1c2goXCJzdGlja3ktdGFibGUtaGVhZGVyLWNvbnRhaW5lclwiKTtcbiAgICAgICAgb3JpZ2luYWxUYWJsZUNsYXNzZXMuZm9yRWFjaCgoY3NzQ2xhc3MpID0+XG4gICAgICAgICAgICB0aGlzLnJlbmRlcmVyLmFkZENsYXNzKHRoaXMuc3RpY2t5SGVhZENvbnRhaW5lciwgY3NzQ2xhc3MpXG4gICAgICAgICk7XG5cbiAgICAgICAgdGhpcy5yZW5kZXJlci5pbnNlcnRCZWZvcmUoXG4gICAgICAgICAgICB0aGlzLnZpZXdwb3J0RWwucGFyZW50RWxlbWVudCxcbiAgICAgICAgICAgIHdyYXBwZXIsXG4gICAgICAgICAgICB0aGlzLnZpZXdwb3J0RWxcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHVwZGF0ZU5hdGl2ZUhlYWRlclBsYWNlaG9sZGVyKCk6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuaGVhZFJlZiB8fCAhdGhpcy50YWJsZUVsUmVmIHx8ICF0aGlzLmJvZHlSZWYpIHtcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgICAgICAgICAgICBcIkNhbid0IGFwcGVuZCB0aGVhZCBwbGFjZWhvbGRlci4gVGFibGVSZWYsIEJvZHlSZWYgb3IgSGVhZGVyUmVmIGlzIHVuZGVmaW5lZFwiXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgaGVhZFBsYWNlaG9sZGVyID1cbiAgICAgICAgICAgIHRoaXMudGFibGVFbFJlZi5nZXRFbGVtZW50c0J5VGFnTmFtZShcInRoZWFkXCIpWzBdO1xuICAgICAgICBpZiAoaGVhZFBsYWNlaG9sZGVyKSB7XG4gICAgICAgICAgICBoZWFkUGxhY2Vob2xkZXIucmVtb3ZlKCk7XG4gICAgICAgIH1cblxuICAgICAgICBjb25zdCB0aGVhZFBsYWNlaG9sZGVyOiBOb2RlID0gdGhpcy5oZWFkUmVmLmNsb25lTm9kZSh0cnVlKTtcblxuICAgICAgICAvLyBOb3RlOiBtYWtpbmcgaGVhZGVyIGludmlzaWJsZVxuICAgICAgICB0aGlzLnJlbmRlcmVyLnNldFN0eWxlKHRoZWFkUGxhY2Vob2xkZXIsIFwidmlzaWJpbGl0eVwiLCBcImNvbGxhcHNlXCIpO1xuICAgICAgICAvLyBOb3RlOiBBZGRpbmcgYW4gaWRlbnRpZmllciBmb3IgdGhlIGhlYWRlciBwbGFjZWhvbGRlciB0byBhdm9pZCBjb25mdXNpb25cbiAgICAgICAgdGhpcy5yZW5kZXJlci5hZGRDbGFzcyh0aGVhZFBsYWNlaG9sZGVyLCBcInN0aWNreS1oZWFkZXItcGxhY2Vob2xkZXJcIik7XG4gICAgICAgIC8vIE5vdGU6IEFwcGVuZGluZyBoZWFkIHBsYWNlaG9sZGVyIHRvIHRoZSB0YWJsZVxuICAgICAgICB0aGlzLnJlbmRlcmVyLmluc2VydEJlZm9yZShcbiAgICAgICAgICAgIHRoaXMudGFibGVFbFJlZixcbiAgICAgICAgICAgIHRoZWFkUGxhY2Vob2xkZXIsXG4gICAgICAgICAgICB0aGlzLmJvZHlSZWZcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHVwZGF0ZUhlYWRQb3NpdGlvbihpc1N0aWNreTogYm9vbGVhbik6IHZvaWQge1xuICAgICAgICBpZiAoIWlzU3RpY2t5KSB7XG4gICAgICAgICAgICB0aGlzLnNldE5hdGl2ZSgpO1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuc2V0U3RpY2t5KCk7XG4gICAgfVxufVxuIl19