carbon-components-angular
Version:
Next generation components
483 lines (473 loc) • 46.1 kB
JavaScript
import { Component, Input, Output, EventEmitter } from "@angular/core";
import { merge } from "carbon-components-angular/utils";
import * as i0 from "@angular/core";
import * as i1 from "carbon-components-angular/i18n";
import * as i2 from "carbon-components-angular/experimental";
import * as i3 from "@angular/common";
import * as i4 from "@angular/forms";
import * as i5 from "carbon-components-angular/icon";
import * as i6 from "carbon-components-angular/forms";
/**
* Use pagination when you have multiple pages of data to handle. Get started with importing the module:
*
* ```typescript
* import { PaginationModule } from 'carbon-components-angular';
* ```
*
* ```html
* <cds-pagination [model]="model" (selectPage)="selectPage($event)"></cds-pagination>
* ```
*
* In your `selectPage()` method set the `model.currentPage` to selected page, _after_
* you load the page.
*
* ```typescript
* selectPage(page) {
* // ... your code to load the page goes here
*
* this.model.currentPage = page;
*
* // ... anything you want to do after page selection changes goes here
* }
* ```
*
* [See demo](../../?path=/story/components-pagination--basic)
*/
export class Pagination {
constructor(i18n, experimental) {
this.i18n = i18n;
this.experimental = experimental;
/**
* Set to `true` for a loading pagination component.
*/
this.skeleton = false;
/**
* Set to `true` to disable the backward/forward buttons.
*/
this.disabled = false;
/**
* Set to `true` to disable the select box that changes the page.
*/
this.pageInputDisabled = false;
/**
* Controls wether or not to show the page selects
*/
this.showPageInput = true;
/**
* Set to `true` if the total number of items is unknown.
*/
this.pagesUnknown = false;
this.pageSelectThreshold = 1000;
/**
* Options for items per page select
*
* A default array of options will be defined: [10, 20, 30, 40, 50]
*/
this.itemsPerPageOptions = [10, 20, 30, 40, 50];
/**
* Emits the new page number.
*
* You should tie into this and update `model.currentPage` once the fresh
* data is finally loaded.
*/
this.selectPage = new EventEmitter();
this.itemsPerPageSelectId = `pagination-select-items-per-page-${Pagination.paginationCounter}`;
this.currentPageSelectId = `pagination-select-current-page-${Pagination.paginationCounter}`;
this.itemsPerPageText = this.i18n.getOverridable("PAGINATION.ITEMS_PER_PAGE");
this.optionsListText = this.i18n.getOverridable("PAGINATION.OPEN_LIST_OF_OPTIONS");
this.backwardText = this.i18n.getOverridable("PAGINATION.BACKWARD");
this.forwardText = this.i18n.getOverridable("PAGINATION.FORWARD");
this.totalItemsText = this.i18n.getOverridable("PAGINATION.TOTAL_ITEMS");
this.totalItemText = this.i18n.getOverridable("PAGINATION.TOTAL_ITEM");
this.totalItemsUnknownText = this.i18n.getOverridable("PAGINATION.TOTAL_ITEMS_UNKNOWN");
this.pageText = this.i18n.getOverridable("PAGINATION.PAGE");
this.ofLastPagesText = this.i18n.getOverridable("PAGINATION.OF_LAST_PAGES");
this.ofLastPageText = this.i18n.getOverridable("PAGINATION.OF_LAST_PAGE");
this._pageOptions = [];
Pagination.paginationCounter++;
}
/**
* Expects an object that contains some or all of:
* ```
* {
* "ITEMS_PER_PAGE": "Items per page:",
* "OPEN_LIST_OF_OPTIONS": "Open list of options",
* "BACKWARD": "Backward",
* "FORWARD": "Forward",
* "TOTAL_ITEMS_UNKNOWN": "{{start}}-{{end}} items",
* "TOTAL_ITEMS": "{{start}}-{{end}} of {{total}} items",
* "TOTAL_ITEM": "{{start}}-{{end}} of {{total}} item",
* "OF_LAST_PAGES": "of {{last}} pages",
* "OF_LAST_PAGE": "of {{last}} page"
* }
* ```
*/
set translations(value) {
const valueWithDefaults = merge(this.i18n.getMultiple("PAGINATION"), value);
this.itemsPerPageText.override(valueWithDefaults.ITEMS_PER_PAGE);
this.optionsListText.override(valueWithDefaults.OPEN_LIST_OF_OPTIONS);
this.backwardText.override(valueWithDefaults.BACKWARD);
this.forwardText.override(valueWithDefaults.FORWARD);
this.totalItemsText.override(valueWithDefaults.TOTAL_ITEMS);
this.totalItemText.override(valueWithDefaults.TOTAL_ITEM);
this.totalItemsUnknownText.override(valueWithDefaults.TOTAL_ITEMS_UNKNOWN);
this.pageText.override(valueWithDefaults.PAGE);
this.ofLastPagesText.override(valueWithDefaults.OF_LAST_PAGES);
this.ofLastPageText.override(valueWithDefaults.OF_LAST_PAGE);
}
get itemsPerPage() {
return this.model.pageLength;
}
set itemsPerPage(value) {
this.model.pageLength = Number(value);
this.currentPage = 1; // reset page
}
get currentPage() {
return this.model.currentPage;
}
set currentPage(value) {
value = Number(value);
// emits the value to allow the user to update current page
// in the model once the page is loaded
this.selectPage.emit(value);
}
get totalDataLength() {
return this.model.totalDataLength;
}
/**
* The last page number to display in the pagination view.
*/
get lastPage() {
const last = Math.ceil(this.totalDataLength / this.itemsPerPage);
return last > 0 ? last : 1;
}
get startItemIndex() {
return this.endItemIndex > 0 ? (this.currentPage - 1) * this.itemsPerPage + 1 : 0;
}
get endItemIndex() {
const projectedEndItemIndex = this.currentPage * this.itemsPerPage;
return projectedEndItemIndex < this.totalDataLength ? projectedEndItemIndex : this.totalDataLength;
}
/**
* The previous page number to navigate to, from the current page.
*/
get previousPage() {
return this.currentPage <= 1 ? 1 : this.currentPage - 1;
}
/**
* The next page number to navigate to, from the current page.
*/
get nextPage() {
const lastPage = this.lastPage;
return this.currentPage >= lastPage ? lastPage : this.currentPage + 1;
}
get pageOptions() {
/**
* Calculate number of pages based on totalDataLength and itemsPerPage.
* Even if totalDataLength is 0, numberOfPages should be always at least 1.
* New array will be constructed only if number of pages changes.
*/
const numberOfPages = Math.max(Math.ceil(this.totalDataLength / this.itemsPerPage), 1);
if (this._pageOptions.length !== numberOfPages) {
this._pageOptions = Array(numberOfPages);
}
return this._pageOptions;
}
}
Pagination.paginationCounter = 0;
Pagination.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Pagination, deps: [{ token: i1.I18n }, { token: i2.ExperimentalService }], target: i0.ɵɵFactoryTarget.Component });
Pagination.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.3.0", type: Pagination, selector: "cds-pagination, ibm-pagination", inputs: { skeleton: "skeleton", model: "model", disabled: "disabled", pageInputDisabled: "pageInputDisabled", showPageInput: "showPageInput", pagesUnknown: "pagesUnknown", pageSelectThreshold: "pageSelectThreshold", translations: "translations", itemsPerPageOptions: "itemsPerPageOptions" }, outputs: { selectPage: "selectPage" }, ngImport: i0, template: `
<div
class="cds--pagination"
[ngClass]="{
'cds--skeleton': skeleton
}">
<!-- left skeleton div -->
<div *ngIf="skeleton" class="cds--pagination__left">
<p class="cds--skeleton__text" style="width: 70px"></p>
<p class="cds--skeleton__text" style="width: 35px"></p>
<p class="cds--skeleton__text" style="width: 105px"></p>
</div>
<div *ngIf="!skeleton" class="cds--pagination__left">
<ng-container *ngIf="showPageInput">
<label class="cds--pagination__text" [for]="itemsPerPageSelectId">
{{itemsPerPageText.subject | async}}
</label>
<div
class="cds--select cds--select--inline cds--select__item-count"
[class.cds--select--disabled]="pageInputDisabled">
<select
[id]="itemsPerPageSelectId"
[(ngModel)]="itemsPerPage"
[disabled]="pageInputDisabled"
class="cds--select-input">
<option
class="cds--select-option"
*ngFor="let option of itemsPerPageOptions"
[value]="option">
{{ option }}
</option>
</select>
<svg
cdsIcon="chevron--down"
size="16"
style="display: inherit"
class="cds--select__arrow"
aria-hidden="true"
[attr.ariaLabel]="optionsListText.subject | async">
</svg>
</div>
</ng-container>
<span *ngIf="!pagesUnknown && totalDataLength <= 1" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex, total: totalDataLength } | async}}
</span>
<span *ngIf="!pagesUnknown && totalDataLength > 1" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemsText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex, total: totalDataLength } | async}}
</span>
<span *ngIf="pagesUnknown" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemsUnknownText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex } | async}}
</span>
</div>
<!-- right skeleton div -->
<div *ngIf="skeleton" class="cds--pagination__right">
<p class="cds--skeleton__text" style="width: 70px"></p>
</div>
<div *ngIf="!skeleton" class="cds--pagination__right">
<span *ngIf="pagesUnknown" class="cds--pagination__text cds--pagination__page-text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{pageText.subject | async}}
</span>
<ng-container *ngIf="showPageInput">
<div
class="cds--select cds--select--inline cds--select__page-number"
[class.cds--select--disabled]="pageInputDisabled">
<label [for]="currentPageSelectId" class="cds--label cds--visually-hidden">{{pageText.subject | async}}</label>
<input
*ngIf="pageOptions.length > pageSelectThreshold"
style="padding-right: 1rem; margin-right: 1rem;"
[id]="currentPageSelectId"
type="number"
min="1"
[max]="pageOptions.length"
class="cds--select-input"
[(ngModel)]="currentPage">
<select
*ngIf="pageOptions.length <= pageSelectThreshold"
[id]="currentPageSelectId"
class="cds--select-input"
[disabled]="pageInputDisabled"
[(ngModel)]="currentPage">
<option *ngFor="let page of pageOptions; let i = index;" class="cds--select-option" [value]="i + 1">{{i + 1}}</option>
</select>
<svg
*ngIf="pageOptions.length <= pageSelectThreshold"
cdsIcon="chevron--down"
size="16"
style="display: inherit;"
class="cds--select__arrow"
[attr.ariaLabel]="optionsListText.subject | async">
</svg>
</div>
</ng-container>
<span *ngIf="!pagesUnknown && lastPage <= 1" class="cds--pagination__text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{ofLastPageText.subject | i18nReplace: {last: lastPage} | async}}
</span>
<span *ngIf="!pagesUnknown && lastPage > 1" class="cds--pagination__text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{ofLastPagesText.subject | i18nReplace: {last: lastPage} | async}}
</span>
<div class="cds--pagination__control-buttons">
<button
cdsButton="ghost"
iconOnly="true"
class="cds--pagination__button cds--pagination__button--backward"
[ngClass]="{
'cds--pagination__button--no-index': currentPage <= 1 || disabled
}"
tabindex="0"
[attr.aria-label]="backwardText.subject | async"
(click)="selectPage.emit(previousPage)"
[disabled]="(currentPage <= 1 || disabled ? true : null)">
<svg cdsIcon="caret--left" size="16" class="cds--btn__icon"></svg>
</button>
<button
cdsButton="ghost"
iconOnly="true"
class="
cds--pagination__button
cds--pagination__button--forward"
[ngClass]="{
'cds--pagination__button--no-index': currentPage >= lastPage || disabled
}"
tabindex="0"
[attr.aria-label]="forwardText.subject | async"
(click)="selectPage.emit(nextPage)"
[disabled]="(currentPage >= lastPage || disabled ? true : null)">
<svg cdsIcon="caret--right" size="16" class="cds--btn__icon"></svg>
</button>
</div>
</div>
</div>
`, isInline: true, dependencies: [{ kind: "directive", type: i3.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i3.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i3.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i3.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i4.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i4.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i4.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i4.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i4.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i4.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i4.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i4.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.IconDirective, selector: "[cdsIcon], [ibmIcon]", inputs: ["ibmIcon", "cdsIcon", "size", "title", "ariaLabel", "ariaLabelledBy", "ariaHidden", "isFocusable"] }, { kind: "directive", type: i6.Button, selector: "[cdsButton], [ibmButton]", inputs: ["ibmButton", "cdsButton", "size", "skeleton", "iconOnly", "isExpressive"] }, { kind: "pipe", type: i3.AsyncPipe, name: "async" }, { kind: "pipe", type: i1.ReplacePipe, name: "i18nReplace" }] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.3.0", ngImport: i0, type: Pagination, decorators: [{
type: Component,
args: [{
selector: "cds-pagination, ibm-pagination",
template: `
<div
class="cds--pagination"
[ngClass]="{
'cds--skeleton': skeleton
}">
<!-- left skeleton div -->
<div *ngIf="skeleton" class="cds--pagination__left">
<p class="cds--skeleton__text" style="width: 70px"></p>
<p class="cds--skeleton__text" style="width: 35px"></p>
<p class="cds--skeleton__text" style="width: 105px"></p>
</div>
<div *ngIf="!skeleton" class="cds--pagination__left">
<ng-container *ngIf="showPageInput">
<label class="cds--pagination__text" [for]="itemsPerPageSelectId">
{{itemsPerPageText.subject | async}}
</label>
<div
class="cds--select cds--select--inline cds--select__item-count"
[class.cds--select--disabled]="pageInputDisabled">
<select
[id]="itemsPerPageSelectId"
[(ngModel)]="itemsPerPage"
[disabled]="pageInputDisabled"
class="cds--select-input">
<option
class="cds--select-option"
*ngFor="let option of itemsPerPageOptions"
[value]="option">
{{ option }}
</option>
</select>
<svg
cdsIcon="chevron--down"
size="16"
style="display: inherit"
class="cds--select__arrow"
aria-hidden="true"
[attr.ariaLabel]="optionsListText.subject | async">
</svg>
</div>
</ng-container>
<span *ngIf="!pagesUnknown && totalDataLength <= 1" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex, total: totalDataLength } | async}}
</span>
<span *ngIf="!pagesUnknown && totalDataLength > 1" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemsText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex, total: totalDataLength } | async}}
</span>
<span *ngIf="pagesUnknown" class="cds--pagination__text cds--pagination__items-count" [ngStyle]="{'margin-left': showPageInput ? null : 0}">
{{totalItemsUnknownText.subject | i18nReplace:{start: startItemIndex, end: endItemIndex } | async}}
</span>
</div>
<!-- right skeleton div -->
<div *ngIf="skeleton" class="cds--pagination__right">
<p class="cds--skeleton__text" style="width: 70px"></p>
</div>
<div *ngIf="!skeleton" class="cds--pagination__right">
<span *ngIf="pagesUnknown" class="cds--pagination__text cds--pagination__page-text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{pageText.subject | async}}
</span>
<ng-container *ngIf="showPageInput">
<div
class="cds--select cds--select--inline cds--select__page-number"
[class.cds--select--disabled]="pageInputDisabled">
<label [for]="currentPageSelectId" class="cds--label cds--visually-hidden">{{pageText.subject | async}}</label>
<input
*ngIf="pageOptions.length > pageSelectThreshold"
style="padding-right: 1rem; margin-right: 1rem;"
[id]="currentPageSelectId"
type="number"
min="1"
[max]="pageOptions.length"
class="cds--select-input"
[(ngModel)]="currentPage">
<select
*ngIf="pageOptions.length <= pageSelectThreshold"
[id]="currentPageSelectId"
class="cds--select-input"
[disabled]="pageInputDisabled"
[(ngModel)]="currentPage">
<option *ngFor="let page of pageOptions; let i = index;" class="cds--select-option" [value]="i + 1">{{i + 1}}</option>
</select>
<svg
*ngIf="pageOptions.length <= pageSelectThreshold"
cdsIcon="chevron--down"
size="16"
style="display: inherit;"
class="cds--select__arrow"
[attr.ariaLabel]="optionsListText.subject | async">
</svg>
</div>
</ng-container>
<span *ngIf="!pagesUnknown && lastPage <= 1" class="cds--pagination__text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{ofLastPageText.subject | i18nReplace: {last: lastPage} | async}}
</span>
<span *ngIf="!pagesUnknown && lastPage > 1" class="cds--pagination__text">
<ng-container *ngIf="!showPageInput">{{currentPage}}</ng-container>
{{ofLastPagesText.subject | i18nReplace: {last: lastPage} | async}}
</span>
<div class="cds--pagination__control-buttons">
<button
cdsButton="ghost"
iconOnly="true"
class="cds--pagination__button cds--pagination__button--backward"
[ngClass]="{
'cds--pagination__button--no-index': currentPage <= 1 || disabled
}"
tabindex="0"
[attr.aria-label]="backwardText.subject | async"
(click)="selectPage.emit(previousPage)"
[disabled]="(currentPage <= 1 || disabled ? true : null)">
<svg cdsIcon="caret--left" size="16" class="cds--btn__icon"></svg>
</button>
<button
cdsButton="ghost"
iconOnly="true"
class="
cds--pagination__button
cds--pagination__button--forward"
[ngClass]="{
'cds--pagination__button--no-index': currentPage >= lastPage || disabled
}"
tabindex="0"
[attr.aria-label]="forwardText.subject | async"
(click)="selectPage.emit(nextPage)"
[disabled]="(currentPage >= lastPage || disabled ? true : null)">
<svg cdsIcon="caret--right" size="16" class="cds--btn__icon"></svg>
</button>
</div>
</div>
</div>
`
}]
}], ctorParameters: function () { return [{ type: i1.I18n }, { type: i2.ExperimentalService }]; }, propDecorators: { skeleton: [{
type: Input
}], model: [{
type: Input
}], disabled: [{
type: Input
}], pageInputDisabled: [{
type: Input
}], showPageInput: [{
type: Input
}], pagesUnknown: [{
type: Input
}], pageSelectThreshold: [{
type: Input
}], translations: [{
type: Input
}], itemsPerPageOptions: [{
type: Input
}], selectPage: [{
type: Output
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGFnaW5hdGlvbi5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcGFnaW5hdGlvbi9wYWdpbmF0aW9uLmNvbXBvbmVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQ04sU0FBUyxFQUNULEtBQUssRUFDTCxNQUFNLEVBQ04sWUFBWSxFQUNaLE1BQU0sZUFBZSxDQUFDO0FBSXZCLE9BQU8sRUFBRSxLQUFLLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQzs7Ozs7Ozs7QUFjeEQ7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0F5Qkc7QUErSUgsTUFBTSxPQUFPLFVBQVU7SUE4SnRCLFlBQXNCLElBQVUsRUFBWSxZQUFpQztRQUF2RCxTQUFJLEdBQUosSUFBSSxDQUFNO1FBQVksaUJBQVksR0FBWixZQUFZLENBQXFCO1FBM0o3RTs7V0FFRztRQUNNLGFBQVEsR0FBRyxLQUFLLENBQUM7UUFLMUI7O1dBRUc7UUFDTSxhQUFRLEdBQUcsS0FBSyxDQUFDO1FBQzFCOztXQUVHO1FBQ00sc0JBQWlCLEdBQUcsS0FBSyxDQUFDO1FBQ25DOztXQUVHO1FBQ00sa0JBQWEsR0FBRyxJQUFJLENBQUM7UUFDOUI7O1dBRUc7UUFDTSxpQkFBWSxHQUFHLEtBQUssQ0FBQztRQUNyQix3QkFBbUIsR0FBRyxJQUFJLENBQUM7UUFpQ3BDOzs7O1dBSUc7UUFDTSx3QkFBbUIsR0FBYSxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLENBQUMsQ0FBQztRQUU5RDs7Ozs7V0FLRztRQUNPLGVBQVUsR0FBRyxJQUFJLFlBQVksRUFBVSxDQUFDO1FBcUVsRCx5QkFBb0IsR0FBRyxvQ0FBb0MsVUFBVSxDQUFDLGlCQUFpQixFQUFFLENBQUM7UUFDMUYsd0JBQW1CLEdBQUcsa0NBQWtDLFVBQVUsQ0FBQyxpQkFBaUIsRUFBRSxDQUFDO1FBRXZGLHFCQUFnQixHQUFHLElBQUksQ0FBQyxJ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UUFBUTtzQkFBaEIsS0FBSztnQkFJRyxpQkFBaUI7c0JBQXpCLEtBQUs7Z0JBSUcsYUFBYTtzQkFBckIsS0FBSztnQkFJRyxZQUFZO3NCQUFwQixLQUFLO2dCQUNHLG1CQUFtQjtzQkFBM0IsS0FBSztnQkFtQkYsWUFBWTtzQkFEZixLQUFLO2dCQW9CRyxtQkFBbUI7c0JBQTNCLEtBQUs7Z0JBUUksVUFBVTtzQkFBbkIsTUFBTSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFBhZ2luYXRpb25Nb2RlbCB9IGZyb20gXCIuL3BhZ2luYXRpb24tbW9kZWwuY2xhc3NcIjtcbmltcG9ydCB7XG5cdENvbXBvbmVudCxcblx0SW5wdXQsXG5cdE91dHB1dCxcblx0RXZlbnRFbWl0dGVyXG59IGZyb20gXCJAYW5ndWxhci9jb3JlXCI7XG5cbmltcG9ydCB7IEkxOG4sIE92ZXJyaWRhYmxlIH0gZnJvbSBcImNhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXIvaTE4blwiO1xuaW1wb3J0IHsgRXhwZXJpbWVudGFsU2VydmljZSB9IGZyb20gXCJjYXJib24tY29tcG9uZW50cy1hbmd1bGFyL2V4cGVyaW1lbnRhbFwiO1xuaW1wb3J0IHsgbWVyZ2UgfSBmcm9tIFwiY2FyYm9uLWNvbXBvbmVudHMtYW5ndWxhci91dGlsc1wiO1xuXG5leHBvcnQgaW50ZXJmYWNlIFBhZ2luYXRpb25UcmFuc2xhdGlvbnMge1xuXHRJVEVNU19QRVJfUEFHRTogc3RyaW5nO1xuXHRPUEVOX0xJU1RfT0ZfT1BUSU9OUzogc3RyaW5nO1xuXHRCQUNLV0FSRDogc3RyaW5nO1xuXHRGT1JXQVJEOiBzdHJpbmc7XG5cdFRPVEFMX0lURU1TX1VOS05PV046IHN0cmluZztcblx0VE9UQUxfSVRFTVM6IHN0cmluZztcblx0VE9UQUxfSVRFTTogc3RyaW5nO1xuXHRPRl9MQVNUX1BBR0VTOiBzdHJpbmc7XG5cdE9GX0xBU1RfUEFHRTogc3RyaW5nO1xufVxuXG4vKipcbiAqIFVzZSBwYWdpbmF0aW9uIHdoZW4geW91IGhhdmUgbXVsdGlwbGUgcGFnZXMgb2YgZGF0YSB0byBoYW5kbGUuIEdldCBzdGFydGVkIHdpdGggaW1wb3J0aW5nIHRoZSBtb2R1bGU6XG4gKlxuICogYGBgdHlwZXNjcmlwdFxuICogaW1wb3J0IHsgUGFnaW5hdGlvbk1vZHVsZSB9IGZyb20gJ2NhcmJvbi1jb21wb25lbnRzLWFuZ3VsYXInO1xuICogYGBgXG4gKlxuICogYGBgaHRtbFxuICpcdDxjZHMtcGFnaW5hdGlvbiBbbW9kZWxdPVwibW9kZWxcIiAoc2VsZWN0UGFnZSk9XCJzZWxlY3RQYWdlKCRldmVudClcIj48L2Nkcy1wYWdpbmF0aW9uPlxuICogYGBgXG4gKlxuICogSW4geW91ciBgc2VsZWN0UGFnZSgpYCBtZXRob2Qgc2V0IHRoZSBgbW9kZWwuY3VycmVudFBhZ2VgIHRvIHNlbGVjdGVkIHBhZ2UsIF9hZnRlcl9cbiAqIHlvdSBsb2FkIHRoZSBwYWdlLlxuICpcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIHNlbGVjdFBhZ2UocGFnZSkge1xuICogXHQvLyAuLi4geW91ciBjb2RlIHRvIGxvYWQgdGhlIHBhZ2UgZ29lcyBoZXJlXG4gKlxuICogXHR0aGlzLm1vZGVsLmN1cnJlbnRQYWdlID0gcGFnZTtcbiAqXG4gKiBcdC8vIC4uLiBhbnl0aGluZyB5b3Ugd2FudCB0byBkbyBhZnRlciBwYWdlIHNlbGVjdGlvbiBjaGFuZ2VzIGdvZXMgaGVyZVxuICogfVxuICogYGBgXG4gKlxuICogW1NlZSBkZW1vXSguLi8uLi8/cGF0aD0vc3RvcnkvY29tcG9uZW50cy1wYWdpbmF0aW9uLS1iYXNpYylcbiAqL1xuQENvbXBvbmVudCh7XG5cdHNlbGVjdG9yOiBcImNkcy1wYWdpbmF0aW9uLCBpYm0tcGFnaW5hdGlvblwiLFxuXHR0ZW1wbGF0ZTogYFxuXHQ8ZGl2XG5cdFx0Y2xhc3M9XCJjZHMtLXBhZ2luYXRpb25cIlxuXHRcdFtuZ0NsYXNzXT1cIntcblx0XHRcdCdjZHMtLXNrZWxldG9uJzogc2tlbGV0b25cblx0XHR9XCI+XG5cdFx0PCEtLSBsZWZ0IHNrZWxldG9uIGRpdiAtLT5cblx0XHQ8ZGl2ICpuZ0lmPVwic2tlbGV0b25cIiBjbGFzcz1cImNkcy0tcGFnaW5hdGlvbl9fbGVmdFwiPlxuXHRcdFx0PHAgY2xhc3M9XCJjZHMtLXNrZWxldG9uX190ZXh0XCIgc3R5bGU9XCJ3aWR0aDogNzBweFwiPjwvcD5cblx0XHRcdDxwIGNsYXNzPVwiY2RzLS1za2VsZXRvbl9fdGV4dFwiIHN0eWxlPVwid2lkdGg6IDM1cHhcIj48L3A+XG5cdFx0XHQ8cCBjbGFzcz1cImNkcy0tc2tlbGV0b25fX3RleHRcIiBzdHlsZT1cIndpZHRoOiAxMDVweFwiPjwvcD5cblx0XHQ8L2Rpdj5cblxuXHRcdDxkaXYgKm5nSWY9XCIhc2tlbGV0b25cIiBjbGFzcz1cImNkcy0tcGFnaW5hdGlvbl9fbGVmdFwiPlxuXHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cInNob3dQYWdlSW5wdXRcIj5cblx0XHRcdFx0PGxhYmVsIGNsYXNzPVwiY2RzLS1wYWdpbmF0aW9uX190ZXh0XCIgW2Zvcl09XCJpdGVtc1BlclBhZ2VTZWxlY3RJZFwiPlxuXHRcdFx0XHRcdHt7aXRlbXNQZXJQYWdlVGV4dC5zdWJqZWN0IHwgYXN5bmN9fVxuXHRcdFx0XHQ8L2xhYmVsPlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNlbGVjdCBjZHMtLXNlbGVjdC0taW5saW5lIGNkcy0tc2VsZWN0X19pdGVtLWNvdW50XCJcblx0XHRcdFx0XHRbY2xhc3MuY2RzLS1zZWxlY3QtLWRpc2FibGVkXT1cInBhZ2VJbnB1dERpc2FibGVkXCI+XG5cdFx0XHRcdFx0PHNlbGVjdFxuXHRcdFx0XHRcdFx0W2lkXT1cIml0ZW1zUGVyUGFnZVNlbGVjdElkXCJcblx0XHRcdFx0XHRcdFsobmdNb2RlbCldPVwiaXRlbXNQZXJQYWdlXCJcblx0XHRcdFx0XHRcdFtkaXNhYmxlZF09XCJwYWdlSW5wdXREaXNhYmxlZFwiXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tc2VsZWN0LWlucHV0XCI+XG5cdFx0XHRcdFx0XHQ8b3B0aW9uXG5cdFx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zZWxlY3Qtb3B0aW9uXCJcblx0XHRcdFx0XHRcdFx0Km5nRm9yPVwibGV0IG9wdGlvbiBvZiBpdGVtc1BlclBhZ2VPcHRpb25zXCJcblx0XHRcdFx0XHRcdFx0W3ZhbHVlXT1cIm9wdGlvblwiPlxuXHRcdFx0XHRcdFx0XHRcdHt7IG9wdGlvbiB9fVxuXHRcdFx0XHRcdFx0PC9vcHRpb24+XG5cdFx0XHRcdFx0PC9zZWxlY3Q+XG5cdFx0XHRcdFx0PHN2Z1xuXHRcdFx0XHRcdFx0Y2RzSWNvbj1cImNoZXZyb24tLWRvd25cIlxuXHRcdFx0XHRcdFx0c2l6ZT1cIjE2XCJcblx0XHRcdFx0XHRcdHN0eWxlPVwiZGlzcGxheTogaW5oZXJpdFwiXG5cdFx0XHRcdFx0XHRjbGFzcz1cImNkcy0tc2VsZWN0X19hcnJvd1wiXG5cdFx0XHRcdFx0XHRhcmlhLWhpZGRlbj1cInRydWVcIlxuXHRcdFx0XHRcdFx0W2F0dHIuYXJpYUxhYmVsXT1cIm9wdGlvbnNMaXN0VGV4dC5zdWJqZWN0IHwgYXN5bmNcIj5cblx0XHRcdFx0XHQ8L3N2Zz5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L25nLWNvbnRhaW5lcj5cblx0XHRcdDxzcGFuICpuZ0lmPVwiIXBhZ2VzVW5rbm93biAmJiB0b3RhbERhdGFMZW5ndGggPD0gMVwiIGNsYXNzPVwiY2RzLS1wYWdpbmF0aW9uX190ZXh0IGNkcy0tcGFnaW5hdGlvbl9faXRlbXMtY291bnRcIiBbbmdTdHlsZV09XCJ7J21hcmdpbi1sZWZ0Jzogc2hvd1BhZ2VJbnB1dCA/IG51bGwgOiAwfVwiPlxuXHRcdFx0XHR7e3RvdGFsSXRlbVRleHQuc3ViamVjdCB8IGkxOG5SZXBsYWNlOntzdGFydDogc3RhcnRJdGVtSW5kZXgsIGVuZDogZW5kSXRlbUluZGV4LCB0b3RhbDogdG90YWxEYXRhTGVuZ3RoIH0gfCBhc3luY319XG5cdFx0XHQ8L3NwYW4+XG5cdFx0XHQ8c3BhbiAqbmdJZj1cIiFwYWdlc1Vua25vd24gJiYgdG90YWxEYXRhTGVuZ3RoID4gMVwiIGNsYXNzPVwiY2RzLS1wYWdpbmF0aW9uX190ZXh0IGNkcy0tcGFnaW5hdGlvbl9faXRlbXMtY291bnRcIiBbbmdTdHlsZV09XCJ7J21hcmdpbi1sZWZ0Jzogc2hvd1BhZ2VJbnB1dCA/IG51bGwgOiAwfVwiPlxuXHRcdFx0XHR7e3RvdGFsSXRlbXNUZXh0LnN1YmplY3QgfCBpMThuUmVwbGFjZTp7c3RhcnQ6IHN0YXJ0SXRlbUluZGV4LCBlbmQ6IGVuZEl0ZW1JbmRleCwgdG90YWw6IHRvdGFsRGF0YUxlbmd0aCB9IHwgYXN5bmN9fVxuXHRcdFx0PC9zcGFuPlxuXHRcdFx0PHNwYW4gKm5nSWY9XCJwYWdlc1Vua25vd25cIiBjbGFzcz1cImNkcy0tcGFnaW5hdGlvbl9fdGV4dCBjZHMtLXBhZ2luYXRpb25fX2l0ZW1zLWNvdW50XCIgW25nU3R5bGVdPVwieydtYXJnaW4tbGVmdCc6IHNob3dQYWdlSW5wdXQgPyBudWxsIDogMH1cIj5cblx0XHRcdFx0e3t0b3RhbEl0ZW1zVW5rbm93blRleHQuc3ViamVjdCB8IGkxOG5SZXBsYWNlOntzdGFydDogc3RhcnRJdGVtSW5kZXgsIGVuZDogZW5kSXRlbUluZGV4IH0gfCBhc3luY319XG5cdFx0XHQ8L3NwYW4+XG5cdFx0PC9kaXY+XG5cblx0XHQ8IS0tIHJpZ2h0IHNrZWxldG9uIGRpdiAtLT5cblx0XHQ8ZGl2ICpuZ0lmPVwic2tlbGV0b25cIiBjbGFzcz1cImNkcy0tcGFnaW5hdGlvbl9fcmlnaHRcIj5cblx0XHRcdDxwIGNsYXNzPVwiY2RzLS1za2VsZXRvbl9fdGV4dFwiIHN0eWxlPVwid2lkdGg6IDcwcHhcIj48L3A+XG5cdFx0PC9kaXY+XG5cblx0XHQ8ZGl2ICpuZ0lmPVwiIXNrZWxldG9uXCIgY2xhc3M9XCJjZHMtLXBhZ2luYXRpb25fX3JpZ2h0XCI+XG5cdFx0XHQ8c3BhbiAqbmdJZj1cInBhZ2VzVW5rbm93blwiIGNsYXNzPVwiY2RzLS1wYWdpbmF0aW9uX190ZXh0IGNkcy0tcGFnaW5hdGlvbl9fcGFnZS10ZXh0XCI+XG5cdFx0XHRcdDxuZy1jb250YWluZXIgKm5nSWY9XCIhc2hvd1BhZ2VJbnB1dFwiPnt7Y3VycmVudFBhZ2V9fTwvbmctY29udGFpbmVyPlxuXHRcdFx0XHR7e3BhZ2VUZXh0LnN1YmplY3QgfCBhc3luY319XG5cdFx0XHQ8L3NwYW4+XG5cdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwic2hvd1BhZ2VJbnB1dFwiPlxuXHRcdFx0XHQ8ZGl2XG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNlbGVjdCBjZHMtLXNlbGVjdC0taW5saW5lIGNkcy0tc2VsZWN0X19wYWdlLW51bWJlclwiXG5cdFx0XHRcdFx0W2NsYXNzLmNkcy0tc2VsZWN0LS1kaXNhYmxlZF09XCJwYWdlSW5wdXREaXNhYmxlZFwiPlxuXHRcdFx0XHRcdDxsYWJlbCBbZm9yXT1cImN1cnJlbnRQYWdlU2VsZWN0SWRcIiBjbGFzcz1cImNkcy0tbGFiZWwgY2RzLS12aXN1YWxseS1oaWRkZW5cIj57e3BhZ2VUZXh0LnN1YmplY3QgfCBhc3luY319PC9sYWJlbD5cblx0XHRcdFx0XHQ8aW5wdXRcblx0XHRcdFx0XHRcdCpuZ0lmPVwicGFnZU9wdGlvbnMubGVuZ3RoID4gcGFnZVNlbGVjdFRocmVzaG9sZFwiXG5cdFx0XHRcdFx0XHRzdHlsZT1cInBhZGRpbmctcmlnaHQ6IDFyZW07IG1hcmdpbi1yaWdodDogMXJlbTtcIlxuXHRcdFx0XHRcdFx0W2lkXT1cImN1cnJlbnRQYWdlU2VsZWN0SWRcIlxuXHRcdFx0XHRcdFx0dHlwZT1cIm51bWJlclwiXG5cdFx0XHRcdFx0XHRtaW49XCIxXCJcblx0XHRcdFx0XHRcdFttYXhdPVwicGFnZU9wdGlvbnMubGVuZ3RoXCJcblx0XHRcdFx0XHRcdGNsYXNzPVwiY2RzLS1zZWxlY3QtaW5wdXRcIlxuXHRcdFx0XHRcdFx0WyhuZ01vZGVsKV09XCJjdXJyZW50UGFnZVwiPlxuXHRcdFx0XHRcdDxzZWxlY3Rcblx0XHRcdFx0XHRcdCpuZ0lmPVwicGFnZU9wdGlvbnMubGVuZ3RoIDw9IHBhZ2VTZWxlY3RUaHJlc2hvbGRcIlxuXHRcdFx0XHRcdFx0W2lkXT1cImN1cnJlbnRQYWdlU2VsZWN0SWRcIlxuXHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNlbGVjdC1pbnB1dFwiXG5cdFx0XHRcdFx0XHRbZGlzYWJsZWRdPVwicGFnZUlucHV0RGlzYWJsZWRcIlxuXHRcdFx0XHRcdFx0WyhuZ01vZGVsKV09XCJjdXJyZW50UGFnZVwiPlxuXHRcdFx0XHRcdFx0PG9wdGlvbiAqbmdGb3I9XCJsZXQgcGFnZSBvZiBwYWdlT3B0aW9uczsgbGV0IGkgPSBpbmRleDtcIiBjbGFzcz1cImNkcy0tc2VsZWN0LW9wdGlvblwiIFt2YWx1ZV09XCJpICsgMVwiPnt7aSArIDF9fTwvb3B0aW9uPlxuXHRcdFx0XHRcdDwvc2VsZWN0PlxuXHRcdFx0XHRcdDxzdmdcblx0XHRcdFx0XHRcdCpuZ0lmPVwicGFnZU9wdGlvbnMubGVuZ3RoIDw9IHBhZ2VTZWxlY3RUaHJlc2hvbGRcIlxuXHRcdFx0XHRcdFx0Y2RzSWNvbj1cImNoZXZyb24tLWRvd25cIlxuXHRcdFx0XHRcdFx0c2l6ZT1cIjE2XCJcblx0XHRcdFx0XHRcdHN0eWxlPVwiZGlzcGxheTogaW5oZXJpdDtcIlxuXHRcdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXNlbGVjdF9fYXJyb3dcIlxuXHRcdFx0XHRcdFx0W2F0dHIuYXJpYUxhYmVsXT1cIm9wdGlvbnNMaXN0VGV4dC5zdWJqZWN0IHwgYXN5bmNcIj5cblx0XHRcdFx0XHQ8L3N2Zz5cblx0XHRcdFx0PC9kaXY+XG5cdFx0XHQ8L25nLWNvbnRhaW5lcj5cblxuXHRcdFx0PHNwYW4gKm5nSWY9XCIhcGFnZXNVbmtub3duICYmIGxhc3RQYWdlIDw9IDFcIiBjbGFzcz1cImNkcy0tcGFnaW5hdGlvbl9fdGV4dFwiPlxuXHRcdFx0XHQ8bmctY29udGFpbmVyICpuZ0lmPVwiIXNob3dQYWdlSW5wdXRcIj57e2N1cnJlbnRQYWdlfX08L25nLWNvbnRhaW5lcj5cblx0XHRcdFx0e3tvZkxhc3RQYWdlVGV4dC5zdWJqZWN0IHwgaTE4blJlcGxhY2U6IHtsYXN0OiBsYXN0UGFnZX0gfCBhc3luY319XG5cdFx0XHQ8L3NwYW4+XG5cdFx0XHQ8c3BhbiAqbmdJZj1cIiFwYWdlc1Vua25vd24gJiYgbGFzdFBhZ2UgPiAxXCIgY2xhc3M9XCJjZHMtLXBhZ2luYXRpb25fX3RleHRcIj5cblx0XHRcdFx0PG5nLWNvbnRhaW5lciAqbmdJZj1cIiFzaG93UGFnZUlucHV0XCI+e3tjdXJyZW50UGFnZX19PC9uZy1jb250YWluZXI+XG5cdFx0XHRcdHt7b2ZMYXN0UGFnZXNUZXh0LnN1YmplY3QgfCBpMThuUmVwbGFjZToge2xhc3Q6IGxhc3RQYWdlfSB8IGFzeW5jfX1cblx0XHRcdDwvc3Bhbj5cblx0XHRcdDxkaXYgY2xhc3M9XCJjZHMtLXBhZ2luYXRpb25fX2NvbnRyb2wtYnV0dG9uc1wiPlxuXHRcdFx0XHQ8YnV0dG9uXG5cdFx0XHRcdFx0Y2RzQnV0dG9uPVwiZ2hvc3RcIlxuXHRcdFx0XHRcdGljb25Pbmx5PVwidHJ1ZVwiXG5cdFx0XHRcdFx0Y2xhc3M9XCJjZHMtLXBhZ2luYXRpb25fX2J1dHRvbiBjZHMtLXBhZ2luYXRpb25fX2J1dHRvbi0tYmFja3dhcmRcIlxuXHRcdFx0XHRcdFtuZ0NsYXNzXT1cIntcblx0XHRcdFx0XHRcdCdjZHMtLXBhZ2luYXRpb25fX2J1dHRvbi0tbm8taW5kZXgnOiBjdXJyZW50UGFnZSA8PSAxIHx8IGRpc2FibGVkXG5cdFx0XHRcdFx0fVwiXG5cdFx0XHRcdFx0dGFiaW5kZXg9XCIwXCJcblx0XHRcdFx0XHRbYXR0ci5hcmlhLWxhYmVsXT1cImJhY2t3YXJkVGV4dC5zdWJqZWN0IHwgYXN5bmNcIlxuXHRcdFx0XHRcdChjbGljayk9XCJzZWxlY3RQYWdlLmVtaXQocHJldmlvdXNQYWdlKVwiXG5cdFx0XHRcdFx0W2Rpc2FibGVkXT1cIihjdXJyZW50UGFnZSA8PSAxIHx8IGRpc2FibGVkID8gdHJ1ZSA6IG51bGwpXCI+XG5cdFx0XHRcdFx0PHN2ZyBjZHNJY29uPVwiY2FyZXQtLWxlZnRcIiBzaXplPVwiMTZcIiBjbGFzcz1cImNkcy0tYnRuX19pY29uXCI+PC9zdmc+XG5cdFx0XHRcdDwvYnV0dG9uPlxuXG5cdFx0XHRcdDxidXR0b25cblx0XHRcdFx0XHRjZHNCdXR0b249XCJnaG9zdFwiXG5cdFx0XHRcdFx0aWNvbk9ubHk9XCJ0cnVlXCJcblx0XHRcdFx0XHRjbGFzcz1cIlxuXHRcdFx0XHRcdFx0Y2RzLS1wYWdpbmF0aW9uX19idXR0b25cblx0XHRcdFx0XHRcdGNkcy0tcGFnaW5hdGlvbl9fYnV0dG9uLS1mb3J3YXJkXCJcblx0XHRcdFx0XHRbbmdDbGFzc109XCJ7XG5cdFx0XHRcdFx0XHQnY2RzLS1wYWdpbmF0aW9uX19idXR0b24tLW5vLWluZGV4JzogY3VycmVudFBhZ2UgPj0gbGFzdFBhZ2UgfHwgZGlzYWJsZWRcblx0XHRcdFx0XHR9XCJcblx0XHRcdFx0XHR0YWJpbmRleD1cIjBcIlxuXHRcdFx0XHRcdFthdHRyLmFyaWEtbGFiZWxdPVwiZm9yd2FyZFRleHQuc3ViamVjdCB8IGFzeW5jXCJcblx0XHRcdFx0XHQoY2xpY2spPVwic2VsZWN0UGFnZS5lbWl0KG5leHRQYWdlKVwiXG5cdFx0XHRcdFx0W2Rpc2FibGVkXT1cIihjdXJyZW50UGFnZSA+PSBsYXN0UGFnZSB8fCBkaXNhYmxlZCA/IHRydWUgOiBudWxsKVwiPlxuXHRcdFx0XHRcdDxzdmcgY2RzSWNvbj1cImNhcmV0LS1yaWdodFwiIHNpemU9XCIxNlwiIGNsYXNzPVwiY2RzLS1idG5fX2ljb25cIj48L3N2Zz5cblx0XHRcdFx0PC9idXR0b24+XG5cdFx0XHQ8L2Rpdj5cblx0XHQ8L2Rpdj5cblx0PC9kaXY+XG5cdGBcbn0pXG5leHBvcnQgY2xhc3MgUGFnaW5hdGlvbiB7XG5cdHN0YXRpYyBwYWdpbmF0aW9uQ291bnRlciA9IDA7XG5cblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgZm9yIGEgbG9hZGluZyBwYWdpbmF0aW9uIGNvbXBvbmVudC5cblx0ICovXG5cdEBJbnB1dCgpIHNrZWxldG9uID0gZmFsc2U7XG5cdC8qKlxuXHQgKiBgUGFnaW5hdGlvbk1vZGVsYCB3aXRoIHRoZSBpbmZvcm1hdGlvbiBhYm91dCBwYWdlcyB5b3UncmUgY29udHJvbGxpbmcuXG5cdCAqL1xuXHRASW5wdXQoKSBtb2RlbDogUGFnaW5hdGlvbk1vZGVsO1xuXHQvKipcbiBcdCAqIFNldCB0byBgdHJ1ZWAgdG8gZGlzYWJsZSB0aGUgYmFja3dhcmQvZm9yd2FyZCBidXR0b25zLlxuXHQgKi9cblx0QElucHV0KCkgZGlzYWJsZWQgPSBmYWxzZTtcblx0LyoqXG5cdCAqIFNldCB0byBgdHJ1ZWAgdG8gZGlzYWJsZSB0aGUgc2VsZWN0IGJveCB0aGF0IGNoYW5nZXMgdGhlIHBhZ2UuXG5cdCAqL1xuXHRASW5wdXQoKSBwYWdlSW5wdXREaXNhYmxlZCA9IGZhbHNlO1xuXHQvKipcblx0ICogQ29udHJvbHMgd2V0aGVyIG9yIG5vdCB0byBzaG93IHRoZSBwYWdlIHNlbGVjdHNcblx0ICovXG5cdEBJbnB1dCgpIHNob3dQYWdlSW5wdXQgPSB0cnVlO1xuXHQvKipcblx0ICogU2V0IHRvIGB0cnVlYCBpZiB0aGUgdG90YWwgbnVtYmVyIG9mIGl0ZW1zIGlzIHVua25vd24uXG5cdCAqL1xuXHRASW5wdXQoKSBwYWdlc1Vua25vd24gPSBmYWxzZTtcblx0QElucHV0KCkgcGFnZVNlbGVjdFRocmVzaG9sZCA9IDEwMDA7XG5cblx0LyoqXG5cdCAqIEV4cGVjdHMgYW4gb2JqZWN0IHRoYXQgY29udGFpbnMgc29tZSBvciBhbGwgb2Y6XG5cdCAqIGBgYFxuXHQgKiB7XG5cdCAqXHRcdFwiSVRFTVNfUEVSX1BBR0VcIjogXCJJdGVtcyBwZXIgcGFnZTpcIixcblx0ICpcdFx0XCJPUEVOX0xJU1RfT0ZfT1BUSU9OU1wiOiBcIk9wZW4gbGlzdCBvZiBvcHRpb25zXCIsXG5cdCAqXHRcdFwiQkFDS1dBUkRcIjogXCJCYWNrd2FyZFwiLFxuXHQgKlx0XHRcIkZPUldBUkRcIjogXCJGb3J3YXJkXCIsXG5cdCAqXHRcdFwiVE9UQUxfSVRFTVNfVU5LTk9XTlwiOiBcInt7c3RhcnR9fS17e2VuZH19IGl0ZW1zXCIsXG5cdCAqXHRcdFwiVE9UQUxfSVRFTVNcIjogXCJ7e3N0YXJ0fX0te3tlbmR9fSBvZiB7e3RvdGFsfX0gaXRlbXNcIixcblx0ICpcdFx0XCJUT1RBTF9JVEVNXCI6IFwie3tzdGFydH19LXt7ZW5kfX0gb2Yge3t0b3RhbH19IGl0ZW1cIixcblx0ICpcdFx0XCJPRl9MQVNUX1BBR0VTXCI6IFwib2Yge3tsYXN0fX0gcGFnZXNcIixcblx0ICpcdFx0XCJPRl9MQVNUX1BBR0VcIjogXCJvZiB7e2xhc3R9fSBwYWdlXCJcblx0ICogfVxuXHQgKiBgYGBcblx0ICovXG5cdEBJbnB1dCgpXG5cdHNldCB0cmFuc2xhdGlvbnMgKHZhbHVlOiBQYWdpbmF0aW9uVHJhbnNsYXRpb25zKSB7XG5cdFx0Y29uc3QgdmFsdWVXaXRoRGVmYXVsdHMgPSBtZXJnZSh0aGlzLmkxOG4uZ2V0TXVsdGlwbGUoXCJQQUdJTkFUSU9OXCIpLCB2YWx1ZSk7XG5cdFx0dGhpcy5pdGVtc1BlclBhZ2VUZXh0Lm92ZXJyaWRlKHZhbHVlV2l0aERlZmF1bHRzLklURU1TX1BFUl9QQUdFKTtcblx0XHR0aGlzLm9wdGlvbnNMaXN0VGV4dC5vdmVycmlkZSh2YWx1ZVdpdGhEZWZhdWx0cy5PUEVOX0xJU1RfT0ZfT1BUSU9OUyk7XG5cdFx0dGhpcy5iYWNrd2FyZFRleHQub3ZlcnJpZGUodmFsdWVXaXRoRGVmYXVsdHMuQkFDS1dBUkQpO1xuXHRcdHRoaXMuZm9yd2FyZFRleHQub3ZlcnJpZGUodmFsdWVXaXRoRGVmYXVsdHMuRk9SV0FSRCk7XG5cdFx0dGhpcy50b3RhbEl0ZW1zVGV4dC5vdmVycmlkZSh2YWx1ZVdpdGhEZWZhdWx0cy5UT1RBTF9JVEVNUyk7XG5cdFx0dGhpcy50b3RhbEl0ZW1UZXh0Lm92ZXJyaWRlKHZhbHVlV2l0aERlZmF1bHRzLlRPVEFMX0lURU0pO1xuXHRcdHRoaXMudG90YWxJdGVtc1Vua25vd25UZXh0Lm92ZXJyaWRlKHZhbHVlV2l0aERlZmF1bHRzLlRPVEFMX0lURU1TX1VOS05PV04pO1xuXHRcdHRoaXMucGFnZVRleHQub3ZlcnJpZGUodmFsdWVXaXRoRGVmYXVsdHMuUEFHRSk7XG5cdFx0dGhpcy5vZkxhc3RQYWdlc1RleHQub3ZlcnJpZGUodmFsdWVXaXRoRGVmYXVsdHMuT0ZfTEFTVF9QQUdFUyk7XG5cdFx0dGhpcy5vZkxhc3RQYWdlVGV4dC5vdmVycmlkZSh2YWx1ZVdpdGhEZWZhdWx0cy5PRl9MQVNUX1BBR0UpO1xuXHR9XG5cblx0LyoqXG5cdCAqIE9wdGlvbnMgZm9yIGl0ZW1zIHBlciBwYWdlIHNlbGVjdFxuXHQgKlxuXHQgKiBBIGRlZmF1bHQgYXJyYXkgb2Ygb3B0aW9ucyB3aWxsIGJlIGRlZmluZWQ6IFsxMCwgMjAsIDMwLCA0MCwgNTBdXG5cdCAqL1xuXHRASW5wdXQoKSBpdGVtc1BlclBhZ2VPcHRpb25zOiBudW1iZXJbXSA9IFsxMCwgMjAsIDMwLCA0MCwgNTBdO1xuXG5cdC8qKlxuXHQgKiBFbWl0cyB0aGUgbmV3IHBhZ2UgbnVtYmVyLlxuXHQgKlxuXHQgKiBZb3Ugc2hvdWxkIHRpZSBpbnRvIHRoaXMgYW5kIHVwZGF0ZSBgbW9kZWwuY3VycmVudFBhZ2VgIG9uY2UgdGhlIGZyZXNoXG5cdCAqIGRhdGEgaXMgZmluYWxseSBsb2FkZWQuXG5cdCAqL1xuXHRAT3V0cHV0KCkgc2VsZWN0UGFnZSA9IG5ldyBFdmVudEVtaXR0ZXI8bnVtYmVyPigpO1xuXG5cdGdldCBpdGVtc1BlclBhZ2UoKSB7XG5cdFx0cmV0dXJuIHRoaXMubW9kZWwucGFnZUxlbmd0aDtcblx0fVxuXHRzZXQgaXRlbXNQZXJQYWdlKHZhbHVlKSB7XG5cdFx0dGhpcy5tb2RlbC5wYWdlTGVuZ3RoID0gTnVtYmVyKHZhbHVlKTtcblx0XHR0aGlzLmN1cnJlbnRQYWdlID0gMTsgLy8gcmVzZXQgcGFnZVxuXHR9XG5cblx0Z2V0IGN1cnJlbnRQYWdlKCkge1xuXHRcdHJldHVybiB0aGlzLm1vZGVsLmN1cnJlbnRQYWdlO1xuXHR9XG5cdHNldCBjdXJyZW50UGFnZSh2YWx1ZSkge1xuXHRcdHZhbHVlID0gTnVtYmVyKHZhbHVlKTtcblx0XHQvLyBlbWl0cyB0aGUgdmFsdWUgdG8gYWxsb3cgdGhlIHVzZXIgdG8gdXBkYXRlIGN1cnJlbnQgcGFnZVxuXHRcdC8vIGluIHRoZSBtb2RlbCBvbmNlIHRoZSBwYWdlIGlzIGxvYWRlZFxuXHRcdHRoaXMuc2VsZWN0UGFnZS5lbWl0KHZhbHVlKTtcblx0fVxuXG5cdGdldCB0b3RhbERhdGFMZW5ndGgoKSB7XG5cdFx0cmV0dXJuIHRoaXMubW9kZWwudG90YWxEYXRhTGVuZ3RoO1xuXHR9XG5cdC8qKlxuXHQgKiBUaGUgbGFzdCBwYWdlIG51bWJlciB0byBkaXNwbGF5IGluIHRoZSBwYWdpbmF0aW9uIHZpZXcuXG5cdCAqL1xuXHRnZXQgbGFzdFBhZ2UoKTogbnVtYmVyIHtcblx0XHRjb25zdCBsYXN0ID0gTWF0aC5jZWlsKHRoaXMudG90YWxEYXRhTGVuZ3RoIC8gdGhpcy5pdGVtc1BlclBhZ2UpO1xuXHRcdHJldHVybiBsYXN0ID4gMCA/IGxhc3QgOiAxO1xuXHR9XG5cblx0Z2V0IHN0YXJ0SXRlbUluZGV4KCkge1xuXHRcdHJldHVybiB0aGlzLmVuZEl0ZW1JbmRleCA+IDAgPyAodGhpcy5jdXJyZW50UGFnZSAtIDEpICogdGhpcy5pdGVtc1BlclBhZ2UgKyAxIDogMDtcblx0fVxuXG5cdGdldCBlbmRJdGVtSW5kZXgoKSB7XG5cdFx0Y29uc3QgcHJvamVjdGVkRW5kSXRlbUluZGV4ID0gdGhpcy5jdXJyZW50UGFnZSAqIHRoaXMuaXRlbXNQZXJQYWdlO1xuXG5cdFx0cmV0dXJuIHByb2plY3RlZEVuZEl0ZW1JbmRleCA8IHRoaXMudG90YWxEYXRhTGVuZ3RoID8gcHJvamVjdGVkRW5kSXRlbUluZGV4IDogdGhpcy50b3RhbERhdGFMZW5ndGg7XG5cdH1cblxuXHQvKipcblx0ICogVGhlIHByZXZpb3VzIHBhZ2UgbnVtYmVyIHRvIG5hdmlnYXRlIHRvLCBmcm9tIHRoZSBjdXJyZW50IHBhZ2UuXG5cdCAqL1xuXHRnZXQgcHJldmlvdXNQYWdlKCk6IG51bWJlciB7XG5cdFx0cmV0dXJuIHRoaXMuY3VycmVudFBhZ2UgPD0gMSA/IDEgOiB0aGlzLmN1cnJlbnRQYWdlIC0gMTtcblx0fVxuXG5cdC8qKlxuXHQgKiBUaGUgbmV4dCBwYWdlIG51bWJlciB0byBuYXZpZ2F0ZSB0bywgZnJvbSB0aGUgY3VycmVudCBwYWdlLlxuXHQgKi9cblx0Z2V0IG5leHRQYWdlKCk6IG51bWJlciB7XG5cdFx0Y29uc3QgbGFzdFBhZ2UgPSB0aGlzLmxhc3RQYWdlO1xuXHRcdHJldHVybiB0aGlzLmN1cnJlbnRQYWdlID49IGxhc3RQYWdlID8gbGFzdFBhZ2UgOiB0aGlzLmN1cnJlbnRQYWdlICsgMTtcblx0fVxuXG5cdGdldCBwYWdlT3B0aW9ucygpIHtcblx0XHQvKipcblx0XHQgKiBDYWxjdWxhdGUgbnVtYmVyIG9mIHBhZ2VzIGJhc2VkIG9uIHRvdGFsRGF0YUxlbmd0aCBhbmQgaXRlbXNQZXJQYWdlLlxuXHRcdCAqIEV2ZW4gaWYgdG90YWxEYXRhTGVuZ3RoIGlzIDAsIG51bWJlck9mUGFnZXMgc2hvdWxkIGJlIGFsd2F5cyBhdCBsZWFzdCAxLlxuXHRcdCAqIE5ldyBhcnJheSB3aWxsIGJlIGNvbnN0cnVjdGVkIG9ubHkgaWYgbnVtYmVyIG9mIHBhZ2VzIGNoYW5nZXMuXG5cdFx0ICovXG5cdFx0Y29uc3QgbnVtYmVyT2ZQYWdlcyA9IE1hdGgubWF4KE1hdGguY2VpbCh0aGlzLnRvdGFsRGF0YUxlbmd0aCAvIHRoaXMuaXRlbXNQZXJQYWdlKSwgMSk7XG5cdFx0aWYgKHRoaXMuX3BhZ2VPcHRpb25zLmxlbmd0aCAhPT0gbnVtYmVyT2ZQYWdlcykge1xuXHRcdFx0dGhpcy5fcGFnZU9wdGlvbnMgPSBBcnJheShudW1iZXJPZlBhZ2VzKTtcblx0XHR9XG5cdFx0cmV0dXJuIHRoaXMuX3BhZ2VPcHRpb25zO1xuXHR9XG5cblx0aXRlbXNQZXJQYWdlU2VsZWN0SWQgPSBgcGFnaW5hdGlvbi1zZWxlY3QtaXRlbXMtcGVyLXBhZ2UtJHtQYWdpbmF0aW9uLnBhZ2luYXRpb25Db3VudGVyfWA7XG5cdGN1cnJlbnRQYWdlU2VsZWN0SWQgPSBgcGFnaW5hdGlvbi1zZWxlY3QtY3VycmVudC1wYWdlLSR7UGFnaW5hdGlvbi5wYWdpbmF0aW9uQ291bnRlcn1gO1xuXG5cdGl0ZW1zUGVyUGFnZVRleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLklURU1TX1BFUl9QQUdFXCIpO1xuXHRvcHRpb25zTGlzdFRleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLk9QRU5fTElTVF9PRl9PUFRJT05TXCIpO1xuXHRiYWNrd2FyZFRleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLkJBQ0tXQVJEXCIpO1xuXHRmb3J3YXJkVGV4dCA9IHRoaXMuaTE4bi5nZXRPdmVycmlkYWJsZShcIlBBR0lOQVRJT04uRk9SV0FSRFwiKTtcblx0dG90YWxJdGVtc1RleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLlRPVEFMX0lURU1TXCIpO1xuXHR0b3RhbEl0ZW1UZXh0ID0gdGhpcy5pMThuLmdldE92ZXJyaWRhYmxlKFwiUEFHSU5BVElPTi5UT1RBTF9JVEVNXCIpO1xuXHR0b3RhbEl0ZW1zVW5rbm93blRleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLlRPVEFMX0lURU1TX1VOS05PV05cIik7XG5cdHBhZ2VUZXh0ID0gdGhpcy5pMThuLmdldE92ZXJyaWRhYmxlKFwiUEFHSU5BVElPTi5QQUdFXCIpO1xuXHRvZkxhc3RQYWdlc1RleHQgPSB0aGlzLmkxOG4uZ2V0T3ZlcnJpZGFibGUoXCJQQUdJTkFUSU9OLk9GX0xBU1RfUEFHRVNcIik7XG5cdG9mTGFzdFBhZ2VUZXh0ID0gdGhpcy5pMThuLmdldE92ZXJyaWRhYmxlKFwiUEFHSU5BVElPTi5PRl9MQVNUX1BBR0VcIik7XG5cblx0cHJvdGVjdGVkIF9wYWdlT3B0aW9ucyA9IFtdO1xuXG5cdGNvbnN0cnVjdG9yKHByb3RlY3RlZCBpMThuOiBJMThuLCBwcm90ZWN0ZWQgZXhwZXJpbWVudGFsOiBFeHBlcmltZW50YWxTZXJ2aWNlKSB7XG5cdFx0UGFnaW5hdGlvbi5wYWdpbmF0aW9uQ291bnRlcisrO1xuXHR9XG59XG4iXX0=