UNPKG

@ux-aspects/ux-aspects

Version:

Open source user interface framework for building modern, responsive, mobile big data applications

154 lines 26.1 kB
import { END, HOME, PAGE_DOWN, PAGE_UP } from '@angular/cdk/keycodes'; import { Component, ContentChild, ElementRef, HostListener, inject, Input } from '@angular/core'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; import { TabbableListService } from '../../../directives/accessibility/index'; import { VirtualForDirective } from './virtual-for.directive'; import { VirtualForService } from './virtual-for.service'; import * as i0 from "@angular/core"; export class VirtualForContainerComponent { constructor() { /** Get the ElementRef of the container element */ this._elementRef = inject(ElementRef); /** A service to share values between the container and child elements */ this._virtualScroll = inject(VirtualForService); /** Handle key presses if there is a tabbable list */ this._tabbableList = inject(TabbableListService, { optional: true, self: true }); /** Keep a local reference of the dataset */ this._dataset = []; /** Indicate if the component has finished initialising */ this._initialized = false; /** Unsubscribe from all observables */ this._onDestroy = new Subject(); } /** Define the height of each virtual item */ set itemSize(itemSize) { this._virtualScroll.itemSize = itemSize; if (this._initialized) { requestAnimationFrame(() => { this.updateContainer(); this.virtualFor.updateContexts(); }); } } get itemSize() { return this._virtualScroll.itemSize; } /** Determine if this is a table */ get _isTable() { return this._elementRef.nativeElement.tagName === 'TABLE' || this._elementRef.nativeElement.tagName === 'TBODY'; } /** Determine if this is a list */ get _isList() { return this._elementRef.nativeElement.tagName === 'OL' || this._elementRef.nativeElement.tagName === 'UL'; } ngAfterViewInit() { // subscribe to changes to the dataset this._virtualScroll.dataset.pipe(takeUntil(this._onDestroy)).subscribe(dataset => { // store the latest dataset this._dataset = dataset; // update the container properties requestAnimationFrame(() => { this.updateContainer(); // mark the component as ready this._initialized = true; }); }); } ngOnDestroy() { this._onDestroy.next(); this._onDestroy.complete(); } updateContainer() { if (this.itemSize === 0 && this._dataset.length > 0) { this.itemSize = this.virtualFor.getHeight(this._dataset[0], this._dataset.length); } // calculate the total height of all the items this._totalHeight = this._dataset.length * this.itemSize; // get the actual height of the container element const height = this.getContainerHeight(); // determine the number of items it takes to fill the container height (multiply by 2 to give us some buffer items) const itemCount = Math.ceil((height / this.itemSize) * 2); /** Determine the number of items we have as a top buffer */ const topBufferCount = Math.ceil((height / this.itemSize) * 0.5); // get the scroll offset const scrollOffset = this.getScrollOffset(); // determine the start index based on the scroll offset const startIdx = Math.max(Math.floor(scrollOffset / this.itemSize) - Math.floor(topBufferCount), 0); // determine the end index based on the start and the number of items to display const endIdx = Math.min(startIdx + itemCount, this._dataset.length); // update the range this._range = { start: startIdx, end: endIdx }; // emit the new visible range this._virtualScroll.range.next(this._range); } /** If cells are automatically getting their height detected you may want to update the size */ recalculateCellSize() { this.itemSize = 0; } onKeydown(event, keyCode) { if (!this._tabbableList) { return; } switch (keyCode) { case PAGE_UP: this._tabbableList.focusKeyManager.setFirstItemActive(); event.preventDefault(); break; case PAGE_DOWN: this._tabbableList.focusKeyManager.setLastItemActive(); event.preventDefault(); break; case HOME: // ensure the QueryList doesn't do any updates until we have finished this._tabbableList.shouldFocusOnChange = false; // scroll to the top of the container this._elementRef.nativeElement.scrollTop = 0; // after the update the activate the first item requestAnimationFrame(() => { this._tabbableList.focusKeyManager.setFirstItemActive(); this._tabbableList.shouldFocusOnChange = true; }); event.preventDefault(); break; case END: // ensure the QueryList doesn't do any updates until we have finished this._tabbableList.shouldFocusOnChange = false; // scroll to the bottom of the container this._elementRef.nativeElement.scrollTop = this._elementRef.nativeElement.scrollHeight; // after the update the activate the last item requestAnimationFrame(() => { this._tabbableList.focusKeyManager.setLastItemActive(); this._tabbableList.shouldFocusOnChange = true; }); event.preventDefault(); break; } } getScrollOffset() { return this._elementRef.nativeElement.scrollTop; } getContainerHeight() { return this._elementRef.nativeElement.clientHeight; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: VirtualForContainerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); } static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: VirtualForContainerComponent, selector: "[uxVirtualForContainer]", inputs: { itemSize: "itemSize" }, host: { listeners: { "scroll": "updateContainer()", "keydown": "onKeydown($event,$event.keyCode)" }, properties: { "style.position": "\"relative\"" } }, providers: [VirtualForService], queries: [{ propertyName: "virtualFor", first: true, predicate: VirtualForDirective, descendants: true }], ngImport: i0, template: "<!-- Display the appropriate top spacer -->\n@if (_isTable) {\n <tr class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </tr>\n}\n\n@if (_isList) {\n <li class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </li>\n}\n\n@if (!_isTable && !_isList) {\n <div class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </div>\n}\n\n\n<ng-content></ng-content>\n" }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: VirtualForContainerComponent, decorators: [{ type: Component, args: [{ selector: '[uxVirtualForContainer]', providers: [VirtualForService], host: { '[style.position]': '"relative"' }, template: "<!-- Display the appropriate top spacer -->\n@if (_isTable) {\n <tr class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </tr>\n}\n\n@if (_isList) {\n <li class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </li>\n}\n\n@if (!_isTable && !_isList) {\n <div class=\"ux-virtual-scroll-spacer\"\n [style.height.px]=\"_totalHeight\"\n [style.padding.px]=\"0\"\n [style.margin.px]=\"0\"\n [style.border]=\"'none'\"\n >\n </div>\n}\n\n\n<ng-content></ng-content>\n" }] }], propDecorators: { itemSize: [{ type: Input }], virtualFor: [{ type: ContentChild, args: [VirtualForDirective, { static: false }] }], updateContainer: [{ type: HostListener, args: ['scroll'] }], onKeydown: [{ type: HostListener, args: ['keydown', ['$event', '$event.keyCode']] }] } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmlydHVhbC1mb3IuY29tcG9uZW50LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdmlydHVhbC1zY3JvbGwvdmlydHVhbC1mb3IvdmlydHVhbC1mb3IuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvdmlydHVhbC1zY3JvbGwvdmlydHVhbC1mb3IvdmlydHVhbC1mb3IuY29tcG9uZW50Lmh0bWwiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsU0FBUyxFQUFFLE9BQU8sRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQ3RFLE9BQU8sRUFBaUIsU0FBUyxFQUFFLFlBQVksRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxLQUFLLEVBQWEsTUFBTSxlQUFlLENBQUM7QUFDM0gsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUMvQixPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDM0MsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUNBQXlDLENBQUM7QUFDOUUsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUFtQixpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDOztBQVUzRSxNQUFNLE9BQU8sNEJBQTRCO0lBUnpDO1FBU0ksa0RBQWtEO1FBQ2pDLGdCQUFXLEdBQUcsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBRWxELHlFQUF5RTtRQUN4RCxtQkFBYyxHQUFHLE1BQU0sQ0FBdUIsaUJBQWlCLENBQUMsQ0FBQztRQUVsRixxREFBcUQ7UUFDcEMsa0JBQWEsR0FBRyxNQUFNLENBQUMsbUJBQW1CLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsQ0FB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LGtCQUFrQjtRQUN0QixPQUFRLElBQUksQ0FBQyxXQUFXLENBQUMsYUFBNkIsQ0FBQyxZQUFZLENBQUM7SUFDeEUsQ0FBQzsrR0E5S1EsNEJBQTRCO21HQUE1Qiw0QkFBNEIsNk9BTDFCLENBQUMsaUJBQWlCLENBQUMsa0VBeURoQixtQkFBbUIsZ0RDcEVyQyxtdEJBaUNBOzs0RkRqQmEsNEJBQTRCO2tCQVJ4QyxTQUFTOytCQUNJLHlCQUF5QixhQUV4QixDQUFDLGlCQUFpQixDQUFDLFFBQ3hCO3dCQUNGLGtCQUFrQixFQUFFLFlBQVk7cUJBQ25DOzhCQWFZLFFBQVE7c0JBQXBCLEtBQUs7Z0JBeUNnRCxVQUFVO3NCQUEvRCxZQUFZO3VCQUFDLG1CQUFtQixFQUFFLEVBQUUsTUFBTSxFQUFFLEtBQUssRUFBRTtnQkF3QnBELGVBQWU7c0JBRGQsWUFBWTt1QkFBQyxRQUFRO2dCQXlDdEIsU0FBUztzQkFEUixZQUFZO3VCQUFDLFNBQVMsRUFBRSxDQUFDLFFBQVEsRUFBRSxnQkFBZ0IsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEVORCwgSE9NRSwgUEFHRV9ET1dOLCBQQUdFX1VQIH0gZnJvbSAnQGFuZ3VsYXIvY2RrL2tleWNvZGVzJztcbmltcG9ydCB7IEFmdGVyVmlld0luaXQsIENvbXBvbmVudCwgQ29udGVudENoaWxkLCBFbGVtZW50UmVmLCBIb3N0TGlzdGVuZXIsIGluamVjdCwgSW5wdXQsIE9uRGVzdHJveSB9IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgU3ViamVjdCB9IGZyb20gJ3J4anMnO1xuaW1wb3J0IHsgdGFrZVVudGlsIH0gZnJvbSAncnhqcy9vcGVyYXRvcnMnO1xuaW1wb3J0IHsgVGFiYmFibGVMaXN0U2VydmljZSB9IGZyb20gJy4uLy4uLy4uL2RpcmVjdGl2ZXMvYWNjZXNzaWJpbGl0eS9pbmRleCc7XG5pbXBvcnQgeyBWaXJ0dWFsRm9yRGlyZWN0aXZlIH0gZnJvbSAnLi92aXJ0dWFsLWZvci5kaXJlY3RpdmUnO1xuaW1wb3J0IHsgVmlydHVhbEZvclJhbmdlLCBWaXJ0dWFsRm9yU2VydmljZSB9IGZyb20gJy4vdmlydHVhbC1mb3Iuc2VydmljZSc7XG5cbkBDb21wb25lbnQoe1xuICAgIHNlbGVjdG9yOiAnW3V4VmlydHVhbEZvckNvbnRhaW5lcl0nLFxuICAgIHRlbXBsYXRlVXJsOiAnLi92aXJ0dWFsLWZvci5jb21wb25lbnQuaHRtbCcsXG4gICAgcHJvdmlkZXJzOiBbVmlydHVhbEZvclNlcnZpY2VdLFxuICAgIGhvc3Q6IHtcbiAgICAgICAgJ1tzdHlsZS5wb3NpdGlvbl0nOiAnXCJyZWxhdGl2ZVwiJ1xuICAgIH1cbn0pXG5leHBvcnQgY2xhc3MgVmlydHVhbEZvckNvbnRhaW5lckNvbXBvbmVudDxUPiBpbXBsZW1lbnRzIEFmdGVyVmlld0luaXQsIE9uRGVzdHJveSB7XG4gICAgLyoqIEdldCB0aGUgRWxlbWVudFJlZiBvZiB0aGUgY29udGFpbmVyIGVsZW1lbnQgKi9cbiAgICBwcml2YXRlIHJlYWRvbmx5IF9lbGVtZW50UmVmID0gaW5qZWN0KEVsZW1lbnRSZWYpO1xuXG4gICAgLyoqIEEgc2VydmljZSB0byBzaGFyZSB2YWx1ZXMgYmV0d2VlbiB0aGUgY29udGFpbmVyIGFuZCBjaGlsZCBlbGVtZW50cyAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX3ZpcnR1YWxTY3JvbGwgPSBpbmplY3Q8VmlydHVhbEZvclNlcnZpY2U8VD4+KFZpcnR1YWxGb3JTZXJ2aWNlKTtcblxuICAgIC8qKiBIYW5kbGUga2V5IHByZXNzZXMgaWYgdGhlcmUgaXMgYSB0YWJiYWJsZSBsaXN0ICovXG4gICAgcHJpdmF0ZSByZWFkb25seSBfdGFiYmFibGVMaXN0ID0gaW5qZWN0KFRhYmJhYmxlTGlzdFNlcnZpY2UsIHsgb3B0aW9uYWw6IHRydWUsIHNlbGY6IHRydWUgfSk7XG5cbiAgICAvKiogRGVmaW5lIHRoZSBoZWlnaHQgb2YgZWFjaCB2aXJ0dWFsIGl0ZW0gKi9cbiAgICBASW5wdXQoKSBzZXQgaXRlbVNpemUoaXRlbVNpemU6IG51bWJlcikge1xuICAgICAgICB0aGlzLl92aXJ0dWFsU2Nyb2xsLml0ZW1TaXplID0gaXRlbVNpemU7XG5cbiAgICAgICAgaWYgKHRoaXMuX2luaXRpYWxpemVkKSB7XG4gICAgICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4ge1xuICAgICAgICAgICAgICAgIHRoaXMudXBkYXRlQ29udGFpbmVyKCk7XG4gICAgICAgICAgICAgICAgdGhpcy52aXJ0dWFsRm9yLnVwZGF0ZUNvbnRleHRzKCk7XG4gICAgICAgICAgICB9KTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIGdldCBpdGVtU2l6ZSgpOiBudW1iZXIge1xuICAgICAgICByZXR1cm4gdGhpcy5fdmlydHVhbFNjcm9sbC5pdGVtU2l6ZTtcbiAgICB9XG5cbiAgICAvKiogU3RvcmUgdGhlIGNvbnRhaW5lciBoZWlnaHQgKi9cbiAgICBfdG90YWxIZWlnaHQ6IG51bWJlcjtcblxuICAgIC8qKiBLZWVwIGEgbG9jYWwgcmVmZXJlbmNlIG9mIHRoZSBkYXRhc2V0ICovXG4gICAgcHJpdmF0ZSBfZGF0YXNldDogVFtdID0gW107XG5cbiAgICAvKiogU3RvcmUgdGhlIGN1cnJlbnQgdmlzaWJsZSByYW5nZSAqL1xuICAgIHByaXZhdGUgX3JhbmdlOiBWaXJ0dWFsRm9yUmFuZ2U7XG5cbiAgICAvKiogSW5kaWNhdGUgaWYgdGhlIGNvbXBvbmVudCBoYXMgZmluaXNoZWQgaW5pdGlhbGlzaW5nICovXG4gICAgcHJpdmF0ZSBfaW5pdGlhbGl6ZWQ6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKiBVbnN1YnNjcmliZSBmcm9tIGFsbCBvYnNlcnZhYmxlcyAqL1xuICAgIHByaXZhdGUgcmVhZG9ubHkgX29uRGVzdHJveSA9IG5ldyBTdWJqZWN0PHZvaWQ+KCk7XG5cbiAgICAvKiogRGV0ZXJtaW5lIGlmIHRoaXMgaXMgYSB0YWJsZSAqL1xuICAgIGdldCBfaXNUYWJsZSgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC50YWdOYW1lID09PSAnVEFCTEUnIHx8IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC50YWdOYW1lID09PSAnVEJPRFknO1xuICAgIH1cblxuICAgIC8qKiBEZXRlcm1pbmUgaWYgdGhpcyBpcyBhIGxpc3QgKi9cbiAgICBnZXQgX2lzTGlzdCgpOiBib29sZWFuIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC50YWdOYW1lID09PSAnT0wnIHx8IHRoaXMuX2VsZW1lbnRSZWYubmF0aXZlRWxlbWVudC50YWdOYW1lID09PSAnVUwnO1xuICAgIH1cblxuICAgIC8qKiBBY2Nlc3MgdGhlIHV4VmlydHVhbEZvciBjaGlsZCBkaXJlY3RpdmUgKi9cbiAgICBAQ29udGVudENoaWxkKFZpcnR1YWxGb3JEaXJlY3RpdmUsIHsgc3RhdGljOiBmYWxzZSB9KSB2aXJ0dWFsRm9yOiBWaXJ0dWFsRm9yRGlyZWN0aXZlPFQ+O1xuXG4gICAgbmdBZnRlclZpZXdJbml0KCk6IHZvaWQge1xuICAgICAgICAvLyBzdWJzY3JpYmUgdG8gY2hhbmdlcyB0byB0aGUgZGF0YXNldFxuICAgICAgICB0aGlzLl92aXJ0dWFsU2Nyb2xsLmRhdGFzZXQucGlwZSh0YWtlVW50aWwodGhpcy5fb25EZXN0cm95KSkuc3Vic2NyaWJlKGRhdGFzZXQgPT4ge1xuICAgICAgICAgICAgLy8gc3RvcmUgdGhlIGxhdGVzdCBkYXRhc2V0XG4gICAgICAgICAgICB0aGlzLl9kYXRhc2V0ID0gZGF0YXNldDtcblxuICAgICAgICAgICAgLy8gdXBkYXRlIHRoZSBjb250YWluZXIgcHJvcGVydGllc1xuICAgICAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHtcbiAgICAgICAgICAgICAgICB0aGlzLnVwZGF0ZUNvbnRhaW5lcigpO1xuXG4gICAgICAgICAgICAgICAgLy8gbWFyayB0aGUgY29tcG9uZW50IGFzIHJlYWR5XG4gICAgICAgICAgICAgICAgdGhpcy5faW5pdGlhbGl6ZWQgPSB0cnVlO1xuICAgICAgICAgICAgfSk7XG4gICAgICAgIH0pO1xuICAgIH1cblxuICAgIG5nT25EZXN0cm95KCk6IHZvaWQge1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kubmV4dCgpO1xuICAgICAgICB0aGlzLl9vbkRlc3Ryb3kuY29tcGxldGUoKTtcbiAgICB9XG5cbiAgICBASG9zdExpc3RlbmVyKCdzY3JvbGwnKVxuICAgIHVwZGF0ZUNvbnRhaW5lcigpOiB2b2lkIHtcblxuICAgICAgICBpZiAodGhpcy5pdGVtU2l6ZSA9PT0gMCAmJiB0aGlzLl9kYXRhc2V0Lmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIHRoaXMuaXRlbVNpemUgPSB0aGlzLnZpcnR1YWxGb3IuZ2V0SGVpZ2h0KHRoaXMuX2RhdGFzZXRbMF0sIHRoaXMuX2RhdGFzZXQubGVuZ3RoKTtcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIGNhbGN1bGF0ZSB0aGUgdG90YWwgaGVpZ2h0IG9mIGFsbCB0aGUgaXRlbXNcbiAgICAgICAgdGhpcy5fdG90YWxIZWlnaHQgPSB0aGlzLl9kYXRhc2V0Lmxlbmd0aCAqIHRoaXMuaXRlbVNpemU7XG5cbiAgICAgICAgLy8gZ2V0IHRoZSBhY3R1YWwgaGVpZ2h0IG9mIHRoZSBjb250YWluZXIgZWxlbWVudFxuICAgICAgICBjb25zdCBoZWlnaHQgPSB0aGlzLmdldENvbnRhaW5lckhlaWdodCgpO1xuXG4gICAgICAgIC8vIGRldGVybWluZSB0aGUgbnVtYmVyIG9mIGl0ZW1zIGl0IHRha2VzIHRvIGZpbGwgdGhlIGNvbnRhaW5lciBoZWlnaHQgKG11bHRpcGx5IGJ5IDIgdG8gZ2l2ZSB1cyBzb21lIGJ1ZmZlciBpdGVtcylcbiAgICAgICAgY29uc3QgaXRlbUNvdW50ID0gTWF0aC5jZWlsKChoZWlnaHQgLyB0aGlzLml0ZW1TaXplKSAqIDIpO1xuXG4gICAgICAgIC8qKiBEZXRlcm1pbmUgdGhlIG51bWJlciBvZiBpdGVtcyB3ZSBoYXZlIGFzIGEgdG9wIGJ1ZmZlciAqL1xuICAgICAgICBjb25zdCB0b3BCdWZmZXJDb3VudCA9IE1hdGguY2VpbCgoaGVpZ2h0IC8gdGhpcy5pdGVtU2l6ZSkgKiAwLjUpO1xuXG4gICAgICAgIC8vIGdldCB0aGUgc2Nyb2xsIG9mZnNldFxuICAgICAgICBjb25zdCBzY3JvbGxPZmZzZXQgPSB0aGlzLmdldFNjcm9sbE9mZnNldCgpO1xuXG4gICAgICAgIC8vIGRldGVybWluZSB0aGUgc3RhcnQgaW5kZXggYmFzZWQgb24gdGhlIHNjcm9sbCBvZmZzZXRcbiAgICAgICAgY29uc3Qgc3RhcnRJZHggPSBNYXRoLm1heChNYXRoLmZsb29yKHNjcm9sbE9mZnNldCAvIHRoaXMuaXRlbVNpemUpIC0gTWF0aC5mbG9vcih0b3BCdWZmZXJDb3VudCksIDApO1xuXG4gICAgICAgIC8vIGRldGVybWluZSB0aGUgZW5kIGluZGV4IGJhc2VkIG9uIHRoZSBzdGFydCBhbmQgdGhlIG51bWJlciBvZiBpdGVtcyB0byBkaXNwbGF5XG4gICAgICAgIGNvbnN0IGVuZElkeCA9IE1hdGgubWluKHN0YXJ0SWR4ICsgaXRlbUNvdW50LCB0aGlzLl9kYXRhc2V0Lmxlbmd0aCk7XG5cbiAgICAgICAgLy8gdXBkYXRlIHRoZSByYW5nZVxuICAgICAgICB0aGlzLl9yYW5nZSA9IHsgc3RhcnQ6IHN0YXJ0SWR4LCBlbmQ6IGVuZElkeCB9O1xuXG4gICAgICAgIC8vIGVtaXQgdGhlIG5ldyB2aXNpYmxlIHJhbmdlXG4gICAgICAgIHRoaXMuX3ZpcnR1YWxTY3JvbGwucmFuZ2UubmV4dCh0aGlzLl9yYW5nZSk7XG4gICAgfVxuXG4gICAgLyoqIElmIGNlbGxzIGFyZSBhdXRvbWF0aWNhbGx5IGdldHRpbmcgdGhlaXIgaGVpZ2h0IGRldGVjdGVkIHlvdSBtYXkgd2FudCB0byB1cGRhdGUgdGhlIHNpemUgKi9cbiAgICByZWNhbGN1bGF0ZUNlbGxTaXplKCk6IHZvaWQge1xuICAgICAgICB0aGlzLml0ZW1TaXplID0gMDtcbiAgICB9XG5cbiAgICBASG9zdExpc3RlbmVyKCdrZXlkb3duJywgWyckZXZlbnQnLCAnJGV2ZW50LmtleUNvZGUnXSlcbiAgICBvbktleWRvd24oZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGtleUNvZGU6IG51bWJlcik6IHZvaWQge1xuICAgICAgICBpZiAoIXRoaXMuX3RhYmJhYmxlTGlzdCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG5cbiAgICAgICAgc3dpdGNoIChrZXlDb2RlKSB7XG5cbiAgICAgICAgICAgIGNhc2UgUEFHRV9VUDpcbiAgICAgICAgICAgICAgICB0aGlzLl90YWJiYWJsZUxpc3QuZm9jdXNLZXlNYW5hZ2VyLnNldEZpcnN0SXRlbUFjdGl2ZSgpO1xuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgICAgIGNhc2UgUEFHRV9ET1dOOlxuICAgICAgICAgICAgICAgIHRoaXMuX3RhYmJhYmxlTGlzdC5mb2N1c0tleU1hbmFnZXIuc2V0TGFzdEl0ZW1BY3RpdmUoKTtcbiAgICAgICAgICAgICAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgICAgICAgICAgICAgIGJyZWFrO1xuXG4gICAgICAgICAgICBjYXNlIEhPTUU6XG4gICAgICAgICAgICAgICAgLy8gZW5zdXJlIHRoZSBRdWVyeUxpc3QgZG9lc24ndCBkbyBhbnkgdXBkYXRlcyB1bnRpbCB3ZSBoYXZlIGZpbmlzaGVkXG4gICAgICAgICAgICAgICAgdGhpcy5fdGFiYmFibGVMaXN0LnNob3VsZEZvY3VzT25DaGFuZ2UgPSBmYWxzZTtcblxuICAgICAgICAgICAgICAgIC8vIHNjcm9sbCB0byB0aGUgdG9wIG9mIHRoZSBjb250YWluZXJcbiAgICAgICAgICAgICAgICB0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gMDtcblxuICAgICAgICAgICAgICAgIC8vIGFmdGVyIHRoZSB1cGRhdGUgdGhlIGFjdGl2YXRlIHRoZSBmaXJzdCBpdGVtXG4gICAgICAgICAgICAgICAgcmVxdWVzdEFuaW1hdGlvbkZyYW1lKCgpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fdGFiYmFibGVMaXN0LmZvY3VzS2V5TWFuYWdlci5zZXRGaXJzdEl0ZW1BY3RpdmUoKTtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fdGFiYmFibGVMaXN0LnNob3VsZEZvY3VzT25DaGFuZ2UgPSB0cnVlO1xuICAgICAgICAgICAgICAgIH0pO1xuXG4gICAgICAgICAgICAgICAgZXZlbnQucHJldmVudERlZmF1bHQoKTtcbiAgICAgICAgICAgICAgICBicmVhaztcblxuICAgICAgICAgICAgY2FzZSBFTkQ6XG4gICAgICAgICAgICAgICAgLy8gZW5zdXJlIHRoZSBRdWVyeUxpc3QgZG9lc24ndCBkbyBhbnkgdXBkYXRlcyB1bnRpbCB3ZSBoYXZlIGZpbmlzaGVkXG4gICAgICAgICAgICAgICAgdGhpcy5fdGFiYmFibGVMaXN0LnNob3VsZEZvY3VzT25DaGFuZ2UgPSBmYWxzZTtcblxuICAgICAgICAgICAgICAgIC8vIHNjcm9sbCB0byB0aGUgYm90dG9tIG9mIHRoZSBjb250YWluZXJcbiAgICAgICAgICAgICAgICB0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQuc2Nyb2xsVG9wID0gdGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50LnNjcm9sbEhlaWdodDtcblxuICAgICAgICAgICAgICAgIC8vIGFmdGVyIHRoZSB1cGRhdGUgdGhlIGFjdGl2YXRlIHRoZSBsYXN0IGl0ZW1cbiAgICAgICAgICAgICAgICByZXF1ZXN0QW5pbWF0aW9uRnJhbWUoKCkgPT4ge1xuICAgICAgICAgICAgICAgICAgICB0aGlzLl90YWJiYWJsZUxpc3QuZm9jdXNLZXlNYW5hZ2VyLnNldExhc3RJdGVtQWN0aXZlKCk7XG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3RhYmJhYmxlTGlzdC5zaG91bGRGb2N1c09uQ2hhbmdlID0gdHJ1ZTtcbiAgICAgICAgICAgICAgICB9KTtcblxuICAgICAgICAgICAgICAgIGV2ZW50LnByZXZlbnREZWZhdWx0KCk7XG4gICAgICAgICAgICAgICAgYnJlYWs7XG5cbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgZ2V0U2Nyb2xsT2Zmc2V0KCk6IG51bWJlciB7XG4gICAgICAgIHJldHVybiAodGhpcy5fZWxlbWVudFJlZi5uYXRpdmVFbGVtZW50IGFzIEhUTUxFbGVtZW50KS5zY3JvbGxUb3A7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBnZXRDb250YWluZXJIZWlnaHQoKTogbnVtYmVyIHtcbiAgICAgICAgcmV0dXJuICh0aGlzLl9lbGVtZW50UmVmLm5hdGl2ZUVsZW1lbnQgYXMgSFRNTEVsZW1lbnQpLmNsaWVudEhlaWdodDtcbiAgICB9XG5cbn1cbiIsIjwhLS0gRGlzcGxheSB0aGUgYXBwcm9wcmlhdGUgdG9wIHNwYWNlciAtLT5cbkBpZiAoX2lzVGFibGUpIHtcbiAgPHRyIGNsYXNzPVwidXgtdmlydHVhbC1zY3JvbGwtc3BhY2VyXCJcbiAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cIl90b3RhbEhlaWdodFwiXG4gICAgW3N0eWxlLnBhZGRpbmcucHhdPVwiMFwiXG4gICAgW3N0eWxlLm1hcmdpbi5weF09XCIwXCJcbiAgICBbc3R5bGUuYm9yZGVyXT1cIidub25lJ1wiXG4gICAgPlxuICA8L3RyPlxufVxuXG5AaWYgKF9pc0xpc3QpIHtcbiAgPGxpIGNsYXNzPVwidXgtdmlydHVhbC1zY3JvbGwtc3BhY2VyXCJcbiAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cIl90b3RhbEhlaWdodFwiXG4gICAgW3N0eWxlLnBhZGRpbmcucHhdPVwiMFwiXG4gICAgW3N0eWxlLm1hcmdpbi5weF09XCIwXCJcbiAgICBbc3R5bGUuYm9yZGVyXT1cIidub25lJ1wiXG4gICAgPlxuICA8L2xpPlxufVxuXG5AaWYgKCFfaXNUYWJsZSAmJiAhX2lzTGlzdCkge1xuICA8ZGl2IGNsYXNzPVwidXgtdmlydHVhbC1zY3JvbGwtc3BhY2VyXCJcbiAgICBbc3R5bGUuaGVpZ2h0LnB4XT1cIl90b3RhbEhlaWdodFwiXG4gICAgW3N0eWxlLnBhZGRpbmcucHhdPVwiMFwiXG4gICAgW3N0eWxlLm1hcmdpbi5weF09XCIwXCJcbiAgICBbc3R5bGUuYm9yZGVyXT1cIidub25lJ1wiXG4gICAgPlxuICA8L2Rpdj5cbn1cblxuXG48bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4iXX0=