@edugouvfr/ngx-dsfr
Version:
NgxDsfr est un portage Angular des éléments d'interface du Système de Design de l'État Français (DSFR).
160 lines • 29.2 kB
JavaScript
import { CommonModule } from '@angular/common';
import { Component, ContentChildren, EventEmitter, Input, Optional, Output, ViewEncapsulation, } from '@angular/core';
import { RouterModule } from '@angular/router';
import { newUniqueId } from '../../shared';
import { DsfrTabComponent } from './tab.component';
import { BUTTON_TAB_ID_PREFIX } from './tabs.model';
import * as i0 from "@angular/core";
import * as i1 from "@angular/router";
export class DsfrTabsComponent {
constructor(router, activatedRoute, elRef) {
this.router = router;
this.activatedRoute = activatedRoute;
this.elRef = elRef;
/**
* Attribut aria-label de la liste d'onglets.
*/
this.tabsAriaLabel = "Système d'onglets";
/**
* Onglet sélectionné (commence à 0).
* Ne peut pas être utilisé quand on utilise l'input `routes`.
*/
this.selectedTabIndex = 0;
/**
* Permet de positionner la largeur du viewport à 100% en mobile.
*/
this.fullViewport = false;
/**
* Émis lors de la sélection d'un onglet. La valeur émise correspond au `tabId` de l'onglet sélectionné.
*/
this.tabSelect = new EventEmitter();
this.routerTabId = newUniqueId();
}
get headers() {
return this.routes || this.tabComponents?.toArray();
}
get tabsCount() {
return this.routes ? this.routes.length : this.tabComponents ? this.tabComponents.length : 0;
}
/** @internal */
getHeaderValue(header, key) {
return header[key];
}
/** @internal */
asRoute(header) {
return header;
}
/** @internal */
getIconClasses(tabHeader) {
return !tabHeader.icon ? '' : `${tabHeader.icon} fr-tabs__tab--icon-left`;
}
/**
* [mode routes] Retourne l'identifiant unique du bouton de contrôle de l'onglet passé en paramètre.
*
* @internal
*/
getButtonTabId(header) {
return BUTTON_TAB_ID_PREFIX + header.tabId;
}
/**
* [mode routes] Retourne l'identifiant unique du bouton de contrôle de l'onglet courant.
*
* @internal
*/
getCurrentButtonTabId() {
return this.selectedTabIndex >= 0 ? BUTTON_TAB_ID_PREFIX + this.routes[this.selectedTabIndex].tabId : null;
}
/** @internal */
onSelect(event, index) {
event.preventDefault();
event.stopPropagation();
this.selectedTabIndex = index;
this.tabSelect.emit(this.headers[this.selectedTabIndex].tabId);
}
/**
* Gestion de la navigation au clavier sans routes : emission de tabSelect
* @internal
*/
onRoutlessTabKeydown(event, index) {
const newIndex = this.calculateNewIndex(event, index);
if (newIndex !== -1) {
this.tabSelect.emit(this.headers[newIndex].tabId);
}
}
/** @internal */
onRouterLinkActive(isActive, tabIndex) {
if (isActive) {
this.selectedTabIndex = tabIndex;
}
}
/**
* Gestion de la navigation au clavier en mode routes.
*
* @internal
*/
onButtonTabKeydown(event, index) {
// just in case
if (this.selectedTabIndex !== index)
return;
const newIndex = this.calculateNewIndex(event, index);
if (newIndex > -1) {
this.performNavigation(newIndex);
}
}
isFirstTab(index) {
return index === 0;
}
isLastTab(index) {
return index === this.tabsCount - 1;
}
performNavigation(newIndex) {
const route = this.routes[newIndex];
const extras = route.routerLinkExtras || { relativeTo: this.activatedRoute };
this.router.navigate([route.path], extras);
this.elRef.nativeElement.querySelector('#' + this.getButtonTabId(route))?.focus();
}
calculateNewIndex(event, index) {
let newIndex = -1;
switch (event.code) {
case 'ArrowLeft':
newIndex = this.isFirstTab(index) ? this.tabsCount - 1 : index - 1;
break;
case 'ArrowRight':
newIndex = this.isLastTab(index) ? 0 : index + 1;
break;
case 'Home':
newIndex = 0;
break;
case 'End':
newIndex = this.tabsCount - 1;
break;
default:
break;
}
return newIndex;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrTabsComponent, deps: [{ token: i1.Router, optional: true }, { token: i1.ActivatedRoute, optional: true }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DsfrTabsComponent, isStandalone: true, selector: "dsfr-tabs", inputs: { tabsAriaLabel: "tabsAriaLabel", selectedTabIndex: "selectedTabIndex", fullViewport: "fullViewport", routes: "routes" }, outputs: { tabSelect: "tabSelect" }, queries: [{ propertyName: "tabComponents", predicate: DsfrTabComponent }], ngImport: i0, template: "<div class=\"fr-tabs\" [class.fr-tabs--viewport-width]=\"fullViewport\">\n <ul class=\"fr-tabs__list\" role=\"tablist\" [attr.aria-label]=\"tabsAriaLabel\">\n @for (header of headers; track header; let index = $index) {\n <li role=\"presentation\">\n @if (!routes) {\n <button\n [attr.id]=\"getHeaderValue(header, 'buttonId') || null\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"index === selectedTabIndex ? 0 : -1\"\n [attr.aria-controls]=\"header?.tabId || null\"\n [attr.aria-selected]=\"index === selectedTabIndex\"\n [disabled]=\"header?.disabled || null\"\n (keydown)=\"onRoutlessTabKeydown($event, index)\"\n (click)=\"onSelect($event, index)\">\n {{ header.label }}\n </button>\n } @else {\n <button\n [attr.id]=\"getButtonTabId(header)\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"rla.isActive ? 0 : -1\"\n [attr.aria-controls]=\"rla.isActive ? routerTabId : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [disabled]=\"header?.disabled || null\"\n [routerLink]=\"asRoute(header).path || null\"\n routerLinkActive\n [routerLinkActiveOptions]=\"asRoute(header).routerLinkActiveOptions ?? { exact: false }\"\n [queryParams]=\"asRoute(header).routerLinkExtras?.queryParams\"\n [fragment]=\"asRoute(header).routerLinkExtras?.fragment\"\n [queryParamsHandling]=\"asRoute(header).routerLinkExtras?.queryParamsHandling\"\n [preserveFragment]=\"asRoute(header).routerLinkExtras?.preserveFragment\"\n [skipLocationChange]=\"asRoute(header).routerLinkExtras?.skipLocationChange\"\n [replaceUrl]=\"asRoute(header).routerLinkExtras?.replaceUrl\"\n [state]=\"asRoute(header).routerLinkExtras?.state\"\n (isActiveChange)=\"onRouterLinkActive($event, index)\"\n #rla=\"routerLinkActive\"\n (click)=\"onSelect($event, index)\"\n (keydown)=\"onButtonTabKeydown($event, index)\">\n {{ header.label }}\n </button>\n }\n </li>\n }\n </ul>\n @if (!routes) {\n <ng-content></ng-content>\n } @else {\n <div\n [attr.id]=\"routerTabId\"\n role=\"tabpanel\"\n class=\"fr-tabs__panel fr-tabs__panel--selected\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"getCurrentButtonTabId()\">\n <router-outlet></router-outlet>\n </div>\n @for (header of headers; track header) {\n <div [attr.aria-labelledby]=\"getCurrentButtonTabId()\"></div>\n }\n }\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i1.RouterOutlet, selector: "router-outlet", inputs: ["name"], outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }, { kind: "directive", type: i1.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i1.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrTabsComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-tabs', encapsulation: ViewEncapsulation.None, standalone: true, imports: [CommonModule, RouterModule], template: "<div class=\"fr-tabs\" [class.fr-tabs--viewport-width]=\"fullViewport\">\n <ul class=\"fr-tabs__list\" role=\"tablist\" [attr.aria-label]=\"tabsAriaLabel\">\n @for (header of headers; track header; let index = $index) {\n <li role=\"presentation\">\n @if (!routes) {\n <button\n [attr.id]=\"getHeaderValue(header, 'buttonId') || null\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"index === selectedTabIndex ? 0 : -1\"\n [attr.aria-controls]=\"header?.tabId || null\"\n [attr.aria-selected]=\"index === selectedTabIndex\"\n [disabled]=\"header?.disabled || null\"\n (keydown)=\"onRoutlessTabKeydown($event, index)\"\n (click)=\"onSelect($event, index)\">\n {{ header.label }}\n </button>\n } @else {\n <button\n [attr.id]=\"getButtonTabId(header)\"\n type=\"button\"\n role=\"tab\"\n class=\"fr-tabs__tab\"\n [class]=\"getIconClasses(header) || null\"\n [attr.tabindex]=\"rla.isActive ? 0 : -1\"\n [attr.aria-controls]=\"rla.isActive ? routerTabId : null\"\n [attr.aria-selected]=\"rla.isActive\"\n [disabled]=\"header?.disabled || null\"\n [routerLink]=\"asRoute(header).path || null\"\n routerLinkActive\n [routerLinkActiveOptions]=\"asRoute(header).routerLinkActiveOptions ?? { exact: false }\"\n [queryParams]=\"asRoute(header).routerLinkExtras?.queryParams\"\n [fragment]=\"asRoute(header).routerLinkExtras?.fragment\"\n [queryParamsHandling]=\"asRoute(header).routerLinkExtras?.queryParamsHandling\"\n [preserveFragment]=\"asRoute(header).routerLinkExtras?.preserveFragment\"\n [skipLocationChange]=\"asRoute(header).routerLinkExtras?.skipLocationChange\"\n [replaceUrl]=\"asRoute(header).routerLinkExtras?.replaceUrl\"\n [state]=\"asRoute(header).routerLinkExtras?.state\"\n (isActiveChange)=\"onRouterLinkActive($event, index)\"\n #rla=\"routerLinkActive\"\n (click)=\"onSelect($event, index)\"\n (keydown)=\"onButtonTabKeydown($event, index)\">\n {{ header.label }}\n </button>\n }\n </li>\n }\n </ul>\n @if (!routes) {\n <ng-content></ng-content>\n } @else {\n <div\n [attr.id]=\"routerTabId\"\n role=\"tabpanel\"\n class=\"fr-tabs__panel fr-tabs__panel--selected\"\n tabindex=\"0\"\n [attr.aria-labelledby]=\"getCurrentButtonTabId()\">\n <router-outlet></router-outlet>\n </div>\n @for (header of headers; track header) {\n <div [attr.aria-labelledby]=\"getCurrentButtonTabId()\"></div>\n }\n }\n</div>\n" }]
}], ctorParameters: () => [{ type: i1.Router, decorators: [{
type: Optional
}] }, { type: i1.ActivatedRoute, decorators: [{
type: Optional
}] }, { type: i0.ElementRef }], propDecorators: { tabsAriaLabel: [{
type: Input
}], selectedTabIndex: [{
type: Input
}], fullViewport: [{
type: Input
}], routes: [{
type: Input
}], tabSelect: [{
type: Output
}], tabComponents: [{
type: ContentChildren,
args: [DsfrTabComponent]
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidGFicy5jb21wb25lbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi8uLi8uLi9wcm9qZWN0cy9uZ3gtZHNmci1jb21wb25lbnRzL3NyYy9saWIvY29tcG9uZW50cy90YWJzL3RhYnMuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWRzZnItY29tcG9uZW50cy9zcmMvbGliL2NvbXBvbmVudHMvdGFicy90YWJzLmNvbXBvbmVudC5odG1sIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUMvQyxPQUFPLEVBQ0wsU0FBUyxFQUNULGVBQWUsRUFFZixZQUFZLEVBQ1osS0FBSyxFQUNMLFFBQVEsRUFDUixNQUFNLEVBRU4saUJBQWlCLEdBQ2xCLE1BQU0sZUFBZSxDQUFDO0FBQ3ZCLE9BQU8sRUFBNEMsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDekYsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUMzQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQztBQUNuRCxPQUFPLEVBQUUsb0JBQW9CLEVBQThCLE1BQU0sY0FBYyxDQUFDOzs7QUFTaEYsTUFBTSxPQUFPLGlCQUFpQjtJQW1DNUIsWUFDc0IsTUFBYyxFQUNkLGNBQThCLEVBQzFDLEtBQWlCO1FBRkwsV0FBTSxHQUFOLE1BQU0sQ0FBUTtRQUNkLG1CQUFjLEdBQWQsY0FBYyxDQUFnQjtRQUMxQyxVQUFLLEdBQUwsS0FBSyxDQUFZO1FBckMzQjs7V0FFRztRQUNNLGtCQUFhLEdBQUcsbUJBQW1CLENBQUM7UUFFN0M7OztXQUdHO1FBQ00scUJBQWdCLEdBQUcsQ0FBQyxDQUFDO1FBRTlCOztXQUVHO1FBQ00saUJBQVksR0FBRyxLQUFLLENBQUM7UUFTOUI7O1dBRUc7UUFDTyxjQUFTLEdBQUcsSUFBSSxZQUFZLEVBQVUs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QUFDO2dCQUNqRCxNQUFNO1lBRVIsS0FBSyxNQUFNO2dCQUNULFFBQVEsR0FBRyxDQUFDLENBQUM7Z0JBQ2IsTUFBTTtZQUVSLEtBQUssS0FBSztnQkFDUixRQUFRLEdBQUcsSUFBSSxDQUFDLFNBQVMsR0FBRyxDQUFDLENBQUM7Z0JBQzlCLE1BQU07WUFFUjtnQkFDRSxNQUFNO1FBQ1YsQ0FBQztRQUVELE9BQU8sUUFBUSxDQUFDO0lBQ2xCLENBQUM7K0dBdktVLGlCQUFpQjttR0FBakIsaUJBQWlCLDBRQThCWCxnQkFBZ0IsNkJDdERuQyx1MkZBa0VBLDJDRDVDWSxZQUFZLDhCQUFFLFlBQVk7OzRGQUV6QixpQkFBaUI7a0JBUDdCLFNBQVM7K0JBQ0UsV0FBVyxpQkFFTixpQkFBaUIsQ0FBQyxJQUFJLGNBQ3pCLElBQUksV0FDUCxDQUFDLFlBQVksRUFBRSxZQUFZLENBQUM7OzBCQXNDbEMsUUFBUTs7MEJBQ1IsUUFBUTtrRUFqQ0YsYUFBYTtzQkFBckIsS0FBSztnQkFNRyxnQkFBZ0I7c0JBQXhCLEtBQUs7Z0JBS0csWUFBWTtzQkFBcEIsS0FBSztnQkFPRyxNQUFNO3NCQUFkLEtBQUs7Z0JBS0ksU0FBUztzQkFBbEIsTUFBTTtnQkFHNEIsYUFBYTtzQkFBL0MsZUFBZTt1QkFBQyxnQkFBZ0IiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBDb21tb25Nb2R1bGUgfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHtcbiAgQ29tcG9uZW50LFxuICBDb250ZW50Q2hpbGRyZW4sXG4gIEVsZW1lbnRSZWYsXG4gIEV2ZW50RW1pdHRlcixcbiAgSW5wdXQsXG4gIE9wdGlvbmFsLFxuICBPdXRwdXQsXG4gIFF1ZXJ5TGlzdCxcbiAgVmlld0VuY2Fwc3VsYXRpb24sXG59IGZyb20gJ0Bhbmd1bGFyL2NvcmUnO1xuaW1wb3J0IHsgQWN0aXZhdGVkUm91dGUsIE5hdmlnYXRpb25FeHRyYXMsIFJvdXRlciwgUm91dGVyTW9kdWxlIH0gZnJvbSAnQGFuZ3VsYXIvcm91dGVyJztcbmltcG9ydCB7IG5ld1VuaXF1ZUlkIH0gZnJvbSAnLi4vLi4vc2hhcmVkJztcbmltcG9ydCB7IERzZnJUYWJDb21wb25lbnQgfSBmcm9tICcuL3RhYi5jb21wb25lbnQnO1xuaW1wb3J0IHsgQlVUVE9OX1RBQl9JRF9QUkVGSVgsIERzZnJUYWJSb3V0ZSwgRWR1VGFiSGVhZGVyIH0gZnJvbSAnLi90YWJzLm1vZGVsJztcblxuQENvbXBvbmVudCh7XG4gIHNlbGVjdG9yOiAnZHNmci10YWJzJyxcbiAgdGVtcGxhdGVVcmw6ICcuL3RhYnMuY29tcG9uZW50Lmh0bWwnLFxuICBlbmNhcHN1bGF0aW9uOiBWaWV3RW5jYXBzdWxhdGlvbi5Ob25lLFxuICBzdGFuZGFsb25lOiB0cnVlLFxuICBpbXBvcnRzOiBbQ29tbW9uTW9kdWxlLCBSb3V0ZXJNb2R1bGVdLFxufSlcbmV4cG9ydCBjbGFzcyBEc2ZyVGFic0NvbXBvbmVudCB7XG4gIC8qKlxuICAgKiBBdHRyaWJ1dCBhcmlhLWxhYmVsIGRlIGxhIGxpc3RlIGQnb25nbGV0cy5cbiAgICovXG4gIEBJbnB1dCgpIHRhYnNBcmlhTGFiZWwgPSBcIlN5c3TDqG1lIGQnb25nbGV0c1wiO1xuXG4gIC8qKlxuICAgKiBPbmdsZXQgc8OpbGVjdGlvbm7DqSAoY29tbWVuY2Ugw6AgMCkuXG4gICAqIE5lIHBldXQgcGFzIMOqdHJlIHV0aWxpc8OpIHF1YW5kIG9uIHV0aWxpc2UgbCdpbnB1dCBgcm91dGVzYC5cbiAgICovXG4gIEBJbnB1dCgpIHNlbGVjdGVkVGFiSW5kZXggPSAwO1xuXG4gIC8qKlxuICAgKiBQZXJtZXQgZGUgcG9zaXRpb25uZXIgbGEgbGFyZ2V1ciBkdSB2aWV3cG9ydCDDoCAxMDAlIGVuIG1vYmlsZS5cbiAgICovXG4gIEBJbnB1dCgpIGZ1bGxWaWV3cG9ydCA9IGZhbHNlO1xuXG4gIC8qKlxuICAgKiBEw6lmaW5pdGlvbiBkZXMgcm91dGVzIHNpIGwnb24gc291aGFpdGUgdXRpbGlzZXIgYDxyb3V0ZXItb3V0bGV0PmAuXG4gICAqXG4gICAqIEBzaW5jZSAxLjkuMFxuICAgKi9cbiAgQElucHV0KCkgcm91dGVzITogRHNmclRhYlJvdXRlW107XG5cbiAgLyoqXG4gICAqIMOJbWlzIGxvcnMgZGUgbGEgc8OpbGVjdGlvbiBkJ3VuIG9uZ2xldC4gTGEgdmFsZXVyIMOpbWlzZSBjb3JyZXNwb25kIGF1IGB0YWJJZGAgZGUgbCdvbmdsZXQgc8OpbGVjdGlvbm7DqS5cbiAgICovXG4gIEBPdXRwdXQoKSB0YWJTZWxlY3QgPSBuZXcgRXZlbnRFbWl0dGVyPHN0cmluZz4oKTtcblxuICAvKiogQGludGVybmFsICovXG4gIEBDb250ZW50Q2hpbGRyZW4oRHNmclRhYkNvbXBvbmVudCkgdGFiQ29tcG9uZW50cyE6IFF1ZXJ5TGlzdDxEc2ZyVGFiQ29tcG9uZW50PjtcblxuICAvKiogQGludGVybmFsICovXG4gIHJvdXRlclRhYklkOiBzdHJpbmc7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgQE9wdGlvbmFsKCkgcHJpdmF0ZSByb3V0ZXI6IFJvdXRlcixcbiAgICBAT3B0aW9uYWwoKSBwcml2YXRlIGFjdGl2YXRlZFJvdXRlOiBBY3RpdmF0ZWRSb3V0ZSxcbiAgICBwcml2YXRlIGVsUmVmOiBFbGVtZW50UmVmLFxuICApIHtcbiAgICB0aGlzLnJvdXRlclRhYklkID0gbmV3VW5pcXVlSWQoKTtcbiAgfVxuXG4gIGdldCBoZWFkZXJzKCk6IEVkdVRhYkhlYWRlcltdIHtcbiAgICByZXR1cm4gdGhpcy5yb3V0ZXMgfHwgdGhpcy50YWJDb21wb25lbnRzPy50b0FycmF5KCk7XG4gIH1cblxuICBwcml2YXRlIGdldCB0YWJzQ291bnQoKTogbnVtYmVyIHtcbiAgICByZXR1cm4gdGhpcy5yb3V0ZXMgPyB0aGlzLnJvdXRlcy5sZW5ndGggOiB0aGlzLnRhYkNvbXBvbmVudHMgPyB0aGlzLnRhYkNvbXBvbmVudHMubGVuZ3RoIDogMDtcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgZ2V0SGVhZGVyVmFsdWUoaGVhZGVyOiBFZHVUYWJIZWFkZXIsIGtleTogc3RyaW5nKTogYW55IHtcbiAgICByZXR1cm4gaGVhZGVyWzxuZXZlcj5rZXldO1xuICB9XG5cbiAgLyoqIEBpbnRlcm5hbCAqL1xuICBhc1JvdXRlKGhlYWRlcjogRWR1VGFiSGVhZGVyKTogRHNmclRhYlJvdXRlIHtcbiAgICByZXR1cm4gPERzZnJUYWJSb3V0ZT5oZWFkZXI7XG4gIH1cblxuICAvKiogQGludGVybmFsICovXG4gIGdldEljb25DbGFzc2VzKHRhYkhlYWRlcjogRWR1VGFiSGVhZGVyKTogc3RyaW5nIHtcbiAgICByZXR1cm4gIXRhYkhlYWRlci5pY29uID8gJycgOiBgJHt0YWJIZWFkZXIuaWNvbn0gZnItdGFic19fdGFiLS1pY29uLWxlZnRgO1xuICB9XG5cbiAgLyoqXG4gICAqIFttb2RlIHJvdXRlc10gUmV0b3VybmUgbCdpZGVudGlmaWFudCB1bmlxdWUgZHUgYm91dG9uIGRlIGNvbnRyw7RsZSBkZSBsJ29uZ2xldCBwYXNzw6kgZW4gcGFyYW3DqHRyZS5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqL1xuICBnZXRCdXR0b25UYWJJZChoZWFkZXI6IEVkdVRhYkhlYWRlcikge1xuICAgIHJldHVybiBCVVRUT05fVEFCX0lEX1BSRUZJWCArIGhlYWRlci50YWJJZDtcbiAgfVxuXG4gIC8qKlxuICAgKiBbbW9kZSByb3V0ZXNdIFJldG91cm5lIGwnaWRlbnRpZmlhbnQgdW5pcXVlIGR1IGJvdXRvbiBkZSBjb250csO0bGUgZGUgbCdvbmdsZXQgY291cmFudC5cbiAgICpcbiAgICogQGludGVybmFsXG4gICAqL1xuICBnZXRDdXJyZW50QnV0dG9uVGFiSWQoKSB7XG4gICAgcmV0dXJuIHRoaXMuc2VsZWN0ZWRUYWJJbmRleCA+PSAwID8gQlVUVE9OX1RBQl9JRF9QUkVGSVggKyB0aGlzLnJvdXRlc1t0aGlzLnNlbGVjdGVkVGFiSW5kZXhdLnRhYklkIDogbnVsbDtcbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgb25TZWxlY3QoZXZlbnQ6IE1vdXNlRXZlbnQsIGluZGV4OiBudW1iZXIpIHtcbiAgICBldmVudC5wcmV2ZW50RGVmYXVsdCgpO1xuICAgIGV2ZW50LnN0b3BQcm9wYWdhdGlvbigpO1xuICAgIHRoaXMuc2VsZWN0ZWRUYWJJbmRleCA9IGluZGV4O1xuICAgIHRoaXMudGFiU2VsZWN0LmVtaXQodGhpcy5oZWFkZXJzW3RoaXMuc2VsZWN0ZWRUYWJJbmRleF0udGFiSWQpO1xuICB9XG5cbiAgLyoqXG4gICAqIEdlc3Rpb24gZGUgbGEgbmF2aWdhdGlvbiBhdSBjbGF2aWVyIHNhbnMgcm91dGVzIDogZW1pc3Npb24gZGUgdGFiU2VsZWN0XG4gICAqICBAaW50ZXJuYWxcbiAgICovXG4gIG9uUm91dGxlc3NUYWJLZXlkb3duKGV2ZW50OiBLZXlib2FyZEV2ZW50LCBpbmRleDogbnVtYmVyKSB7XG4gICAgY29uc3QgbmV3SW5kZXggPSB0aGlzLmNhbGN1bGF0ZU5ld0luZGV4KGV2ZW50LCBpbmRleCk7XG5cbiAgICBpZiAobmV3SW5kZXggIT09IC0xKSB7XG4gICAgICB0aGlzLnRhYlNlbGVjdC5lbWl0KHRoaXMuaGVhZGVyc1tuZXdJbmRleF0udGFiSWQpO1xuICAgIH1cbiAgfVxuXG4gIC8qKiBAaW50ZXJuYWwgKi9cbiAgb25Sb3V0ZXJMaW5rQWN0aXZlKGlzQWN0aXZlOiBib29sZWFuLCB0YWJJbmRleDogbnVtYmVyKSB7XG4gICAgaWYgKGlzQWN0aXZlKSB7XG4gICAgICB0aGlzLnNlbGVjdGVkVGFiSW5kZXggPSB0YWJJbmRleDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogR2VzdGlvbiBkZSBsYSBuYXZpZ2F0aW9uIGF1IGNsYXZpZXIgZW4gbW9kZSByb3V0ZXMuXG4gICAqXG4gICAqICBAaW50ZXJuYWxcbiAgICovXG4gIG9uQnV0dG9uVGFiS2V5ZG93bihldmVudDogS2V5Ym9hcmRFdmVudCwgaW5kZXg6IG51bWJlcik6IHZvaWQge1xuICAgIC8vIGp1c3QgaW4gY2FzZVxuICAgIGlmICh0aGlzLnNlbGVjdGVkVGFiSW5kZXggIT09IGluZGV4KSByZXR1cm47XG5cbiAgICBjb25zdCBuZXdJbmRleCA9IHRoaXMuY2FsY3VsYXRlTmV3SW5kZXgoZXZlbnQsIGluZGV4KTtcblxuICAgIGlmIChuZXdJbmRleCA+IC0xKSB7XG4gICAgICB0aGlzLnBlcmZvcm1OYXZpZ2F0aW9uKG5ld0luZGV4KTtcbiAgICB9XG4gIH1cblxuICBwcml2YXRlIGlzRmlyc3RUYWIoaW5kZXg6IG51bWJlcik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBpbmRleCA9PT0gMDtcbiAgfVxuXG4gIHByaXZhdGUgaXNMYXN0VGFiKGluZGV4OiBudW1iZXIpOiBib29sZWFuIHtcbiAgICByZXR1cm4gaW5kZXggPT09IHRoaXMudGFic0NvdW50IC0gMTtcbiAgfVxuXG4gIHByaXZhdGUgcGVyZm9ybU5hdmlnYXRpb24obmV3SW5kZXg6IG51bWJlcikge1xuICAgIGNvbnN0IHJvdXRlID0gdGhpcy5yb3V0ZXNbbmV3SW5kZXhdO1xuICAgIGNvbnN0IGV4dHJhczogTmF2aWdhdGlvbkV4dHJhcyA9IHJvdXRlLnJvdXRlckxpbmtFeHRyYXMgfHwgeyByZWxhdGl2ZVRvOiB0aGlzLmFjdGl2YXRlZFJvdXRlIH07XG4gICAgdGhpcy5yb3V0ZXIubmF2aWdhdGUoW3JvdXRlLnBhdGhdLCBleHRyYXMpO1xuICAgIHRoaXMuZWxSZWYubmF0aXZlRWxlbWVudC5xdWVyeVNlbGVjdG9yKCcjJyArIHRoaXMuZ2V0QnV0dG9uVGFiSWQocm91dGUpKT8uZm9jdXMoKTtcbiAgfVxuXG4gIHByaXZhdGUgY2FsY3VsYXRlTmV3SW5kZXgoZXZlbnQ6IEtleWJvYXJkRXZlbnQsIGluZGV4OiBudW1iZXIpOiBudW1iZXIge1xuICAgIGxldCBuZXdJbmRleCA9IC0xO1xuXG4gICAgc3dpdGNoIChldmVudC5jb2RlKSB7XG4gICAgICBjYXNlICdBcnJvd0xlZnQnOlxuICAgICAgICBuZXdJbmRleCA9IHRoaXMuaXNGaXJzdFRhYihpbmRleCkgPyB0aGlzLnRhYnNDb3VudCAtIDEgOiBpbmRleCAtIDE7XG4gICAgICAgIGJyZWFrO1xuXG4gICAgICBjYXNlICdBcnJvd1JpZ2h0JzpcbiAgICAgICAgbmV3SW5kZXggPSB0aGlzLmlzTGFzdFRhYihpbmRleCkgPyAwIDogaW5kZXggKyAxO1xuICAgICAgICBicmVhaztcblxuICAgICAgY2FzZSAnSG9tZSc6XG4gICAgICAgIG5ld0luZGV4ID0gMDtcbiAgICAgICAgYnJlYWs7XG5cbiAgICAgIGNhc2UgJ0VuZCc6XG4gICAgICAgIG5ld0luZGV4ID0gdGhpcy50YWJzQ291bnQgLSAxO1xuICAgICAgICBicmVhaztcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgYnJlYWs7XG4gICAgfVxuXG4gICAgcmV0dXJuIG5ld0luZGV4O1xuICB9XG59XG4iLCI8ZGl2IGNsYXNzPVwiZnItdGFic1wiIFtjbGFzcy5mci10YWJzLS12aWV3cG9ydC13aWR0aF09XCJmdWxsVmlld3BvcnRcIj5cbiAgPHVsIGNsYXNzPVwiZnItdGFic19fbGlzdFwiIHJvbGU9XCJ0YWJsaXN0XCIgW2F0dHIuYXJpYS1sYWJlbF09XCJ0YWJzQXJpYUxhYmVsXCI+XG4gICAgQGZvciAoaGVhZGVyIG9mIGhlYWRlcnM7IHRyYWNrIGhlYWRlcjsgbGV0IGluZGV4ID0gJGluZGV4KSB7XG4gICAgICA8bGkgcm9sZT1cInByZXNlbnRhdGlvblwiPlxuICAgICAgICBAaWYgKCFyb3V0ZXMpIHtcbiAgICAgICAgICA8YnV0dG9uXG4gICAgICAgICAgICBbYXR0ci5pZF09XCJnZXRIZWFkZXJWYWx1ZShoZWFkZXIsICdidXR0b25JZCcpIHx8IG51bGxcIlxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICByb2xlPVwidGFiXCJcbiAgICAgICAgICAgIGNsYXNzPVwiZnItdGFic19fdGFiXCJcbiAgICAgICAgICAgIFtjbGFzc109XCJnZXRJY29uQ2xhc3NlcyhoZWFkZXIpIHx8IG51bGxcIlxuICAgICAgICAgICAgW2F0dHIudGFiaW5kZXhdPVwiaW5kZXggPT09IHNlbGVjdGVkVGFiSW5kZXggPyAwIDogLTFcIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1jb250cm9sc109XCJoZWFkZXI/LnRhYklkIHx8IG51bGxcIlxuICAgICAgICAgICAgW2F0dHIuYXJpYS1zZWxlY3RlZF09XCJpbmRleCA9PT0gc2VsZWN0ZWRUYWJJbmRleFwiXG4gICAgICAgICAgICBbZGlzYWJsZWRdPVwiaGVhZGVyPy5kaXNhYmxlZCB8fCBudWxsXCJcbiAgICAgICAgICAgIChrZXlkb3duKT1cIm9uUm91dGxlc3NUYWJLZXlkb3duKCRldmVudCwgaW5kZXgpXCJcbiAgICAgICAgICAgIChjbGljayk9XCJvblNlbGVjdCgkZXZlbnQsIGluZGV4KVwiPlxuICAgICAgICAgICAge3sgaGVhZGVyLmxhYmVsIH19XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIH0gQGVsc2Uge1xuICAgICAgICAgIDxidXR0b25cbiAgICAgICAgICAgIFthdHRyLmlkXT1cImdldEJ1dHRvblRhYklkKGhlYWRlcilcIlxuICAgICAgICAgICAgdHlwZT1cImJ1dHRvblwiXG4gICAgICAgICAgICByb2xlPVwidGFiXCJcbiAgICAgICAgICAgIGNsYXNzPVwiZnItdGFic19fdGFiXCJcbiAgICAgICAgICAgIFtjbGFzc109XCJnZXRJY29uQ2xhc3NlcyhoZWFkZXIpIHx8IG51bGxcIlxuICAgICAgICAgICAgW2F0dHIudGFiaW5kZXhdPVwicmxhLmlzQWN0aXZlID8gMCA6IC0xXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtY29udHJvbHNdPVwicmxhLmlzQWN0aXZlID8gcm91dGVyVGFiSWQgOiBudWxsXCJcbiAgICAgICAgICAgIFthdHRyLmFyaWEtc2VsZWN0ZWRdPVwicmxhLmlzQWN0aXZlXCJcbiAgICAgICAgICAgIFtkaXNhYmxlZF09XCJoZWFkZXI/LmRpc2FibGVkIHx8IG51bGxcIlxuICAgICAgICAgICAgW3JvdXRlckxpbmtdPVwiYXNSb3V0ZShoZWFkZXIpLnBhdGggfHwgbnVsbFwiXG4gICAgICAgICAgICByb3V0ZXJMaW5rQWN0aXZlXG4gICAgICAgICAgICBbcm91dGVyTGlua0FjdGl2ZU9wdGlvbnNdPVwiYXNSb3V0ZShoZWFkZXIpLnJvdXRlckxpbmtBY3RpdmVPcHRpb25zID8/IHsgZXhhY3Q6IGZhbHNlIH1cIlxuICAgICAgICAgICAgW3F1ZXJ5UGFyYW1zXT1cImFzUm91dGUoaGVhZGVyKS5yb3V0ZXJMaW5rRXh0cmFzPy5xdWVyeVBhcmFtc1wiXG4gICAgICAgICAgICBbZnJhZ21lbnRdPVwiYXNSb3V0ZShoZWFkZXIpLnJvdXRlckxpbmtFeHRyYXM/LmZyYWdtZW50XCJcbiAgICAgICAgICAgIFtxdWVyeVBhcmFtc0hhbmRsaW5nXT1cImFzUm91dGUoaGVhZGVyKS5yb3V0ZXJMaW5rRXh0cmFzPy5xdWVyeVBhcmFtc0hhbmRsaW5nXCJcbiAgICAgICAgICAgIFtwcmVzZXJ2ZUZyYWdtZW50XT1cImFzUm91dGUoaGVhZGVyKS5yb3V0ZXJMaW5rRXh0cmFzPy5wcmVzZXJ2ZUZyYWdtZW50XCJcbiAgICAgICAgICAgIFtza2lwTG9jYXRpb25DaGFuZ2VdPVwiYXNSb3V0ZShoZWFkZXIpLnJvdXRlckxpbmtFeHRyYXM/LnNraXBMb2NhdGlvbkNoYW5nZVwiXG4gICAgICAgICAgICBbcmVwbGFjZVVybF09XCJhc1JvdXRlKGhlYWRlcikucm91dGVyTGlua0V4dHJhcz8ucmVwbGFjZVVybFwiXG4gICAgICAgICAgICBbc3RhdGVdPVwiYXNSb3V0ZShoZWFkZXIpLnJvdXRlckxpbmtFeHRyYXM/LnN0YXRlXCJcbiAgICAgICAgICAgIChpc0FjdGl2ZUNoYW5nZSk9XCJvblJvdXRlckxpbmtBY3RpdmUoJGV2ZW50LCBpbmRleClcIlxuICAgICAgICAgICAgI3JsYT1cInJvdXRlckxpbmtBY3RpdmVcIlxuICAgICAgICAgICAgKGNsaWNrKT1cIm9uU2VsZWN0KCRldmVudCwgaW5kZXgpXCJcbiAgICAgICAgICAgIChrZXlkb3duKT1cIm9uQnV0dG9uVGFiS2V5ZG93bigkZXZlbnQsIGluZGV4KVwiPlxuICAgICAgICAgICAge3sgaGVhZGVyLmxhYmVsIH19XG4gICAgICAgICAgPC9idXR0b24+XG4gICAgICAgIH1cbiAgICAgIDwvbGk+XG4gICAgfVxuICA8L3VsPlxuICBAaWYgKCFyb3V0ZXMpIHtcbiAgICA8bmctY29udGVudD48L25nLWNvbnRlbnQ+XG4gIH0gQGVsc2Uge1xuICAgIDxkaXZcbiAgICAgIFthdHRyLmlkXT1cInJvdXRlclRhYklkXCJcbiAgICAgIHJvbGU9XCJ0YWJwYW5lbFwiXG4gICAgICBjbGFzcz1cImZyLXRhYnNfX3BhbmVsIGZyLXRhYnNfX3BhbmVsLS1zZWxlY3RlZFwiXG4gICAgICB0YWJpbmRleD1cIjBcIlxuICAgICAgW2F0dHIuYXJpYS1sYWJlbGxlZGJ5XT1cImdldEN1cnJlbnRCdXR0b25UYWJJZCgpXCI+XG4gICAgICA8cm91dGVyLW91dGxldD48L3JvdXRlci1vdXRsZXQ+XG4gICAgPC9kaXY+XG4gICAgQGZvciAoaGVhZGVyIG9mIGhlYWRlcnM7IHRyYWNrIGhlYWRlcikge1xuICAgICAgPGRpdiBbYXR0ci5hcmlhLWxhYmVsbGVkYnldPVwiZ2V0Q3VycmVudEJ1dHRvblRhYklkKClcIj48L2Rpdj5cbiAgICB9XG4gIH1cbjwvZGl2PlxuIl19