1document.addEventListener("DOMContentLoaded", () => { 2 /** 3 * --- Configuration & Selectors --- 4 */ 5 const SELECTORS = { 6 NAV_LINKS: '.t4-megamenu .nav-link, .t4-megamenu .dropdown-toggle, .t4-megamenu .dropdown-item', 7 LEVEL0: [ 8 '.t4-header-inner .t4-megamenu .navbar-nav.level0 > .nav-item > .nav-link:visible', 9 '.t4-header-inner .navbar-brand.logo-image > a:visible', 10 '.mod-languages .langChoose:visible', 11 ].join(','), 12 DROPDOWN_LINKS: '.t4-megamenu .dropdown-menu a', 13 DROPDOWN_LINKS_VISIBLE: '.t4-megamenu .dropdown-menu a:visible', 14 LEVEL1_DROPDOWNS: '.t4-megamenu .nav-item[data-level="1"] > .dropdown-menu', 15 LANG_MODULE: '.mod-languages', 16 }; 17 18 const KEYS = { 19 ARROW_RIGHT: 'ArrowRight', 20 ARROW_LEFT: 'ArrowLeft', 21 ARROW_UP: 'ArrowUp', 22 ARROW_DOWN: 'ArrowDown', 23 ESCAPE: 'Escape', 24 ENTER: 'Enter', 25 SPACE: ' ' 26 }; 27 28 /** 29 * --- Helper Functions --- 30 */ 31 32 const focusItem = ($items, index) => { 33 if (index >= 0 && index < $items.length) { 34 $items.eq(index).focus(); 35 } 36 }; 37 38 const getMenuItems = ($container) => $container.find('.dropdown-menu a').filter(':visible'); 39 40 const getFirstMenuItem = ($container) => getMenuItems($container).first(); 41 42 /** 43 * --- Logic & Initialization --- 44 */ 45 46 const ensureKeyboardAccessible = () => { 47 $(SELECTORS.NAV_LINKS).each((_, el) => { 48 const $el = $(el); 49 if ($el.is('a')) { 50 const href = $el.attr('href'); 51 const isPlaceholder = href === '#' || href === 'javascript:;' || !href || (href.indexOf('#') === 0 && href.length === 1); 52 53 if (isPlaceholder && !$el.attr('tabindex')) { 54 $el.attr('tabindex', '0'); 55 } 56 } 57 }); 58 }; 59 60 const setupLevel0Handlers = () => { 61 const $level0 = $(SELECTORS.LEVEL0); 62 const isRtl = document.documentElement.dir === "rtl" || $("html").attr("dir") === "rtl"; 63 64 $level0.off('keydown.level0').on('keydown.level0', (event) => { 65 const $current = $(event.currentTarget); 66 const currentIndex = $level0.index($current); 67 68 switch (event.key) { 69 case KEYS.ARROW_RIGHT: 70 focusItem($level0, isRtl ? currentIndex - 1 : currentIndex + 1); 71 break; 72 73 case KEYS.ARROW_LEFT: 74 focusItem($level0, isRtl ? currentIndex + 1 : currentIndex - 1); 75 break; 76 77 case KEYS.ARROW_UP: 78 event.preventDefault(); 79 event.stopPropagation(); 80 break; 81 82 case KEYS.ARROW_DOWN: 83 case KEYS.SPACE: 84 event.preventDefault(); 85 event.stopPropagation(); 86 87 if ($current.hasClass('dropdown-toggle')) { 88 const $megaItem = $current.parent(); 89 const focusFirst = () => { 90 const $firstChild = getFirstMenuItem($megaItem); 91 if ($firstChild.length) $firstChild.focus(); 92 }; 93 94 if ($megaItem.hasClass('show')) { 95 focusFirst(); 96 } else if (typeof T4MegaMenu !== 'undefined') { 97 T4MegaMenu.showDropdown($megaItem); 98 setTimeout(focusFirst, 150); 99 } 100 } 101 break; 102 } 103 }); 104 }; 105 106 const setupDropdownHandlers = () => { 107 $(SELECTORS.DROPDOWN_LINKS).off('keydown.inside-dropdown').on('keydown.inside-dropdown', (event) => { 108 const $current = $(event.currentTarget); 109 const $visibleLinks = $(SELECTORS.DROPDOWN_LINKS_VISIBLE); 110 const currentIndex = $visibleLinks.index($current); 111 112 switch (event.key) { 113 case KEYS.ARROW_DOWN: 114 event.preventDefault(); 115 event.stopPropagation(); 116 focusItem($visibleLinks, currentIndex + 1); 117 break; 118 119 case KEYS.ARROW_UP: 120 event.preventDefault(); 121 event.stopPropagation(); 122 focusItem($visibleLinks, currentIndex - 1); 123 break; 124 125 case KEYS.ESCAPE: { 126 event.preventDefault(); 127 event.stopPropagation(); 128 const $navItem = $current.parents('.nav-item[data-level="1"]').last(); 129 if ($navItem.length) $navItem.children('.dropdown-toggle').focus(); 130 break; 131 } 132 133 case KEYS.SPACE: 134 event.preventDefault(); 135 event.stopPropagation(); 136 break; 137 } 138 }); 139 140 $(SELECTORS.LEVEL1_DROPDOWNS).off('focusout.outside-dropdown').on('focusout.outside-dropdown', (event) => { 141 setTimeout(() => { 142 const $currentDropdown = $(event.currentTarget); 143 if (!$(':focus').closest($currentDropdown).length) { 144 if (typeof T4MegaMenu !== 'undefined') { 145 T4MegaMenu.hideDropdowns($currentDropdown.parent()); 146 } 147 } 148 }, 100); 149 }); 150 }; 151 152 const setupLanguageHandlers = () => { 153 const $langModules = $(SELECTORS.LANG_MODULE); 154 if (!$langModules.length) return; 155 156 $langModules.each((_, module) => { 157 const $langModule = $(module); 158 const $btn = $langModule.find('.langChoose').first(); 159 const $menu = $langModule.find('.lang-block, .lang-inline').first(); 160 const $links = $menu.find('a'); 161 let showTimer = null; 162 let hideTimer = null; 163 164 if (!$btn.length || !$menu.length) return; 165 166 $menu.hide(); 167 168 const clearTimers = () => { 169 if (showTimer) { 170 clearTimeout(showTimer); 171 showTimer = null; 172 } 173 if (hideTimer) { 174 clearTimeout(hideTimer); 175 hideTimer = null; 176 } 177 }; 178 179 const isFocusInsideModule = () => $(document.activeElement).closest($langModule).length > 0; 180 181 const showMenu = (focusFirst) => { 182 clearTimers(); 183 $menu.show(); 184 185 showTimer = setTimeout(() => { 186 $menu.addClass('displayed'); 187 188 if (focusFirst) { 189 $links.first().focus(); 190 } 191 }, 100); 192 }; 193 194 const hideMenu = (focusBack) => { 195 clearTimers(); 196 $menu.removeClass('displayed'); 197 198 if (focusBack) { 199 $btn.focus(); 200 } 201 202 hideTimer = setTimeout(() => { 203 $menu.hide(); 204 }, 100); 205 }; 206 207 $langModule.off('mouseenter.language-dropdown').on('mouseenter.language-dropdown', () => { 208 showMenu(); 209 }); 210 211 $langModule.off('mouseleave.language-dropdown').on('mouseleave.language-dropdown', () => { 212 // Keep menu open while keyboard focus is still inside the language module. 213 if (!isFocusInsideModule()) { 214 hideMenu(); 215 } 216 }); 217 218 $langModule.off('focusout.language-dropdown').on('focusout.language-dropdown', () => { 219 setTimeout(() => { 220 if (!isFocusInsideModule()) { 221 hideMenu(false); 222 } 223 }, 0); 224 }); 225 226 $btn.off('keydown.language-dropdown').on('keydown.language-dropdown', (event) => { 227 if (event.key === KEYS.ARROW_DOWN || event.key === KEYS.SPACE || event.key === KEYS.ENTER) { 228 event.preventDefault(); 229 event.stopPropagation(); 230 showMenu(true); 231 } 232 }); 233
234 $links.off('keydown.lang-link').on('keydown.lang-link', (event) => { 235 const $current = $(event.currentTarget); 236 const currentIndex = $links.index($current); 237 238 switch (event.key) { 239 case KEYS.ARROW_DOWN: 240 event.preventDefault(); 241 event.stopPropagation(); 242 focusItem($links, currentIndex + 1); 243 break; 244 245 case KEYS.ARROW_UP: 246 event.preventDefault(); 247 event.stopPropagation(); 248 focusItem($links, currentIndex - 1); 249 break; 250 251 case KEYS.ESCAPE: 252 hideMenu(true); 253 break; 254 255 case KEYS.SPACE: 256 event.preventDefault(); 257 event.stopPropagation(); 258 break; 259 } 260 }); 261 }); 262 }; 263 264 const init = () => { 265 setupDropdownHandlers(); 266 setupLanguageHandlers(); 267 ensureKeyboardAccessible(); 268 setupLevel0Handlers(); 269 }; 270 271 init(); 272});
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.