The following has evaluated to null or missing: ==> entries?filter(e -> e.isSelected())?first [in template "48914885574227#20121#275046" at line 146, column 33] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #assign currentEntry = entries?filter... [in template "48914885574227#20121#275046" at line 146, column 9] ----
1<div class="sbil_header_language_dropdown">
2 <#if entries?has_content>
3 <#assign languageId = localeUtil.toLanguageId(locale) />
4
5 <@liferay_aui["select"]
6 changesContext=true
7 label=""
8 name='${randomNamespace + name}'
9 onChange='${randomNamespace + "changeLanguage();"}'
10 title="language"
11 data={
12 "toggle": "tooltip",
13 "placement": "left"
14 }
15 useNamespace=false
16 >
17 <#list entries as entry>
18 <#assign originalUrl = entry.getURL()!"" />
19
20 <@liferay_aui["option"]
21 cssClass="taglib-language-option taglib-language-option-${entry.getW3cLanguageId()}"
22 disabled=entry.isDisabled()
23 label=stringUtil.toUpperCase(entry.getLongDisplayName())
24 lang=entry.getW3cLanguageId()
25 localizeLabel=false
26 selected=entry.isSelected()
27 value=originalUrl
28 />
29 </#list>
30 </@>
31
32 <@liferay_aui["script"]>
33 function ${randomNamespace}changeLanguage() {
34 window.location.href=event.target.value;
35 }
36 </@>
37 </#if>
38</div>
39
40<style>
41 .sbil_header_language_dropdown select,
42 .sbil_header_language_dropdown select::picker(select),
43 .sbil_header_language_dropdown .form-control:not([type=range]) {
44 appearance: base-select;
45 }
46
47 .sbil_header_language_dropdown .form-group {
48 margin-bottom: 0;
49 }
50
51 .sbil_header_language_dropdown .form-control {
52 display: flex;
53 height: 2rem;
54 padding: 0 0.75rem; /* 8px 12px */
55 justify-content: center;
56 align-items: center;
57 gap: 0.25rem; /* 4px */
58 border-radius: 1.25rem; /* 20px */
59 background: rgba(30, 30, 30, 0.05);
60 border: none;
61
62 color: #2a2076;
63 font-size: 0.875rem; /* 14px */
64 font-style: normal;
65 font-weight: 500;
66 line-height: 1.1875rem; /* 19px */
67 letter-spacing: 0.002625rem; /* 0.042px */
68 }
69
70 .sbil_header_language_dropdown .form-control::picker(select) {
71 top: calc(anchor(bottom) + 0.3125rem); /* 5px */
72 position-area: bottom;
73 justify-self: anchor-center;
74 border: none;
75 border-radius: 1.25rem; /* 20px */
76 background: #f4f4f4;
77 padding: 0.15625rem 0; /* 2.5px */
78 width: 6.25rem;
79 }
80
81 .sbil_header_language_dropdown .form-control::picker-icon {
82 content: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='11' height='7' viewBox='0 0 11 7' fill='none'><path d='M10.8537 1.35372L5.85372 6.35372C5.80729 6.40021 5.75214 6.43709 5.69144 6.46225C5.63074 6.48742 5.56568 6.50037 5.49997 6.50037C5.43427 6.50037 5.3692 6.48742 5.3085 6.46225C5.2478 6.43709 5.19266 6.40021 5.14622 6.35372L0.146223 1.35372C0.0524025 1.2599 -0.000305176 1.13265 -0.000305176 0.999973C-0.000305176 0.867291 0.0524025 0.740043 0.146223 0.646223C0.240043 0.552402 0.367291 0.499695 0.499973 0.499695C0.632655 0.499695 0.759902 0.552402 0.853723 0.646223L5.49997 5.2931L10.1462 0.646223C10.1927 0.599767 10.2478 0.562917 10.3085 0.537776C10.3692 0.512635 10.4343 0.499695 10.5 0.499695C10.5657 0.499695 10.6307 0.512635 10.6914 0.537776C10.7521 0.562917 10.8073 0.599767 10.8537 0.646223C10.9002 0.692678 10.937 0.747828 10.9622 0.808525C10.9873 0.869221 11.0003 0.934275 11.0003 0.999973C11.0003 1.06567 10.9873 1.13072 10.9622 1.19142C10.937 1.25212 10.9002 1.30727 10.8537 1.35372Z' fill='%232A2076'/></svg>");
83 transition: 0.4s transform;
84 }
85
86 .sbil_header_language_dropdown .form-control:open::picker-icon {
87 transform-origin: center;
88 transform: scaleY(-1);
89 }
90
91 .sbil_header_language_dropdown .form-control option {
92 display: flex;
93 justify-content: flex-start;
94
95 font-size: 0.875rem; /* 14px */
96 border: 0.3125rem solid #f4f4f4; /* 5px */
97 border-top-width: 0.15625rem; /* 2.5px */
98 border-bottom-width: 0.15625rem; /* 2.5px */
99 border-radius: 1.25rem; /* 20px */
100 height: 1.875rem; /* 30px */
101 transition: 0.4s;
102 }
103
104 .sbil_header_language_dropdown .form-control option:hover {
105 cursor: pointer;
106 }
107
108 .sbil_header_language_dropdown .form-control option:hover,
109 .sbil_header_language_dropdown .form-control option:focus {
110 background: #dddbe8;
111 }
112
113 .sbil_header_language_dropdown .form-control option .icon {
114 font-size: 1.6rem;
115 text-box: trim-both cap alphabetic;
116 }
117
118 .sbil_header_language_dropdown .form-control option:checked {
119 font-weight: bold;
120 background:rgb(196, 191, 227);
121 }
122
123 .sbil_header_language_dropdown .form-control option::checkmark {
124 display: none;
125 }
126
127 @media screen and (min-width: 800px) and (max-width: 1048px) {
128 :root {
129 font-size: 75%;
130 }
131
132 .sbil_header_language_dropdown .form-control::picker-icon {
133 content: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='8.25' height='5.25' viewBox='0 0 11 7' fill='none'><path d='M10.8537 1.35372L5.85372 6.35372C5.80729 6.40021 5.75214 6.43709 5.69144 6.46225C5.63074 6.48742 5.56568 6.50037 5.49997 6.50037C5.43427 6.50037 5.3692 6.48742 5.3085 6.46225C5.2478 6.43709 5.19266 6.40021 5.14622 6.35372L0.146223 1.35372C0.0524025 1.2599 -0.000305176 1.13265 -0.000305176 0.999973C-0.000305176 0.867291 0.0524025 0.740043 0.146223 0.646223C0.240043 0.552402 0.367291 0.499695 0.499973 0.499695C0.632655 0.499695 0.759902 0.552402 0.853723 0.646223L5.49997 5.2931L10.1462 0.646223C10.1927 0.599767 10.2478 0.562917 10.3085 0.537776C10.3692 0.512635 10.4343 0.499695 10.5 0.499695C10.5657 0.499695 10.6307 0.512635 10.6914 0.537776C10.7521 0.562917 10.8073 0.599767 10.8537 0.646223C10.9002 0.692678 10.937 0.747828 10.9622 0.808525C10.9873 0.869221 11.0003 0.934275 11.0003 0.999973C11.0003 1.06567 10.9873 1.13072 10.9622 1.19142C10.937 1.25212 10.9002 1.30727 10.8537 1.35372Z' fill='%232A2076'/></svg>");
134 }
135 }
136</style>
137
138<script type="module">
139 (function () {
140 var mobileLanguageSelector = document.querySelector(".mobile_language_selector");
141 if (!mobileLanguageSelector) return;
142 var parentNavbar = mobileLanguageSelector.closest(".sbil-mobile-header-navbar");
143 if (!parentNavbar) return;
144
145 // ----- Build UI (no template literals; uses FreeMarker injections) -----
146 <#assign currentEntry = entries?filter(e -> e.isSelected())?first />
147
148 // create button
149 var collapseBtn = document.createElement("button");
150 collapseBtn.className = "mobile-language-selector-button";
151 collapseBtn.type = "button";
152 collapseBtn.setAttribute("data-toggle", "collapse");
153 collapseBtn.setAttribute("data-target", "#" + "${randomNamespace}" + "mobileLanguageCollapse");
154 collapseBtn.setAttribute("aria-expanded", "false");
155 collapseBtn.setAttribute("aria-controls", "${randomNamespace}" + "mobileLanguageCollapse");
156
157 var btnHtml = '<span>' + '${stringUtil.toUpperCase(currentEntry.getLongDisplayName())}' + '</span>';
158 btnHtml += '<svg fill="none" focusable="false" role="presentation">';
159 btnHtml += '<use xlink:href="' + '${themeDisplay.getPathThemeSpritemap()}' + '#sbil-mobile-header-drop-down-arrow' + '" />';
160 btnHtml += '</svg>';
161 collapseBtn.innerHTML = btnHtml;
162
163 // create collapse container
164 var collapseDiv = document.createElement("div");
165 collapseDiv.className = "collapse mobile-language-selector-collapse";
166 collapseDiv.id = "${randomNamespace}" + "mobileLanguageCollapse";
167 collapseDiv.setAttribute("aria-hidden", "true");
168 collapseDiv.setAttribute("role", "region");
169
170 // create list
171 var list = document.createElement("ul");
172 list.className = "mobile-language-list";
173
174 <#list entries as entry>
175 {
176 var li = document.createElement("li");
177 li.className = "mobile-language-list-item";
178
179 // --- Clean up home URLs like /home or /fr/home ---
180 var rawUrl = "${entry.getURL()!}";
181
182 var a = document.createElement("a");
183 a.href = rawUrl;
184 a.className = "taglib-language-option taglib-language-option-" + "${entry.getW3cLanguageId()}";
185 a.textContent = "${stringUtil.toUpperCase(entry.getLongDisplayName())}";
186 a.setAttribute("role", "link");
187 a.setAttribute("hreflang", "${entry.getW3cLanguageId()}");
188 <#if entry.isSelected()>
189 a.setAttribute("aria-current", "true");
190 a.classList.add("mobile-language-current");
191 </#if>
192
193 li.appendChild(a);
194 list.appendChild(li);
195 }
196 </#list>
197
198
199 collapseDiv.appendChild(list);
200
201 // insert into DOM
202 mobileLanguageSelector.innerHTML = "";
203 mobileLanguageSelector.appendChild(collapseBtn);
204 mobileLanguageSelector.appendChild(collapseDiv);
205
206 var $ = window.jQuery;
207
208 if ($ && collapseDiv && typeof $.fn.collapse === 'function') {
209 var $collapse = $("#" + collapseDiv.id);
210
211 $collapse.on('show.bs.collapse', function () {
212 parentNavbar.classList.add('mobile-selector-open');
213 collapseBtn.classList.add('open');
214 collapseDiv.setAttribute('aria-hidden', 'false');
215 var firstLink = collapseDiv.querySelector('a');
216 if (firstLink) firstLink.focus();
217 });
218
219 $collapse.on('hide.bs.collapse', function () {
220 parentNavbar.classList.remove('mobile-selector-open');
221 collapseDiv.setAttribute('aria-hidden', 'true');
222 collapseBtn.classList.remove('open');
223 collapseBtn.focus();
224 });
225
226 // optional: close on Escape and click-outside (namespaced)
227 $(document).on('keydown.mobileLangSelector', function (ev) {
228 if (ev.key === 'Escape' || ev.key === 'Esc') {
229 if ($collapse.hasClass('show')) {
230 $collapse.collapse('hide');
231 }
232 }
233 });
234
235 $(document).on('click.mobileLangSelector', function (ev) {
236 if ($collapse.hasClass('show') && !mobileLanguageSelector.contains(ev.target)) {
237 $collapse.collapse('hide');
238 }
239 });
240
241 // when a language link is clicked, let navigation happen but also hide the collapse (SPA-friendly)
242 $collapse.on('click', 'a', function () {
243 setTimeout(function () { $collapse.collapse('hide'); }, 50);
244 });
245
246 } else {
247 // Fallback: no jQuery/Bootstrap collapse - toggle the parent class on button click.
248 // This keeps behavior predictable even without Bootstrap JS.
249 collapseBtn.addEventListener('click', function (ev) {
250 // allow default Bootstrap handler if present; fallback toggles the class
251 if (parentNavbar.classList.contains('mobile-selector-open')) {
252 parentNavbar.classList.remove('mobile-selector-open');
253 collapseBtn.classList.remove('open'); // unflip
254 collapseDiv.classList.remove('show');
255 collapseDiv.setAttribute('aria-hidden', 'true');
256 collapseBtn.setAttribute('aria-expanded', 'false');
257 collapseBtn.focus();
258 } else {
259 parentNavbar.classList.add('mobile-selector-open');
260 collapseBtn.classList.add('open'); // flip
261 collapseDiv.classList.add('show');
262 collapseDiv.setAttribute('aria-hidden', 'false');
263 collapseBtn.setAttribute('aria-expanded', 'true');
264 // focus first link
265 var firstLink = collapseDiv.querySelector('a');
266 if (firstLink) firstLink.focus();
267 }
268 });
269
270 // hide when clicking a link
271 collapseDiv.addEventListener('click', function (ev) {
272 if (ev.target && ev.target.tagName === 'A') {
273 parentNavbar.classList.remove('mobile-selector-open');
274 collapseDiv.classList.remove('show');
275 collapseDiv.setAttribute('aria-hidden', 'true');
276 }
277 });
278 }
279
280 })();
281 (function () {
282 // Accessibility: Add aria-labels to language options and select element
283 var languageSelect = document.querySelector('.sbil_header_language_dropdown select[name="${randomNamespace + name}"]');
284 if (languageSelect) {
285 languageSelect.setAttribute('aria-label', 'Select Language');
286
287 var options = languageSelect.querySelectorAll('option');
288 options.forEach(function (option) {
289 option.setAttribute('aria-label', option.textContent.trim());
290 });
291 }
292 })();
293</script>
Description goes here