blob: 0d028a9c0971bc4852295ef797655bb9a4a63287 [file] [log] [blame]
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,extraRequire,missingReturn,uselessCode} checked by tsc
*/
import { Component, Input, Output, EventEmitter, ContentChild, ChangeDetectionStrategy, ChangeDetectorRef, QueryList, ViewChildren, ElementRef, HostListener, Renderer2 } from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import { Subject } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { TdVirtualScrollRowDirective } from './virtual-scroll-row.directive';
/** @type {?} */
const TD_VIRTUAL_OFFSET = 2;
/** @type {?} */
const SCROLL_DEBOUNCE = 200;
/**
* @record
*/
export function ITdVirtualScrollBottomEvent() { }
if (false) {
/** @type {?} */
ITdVirtualScrollBottomEvent.prototype.lastRow;
/** @type {?} */
ITdVirtualScrollBottomEvent.prototype.lastIndex;
}
export class TdVirtualScrollContainerComponent {
/**
* @param {?} _elementRef
* @param {?} _domSanitizer
* @param {?} _renderer
* @param {?} _changeDetectorRef
*/
constructor(_elementRef, _domSanitizer, _renderer, _changeDetectorRef) {
this._elementRef = _elementRef;
this._domSanitizer = _domSanitizer;
this._renderer = _renderer;
this._changeDetectorRef = _changeDetectorRef;
this._subs = [];
this._bottom = new Subject();
this._initialized = false;
this._totalHeight = 0;
this._hostHeight = 0;
this._scrollVerticalOffset = 0;
this._fromRow = 0;
this._toRow = 0;
/**
* bottom: function
* Method to be executed when user scrolled to the last item of the list.
* An [ITdVirtualScrollBottomEvent] event is emitted
*/
this.bottom = new EventEmitter();
/**
* trackBy?: TrackByFunction
* This accepts the same trackBy function [ngFor] does.
* https://angular.io/api/core/TrackByFunction
*/
this.trackBy = (index, item) => {
return item;
};
}
/**
* data: any[]
* List of items to virtually iterate on.
* @param {?} data
* @return {?}
*/
set data(data) {
this._data = data;
if (this._initialized) {
this._calculateVirtualRows();
}
this._changeDetectorRef.markForCheck();
}
/**
* @return {?}
*/
get data() {
return this._data;
}
/**
* @return {?}
*/
get virtualData() {
return this._virtualData;
}
/**
* @return {?}
*/
get rowHeight() {
if (this._rows && this._rows.toArray()[0]) {
return this._rows.toArray()[0].nativeElement.getBoundingClientRect().height;
}
return 0;
}
/**
* @return {?}
*/
get totalHeight() {
return this._totalHeight;
}
/**
* @return {?}
*/
get fromRow() {
return this._fromRow;
}
/**
* @return {?}
*/
get toRow() {
return this._toRow;
}
/**
* @return {?}
*/
get offsetTransform() {
return this._offsetTransform;
}
/**
* @return {?}
*/
ngAfterViewInit() {
this._subs.push(this._rows.changes.subscribe(() => {
this._calculateVirtualRows();
}));
this._initialized = true;
this._calculateVirtualRows();
this._subs.push(this._bottom.pipe(debounceTime(SCROLL_DEBOUNCE)).subscribe(() => {
this.bottom.emit({
lastRow: this._data[this._data.length - 1],
lastIndex: this.toRow,
});
}));
}
/**
* @return {?}
*/
ngAfterViewChecked() {
/** @type {?} */
let newHostHeight = this._elementRef.nativeElement.getBoundingClientRect().height;
if (this._hostHeight !== newHostHeight) {
this._hostHeight = newHostHeight;
if (this._initialized) {
this._calculateVirtualRows();
}
}
}
/**
* @return {?}
*/
ngOnDestroy() {
if (this._subs) {
this._subs.forEach((sub) => {
sub.unsubscribe();
});
}
}
/**
* @param {?} event
* @return {?}
*/
handleScroll(event) {
/** @type {?} */
let element = ((/** @type {?} */ (event.target)));
if (element) {
/** @type {?} */
let verticalScroll = element.scrollTop;
if (this._scrollVerticalOffset !== verticalScroll) {
this._scrollVerticalOffset = verticalScroll;
if (this._initialized) {
this._calculateVirtualRows();
}
}
if (this._initialized) {
// check to see if bottom was hit to throw the bottom event
if ((this._data.length * this.rowHeight) - (verticalScroll + this._hostHeight) === 0) {
this._bottom.next();
}
}
}
}
/**
* Method to refresh and recalculate the virtual rows
* e.g. after changing the [data] content
* @return {?}
*/
refresh() {
this._calculateVirtualRows();
}
/**
* Method to scroll to a specific row of the list.
* @param {?} row
* @return {?}
*/
scrollTo(row) {
this._elementRef.nativeElement.scrollTop = row * this.rowHeight;
this._changeDetectorRef.markForCheck();
}
/**
* Method to scroll to the start of the list.
* @return {?}
*/
scrollToStart() {
this.scrollTo(0);
this._changeDetectorRef.markForCheck();
}
/**
* Method to scroll to the end of the list.
* @return {?}
*/
scrollToEnd() {
this.scrollTo(this.totalHeight / this.rowHeight);
this._changeDetectorRef.markForCheck();
}
/**
* @return {?}
*/
_calculateVirtualRows() {
if (this._data) {
this._totalHeight = this._data.length * this.rowHeight;
/** @type {?} */
let fromRow = Math.floor((this._scrollVerticalOffset / this.rowHeight)) - TD_VIRTUAL_OFFSET;
this._fromRow = fromRow > 0 ? fromRow : 0;
/** @type {?} */
let range = Math.floor((this._hostHeight / this.rowHeight)) + (TD_VIRTUAL_OFFSET * 2);
/** @type {?} */
let toRow = range + this.fromRow;
if (isFinite(toRow) && toRow > this._data.length) {
toRow = this._data.length;
}
else if (!isFinite(toRow)) {
toRow = TD_VIRTUAL_OFFSET;
}
this._toRow = toRow;
}
else {
this._totalHeight = 0;
this._fromRow = 0;
this._toRow = 0;
}
/** @type {?} */
let offset = 0;
if (this._scrollVerticalOffset > (TD_VIRTUAL_OFFSET * this.rowHeight)) {
offset = this.fromRow * this.rowHeight;
}
this._offsetTransform = this._domSanitizer.bypassSecurityTrustStyle('translateY(' + (offset - this.totalHeight) + 'px)');
if (this._data) {
this._virtualData = this.data.slice(this.fromRow, this.toRow);
}
// mark for check at the end of the queue so we are sure
// that the changes will be marked
Promise.resolve().then(() => {
this._changeDetectorRef.markForCheck();
});
}
}
TdVirtualScrollContainerComponent.decorators = [
{ type: Component, args: [{
selector: 'td-virtual-scroll-container',
template: "<div [style.height.px]=\"totalHeight\"></div>\n<div [style.transform]=\"offsetTransform\"\n [style.position]=\"'absolute'\"\n [style.width.%]=\"100\">\n <ng-template let-row\n let-index=\"index\"\n ngFor\n [ngForOf]=\"virtualData\"\n [ngForTrackBy]=\"trackBy\">\n <div #rowElement\n [style.width.%]=\"100\">\n <ng-template *ngIf=\"_rowTemplate\"\n [ngTemplateOutlet]=\"_rowTemplate.templateRef\"\n [ngTemplateOutletContext]=\"{row: row,\n index: (fromRow + index),\n first: (fromRow + index) === 0,\n last: (fromRow + index) === (data.length - 1),\n odd: ((fromRow + index + 1) % 2) === 1,\n even: ((fromRow + index + 1) % 2) === 0}\">\n </ng-template>\n </div>\n </ng-template>\n</div>",
changeDetection: ChangeDetectionStrategy.OnPush,
styles: [":host{display:block;height:100%;width:100%;overflow:auto;position:relative}"]
}] }
];
/** @nocollapse */
TdVirtualScrollContainerComponent.ctorParameters = () => [
{ type: ElementRef },
{ type: DomSanitizer },
{ type: Renderer2 },
{ type: ChangeDetectorRef }
];
TdVirtualScrollContainerComponent.propDecorators = {
data: [{ type: Input, args: ['data',] }],
bottom: [{ type: Output }],
_rows: [{ type: ViewChildren, args: ['rowElement',] }],
_rowTemplate: [{ type: ContentChild, args: [TdVirtualScrollRowDirective,] }],
trackBy: [{ type: Input, args: ['trackBy',] }],
handleScroll: [{ type: HostListener, args: ['scroll', ['$event'],] }]
};
if (false) {
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._subs;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._bottom;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._initialized;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._totalHeight;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._hostHeight;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._scrollVerticalOffset;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._offsetTransform;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._fromRow;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._toRow;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._data;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._virtualData;
/**
* bottom: function
* Method to be executed when user scrolled to the last item of the list.
* An [ITdVirtualScrollBottomEvent] event is emitted
* @type {?}
*/
TdVirtualScrollContainerComponent.prototype.bottom;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._rows;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._rowTemplate;
/**
* trackBy?: TrackByFunction
* This accepts the same trackBy function [ngFor] does.
* https://angular.io/api/core/TrackByFunction
* @type {?}
*/
TdVirtualScrollContainerComponent.prototype.trackBy;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._elementRef;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._domSanitizer;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._renderer;
/** @type {?} */
TdVirtualScrollContainerComponent.prototype._changeDetectorRef;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlydHVhbC1zY3JvbGwtY29udGFpbmVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiJuZzovL0Bjb3ZhbGVudC9jb3JlLyIsInNvdXJjZXMiOlsidmlydHVhbC1zY3JvbGwvdmlydHVhbC1zY3JvbGwtY29udGFpbmVyLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7O0FBQUEsT0FBTyxFQUFFLFNBQVMsRUFBYSxLQUFLLEVBQUUsTUFBTSxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQy9ELHVCQUF1QixFQUFFLGlCQUFpQixFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFDN0YsU0FBUyxFQUFnRCxNQUFNLGVBQWUsQ0FBQztBQUN4RixPQUFPLEVBQUUsWUFBWSxFQUFhLE1BQU0sMkJBQTJCLENBQUM7QUFFcEUsT0FBTyxFQUFnQixPQUFPLEVBQUUsTUFBTSxNQUFNLENBQUM7QUFDN0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRTlDLE9BQU8sRUFBRSwyQkFBMkIsRUFBRSxNQUFNLGdDQUFnQyxDQUFDOztNQUV2RSxpQkFBaUIsR0FBVyxDQUFDOztNQUM3QixlQUFlLEdBQVcsR0FBRzs7OztBQUVuQyxpREFHQzs7O0lBRkMsOENBQWE7O0lBQ2IsZ0RBQWtCOztBQVNwQixNQUFNLE9BQU8saUNBQWlDOzs7Ozs7O0lBdUU1QyxZQUFvQixXQUF1QixFQUN2QixhQUEyQixFQUMzQixTQUFvQixFQUNwQixrQkFBcUM7UUFIckMsZ0JBQVcsR0FBWCxXQUFXLENBQVk7UUFDdkIsa0JBQWEsR0FBYixhQUFhLENBQWM7UUFDM0IsY0FBUyxHQUFULFNBQVMsQ0FBVztRQUNwQix1QkFBa0IsR0FBbEIsa0JBQWtCLENBQW1CO1FBeEVqRCxVQUFLLEdBQW1CLEVBQUUsQ0FBQztRQUMzQixZQUFPLEdBQWlCLElBQUksT0FBTyxFQUFFLENBQUM7UUFDdEMsaUJBQVksR0FBWSxLQUFLLENBQUM7UUFFOUIsaUJBQVksR0FBVyxDQUFDLENBQUM7UUFDekIsZ0JBQVcsR0FBVyxDQUFDLENBQUM7UUFDeEIsMEJBQXFCLEdBQVcsQ0FBQyxDQUFDO1FBR2xDLGFBQVEsR0FBVyxDQUFDLENBQUM7UUFDckIsV0FBTSxHQUFXLENBQUMsQ0FBQzs7Ozs7O1FBOEJqQixXQUFNLEdBQThDLElBQUksWUFBWSxFQUErQixDQUFDOzs7Ozs7UUEwRTVGLFlBQU8sR0FBMEIsQ0FBQyxLQUFhLEVBQUUsSUFBUyxFQUFFLEVBQUU7WUFDOUUsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDLENBQUE7SUE1QzJELENBQUM7Ozs7Ozs7SUFyRDdELElBQ0ksSUFBSSxDQUFDLElBQVc7UUFDbEIsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUM7UUFDbEIsSUFBSSxJQUFJLENBQUMsWUFBWSxFQUFFO1lBQ3JCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1NBQzlCO1FBQ0QsSUFBSSxDQUFDLGtCQUFrQixDQUFDLFlBQVksRUFBRSxDQUFDO0lBQ3pDLENBQUM7Ozs7SUFDRCxJQUFJLElBQUk7UUFDTixPQUFPLElBQUksQ0FBQyxLQUFLLENBQUM7SUFDcEIsQ0FBQzs7OztJQUVELElBQUksV0FBVztRQUNiLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQztJQUMzQixDQUFDOzs7O0lBYUQsSUFBSSxTQUFTO1FBQ1gsSUFBSSxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUU7WUFDekMsT0FBTyxJQUFJLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLE1BQU0sQ0FBQztTQUM3RTtRQUNELE9BQU8sQ0FBQyxDQUFDO0lBQ1gsQ0FBQzs7OztJQUVELElBQUksV0FBVztRQUNiLE9BQU8sSUFBSSxDQUFDLFlBQVksQ0FBQztJQUMzQixDQUFDOzs7O0lBRUQsSUFBSSxPQUFPO1FBQ1QsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDO0lBQ3ZCLENBQUM7Ozs7SUFFRCxJQUFJLEtBQUs7UUFDUCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUM7SUFDckIsQ0FBQzs7OztJQUVELElBQUksZUFBZTtRQUNqQixPQUFPLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQztJQUMvQixDQUFDOzs7O0lBT0QsZUFBZTtRQUNiLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxHQUFHLEVBQUU7WUFDaEQsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7UUFDL0IsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNKLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO1FBRTdCLElBQUksQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUMvQixZQUFZLENBQUMsZUFBZSxDQUFDLENBQzlCLENBQUMsU0FBUyxDQUFDLEdBQUcsRUFBRTtZQUNmLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDO2dCQUNmLE9BQU8sRUFBRSxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLENBQUMsQ0FBQztnQkFDMUMsU0FBUyxFQUFFLElBQUksQ0FBQyxLQUFLO2FBQ3RCLENBQUMsQ0FBQztRQUNMLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDTixDQUFDOzs7O0lBRUQsa0JBQWtCOztZQUNaLGFBQWEsR0FBVyxJQUFJLENBQUMsV0FBVyxDQUFDLGFBQWEsQ0FBQyxxQkFBcUIsRUFBRSxDQUFDLE1BQU07UUFDekYsSUFBSSxJQUFJLENBQUMsV0FBVyxLQUFLLGFBQWEsRUFBRTtZQUN0QyxJQUFJLENBQUMsV0FBVyxHQUFHLGFBQWEsQ0FBQztZQUNqQyxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7Z0JBQ3JCLElBQUksQ0FBQyxxQkFBcUIsRUFBRSxDQUFDO2FBQzlCO1NBQ0Y7SUFDSCxDQUFDOzs7O0lBRUQsV0FBVztRQUNULElBQUksSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNkLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsR0FBaUIsRUFBRSxFQUFFO2dCQUN2QyxHQUFHLENBQUMsV0FBVyxFQUFFLENBQUM7WUFDcEIsQ0FBQyxDQUFDLENBQUM7U0FDSjtJQUNILENBQUM7Ozs7O0lBWUQsWUFBWSxDQUFDLEtBQVk7O1lBQ25CLE9BQU8sR0FBZ0IsQ0FBQyxtQkFBYSxLQUFLLENBQUMsTUFBTSxFQUFBLENBQUM7UUFDdEQsSUFBSSxPQUFPLEVBQUU7O2dCQUNQLGNBQWMsR0FBVyxPQUFPLENBQUMsU0FBUztZQUM5QyxJQUFJLElBQUksQ0FBQyxxQkFBcUIsS0FBSyxjQUFjLEVBQUU7Z0JBQ2pELElBQUksQ0FBQyxxQkFBcUIsR0FBRyxjQUFjLENBQUM7Z0JBQzVDLElBQUksSUFBSSxDQUFDLFlBQVksRUFBRTtvQkFDckIsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7aUJBQzlCO2FBQ0Y7WUFDRCxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUU7Z0JBQ3JCLDJEQUEyRDtnQkFDM0QsSUFBSSxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsR0FBRyxDQUFDLGNBQWMsR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxFQUFFO29CQUNwRixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksRUFBRSxDQUFDO2lCQUNyQjthQUNGO1NBQ0Y7SUFDSCxDQUFDOzs7Ozs7SUFNRCxPQUFPO1FBQ0wsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7SUFDL0IsQ0FBQzs7Ozs7O0lBS0QsUUFBUSxDQUFDLEdBQVc7UUFDbEIsSUFBSSxDQUFDLFdBQVcsQ0FBQyxhQUFhLENBQUMsU0FBUyxHQUFHLEdBQUcsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDO1FBQ2hFLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN6QyxDQUFDOzs7OztJQUtELGFBQWE7UUFDWCxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ2pCLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN6QyxDQUFDOzs7OztJQUtELFdBQVc7UUFDVCxJQUFJLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxXQUFXLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ2pELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN6QyxDQUFDOzs7O0lBRU8scUJBQXFCO1FBQzNCLElBQUksSUFBSSxDQUFDLEtBQUssRUFBRTtZQUNkLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQzs7Z0JBQ25ELE9BQU8sR0FBVyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsSUFBSSxDQUFDLHFCQUFxQixHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLGlCQUFpQjtZQUNuRyxJQUFJLENBQUMsUUFBUSxHQUFHLE9BQU8sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDOztnQkFDdEMsS0FBSyxHQUFXLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsV0FBVyxHQUFHLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxHQUFHLENBQUMsaUJBQWlCLEdBQUcsQ0FBQyxDQUFDOztnQkFDekYsS0FBSyxHQUFXLEtBQUssR0FBRyxJQUFJLENBQUMsT0FBTztZQUN4QyxJQUFJLFFBQVEsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEVBQUU7Z0JBQ2hELEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQzthQUMzQjtpQkFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxFQUFFO2dCQUMzQixLQUFLLEdBQUcsaUJBQWlCLENBQUM7YUFDM0I7WUFDRCxJQUFJLENBQUMsTUFBTSxHQUFHLEtBQUssQ0FBQztTQUNyQjthQUFNO1lBQ0wsSUFBSSxDQUFDLFlBQVksR0FBRyxDQUFDLENBQUM7WUFDdEIsSUFBSSxDQUFDLFFBQVEsR0FBRyxDQUFDLENBQUM7WUFDbEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUM7U0FDakI7O1lBRUcsTUFBTSxHQUFXLENBQUM7UUFDdEIsSUFBSSxJQUFJLENBQUMscUJBQXFCLEdBQUcsQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLEVBQUU7WUFDckUsTUFBTSxHQUFHLElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQztTQUN4QztRQUVELElBQUksQ0FBQyxnQkFBZ0IsR0FBRyxJQUFJLENBQUMsYUFBYSxDQUFDLHdCQUF3QixDQUFDLGFBQWEsR0FBRyxDQUFDLE1BQU0sR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLEdBQUcsS0FBSyxDQUFDLENBQUM7UUFDekgsSUFBSSxJQUFJLENBQUMsS0FBSyxFQUFFO1lBQ2QsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztTQUMvRDtRQUVELHdEQUF3RDtRQUN4RCxrQ0FBa0M7UUFDbEMsT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDMUIsSUFBSSxDQUFDLGtCQUFrQixDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ3pDLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzs7O1lBcE5GLFNBQVMsU0FBQztnQkFDVCxRQUFRLEVBQUUsNkJBQTZCO2dCQUV2QyxxL0JBQXdEO2dCQUN4RCxlQUFlLEVBQUUsdUJBQXVCLENBQUMsTUFBTTs7YUFDaEQ7Ozs7WUF0QjZFLFVBQVU7WUFFL0UsWUFBWTtZQURaLFNBQVM7WUFEZ0IsaUJBQWlCOzs7bUJBNENoRCxLQUFLLFNBQUMsTUFBTTtxQkFxQlosTUFBTTtvQkFFTixZQUFZLFNBQUMsWUFBWTsyQkFFekIsWUFBWSxTQUFDLDJCQUEyQjtzQkFzRXhDLEtBQUssU0FBQyxTQUFTOzJCQUlmLFlBQVksU0FBQyxRQUFRLEVBQUUsQ0FBQyxRQUFRLENBQUM7Ozs7SUF0SGxDLGtEQUFtQzs7SUFDbkMsb0RBQThDOztJQUM5Qyx5REFBc0M7O0lBRXRDLHlEQUFpQzs7SUFDakMsd0RBQWdDOztJQUNoQyxrRUFBMEM7O0lBQzFDLDZEQUFvQzs7SUFFcEMscURBQTZCOztJQUM3QixtREFBMkI7O0lBRTNCLGtEQUFxQjs7SUFDckIseURBQTRCOzs7Ozs7O0lBMkI1QixtREFBOEc7O0lBRTlHLGtEQUF5RDs7SUFFekQseURBQXFGOzs7Ozs7O0lBc0VyRixvREFFQzs7SUEvQ1csd0RBQStCOztJQUMvQiwwREFBbUM7O0lBQ25DLHNEQUE0Qjs7SUFDNUIsK0RBQTZDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgQ29tcG9uZW50LCBEaXJlY3RpdmUsIElucHV0LCBPdXRwdXQsIEV2ZW50RW1pdHRlciwgQ29udGVudENoaWxkLCBBZnRlclZpZXdJbml0LCBWaWV3Q2hpbGQsXG4gICAgICAgICBDaGFuZ2VEZXRlY3Rpb25TdHJhdGVneSwgQ2hhbmdlRGV0ZWN0b3JSZWYsIFF1ZXJ5TGlzdCwgVmlld0NoaWxkcmVuLCBFbGVtZW50UmVmLCBIb3N0TGlzdGVuZXIsXG4gICAgICAgICBSZW5kZXJlcjIsIEFmdGVyVmlld0NoZWNrZWQsIE9uRGVzdHJveSwgVHJhY2tCeUZ1bmN0aW9uIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5pbXBvcnQgeyBEb21TYW5pdGl6ZXIsIFNhZmVTdHlsZSB9IGZyb20gJ0Bhbmd1bGFyL3BsYXRmb3JtLWJyb3dzZXInO1xuXG5pbXBvcnQgeyBTdWJzY3JpcHRpb24sIFN1YmplY3QgfSBmcm9tICdyeGpzJztcbmltcG9ydCB7IGRlYm91bmNlVGltZSB9IGZyb20gJ3J4anMvb3BlcmF0b3JzJztcblxuaW1wb3J0IHsgVGRWaXJ0dWFsU2Nyb2xsUm93RGlyZWN0aXZlIH0gZnJvbSAnLi92aXJ0dWFsLXNjcm9sbC1yb3cuZGlyZWN0aXZlJztcblxuY29uc3QgVERfVklSVFVBTF9PRkZTRVQ6IG51bWJlciA9IDI7XG5jb25zdCBTQ1JPTExfREVCT1VOQ0U6IG51bWJlciA9IDIwMDtcblxuZXhwb3J0IGludGVyZmFjZSBJVGRWaXJ0dWFsU2Nyb2xsQm90dG9tRXZlbnQge1xuICBsYXN0Um93OiBhbnk7XG4gIGxhc3RJbmRleDogbnVtYmVyO1xufVxuXG5AQ29tcG9uZW50KHtcbiAgc2VsZWN0b3I6ICd0ZC12aXJ0dWFsLXNjcm9sbC1jb250YWluZXInLFxuICBzdHlsZVVybHM6IFsnLi92aXJ0dWFsLXNjcm9sbC1jb250YWluZXIuY29tcG9uZW50LnNjc3MnIF0sXG4gIHRlbXBsYXRlVXJsOiAnLi92aXJ0dWFsLXNjcm9sbC1jb250YWluZXIuY29tcG9uZW50Lmh0bWwnLFxuICBjaGFuZ2VEZXRlY3Rpb246IENoYW5nZURldGVjdGlvblN0cmF0ZWd5Lk9uUHVzaCxcbn0pXG5leHBvcnQgY2xhc3MgVGRWaXJ0dWFsU2Nyb2xsQ29udGFpbmVyQ29tcG9uZW50IGltcGxlbWVudHMgQWZ0ZXJWaWV3SW5pdCwgQWZ0ZXJWaWV3Q2hlY2tlZCwgT25EZXN0cm95IHtcblxuICBwcml2YXRlIF9zdWJzOiBTdWJzY3JpcHRpb25bXSA9IFtdO1xuICBwcml2YXRlIF9ib3R0b206IFN1YmplY3Q8YW55PiA9IG5ldyBTdWJqZWN0KCk7XG4gIHByaXZhdGUgX2luaXRpYWxpemVkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSBfdG90YWxIZWlnaHQ6IG51bWJlciA9IDA7XG4gIHByaXZhdGUgX2hvc3RIZWlnaHQ6IG51bWJlciA9IDA7XG4gIHByaXZhdGUgX3Njcm9sbFZlcnRpY2FsT2Zmc2V0OiBudW1iZXIgPSAwO1xuICBwcml2YXRlIF9vZmZzZXRUcmFuc2Zvcm06IFNhZmVTdHlsZTtcblxuICBwcml2YXRlIF9mcm9tUm93OiBudW1iZXIgPSAwO1xuICBwcml2YXRlIF90b1JvdzogbnVtYmVyID0gMDtcblxuICBwcml2YXRlIF9kYXRhOiBhbnlbXTtcbiAgcHJpdmF0ZSBfdmlydHVhbERhdGE6IGFueVtdO1xuXG4gIC8qKlxuICAgKiBkYXRhOiBhbnlbXVxuICAgKiBMaXN0IG9mIGl0ZW1zIHRvIHZpcnR1YWxseSBpdGVyYXRlIG9uLlxuICAgKi9cbiAgQElucHV0KCdkYXRhJylcbiAgc2V0IGRhdGEoZGF0YTogYW55W10pIHtcbiAgICB0aGlzLl9kYXRhID0gZGF0YTtcbiAgICBpZiAodGhpcy5faW5pdGlhbGl6ZWQpIHtcbiAgICAgIHRoaXMuX2NhbGN1bGF0ZVZpcnR1YWxSb3dzKCk7XG4gICAgfVxuICAgIHRoaXMuX2NoYW5nZURldGVjdG9yUmVmLm1hcmtGb3JDaGVjaygpO1xuICB9XG4gIGdldCBkYXRhKCk6IGFueVtdIHtcbiAgICByZXR1cm4gdGhpcy5fZGF0YTtcbiAgfVxuXG4gIGdldCB2aXJ0dWFsRGF0YSgpOiBhbnlbXSB7XG4gICAgcmV0dXJuIHRoaXMuX3ZpcnR1YWxEYXRhO1xuICB9XG5cbiAgLyoqXG4gICAqIGJvdHRvbTogZnVuY3Rpb25cbiAgICogTWV0aG9kIHRvIGJlIGV4ZWN1dGVkIHdoZW4gdXNlciBzY3JvbGxlZCB0byB0aGUgbGFzdCBpdGVtIG9mIHRoZSBsaXN0LlxuICAgKiBBbiBbSVRkVmlydHVhbFNjcm9sbEJvdHRvbUV2ZW50XSBldmVudCBpcyBlbWl0dGVkXG4gICAqL1xuICBAT3V0cHV0KCkgYm90dG9tOiBFdmVudEVtaXR0ZXI8SVRkVmlydHVhbFNjcm9sbEJvdHRvbUV2ZW50PiA9IG5ldyBFdmVudEVtaXR0ZXI8SVRkVmlydHVhbFNjcm9sbEJvdHRvbUV2ZW50PigpO1xuXG4gIEBWaWV3Q2hpbGRyZW4oJ3Jvd0VsZW1lbnQnKSBfcm93czogUXVlcnlMaXN0PEVsZW1lbnRSZWY+O1xuXG4gIEBDb250ZW50Q2hpbGQoVGRWaXJ0dWFsU2Nyb2xsUm93RGlyZWN0aXZlKSBfcm93VGVtcGxhdGU6IFRkVmlydHVhbFNjcm9sbFJvd0RpcmVjdGl2ZTtcblxuICBnZXQgcm93SGVpZ2h0KCk6IG51bWJlciB7XG4gICAgaWYgKHRoaXMuX3Jvd3MgJiYgdGhpcy5fcm93cy50b0FycmF5KClbMF0pIHtcbiAgICAgIHJldHVybiB0aGlzLl9yb3dzLnRvQXJyYXkoKVswXS5uYXRpdmVFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmhlaWdodDtcbiAgICB9XG4gICAgcmV0dXJuIDA7XG4gIH1cblxuICBnZXQgdG90YWxIZWlnaHQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5fdG90YWxIZWlnaHQ7XG4gIH1cblxuICBnZXQgZnJvbVJvdygpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLl9mcm9tUm93O1xuICB9XG5cbiAgZ2V0IHRvUm93KCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX3RvUm93O1xuICB9XG5cbiAgZ2V0IG9mZnNldFRyYW5zZm9ybSgpOiBTYWZlU3R5bGUge1xuICAgIHJldHVybiB0aGlzLl9vZmZzZXRUcmFuc2Zvcm07XG4gIH1cblxuICBjb25zdHJ1Y3Rvcihwcml2YXRlIF9lbGVtZW50UmVmOiBFbGVtZW50UmVmLFxuICAgICAgICAgICAgICBwcml2YXRlIF9kb21TYW5pdGl6ZXI6IERvbVNhbml0aXplcixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfcmVuZGVyZXI6IFJlbmRlcmVyMixcbiAgICAgICAgICAgICAgcHJpdmF0ZSBfY2hhbmdlRGV0ZWN0b3JSZWY6IENoYW5nZURldGVjdG9yUmVmKSB7fVxuXG4gIG5nQWZ0ZXJWaWV3SW5pdCgpOiB2b2lkIHtcbiAgICB0aGlzLl9zdWJzLnB1c2godGhpcy5fcm93cy5jaGFuZ2VzLnN1YnNjcmliZSgoKSA9PiB7XG4gICAgICB0aGlzLl9jYWxjdWxhdGVWaXJ0dWFsUm93cygpO1xuICAgIH0pKTtcbiAgICB0aGlzLl9pbml0aWFsaXplZCA9IHRydWU7XG4gICAgdGhpcy5fY2FsY3VsYXRlVmlydHVhbFJvd3MoKTtcblxuICAgIHRoaXMuX3N1YnMucHVzaCh0aGlzLl9ib3R0b20ucGlwZShcbiAgICAgIGRlYm91bmNlVGltZShTQ1JPTExfREVCT1VOQ0UpLFxuICAgICkuc3Vic2NyaWJlKCgpID0+IHtcbiAgICAgIHRoaXMuYm90dG9tLmVtaXQoe1xuICAgICAgICBsYXN0Um93OiB0aGlzLl9kYXRhW3RoaXMuX2RhdGEubGVuZ3RoIC0gMV0sXG4gICAgICAgIGxhc3RJbmRleDogdGhpcy50b1JvdyxcbiAgICAgIH0pO1xuICAgIH0pKTtcbiAgfVxuXG4gIG5nQWZ0ZXJWaWV3Q2hlY2tlZCgpOiB2b2lkIHtcbiAgICBsZXQgbmV3SG9zdEhlaWdodDogbnVtYmVyID0gdGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpLmhlaWdodDtcbiAgICBpZiAodGhpcy5faG9zdEhlaWdodCAhPT0gbmV3SG9zdEhlaWdodCkge1xuICAgICAgdGhpcy5faG9zdEhlaWdodCA9IG5ld0hvc3RIZWlnaHQ7XG4gICAgICBpZiAodGhpcy5faW5pdGlhbGl6ZWQpIHtcbiAgICAgICAgdGhpcy5fY2FsY3VsYXRlVmlydHVhbFJvd3MoKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBuZ09uRGVzdHJveSgpOiB2b2lkIHtcbiAgICBpZiAodGhpcy5fc3Vicykge1xuICAgICAgdGhpcy5fc3Vicy5mb3JFYWNoKChzdWI6IFN1YnNjcmlwdGlvbikgPT4ge1xuICAgICAgICBzdWIudW5zdWJzY3JpYmUoKTtcbiAgICAgIH0pO1xuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiB0cmFja0J5PzogVHJhY2tCeUZ1bmN0aW9uXG4gICAqIFRoaXMgYWNjZXB0cyB0aGUgc2FtZSB0cmFja0J5IGZ1bmN0aW9uIFtuZ0Zvcl0gZG9lcy5cbiAgICogaHR0cHM6Ly9hbmd1bGFyLmlvL2FwaS9jb3JlL1RyYWNrQnlGdW5jdGlvblxuICAgKi9cbiAgQElucHV0KCd0cmFja0J5JykgdHJhY2tCeTogVHJhY2tCeUZ1bmN0aW9uPGFueT4gPSAgKGluZGV4OiBudW1iZXIsIGl0ZW06IGFueSkgPT4ge1xuICAgIHJldHVybiBpdGVtO1xuICB9XG5cbiAgQEhvc3RMaXN0ZW5lcignc2Nyb2xsJywgWyckZXZlbnQnXSlcbiAgaGFuZGxlU2Nyb2xsKGV2ZW50OiBFdmVudCk6IHZvaWQge1xuICAgIGxldCBlbGVtZW50OiBIVE1MRWxlbWVudCA9ICg8SFRNTEVsZW1lbnQ+ZXZlbnQudGFyZ2V0KTtcbiAgICBpZiAoZWxlbWVudCkge1xuICAgICAgbGV0IHZlcnRpY2FsU2Nyb2xsOiBudW1iZXIgPSBlbGVtZW50LnNjcm9sbFRvcDtcbiAgICAgIGlmICh0aGlzLl9zY3JvbGxWZXJ0aWNhbE9mZnNldCAhPT0gdmVydGljYWxTY3JvbGwpIHtcbiAgICAgICAgdGhpcy5fc2Nyb2xsVmVydGljYWxPZmZzZXQgPSB2ZXJ0aWNhbFNjcm9sbDtcbiAgICAgICAgaWYgKHRoaXMuX2luaXRpYWxpemVkKSB7XG4gICAgICAgICAgdGhpcy5fY2FsY3VsYXRlVmlydHVhbFJvd3MoKTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKHRoaXMuX2luaXRpYWxpemVkKSB7XG4gICAgICAgIC8vIGNoZWNrIHRvIHNlZSBpZiBib3R0b20gd2FzIGhpdCB0byB0aHJvdyB0aGUgYm90dG9tIGV2ZW50XG4gICAgICAgIGlmICgodGhpcy5fZGF0YS5sZW5ndGggKiB0aGlzLnJvd0hlaWdodCkgLSAodmVydGljYWxTY3JvbGwgKyB0aGlzLl9ob3N0SGVpZ2h0KSA9PT0gMCkge1xuICAgICAgICAgIHRoaXMuX2JvdHRvbS5uZXh0KCk7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIHJlZnJlc2ggYW5kIHJlY2FsY3VsYXRlIHRoZSB2aXJ0dWFsIHJvd3NcbiAgICogZS5nLiBhZnRlciBjaGFuZ2luZyB0aGUgW2RhdGFdIGNvbnRlbnRcbiAgICovXG4gIHJlZnJlc2goKTogdm9pZCB7XG4gICAgdGhpcy5fY2FsY3VsYXRlVmlydHVhbFJvd3MoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBNZXRob2QgdG8gc2Nyb2xsIHRvIGEgc3BlY2lmaWMgcm93IG9mIHRoZSBsaXN0LlxuICAgKi9cbiAgc2Nyb2xsVG8ocm93OiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gcm93ICogdGhpcy5yb3dIZWlnaHQ7XG4gICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIHNjcm9sbCB0byB0aGUgc3RhcnQgb2YgdGhlIGxpc3QuXG4gICAqL1xuICBzY3JvbGxUb1N0YXJ0KCk6IHZvaWQge1xuICAgIHRoaXMuc2Nyb2xsVG8oMCk7XG4gICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gIH1cblxuICAvKipcbiAgICogTWV0aG9kIHRvIHNjcm9sbCB0byB0aGUgZW5kIG9mIHRoZSBsaXN0LlxuICAgKi9cbiAgc2Nyb2xsVG9FbmQoKTogdm9pZCB7XG4gICAgdGhpcy5zY3JvbGxUbyh0aGlzLnRvdGFsSGVpZ2h0IC8gdGhpcy5yb3dIZWlnaHQpO1xuICAgIHRoaXMuX2NoYW5nZURldGVjdG9yUmVmLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgcHJpdmF0ZSBfY2FsY3VsYXRlVmlydHVhbFJvd3MoKTogdm9pZCB7XG4gICAgaWYgKHRoaXMuX2RhdGEpIHtcbiAgICAgIHRoaXMuX3RvdGFsSGVpZ2h0ID0gdGhpcy5fZGF0YS5sZW5ndGggKiB0aGlzLnJvd0hlaWdodDtcbiAgICAgIGxldCBmcm9tUm93OiBudW1iZXIgPSBNYXRoLmZsb29yKCh0aGlzLl9zY3JvbGxWZXJ0aWNhbE9mZnNldCAvIHRoaXMucm93SGVpZ2h0KSkgLSBURF9WSVJUVUFMX09GRlNFVDtcbiAgICAgIHRoaXMuX2Zyb21Sb3cgPSBmcm9tUm93ID4gMCA/IGZyb21Sb3cgOiAwO1xuICAgICAgbGV0IHJhbmdlOiBudW1iZXIgPSBNYXRoLmZsb29yKCh0aGlzLl9ob3N0SGVpZ2h0IC8gdGhpcy5yb3dIZWlnaHQpKSArIChURF9WSVJUVUFMX09GRlNFVCAqIDIpO1xuICAgICAgbGV0IHRvUm93OiBudW1iZXIgPSByYW5nZSArIHRoaXMuZnJvbVJvdztcbiAgICAgIGlmIChpc0Zpbml0ZSh0b1JvdykgJiYgdG9Sb3cgPiB0aGlzLl9kYXRhLmxlbmd0aCkge1xuICAgICAgICB0b1JvdyA9IHRoaXMuX2RhdGEubGVuZ3RoO1xuICAgICAgfSBlbHNlIGlmICghaXNGaW5pdGUodG9Sb3cpKSB7XG4gICAgICAgIHRvUm93ID0gVERfVklSVFVBTF9PRkZTRVQ7XG4gICAgICB9XG4gICAgICB0aGlzLl90b1JvdyA9IHRvUm93O1xuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLl90b3RhbEhlaWdodCA9IDA7XG4gICAgICB0aGlzLl9mcm9tUm93ID0gMDtcbiAgICAgIHRoaXMuX3RvUm93ID0gMDtcbiAgICB9XG5cbiAgICBsZXQgb2Zmc2V0OiBudW1iZXIgPSAwO1xuICAgIGlmICh0aGlzLl9zY3JvbGxWZXJ0aWNhbE9mZnNldCA+IChURF9WSVJUVUFMX09GRlNFVCAqIHRoaXMucm93SGVpZ2h0KSkge1xuICAgICAgb2Zmc2V0ID0gdGhpcy5mcm9tUm93ICogdGhpcy5yb3dIZWlnaHQ7XG4gICAgfVxuXG4gICAgdGhpcy5fb2Zmc2V0VHJhbnNmb3JtID0gdGhpcy5fZG9tU2FuaXRpemVyLmJ5cGFzc1NlY3VyaXR5VHJ1c3RTdHlsZSgndHJhbnNsYXRlWSgnICsgKG9mZnNldCAtIHRoaXMudG90YWxIZWlnaHQpICsgJ3B4KScpO1xuICAgIGlmICh0aGlzLl9kYXRhKSB7XG4gICAgICB0aGlzLl92aXJ0dWFsRGF0YSA9IHRoaXMuZGF0YS5zbGljZSh0aGlzLmZyb21Sb3csIHRoaXMudG9Sb3cpO1xuICAgIH1cblxuICAgIC8vIG1hcmsgZm9yIGNoZWNrIGF0IHRoZSBlbmQgb2YgdGhlIHF1ZXVlIHNvIHdlIGFyZSBzdXJlXG4gICAgLy8gdGhhdCB0aGUgY2hhbmdlcyB3aWxsIGJlIG1hcmtlZFxuICAgIFByb21pc2UucmVzb2x2ZSgpLnRoZW4oKCkgPT4ge1xuICAgICAgdGhpcy5fY2hhbmdlRGV0ZWN0b3JSZWYubWFya0ZvckNoZWNrKCk7XG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==