1let retryCount = 0; 2const maxRetries = 5; // Maximum number of retries 3const retryInterval = 3000; // Retry interval in ms (3 seconds) 4 5function isFormLoaded() { 6 // Check if .EPiServerForms elements exist 7 const epiForms = document.querySelectorAll('.EPiServerForms'); 8 if (epiForms.length > 0) { 9 loadCustomStyles(); 10 } else { 11 if (retryCount < maxRetries) { 12 retryCount++; 13 setTimeout(isFormLoaded, retryInterval); 14 } else { 15 console.log(`No .EPiServerForms elements found after ${maxRetries * retryInterval / 1000} seconds. Stopping retry attempts.`); 16 } 17 } 18} 19 20function loadCustomStyles() { 21 document.querySelectorAll('input:not([type="radio"]):not([type="checkbox"]), select,textarea').forEach(el => { 22 el.classList.add('form-control'); 23 }); 24 25 document.querySelectorAll('.EPiServerForms button').forEach(el => { 26 el.classList.add('btn'); 27 el.classList.add('btn-outline-secondary'); 28 }) 29 document.querySelectorAll('.FormDateTime__Input').forEach(el => { 30 el.classList.add('w-100'); 31 }) 32 document.querySelectorAll('.FormCaptcha').forEach(el => { 33 const button = el.querySelector('.FormCaptcha__Refresh'); 34 const img = el.querySelector('.FormCaptcha__Image'); 35 36 if (button && img) { 37 // Move the button after the image 38 img.insertAdjacentElement('afterend', button); 39 img.style.marginRight = '0.375rem'; 40 img.style.marginBottom = '0.375rem'; 41 } 42 }) 43 document.querySelectorAll('.FormCaptcha .Form__Element__Caption').forEach(el => { 44 el.style.display = 'block'; 45 }) 46 document.querySelectorAll('.FormCaptcha button').forEach(el => { 47 el.setAttribute('aria-label', 'refresh captcha') 48 el.innerHTML = '<span class="ca-gov-icon-update" aria-hidden="true"></span>' 49 el.style.borderRadius = '50%'; 50 }) 51 52 document.querySelectorAll('.FormCaptcha input').forEach(el => { 53 el.style.verticalAlign = 'bottom'; 54 }) 55} 56 57// Initial check when DOM content is fully loaded 58document.addEventListener('DOMContentLoaded', function () { 59 isFormLoaded(); 60}); 61 62((($) => { 63 'use strict'; 64 /**----------------------------------------------------------------- 65 * UTILITY HEADER 66 * 67 * Note: utility-header.js does not modify objects in the DOM by adding 68 * classes or other markup other than the following two things: 69 * 70 * 1. Setting aria-expanded on any drawer, and any button that has a 71 * drawer, depending upon whether the drawer is expanded or not. 72 * 73 * 2. Changing the tabindex to 0 if a tabable item is 74 * focused, or -1 if not (i.e. - "roving tab index"). This feature 75 * currently disabled (ie - commented out). 76 * 77 * Author <Muir Adams - SymSoft Solutions> 78 -----------------------------------------------------------------*/ 79 80 function utilityHeader() { 81 // JQuery DOM objects 82 const $utilityHeader = $('.utility-header'); 83 const $menuItems = $utilityHeader 84 .find('.utility-header__drawer-toggle, .settings-links a, .header-cagov-logo a'); 85 const $menuItemsWithDrawers = $utilityHeader.find('.utility-header__drawer-toggle'); 86 const $drawers = $('.utility-header__drawer'); 87 const $drawersClose = $('.utility-header__drawer-close'); 88 const elementsThatReceiveFocus = 'a, button, input, select, textarea, [tabindex="0"]'; 89 const $allDrawersTabContents = $drawers.find(elementsThatReceiveFocus); 90 91 // Conditional variables (true/false) 92 const isTouch = Modernizr.touchevents; 93 94 function isScreenXS() { 95 return window.innerWidth; 96 } 97 98 function isSubMenuOpen($toggle) { 99 return $toggle.attr('aria-expanded') === 'true'; 100 } 101 102 function doesItHaveADrawer($menuItem) { 103 return $menuItem.hasClass('utility-header__drawer-toggle'); 104 } 105 106 function openSubMenu($toggle) { 107 const drawerId = $toggle.attr('aria-controls'); 108 // const $drawer = $utilityHeader.find(`#${drawerId}`); 109 const $drawer = $drawers.filter(`#${drawerId}`); 110 111 $toggle.attr('aria-expanded', 'true'); 112 $drawer.attr('aria-expanded', 'true'); 113 114 // unsetTabIndex($toggle); 115 setTabIndexToDrawersFirstTabContent($toggle); 116 } 117 118 function closeSubMenu($toggle) { 119 const drawerId = $toggle.attr('aria-controls'); 120 // const $drawer = $utilityHeader.find(`#${drawerId}`);
121 const $drawer = $drawers.filter(`#${drawerId}`); 122 123 $toggle.attr('aria-expanded', 'false'); 124 $drawer.attr('aria-expanded', 'false'); 125 126 unsetTabIndexToDrawersFirstTabContent($toggle); 127 // setTabIndex($toggle); 128 } 129 130 function closeOpenSubMenus($toggle) { 131 $menuItemsWithDrawers.each(function (index, toggle) { 132 const $toggle = $(toggle); 133 134 if (isSubMenuOpen($toggle)) closeSubMenu($toggle); 135 }); 136 137 // Put the focus back on whichever toggle was sent as a parameter 138 // $toggle.focus(); 139 } 140 141 function setTabIndex($menuItem) { 142 $menuItem.attr('tabindex', '0'); 143 return $menuItem; 144 } 145 146 function unsetTabIndex($menuItem) { 147 $menuItem.attr('tabindex', '-1'); 148 return $menuItem; 149 } 150 151 function findDrawerToggle($drawer) { 152 return $utilityHeader.find(`[aria-controls=${$drawer.attr('id')}]`); 153 } 154 155 function findDrawerTabContents($drawer) { 156 return $drawer.find('a, button, input, select, textarea, [tabindex="0"]'); 157 } 158 159 function setTabIndexToDrawersFirstTabContent($toggle) { 160 const drawerId = $toggle.attr('aria-controls'); 161 // const $drawer = $utilityHeader.find(`#${drawerId}`); 162 const $drawer = $drawers.filter(`#${drawerId}`); 163 164 const $drawerTabContents = findDrawerTabContents($drawer); 165 166 if ($drawerTabContents.length > 0) setTabIndex($($drawerTabContents[0])).focus(); 167 } 168 169 function unsetTabIndexToDrawersFirstTabContent($toggle) { 170 const drawerId = $toggle.attr('aria-controls'); 171 // const $drawer = $utilityHeader.find(`#${drawerId}`); 172 const $drawer = $drawers.filter(`#${drawerId}`); 173 174 const $drawerTabContents = findDrawerTabContents($drawer); 175 176 if ($drawerTabContents.length > 0) unsetTabIndex($drawerTabContents); 177 } 178 179 // Set tabindex of all menuItems - first item is 0, and the rest are -1 for roving tab index. 180 // $menuItems.each(function(index, menuItem) { 181 // const $menuItem = $(menuItem); 182 183 // if (index === 0) { 184 // setTabIndex($menuItem); 185 // } else { 186 // unsetTabIndex($menuItem); 187 // } 188 // }); 189 190 // Set tabindex of all drawer elements to -1 for roving tab index. 191 // unsetTabIndex($allDrawersTabContents); 192 193 /**----------------------------------------------------------------- 194 * 195 * MOUSE & TOUCH EVENTS 196 * 197 -----------------------------------------------------------------*/ 198 199 function closeMenuOnClickOutside(event) { 200 const $clickTarget = $(event.target); 201 const isClickedOutsideUtilityHeader = $clickTarget.closest('.utility-header__drawer').length === 0; 202 const isClickedOutsideUtilityHeaderButton = $clickTarget.closest('.utility-header__drawer-toggle[aria-expanded="true"]').length === 0; 203 204 if (isClickedOutsideUtilityHeader && isClickedOutsideUtilityHeaderButton) { 205 const $openMenuToggle = $menuItemsWithDrawers.filter('[aria-expanded="true"]'); 206 207 // Is there a menu toggle that is expanded 208 if ($openMenuToggle.length > 0) { 209 closeSubMenu($openMenuToggle); 210 211 // If user didn't click on an element that can receive focus, then put the focus 212 // on the toggle of the menu that was just closed. 213 if (!$clickTarget.is(elementsThatReceiveFocus) && 214 $clickTarget.closest(elementsThatReceiveFocus).length === 0) { 215 216 $openMenuToggle.focus(); 217 } 218 } 219 } 220 } 221 222 // Toggle the menu when user clicks the toggle button 223 $menuItemsWithDrawers.on('click', function (event) { 224 const $toggle = $(this); 225 226 if (!isSubMenuOpen($toggle)) { 227 closeOpenSubMenus($toggle); 228 openSubMenu($toggle); 229 } else { 230 closeSubMenu($toggle); 231 $toggle.focus(); 232 } 233 234 event.preventDefault(); 235 }); 236 237 // Close button 238 $drawersClose.on('click', function (event) { 239 const $toggle = $('.utility-header__drawer-toggle[aria-expanded="true"]'); 240 closeOpenSubMenus($toggle); 241 242 // Allows time for toggle's aria-expanded to change before focusing 243 setTimeout(() => { 244 $toggle.focus(); 245 }, 300); 246 }); 247 248 // Toggle tabindex when user clicks on a menu item 249 // $menuItems.on('click', function(event) { 250 // $menuItems.each(function(index, menuItem) { 251 // if (menuItem !== event.target) { 252 // unsetTabIndex($(menuItem)); 253 // } 254 // }); 255 // }); 256 257 // Close menu if user clicked/touched outside the menu 258 if (isTouch) { 259 // iOS scrolling fires touchstart --> touchmove --> touchend, 260 // and we need to be sure we're not registering a scroll as a click. 261 let documentClick; 262 263 $(document).on('touchstart', function () { 264 documentClick = true; 265 }); 266 267 $(document).on('touchmove', function () { 268 documentClick = false; 269 }); 270 271 $(document).on('click touchend', function (event) { 272 if (event.type == "click") documentClick = true; 273 if (documentClick) { 274 closeMenuOnClickOutside(event); 275 } 276 }); 277 } else { 278 $(document).on('click', function (event) { 279 closeMenuOnClickOutside(event); 280 }); 281 } 282 283 /**----------------------------------------------------------------- 284 * 285 * KEYBOARD EVENTS 286 * 287 -----------------------------------------------------------------*/ 288 289 // Keycodes 290 const enterKey = 13; 291 const spaceKey = 32; 292 const escKey = 27; 293 const tabKey = 9; 294 const downArrowKey = 40; 295 const upArrowKey = 38; 296 const leftArrowKey = 37; 297 const rightArrowKey = 39; 298 const homeKey = 36; 299 const endKey = 35; 300 301 function moveFocusToFirstItem($tabItems) { 302 const $firstTabItem = $($tabItems[0]); 303 304 // Move to the first navItem 305 setTabIndex($firstTabItem).focus(); 306 } 307 308 function moveFocusToLastItem($tabItems) { 309 const $lastTabItem = $($tabItems[$tabItems.length - 1]); 310 311 // Move to the last navItem 312 setTabIndex($lastTabItem).focus(); 313 } 314 315 function moveFocusToNextItem($tabItems, event) { 316 $tabItems.each(function (index, tabItem) { 317 318 if (tabItem === event.target) { 319 const $tabItem = $(tabItem); 320 unsetTabIndex($tabItem); 321 322 // If it's the last navItem then... 323 if (index === $tabItems.length - 1) { 324 moveFocusToFirstItem($tabItems); 325 } else { 326 const $nextTabItem = $($tabItems[index + 1]); 327 328 // Move tabindex to the next navItem 329 setTabIndex($nextTabItem).focus(); 330 } 331 } 332 }); 333 334 // Prevents scrolling when down arrow key is pressed 335 event.preventDefault(); 336 } 337 338 function moveFocusToPreviousItem($tabItems, event) { 339 $tabItems.each(function (index, tabItem) { 340 341 if (tabItem === event.target) { 342 const $tabItem = $(tabItem); 343 unsetTabIndex($tabItem); 344 345 // If it's the first navItem then... 346 if (index === 0) { 347 moveFocusToLastItem($tabItems); 348 } else { 349 const $prevTabItem = $($tabItems[index - 1]); 350 // Move tabindex to the previous navItem 351 setTabIndex($prevTabItem).focus(); 352 } 353 } 354 }); 355 356 // Prevents scrolling when down arrow key is pressed 357 event.preventDefault(); 358 } 359 360 function moveFocusToNextItemThatMatchesCharacter($tabItems, event) { 361 $tabItems.each(function (index, tabItem) { 362 if (tabItem === event.target) { // Find the currently focused item 363 const $tabItem = $(tabItem); 364 365 for (let i = 1; i < $tabItems.length + 1; i++) {
366 const indexToSearch = (index + i) % $tabItems.length; 367 const $thisTabItem = $($tabItems[indexToSearch]); 368 const firstLetter = $thisTabItem.text().replace(/[^0-9a-z]/gi, '').charCodeAt(0); 369 const pressedKey = event.which; 370 371 if (pressedKey === firstLetter) { 372 unsetTabIndex($tabItem); 373 setTabIndex($thisTabItem).focus(); 374 break; 375 } 376 } 377 } 378 }); 379 } 380 381 // Utility Header Menu Items 382 $menuItems.on('keydown', function (event) { 383 const $thisMenuItem = $(this); 384 const pressedKey = event.which; 385 386 // Move between menu items 387 // if(!isSubMenuOpen($thisMenuItem)) { 388 // if (pressedKey === homeKey) { 389 // unsetTabIndex($thisMenuItem); 390 // moveFocusToFirstItem($menuItems); 391 // event.preventDefault(); 392 // } 393 394 // if (pressedKey === endKey) { 395 // unsetTabIndex($thisMenuItem); 396 // moveFocusToLastItem($menuItems); 397 // event.preventDefault(); 398 // } 399 400 // // Moves focus to the next item having a name that starts with the typed character 401 // if ((pressedKey >= 48 && pressedKey <= 57) || // 0 - 9 402 // (pressedKey >= 65 && pressedKey <= 90)) { // a - z 403 404 // moveFocusToNextItemThatMatchesCharacter($menuItems, event); 405 // } 406 407 // // Move tabindex down the list 408 // if (pressedKey === rightArrowKey) { 409 // moveFocusToNextItem($menuItems, event); 410 // } 411 412 // // Move tabindex up the list 413 // if (pressedKey === leftArrowKey) { 414 // moveFocusToPreviousItem($menuItems, event); 415 // } 416 417 // if (pressedKey === downArrowKey || 418 // pressedKey === upArrowKey) event.preventDefault(); 419 // } 420 421 // Open and close menu items with drawers 422 if (doesItHaveADrawer($thisMenuItem)) { 423 const $toggle = $thisMenuItem; 424 425 if (pressedKey === spaceKey || 426 pressedKey === enterKey) { 427 428 if (!isSubMenuOpen($thisMenuItem)) { 429 closeOpenSubMenus($toggle); 430 openSubMenu($toggle); 431 } else { 432 closeSubMenu($toggle); 433 } 434 435 event.preventDefault() 436 } 437 438 if (pressedKey === escKey && isSubMenuOpen($thisMenuItem)) { 439 closeSubMenu($toggle); 440 $toggle.focus(); 441 } 442 443 if (pressedKey === tabKey && isSubMenuOpen($thisMenuItem)) { 444 event.preventDefault(); 445 } 446 } 447 }); 448 449 // Drawers 450 $drawers.on('keydown', function (event) { 451 const $thisDrawer = $(this); 452 const $thisToggle = findDrawerToggle($thisDrawer); 453 const pressedKey = event.which; 454 455 if (pressedKey === escKey) { 456 closeSubMenu($thisToggle); 457 458 // Allows time for toggle's aria-expanded to change before focusing 459 setTimeout(() => { 460 $thisToggle.focus(); 461 }, 300); 462 } 463 464 if (pressedKey === tabKey) { 465 event.preventDefault(); 466 } 467 }); 468 469 // Drawers' tabable content 470 $allDrawersTabContents.on('keydown', function (event) { 471 const $thisContent = $(this); 472 const $thisDrawer = $thisContent.closest('.utility-header__drawer'); 473 const $thisDrawerTabContent = findDrawerTabContents($thisDrawer); 474 const pressedKey = event.which; 475 476 if (pressedKey === tabKey) { 477 if (event.shiftKey) { 478 unsetTabIndex($thisContent); 479 moveFocusToPreviousItem($thisDrawerTabContent, event) 480 event.preventDefault(); 481 } else { 482 unsetTabIndex($thisContent); 483 moveFocusToNextItem($thisDrawerTabContent, event) 484 event.preventDefault(); 485 } 486 } 487 }); 488 } 489 490 // Requires on load because Google Translate markup is loaded via JS 491 $(window).on('load', function () { 492 utilityHeader(); 493 }); 494})(jQuery));
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.