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).

368 lines 97.9 kB
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