@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
471 lines • 60.3 kB
JavaScript
import { ContentChildren, Directive, ElementRef, EventEmitter, inject, Input, Output } from '@angular/core';
import { BehaviorSubject, from, fromEvent, of, Subject } from 'rxjs';
import { auditTime, combineLatest, filter as filterOperator, first, takeUntil } from 'rxjs/operators';
import { InfiniteScrollLoadButtonDirective } from './infinite-scroll-load-button.directive';
import { InfiniteScrollLoadingDirective } from './infinite-scroll-loading.directive';
import * as i0 from "@angular/core";
export class InfiniteScrollDirective {
get collection() {
return this._collection;
}
set collection(value) {
this.collectionChange.emit(value);
this._collection = value;
}
set scrollElement(element) {
this._scrollElement = element instanceof ElementRef ? element : new ElementRef(element);
}
constructor() {
this._element = inject(ElementRef);
this._collection = [];
this.enabled = true;
this.loadOnInit = true;
this.loadOnScroll = true;
this.pageSize = 20;
this.collectionChange = new EventEmitter();
this.loadingEvent = new EventEmitter();
this.loadedEvent = new EventEmitter();
this.loadErrorEvent = new EventEmitter();
this._nextPageNum = 0;
this._updateRequests = new Subject();
this._isLoading = new BehaviorSubject(false);
this._isExhausted = new BehaviorSubject(false);
this._loadButtonEnabled = new BehaviorSubject(false);
this._subscriptions = [];
this._loadButtonSubscriptions = [];
this._onDestroy = new Subject();
this._canLoadManually = this._isLoading.pipe(combineLatest(this._isExhausted, this._loadButtonEnabled, (isLoading, isExhausted, loadButtonEnabled) => {
return !isLoading && !isExhausted && loadButtonEnabled;
}));
}
ngOnInit() {
if (!this._scrollElement) {
this._scrollElement = this._element;
}
this._loadButtonEnabled.next(!this.loadOnScroll);
}
ngAfterContentInit() {
// There are two kinds of update requests: check and load.
// Check requests are throttled and will only cause an update if more data is required
// to fill the scrolling view, and it isn't already loading some.
// Load requests are not throttled and always request a page of data.
this._updateRequests.pipe(filterOperator(request => request.check), auditTime(200), takeUntil(this._onDestroy)).subscribe(this.doRequest.bind(this));
this._updateRequests.pipe(filterOperator(request => !request.check), takeUntil(this._onDestroy)).subscribe(this.doRequest.bind(this));
if (this.enabled) {
// Subscribe to scroll events and DOM changes.
this.attachEventHandlers();
}
// Connect the Load More button visible state.
this._canLoadManually.pipe(takeUntil(this._onDestroy)).subscribe(canLoad => {
this._loadButtonQuery.forEach(loadButton => {
loadButton.visible = canLoad;
});
});
// Connect the loading indicator visible state.
this._isLoading.pipe(takeUntil(this._onDestroy)).subscribe(isLoading => {
this._loadingIndicatorQuery.forEach(loading => {
loading.visible = isLoading;
});
});
// Link the Load More button click event to trigger an update.
this.attachLoadButtonEvents();
this._loadButtonQuery.changes.pipe(takeUntil(this._onDestroy)).subscribe(() => {
this.attachLoadButtonEvents();
});
// Initial update.
if (this.loadOnInit) {
this.loadNextPage();
}
}
ngOnChanges(changes) {
let check = true;
if (changes.enabled && changes.enabled.currentValue !== changes.enabled.previousValue) {
if (changes.enabled.currentValue) {
this.attachEventHandlers();
this.reset();
check = false;
}
else {
this.detachEventHandlers();
}
}
if (this.enabled) {
if (changes.filter && this.coerceFilter(changes.filter.currentValue) !== this.coerceFilter(changes.filter.previousValue)) {
this.reset();
check = false;
}
if (changes.loadOnScroll) {
this._loadButtonEnabled.next(!changes.loadOnScroll.currentValue);
}
if (changes.pageSize && changes.pageSize.currentValue !== changes.pageSize.previousValue) {
this.reset();
check = false;
}
this._updateRequests.next({
check,
pageNumber: this._nextPageNum,
pageSize: this.pageSize,
filter: this.coerceFilter(this.filter)
});
}
}
ngOnDestroy() {
this.detachEventHandlers();
this._onDestroy.next();
this._onDestroy.complete();
}
/**
* Request an additional page of data.
*/
loadNextPage() {
if (!this.enabled) {
return;
}
this._updateRequests.next({
check: false,
pageNumber: this._nextPageNum,
pageSize: this.pageSize,
filter: this.coerceFilter(this.filter)
});
}
/**
* Request a check for whether an additional page of data is required. This is throttled.
*/
check() {
if (!this.enabled) {
return;
}
this._updateRequests.next({
check: true,
pageNumber: this._nextPageNum,
pageSize: this.pageSize,
filter: this.coerceFilter(this.filter)
});
}
/**
* Clear the collection. Future requests will load from page 0.
*/
reset(clearSubscriptions = true) {
if (!this.enabled) {
return;
}
// Reset the page counter.
this._nextPageNum = 0;
// Clear the collection (without changing the reference).
if (this.collection) {
this.collection.length = 0;
}
if (this._subscriptions && clearSubscriptions) {
this._pages = [];
// Reset the exhausted flag, allowing the Load More button to appear.
this._isExhausted.next(false);
// reset the loading state
this._isLoading.next(false);
// Cancel any pending requests
this._subscriptions.forEach(request => request.unsubscribe());
}
}
/**
* Reload the data without clearing the view.
*/
reload() {
this._pages?.forEach((page, i) => this.reloadPage(i));
}
/**
* Reload the data in a specific page without clearing the view.
* @param pageNum Page number
*/
reloadPage(pageNum) {
if (!this.enabled) {
return;
}
this._updateRequests.next({
check: false,
pageNumber: pageNum,
pageSize: this.pageSize,
filter: this.coerceFilter(this.filter),
reload: true
});
}
/** A filter value of null or undefined should be considered the same as an empty string */
coerceFilter(value) {
return value === undefined || value === null ? '' : value;
}
/**
* Attach scroll event handler and DOM observer.
*/
attachEventHandlers() {
// if the scrollElement is documentElement we must watch for a scroll event on the document
const target = this._scrollElement.nativeElement instanceof HTMLHtmlElement ? document : this._scrollElement.nativeElement;
// Subscribe to the scroll event on the target element.
this._scrollEventSub = fromEvent(target, 'scroll').subscribe(this.check.bind(this));
// Subscribe to child DOM changes. The main effect of this is to check whether even more data is
// required after the initial load.
this._domObserver = new MutationObserver(this.check.bind(this));
this._domObserver.observe(this._scrollElement.nativeElement, {
childList: true,
subtree: true
});
}
/**
* Detach scroll event handler and DOM observer.
*/
detachEventHandlers() {
if (this._scrollEventSub) {
this._scrollEventSub.unsubscribe();
this._scrollEventSub = null;
}
if (this._domObserver) {
this._domObserver.disconnect();
this._domObserver = null;
}
}
/**
* Remove any existing event subscriptions for the load button `loading` event, then attach
* subscriptions
* for any in the query.
*/
attachLoadButtonEvents() {
this._loadButtonSubscriptions.forEach(s => s.unsubscribe());
this._loadButtonSubscriptions = this._loadButtonQuery.map(loadButton => loadButton.loading.subscribe(this.loadNextPage.bind(this)));
}
/**
* Conditionally loads a page into the collection based on directive state and request parameters.
*/
doRequest(request) {
// Load a new page if the scroll position is beyond the threshhold and if the client code did not
// cancel.
if (this.needsData(request) && this.beginLoading(request)) {
// Invoke the callback load function, which returns a promose or plain data.
const loadResult = this.load(request.pageNumber, request.pageSize, request.filter);
if (loadResult === null) {
return;
}
const observable = Array.isArray(loadResult) ? of(loadResult) : from(loadResult);
let completed = false;
// subscription needs to be a let here if subscription completes right away the complete function can be called
// before the assignment. While browsers will ignore this currently as we transpile
// all const/let statements to var, when this is no longer the case (or we are running in a test environment)
// const will be used and this can throw an error if we try to access a const variable before it is assigned
const subscription = observable.pipe(first()).subscribe(items => {
// Make sure that the parameters have not changed since the load started;
// otherwise discard the results.
if (request.filter === this.coerceFilter(this.filter) && request.pageSize === this.pageSize) {
if (items && items.length) {
this.setPageItems(request.pageNumber, items);
}
// Emit the loaded event
this.endLoading(request, items);
}
}, (reason) => {
// Emit the loadError event
this.endLoadingWithError(request, reason);
}, () => {
// remove this request from the list
this._subscriptions = this._subscriptions.filter(s => s !== subscription);
completed = true;
});
// only add the subscription to the list of requests if it isnt complete.
if (!completed) {
this._subscriptions.push(subscription);
}
}
}
/**
* Returns true if the request should be fulfilled.
*/
needsData(request) {
if (!this.enabled) {
return false;
}
// Always load for a load request
if (!request.check) {
return true;
}
// Ignore a check request when the end of data has been detected, or if data is currently loading.
if (this._isExhausted.getValue() || this._isLoading.getValue()) {
return false;
}
// Load if the remaining scroll area is <= the element height.
if (this._scrollElement && this.loadOnScroll) {
const element = this._scrollElement.nativeElement;
const remainingScroll = element.scrollHeight - (element.scrollTop + element.clientHeight);
const isVisible = element.scrollHeight > 0;
return isVisible && remainingScroll <= element.clientHeight;
}
return false;
}
/**
* Updates state for the beginning of a load. Returns false if the `loading` event was cancelled.
*/
beginLoading(request) {
const event = new InfiniteScrollLoadingEvent(request.pageNumber, request.pageSize, request.filter);
this.loadingEvent.emit(event);
this._isLoading.next(!event.defaultPrevented());
return !event.defaultPrevented();
}
setPageItems(pageNum, items) {
this._pages[pageNum] = items;
this.collection = this._pages.reduce((previous, current) => previous.concat(current), []);
}
/**
* Updates state from a successful load. Raises the `loaded` event.
*/
endLoading(request, data) {
this._isLoading.next(false);
const isExhausted = !!(data && data.length < this.pageSize);
this._isExhausted.next(isExhausted);
this.loadedEvent.emit(new InfiniteScrollLoadedEvent(request.pageNumber, request.pageSize, request.filter, data, isExhausted));
if (!request.reload) {
this._nextPageNum += 1;
}
}
/**
* Updates state from a failed load. Raises the `loadError` event.
*/
endLoadingWithError(request, error) {
this._isLoading.next(false);
this.loadErrorEvent.emit(new InfiniteScrollLoadErrorEvent(request.pageNumber, request.pageSize, request.filter, error));
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: InfiniteScrollDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "18.2.12", type: InfiniteScrollDirective, selector: "[uxInfiniteScroll]", inputs: { load: ["uxInfiniteScroll", "load"], _collection: ["collection", "_collection"], scrollElement: "scrollElement", enabled: "enabled", filter: "filter", loadOnInit: "loadOnInit", loadOnScroll: "loadOnScroll", pageSize: "pageSize" }, outputs: { collectionChange: "collectionChange", loadingEvent: "loading", loadedEvent: "loaded", loadErrorEvent: "loadError" }, queries: [{ propertyName: "_loadButtonQuery", predicate: InfiniteScrollLoadButtonDirective }, { propertyName: "_loadingIndicatorQuery", predicate: InfiniteScrollLoadingDirective }], exportAs: ["uxInfiniteScroll"], usesOnChanges: true, ngImport: i0 }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: InfiniteScrollDirective, decorators: [{
type: Directive,
args: [{
selector: '[uxInfiniteScroll]',
exportAs: 'uxInfiniteScroll'
}]
}], ctorParameters: () => [], propDecorators: { load: [{
type: Input,
args: ['uxInfiniteScroll']
}], _collection: [{
type: Input,
args: ['collection']
}], scrollElement: [{
type: Input
}], enabled: [{
type: Input
}], filter: [{
type: Input
}], loadOnInit: [{
type: Input
}], loadOnScroll: [{
type: Input
}], pageSize: [{
type: Input
}], collectionChange: [{
type: Output
}], loadingEvent: [{
type: Output,
args: ['loading']
}], loadedEvent: [{
type: Output,
args: ['loaded']
}], loadErrorEvent: [{
type: Output,
args: ['loadError']
}], _loadButtonQuery: [{
type: ContentChildren,
args: [InfiniteScrollLoadButtonDirective]
}], _loadingIndicatorQuery: [{
type: ContentChildren,
args: [InfiniteScrollLoadingDirective]
}] } });
/**
* The internal data associated with a load/check request.
*/
class InfiniteScrollRequest {
}
/**
* Event raised before the `loading` function is called.
*/
export class InfiniteScrollLoadingEvent {
constructor(
/**
* The index of the requested page, starting from 0.
*/
pageNumber,
/**
* The number of items requested.
*/
pageSize,
/**
* The filter details as provided via the `filter` binding.
*/
filter) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.filter = filter;
this._defaultPrevented = false;
}
/**
* Prevents the default behaviour of the `loading` event (loading function will not be called).
*/
preventDefault() {
this._defaultPrevented = true;
}
defaultPrevented() {
return this._defaultPrevented;
}
}
/**
* Event raised when the loading function result has been resolved and added to the collection.
*/
export class InfiniteScrollLoadedEvent {
constructor(
/**
* The index of the requested page, starting from 0.
*/
pageNumber,
/**
* The number of items requested.
*/
pageSize,
/**
* The filter details as provided via the `filter` binding.
*/
filter,
/**
* The result of the promise returned from the loading function.
*/
data,
/**
* True if the data is considered exhausted (number of items returned less than `pageSize`).
*/
exhausted) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.filter = filter;
this.data = data;
this.exhausted = exhausted;
}
}
/**
* Event raised if the loading function returns a rejected promise.
*/
export class InfiniteScrollLoadErrorEvent {
constructor(
/**
* The index of the requested page, starting from 0.
*/
pageNumber,
/**
* The number of items requested.
*/
pageSize,
/**
* The filter details as provided via the `filter` binding.
*/
filter,
/**
* The object provided when rejecting the promise.
*/
error) {
this.pageNumber = pageNumber;
this.pageSize = pageSize;
this.filter = filter;
this.error = error;
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5maW5pdGUtc2Nyb2xsLmRpcmVjdGl2ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9kaXJlY3RpdmVzL2luZmluaXRlLXNjcm9sbC9pbmZpbml0ZS1zY3JvbGwuZGlyZWN0aXZlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBb0IsZUFBZSxFQUFFLFNBQVMsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQWdDLE1BQU0sRUFBNEIsTUFBTSxlQUFlLENBQUM7QUFDdEwsT0FBTyxFQUFFLGVBQWUsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFjLEVBQUUsRUFBRSxPQUFPLEVBQWdCLE1BQU0sTUFBTSxDQUFDO0FBQy9GLE9BQU8sRUFBRSxTQUFTLEVBQUUsYUFBYSxFQUFFLE1BQU0sSUFBSSxjQUFjLEVBQUUsS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBQ3RHLE9BQU8sRUFBRSxpQ0FBaUMsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQzVGLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxNQUFNLHFDQUFxQyxDQUFDOztBQU1yRixNQUFNLE9BQU8sdUJBQXVCO0lBTWhDLElBQUksVUFBVTtRQUNWLE9BQU8sSUFBSSxDQUFDLFdBQVcsQ0FBQztJQUM1QixDQUFDO0lBQ0QsSUFBSSxVQUFVLENBQUMsS0FBVTtRQUNyQixJQUFJLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2xDLElBQUksQ0FBQyxXQUFXLEdBQUcsS0FBSyxDQUFDO0lBQzdCLENBQUM7SUFHRCxJQUFhLGFBQWEsQ0FBQyxPQUFpQztRQUN4RCxJQUFJLENBQUMsY0FBYyxHQUFHLE9BQU8sWUFBWSxVQUFVLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsSUFBSSxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUYsQ0FBQztJQTBD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QUNJOztPQUVHO0lBQ0ksVUFBa0I7SUFDekI7O09BRUc7SUFDSSxRQUFnQjtJQUN2Qjs7T0FFRztJQUNJLE1BQVM7SUFDaEI7O09BRUc7SUFDSSxJQUFTO0lBQ2hCOztPQUVHO0lBQ0ksU0FBa0I7UUFoQmxCLGVBQVUsR0FBVixVQUFVLENBQVE7UUFJbEIsYUFBUSxHQUFSLFFBQVEsQ0FBUTtRQUloQixXQUFNLEdBQU4sTUFBTSxDQUFHO1FBSVQsU0FBSSxHQUFKLElBQUksQ0FBSztRQUlULGNBQVMsR0FBVCxTQUFTLENBQVM7SUFDekIsQ0FBQztDQUNSO0FBRUQ7O0dBRUc7QUFFSCxNQUFNLE9BQU8sNEJBQTRCO0lBQ3JDO0lBQ0k7O09BRUc7SUFDSSxVQUFrQjtJQUN6Qjs7T0FFRztJQUNJLFFBQWdCO0lBQ3ZCOztPQUVHO0lBQ0ksTUFBUztJQUNoQjs7T0FFRztJQUVJLEtBQVU7UUFiVixlQUFVLEdBQVYsVUFBVSxDQUFRO1FBSWxCLGFBQVEsR0FBUixRQUFRLENBQVE7UUFJaEIsV0FBTSxHQUFOLE1BQU0sQ0FBRztRQUtULFVBQUssR0FBTCxLQUFLLENBQUs7SUFDakIsQ0FBQztDQUNSIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQWZ0ZXJDb250ZW50SW5pdCwgQ29udGVudENoaWxkcmVuLCBEaXJlY3RpdmUsIEVsZW1lbnRSZWYsIEV2ZW50RW1pdHRlciwgaW5qZWN0LCBJbnB1dCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3ksIE9uSW5pdCwgT3V0cHV0LCBRdWVyeUxpc3QsIFNpbXBsZUNoYW5nZXMgfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IEJlaGF2aW9yU3ViamVjdCwgZnJvbSwgZnJvbUV2ZW50LCBPYnNlcnZhYmxlLCBvZiwgU3ViamVjdCwgU3Vic2NyaXB0aW9uIH0gZnJvbSAncnhqcyc7XG5pbXBvcnQgeyBhdWRpdFRpbWUsIGNvbWJpbmVMYXRlc3QsIGZpbHRlciBhcyBmaWx0ZXJPcGVyYXRvciwgZmlyc3QsIHRha2VVbnRpbCB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcbmltcG9ydCB7IEluZmluaXRlU2Nyb2xsTG9hZEJ1dHRvbkRpcmVjdGl2ZSB9IGZyb20gJy4vaW5maW5pdGUtc2Nyb2xsLWxvYWQtYnV0dG9uLmRpcmVjdGl2ZSc7XG5pbXBvcnQgeyBJbmZpbml0ZVNjcm9sbExvYWRpbmdEaXJlY3RpdmUgfSBmcm9tICcuL2luZmluaXRlLXNjcm9sbC1sb2FkaW5nLmRpcmVjdGl2ZSc7XG5cbkBEaXJlY3RpdmUoe1xuICAgIHNlbGVjdG9yOiAnW3V4SW5maW5pdGVTY3JvbGxdJyxcbiAgICBleHBvcnRBczogJ3V4SW5maW5pdGVTY3JvbGwnXG59KVxuZXhwb3J0IGNsYXNzIEluZmluaXRlU2Nyb2xsRGlyZWN0aXZlPFQgPSB1bmtub3duPiBpbXBsZW1lbnRzIE9uSW5pdCwgQWZ0ZXJDb250ZW50SW5pdCwgT25DaGFuZ2VzLCBPbkRlc3Ryb3kge1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2VsZW1lbnQgPSBpbmplY3QoRWxlbWVudFJlZik7XG5cbiAgICBASW5wdXQoJ3V4SW5maW5pdGVTY3JvbGwnKSBsb2FkOiBJbmZpbml0ZVNjcm9sbExvYWRGdW5jdGlvbjxUPjtcblxuICAgIEBJbnB1dCgnY29sbGVjdGlvbicpIF9jb2xsZWN0aW9uOiBUW10gPSBbXTtcbiAgICBnZXQgY29sbGVjdGlvbigpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NvbGxlY3Rpb247XG4gICAgfVxuICAgIHNldCBjb2xsZWN0aW9uKHZhbHVlOiBUW10pIHtcbiAgICAgICAgdGhpcy5jb2xsZWN0aW9uQ2hhbmdlLmVtaXQodmFsdWUpO1xuICAgICAgICB0aGlzLl9jb2xsZWN0aW9uID0gdmFsdWU7XG4gICAgfVxuXG5cbiAgICBASW5wdXQoKSBzZXQgc2Nyb2xsRWxlbWVudChlbGVtZW50OiBFbGVtZW50UmVmIHwgSFRNTEVsZW1lbnQpIHtcbiAgICAgICAgdGhpcy5fc2Nyb2xsRWxlbWVudCA9IGVsZW1lbnQgaW5zdGFuY2VvZiBFbGVtZW50UmVmID8gZWxlbWVudCA6IG5ldyBFbGVtZW50UmVmKGVsZW1lbnQpO1xuICAgIH1cblxuICAgIEBJbnB1dCgpIGVuYWJsZWQ6IGJvb2xlYW4gPSB0cnVlO1xuICAgICBcbiAgICBASW5wdXQoKSBmaWx0ZXI6IGFueTtcbiAgICBASW5wdXQoKSBsb2FkT25Jbml0OiBib29sZWFuID0gdHJ1ZTtcbiAgICBASW5wdXQoKSBsb2FkT25TY3JvbGw6IGJvb2xlYW4gPSB0cnVlO1xuICAgIEBJbnB1dCgpIHBhZ2VTaXplOiBudW1iZXIgPSAyMDtcblxuICAgIEBPdXRwdXQoKSBjb2xsZWN0aW9uQ2hhbmdlID0gbmV3IEV2ZW50RW1pdHRlcjxUW10+KCk7XG5cbiAgICBAT3V0cHV0KCdsb2FkaW5nJylcbiAgICBsb2FkaW5nRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyPEluZmluaXRlU2Nyb2xsTG9hZGluZ0V2ZW50PigpO1xuXG4gICAgQE91dHB1dCgnbG9hZGVkJylcbiAgICBsb2FkZWRFdmVudCA9IG5ldyBFdmVudEVtaXR0ZXI8SW5maW5pdGVTY3JvbGxMb2FkZWRFdmVudD4oKTtcblxuICAgIEBPdXRwdXQoJ2xvYWRFcnJvcicpXG4gICAgbG9hZEVycm9yRXZlbnQgPSBuZXcgRXZlbnRFbWl0dGVyPEluZmluaXRlU2Nyb2xsTG9hZEVycm9yRXZlbnQ+KCk7XG5cbiAgICBAQ29udGVudENoaWxkcmVuKEluZmluaXRlU2Nyb2xsTG9hZEJ1dHRvbkRpcmVjdGl2ZSlcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9sb2FkQnV0dG9uUXVlcnk6IFF1ZXJ5TGlzdDxJbmZpbml0ZVNjcm9sbExvYWRCdXR0b25EaXJlY3RpdmU+O1xuXG4gICAgQENvbnRlbnRDaGlsZHJlbihJbmZpbml0ZVNjcm9sbExvYWRpbmdEaXJlY3RpdmUpXG4gICAgcHJpdmF0ZSByZWFkb25seSBfbG9hZGluZ0luZGljYXRvclF1ZXJ5OiBRdWVyeUxpc3Q8SW5maW5pdGVTY3JvbGxMb2FkaW5nRGlyZWN0aXZlPjtcblxuICAgIHByaXZhdGUgX3BhZ2VzOiBUW11bXTtcbiAgICBwcml2YXRlIF9uZXh0UGFnZU51bSA9IDA7XG4gICAgcHJpdmF0ZSBfZG9tT2JzZXJ2ZXI6IE11dGF0aW9uT2JzZXJ2ZXI7XG4gICAgcHJpdmF0ZSBfc2Nyb2xsRXZlbnRTdWI6IFN1YnNjcmlwdGlvbjtcbiAgICBwcml2YXRlIHJlYWRvbmx5IF91cGRhdGVSZXF1ZXN0cyA9IG5ldyBTdWJqZWN0PEluZmluaXRlU2Nyb2xsUmVxdWVzdDxUPj4oKTtcblxuICAgIHByaXZhdGUgcmVhZG9ubHkgX2lzTG9hZGluZyA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX2lzRXhoYXVzdGVkID0gbmV3IEJlaGF2aW9yU3ViamVjdDxib29sZWFuPihmYWxzZSk7XG4gICAgcHJpdmF0ZSByZWFkb25seSBfbG9hZEJ1dHRvbkVuYWJsZWQgPSBuZXcgQmVoYXZpb3JTdWJqZWN0PGJvb2xlYW4+KGZhbHNlKTtcbiAgICBwcml2YXRlIHJlYWRvbmx5IF9jYW5Mb2FkTWFudWFsbHk6IE9ic2VydmFibGU8Ym9vbGVhbj47XG5cbiAgICBwcml2YXRlIF9zY3JvbGxFbGVtZW50OiBFbGVtZW50UmVmO1xuICAgIHByaXZhdGUgX3N1YnNjcmlwdGlvbnM6IFN1YnNjcmlwdGlvbltdID0gW107XG4gICAgcHJpdmF0ZSBfbG9hZEJ1dHRvblN1YnNjcmlwdGlvbnM6IFN1YnNjcmlwdGlvbltdID0gW107XG4gICAgcHJpdmF0ZSByZWFkb25seSBfb25EZXN0cm95ID0gbmV3IFN1YmplY3Q8dm9pZD4oKTtcblxuICAgIGNvbnN0cnVjdG9yKCkge1xuICAgICAgICB0aGlzLl9jYW5Mb2FkTWFudWFsbHkgPSB0aGlzLl9pc0xvYWRpbmcucGlwZShjb21iaW5lTGF0ZXN0KFxuICAgICAgICAgICAgdGhpcy5faXNFeGhhdXN0ZWQsXG4gICAgICAgICAgICB0aGlzLl9sb2FkQnV0dG9uRW5hYmxlZCxcbiAgICAgICAgICAgIChpc0xvYWRpbmcsIGlzRXhoYXVzdGVkLCBsb2FkQnV0dG9uRW5hYmxlZCkgPT4ge1xuICAgICAgICAgICAgICAgIHJldHVybiAhaXNMb2FkaW5nICYmICFpc0V4aGF1c3RlZCAmJiBsb2FkQnV0dG9uRW5hYmxlZDtcbiAgICAgICAgICAgIH1cbiAgICAgICAgKSk7XG4gICAgfVxuXG4gICAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5fc2Nyb2xsRWxlbWVudCkge1xuICAgICAgICAgICAgdGhpcy5fc2Nyb2xsRWxlbWVudCA9IHRoaXMuX2VsZW1lbnQ7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9sb2FkQnV0dG9uRW5hYmxlZC5uZXh0KCF0aGlzLmxvYWRPblNjcm9sbCk7XG4gICAgfVxuXG4gICAgbmdBZnRlckNvbnRlbnRJbml0KCk6IHZvaWQge1xuXG4gICAgICAgIC8vIFRoZXJlIGFyZSB0d28ga2luZHMgb2YgdXBkYXRlIHJlcXVlc3RzOiBjaGVjayBhbmQgbG9hZC5cbiAgICAgICAgLy8gQ2hlY2sgcmVxdWVzdHMgYXJlIHRocm90dGxlZCBhbmQgd2lsbCBvbmx5IGNhdXNlIGFuIHVwZGF0ZSBpZiBtb3JlIGRhdGEgaXMgcmVxdWlyZWRcbiAgICAgICAgLy8gdG8gZmlsbCB0aGUgc2Nyb2xsaW5nIHZpZXcsIGFuZCBpdCBpc24ndCBhbHJlYWR5IGxvYWRpbmcgc29tZS5cbiAgICAgICAgLy8gTG9hZCByZXF1ZXN0cyBhcmUgbm90IHRocm90dGxlZCBhbmQgYWx3YXlzIHJlcXVlc3QgYSBwYWdlIG9mIGRhdGEuXG4gICAgICAgIHRoaXMuX3VwZGF0ZVJlcXVlc3RzLnBpcGUoZmlsdGVyT3BlcmF0b3IocmVxdWVzdCA9PiByZXF1ZXN0LmNoZWNrKSwgYXVkaXRUaW1lKDIwMCksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUodGhpcy5kb1JlcXVlc3QuYmluZCh0aGlzKSk7XG4gICAgICAgIHRoaXMuX3VwZGF0ZVJlcXVlc3RzLnBpcGUoZmlsdGVyT3BlcmF0b3IocmVxdWVzdCA9PiAhcmVxdWVzdC5jaGVjayksIHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUodGhpcy5kb1JlcXVlc3QuYmluZCh0aGlzKSk7XG5cbiAgICAgICAgaWYgKHRoaXMuZW5hYmxlZCkge1xuICAgICAgICAgICAgLy8gU3Vic2NyaWJlIHRvIHNjcm9sbCBldmVudHMgYW5kIERPTSBjaGFuZ2VzLlxuICAgICAgICAgICAgdGhpcy5hdHRhY2hFdmVudEhhbmRsZXJzKCk7XG4gICAgICAgIH1cblxuICAgICAgICAvLyBDb25uZWN0IHRoZSBMb2FkIE1vcmUgYnV0dG9uIHZpc2libGUgc3RhdGUuXG4gICAgICAgIHRoaXMuX2NhbkxvYWRNYW51YWxseS5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoY2FuTG9hZCA9PiB7XG4gICAgICAgICAgICB0aGlzLl9sb2FkQnV0dG9uUXVlcnkuZm9yRWFjaChsb2FkQnV0dG9uID0+IHtcbiAgICAgICAgICAgICAgICBsb2FkQnV0dG9uLnZpc2libGUgPSBjYW5Mb2FkO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuXG4gICAgICAgIC8vIENvbm5lY3QgdGhlIGxvYWRpbmcgaW5kaWNhdG9yIHZpc2libGUgc3RhdGUuXG4gICAgICAgIHRoaXMuX2lzTG9hZGluZy5waXBlKHRha2VVbnRpbCh0aGlzLl9vbkRlc3Ryb3kpKS5zdWJzY3JpYmUoaXNMb2FkaW5nID0+IHtcbiAgICAgICAgICAgIHRoaXMuX2xvYWRpbmdJbmRpY2F0b3JRdWVyeS5mb3JFYWNoKGxvYWRpbmcgPT4ge1xuICAgICAgICAgICAgICAgIGxvYWRpbmcudmlzaWJsZSA9IGlzTG9hZGluZztcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBMaW5rIHRoZSBMb2FkIE1vcmUgYnV0dG9uIGNsaWNrIGV2ZW50IHRvIHRyaWdnZXIgYW4gdXBkYXRlLlxuICAgICAgICB0aGlzLmF0dGFjaExvYWRCdXR0b25FdmVudHMoKTtcbiAgICAgICAgdGhpcy5fbG9hZEJ1dHRvblF1ZXJ5LmNoYW5nZXMucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgICAgICAgIHRoaXMuYXR0YWNoTG9hZEJ1dHRvbkV2ZW50cygpO1xuICAgICAgICB9KTtcblxuICAgICAgICAvLyBJbml0aWFsIHVwZGF0ZS5cbiAgICAgICAgaWYgKHRoaXMubG9hZE9uSW5pdCkge1xuICAgICAgICAgICAgdGhpcy5sb2FkTmV4dFBhZ2UoKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIG5nT25DaGFuZ2VzKGNoYW5nZXM6IFNpbXBsZUNoYW5nZXMpOiB2b2lkIHtcbiAgICAgICAgbGV0IGNoZWNrID0gdHJ1ZTtcblxuICAgICAgICBpZiAoY2hhbmdlcy5lbmFibGVkICYmIGNoYW5nZXMuZW5hYmxlZC5jdXJyZW50VmFsdWUgIT09IGNoYW5nZXMuZW5hYmxlZC5wcmV2aW91c1ZhbHVlKSB7XG4gICAgICAgICAgICBpZiAoY2hhbmdlcy5lbmFibGVkLmN1cnJlbnRWYWx1ZSkge1xuICAgICAgICAgICAgICAgIHRoaXMuYXR0YWNoRXZlbnRIYW5kbGVycygpO1xuICAgICAgICAgICAgICAgIHRoaXMucmVzZXQoKTtcbiAgICAgICAgICAgICAgICBjaGVjayA9IGZhbHNlO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICB0aGlzLmRldGFjaEV2ZW50SGFuZGxlcnMoKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLmVuYWJsZWQpIHtcblxuICAgICAgICAgICAgaWYgKGNoYW5nZXMuZmlsdGVyICYmIHRoaXMuY29lcmNlRmlsdGVyKGNoYW5nZXMuZmlsdGVyLmN1cnJlbnRWYWx1ZSkgIT09IHRoaXMuY29lcmNlRmlsdGVyKGNoYW5nZXMuZmlsdGVyLnByZXZpb3VzVmFsdWUpKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZXNldCgpO1xuICAgICAgICAgICAgICAgIGNoZWNrID0gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIGlmIChjaGFuZ2VzLmxvYWRPblNjcm9sbCkge1xuICAgICAgICAgICAgICAgIHRoaXMuX2xvYWRCdXR0b25FbmFibGVkLm5leHQoXG4gICAgICAgICAgICAgICAgICAgICFjaGFuZ2VzLmxvYWRPblNjcm9sbC5jdXJyZW50VmFsdWVcbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICBpZiAoY2hhbmdlcy5wYWdlU2l6ZSAmJiBjaGFuZ2VzLnBhZ2VTaXplLmN1cnJlbnRWYWx1ZSAhPT0gY2hhbmdlcy5wYWdlU2l6ZS5wcmV2aW91c1ZhbHVlKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZXNldCgpO1xuICAgICAgICAgICAgICAgIGNoZWNrID0gZmFsc2U7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICAgIHRoaXMuX3VwZGF0ZVJlcXVlc3RzLm5leHQoe1xuICAgICAgICAgICAgICAgIGNoZWNrLFxuICAgICAgICAgICAgICAgIHBhZ2VOdW1iZXI6IHRoaXMuX25leHRQYWdlTnVtLFxuICAgICAgICAgICAgICAgIHBhZ2VTaXplOiB0aGlzLnBhZ2VTaXplLFxuICAgICAgICAgICAgICAgIGZpbHRlcjogdGhpcy5jb2VyY2VGaWx0ZXIodGhpcy5maWx0ZXIpIGFzIFRcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgbmdPbkRlc3Ryb3koKTogdm9pZCB7XG4gICAgICAgIHRoaXMuZGV0YWNoRXZlbnRIYW5kbGVycygpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZXF1ZXN0IGFuIGFkZGl0aW9uYWwgcGFnZSBvZiBkYXRhLlxuICAgICAqL1xuICAgIGxvYWROZXh0UGFnZSgpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmVuYWJsZWQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3VwZGF0ZVJlcXVlc3RzLm5leHQoe1xuICAgICAgICAgICAgY2hlY2s6IGZhbHNlLFxuICAgICAgICAgICAgcGFnZU51bWJlcjogdGhpcy5fbmV4dFBhZ2VOdW0sXG4gICAgICAgICAgICBwYWdlU2l6ZTogdGhpcy5wYWdlU2l6ZSxcbiAgICAgICAgICAgIGZpbHRlcjogdGhpcy5jb2VyY2VGaWx0ZXIodGhpcy5maWx0ZXIpIGFzIFRcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVxdWVzdCBhIGNoZWNrIGZvciB3aGV0aGVyIGFuIGFkZGl0aW9uYWwgcGFnZSBvZiBkYXRhIGlzIHJlcXVpcmVkLiBUaGlzIGlzIHRocm90dGxlZC5cbiAgICAgKi9cbiAgICBjaGVjaygpOiB2b2lkIHtcbiAgICAgICAgaWYgKCF0aGlzLmVuYWJsZWQpIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX3VwZGF0ZVJlcXVlc3RzLm5leHQoe1xuICAgICAgICAgICAgY2hlY2s6IHRydWUsXG4gICAgICAgICAgICBwYWdlTnVtYmVyOiB0aGlzLl9uZXh0UGFnZU51bSxcbiAgICAgICAgICAgIHBhZ2VTaXplOiB0aGlzLnBhZ2VTaXplLFxuICAgICAgICAgICAgZmlsdGVyOiB0aGlzLmNvZXJjZUZpbHRlcih0aGlzLmZpbHRlcikgYXMgVFxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciB0aGUgY29sbGVjdGlvbi4gRnV0dXJlIHJlcXVlc3RzIHdpbGwgbG9hZCBmcm9tIHBhZ2UgMC5cbiAgICAgKi9cbiAgICByZXNldChjbGVhclN1YnNjcmlwdGlvbnM6IGJvb2xlYW4gPSB0cnVlKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5lbmFibGVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBSZXNldCB0aGUgcGFnZSBjb3VudGVyLlxuICAgICAgICB0aGlzLl9uZXh0UGFnZU51bSA9IDA7XG5cbiAgICAgICAgLy8gQ2xlYXIgdGhlIGNvbGxlY3Rpb24gKHdpdGhvdXQgY2hhbmdpbmcgdGhlIHJlZmVyZW5jZSkuXG4gICAgICAgIGlmICh0aGlzLmNvbGxlY3Rpb24pIHtcbiAgICAgICAgICAgIHRoaXMuY29sbGVjdGlvbi5sZW5ndGggPSAwO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHRoaXMuX3N1YnNjcmlwdGlvbnMgJiYgY2xlYXJTdWJzY3JpcHRpb25zKSB7XG4gICAgICAgICAgICB0aGlzLl9wYWdlcyA9IFtdO1xuXG4gICAgICAgICAgICAvLyBSZXNldCB0aGUgZXhoYXVzdGVkIGZsYWcsIGFsbG93aW5nIHRoZSBMb2FkIE1vcmUgYnV0dG9uIHRvIGFwcGVhci5cbiAgICAgICAgICAgIHRoaXMuX2lzRXhoYXVzdGVkLm5leHQoZmFsc2UpO1xuXG4gICAgICAgICAgICAvLyByZXNldCB0aGUgbG9hZGluZyBzdGF0ZVxuICAgICAgICAgICAgdGhpcy5faXNMb2FkaW5nLm5leHQoZmFsc2UpO1xuXG4gICAgICAgICAgICAvLyBDYW5jZWwgYW55IHBlbmRpbmcgcmVxdWVzdHNcbiAgICAgICAgICAgIHRoaXMuX3N1YnNjcmlwdGlvbnMuZm9yRWFjaChyZXF1ZXN0ID0+IHJlcXVlc3QudW5zdWJzY3JpYmUoKSk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBSZWxvYWQgdGhlIGRhdGEgd2l0aG91dCBjbGVhcmluZyB0aGUgdmlldy5cbiAgICAgKi9cbiAgICByZWxvYWQoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX3BhZ2VzPy5mb3JFYWNoKChwYWdlLCBpKSA9PiB0aGlzLnJlbG9hZFBhZ2UoaSkpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFJlbG9hZCB0aGUgZGF0YSBpbiBhIHNwZWNpZmljIHBhZ2Ugd2l0aG91dCBjbGVhcmluZyB0aGUgdmlldy5cbiAgICAgKiBAcGFyYW0gcGFnZU51bSBQYWdlIG51bWJlclxuICAgICAqL1xuICAgIHJlbG9hZFBhZ2UocGFnZU51bTogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIGlmICghdGhpcy5lbmFibGVkKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl91cGRhdGVSZXF1ZXN0cy5uZXh0KHtcbiAgICAgICAgICAgIGNoZWNrOiBmYWxzZSxcbiAgICAgICAgICAgIHBhZ2VOdW1iZXI6IHBhZ2VOdW0sXG4gICAgICAgICAgICBwYWdlU2l6ZTogdGhpcy5wYWdlU2l6ZSxcbiAgICAgICAgICAgIGZpbHRlcjogdGhpcy5jb2VyY2VGaWx0ZXIodGhpcy5maWx0ZXIpIGFzIFQsXG4gICAgICAgICAgICByZWxvYWQ6IHRydWVcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgLyoqIEEgZmlsdGVyIHZhbHVlIG9mIG51bGwgb3IgdW5kZWZpbmVkIHNob3VsZCBiZSBjb25zaWRlcmVkIHRoZSBzYW1lIGFzIGFuIGVtcHR5IHN0cmluZyAqL1xuICAgIHByaXZhdGUgY29lcmNlRmlsdGVyKHZhbHVlOiBUIHwgbnVsbCB8IHVuZGVmaW5lZCk6IFQgfCBzdHJpbmcge1xuICAgICAgICByZXR1cm4gdmFsdWUgPT09IHVuZGVmaW5lZCB8fCB2YWx1ZSA9PT0gbnVsbCA/ICcnIDogdmFsdWU7XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogQXR0YWNoIHNjcm9sbCBldmVudCBoYW5kbGVyIGFuZCBET00gb2JzZXJ2ZXIuXG4gICAgICovXG4gICAgcHJpdmF0ZSBhdHRhY2hFdmVudEhhbmRsZXJzKCk6IHZvaWQge1xuXG4gICAgICAgIC8vIGlmIHRoZSBzY3JvbGxFbGVtZW50IGlzIGRvY3VtZW50RWxlbWVudCB3ZSBtdXN0IHdhdGNoIGZvciBhIHNjcm9sbCBldmVudCBvbiB0aGUgZG9jdW1lbnRcbiAgICAgICAgY29uc3QgdGFyZ2V0ID0gdGhpcy5fc2Nyb2xsRWxlbWVudC5uYXRpdmVFbGVtZW50IGluc3RhbmNlb2YgSFRNTEh0bWxFbGVtZW50ID8gZG9jdW1lbnQgOiB0aGlzLl9zY3JvbGxFbGVtZW50Lm5hdGl2ZUVsZW1lbnQ7XG5cbiAgICAgICAgLy8gU3Vic2NyaWJlIHRvIHRoZSBzY3JvbGwgZXZlbnQgb24gdGhlIHRhcmdldCBlbGVtZW50LlxuICAgICAgICB0aGlzLl9zY3JvbGxFdmVudFN1YiA9IGZyb21FdmVudCh0YXJnZXQsICdzY3JvbGwnKS5zdWJzY3JpYmUodGhpcy5jaGVjay5iaW5kKHRoaXMpKTtcblxuICAgICAgICAvLyBTdWJzY3JpYmUgdG8gY2hpbGQgRE9NIGNoYW5nZXMuIFRoZSBtYWluIGVmZmVjdCBvZiB0aGlzIGlzIHRvIGNoZWNrIHdoZXRoZXIgZXZlbiBtb3JlIGRhdGEgaXNcbiAgICAgICAgLy8gcmVxdWlyZWQgYWZ0ZXIgdGhlIGluaXRpYWwgbG9hZC5cbiAgICAgICAgdGhpcy5fZG9tT2JzZXJ2ZXIgPSBuZXcgTXV0YXRpb25PYnNlcnZlcih0aGlzLmNoZWNrLmJpbmQodGhpcykpO1xuICAgICAgICB0aGlzLl9kb21PYnNlcnZlci5vYnNlcnZlKHRoaXMuX3Njcm9sbEVsZW1lbnQubmF0aXZlRWxlbWVudCwge1xuICAgICAgICAgICAgY2hpbGRMaXN0OiB0cnVlLFxuICAgICAgICAgICAgc3VidHJlZTogdHJ1ZVxuICAgICAgICB9KTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBEZXRhY2ggc2Nyb2xsIGV2ZW50IGhhbmRsZXIgYW5kIERPTSBvYnNlcnZlci5cbiAgICAgKi9cbiAgICBwcml2YXRlIGRldGFjaEV2ZW50SGFuZGxlcnMoKTogdm9pZCB7XG4gICAgICAgIGlmICh0aGlzLl9zY3JvbGxFdmVudFN1Yikge1xuICAgICAgICAgICAgdGhpcy5fc2Nyb2xsRXZlbnRTdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgICAgICAgIHRoaXMuX3Njcm9sbEV2ZW50U3ViID0gbnVsbDtcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICh0aGlzLl9kb21PYnNlcnZlcikge1xuICAgICAgICAgICAgdGhpcy5fZG9tT2JzZXJ2ZXIuZGlzY29ubmVjdCgpO1xuICAgICAgICAgICAgdGhpcy5fZG9tT2JzZXJ2ZXIgPSBudWxsO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgLyoqXG4gICAgICogUmVtb3ZlIGFueSBleGlzdGluZyBldmVudCBzdWJzY3JpcHRpb25zIGZvciB0aGUgbG9hZCBidXR0b24gYGxvYWRpbmdgIGV2ZW50LCB0aGVuIGF0dGFjaFxuICAgICAqIHN1YnNjcmlwdGlvbnNcbiAgICAgKiBmb3IgYW55IGluIHRoZSBxdWVyeS5cbiAgICAgKi9cbiAgICBwcml2YXRlIGF0dGFjaExvYWRCdXR0b25FdmVudHMoKTogdm9pZCB7XG4gICAgICAgIHRoaXMuX2xvYWRCdXR0b25TdWJzY3JpcHRpb25zLmZvckVhY2gocyA9PiBzLnVuc3Vic2NyaWJlKCkpO1xuICAgICAgICB0aGlzLl9sb2FkQnV0dG9uU3Vic2NyaXB0aW9ucyA9IHRoaXMuX2xvYWRCdXR0b25RdWVyeS5tYXAoXG4gICAgICAgICAgICBsb2FkQnV0dG9uID0+IGxvYWRCdXR0b24ubG9hZGluZy5zdWJzY3JpYmUodGhpcy5sb2FkTmV4dFBhZ2UuYmluZCh0aGlzKSlcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDb25kaXRpb25hbGx5IGxvYWRzIGEgcGFnZSBpbnRvIHRoZSBjb2xsZWN0aW9uIGJhc2VkIG9uIGRpcmVjdGl2ZSBzdGF0ZSBhbmQgcmVxdWVzdCBwYXJhbWV0ZXJzLlxuICAgICAqL1xuICAgIHByaXZhdGUgZG9SZXF1ZXN0KHJlcXVlc3Q6IEluZmluaXRlU2Nyb2xsUmVxdWVzdDxUPik6IHZvaWQge1xuXG4gICAgICAgIC8vIExvYWQgYSBuZXcgcGFnZSBpZiB0aGUgc2Nyb2xsIHBvc2l0aW9uIGlzIGJleW9uZCB0aGUgdGhyZXNoaG9sZCBhbmQgaWYgdGhlIGNsaWVudCBjb2RlIGRpZCBub3RcbiAgICAgICAgLy8gY2FuY2VsLlxuICAgICAgICBpZiAodGhpcy5uZWVkc0RhdGEocmVxdWVzdCkgJiYgdGhpcy5iZWdpbkxvYWRpbmcocmVxdWVzdCkpIHtcblxuICAgICAgICAgICAgLy8gSW52b2tlIHRoZSBjYWxsYmFjayBsb2FkIGZ1bmN0aW9uLCB3aGljaCByZXR1cm5zIGEgcHJvbW9zZSBvciBwbGFpbiBkYXRhLlxuICAgICAgICAgICAgY29uc3QgbG9hZFJlc3VsdCA9IHRoaXMubG9