UNPKG

@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
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