@ux-aspects/ux-aspects
Version:
Open source user interface framework for building modern, responsive, mobile big data applications
173 lines • 31.4 kB
JavaScript
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, forwardRef, inject, Input, Output, } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';
import * as i0 from "@angular/core";
import * as i1 from "../../directives/accessibility/focus-indicator/focus-indicator.directive";
import * as i2 from "@angular/common";
import * as i3 from "../../directives/focus-if/focus-if.directive";
import * as i4 from "../icon/icon.component";
export const PAGINATION_CONTROL_VALUE_ACCESSOR = {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => PaginationComponent),
multi: true,
};
export class PaginationComponent {
constructor() {
this._changeDetector = inject(ChangeDetectorRef);
/** Specify if we should show the next and previous buttons */
this.directionButtons = true;
/** Limit the number of pages shown at any given time */
this.maxSize = 5;
/** Specify if the component should be disabled */
this.disabled = false;
/** Aria Label for the component navigation */
this.ariaLabel = 'Pagination Navigation';
/** Aria label for the previous button */
this.previousAriaLabel = 'Navigate to the previous page';
/** Aria label for the next button */
this.nextAriaLabel = 'Navigate to the next page';
/** Emit the current page number */
this.pageChange = new EventEmitter();
/** Emit the total number of pages */
this.numPages = new EventEmitter();
/** Store a list of pages to display in the UI */
this.pages = [];
/** ControlValueAccessor functions */
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.onTouched = () => { };
// eslint-disable-next-line @typescript-eslint/no-empty-function
this.onChange = () => { };
this.isKeyboardEvent = false;
this._page = 1;
this._total = 100;
this._pagesize = 10;
}
/** Specify the index of the active page */
set page(page) {
// do nothing if the page has not changed
if (page === this._page) {
return;
}
this._page = page;
this.pages = this.getPages();
// mark this component as changed
this.onChange(this.page);
}
get page() {
return this._page;
}
/** Specify the page size */
set itemsPerPage(pagesize) {
this._pagesize = pagesize;
this.pages = this.getPages();
}
/** Specify how many items there are in total */
set totalItems(total) {
this._total = total;
this.pages = this.getPages();
}
get pageCount() {
return Math.ceil(this._total / this._pagesize);
}
ngOnInit() {
this.pages = this.getPages();
}
select(index) {
// find the page we want to go to
const target = this.pages.find(page => page.index === index);
// if the page is out of bounds then do nothing
if (!target) {
return;
}
// mark this component as touched
this.onTouched();
// set this as the current page
this.page = target.index;
// update the visible pages
this.pages = this.getPages();
// emit the current page
this.pageChange.emit(this.page);
}
trackByFn(_index, item) {
return item.index;
}
registerOnChange(fn) {
this.onChange = fn;
}
registerOnTouched(fn) {
this.onTouched = fn;
}
setDisabledState(isDisabled) {
this.disabled = isDisabled;
this._changeDetector.markForCheck();
}
writeValue(page) {
this.page = page;
this._changeDetector.markForCheck();
}
getPages() {
// create a new array to store the pages
const pages = [];
// create all possible pages
for (let index = 1; index <= this.pageCount; index++) {
pages.push({ index, visible: this.isPageVisible(index) });
}
// emit the number of pages
this.numPages.emit(this.pageCount);
return pages;
}
isPageVisible(index) {
// if we do not have a max size specified or the number of pages is less than the max size then it is always visible
if (!this.maxSize || this.pageCount <= this.maxSize) {
return true;
}
// find the starting position
let start = Math.max(1, Math.ceil(this.page - this.maxSize / 2));
const end = Math.min(start + this.maxSize, this.pageCount + 1);
// if the range is less than the max size we need to adjust the starting point
const range = end - start;
if (range < this.maxSize) {
start = start - (this.maxSize - range);
}
// if the item equals the start position or is less than the end position then show it
return index >= start && index < end;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: PaginationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.12", type: PaginationComponent, selector: "ux-pagination", inputs: { directionButtons: "directionButtons", maxSize: "maxSize", disabled: "disabled", classes: ["class", "classes"], pageBtnClass: "pageBtnClass", ariaLabel: ["aria-label", "ariaLabel"], previousAriaLabel: "previousAriaLabel", nextAriaLabel: "nextAriaLabel", page: "page", previousBtnTemplate: "previousBtnTemplate", nextBtnTemplate: "nextBtnTemplate", itemsPerPage: "itemsPerPage", totalItems: "totalItems" }, outputs: { pageChange: "pageChange", numPages: "numPages" }, providers: [PAGINATION_CONTROL_VALUE_ACCESSOR], ngImport: i0, template: "<nav role=\"navigation\" [attr.aria-label]=\"ariaLabel\">\n <ul #container class=\"pagination\"\n [ngClass]=\"classes\"\n direction=\"horizontal\"\n (blur)=\"isKeyboardEvent = false\"\n (keydown.ArrowLeft)=\"select(page - 1); isKeyboardEvent = true\"\n (keydown.ArrowRight)=\"select(page + 1); isKeyboardEvent = true\"\n (keydown.Home)=\"select(1); isKeyboardEvent = true; $event.preventDefault()\"\n (keydown.End)=\"select(pageCount); isKeyboardEvent = true; $event.preventDefault()\">\n\n @if (directionButtons) {\n <li class=\"pagination-prev page-item\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"page === 1 || disabled\">\n <button class=\"page-link\"\n [tabindex]=\"page === 1 || disabled ? -1 : 0\"\n [attr.aria-label]=\"previousAriaLabel\"\n [ngClass]=\"pageBtnClass\"\n (click)=\"select(page - 1)\"\n (keydown.enter)=\"select(page - 1)\">\n <ng-container [ngTemplateOutlet]=\"previousBtnTemplate || defaultPreviousBtnTemplate\"></ng-container>\n </button>\n </li>\n }\n\n @for (pg of pages; track trackByFn($index, pg)) {\n @if (pg.visible) {\n <li\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"disabled\"\n [attr.aria-setsize]=\"pageCount\"\n [attr.aria-posinset]=\"pg.index\"\n [class.active]=\"page === pg.index\"\n class=\"pagination-page page-item\">\n <button class=\"page-link\"\n tabindex=\"0\"\n [ngClass]=\"pageBtnClass\"\n [focusIf]=\"isKeyboardEvent && page === pg.index\"\n [attr.aria-current]=\"page === pg.index\"\n (click)=\"select(pg.index)\"\n (keydown.enter)=\"select(pg.index)\">\n {{ pg.index }}\n </button>\n </li>\n }\n }\n\n @if (directionButtons) {\n <li class=\"pagination-next page-item\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"page === pageCount || disabled\">\n <button class=\"page-link\"\n [tabindex]=\"page === pageCount || disabled ? -1 : 0\"\n [attr.aria-label]=\"nextAriaLabel\"\n [ngClass]=\"pageBtnClass\"\n (click)=\"select(page + 1)\"\n (keydown.enter)=\"select(page + 1)\">\n <ng-container [ngTemplateOutlet]=\"nextBtnTemplate || defaultNextBtnTemplate\"></ng-container>\n </button>\n </li>\n }\n </ul>\n</nav>\n\n<ng-template #defaultPreviousBtnTemplate>\n <ux-icon class=\"pagination-prev-icon-default\" name=\"previous\"></ux-icon>\n</ng-template>\n\n<ng-template #defaultNextBtnTemplate>\n <ux-icon class=\"pagination-next-icon-default\" name=\"next\"></ux-icon>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i1.FocusIndicatorDirective, selector: "[uxFocusIndicator]", inputs: ["checkChildren", "mouseFocusIndicator", "touchFocusIndicator", "keyboardFocusIndicator", "programmaticFocusIndicator"], outputs: ["indicator"], exportAs: ["ux-focus-indicator"] }, { kind: "directive", type: i2.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i3.FocusIfDirective, selector: "[focusIf]", inputs: ["focusIfDelay", "focusIfScroll", "focusIf"] }, { kind: "component", type: i4.IconComponent, selector: "ux-icon", inputs: ["name", "size", "rotate", "flipHorizontal", "flipVertical"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.12", ngImport: i0, type: PaginationComponent, decorators: [{
type: Component,
args: [{ selector: 'ux-pagination', providers: [PAGINATION_CONTROL_VALUE_ACCESSOR], changeDetection: ChangeDetectionStrategy.OnPush, template: "<nav role=\"navigation\" [attr.aria-label]=\"ariaLabel\">\n <ul #container class=\"pagination\"\n [ngClass]=\"classes\"\n direction=\"horizontal\"\n (blur)=\"isKeyboardEvent = false\"\n (keydown.ArrowLeft)=\"select(page - 1); isKeyboardEvent = true\"\n (keydown.ArrowRight)=\"select(page + 1); isKeyboardEvent = true\"\n (keydown.Home)=\"select(1); isKeyboardEvent = true; $event.preventDefault()\"\n (keydown.End)=\"select(pageCount); isKeyboardEvent = true; $event.preventDefault()\">\n\n @if (directionButtons) {\n <li class=\"pagination-prev page-item\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"page === 1 || disabled\">\n <button class=\"page-link\"\n [tabindex]=\"page === 1 || disabled ? -1 : 0\"\n [attr.aria-label]=\"previousAriaLabel\"\n [ngClass]=\"pageBtnClass\"\n (click)=\"select(page - 1)\"\n (keydown.enter)=\"select(page - 1)\">\n <ng-container [ngTemplateOutlet]=\"previousBtnTemplate || defaultPreviousBtnTemplate\"></ng-container>\n </button>\n </li>\n }\n\n @for (pg of pages; track trackByFn($index, pg)) {\n @if (pg.visible) {\n <li\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"disabled\"\n [attr.aria-setsize]=\"pageCount\"\n [attr.aria-posinset]=\"pg.index\"\n [class.active]=\"page === pg.index\"\n class=\"pagination-page page-item\">\n <button class=\"page-link\"\n tabindex=\"0\"\n [ngClass]=\"pageBtnClass\"\n [focusIf]=\"isKeyboardEvent && page === pg.index\"\n [attr.aria-current]=\"page === pg.index\"\n (click)=\"select(pg.index)\"\n (keydown.enter)=\"select(pg.index)\">\n {{ pg.index }}\n </button>\n </li>\n }\n }\n\n @if (directionButtons) {\n <li class=\"pagination-next page-item\"\n uxFocusIndicator\n [programmaticFocusIndicator]=\"true\"\n [checkChildren]=\"true\"\n [class.disabled]=\"page === pageCount || disabled\">\n <button class=\"page-link\"\n [tabindex]=\"page === pageCount || disabled ? -1 : 0\"\n [attr.aria-label]=\"nextAriaLabel\"\n [ngClass]=\"pageBtnClass\"\n (click)=\"select(page + 1)\"\n (keydown.enter)=\"select(page + 1)\">\n <ng-container [ngTemplateOutlet]=\"nextBtnTemplate || defaultNextBtnTemplate\"></ng-container>\n </button>\n </li>\n }\n </ul>\n</nav>\n\n<ng-template #defaultPreviousBtnTemplate>\n <ux-icon class=\"pagination-prev-icon-default\" name=\"previous\"></ux-icon>\n</ng-template>\n\n<ng-template #defaultNextBtnTemplate>\n <ux-icon class=\"pagination-next-icon-default\" name=\"next\"></ux-icon>\n</ng-template>\n" }]
}], propDecorators: { directionButtons: [{
type: Input
}], maxSize: [{
type: Input
}], disabled: [{
type: Input
}], classes: [{
type: Input,
args: ['class']
}], pageBtnClass: [{
type: Input
}], ariaLabel: [{
type: Input,
args: ['aria-label']
}], previousAriaLabel: [{
type: Input
}], nextAriaLabel: [{
type: Input
}], page: [{
type: Input
}], previousBtnTemplate: [{
type: Input
}], nextBtnTemplate: [{
type: Input
}], itemsPerPage: [{
type: Input
}], totalItems: [{
type: Input
}], pageChange: [{
type: Output
}], numPages: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy9wYWdpbmF0aW9uL3BhZ2luYXRpb24uY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vc3JjL2NvbXBvbmVudHMvcGFnaW5hdGlvbi9wYWdpbmF0aW9uLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFDTCx1QkFBdUIsRUFDdkIsaUJBQWlCLEVBQ2pCLFNBQVMsRUFDVCxZQUFZLEVBQ1osVUFBVSxFQUNWLE1BQU0sRUFDTixLQUFLLEVBRUwsTUFBTSxHQUVQLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBd0IsaUJBQWlCLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQzs7Ozs7O0FBRXpFLE1BQU0sQ0FBQyxNQUFNLGlDQUFpQyxHQUFHO0lBQy9DLE9BQU8sRUFBRSxpQkFBaUI7SUFDMUIsV0FBVyxFQUFFLFVBQVUsQ0FBQyxHQUFHLEVBQUUsQ0FBQyxtQkFBbUIsQ0FBQztJQUNsRCxLQUFLLEVBQUUsSUFBSTtDQUNaLENBQUM7QUFRRixNQUFNLE9BQU8sbUJBQW1CO0lBTmhDO1FBT21CLG9CQUFlLEdBQUcsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7UUFFN0QsOERBQThEO1FBQ3JELHFCQUFnQixHQUFZLElBQUksQ0FBQztRQUUxQyx3REFBd0Q7UUFDL0MsWUFBTyxHQUFXLENBQUMsQ0FBQztRQUU3QixrREFBa0Q7UUFDekMsYUFBUSxHQUFZLEtBQUssQ0FBQztRQVFuQyw4Q0FBOEM7UUFDekIsY0FBUyxHQUFXLHVCQUF1QixDQUFDO1FBRWpFLHlDQUF5QztRQUNoQyxzQkFBaUIsR0FBVywrQkFBK0IsQ0FBQztRQUVyRSxxQ0FBcUM7UUFDNUIsa0JBQWEsR0FBVywyQkFBMkIsQ0FBQztRQXNDN0QsbUNBQW1DO1FBQ3pCLGVBQVUsR0FBRyxJQUFJLFlBQVksRUFB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dGVjdG9yUmVmLFxuICBDb21wb25lbnQsXG4gIEV2ZW50RW1pdHRlcixcbiAgZm9yd2FyZFJlZixcbiAgaW5qZWN0LFxuICBJbnB1dCxcbiAgT25Jbml0LFxuICBPdXRwdXQsXG4gIFRlbXBsYXRlUmVmLFxufSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7IENvbnRyb2xWYWx1ZUFjY2Vzc29yLCBOR19WQUxVRV9BQ0NFU1NPUiB9IGZyb20gJ0Bhbmd1bGFyL2Zvcm1zJztcblxuZXhwb3J0IGNvbnN0IFBBR0lOQVRJT05fQ09OVFJPTF9WQUxVRV9BQ0NFU1NPUiA9IHtcbiAgcHJvdmlkZTogTkdfVkFMVUVfQUNDRVNTT1IsXG4gIHVzZUV4aXN0aW5nOiBmb3J3YXJkUmVmKCgpID0+IFBhZ2luYXRpb25Db21wb25lbnQpLFxuICBtdWx0aTogdHJ1ZSxcbn07XG5cbkBDb21wb25lbnQoe1xuICBzZWxlY3RvcjogJ3V4LXBhZ2luYXRpb24nLFxuICB0ZW1wbGF0ZVVybDogJy4vcGFnaW5hdGlvbi5jb21wb25lbnQuaHRtbCcsXG4gIHByb3ZpZGVyczogW1BBR0lOQVRJT05fQ09OVFJPTF9WQUxVRV9BQ0NFU1NPUl0sXG4gIGNoYW5nZURldGVjdGlvbjogQ2hhbmdlRGV0ZWN0aW9uU3RyYXRlZ3kuT25QdXNoLFxufSlcbmV4cG9ydCBjbGFzcyBQYWdpbmF0aW9uQ29tcG9uZW50IGltcGxlbWVudHMgT25Jbml0LCBDb250cm9sVmFsdWVBY2Nlc3NvciB7XG4gIHByaXZhdGUgcmVhZG9ubHkgX2NoYW5nZURldGVjdG9yID0gaW5qZWN0KENoYW5nZURldGVjdG9yUmVmKTtcblxuICAvKiogU3BlY2lmeSBpZiB3ZSBzaG91bGQgc2hvdyB0aGUgbmV4dCBhbmQgcHJldmlvdXMgYnV0dG9ucyAqL1xuICBASW5wdXQoKSBkaXJlY3Rpb25CdXR0b25zOiBib29sZWFuID0gdHJ1ZTtcblxuICAvKiogTGltaXQgdGhlIG51bWJlciBvZiBwYWdlcyBzaG93biBhdCBhbnkgZ2l2ZW4gdGltZSAqL1xuICBASW5wdXQoKSBtYXhTaXplOiBudW1iZXIgPSA1O1xuXG4gIC8qKiBTcGVjaWZ5IGlmIHRoZSBjb21wb25lbnQgc2hvdWxkIGJlIGRpc2FibGVkICovXG4gIEBJbnB1dCgpIGRpc2FibGVkOiBib29sZWFuID0gZmFsc2U7XG5cbiAgLyoqIEFwcGx5IGNsYXNzZXMgdG8gdGhlIGJvb3RzdHJhcCBwYWdpbmF0aW9uIGVsZW1lbnQgKi9cbiAgQElucHV0KCdjbGFzcycpIGNsYXNzZXM6IHN0cmluZztcblxuICAvKiogQWxsb3cgY3VzdG9tIGNsYXNzIHRvIGJlIGFkZGVkIHRvIHBhZ2UgYnV0dG9ucyAqL1xuICBASW5wdXQoKSBwYWdlQnRuQ2xhc3M6IHN0cmluZztcblxuICAvKiogQXJpYSBMYWJlbCBmb3IgdGhlIGNvbXBvbmVudCBuYXZpZ2F0aW9uICovXG4gIEBJbnB1dCgnYXJpYS1sYWJlbCcpIGFyaWFMYWJlbDogc3RyaW5nID0gJ1BhZ2luYXRpb24gTmF2aWdhdGlvbic7XG5cbiAgLyoqIEFyaWEgbGFiZWwgZm9yIHRoZSBwcmV2aW91cyBidXR0b24gKi9cbiAgQElucHV0KCkgcHJldmlvdXNBcmlhTGFiZWw6IHN0cmluZyA9ICdOYXZpZ2F0ZSB0byB0aGUgcHJldmlvdXMgcGFnZSc7XG5cbiAgLyoqIEFyaWEgbGFiZWwgZm9yIHRoZSBuZXh0IGJ1dHRvbiAqL1xuICBASW5wdXQoKSBuZXh0QXJpYUxhYmVsOiBzdHJpbmcgPSAnTmF2aWdhdGUgdG8gdGhlIG5leHQgcGFnZSc7XG5cbiAgLyoqIFNwZWNpZnkgdGhlIGluZGV4IG9mIHRoZSBhY3RpdmUgcGFnZSAqL1xuICBASW5wdXQoKSBzZXQgcGFnZShwYWdlOiBudW1iZXIpIHtcbiAgICAvLyBkbyBub3RoaW5nIGlmIHRoZSBwYWdlIGhhcyBub3QgY2hhbmdlZFxuICAgIGlmIChwYWdlID09PSB0aGlzLl9wYWdlKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuXG4gICAgdGhpcy5fcGFnZSA9IHBhZ2U7XG4gICAgdGhpcy5wYWdlcyA9IHRoaXMuZ2V0UGFnZXMoKTtcblxuICAgIC8vIG1hcmsgdGhpcyBjb21wb25lbnQgYXMgY2hhbmdlZFxuICAgIHRoaXMub25DaGFuZ2UodGhpcy5wYWdlKTtcbiAgfVxuXG4gIGdldCBwYWdlKCk6IG51bWJlciB7XG4gICAgcmV0dXJuIHRoaXMuX3BhZ2U7XG4gIH1cblxuICAvKiogRGVmaW5lIGEgY3VzdG9tIHRlbXBsYXRlIGZvciB0aGUgcHJldmlvdXMgYnV0dG9uICovXG4gIEBJbnB1dCgpIHByZXZpb3VzQnRuVGVtcGxhdGU6IFRlbXBsYXRlUmVmPHZvaWQ+O1xuXG4gIC8qKiBEZWZpbmUgYSBjdXN0b20gdGVtcGxhdGUgZm9yIHRoZSBuZXh0IGJ1dHRvbiAqL1xuICBASW5wdXQoKSBuZXh0QnRuVGVtcGxhdGU6IFRlbXBsYXRlUmVmPHZvaWQ+O1xuXG4gIC8qKiBTcGVjaWZ5IHRoZSBwYWdlIHNpemUgKi9cbiAgQElucHV0KCkgc2V0IGl0ZW1zUGVyUGFnZShwYWdlc2l6ZTogbnVtYmVyKSB7XG4gICAgdGhpcy5fcGFnZXNpemUgPSBwYWdlc2l6ZTtcbiAgICB0aGlzLnBhZ2VzID0gdGhpcy5nZXRQYWdlcygpO1xuICB9XG5cbiAgLyoqIFNwZWNpZnkgaG93IG1hbnkgaXRlbXMgdGhlcmUgYXJlIGluIHRvdGFsICovXG4gIEBJbnB1dCgpIHNldCB0b3RhbEl0ZW1zKHRvdGFsOiBudW1iZXIpIHtcbiAgICB0aGlzLl90b3RhbCA9IHRvdGFsO1xuICAgIHRoaXMucGFnZXMgPSB0aGlzLmdldFBhZ2VzKCk7XG4gIH1cblxuICAvKiogRW1pdCB0aGUgY3VycmVudCBwYWdlIG51bWJlciAqL1xuICBAT3V0cHV0KCkgcGFnZUNoYW5nZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuXG4gIC8qKiBFbWl0IHRoZSB0b3RhbCBudW1iZXIgb2YgcGFnZXMgKi9cbiAgQE91dHB1dCgpIG51bVBhZ2VzID0gbmV3IEV2ZW50RW1pdHRlcjxudW1iZXI+KCk7XG5cbiAgLyoqIFN0b3JlIGEgbGlzdCBvZiBwYWdlcyB0byBkaXNwbGF5IGluIHRoZSBVSSAqL1xuICBwYWdlczogUmVhZG9ubHlBcnJheTxQYWdlPiA9IFtdO1xuXG4gIC8qKiBDb250cm9sVmFsdWVBY2Nlc3NvciBmdW5jdGlvbnMgKi9cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIEB0eXBlc2NyaXB0LWVzbGludC9uby1lbXB0eS1mdW5jdGlvblxuICBvblRvdWNoZWQ6IEZ1bmN0aW9uID0gKCkgPT4ge307XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBAdHlwZXNjcmlwdC1lc2xpbnQvbm8tZW1wdHktZnVuY3Rpb25cbiAgb25DaGFuZ2U6IEZ1bmN0aW9uID0gKCkgPT4ge307XG5cbiAgaXNLZXlib2FyZEV2ZW50OiBib29sZWFuID0gZmFsc2U7XG5cbiAgcHJpdmF0ZSBfcGFnZTogbnVtYmVyID0gMTtcbiAgcHJpdmF0ZSBfdG90YWw6IG51bWJlciA9IDEwMDtcbiAgcHJpdmF0ZSBfcGFnZXNpemU6IG51bWJlciA9IDEwO1xuXG4gIGdldCBwYWdlQ291bnQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gTWF0aC5jZWlsKHRoaXMuX3RvdGFsIC8gdGhpcy5fcGFnZXNpemUpO1xuICB9XG5cbiAgbmdPbkluaXQoKTogdm9pZCB7XG4gICAgdGhpcy5wYWdlcyA9IHRoaXMuZ2V0UGFnZXMoKTtcbiAgfVxuXG4gIHNlbGVjdChpbmRleDogbnVtYmVyKTogdm9pZCB7XG4gICAgLy8gZmluZCB0aGUgcGFnZSB3ZSB3YW50IHRvIGdvIHRvXG4gICAgY29uc3QgdGFyZ2V0ID0gdGhpcy5wYWdlcy5maW5kKHBhZ2UgPT4gcGFnZS5pbmRleCA9PT0gaW5kZXgpO1xuXG4gICAgLy8gaWYgdGhlIHBhZ2UgaXMgb3V0IG9mIGJvdW5kcyB0aGVuIGRvIG5vdGhpbmdcbiAgICBpZiAoIXRhcmdldCkge1xuICAgICAgcmV0dXJuO1xuICAgIH1cblxuICAgIC8vIG1hcmsgdGhpcyBjb21wb25lbnQgYXMgdG91Y2hlZFxuICAgIHRoaXMub25Ub3VjaGVkKCk7XG5cbiAgICAvLyBzZXQgdGhpcyBhcyB0aGUgY3VycmVudCBwYWdlXG4gICAgdGhpcy5wYWdlID0gdGFyZ2V0LmluZGV4O1xuXG4gICAgLy8gdXBkYXRlIHRoZSB2aXNpYmxlIHBhZ2VzXG4gICAgdGhpcy5wYWdlcyA9IHRoaXMuZ2V0UGFnZXMoKTtcblxuICAgIC8vIGVtaXQgdGhlIGN1cnJlbnQgcGFnZVxuICAgIHRoaXMucGFnZUNoYW5nZS5lbWl0KHRoaXMucGFnZSk7XG4gIH1cblxuICB0cmFja0J5Rm4oX2luZGV4OiBudW1iZXIsIGl0ZW06IFBhZ2UpOiBudW1iZXIge1xuICAgIHJldHVybiBpdGVtLmluZGV4O1xuICB9XG5cbiAgcmVnaXN0ZXJPbkNoYW5nZShmbjogKCkgPT4gdm9pZCk6IHZvaWQge1xuICAgIHRoaXMub25DaGFuZ2UgPSBmbjtcbiAgfVxuXG4gIHJlZ2lzdGVyT25Ub3VjaGVkKGZuOiAoKSA9PiB2b2lkKTogdm9pZCB7XG4gICAgdGhpcy5vblRvdWNoZWQgPSBmbjtcbiAgfVxuXG4gIHNldERpc2FibGVkU3RhdGUoaXNEaXNhYmxlZDogYm9vbGVhbik6IHZvaWQge1xuICAgIHRoaXMuZGlzYWJsZWQgPSBpc0Rpc2FibGVkO1xuICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgd3JpdGVWYWx1ZShwYWdlOiBudW1iZXIpOiB2b2lkIHtcbiAgICB0aGlzLnBhZ2UgPSBwYWdlO1xuICAgIHRoaXMuX2NoYW5nZURldGVjdG9yLm1hcmtGb3JDaGVjaygpO1xuICB9XG5cbiAgcHJpdmF0ZSBnZXRQYWdlcygpOiBQYWdlW10ge1xuICAgIC8vIGNyZWF0ZSBhIG5ldyBhcnJheSB0byBzdG9yZSB0aGUgcGFnZXNcbiAgICBjb25zdCBwYWdlczogUGFnZVtdID0gW107XG5cbiAgICAvLyBjcmVhdGUgYWxsIHBvc3NpYmxlIHBhZ2VzXG4gICAgZm9yIChsZXQgaW5kZXggPSAxOyBpbmRleCA8PSB0aGlzLnBhZ2VDb3VudDsgaW5kZXgrKykge1xuICAgICAgcGFnZXMucHVzaCh7IGluZGV4LCB2aXNpYmxlOiB0aGlzLmlzUGFnZVZpc2libGUoaW5kZXgpIH0pO1xuICAgIH1cblxuICAgIC8vIGVtaXQgdGhlIG51bWJlciBvZiBwYWdlc1xuICAgIHRoaXMubnVtUGFnZXMuZW1pdCh0aGlzLnBhZ2VDb3VudCk7XG5cbiAgICByZXR1cm4gcGFnZXM7XG4gIH1cblxuICBwcml2YXRlIGlzUGFnZVZpc2libGUoaW5kZXg6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIC8vIGlmIHdlIGRvIG5vdCBoYXZlIGEgbWF4IHNpemUgc3BlY2lmaWVkIG9yIHRoZSBudW1iZXIgb2YgcGFnZXMgaXMgbGVzcyB0aGFuIHRoZSBtYXggc2l6ZSB0aGVuIGl0IGlzIGFsd2F5cyB2aXNpYmxlXG4gICAgaWYgKCF0aGlzLm1heFNpemUgfHwgdGhpcy5wYWdlQ291bnQgPD0gdGhpcy5tYXhTaXplKSB7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9XG5cbiAgICAvLyBmaW5kIHRoZSBzdGFydGluZyBwb3NpdGlvblxuICAgIGxldCBzdGFydCA9IE1hdGgubWF4KDEsIE1hdGguY2VpbCh0aGlzLnBhZ2UgLSB0aGlzLm1heFNpemUgLyAyKSk7XG4gICAgY29uc3QgZW5kID0gTWF0aC5taW4oc3RhcnQgKyB0aGlzLm1heFNpemUsIHRoaXMucGFnZUNvdW50ICsgMSk7XG5cbiAgICAvLyBpZiB0aGUgcmFuZ2UgaXMgbGVzcyB0aGFuIHRoZSBtYXggc2l6ZSB3ZSBuZWVkIHRvIGFkanVzdCB0aGUgc3RhcnRpbmcgcG9pbnRcbiAgICBjb25zdCByYW5nZSA9IGVuZCAtIHN0YXJ0O1xuXG4gICAgaWYgKHJhbmdlIDwgdGhpcy5tYXhTaXplKSB7XG4gICAgICBzdGFydCA9IHN0YXJ0IC0gKHRoaXMubWF4U2l6ZSAtIHJhbmdlKTtcbiAgICB9XG5cbiAgICAvLyBpZiB0aGUgaXRlbSBlcXVhbHMgdGhlIHN0YXJ0IHBvc2l0aW9uIG9yIGlzIGxlc3MgdGhhbiB0aGUgZW5kIHBvc2l0aW9uIHRoZW4gc2hvdyBpdFxuICAgIHJldHVybiBpbmRleCA+PSBzdGFydCAmJiBpbmRleCA8IGVuZDtcbiAgfVxufVxuXG5leHBvcnQgaW50ZXJmYWNlIFBhZ2Uge1xuICBpbmRleDogbnVtYmVyO1xuICB2aXNpYmxlOiBib29sZWFuO1xufVxuIiwiPG5hdiByb2xlPVwibmF2aWdhdGlvblwiIFthdHRyLmFyaWEtbGFiZWxdPVwiYXJpYUxhYmVsXCI+XG4gIDx1bCAjY29udGFpbmVyIGNsYXNzPVwicGFnaW5hdGlvblwiXG4gICAgW25nQ2xhc3NdPVwiY2xhc3Nlc1wiXG4gICAgZGlyZWN0aW9uPVwiaG9yaXpvbnRhbFwiXG4gICAgKGJsdXIpPVwiaXNLZXlib2FyZEV2ZW50ID0gZmFsc2VcIlxuICAgIChrZXlkb3duLkFycm93TGVmdCk9XCJzZWxlY3QocGFnZSAtIDEpOyBpc0tleWJvYXJkRXZlbnQgPSB0cnVlXCJcbiAgICAoa2V5ZG93bi5BcnJvd1JpZ2h0KT1cInNlbGVjdChwYWdlICsgMSk7IGlzS2V5Ym9hcmRFdmVudCA9IHRydWVcIlxuICAgIChrZXlkb3duLkhvbWUpPVwic2VsZWN0KDEpOyBpc0tleWJvYXJkRXZlbnQgPSB0cnVlOyAkZXZlbnQucHJldmVudERlZmF1bHQoKVwiXG4gICAgKGtleWRvd24uRW5kKT1cInNlbGVjdChwYWdlQ291bnQpOyBpc0tleWJvYXJkRXZlbnQgPSB0cnVlOyAkZXZlbnQucHJldmVudERlZmF1bHQoKVwiPlxuXG4gICAgQGlmIChkaXJlY3Rpb25CdXR0b25zKSB7XG4gICAgICA8bGkgY2xhc3M9XCJwYWdpbmF0aW9uLXByZXYgcGFnZS1pdGVtXCJcbiAgICAgICAgdXhGb2N1c0luZGljYXRvclxuICAgICAgICBbcHJvZ3JhbW1hdGljRm9jdXNJbmRpY2F0b3JdPVwidHJ1ZVwiXG4gICAgICAgIFtjaGVja0NoaWxkcmVuXT1cInRydWVcIlxuICAgICAgICBbY2xhc3MuZGlzYWJsZWRdPVwicGFnZSA9PT0gMSB8fCBkaXNhYmxlZFwiPlxuICAgICAgICA8YnV0dG9uIGNsYXNzPVwicGFnZS1saW5rXCJcbiAgICAgICAgICBbdGFiaW5kZXhdPVwicGFnZSA9PT0gMSB8fCBkaXNhYmxlZCA/IC0xIDogMFwiXG4gICAgICAgICAgW2F0dHIuYXJpYS1sYWJlbF09XCJwcmV2aW91c0FyaWFMYWJlbFwiXG4gICAgICAgICAgW25nQ2xhc3NdPVwicGFnZUJ0bkNsYXNzXCJcbiAgICAgICAgICAoY2xpY2spPVwic2VsZWN0KHBhZ2UgLSAxKVwiXG4gICAgICAgICAgKGtleWRvd24uZW50ZXIpPVwic2VsZWN0KHBhZ2UgLSAxKVwiPlxuICAgICAgICAgIDxuZy1jb250YWluZXIgW25nVGVtcGxhdGVPdXRsZXRdPVwicHJldmlvdXNCdG5UZW1wbGF0ZSB8fCBkZWZhdWx0UHJldmlvdXNCdG5UZW1wbGF0ZVwiPjwvbmctY29udGFpbmVyPlxuICAgICAgICA8L2J1dHRvbj5cbiAgICAgIDwvbGk+XG4gICAgfVxuXG4gICAgQGZvciAocGcgb2YgcGFnZXM7IHRyYWNrIHRyYWNrQnlGbigkaW5kZXgsIHBnKSkge1xuICAgICAgQGlmIChwZy52aXNpYmxlKSB7XG4gICAgICAgIDxsaVxuICAgICAgICAgIHV4Rm9jdXNJbmRpY2F0b3JcbiAgICAgICAgICBbcHJvZ3JhbW1hdGljRm9jdXNJbmRpY2F0b3JdPVwidHJ1ZVwiXG4gICAgICAgICAgW2NoZWNrQ2hpbGRyZW5dPVwidHJ1ZVwiXG4gICAgICAgICAgW2NsYXNzLmRpc2FibGVkXT1cImRpc2FibGVkXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLXNldHNpemVdPVwicGFnZUNvdW50XCJcbiAgICAgICAgICBbYXR0ci5hcmlhLXBvc2luc2V0XT1cInBnLmluZGV4XCJcbiAgICAgICAgICBbY2xhc3MuYWN0aXZlXT1cInBhZ2UgPT09IHBnLmluZGV4XCJcbiAgICAgICAgICBjbGFzcz1cInBhZ2luYXRpb24tcGFnZSBwYWdlLWl0ZW1cIj5cbiAgICAgICAgICA8YnV0dG9uIGNsYXNzPVwicGFnZS1saW5rXCJcbiAgICAgICAgICAgIHRhYmluZGV4PVwiMFwiXG4gICAgICAgICAgICBbbmdDbGFzc109XCJwYWdlQnRuQ2xhc3NcIlxuICAgICAgICAgICAgW2ZvY3VzSWZdPVwiaXNLZXlib2FyZEV2ZW50ICYmIHBhZ2UgPT09IHBnLmluZGV4XCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtY3VycmVudF09XCJwYWdlID09PSBwZy5pbmRleFwiXG4gICAgICAgICAgICAoY2xpY2spPVwic2VsZWN0KHBnLmluZGV4KVwiXG4gICAgICAgICAgICAoa2V5ZG93bi5lbnRlcik9XCJzZWxlY3QocGcuaW5kZXgpXCI+XG4gICAgICAgICAgICB7eyBwZy5pbmRleCB9fVxuICAgICAgICAgIDwvYnV0dG9uPlxuICAgICAgICA8L2xpPlxuICAgICAgfVxuICAgIH1cblxuICAgIEBpZiAoZGlyZWN0aW9uQnV0dG9ucykge1xuICAgICAgPGxpIGNsYXNzPVwicGFnaW5hdGlvbi1uZXh0IHBhZ2UtaXRlbVwiXG4gICAgICAgIHV4Rm9jdXNJbmRpY2F0b3JcbiAgICAgICAgW3Byb2dyYW1tYXRpY0ZvY3VzSW5kaWNhdG9yXT1cInRydWVcIlxuICAgICAgICBbY2hlY2tDaGlsZHJlbl09XCJ0cnVlXCJcbiAgICAgICAgW2NsYXNzLmRpc2FibGVkXT1cInBhZ2UgPT09IHBhZ2VDb3VudCB8fCBkaXNhYmxlZFwiPlxuICAgICAgICA8YnV0dG9uIGNsYXNzPVwicGFnZS1saW5rXCJcbiAgICAgICAgICBbdGFiaW5kZXhdPVwicGFnZSA9PT0gcGFnZUNvdW50IHx8IGRpc2FibGVkID8gLTEgOiAwXCJcbiAgICAgICAgICBbYXR0ci5hcmlhLWxhYmVsXT1cIm5leHRBcmlhTGFiZWxcIlxuICAgICAgICAgIFtuZ0NsYXNzXT1cInBhZ2VCdG5DbGFzc1wiXG4gICAgICAgICAgKGNsaWNrKT1cInNlbGVjdChwYWdlICsgMSlcIlxuICAgICAgICAgIChrZXlkb3duLmVudGVyKT1cInNlbGVjdChwYWdlICsgMSlcIj5cbiAgICAgICAgICA8bmctY29udGFpbmVyIFtuZ1RlbXBsYXRlT3V0bGV0XT1cIm5leHRCdG5UZW1wbGF0ZSB8fCBkZWZhdWx0TmV4dEJ0blRlbXBsYXRlXCI+PC9uZy1jb250YWluZXI+XG4gICAgICAgIDwvYnV0dG9uPlxuICAgICAgPC9saT5cbiAgICB9XG4gIDwvdWw+XG48L25hdj5cblxuPG5nLXRlbXBsYXRlICNkZWZhdWx0UHJldmlvdXNCdG5UZW1wbGF0ZT5cbiAgPHV4LWljb24gY2xhc3M9XCJwYWdpbmF0aW9uLXByZXYtaWNvbi1kZWZhdWx0XCIgbmFtZT1cInByZXZpb3VzXCI+PC91eC1pY29uPlxuPC9uZy10ZW1wbGF0ZT5cblxuPG5nLXRlbXBsYXRlICNkZWZhdWx0TmV4dEJ0blRlbXBsYXRlPlxuICA8dXgtaWNvbiBjbGFzcz1cInBhZ2luYXRpb24tbmV4dC1pY29uLWRlZmF1bHRcIiBuYW1lPVwibmV4dFwiPjwvdXgtaWNvbj5cbjwvbmctdGVtcGxhdGU+XG4iXX0=