@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).
368 lines • 97.9 kB
JavaScript
import { CommonModule } from '@angular/common';
import { Component, computed, ContentChild, effect, ElementRef, EventEmitter, inject, Inject, Input, Output, Renderer2, viewChild, ViewEncapsulation, } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { DsfrDisableRouterLinkDirective, DsfrI18nPipe, newUniqueId } from '../../shared';
import { DSFR_CONFIG_TOKEN } from '../../shared/config/config-token';
import { DISPLAY_MODAL_ID, DsfrDisplayComponent } from '../display';
import { DsfrLinkComponent } from '../link';
import { ItemLinkComponent } from '../link/item-link.component';
import { DsfrSearchBarComponent } from '../search-bar';
import { DsfrTranslateModule } from '../translate';
import { DsfrMegaMenuComponent } from './component/mega-menu.component';
import { EduToolsLinksComponent } from './component/tools-links.component';
import * as i0 from "@angular/core";
import * as i1 from "@angular/common";
import * as i2 from "@angular/router";
import * as i3 from "../translate/translate.component";
/**
* Modèle de présentation interne du menu.
*/
export class ViewMenu {
constructor(/** Menu wrappé */ menu) {
this.menu = menu;
this.menuId = newUniqueId();
}
}
export class DsfrHeaderComponent {
constructor(config) {
this.config = config;
/**
* Boolean pour afficher le tag Beta (pour un site en beta).
*/
this.beta = false;
/**
* Label associé au bloc marque (Marianne). Respectez la structure avec les <br>.
* (Ministère, gouvernement, république française)
*/
this.logoLabel = 'République<br/>Française';
/** Boolean pour passer le logo opérateur en mode vertical. */
this.operatorImageVertical = false;
/**
* Permet d'afficher la barre de recherche.
*/
this.searchBar = false;
/**
* Label de la barre de recherche (pour les lecteurs d'écrans).
*/
this.searchLabel = 'Rechercher';
/** Evenement émis au changement de langue (si showTranslate). Il contient le code de la langue. */
this.langChange = new EventEmitter();
/** Evènement émis au clic sur un lien en mode `route`. */
this.linkSelect = new EventEmitter();
/** Renvoi la valeur de l'input de la barre de recherche au changement. */
this.searchChange = new EventEmitter();
/** Renvoi la valeur de l'input de la barre de recherche au clic sur rechercher. */
this.searchSelect = new EventEmitter();
this.searchInputId = newUniqueId();
/** Nombre maximum de liens d'accès rapides (3) */
this.maxToolsLinks = 3;
this._useDeprecatedPictoPath = false;
// fixme: supprimer en V2 avec le déplacement de translate dans EduToolsLinksComponent
this.translateElement = viewChild('translateComponent', { read: ElementRef });
this.translateNativeElement = computed(() => this.translateElement()?.nativeElement);
this.toolsLinksCmp = viewChild(EduToolsLinksComponent);
this.toolsLinksNativeElement = computed(() => this.toolsLinksCmp()?.toolsLinksListRef()?.nativeElement);
this.toolsLinksMobile = viewChild('toolLinksMobile');
this.toolsLinksMobileNativeElement = computed(() => this.toolsLinksMobile()?.nativeElement);
this.viewMenu = [];
this._logoLink = { link: '' }; // valeur par défaut pour la rétrocompatibilité
this.renderer = inject(Renderer2);
// Ajout observer a l'init pour la copie du script DSFR puis sur l'ajout d'éléments
if (typeof MutationObserver !== 'undefined') {
this.mutationObserver = new MutationObserver(() => {
this.duplicateToolsLinksMobile(); // dupliquer les liens pour la version mobile
});
}
else {
this.duplicateToolsLinksMobile(); // mode SSR dégradé
}
effect(() => {
if (this.toolsLinksMobileNativeElement())
this.mutationObserver?.observe(this.toolsLinksMobileNativeElement(), { childList: true, subtree: true });
});
}
get menu() {
return this.viewMenu.map((m) => m.menu);
}
get display() {
return this.showDisplay;
}
get headerToolsLinks() {
return this._headerToolsLinks;
}
get displayModalId() {
return DISPLAY_MODAL_ID;
}
get pictoPath() {
return this.artworkDirPath;
}
/** @internal */
get logoNavigation() {
return this._logoLink;
}
get searchInputInitialValue() {
return this.searchInputValue;
}
set menu(menu) {
this.viewMenu = menu.map((m) => new ViewMenu(m));
}
/**
* Url du lien 'retour accueil' du logo de la Marianne.
*/
set logoLink(value) {
if (typeof value === 'string' || !value) {
this._logoLink = { link: value };
}
else {
this._logoLink = value;
}
}
/** Affichage du lien 'Paramètre d'affichage' pour gérer les modes clair/sombre. */
set display(value) {
this.showDisplay = value;
this.maxToolsLinks = this.showDisplay ? 2 : 3;
}
/**
* Tableau de lien d'accès rapide.
* Les icônes acceptées sont ceux du DSFR y compris ceux du tableau `DsfrBtnIcon`.
*/
set headerToolsLinks(links) {
if (links && links.length > this.maxToolsLinks) {
if (this.showDisplay) {
console.warn("Avec l'utilisation du paramètre d'affichage, le nombre d'accès rapides (tools links) est limité à " +
this.maxToolsLinks);
}
else {
console.warn("Le nombre d'accès rapides (tools links) est limité à " + this.maxToolsLinks);
}
}
this._headerToolsLinks = links;
}
/**
* Chemin des pictogrammes (du composant display) renseigné par le développeur.
*
* Note: ce chemin doit permettre de récupérer directement les fichiers SVG suivants : moon.svg, sun.svg, system.svg
*
* @deprecated Use `artworkDirPath` instead.
*/
set pictoPath(path) {
this.artworkDirPath = path;
this._useDeprecatedPictoPath = true;
}
/**
* Permet de positionner la valeur initiale de la barre de recherche, si cette dernière est utilisée.
*
* @deprecated (since 1.15) Utiliser la propriété `searchInputValue` à la place.
*/
set searchInputInitialValue(value) {
this.searchInputValue = value;
}
ngOnInit() {
if (this.artworkDirPath === undefined) {
this.artworkDirPath = this.config.artworkDirPath;
}
this.navigationId ??= newUniqueId();
}
ngOnDestroy() {
this.mutationObserver?.disconnect();
}
/**
* Fix: evenements non dupliqués par le script DSFR pour les liens (toolslinks) version mobile
* On prend la main sur le clic de la div parente en cas de lien 'route' pour envoyer l'event linkSelect
* @param event element cliqué
*/
onSelectLinkMobile(event) {
if (!this._headerToolsLinks)
return;
const parent = event.target?.parentElement;
const item = this._headerToolsLinks.find((l) => l.label === parent?.getAttribute('data-item'));
if (item && item.route && !item.routerLink) {
event.preventDefault();
this.linkSelect.emit(item);
}
}
hasToolsLinks() {
return (this.headerToolsLinks?.length > 0 ||
this.translate?.languages?.length > 0 ||
this.display ||
!!this.headerTools ||
this.toolsLinksTemplate);
}
onLink(item) {
this.linkSelect.emit(item);
}
onSearchSelect(text) {
this.searchSelect.emit(text);
}
onSearchChange(text) {
this.searchChange.emit(text);
}
onLanguageChange(codeLang) {
this.langChange.emit(codeLang);
}
onMegaMenuClose(item) {
item.expanded = false;
}
onMenuItemClick(item) {
this.menu.forEach((i) => (i.expanded = false));
item.expanded = true;
}
getCustomClassHeaderToolsLink(item) {
const customClass = ['fr-btn'];
if (item.icon)
customClass.push(item.icon);
if (item.customClass)
customClass.push(item.customClass);
return customClass.join(' ');
}
createMenuId() {
return newUniqueId();
}
/**
* Dupliquer les liens toolsLinks pour le menu mobile
* /!\ interaction script DSFR : le script copie les liens la premiere fois, mais n'écoute pas les changements
* Les liens à l'interieur de fr-header__tools-links sont dupliqués dans fr-header__menu-links
*/
duplicateToolsLinksMobile() {
this.mutationObserver?.disconnect(); // deconnecter l'observer pour ne pas le déclencher sur le clonage
if (this.toolsLinksMobileNativeElement()) {
const childElements = this.toolsLinksMobileNativeElement().children;
// suppression des noeuds précédent
Array.from(childElements).forEach((e) => {
this.renderer.removeChild(this.toolsLinksMobileNativeElement(), e);
});
// Dupliquer les liens de menu en suffixant tous les ids par -mobile
if (this.toolsLinksNativeElement()) {
const clone = this.toolsLinksNativeElement()?.cloneNode(true);
if (clone) {
this.suffixIdsToolsLinks(clone);
this.renderer.appendChild(this.toolsLinksMobileNativeElement(), clone);
}
}
// Dupliquer le composant translate - a supprimer en V2 (translate dans EduToolsLinksComponent)
if (this.translate) {
this.renderer.appendChild(this.toolsLinksMobileNativeElement(), this.translateNativeElement());
this.translateNativeElement().style.display = 'block';
}
// Dupliquer le custom slot - a supprimer en V2
if (this.headerToolsMobile) {
this.renderer.appendChild(this.toolsLinksMobileNativeElement(), this.headerToolsMobile.elementRef.nativeElement);
}
}
if (this.toolsLinksNativeElement()) {
this.mutationObserver?.observe(this.toolsLinksNativeElement(), { childList: true, subtree: true });
}
}
/**
* Reprise de la logique du script DSFR pour suffixer les ids dupliqués en mobile
* @param toolsLinks container concerné des liens d'accès rapides dupliqués
* @param suffix -mobile pour ne pas avoir plusieurs ids identiques
*/
suffixIdsToolsLinks(toolsLinks, suffix = '-mobile') {
const idMap = new Map();
// suffixes des ids
toolsLinks.querySelectorAll('[id]').forEach((el) => {
const newId = `${el.id}${suffix}`;
idMap.set(el.id, newId);
el.id = newId;
});
// Mise a jour des attributs qui peuvent référencer ces ids
const attributes = ['aria-controls', 'aria-describedby', 'aria-labelledby'];
toolsLinks.querySelectorAll('*').forEach((el) => {
attributes.forEach((attr) => {
const value = el.getAttribute(attr);
if (!value)
return;
const newId = idMap.get(value.trim());
if (newId) {
el.setAttribute(attr, newId);
}
});
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrHeaderComponent, deps: [{ token: DSFR_CONFIG_TOKEN }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.13", type: DsfrHeaderComponent, isStandalone: true, selector: "dsfr-header", inputs: { artworkDirPath: "artworkDirPath", beta: "beta", logoLabel: "logoLabel", logoTooltipMessage: "logoTooltipMessage", navigationId: "navigationId", operatorImageAlt: "operatorImageAlt", operatorImagePath: "operatorImagePath", operatorImageVertical: "operatorImageVertical", serviceTitle: "serviceTitle", serviceTagline: "serviceTagline", searchBar: "searchBar", searchButtonTitle: "searchButtonTitle", searchLabel: "searchLabel", searchInputValue: "searchInputValue", searchInputPlaceholder: "searchInputPlaceholder", translate: "translate", menu: "menu", logoLink: "logoLink", display: "display", headerToolsLinks: "headerToolsLinks", pictoPath: "pictoPath", searchInputInitialValue: "searchInputInitialValue" }, outputs: { langChange: "langChange", linkSelect: "linkSelect", searchChange: "searchChange", searchSelect: "searchSelect" }, queries: [{ propertyName: "toolsLinksTemplate", first: true, predicate: ["toolsLinksTemplate"], descendants: true, static: true }, { propertyName: "headerTools", first: true, predicate: ["headerTools"], descendants: true, static: true }, { propertyName: "headerToolsMobile", first: true, predicate: ["headerToolsMobile"], descendants: true, static: true }], viewQueries: [{ propertyName: "translateElement", first: true, predicate: ["translateComponent"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "toolsLinksCmp", first: true, predicate: EduToolsLinksComponent, descendants: true, isSignal: true }, { propertyName: "toolsLinksMobile", first: true, predicate: ["toolLinksMobile"], descendants: true, isSignal: true }], ngImport: i0, template: "<header role=\"banner\" class=\"fr-header\">\n <div class=\"fr-header__body\">\n <div class=\"fr-container\">\n <div class=\"fr-header__body-row\">\n <div class=\"fr-header__brand fr-enlarge-link\">\n <div class=\"fr-header__brand-top\">\n <div class=\"fr-header__logo\">\n <!--Cas marianne sans titre ni image op\u00E9rateur-->\n @if (!serviceTitle && !operatorImagePath) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logoNavigation.route ?? ''\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <p class=\"fr-logo\" [innerHTML]=\"logoLabel\"></p>\n </dsfr-link>\n } @else if (serviceTitle || operatorImagePath) {\n <!--Cas marianne avec titre et/ou image op\u00E9rateur-->\n <p class=\"fr-logo\" [innerHTML]=\"logoLabel\"></p>\n }\n </div>\n @if (operatorImagePath) {\n <div class=\"fr-header__operator\">\n <!--Cas o\u00F9 il n'y a pas de titre avec l'image-->\n @if (!serviceTitle) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logoNavigation.route ?? ''\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <img\n class=\"fr-responsive-img\"\n [style.width]=\"operatorImageVertical ? '3.5rem' : null\"\n [style.max-width]=\"!operatorImageVertical ? '9.0625rem' : null\"\n [src]=\"operatorImagePath\"\n [attr.alt]=\"operatorImageAlt\" />\n </dsfr-link>\n } @else {\n <!--Cas o\u00F9 il y a un titre avec l'image-->\n <img\n class=\"fr-responsive-img\"\n [style.width]=\"operatorImageVertical ? '3.5rem' : null\"\n [style.max-width]=\"!operatorImageVertical ? '9.0625rem' : null\"\n [src]=\"operatorImagePath\"\n [attr.alt]=\"operatorImageAlt\" />\n }\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit imp\u00E9rativement \u00EAtre renseign\u00E9e et reprendre le texte visible dans l\u2019image -->\n </div>\n }\n <div class=\"fr-header__navbar\">\n @if (searchBar) {\n <button\n type=\"button\"\n class=\"fr-btn--search fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n id=\"{{ navigationId }}-button-search\"\n [title]=\"'commons.search' | dsfrI18n\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n }\n <button\n type=\"button\"\n class=\"fr-btn--menu fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId\"\n id=\"{{ navigationId }}-button-menu\"\n [title]=\"'header.menu.label' | dsfrI18n\">\n {{ 'header.menu.label' | dsfrI18n }}\n </button>\n </div>\n </div>\n @if (serviceTitle || beta) {\n <div class=\"fr-header__service\">\n @if (logoNavigation) {\n <dsfr-link\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [route]=\"logoNavigation.route ?? ''\"\n [ariaCurrentWhenActive]=\"false\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [customClass]=\"'edu-link'\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <p class=\"fr-header__service-title\">\n {{ serviceTitle }}\n @if (beta) {\n <span class=\"fr-badge fr-badge--sm fr-badge--green-emeraude\">BETA</span>\n }\n </p>\n </dsfr-link>\n }\n @if (!logoNavigation) {\n <p class=\"fr-header__service-title\">\n {{ serviceTitle }}\n @if (beta) {\n <span class=\"fr-badge fr-badge--sm fr-badge--green-emeraude\">BETA</span>\n }\n </p>\n }\n <!-- BUG Classe 'fr-header__service-tagline' inconnue en DSFR 1.9.3 -->\n @if (serviceTagline) {\n <p class=\"fr-header__service-tagline\">\n {{ serviceTagline }}\n </p>\n }\n </div>\n }\n </div>\n\n <!-- Tools links -->\n @if (hasToolsLinks() || searchBar) {\n <div class=\"fr-header__tools\">\n @if (hasToolsLinks()) {\n <div class=\"fr-header__tools-links\">\n <!--headerTools pour r\u00E9trocompatibilit\u00E9, d\u00E9pr\u00E9ci\u00E9 et suppression en V2 -->\n @if (headerTools) {\n <div class=\"edu-header__tools--custom\">\n <ng-container *ngTemplateOutlet=\"headerTools\"></ng-container>\n </div>\n }\n <edu-tools-links\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n @if (translate) {\n <!-- en V2 apres suppression de headerTools, d\u00E9placer dans edu-tools-links -->\n <dsfr-translate\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n </div>\n }\n @if (searchBar) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n title=\"{{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <dsfr-search-bar\n id=\"search-header\"\n [label]=\"searchLabel\"\n [inputId]=\"searchInputId\"\n [buttonTitle]=\"searchButtonTitle\"\n [placeholder]=\"searchInputPlaceholder\"\n [value]=\"searchInputValue\"\n (searchChange)=\"onSearchChange($event)\"\n (searchSelect)=\"onSearchSelect($event)\"></dsfr-search-bar>\n </div>\n </div>\n }\n </div>\n }\n <!-- fin header tools ----------------------------------------------------------------------------------------->\n </div>\n </div>\n </div>\n <!-- Menu de navigation principale --------------------------------------------------------------------------------->\n @if (menu) {\n <div class=\"fr-header__menu fr-modal\" [id]=\"navigationId\" [attr.aria-labelledby]=\"navigationId + '-button-menu'\">\n <div class=\"fr-container\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId\"\n title=\" {{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <div #toolLinksMobile (click)=\"onSelectLinkMobile($event)\" class=\"fr-header__menu-links\"></div>\n <!-- Composant translate d\u00E9plac\u00E9 dans toolsLinksMobile. Display none pour \u00E9viter le flickering de l'element\n fixme: A supprimer en V2 -->\n @if (translate) {\n <dsfr-translate\n [ngStyle]=\"{ display: 'none' }\"\n #translateComponent\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n <!-- Custom slot pour mobile. fixme: A supprimer en V2 -->\n @if (headerToolsMobile) {\n <div class=\"fr-header__menu-links\">\n <ng-container *ngTemplateOutlet=\"headerToolsMobile\"></ng-container>\n </div>\n }\n <nav\n class=\"fr-nav\"\n id=\"{{ navigationId }}-nav\"\n role=\"navigation\"\n [attr.aria-label]=\"'header.mainMenu' | dsfrI18n\">\n <ul class=\"fr-nav__list\">\n @for (item of viewMenu; track item) {\n <!-- Entr\u00E9e de menu simple (lien direct) ------------------------------------------------------------------>\n @if (!item.menu.subItems && !item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <edu-item-link\n [item]=\"item.menu\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(item.menu)\"></edu-item-link>\n </li>\n }\n\n <!-- Entr\u00E9e de menu d\u00E9roulant ----------------------------------------------------------------------------->\n @if (item.menu.subItems) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'menu-' + item.menuId }\"></ng-container>\n <div class=\"fr-collapse fr-menu\" id=\"menu-{{ item.menuId }}\">\n <ul class=\"fr-menu__list\">\n @for (subItem of item.menu.subItems; track subItem) {\n <li>\n <edu-item-link\n [item]=\"subItem\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(subItem)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n <!-- Entr\u00E9e de m\u00E9ga menu ---------------------------------------------------------------------------------->\n @if (item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'mega-menu-' + item.menuId }\"></ng-container>\n <div\n [ngClass]=\"{ 'fr-collapse--expanded': item.menu.expanded }\"\n class=\"fr-collapse fr-mega-menu\"\n id=\"mega-menu-{{ item.menuId }}\"\n tabindex=\"-1\">\n <dsfr-mega-menu\n [megaMenu]=\"item.menu.megaMenu\"\n [idMenu]=\"item.menuId\"\n (closeSelect)=\"onMegaMenuClose(item.menu)\"\n (linkSelect)=\"onLink($event)\"></dsfr-mega-menu>\n </div>\n </li>\n }\n }\n </ul>\n </nav>\n </div>\n </div>\n }\n</header>\n\n@if (showDisplay && _useDeprecatedPictoPath) {\n <dsfr-display [pictoPath]=\"artworkDirPath\"></dsfr-display>\n} @else if (showDisplay && !_useDeprecatedPictoPath) {\n <dsfr-display [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n\n<ng-template #buttonMenu let-item=\"item\" let-ariaId=\"ariaId\">\n @if (item.routerLink) {\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [ariaCurrentWhenActive]=\"true\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [disableNavigation]=\"true\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n } @else {\n <!-- S\u00E9parer de la d\u00E9finition avec routerLink pour que l'attribut aria-current ne soit pas \u00E9cras\u00E9 par la directive -->\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n }\n</ng-template>\n", styles: [".fr-collapse:before{content:\"\"}.edu-header__tools--custom{display:none}@media (min-width: 62em){.edu-header__tools--custom{display:flex;flex-direction:row;justify-content:flex-end;gap:.5rem}}.fr-header__tools-links .fr-btns-group:not(:has(dsfr-tool-link)){margin-bottom:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "ngmodule", type: RouterModule }, { kind: "directive", type: i2.RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }, { kind: "directive", type: i2.RouterLinkActive, selector: "[routerLinkActive]", inputs: ["routerLinkActiveOptions", "ariaCurrentWhenActive", "routerLinkActive"], outputs: ["isActiveChange"], exportAs: ["routerLinkActive"] }, { kind: "component", type: DsfrSearchBarComponent, selector: "dsfr-search-bar", inputs: ["inputId", "buttonText", "label", "large", "placeholder", "buttonTitle", "value", "initialValue", "id"], outputs: ["searchChange", "searchSelect"] }, { kind: "component", type: ItemLinkComponent, selector: "edu-item-link", inputs: ["defaultIconPosition", "item"] }, { kind: "ngmodule", type: DsfrTranslateModule }, { kind: "component", type: i3.DsfrTranslateComponent, selector: "dsfr-translate", inputs: ["languages", "outline", "currentLangCode"], outputs: ["langChange"] }, { kind: "component", type: DsfrDisplayComponent, selector: "dsfr-display", inputs: ["displayId", "artworkDirPath", "pictoPath"], outputs: ["displayChange"] }, { kind: "component", type: DsfrLinkComponent, selector: "dsfr-link", inputs: ["ariaCurrent", "ariaLabel", "ariaControls", "customClass", "disabled", "icon", "iconPosition", "linkId", "label", "link", "linkTarget", "route", "routePath", "routerLinkActive", "routerLinkActiveOptions", "routerLinkExtras", "linkSize", "tooltipMessage", "mode", "ariaCurrentWhenActive", "size", "targetLink", "routerLink"], outputs: ["linkSelect"] }, { kind: "component", type: DsfrMegaMenuComponent, selector: "dsfr-mega-menu", inputs: ["megaMenu", "idMenu"], outputs: ["linkSelect", "closeSelect"] }, { kind: "directive", type: DsfrDisableRouterLinkDirective, selector: "button[routerLink][disableNavigation]", inputs: ["disableNavigation"] }, { kind: "component", type: EduToolsLinksComponent, selector: "edu-tools-links", inputs: ["toolsLinks", "showDisplay", "displayId", "toolsLinksTemplate", "maxToolsLinks"], outputs: ["linkSelect"] }, { kind: "pipe", type: DsfrI18nPipe, name: "dsfrI18n" }], encapsulation: i0.ViewEncapsulation.None }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.13", ngImport: i0, type: DsfrHeaderComponent, decorators: [{
type: Component,
args: [{ selector: 'dsfr-header', encapsulation: ViewEncapsulation.None, standalone: true, imports: [
CommonModule,
FormsModule,
RouterModule,
DsfrSearchBarComponent,
ItemLinkComponent,
DsfrTranslateModule,
DsfrDisplayComponent,
DsfrLinkComponent,
DsfrMegaMenuComponent,
DsfrDisableRouterLinkDirective,
EduToolsLinksComponent,
DsfrI18nPipe,
], template: "<header role=\"banner\" class=\"fr-header\">\n <div class=\"fr-header__body\">\n <div class=\"fr-container\">\n <div class=\"fr-header__body-row\">\n <div class=\"fr-header__brand fr-enlarge-link\">\n <div class=\"fr-header__brand-top\">\n <div class=\"fr-header__logo\">\n <!--Cas marianne sans titre ni image op\u00E9rateur-->\n @if (!serviceTitle && !operatorImagePath) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logoNavigation.route ?? ''\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <p class=\"fr-logo\" [innerHTML]=\"logoLabel\"></p>\n </dsfr-link>\n } @else if (serviceTitle || operatorImagePath) {\n <!--Cas marianne avec titre et/ou image op\u00E9rateur-->\n <p class=\"fr-logo\" [innerHTML]=\"logoLabel\"></p>\n }\n </div>\n @if (operatorImagePath) {\n <div class=\"fr-header__operator\">\n <!--Cas o\u00F9 il n'y a pas de titre avec l'image-->\n @if (!serviceTitle) {\n <dsfr-link\n [customClass]=\"'edu-link'\"\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [ariaCurrentWhenActive]=\"false\"\n [route]=\"logoNavigation.route ?? ''\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <img\n class=\"fr-responsive-img\"\n [style.width]=\"operatorImageVertical ? '3.5rem' : null\"\n [style.max-width]=\"!operatorImageVertical ? '9.0625rem' : null\"\n [src]=\"operatorImagePath\"\n [attr.alt]=\"operatorImageAlt\" />\n </dsfr-link>\n } @else {\n <!--Cas o\u00F9 il y a un titre avec l'image-->\n <img\n class=\"fr-responsive-img\"\n [style.width]=\"operatorImageVertical ? '3.5rem' : null\"\n [style.max-width]=\"!operatorImageVertical ? '9.0625rem' : null\"\n [src]=\"operatorImagePath\"\n [attr.alt]=\"operatorImageAlt\" />\n }\n <!-- L\u2019alternative de l\u2019image (attribut alt) doit imp\u00E9rativement \u00EAtre renseign\u00E9e et reprendre le texte visible dans l\u2019image -->\n </div>\n }\n <div class=\"fr-header__navbar\">\n @if (searchBar) {\n <button\n type=\"button\"\n class=\"fr-btn--search fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n id=\"{{ navigationId }}-button-search\"\n [title]=\"'commons.search' | dsfrI18n\">\n {{ 'commons.search' | dsfrI18n }}\n </button>\n }\n <button\n type=\"button\"\n class=\"fr-btn--menu fr-btn\"\n data-fr-opened=\"false\"\n [attr.aria-controls]=\"navigationId\"\n id=\"{{ navigationId }}-button-menu\"\n [title]=\"'header.menu.label' | dsfrI18n\">\n {{ 'header.menu.label' | dsfrI18n }}\n </button>\n </div>\n </div>\n @if (serviceTitle || beta) {\n <div class=\"fr-header__service\">\n @if (logoNavigation) {\n <dsfr-link\n [link]=\"logoNavigation.link ?? ''\"\n [linkTarget]=\"logoNavigation.linkTarget\"\n [route]=\"logoNavigation.route ?? ''\"\n [ariaCurrentWhenActive]=\"false\"\n [tooltipMessage]=\"logoTooltipMessage\"\n [customClass]=\"'edu-link'\"\n [routePath]=\"logoNavigation.routerLink ?? ''\">\n <p class=\"fr-header__service-title\">\n {{ serviceTitle }}\n @if (beta) {\n <span class=\"fr-badge fr-badge--sm fr-badge--green-emeraude\">BETA</span>\n }\n </p>\n </dsfr-link>\n }\n @if (!logoNavigation) {\n <p class=\"fr-header__service-title\">\n {{ serviceTitle }}\n @if (beta) {\n <span class=\"fr-badge fr-badge--sm fr-badge--green-emeraude\">BETA</span>\n }\n </p>\n }\n <!-- BUG Classe 'fr-header__service-tagline' inconnue en DSFR 1.9.3 -->\n @if (serviceTagline) {\n <p class=\"fr-header__service-tagline\">\n {{ serviceTagline }}\n </p>\n }\n </div>\n }\n </div>\n\n <!-- Tools links -->\n @if (hasToolsLinks() || searchBar) {\n <div class=\"fr-header__tools\">\n @if (hasToolsLinks()) {\n <div class=\"fr-header__tools-links\">\n <!--headerTools pour r\u00E9trocompatibilit\u00E9, d\u00E9pr\u00E9ci\u00E9 et suppression en V2 -->\n @if (headerTools) {\n <div class=\"edu-header__tools--custom\">\n <ng-container *ngTemplateOutlet=\"headerTools\"></ng-container>\n </div>\n }\n <edu-tools-links\n [toolsLinks]=\"headerToolsLinks\"\n [maxToolsLinks]=\"maxToolsLinks\"\n [displayId]=\"displayModalId\"\n [showDisplay]=\"showDisplay\"\n [toolsLinksTemplate]=\"toolsLinksTemplate\"></edu-tools-links>\n @if (translate) {\n <!-- en V2 apres suppression de headerTools, d\u00E9placer dans edu-tools-links -->\n <dsfr-translate\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n </div>\n }\n @if (searchBar) {\n <div\n [attr.aria-labelledby]=\"'search-btn-' + searchInputId\"\n class=\"fr-header__search fr-modal\"\n [id]=\"navigationId + '-search'\">\n <div class=\"fr-container fr-container-lg--fluid\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId + '-search'\"\n title=\"{{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <dsfr-search-bar\n id=\"search-header\"\n [label]=\"searchLabel\"\n [inputId]=\"searchInputId\"\n [buttonTitle]=\"searchButtonTitle\"\n [placeholder]=\"searchInputPlaceholder\"\n [value]=\"searchInputValue\"\n (searchChange)=\"onSearchChange($event)\"\n (searchSelect)=\"onSearchSelect($event)\"></dsfr-search-bar>\n </div>\n </div>\n }\n </div>\n }\n <!-- fin header tools ----------------------------------------------------------------------------------------->\n </div>\n </div>\n </div>\n <!-- Menu de navigation principale --------------------------------------------------------------------------------->\n @if (menu) {\n <div class=\"fr-header__menu fr-modal\" [id]=\"navigationId\" [attr.aria-labelledby]=\"navigationId + '-button-menu'\">\n <div class=\"fr-container\">\n <button\n type=\"button\"\n class=\"fr-btn--close fr-btn\"\n [attr.aria-controls]=\"navigationId\"\n title=\" {{ 'commons.close' | dsfrI18n }}\">\n {{ 'commons.close' | dsfrI18n }}\n </button>\n <div #toolLinksMobile (click)=\"onSelectLinkMobile($event)\" class=\"fr-header__menu-links\"></div>\n <!-- Composant translate d\u00E9plac\u00E9 dans toolsLinksMobile. Display none pour \u00E9viter le flickering de l'element\n fixme: A supprimer en V2 -->\n @if (translate) {\n <dsfr-translate\n [ngStyle]=\"{ display: 'none' }\"\n #translateComponent\n (langChange)=\"onLanguageChange($event)\"\n [languages]=\"translate.languages\"></dsfr-translate>\n }\n <!-- Custom slot pour mobile. fixme: A supprimer en V2 -->\n @if (headerToolsMobile) {\n <div class=\"fr-header__menu-links\">\n <ng-container *ngTemplateOutlet=\"headerToolsMobile\"></ng-container>\n </div>\n }\n <nav\n class=\"fr-nav\"\n id=\"{{ navigationId }}-nav\"\n role=\"navigation\"\n [attr.aria-label]=\"'header.mainMenu' | dsfrI18n\">\n <ul class=\"fr-nav__list\">\n @for (item of viewMenu; track item) {\n <!-- Entr\u00E9e de menu simple (lien direct) ------------------------------------------------------------------>\n @if (!item.menu.subItems && !item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <edu-item-link\n [item]=\"item.menu\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(item.menu)\"></edu-item-link>\n </li>\n }\n\n <!-- Entr\u00E9e de menu d\u00E9roulant ----------------------------------------------------------------------------->\n @if (item.menu.subItems) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'menu-' + item.menuId }\"></ng-container>\n <div class=\"fr-collapse fr-menu\" id=\"menu-{{ item.menuId }}\">\n <ul class=\"fr-menu__list\">\n @for (subItem of item.menu.subItems; track subItem) {\n <li>\n <edu-item-link\n [item]=\"subItem\"\n customClass=\"fr-nav__link\"\n (linkSelect)=\"onLink(subItem)\"></edu-item-link>\n </li>\n }\n </ul>\n </div>\n </li>\n }\n <!-- Entr\u00E9e de m\u00E9ga menu ---------------------------------------------------------------------------------->\n @if (item.menu.megaMenu) {\n <li class=\"fr-nav__item\">\n <ng-container\n [ngTemplateOutlet]=\"buttonMenu\"\n [ngTemplateOutletContext]=\"{ item: item.menu, ariaId: 'mega-menu-' + item.menuId }\"></ng-container>\n <div\n [ngClass]=\"{ 'fr-collapse--expanded': item.menu.expanded }\"\n class=\"fr-collapse fr-mega-menu\"\n id=\"mega-menu-{{ item.menuId }}\"\n tabindex=\"-1\">\n <dsfr-mega-menu\n [megaMenu]=\"item.menu.megaMenu\"\n [idMenu]=\"item.menuId\"\n (closeSelect)=\"onMegaMenuClose(item.menu)\"\n (linkSelect)=\"onLink($event)\"></dsfr-mega-menu>\n </div>\n </li>\n }\n }\n </ul>\n </nav>\n </div>\n </div>\n }\n</header>\n\n@if (showDisplay && _useDeprecatedPictoPath) {\n <dsfr-display [pictoPath]=\"artworkDirPath\"></dsfr-display>\n} @else if (showDisplay && !_useDeprecatedPictoPath) {\n <dsfr-display [artworkDirPath]=\"artworkDirPath\"></dsfr-display>\n}\n\n<ng-template #buttonMenu let-item=\"item\" let-ariaId=\"ariaId\">\n @if (item.routerLink) {\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [routerLink]=\"item.routerLink\"\n [routerLinkActive]=\"item.routerLinkActive ?? ''\"\n [ariaCurrentWhenActive]=\"true\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [disableNavigation]=\"true\"\n [routerLinkActiveOptions]=\"item.routerLinkActiveOptions ? item.routerLinkActiveOptions : { exact: false }\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n } @else {\n <!-- S\u00E9parer de la d\u00E9finition avec routerLink pour que l'attribut aria-current ne soit pas \u00E9cras\u00E9 par la directive -->\n <button\n type=\"button\"\n (click)=\"onMenuItemClick(item)\"\n class=\"fr-nav__btn\"\n [attr.aria-expanded]=\"item.expanded ?? false\"\n [attr.aria-current]=\"item.active && !item.routerLink ? true : null\"\n [attr.aria-controls]=\"ariaId\">\n {{ item.label }}\n </button>\n }\n</ng-template>\n", styles: [".fr-collapse:before{content:\"\"}.edu-header__tools--custom{display:none}@media (min-width: 62em){.edu-header__tools--custom{display:flex;flex-direction:row;justify-content:flex-end;gap:.5rem}}.fr-header__tools-links .fr-btns-group:not(:has(dsfr-tool-link)){margin-bottom:0}\n"] }]
}], ctorParameters: () => [{ type: undefined, decorators: [{
type: Inject,
args: [DSFR_CONFIG_TOKEN]
}] }], propDecorators: { toolsLinksTemplate: [{
type: ContentChild,
args: ['toolsLinksTemplate', { static: true }]
}], headerTools: [{
type: ContentChild,
args: ['headerTools', { static: true }]
}], headerToolsMobile: [{
type: ContentChild,
args: ['headerToolsMobile', { static: true }]
}], artworkDirPath: [{
type: Input
}], beta: [{
type: Input
}], logoLabel: [{
type: Input
}], logoTooltipMessage: [{
type: Input
}], navigationId: [{
type: Input
}], operatorImageAlt: [{
type: Input
}], operatorImagePath: [{
type: Input
}], operatorImageVertical: [{
type: Input
}], serviceTitle: [{
type: Input
}], serviceTagline: [{
type: Input
}], searchBar: [{
type: Input
}], searchButtonTitle: [{
type: Input
}], searchLabel: [{
type: Input
}], searchInputValue: [{
type: Input
}], searchInputPlaceholder: [{
type: Input
}], translate: [{
type: Input
}], langChange: [{
type: Output
}], linkSelect: [{
type: Output
}], searchChange: [{
type: Output
}], searchSelect: [{
type: Output
}], menu: [{
type: Input
}], logoLink: [{
type: Input
}], display: [{
type: Input
}], headerToolsLinks: [{
type: Input
}], pictoPath: [{
type: Input
}], searchInputInitialValue: [{
type: Input
}] } });
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVhZGVyLmNvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uLy4uL3Byb2plY3RzL25neC1kc2ZyLWNvbXBvbmVudHMvc3JjL2xpYi9jb21wb25lbnRzL2hlYWRlci9oZWFkZXIuY29tcG9uZW50LnRzIiwiLi4vLi4vLi4vLi4vLi4vLi4vcHJvamVjdHMvbmd4LWRzZnItY29tcG9uZW50cy9zcmMvbGliL2NvbXBvbmVudHMvaGVhZGVyL2hlYWRlci5jb21wb25lbnQuaHRtbCJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFDL0MsT0FBTyxFQUNMLFNBQVMsRUFDVCxRQUFRLEVBQ1IsWUFBWSxFQUNaLE1BQU0sRUFDTixVQUFVLEVBQ1YsWUFBWSxFQUNaLE1BQU0sRUFDTixNQUFNLEVBQ04sS0FBSyxFQUdMLE1BQU0sRUFDTixTQUFTLEVBRVQsU0FBUyxFQUNULGlCQUFpQixHQUNsQixNQUFNLGVBQWUsQ0FBQztBQUN2QixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDN0MsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQy9DLE9BQU8sRUFBRSw4QkFBOEIsRUFBRSxZQUFZLEVBQTRCLFdBQVcsRUFBRSxNQUFNLGNBQWMsQ0FBQztBQUNuSCxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxrQ0FBa0MsQ0FBQztBQUVyRSxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDcEUsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzVDLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLDZCQUE2QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUN2RCxPQUFPLEVBQUUsbUJBQW1CLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDbkQsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0saUNBQWlDLENBQUM7QUFDeEUsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sbUNBQW1DLENBQUM7Ozs7O0FBRzNFOztHQUVHO0FBQ0gsTUFBTSxPQUFPLFFBQVE7SUFNbkIsWUFBWSxrQkFBa0IsQ0FBUSxJQUF3QjtRQUF4QixTQUFJLEdBQUosSUFBSSxDQUFvQjtRQUM1RCxJQUFJLENBQUMsTUFBTSxHQUFHLFdBQVcsRUFBRSxDQUFDO0lBQzlCLENBQUM7Q0FDRjtBQXVCRCxNQUFNLE9BQU8sbUJBQW1CO0lBaUk5QixZQUErQyxNQUFrQjtRQUFsQixXQUFNLEdBQU4sTUFBTSxDQUFZO1FBOUdqRTs7V0FFRztRQUNNLFNBQUksR0FBRyxLQUFLLENBQUM7UUFFdEI7OztXQUdHO1FBQ00sY0FBUyxHQUFHLDBCQUEwQixDQUFDO1FBb0JoRCwrREFBK0Q7UUFDdEQsMEJBQXFCLEdBQUcsS0FBSyxDQUFDO1FBUXZDOztXQUVHO1FBQ00sY0FBUyxHQUFHLEtBQUssQ0FBQztRQU8zQjs7V0FFRztRQUNNLGdCQUFXLEdBQUcsWUFBWSxDQUFDO1FBbUJwQyxtR0FBbUc7UUFDaEYsZUFBVSxHQUFHLElBQUksWUFBWSxFQUFVLENBQUM7UUFFM0QsMERBQTBEO1FBQ3ZDLGVBQVUsR0FBRyxJQUFJLFlBQVksRUFBWSxDQUFDO1FBRTdELDJFQUEyRTtRQUN4RCxpQkFBWSxHQUF5QixJQUFJLFlBQVksRUFBRSxDQUFDO1FBRTNFLG9GQUFvRjtRQUNqRSxpQkFBWSxHQUF5QixJQUFJLFlBQVksRUFBRSxDQUFDO1FBRWpFLGtCQUFhLEdBQUcsV0FBVyxFQUFFLENBQUM7UUFFeEMsbURBQW1EO1FBQ3pDLGtCQUFhLEdBQUcsQ0FBQyxDQUFDO1FBRWxCLDRCQUF1QixHQUFHLEtBQUssQ0FBQztRQUkxQyxzRkFBc0Y7UUFDbkUscUJBQWdCLEdBQUcsU0FBUyxDQUFDLG9CQUFvQixFQUFFLEVBQUUsSUFBSSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUM7UUFDekUsMkJBQXNCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxFQUFFLGFBQWEsQ0FBQyxDQUFDO1FBRWhGLGtCQUFhLEdBQUcsU0FBUyxDQUFDLHNCQUFzQixDQUFDLENBQUM7UUFDbEQsNEJBQXVCLEdBQUcsUUFBUSxDQUFDLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsRUFBRSxpQkFBaUIsRUFBRSxFQUFFLG