@nova-ui/bits
Version:
SolarWinds Nova Framework
284 lines • 48.8 kB
JavaScript
// © 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