@visa/nova-angular
Version:
Visa Product Design System Nova Angular library
204 lines • 36 kB
JavaScript
/**
* Copyright (c) 2025 Visa, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**/
/* eslint-disable no-mixed-spaces-and-tabs */
import { computed, signal } from '@angular/core';
/**
* Generates an array from number to from + length
* @param {number} from
* @param {number} length
* @returns {number[]}
*/
export const generateArray = (from, length) => Array.from({ length }, (_, i) => i + from);
const defaultOptions = {
blockMaxLength: 3,
compact: false,
defaultSelected: 1,
defaultTotalPages: 1,
delimiter: -1,
maxPageNumber: null,
startPage: 1
};
/**
* A signals based approach to handling and controlling pagination components. It is very customizable, re-usable, and even allows you to bring your own signal for selected page.
* @docs {@link https://design.visa.com/angular/components/pagination | See docs}
*/
export class PaginationControl {
constructor(options) {
this.totalPages = signal(0);
/// DERIVED STATE:
/** Can paginate or just show all pages */
this.canPaginate = computed(() => this.totalPages() > Math.max(this.options.endBlockMaxLength, this.options.startBlockMaxLength) + 2 && // 1 for end or start number + 1 for single separator show while in start/end blocks
this.totalPages() > this.options.middleBlockMaxLength + 4 // 2 for start and end numbers + 2 for separators shown while in middle block
);
/** Pages to show at the end */
this.endBlock = computed(() => this.isInEndBlock()
? generateArray(this.lastPage() - this.options.endBlockMaxLength + 1, this.options.endBlockMaxLength)
: [this.lastPage()]);
/** Ideal last page without maxPageNumber interfering */
this.idealLastPage = computed(() => this.totalPages() + this.options.startPage - 1);
/** Is first element selected */
this.isFirstPage = computed(() => this.selectedPage() === this.options.startPage);
/** Selected page is in end block */
this.isInEndBlock = computed(() => this.selectedPage() > this.lastPage() - this.options.endBlockMaxLength);
/** Selected page is in middle block */
this.isInMiddleBlock = computed(() => !this.isInStartBlock() && !this.isInEndBlock());
/** Selected page is in start block */
this.isInStartBlock = computed(() => this.selectedPage() < this.options.startPage + this.options.startBlockMaxLength);
/** Is last element selected */
this.isLastPage = computed(() => this.selectedPage() === this.lastPage());
/** Last page */
this.lastPage = computed(() => this.options.maxPageNumber === null
? this.idealLastPage()
: Math.min(this.options.maxPageNumber, this.idealLastPage()));
/** Pages to show in the middle */
this.middleBlock = computed(() => {
if (!this.isInMiddleBlock())
return [];
const middleBlockPadding = Math.floor(this.options.middleBlockMaxLength / 2);
if (this.selectedPage() - middleBlockPadding <= this.options.startPage)
return generateArray(this.selectedPage() - (this.selectedPage() - this.options.startPage) + 1, this.options.middleBlockMaxLength);
if (this.selectedPage() + middleBlockPadding >= this.lastPage())
return generateArray(this.selectedPage() + (this.lastPage() - this.selectedPage()) - this.options.middleBlockMaxLength, this.options.middleBlockMaxLength);
return generateArray(this.selectedPage() - middleBlockPadding, this.options.middleBlockMaxLength);
});
/** Array of pages arrays to loop over */
this.pages = computed(() => {
if (this.options.compact)
return this.compactPages;
return this.canPaginate()
? [this.startBlock(), this.middleBlock(), this.endBlock()]
.map((block) => (block.length ? [...block, this.options.delimiter] : []))
.flat()
.slice(0, -1)
: generateArray(this.options.startPage, this.lastPage() - this.options.startPage + 1);
});
/** Pages to show at the start */
this.startBlock = computed(() => this.isInStartBlock()
? generateArray(this.options.startPage, this.options.startBlockMaxLength)
: [this.options.startPage]);
/** Helper to calculate total pages */
this.getTotalPages = PaginationControl.getTotalPages;
const blockMaxLength = options?.blockMaxLength ?? defaultOptions.blockMaxLength;
// If we have a blockMaxLength, we should set it for all block max lengths, then we override it with all the options provided by the user
this.options = {
...defaultOptions,
endBlockMaxLength: blockMaxLength,
middleBlockMaxLength: blockMaxLength,
startBlockMaxLength: blockMaxLength,
...options
};
this.totalPages.set(this.options.defaultTotalPages);
const defaultStartPage = Math.min(Math.max(this.options.defaultSelected, this.options.startPage), this.options.defaultTotalPages);
this.selectedPage = this.options.selectedPage ?? signal(defaultStartPage);
this.selectedPage.set(defaultStartPage);
}
/// UTILITIES:
/** Getter for selectedPage, we shouldn't directly manipulate the `selectedPage` signal, use `goToPage` to do this safely */
get currentPage() {
return this.selectedPage();
}
get compactPages() {
const { blockMaxLength } = this.options;
// Show all pages if we have less than blockMaxLength
if (blockMaxLength > this.totalPages())
return generateArray(this.options.startPage, this.lastPage() - this.options.startPage + 1);
// Show chunk of blockMaxLength pages
const padding = Math.floor(blockMaxLength / 2);
if (this.selectedPage() - padding <= this.options.startPage)
return generateArray(this.options.startPage, blockMaxLength);
if (this.selectedPage() + padding >= this.lastPage())
return generateArray(this.lastPage() - blockMaxLength + 1, blockMaxLength);
return generateArray(this.selectedPage() - padding, blockMaxLength);
}
/**
* Calculates which range of items that is currently visible from pagination
* @param {number} items - how many items we have
* @param {number} itemsPerPage - how many items there are, per page
* @returns to from object with calculated values
*/
getToFrom(items, itemsPerPage) {
return PaginationControl.getToFrom(items, itemsPerPage, this.selectedPage(), this.options.startPage);
}
/** On first page event */
goToFirstPage() {
this.goToPage(this.options.startPage);
}
/** On last page event */
goToLastPage() {
this.goToPage(this.lastPage());
}
/** On next page event */
goToNextPage() {
this.goToPage(this.selectedPage() + 1);
}
/** On page change event */
goToPage(pageNumber) {
if (Number.isNaN(+pageNumber))
throw new Error("Can't go to page, invalid number");
if (pageNumber > this.lastPage())
this.selectedPage.set(this.lastPage());
else if (pageNumber < this.options.startPage)
this.selectedPage.set(this.options.startPage);
else
this.selectedPage.set(pageNumber);
}
/** On previous page event */
goToPreviousPage() {
this.goToPage(this.selectedPage() - 1);
}
/** Returns if page is current page */
isCurrentPage(page) {
return page === this.selectedPage();
}
/**
* In the context where total pages is calculated using items per page (much like tables use) we can use this utility to easily adjust the pagination control automatically.
* NOTE: by default this resets the pagination to the first page when called.
*/
resetPageCount(totalItems, itemsPerPage, autoResetToFirstPage = true) {
const totalPages = this.getTotalPages(totalItems, itemsPerPage);
this.totalPages.set(totalPages);
if (autoResetToFirstPage)
this.goToFirstPage();
}
/// STATIC UTILITIES:
/**
* Calculates which range of items that is currently visible from pagination
* @param {number} items - how many items we have
* @param {number} itemsPerPage - how many items there are, per page
* @param {number} currentPage - which page is visible
* @param {number} startPage - which page we're starting from, defaults to page 1 (optional)
* @returns to from object with calculated values
*/
static { this.getToFrom = (items, itemsPerPage, currentPage, startPage = 1) => {
if (itemsPerPage < 1 || items < 1)
return { 0: 0, 1: 0, from: 0, to: 0 };
const normalizedPageNumber = currentPage - startPage + 1;
const from = Math.max((normalizedPageNumber - 1) * itemsPerPage + 1, 0);
const to = Math.max(from + itemsPerPage - 1 > items ? items : from + itemsPerPage - 1, 0);
return { 0: from, 1: to, from, to };
}; }
/**
* Calculates how many pages there are
* @param {number} items - how many items we have
* @param {number} itemsPerPage - how many items there are, per page
* @returns {number} how many pages there are in total
*/
static getTotalPages(items, itemsPerPage) {
return Math.ceil(items / itemsPerPage);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGlvbi5jb250cm9sLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vbGlicy9ub3ZhLWxpYi9zcmMvbGliL3BhZ2luYXRpb24tY29udHJvbC9wYWdpbmF0aW9uLmNvbnRyb2wudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7OztJQWVJO0FBQ0osNkNBQTZDO0FBQzdDLE9BQU8sRUFBRSxRQUFRLEVBQVUsTUFBTSxFQUFrQixNQUFNLGVBQWUsQ0FBQztBQUV6RTs7Ozs7R0FLRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxDQUFDLElBQVksRUFBRSxNQUFjLEVBQVksRUFBRSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsRUFBRSxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztBQTJCcEgsTUFBTSxjQUFjLEdBQUc7SUFDckIsY0FBYyxFQUFFLENBQUM7SUFDakIsT0FBTyxFQUFFLEtBQUs7SUFDZCxlQUFlLEVBQUUsQ0FBQztJQUNsQixpQkFBaUIsRUFBRSxDQUFDO0lBQ3BCLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDYixhQUFhLEVBQUUsSUFBSTtJQUNuQixTQUFTLEVBQUUsQ0FBQztDQUMrQixDQUFDO0FBRTlDOzs7R0FHRztBQUNILE1BQU0sT0FBTyxpQkFBaUI7SUFDNUIsWUFBWSxPQUEyQztRQXlCdkMsZUFBVSxHQUEyQixNQUFNLENBQVMsQ0FBQyxDQUFDLENBQUM7UUFFdkUsa0JBQWtCO1FBRWxCLDBDQUEwQztRQUN6QixnQkFBVyxHQUFvQixRQUFRLENBQ3RELEdBQUcsRUFBRSxDQUNILElBQUksQ0FBQyxVQUFVLEVBQUUsR0FBRyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIs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QUFFLElBQUksRUFBRSxDQUFDLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FBQztJQUN0QyxDQUFDLEFBTnNCLENBTXJCO0lBRUY7Ozs7O09BS0c7SUFDSSxNQUFNLENBQUMsYUFBYSxDQUFDLEtBQWEsRUFBRSxZQUFvQjtRQUM3RCxPQUFPLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxHQUFHLFlBQVksQ0FBQyxDQUFDO0lBQ3pDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqICAgICAgICAgICAgICBDb3B5cmlnaHQgKGMpIDIwMjUgVmlzYSwgSW5jLlxuICpcbiAqIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAgKHRoZSBcIkxpY2Vuc2VcIik7XG4gKiB5b3UgbWF5IG5vdCB1c2UgdGhpcyBmaWxlIGV4Y2VwdCBpbiBjb21wbGlhbmNlIHdpdGggdGhlIExpY2Vuc2UuXG4gKiBZb3UgbWF5IG9idGFpbiBhIGNvcHkgb2YgdGhlIExpY2Vuc2UgYXRcbiAqXG4gKiAgICAgICAgIGh0dHA6Ly93d3cuYXBhY2hlLm9yZy9saWNlbnNlcy9MSUNFTlNFLTIuMFxuICpcbiAqIFVubGVzcyByZXF1aXJlZCBieSBhcHBsaWNhYmxlIGxhdyBvciBhZ3JlZWQgdG8gaW4gd3JpdGluZywgc29mdHdhcmVcbiAqIGRpc3RyaWJ1dGVkIHVuZGVyIHRoZSBMaWNlbnNlIGlzIGRpc3RyaWJ1dGVkIG9uIGFuIFwiQVMgSVNcIiBCQVNJUyxcbiAqIFdJVEhPVVQgV0FSUkFOVElFUyBPUiBDT05ESVRJT05TIE9GIEFOWSBLSU5ELCBlaXRoZXIgZXhwcmVzcyBvciBpbXBsaWVkLlxuICogU2VlIHRoZSBMaWNlbnNlIGZvciB0aGUgc3BlY2lmaWMgbGFuZ3VhZ2UgZ292ZXJuaW5nIHBlcm1pc3Npb25zIGFuZFxuICogbGltaXRhdGlvbnMgdW5kZXIgdGhlIExpY2Vuc2UuXG4gKlxuICoqL1xuLyogZXNsaW50LWRpc2FibGUgbm8tbWl4ZWQtc3BhY2VzLWFuZC10YWJzICovXG5pbXBvcnQgeyBjb21wdXRlZCwgU2lnbmFsLCBzaWduYWwsIFdyaXRhYmxlU2lnbmFsIH0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbi8qKlxuICogR2VuZXJhdGVzIGFuIGFycmF5IGZyb20gbnVtYmVyIHRvIGZyb20gKyBsZW5ndGhcbiAqIEBwYXJhbSB7bnVtYmVyfSBmcm9tXG4gKiBAcGFyYW0ge251bWJlcn0gbGVuZ3RoXG4gKiBAcmV0dXJucyB7bnVtYmVyW119XG4gKi9cbmV4cG9ydCBjb25zdCBnZW5lcmF0ZUFycmF5ID0gKGZyb206IG51bWJlciwgbGVuZ3RoOiBudW1iZXIpOiBudW1iZXJbXSA9PiBBcnJheS5mcm9tKHsgbGVuZ3RoIH0sIChfLCBpKSA9PiBpICsgZnJvbSk7XG5cbmV4cG9ydCB0eXBlIFBhZ2luYXRpb25Db250cm9sT3B0aW9ucyA9IHtcbiAgLyoqIE1heCBibG9jayBsZW5ndGggZm9yIGFsbCBibG9ja3MsIHRoaXMgZ2V0cyBvdmVyd3JpdHRlbiBieSBgc3RhcnRCbG9ja01heExlbmd0aGAsIGBtaWRkbGVCbG9ja01heExlbmd0aGAsIGBlbmRCbG9ja01heExlbmd0aGAgKi9cbiAgYmxvY2tNYXhMZW5ndGg6IG51bWJlcjtcbiAgLyoqIEZvcmNlcyBwYWdlcyBub3QgdG8gcGFnaW5hdGUgKi9cbiAgY29tcGFjdDogYm9vbGVhbjtcbiAgLyoqIERlZmF1bHQgc2VsZWN0ZWQgcGFnZSAqL1xuICBkZWZhdWx0U2VsZWN0ZWQ6IG51bWJlcjtcbiAgLyoqIERlZmF1bHQgdG90YWwgYW1vdW50IG9mIHBhZ2UgKi9cbiAgZGVmYXVsdFRvdGFsUGFnZXM6IG51bWJlcjtcbiAgLyoqIFdoYXQgdG8gc2VwYXJhdGUgdGhlIHBhZ2luYXRpb24gYXJyYXkgdXAgd2l0aCwgdXN1YWxseSB0aGlzIGRlbGltaXRlciB3aWxsIGJlIHJlcGxhY2VkIHdpdGggaWNvbiBvciBlbGxpcHNlcyB3aGVuIHNob3duIGluIHRoZSBVSSAqL1xuICBkZWxpbWl0ZXI6IG51bWJlciB8IHN0cmluZztcbiAgLyoqIE1heGltdW0gbGVuZ3RoIG9mIHBhZ2VzIHRvIHNob3cgb24gdGhlIGVuZCBwYWdpbmF0aW9uIGJsb2NrLCBvdmVyd3JpdGVzIGBibG9ja01heExlbmd0aGAgZm9yIGVuZCBibG9jayAqL1xuICBlbmRCbG9ja01heExlbmd0aDogbnVtYmVyO1xuICAvKiogTWF4aW11bSBwYWdlIG51bWJlciB0byBiZSBzaG93biwgKGRlZmF1bHQgbnVsbCBmb3Igbm8gbWF4aW11bSkgKi9cbiAgbWF4UGFnZU51bWJlcjogbnVtYmVyIHwgbnVsbDtcbiAgLyoqIE1heGltdW0gbGVuZ3RoIG9mIHBhZ2VzIHRvIHNob3cgb24gdGhlIG1pZGRsZSBwYWdpbmF0aW9uIGJsb2NrLCBvdmVyd3JpdGVzIGBibG9ja01heExlbmd0aGAgZm9yIG1pZGRsZSBibG9jayAqL1xuICBtaWRkbGVCbG9ja01heExlbmd0aDogbnVtYmVyO1xuICAvKiogU2lnbmFsIHRvIGJlIHVzZWQgZm9yIHNlbGVjdGVkIHBhZ2UsIG90aGVyd2lzZSBjb250cm9sIHdpbGwgY3JlYXRlIG9uZSAqL1xuICBzZWxlY3RlZFBhZ2U/OiBXcml0YWJsZVNpZ25hbDxudW1iZXI+IHwgbnVsbDtcbiAgLyoqIE1heGltdW0gbGVuZ3RoIG9mIHBhZ2VzIHRvIHNob3cgb24gdGhlIHN0YXJ0IHBhZ2luYXRpb24gYmxvY2ssIG92ZXJ3cml0ZXMgYGJsb2NrTWF4TGVuZ3RoYCBmb3Igc3RhcnQgYmxvY2sgKi9cbiAgc3RhcnRCbG9ja01heExlbmd0aDogbnVtYmVyO1xuICAvKiogU3RhcnQgZnJvbSB0aGlzIHBhZ2UgKi9cbiAgc3RhcnRQYWdlOiBudW1iZXI7XG59O1xuXG5jb25zdCBkZWZhdWx0T3B0aW9ucyA9IHtcbiAgYmxvY2tNYXhMZW5ndGg6IDMsXG4gIGNvbXBhY3Q6IGZhbHNlLFxuICBkZWZhdWx0U2VsZWN0ZWQ6IDEsXG4gIGRlZmF1bHRUb3RhbFBhZ2VzOiAxLFxuICBkZWxpbWl0ZXI6IC0xLFxuICBtYXhQYWdlTnVtYmVyOiBudWxsLFxuICBzdGFydFBhZ2U6IDFcbn0gc2F0aXNmaWVzIFBhcnRpYWw8UGFnaW5hdGlvbkNvbnRyb2xPcHRpb25zPjtcblxuLyoqXG4gKiBBIHNpZ25hbHMgYmFzZWQgYXBwcm9hY2ggdG8gaGFuZGxpbmcgYW5kIGNvbnRyb2xsaW5nIHBhZ2luYXRpb24gY29tcG9uZW50cy4gSXQgaXMgdmVyeSBjdXN0b21pemFibGUsIHJlLXVzYWJsZSwgYW5kIGV2ZW4gYWxsb3dzIHlvdSB0byBicmluZyB5b3VyIG93biBzaWduYWwgZm9yIHNlbGVjdGVkIHBhZ2UuXG4gKiBAZG9jcyB7QGxpbmsgaHR0cHM6Ly9kZXNpZ24udmlzYS5jb20vYW5ndWxhci9jb21wb25lbnRzL3BhZ2luYXRpb24gfCBTZWUgZG9jc31cbiAqL1xuZXhwb3J0IGNsYXNzIFBhZ2luYXRpb25Db250cm9sIHtcbiAgY29uc3RydWN0b3Iob3B0aW9ucz86IFBhcnRpYWw8UGFnaW5hdGlvbkNvbnRyb2xPcHRpb25zPikge1xuICAgIGNvbnN0IGJsb2NrTWF4TGVuZ3RoID0gb3B0aW9ucz8uYmxvY2tNYXhMZW5ndGggPz8gZGVmYXVsdE9wdGlvbnMuYmxvY2tNYXhMZW5ndGg7XG4gICAgLy8gSWYgd2UgaGF2ZSBhIGJsb2NrTWF4TGVuZ3RoLCB3ZSBzaG91bGQgc2V0IGl0IGZvciBhbGwgYmxvY2sgbWF4IGxlbmd0aHMsIHRoZW4gd2Ugb3ZlcnJpZGUgaXQgd2l0aCBhbGwgdGhlIG9wdGlvbnMgcHJvdmlkZWQgYnkgdGhlIHVzZXJcbiAgICB0aGlzLm9wdGlvbnMgPSB7XG4gICAgICAuLi5kZWZhdWx0T3B0aW9ucyxcbiAgICAgIGVuZEJsb2NrTWF4TGVuZ3RoOiBibG9ja01heExlbmd0aCxcbiAgICAgIG1pZGRsZUJsb2NrTWF4TGVuZ3RoOiBibG9ja01heExlbmd0aCxcbiAgICAgIHN0YXJ0QmxvY2tNYXhMZW5ndGg6IGJsb2NrTWF4TGVuZ3RoLFxuICAgICAgLi4ub3B0aW9uc1xuICAgIH0gYXMgUGFnaW5hdGlvbkNvbnRyb2xPcHRpb25zO1xuICAgIHRoaXMudG90YWxQYWdlcy5zZXQodGhpcy5vcHRpb25zLmRlZmF1bHRUb3RhbFBhZ2VzKTtcbiAgICBjb25zdCBkZWZhdWx0U3RhcnRQYWdlID0gTWF0aC5taW4oXG4gICAgICBNYXRoLm1heCh0aGlzLm9wdGlvbnMuZGVmYXVsdFNlbGVjdGVkLCB0aGlzLm9wdGlvbnMuc3RhcnRQYWdlKSxcbiAgICAgIHRoaXMub3B0aW9ucy5kZWZhdWx0VG90YWxQYWdlc1xuICAgICk7XG4gICAgdGhpcy5zZWxlY3RlZFBhZ2UgPSB0aGlzLm9wdGlvbnMuc2VsZWN0ZWRQYWdlID8/IHNpZ25hbChkZWZhdWx0U3RhcnRQYWdlKTtcbiAgICB0aGlzLnNlbGVjdGVkUGFnZS5zZXQoZGVmYXVsdFN0YXJ0UGFnZSk7XG4gIH1cblxuICAvLy8gU1RBVEU6XG5cbiAgcHJpdmF0ZSByZWFkb25seSBvcHRpb25zOiBQYWdpbmF0aW9uQ29udHJvbE9wdGlvbnM7XG5cbiAgcHJpdmF0ZSByZWFkb25seSBzZWxlY3RlZFBhZ2U6IFdyaXRhYmxlU2lnbmFsPG51bWJlcj47XG5cbiAgcHVibGljIHJlYWRvbmx5IHRvdGFsUGFnZXM6IFdyaXRhYmxlU2lnbmFsPG51bWJlcj4gPSBzaWduYWw8bnVtYmVyPigwKTtcblxuICAvLy8gREVSSVZFRCBTVEFURTpcblxuICAvKiogQ2FuIHBhZ2luYXRlIG9yIGp1c3Qgc2hvdyBhbGwgcGFnZXMgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBjYW5QYWdpbmF0ZTogU2lnbmFsPGJvb2xlYW4+ID0gY29tcHV0ZWQoXG4gICAgKCkgPT5cbiAgICAgIHRoaXMudG90YWxQYWdlcygpID4gTWF0aC5tYXgodGhpcy5vcHRpb25zLmVuZEJsb2NrTWF4TGVuZ3RoLCB0aGlzLm9wdGlvbnMuc3RhcnRCbG9ja01heExlbmd0aCkgKyAyICYmIC8vIDEgZm9yIGVuZCBvciBzdGFydCBudW1iZXIgKyAxIGZvciBzaW5nbGUgc2VwYXJhdG9yIHNob3cgd2hpbGUgaW4gc3RhcnQvZW5kIGJsb2Nrc1xuICAgICAgdGhpcy50b3RhbFBhZ2VzKCkgPiB0aGlzLm9wdGlvbnMubWlkZGxlQmxvY2tNYXhMZW5ndGggKyA0IC8vIDIgZm9yIHN0YXJ0IGFuZCBlbmQgbnVtYmVycyArIDIgZm9yIHNlcGFyYXRvcnMgc2hvd24gd2hpbGUgaW4gbWlkZGxlIGJsb2NrXG4gICk7XG5cbiAgLyoqIFBhZ2VzIHRvIHNob3cgYXQgdGhlIGVuZCAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGVuZEJsb2NrOiBTaWduYWw8bnVtYmVyW10+ID0gY29tcHV0ZWQoKCkgPT5cbiAgICB0aGlzLmlzSW5FbmRCbG9jaygpXG4gICAgICA/IGdlbmVyYXRlQXJyYXkodGhpcy5sYXN0UGFnZSgpIC0gdGhpcy5vcHRpb25zLmVuZEJsb2NrTWF4TGVuZ3RoICsgMSwgdGhpcy5vcHRpb25zLmVuZEJsb2NrTWF4TGVuZ3RoKVxuICAgICAgOiBbdGhpcy5sYXN0UGFnZSgpXVxuICApO1xuXG4gIC8qKiBJZGVhbCBsYXN0IHBhZ2Ugd2l0aG91dCBtYXhQYWdlTnVtYmVyIGludGVyZmVyaW5nICovXG4gIHByaXZhdGUgcmVhZG9ubHkgaWRlYWxMYXN0UGFnZTogU2lnbmFsPG51bWJlcj4gPSBjb21wdXRlZCgoKSA9PiB0aGlzLnRvdGFsUGFnZXMoKSArIHRoaXMub3B0aW9ucy5zdGFydFBhZ2UgLSAxKTtcblxuICAvKiogSXMgZmlyc3QgZWxlbWVudCBzZWxlY3RlZCAqL1xuICBwdWJsaWMgcmVhZG9ubHkgaXNGaXJzdFBhZ2U6IFNpZ25hbDxib29sZWFuPiA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2VsZWN0ZWRQYWdlKCkgPT09IHRoaXMub3B0aW9ucy5zdGFydFBhZ2UpO1xuXG4gIC8qKiBTZWxlY3RlZCBwYWdlIGlzIGluIGVuZCBibG9jayAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGlzSW5FbmRCbG9jazogU2lnbmFsPGJvb2xlYW4+ID0gY29tcHV0ZWQoXG4gICAgKCkgPT4gdGhpcy5zZWxlY3RlZFBhZ2UoKSA+IHRoaXMubGFzdFBhZ2UoKSAtIHRoaXMub3B0aW9ucy5lbmRCbG9ja01heExlbmd0aFxuICApO1xuXG4gIC8qKiBTZWxlY3RlZCBwYWdlIGlzIGluIG1pZGRsZSBibG9jayAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGlzSW5NaWRkbGVCbG9jazogU2lnbmFsPGJvb2xlYW4+ID0gY29tcHV0ZWQoKCkgPT4gIXRoaXMuaXNJblN0YXJ0QmxvY2soKSAmJiAhdGhpcy5pc0luRW5kQmxvY2soKSk7XG5cbiAgLyoqIFNlbGVjdGVkIHBhZ2UgaXMgaW4gc3RhcnQgYmxvY2sgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBpc0luU3RhcnRCbG9jazogU2lnbmFsPGJvb2xlYW4+ID0gY29tcHV0ZWQoXG4gICAgKCkgPT4gdGhpcy5zZWxlY3RlZFBhZ2UoKSA8IHRoaXMub3B0aW9ucy5zdGFydFBhZ2UgKyB0aGlzLm9wdGlvbnMuc3RhcnRCbG9ja01heExlbmd0aFxuICApO1xuXG4gIC8qKiBJcyBsYXN0IGVsZW1lbnQgc2VsZWN0ZWQgKi9cbiAgcHVibGljIHJlYWRvbmx5IGlzTGFzdFBhZ2U6IFNpZ25hbDxib29sZWFuPiA9IGNvbXB1dGVkKCgpID0+IHRoaXMuc2VsZWN0ZWRQYWdlKCkgPT09IHRoaXMubGFzdFBhZ2UoKSk7XG5cbiAgLyoqIExhc3QgcGFnZSAqL1xuICBwcml2YXRlIHJlYWRvbmx5IGxhc3RQYWdlOiBTaWduYWw8bnVtYmVyPiA9IGNvbXB1dGVkKCgpID0+XG4gICAgdGhpcy5vcHRpb25zLm1heFBhZ2VOdW1iZXIgPT09IG51bGxcbiAgICAgID8gdGhpcy5pZGVhbExhc3RQYWdlKClcbiAgICAgIDogTWF0aC5taW4odGhpcy5vcHRpb25zLm1heFBhZ2VOdW1iZXIsIHRoaXMuaWRlYWxMYXN0UGFnZSgpKVxuICApO1xuXG4gIC8qKiBQYWdlcyB0byBzaG93IGluIHRoZSBtaWRkbGUgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBtaWRkbGVCbG9jazogU2lnbmFsPG51bWJlcltdPiA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBpZiAoIXRoaXMuaXNJbk1pZGRsZUJsb2NrKCkpIHJldHVybiBbXTtcbiAgICBjb25zdCBtaWRkbGVCbG9ja1BhZGRpbmcgPSBNYXRoLmZsb29yKHRoaXMub3B0aW9ucy5taWRkbGVCbG9ja01heExlbmd0aCAvIDIpO1xuICAgIGlmICh0aGlzLnNlbGVjdGVkUGFnZSgpIC0gbWlkZGxlQmxvY2tQYWRkaW5nIDw9IHRoaXMub3B0aW9ucy5zdGFydFBhZ2UpXG4gICAgICByZXR1cm4gZ2VuZXJhdGVBcnJheShcbiAgICAgICAgdGhpcy5zZWxlY3RlZFBhZ2UoKSAtICh0aGlzLnNlbGVjdGVkUGFnZSgpIC0gdGhpcy5vcHRpb25zLnN0YXJ0UGFnZSkgKyAxLFxuICAgICAgICB0aGlzLm9wdGlvbnMubWlkZGxlQmxvY2tNYXhMZW5ndGhcbiAgICAgICk7XG4gICAgaWYgKHRoaXMuc2VsZWN0ZWRQYWdlKCkgKyBtaWRkbGVCbG9ja1BhZGRpbmcgPj0gdGhpcy5sYXN0UGFnZSgpKVxuICAgICAgcmV0dXJuIGdlbmVyYXRlQXJyYXkoXG4gICAgICAgIHRoaXMuc2VsZWN0ZWRQYWdlKCkgKyAodGhpcy5sYXN0UGFnZSgpIC0gdGhpcy5zZWxlY3RlZFBhZ2UoKSkgLSB0aGlzLm9wdGlvbnMubWlkZGxlQmxvY2tNYXhMZW5ndGgsXG4gICAgICAgIHRoaXMub3B0aW9ucy5taWRkbGVCbG9ja01heExlbmd0aFxuICAgICAgKTtcbiAgICByZXR1cm4gZ2VuZXJhdGVBcnJheSh0aGlzLnNlbGVjdGVkUGFnZSgpIC0gbWlkZGxlQmxvY2tQYWRkaW5nLCB0aGlzLm9wdGlvbnMubWlkZGxlQmxvY2tNYXhMZW5ndGgpO1xuICB9KTtcblxuICAvKiogQXJyYXkgb2YgcGFnZXMgYXJyYXlzIHRvIGxvb3Agb3ZlciAqL1xuICBwdWJsaWMgcmVhZG9ubHkgcGFnZXM6IFNpZ25hbDwoc3RyaW5nIHwgbnVtYmVyKVtdPiA9IGNvbXB1dGVkKCgpID0+IHtcbiAgICBpZiAodGhpcy5vcHRpb25zLmNvbXBhY3QpIHJldHVybiB0aGlzLmNvbXBhY3RQYWdlcztcbiAgICByZXR1cm4gdGhpcy5jYW5QYWdpbmF0ZSgpXG4gICAgICA/IFt0aGlzLnN0YXJ0QmxvY2soKSwgdGhpcy5taWRkbGVCbG9jaygpLCB0aGlzLmVuZEJsb2NrKCldXG4gICAgICAgICAgLm1hcCgoYmxvY2spID0+IChibG9jay5sZW5ndGggPyBbLi4uYmxvY2ssIHRoaXMub3B0aW9ucy5kZWxpbWl0ZXJdIDogW10pKVxuICAgICAgICAgIC5mbGF0KClcbiAgICAgICAgICAuc2xpY2UoMCwgLTEpXG4gICAgICA6IGdlbmVyYXRlQXJyYXkodGhpcy5vcHRpb25zLnN0YXJ0UGFnZSwgdGhpcy5sYXN0UGFnZSgpIC0gdGhpcy5vcHRpb25zLnN0YXJ0UGFnZSArIDEpO1xuICB9KTtcblxuICAvKiogUGFnZXMgdG8gc2hvdyBhdCB0aGUgc3RhcnQgKi9cbiAgcHJpdmF0ZSByZWFkb25seSBzdGFydEJsb2NrOiBTaWduYWw8bnVtYmVyW10+ID0gY29tcHV0ZWQoKCkgPT5cbiAgICB0aGlzLmlzSW5TdGFydEJsb2NrKClcbiAgICAgID8gZ2VuZXJhdGVBcnJheSh0aGlzLm9wdGlvbnMuc3RhcnRQYWdlLCB0aGlzLm9wdGlvbnMuc3RhcnRCbG9ja01heExlbmd0aClcbiAgICAgIDogW3RoaXMub3B0aW9ucy5zdGFydFBhZ2VdXG4gICk7XG5cbiAgLy8vIFVUSUxJVElFUzpcblxuICAvKiogR2V0dGVyIGZvciBzZWxlY3RlZFBhZ2UsIHdlIHNob3VsZG4ndCBkaXJlY3RseSBtYW5pcHVsYXRlIHRoZSBgc2VsZWN0ZWRQYWdlYCBzaWduYWwsIHVzZSBgZ29Ub1BhZ2VgIHRvIGRvIHRoaXMgc2FmZWx5ICovXG4gIHB1YmxpYyBnZXQgY3VycmVudFBhZ2UoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5zZWxlY3RlZFBhZ2UoKTtcbiAgfVxuXG4gIHByaXZhdGUgZ2V0IGNvbXBhY3RQYWdlcygpOiBudW1iZXJbXSB7XG4gICAgY29uc3QgeyBibG9ja01heExlbmd0aCB9ID0gdGhpcy5vcHRpb25zO1xuXG4gICAgLy8gU2hvdyBhbGwgcGFnZXMgaWYgd2UgaGF2ZSBsZXNzIHRoYW4gYmxvY2tNYXhMZW5ndGhcbiAgICBpZiAoYmxvY2tNYXhMZW5ndGggPiB0aGlzLnRvdGFsUGFnZXMoKSlcbiAgICAgIHJldHVybiBnZW5lcmF0ZUFycmF5KHRoaXMub3B0aW9ucy5zdGFydFBhZ2UsIHRoaXMubGFzdFBhZ2UoKSAtIHRoaXMub3B0aW9ucy5zdGFydFBhZ2UgKyAxKTtcblxuICAgIC8vIFNob3cgY2h1bmsgb2YgYmxvY2tNYXhMZW5ndGggcGFnZXNcbiAgICBjb25zdCBwYWRkaW5nID0gTWF0aC5mbG9vcihibG9ja01heExlbmd0aCAvIDIpO1xuICAgIGlmICh0aGlzLnNlbGVjdGVkUGFnZSgpIC0gcGFkZGluZyA8PSB0aGlzLm9wdGlvbnMuc3RhcnRQYWdlKVxuICAgICAgcmV0dXJuIGdlbmVyYXRlQXJyYXkodGhpcy5vcHRpb25zLnN0YXJ0UGFnZSwgYmxvY2tNYXhMZW5ndGgpO1xuICAgIGlmICh0aGlzLnNlbGVjdGVkUGFnZSgpICsgcGFkZGluZyA+PSB0aGlzLmxhc3RQYWdlKCkpXG4gICAgICByZXR1cm4gZ2VuZXJhdGVBcnJheSh0aGlzLmxhc3RQYWdlKCkgLSBibG9ja01heExlbmd0aCArIDEsIGJsb2NrTWF4TGVuZ3RoKTtcbiAgICByZXR1cm4gZ2VuZXJhdGVBcnJheSh0aGlzLnNlbGVjdGVkUGFnZSgpIC0gcGFkZGluZywgYmxvY2tNYXhMZW5ndGgpO1xuICB9XG5cbiAgLyoqXG4gICAqIENhbGN1bGF0ZXMgd2hpY2ggcmFuZ2Ugb2YgaXRlbXMgdGhhdCBpcyBjdXJyZW50bHkgdmlzaWJsZSBmcm9tIHBhZ2luYXRpb25cbiAgICogQHBhcmFtIHtudW1iZXJ9IGl0ZW1zIC0gaG93IG1hbnkgaXRlbXMgd2UgaGF2ZVxuICAgKiBAcGFyYW0ge251bWJlcn0gaXRlbXNQZXJQYWdlIC0gaG93IG1hbnkgaXRlbXMgdGhlcmUgYXJlLCBwZXIgcGFnZVxuICAgKiBAcmV0dXJucyB0byBmcm9tIG9iamVjdCB3aXRoIGNhbGN1bGF0ZWQgdmFsdWVzXG4gICAqL1xuICBwdWJsaWMgZ2V0VG9Gcm9tKGl0ZW1zOiBudW1iZXIsIGl0ZW1zUGVyUGFnZTogbnVtYmVyKSB7XG4gICAgcmV0dXJuIFBhZ2luYXRpb25Db250cm9sLmdldFRvRnJvbShpdGVtcywgaXRlbXNQZXJQYWdlLCB0aGlzLnNlbGVjdGVkUGFnZSgpLCB0aGlzLm9wdGlvbnMuc3RhcnRQYWdlKTtcbiAgfVxuXG4gIC8qKiBIZWxwZXIgdG8gY2FsY3VsYXRlIHRvdGFsIHBhZ2VzICovXG4gIHB1YmxpYyBnZXRUb3RhbFBhZ2VzID0gUGFnaW5hdGlvbkNvbnRyb2wuZ2V0VG90YWxQYWdlcztcblxuICAvKiogT24gZmlyc3QgcGFnZSBldmVudCAqL1xuICBwdWJsaWMgZ29Ub0ZpcnN0UGFnZSgpOiB2b2lkIHtcbiAgICB0aGlzLmdvVG9QYWdlKHRoaXMub3B0aW9ucy5zdGFydFBhZ2UpO1xuICB9XG5cbiAgLyoqIE9uIGxhc3QgcGFnZSBldmVudCAqL1xuICBwdWJsaWMgZ29Ub0xhc3RQYWdlKCk6IHZvaWQge1xuICAgIHRoaXMuZ29Ub1BhZ2UodGhpcy5sYXN0UGFnZSgpKTtcbiAgfVxuXG4gIC8qKiBPbiBuZXh0IHBhZ2UgZXZlbnQgKi9cbiAgcHVibGljIGdvVG9OZXh0UGFnZSgpOiB2b2lkIHtcbiAgICB0aGlzLmdvVG9QYWdlKHRoaXMuc2VsZWN0ZWRQYWdlKCkgKyAxKTtcbiAgfVxuXG4gIC8qKiBPbiBwYWdlIGNoYW5nZSBldmVudCAqL1xuICBwdWJsaWMgZ29Ub1BhZ2UocGFnZU51bWJlcjogbnVtYmVyIHwgc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKE51bWJlci5pc05hTigrcGFnZU51bWJlcikpIHRocm93IG5ldyBFcnJvcihcIkNhbid0IGdvIHRvIHBhZ2UsIGludmFsaWQgbnVtYmVyXCIpO1xuICAgIGlmICgocGFnZU51bWJlciBhcyBudW1iZXIpID4gdGhpcy5sYXN0UGFnZSgpKSB0aGlzLnNlbGVjdGVkUGFnZS5zZXQodGhpcy5sYXN0UGFnZSgpKTtcbiAgICBlbHNlIGlmICgocGFnZU51bWJlciBhcyBudW1iZXIpIDwgdGhpcy5vcHRpb25zLnN0YXJ0UGFnZSkgdGhpcy5zZWxlY3RlZFBhZ2Uuc2V0KHRoaXMub3B0aW9ucy5zdGFydFBhZ2UpO1xuICAgIGVsc2UgdGhpcy5zZWxlY3RlZFBhZ2Uuc2V0KHBhZ2VOdW1iZXIgYXMgbnVtYmVyKTtcbiAgfVxuXG4gIC8qKiBPbiBwcmV2aW91cyBwYWdlIGV2ZW50ICovXG4gIHB1YmxpYyBnb1RvUHJldmlvdXNQYWdlKCk6IHZvaWQge1xuICAgIHRoaXMuZ29Ub1BhZ2UodGhpcy5zZWxlY3RlZFBhZ2UoKSAtIDEpO1xuICB9XG5cbiAgLyoqIFJldHVybnMgaWYgcGFnZSBpcyBjdXJyZW50IHBhZ2UgKi9cbiAgcHVibGljIGlzQ3VycmVudFBhZ2UocGFnZTogbnVtYmVyIHwgc3RyaW5nKTogYm9vbGVhbiB7XG4gICAgcmV0dXJuIHBhZ2UgPT09IHRoaXMuc2VsZWN0ZWRQYWdlKCk7XG4gIH1cblxuICAvKipcbiAgICogSW4gdGhlIGNvbnRleHQgd2hlcmUgdG90YWwgcGFnZXMgaXMgY2FsY3VsYXRlZCB1c2luZyBpdGVtcyBwZXIgcGFnZSAobXVjaCBsaWtlIHRhYmxlcyB1c2UpIHdlIGNhbiB1c2UgdGhpcyB1dGlsaXR5IHRvIGVhc2lseSBhZGp1c3QgdGhlIHBhZ2luYXRpb24gY29udHJvbCBhdXRvbWF0aWNhbGx5LlxuICAgKiBOT1RFOiBieSBkZWZhdWx0IHRoaXMgcmVzZXRzIHRoZSBwYWdpbmF0aW9uIHRvIHRoZSBmaXJzdCBwYWdlIHdoZW4gY2FsbGVkLlxuICAgKi9cbiAgcHVibGljIHJlc2V0UGFnZUNvdW50KHRvdGFsSXRlbXM6IG51bWJlciwgaXRlbXNQZXJQYWdlOiBudW1iZXIsIGF1dG9SZXNldFRvRmlyc3RQYWdlID0gdHJ1ZSk6IHZvaWQge1xuICAgIGNvbnN0IHRvdGFsUGFnZXMgPSB0aGlzLmdldFRvdGFsUGFnZXModG90YWxJdGVtcywgaXRlbXNQZXJQYWdlKTtcbiAgICB0aGlzLnRvdGFsUGFnZXMuc2V0KHRvdGFsUGFnZXMpO1xuICAgIGlmIChhdXRvUmVzZXRUb0ZpcnN0UGFnZSkgdGhpcy5nb1RvRmlyc3RQYWdlKCk7XG4gIH1cblxuICAvLy8gU1RBVElDIFVUSUxJVElFUzpcblxuICAvKipcbiAgICogQ2FsY3VsYXRlcyB3aGljaCByYW5nZSBvZiBpdGVtcyB0aGF0IGlzIGN1cnJlbnRseSB2aXNpYmxlIGZyb20gcGFnaW5hdGlvblxuICAgKiBAcGFyYW0ge251bWJlcn0gaXRlbXMgLSBob3cgbWFueSBpdGVtcyB3ZSBoYXZlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpdGVtc1BlclBhZ2UgLSBob3cgbWFueSBpdGVtcyB0aGVyZSBhcmUsIHBlciBwYWdlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBjdXJyZW50UGFnZSAtIHdoaWNoIHBhZ2UgaXMgdmlzaWJsZVxuICAgKiBAcGFyYW0ge251bWJlcn0gc3RhcnRQYWdlIC0gd2hpY2ggcGFnZSB3ZSdyZSBzdGFydGluZyBmcm9tLCBkZWZhdWx0cyB0byBwYWdlIDEgKG9wdGlvbmFsKVxuICAgKiBAcmV0dXJucyB0byBmcm9tIG9iamVjdCB3aXRoIGNhbGN1bGF0ZWQgdmFsdWVzXG4gICAqL1xuICBwdWJsaWMgc3RhdGljIGdldFRvRnJvbSA9IChpdGVtczogbnVtYmVyLCBpdGVtc1BlclBhZ2U6IG51bWJlciwgY3VycmVudFBhZ2U6IG51bWJlciwgc3RhcnRQYWdlOiBudW1iZXIgPSAxKSA9PiB7XG4gICAgaWYgKGl0ZW1zUGVyUGFnZSA8IDEgfHwgaXRlbXMgPCAxKSByZXR1cm4geyAwOiAwLCAxOiAwLCBmcm9tOiAwLCB0bzogMCB9O1xuICAgIGNvbnN0IG5vcm1hbGl6ZWRQYWdlTnVtYmVyID0gY3VycmVudFBhZ2UgLSBzdGFydFBhZ2UgKyAxO1xuICAgIGNvbnN0IGZyb20gPSBNYXRoLm1heCgobm9ybWFsaXplZFBhZ2VOdW1iZXIgLSAxKSAqIGl0ZW1zUGVyUGFnZSArIDEsIDApO1xuICAgIGNvbnN0IHRvID0gTWF0aC5tYXgoZnJvbSArIGl0ZW1zUGVyUGFnZSAtIDEgPiBpdGVtcyA/IGl0ZW1zIDogZnJvbSArIGl0ZW1zUGVyUGFnZSAtIDEsIDApO1xuICAgIHJldHVybiB7IDA6IGZyb20sIDE6IHRvLCBmcm9tLCB0byB9O1xuICB9O1xuXG4gIC8qKlxuICAgKiBDYWxjdWxhdGVzIGhvdyBtYW55IHBhZ2VzIHRoZXJlIGFyZVxuICAgKiBAcGFyYW0ge251bWJlcn0gaXRlbXMgLSBob3cgbWFueSBpdGVtcyB3ZSBoYXZlXG4gICAqIEBwYXJhbSB7bnVtYmVyfSBpdGVtc1BlclBhZ2UgLSBob3cgbWFueSBpdGVtcyB0aGVyZSBhcmUsIHBlciBwYWdlXG4gICAqIEByZXR1cm5zIHtudW1iZXJ9IGhvdyBtYW55IHBhZ2VzIHRoZXJlIGFyZSBpbiB0b3RhbFxuICAgKi9cbiAgcHVibGljIHN0YXRpYyBnZXRUb3RhbFBhZ2VzKGl0ZW1zOiBudW1iZXIsIGl0ZW1zUGVyUGFnZTogbnVtYmVyKTogbnVtYmVyIHtcbiAgICByZXR1cm4gTWF0aC5jZWlsKGl0ZW1zIC8gaXRlbXNQZXJQYWdlKTtcbiAgfVxufVxuIl19