1/* ========================================================================== 2 TuConnect · navigation.5d42c985.js 3 Header behaviour shared by every page. Handles mobile menu accessibility 4 (ARIA + Keyboard), active link verification across all product areas, 5 Light/Dark theme switching, and accessible marquee pause controls. 6 ========================================================================== */ 7(function () { 8 'use strict'; 9 10 /* -- Light / Dark Theme State Management ----------------------------- */ 11 function getSystemThemePreference() { 12 return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; 13 } 14 15 function getActiveTheme() { 16 var stored = localStorage.getItem('tuconnect-theme'); 17 if (stored === 'dark' || stored === 'light') return stored; 18 return getSystemThemePreference(); 19 } 20 21 function updateToggleLabels(theme) { 22 var buttons = document.querySelectorAll('.theme-toggle'); 23 Array.prototype.forEach.call(buttons, function (btn) { 24 var nextTheme = theme === 'dark' ? 'light' : 'dark'; 25 btn.setAttribute('aria-label', 'Switch to ' + nextTheme + ' theme'); 26 btn.setAttribute('title', 'Switch to ' + nextTheme + ' theme'); 27 }); 28 } 29 30 function swapLogos(theme) { 31 var navLogos = document.querySelectorAll('.nav-logo .logo-img'); 32 Array.prototype.forEach.call(navLogos, function (img) { 33 if (theme === 'dark') { 34 img.src = img.src.replace('logo.svg', 'logo-white.svg'); 35 } else { 36 img.src = img.src.replace('logo-white.svg', 'logo.svg'); 37 } 38 }); 39 } 40 41 function setTheme(theme, save) { 42 document.documentElement.setAttribute('data-theme', theme); 43 if (save) { 44 localStorage.setItem('tuconnect-theme', theme); 45 } 46 swapLogos(theme); 47 updateToggleLabels(theme); 48 } 49 50 // Initialize theme button event listeners 51 var currentTheme = getActiveTheme(); 52 setTheme(currentTheme, false); 53 54 var themeBtns = document.querySelectorAll('.theme-toggle'); 55 Array.prototype.forEach.call(themeBtns, function (btn) { 56 btn.addEventListener('click', function () { 57 var active = document.documentElement.getAttribute('data-theme') || getActiveTheme(); 58 var next = active === 'dark' ? 'light' : 'dark'; 59 setTheme(next, true); 60 }); 61 }); 62 63 // Listen for OS theme preference changes when no explicit override exists 64 if (window.matchMedia) { 65 window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function (e) { 66 if (!localStorage.getItem('tuconnect-theme')) { 67 setTheme(e.matches ? 'dark' : 'light', false); 68 } 69 }); 70 } 71 72 /* -- Mobile menu ----------------------------------------------------- */ 73 var toggle = document.getElementById('navToggle'); 74 var panel = document.getElementById('mobilePanel'); 75 76 if (toggle && panel) { 77 toggle.addEventListener('click', function () { 78 var open = toggle.getAttribute('aria-expanded') === 'true'; 79 var nextState = !open; 80 toggle.setAttribute('aria-expanded', String(nextState)); 81 panel.setAttribute('data-open', String(nextState)); 82 if (nextState) { 83 document.body.classList.add('nav-open'); 84 } else { 85 document.body.classList.remove('nav-open'); 86 } 87 }); 88 89 /* Escape closes and returns focus to the toggle. */ 90 document.addEventListener('keydown', function (e) { 91 if (e.key === 'Escape' && toggle.getAttribute('aria-expanded') === 'true') { 92 toggle.setAttribute('aria-expanded', 'false'); 93 panel.setAttribute('data-open', 'false'); 94 document.body.classList.remove('nav-open'); 95 toggle.focus(); 96 } 97 }); 98 } 99 100 /* -- Active Navigation State Management ------------------------------ */ 101 var rawPath = window.location.pathname; 102 var cleanPath = rawPath.replace(/\/$/, ''); 103 var segment = cleanPath.substring(cleanPath.lastIndexOf('/') + 1) || 'index.html'; 104 105 var currentFile = segment; 106 if (currentFile !== '' && currentFile !== 'index.html' && !currentFile.endsWith('.html')) { 107 currentFile += '.html'; 108 } 109 110 var activeHref = ''; 111 if (currentFile === '' || currentFile === 'index.html') { 112 activeHref = 'index.html'; 113 } else if ([ 114 'for-job-seekers.html', 115 'ai-resume-builder.html', 116 'ai-cover-letter.html', 117 'job-tracker.html', 118 'ai-career-copilot.html', 119 'localized-career-guidance.html', 120 'interview-preparation.html' 121 ].indexOf(currentFile) !== -1) { 122 activeHref = 'for-job-seekers.html'; 123 } else if (currentFile === 'for-employers.html') { 124 activeHref = 'for-employers.html'; 125 } else if (currentFile === 'resources.html') { 126 activeHref = 'resources.html'; 127 } 128 129 if (activeHref) { 130 var allNavLinks = document.querySelectorAll('.nav-links .nav-link, .mobile-panel .nav-link'); 131 Array.prototype.forEach.call(allNavLinks, function (link) { 132 var href = link.getAttribute('href'); 133 if (href === activeHref) { 134 link.classList.add('is-active'); 135 if (link.closest && link.closest('.nav-links')) { 136 link.setAttribute('aria-current', 'page'); 137 } 138 } 139 }); 140 } 141 142 /* -- Marquee pause (WCAG 2.2.2: any motion over 5s needs a control) --- */ 143 Array.prototype.forEach.call(document.querySelectorAll('.marquee'), function (m) { 144 var btn = m.querySelector('.marquee__pause'); 145 if (!btn) return; 146 btn.addEventListener('click', function () { 147 var paused = m.getAttribute('data-paused') === 'true'; 148 m.setAttribute('data-paused', String(!paused)); 149 btn.setAttribute('aria-pressed', String(!paused)); 150 var lbl = btn.querySelector('.visually-hidden'); 151 if (lbl) lbl.textContent = paused ? 'Pause scrolling logos' : 'Re
151sume scrolling logos'; 152 }); 153 }); 154})();
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.