1/** 2 * NDS Partials Loader 3 * Fetches shared HTML partials and injects them into placeholder elements. 4 * After all partials are loaded, dispatches 'nds-partials-loaded' event 5 * and triggers NDS component re-initialization. 6 * 7 * Also provides fallback handlers for NDS.Mainnav features (dropdowns, 8 * digital stamp, mobile toggle) since that module caches DOM refs at 9 * parse time before partials are fetched. 10 */ 11(function () { 12 'use strict'; 13 14 // Determine base path from <base> tag or current page depth 15 var base = document.querySelector('base'); 16 var basePath = base ? base.getAttribute('href') : './'; 17 if (basePath.charAt(basePath.length - 1) !== '/') basePath += '/'; 18 19 var partials = [ 20 { id: 'nds-topbar-placeholder', file: '_partials/topbar.html' }, 21 { id: 'nds-mainnav-placeholder', file: '_partials/mainnav.html' }, 22 { id: 'nds-mainnav-embassy-placeholder', file: '_partials/mainnav-embassy.html' }, 23 { id: 'nds-sidemenu-placeholder', file: '_partials/sidemenu.html' }, 24 { id: 'nds-footer-placeholder', file: '_partials/footer.html' }, 25 { id: 'nds-cookie-placeholder', file: '_partials/cookie-popup.html' }, 26 // { id: 'nds-accessibility-placeholder', file: '_partials/accessibility-panel.html' }, 27 { id: 'nds-user-feedback-placeholder', file: '_partials/user-feedback.html' }, 28 { id: 'nds-hero-main-placeholder', file: '_partials/hero-main.html' }, 29 { id: 'nds-hero-sub-placeholder', file: '_partials/hero-sub.html' }, 30 { id: 'nds-embassies-finder-placeholder', file: '_partials/embassies-finder.html' }, 31 { id: 'nds-embassy-map-placeholder', file: '_partials/embassy-map.html' } 32 ]; 33 34 var promises = []; 35 36 partials.forEach(function (p) { 37 var el = document.getElementById(p.id); 38 if (!el) return; 39 40 var promise = fetch(basePath + p.file + '?v=' + Date.now()) 41 .then(function (res) { 42 if (!res.ok) throw new Error(p.file + ': ' + res.status); 43 return res.text(); 44 }) 45 .then(function (html) { 46 el.innerHTML = html; 47 var scripts = el.querySelectorAll('script'); 48 scripts.forEach(function (orig) { 49 var s = document.createElement('script'); 50 if (orig.type) s.type = orig.type; 51 if (orig.src) { s.src = orig.src; } 52 else { s.textContent = orig.textContent; } 53 orig.parentNode.replaceChild(s, orig); 54 }); 55 }) 56 .catch(function (err) { 57 console.warn('NDS partial load failed:', err.message); 58 }); 59 60 promises.push(promise); 61 }); 62 63 var partialsReady = Promise.all(promises); 64 var domReady = new Promise(function (resolve) { 65 if (document.readyState === 'loading') { 66 document.addEventListener('DOMContentLoaded', resolve); 67 } else { resolve(); } 68 }); 69 70 Promise.all([partialsReady, domReady]).then(function () { 71 setActiveSidemenu(); 72 setHeroSubData(); 73 ensureFontLoaded(); 74 75 document.dispatchEvent(new CustomEvent('nds-partials-loaded')); 76 77 // Re-initialize NDS components 78 if (typeof NDS !== 'undefined') { 79 if (NDS.Init && NDS.Init.reinitialize) { 80 NDS.Init.reinitialize(); 81 } else if (NDS.init) { 82 NDS.init(); 83 } 84 if (NDS.TimeDate && NDS.TimeDate.init) { 85 try { NDS.TimeDate.init(); } catch (e) { /* ignore */ } 86 } 87 } 88 89 revealNavElements(); 90 initDigitalStampToggle(); 91 initNavDropdowns(); 92 initMobileNavToggle(); 93 initMinimalNavMode(); 94 }); 95 96 // âââ Minimal nav mode (toggle body.nds-minimal based on viewport) ââ 97 function initMinimalNavMode() { 98 var mql = window.matchMedia('(max-width: 960px)');
99 function update() { 100 document.body.classList.toggle('nds-minimal', mql.matches); 101 } 102 if (mql.addEventListener) { 103 mql.addEventListener('change', update); 104 } else if (mql.addListener) { 105 mql.addListener(update); 106 } 107 update(); 108 } 109 110 // âââ Font loading fallback ââââââââââââââââââââââââââââââââââââ 111 function ensureFontLoaded() { 112 function markLoaded() { 113 var current = (document.body.getAttribute('data-font-loaded') || '').trim(); 114 if (current.indexOf('hgi-stroke-rounded') === -1) { 115 document.body.setAttribute('data-font-loaded', (current + ' hgi-stroke-rounded').trim()); 116 } 117 } 118 var current = document.body.getAttribute('data-font-loaded') || ''; 119 if (current.indexOf('hgi-stroke-rounded') !== -1) return; 120 if (document.fonts && document.fonts.ready) { 121 document.fonts.ready.then(function () { 122 try { if (document.fonts.check('1em "hgi-stroke-rounded"')) markLoaded(); } 123 catch (e) {} 124 }); 125 } 126 setTimeout(markLoaded, 3000); 127 } 128 129 // âââ Sidemenu active state ââââââââââââââââââââââââââââââââââââ 130 function setActiveSidemenu() { 131 var path = window.location.pathname; 132 var links = document.querySelectorAll('.nds-sidemenu a[href]'); 133 links.forEach(function (a) { 134 var href = a.getAttribute('href'); 135 if (path.indexOf(href) !== -1 || path.endsWith(href)) { 136 var li = a.closest('li'); 137 if (li) li.setAttribute('data-state', 'active'); 138 var parent = a.closest('.nds-sidemenu-group'); 139 while (parent) { 140 var toggle = parent.querySelector('.nds-sidemenu-toggle'); 141 if (toggle) toggle.setAttribute('aria-expanded', 'true'); 142 var content = parent.querySelector('.nds-sidemenu-content'); 143 if (content) content.style.display = 'block'; 144 parent = parent.parentElement ? parent.parentElement.closest('.nds-sidemenu-group') : null; 145 } 146 } 147 }); 148 } 149 150 // âââ Hero sub data ââââââââââââââââââââââââââââââââââââââââââââ 151 function setHeroSubData() { 152 var heroSub = document.getElementById('nds-hero-sub-placeholder'); 153 if (!heroSub) return; 154 var page = document.querySelector('[data-page-title]'); 155 if (!page) return; 156 var title = page.getAttribute('data-page-title'); 157 var desc = page.getAttribute('data-page-description'); 158 if (title) { 159 var h1 = heroSub.querySelector('.nds-section-title'); 160 if (h1) h1.textContent = title; 161 } 162 if (desc) { 163 var p = heroSub.querySelector('.nds-section-description'); 164 if (p) p.textContent = desc; 165 } 166 } 167 168 // âââ Reveal hidden nav elements âââââââââââââââââââââââââââââââ 169 function revealNavElements() { 170 var selectors = [ 171 '.nds-digitalStamp-tab[hidden]', 172 '.nds-nav-minimal[hidden]', 173 '.nds-collapse[hidden]', 174 '.nds-nav-primary[hidden]', 175 '.nds-nav-actions[hidden]' 176 ]; 177 selectors.forEach(function (sel) { 178 document.querySelectorAll(sel).forEach(function (el) { 179 el.removeAttribute('hidden'); 180 }); 181 }); 182 } 183 184 // âââ Digital stamp toggle âââââââââââââââââââââââââââââââââââââ 185 function initDigitalStampToggle() { 186 var tab = document.querySelector('.nds-digitalStamp-tab'); 187 var stamp = document.getElementById('nds-digitalStamp'); 188 if (!tab || !stamp) return; 189 if (tab.hasAttribute('data-nds-bound')) return; 190 tab.setAttribute('data-nds-bound', ''); 191 192 tab.addEventListener('click', function () { 193 var isOpen = hasState(stamp, 'open'); 194 if (isOpen) { 195 stamp.setAttribute('data-state', 'open closing'); 196 tab.setAttribute('aria-expanded', 'false'); 197 removeStateToken(tab, 'expanded'); 198 setTimeout(function () { 199 stamp.removeAttribute('data-state'); 200 stamp.style.display = 'none'; 201 }, 350); 202 } else { 203 closeAllDropdowns(); 204 stamp.removeAttribute('hidden'); 205 stamp.style.display = ''; 206 stamp.setAttribute('data-state', 'open opening'); 207 tab.setAttribute('aria-expanded', 'true'); 208 addStateToken(tab, 'expanded'); 209 setTimeout(function () { 210 stamp.setAttribute('data-state', 'open opened'); 211 }, 350); 212 } 213 }); 214 } 215 216 // âââ Nav dropdown handler (replaces NDS.Mainnav dropdown logic) ââ 217 function initNavDropdowns() { 218 // Use document-level event delegation (same pattern as NDS.Mainnav) 219 document.addEventListener('click', function (e) { 220 // Click on a nav dropdown trigger 221 var trigger = e.target.closest('.nds-main-nav .nds-dropdown > .nds-nav-link'); 222 if (trigger) { 223 e.preventDefault(); 224 var dd = trigger.closest('.nds-dropdown'); 225 var isOpen = hasState(dd, 'open'); 226 227 // Close all other open dropdowns first 228 closeAllDropdowns(dd); 229 230 if (isOpen) { 231 closeDropdown(dd); 232 } else { 233 openDropdown(dd); 234 } 235 return; 236 } 237 238 // Click on hamburger toggler 239 if (e.target.closest('.nds-mainNav-toggler')) { 240 e.preventDefault(); 241 return; // handled by initMobileNavToggle 242 } 243 244 // Click outside â close all open dropdowns 245 var openDDs = document.querySelectorAll('.nds-main-nav .nds-dropdown[data-state~="open"]'); 246 openDDs.forEach(function (dd) { 247 var menu = dd.querySelector('.nds-dropdown-menu'); 248 if (!dd.contains(e.target) && (!menu || !menu.contains(e.target))) { 249 closeDropdown(dd); 250 } 251 }); 252 253 // Close mobile nav if clicking outside 254 var collapse = document.getElementById('ndsNavCollapse'); 255 if (collapse && hasState(collapse, 'open')) { 256 var nav = document.querySelector('.nds-main-nav'); 257 if (nav && !nav.contains(e.target)) { 258 closeMobileNav(collapse); 259 } 260 } 261 }); 262 263 // Escape key closes dropdowns 264 document.addEventListener('keydown', function (e) { 265 if (e.key === 'Escape') { 266 closeAllDropdowns(); 267 var collapse = document.getElementById('ndsNavCollapse'); 268 if (collapse && hasState(collapse, 'open')) { 269 closeMobileNav(collapse); 270 } 271 } 272 }); 273 } 274 275 function openDropdown(dd) { 276 var link = dd.querySelector('.nds-nav-link'); 277 dd.setAttribute('data-state', 'open opening'); 278 if (link) addStateToken(link, 'active'); 279 setTimeout(function () { 280 dd.setAttribute('data-state', 'open opened'); 281 }, 300); 282 } 283 284 function closeDropdown(dd) { 285 var link = dd.querySelector('.nds-nav-link'); 286 dd.setAttribute('data-state', 'open closing'); 287 if (link) removeStateToken(link, 'active'); 288 setTimeout(function () { 289 dd.removeAttribute('data-state'); 290 }, 300); 291 } 292 293 function closeAllDropdowns(except) { 294 var openDDs = document.querySelectorAll('.nds-main-nav .nds-dropdown[data-state~="open"]'); 295 openDDs.forEach(function (dd) { 296 if (dd !== except) closeDropdown(dd); 297 }); 298 } 299 300 // âââ Mobile nav toggle (hamburger) ââââââââââââââââââââââââââââ 301 function initMobileNavToggle() { 302 var toggler = document.querySelector('.nds-mainNav-toggler button'); 303 var collapse = document.getElementById('ndsNavCollapse'); 304 if (!toggler || !collapse) return; 305 if (toggler.hasAttribute('data-nds-bound')) return; 306 toggler.setAttribute('data-nds-bound', ''); 307 308 toggler.addEventListener('click', function (e) { 309 e.preventDefault(); 310 var isOpen = hasState(collapse, 'open'); 311 if (isOpen) { 312 closeMobileNav(collapse); 313 toggler.setAttribute('aria-expanded', 'false'); 314 } else { 315 collapse.style.display = ''; 316 collapse.setAttribute('data-state', 'open opening'); 317 toggler.setAttribute('aria-expanded', 'true'); 318 setTimeout(function () { 319 collapse.setAttribute('data-state', 'open opened'); 320 }, 300); 321 } 322 }); 323 } 324 325 function closeMobileNav(collapse) { 326 collapse.setAttribute('data-state', 'open closing'); 327 var toggler = document.querySelector('.nds-mainNav-toggler button'); 328 if (toggler) toggler.setAttribute('aria-expanded', 'false'); 329 setTimeout(function () { 330 collapse.removeAttribute('data-state'); 331 }, 300); 332 } 333 334 // âââ State helpers ââââââââââââââââââââââââââââââââââââââââââââ 335 function hasState(el, token) { 336 var state = (el.getAttribute('data-state') || '').trim(); 337 return state.split(/\s+/).indexOf(token) !== -1; 338 } 339 340 function addStateToken(el, token) { 341 var current = (el.getAttribute('data-state') || '').trim(); 342 if (current.split(/\s+/).indexOf(token) === -1) { 343 el.setAttribute('data-state', (current + ' ' + token).trim()); 344 } 345 } 346 347 function removeStateToken(el, token) { 348 var current = (el.getAttribute('data-state') || '').trim(); 349 var parts = current.split(/\s+/).filter(function (t) { return t !== token; }); 350 if (parts.length) { 351 el.setAttribute('data-state', parts.join(' '));
352 } else { 353 el.removeAttribute('data-state'); 354 } 355 } 356})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.