1function inMobileMode() { 2 return $("#mobilenav").is(":visible"); 3} 4 5$(document).ready(function () { 6 7 8 let navContainer = document.getElementById( 'navContainer' ); 9 10 // Event listener for nav buttons 11 document.querySelectorAll( '#mainnav>ul>li>button' ).forEach( el => { 12 el.addEventListener( 'click', (e) => { 13 e.preventDefault(); 14 15 let parent = el.parentNode; 16 let dropdown = el.nextElementSibling; 17 let is_mobile = inMobileMode(); 18 19 // Toggle open/expand based on mode 20 if ( is_mobile ) { 21 parent.classList.toggle( 'open' ); 22 } else { 23 parent.classList.toggle( 'expand' ); 24 } 25 26 // Update aria-expanded 27 const expanded = parent.classList.contains('open') || parent.classList.contains('expand'); 28 el.setAttribute( 'aria-expanded', expanded ); 29 30 // Handle dropdown behavior (only in mobile mode) 31 if ( is_mobile && dropdown ) { 32 if ( !parent.classList.contains( 'open' ) ) { 33 // Closing 34 dropdown.inert = true; 35 } else { 36 // Opening 37 dropdown.inert = false; 38 } 39 } 40 41 }); 42 }); 43 44 45 // Close dropdown when it loses focus 46 document.querySelectorAll( '#mainnav>ul>li.menu-item-has-children' ).forEach( el => { 47 el.querySelector( 'a' ).addEventListener( 'focus', e => { 48 if ( !inMobileMode() ) { 49 el.classList.remove( 'expand' ); 50 el.querySelector( 'button' ).setAttribute( 'aria-expanded', false ); 51 } 52 }); 53 54 el.querySelector( 'div' ).addEventListener( 'focusout', function (e) { 55 if ( !this.contains(e.relatedTarget) && !inMobileMode() ) { 56 el.classList.remove( 'expand' ); 57 el.querySelector( 'button' ).setAttribute( 'aria-expanded', false ); 58 } 59 }); 60 }); 61 62 63 64 // Event listener for slideNav toggles (mobile only) 65 document.querySelectorAll( '.toggleNav' ).forEach( menu => { 66 menu.addEventListener( 'click', () => { 67 ws_toggleNav(); 68 }); 69 }); 70 71 72 73 // Toggle open/close the slideNav and set accessibility properties 74 function ws_toggleNav() { 75 document.body.classList.toggle( 'slideNav' ); 76 let expanded = document.body.classList.contains( 'slideNav' ); 77 78 let main = document.querySelector( 'main' ); 79 let footer = document.querySelector( 'body>footer' ); 80 let skipnav = document.getElementById( 'skipNavigation' ); 81 82 if ( expanded ) { 83 main.inert = true; 84 footer.inert = true; 85 skipnav.inert = true; 86 navContainer.inert = false; 87 navContainer.setAttribute( 'aria-modal', true ); 88 } else { 89 main.inert = false; 90 footer.inert = false; 91 skipnav.inert = false; 92 navContainer.inert = true; 93 navContainer.setAttribute( 'aria-modal', false ); 94 } 95 96 document.querySelectorAll( '.toggleNav' ).forEach( button => { 97 button.setAttribute( 'aria-expanded', expanded ? 'true' : 'false' ); 98 }); 99 100 // Focus management with 250ms delay 101 setTimeout( () => { 102 if ( expanded ) { 103 navContainer.querySelector( 'button.toggleNav' ).focus(); 104 } else { 105 document.querySelector( '#mobilenav button.toggleNav' ).focus(); 106 } 107 }, 250); 108 } 109 110 111 112 // Close slidenav with ESC key 113 navContainer.addEventListener( 'keydown', function(event) { 114 if ( event.key === 'Escape' || event.key === 'Esc' && inMobileMode() ) { 115 ws_toggleNav(); 116 } 117 }); 118 119 120 121 // Close slideNav when clicked outside navContainer 122 document.addEventListener( 'click', check_target, false ); 123 document.addEventListener( 'touchstart', check_target, { passive: true, capture: false } ); 124 function check_target(event) { 125 if ( document.body.classList.contains( 'slideNav' ) && 126 !document.querySelector( '#navContainer' ).contains( event.target ) && 127 !document.querySelector( '#mobilenav .toggleNav' ).contains( event.target ) ) { 128 ws_toggleNav(); 129 } 130 } 131 132 133 134 // Trap focus within navContainer 135 navContainer.addEventListener( 'focusout', e => { 136 if ( inMobileMode() && !navContainer.contains( e.relatedTarget ) ) { 137 navContainer.querySelector( 'button.toggleNav' ).focus(); 138 } 139 }); 140 141 142 143 // Expand current page / current page ancestor T1 items in mobile nav 144 document.querySelectorAll( '#navContainer li.on' ).forEach( open_nav => { 145 open_nav.classList.add( 'open' ); 146 var dropdown = open_nav.querySelector( 'div' ); 147 if ( dropdown ) { 148 dropdown.style.height = 'auto'; 149 dropdown.inert = false;
150 dropdown.previousElementSibling.setAttribute( 'aria-expanded', true ); 151 } 152 }); 153 154 155 156 // Set accessibility properties (on pageLoad and after window resize) 157 function ws_set_navigation_attributes() { 158 if ( inMobileMode() ) { 159 navContainer.role = 'dialog'; 160 navContainer.inert = true; 161 navContainer.querySelectorAll( 'li.menu-item-has-children:not(.open)>div' ).forEach( item => { 162 item.inert = true; 163 }); 164 } else { 165 navContainer.inert = false; 166 navContainer.querySelectorAll( 'li.menu-item-has-children>div' ).forEach( item => { 167 item.inert = false; 168 }); 169 } 170 } 171 172 ws_set_navigation_attributes(); // on pageLoad 173 174 175 176 // Stop animations during window resize event / maybe update navigation attributes 177 var timer; 178 var was_mobile_mode = inMobileMode(); // Mobile state on page load 179 180 window.addEventListener( 'resize', () => { 181 document.body.classList.add( 'stop-animations' ); 182 clearTimeout(timer); 183 timer = setTimeout(() => { 184 document.body.classList.remove( 'stop-animations' ); 185 186 // Reset navigation attributes if the mode changed (mobile <-> desktop) 187 let is_mobile_mode = inMobileMode(); 188 if ( is_mobile_mode !== was_mobile_mode ) { 189 was_mobile_mode = is_mobile_mode; 190 ws_set_navigation_attributes(); 191 } 192 193 }, 250); 194 }, true); 195 196 197 198 // Toggle 'scroll' class on body when page is scrolled down 199 window.addEventListener( 'scroll', () => { 200 if (window.scrollY > 0) { 201 document.body.classList.add( 'scroll' ); 202 } else { 203 document.body.classList.remove( 'scroll' ); 204 } 205 }, { passive: true } ); 206 207 208 209 // Determine if using a touch device 210 function is_touch_device() { 211 var prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); 212 var mq = function (query) { 213 return window.matchMedia(query).matches; 214 } 215 if (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints > 0) || window.DocumentTouch && document instanceof DocumentTouch) { 216 return true; 217 } 218 var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); 219 return mq(query); 220 } 221 222 223 // On touch devices, expand dropdowns on first tap 224 // Make sure selector is added in stylesheet 225 if ( is_touch_device() ) { 226 document.querySelectorAll( '#mainnav>ul>li>a' ).forEach( elem => { 227 elem.addEventListener('click', (e) => { 228 if ( !inMobileMode() ) { 229 var parent = elem.parentElement; 230 if ( parent.contains( parent.querySelector( 'ul' ) ) ) { 231 if ( !parent.classList.contains( 'tapped' ) ) { 232 e.preventDefault(); 233 document.querySelector( '#mainnav>ul>li.tapped' )?.classList.remove( 'tapped' ); 234 parent.classList.add( 'tapped' ); 235 } 236 } 237 } 238 }); 239 }); 240 } 241 242 243 244 245 $('a[href*="#"]:not([href="#"])').click(function (e) { 246 if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { 247 var target = $(this.hash); 248 target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); 249 if (target.length) { 250 e.preventDefault(); 251 $('html,body').animate({ 252 scrollTop: target.offset().top - $("body>header").height() 253 }, 1000, function () { 254 // Callback after animation, Must change focus! 255 var $target = $(target); 256 $target.focus(); 257 if ($target.is(":focus")) { // Checking if the target was focused 258 return false; 259 } else { 260 $target.attr('tabindex', '-1'); // Adding tabindex for elements not focusable 261 $target.focus(); // Set focus again 262 } 263 }); 264 } 265 } 266 }); 267 268 269 270 // 271 // SEARCH BUTTON 272 // 273 274 document.querySelectorAll( '.search-toggle' ).forEach( toggle => { 275 toggle.addEventListener( 'click', (e) => { 276 e.preventDefault(); 277 var search_form = document.getElementById( 'search-form' ); 278 var main_nav = document.getElementById( 'mainnav' ); 279 280 if( !search_form.classList.contains( 'show' ) ) { 281 search_form.classList.add('show'); 282 main_nav.classList.add('hide'); 283 setTimeout( () => { document.getElementById( 'search-terms' ).focus(); }, 150); 284 } else { 285 search_form.classList.remove( 'show' ); 286 main_nav.classList.remove( 'hide' ); 287 setTimeout( () => { document.getElementById( 'search-button' ).focus(); }, 150); 288 } 289 }); 290 }); 291 292 293 294 // 295 // STICKY HEADER 296 // 297 var buffer = 100, alertBarHeight, body, delta, distanceFromTop, firstScroll, header, headerHeight, headerTopOffset, initialHeaderOffset, partialScrollDown, partialScrollUp, prevScrollPosition, scrollPosition, topPosition, wpAdminBarHeight, wsMgmtBarHeight; 298 299 alertBarHeight = wpAdminBarHeight = wsMgmtBarHeight = 0; 300 firstScroll = partialScrollDown = partialScrollUp = false;
301 302 body = $('body'); 303 header = $('body>header:first'); 304 305 prevScrollPosition = window.pageYOffset; 306 307 if ($(window).scrollTop() == 0) 308 firstScroll = true; 309 310 $(window).scroll(function() { 311 if($(document).outerHeight() > 1500 && !inMobileMode()) { 312 313 scrollPosition = $(this).scrollTop(); 314 315 if ($('#alertApp').length) 316 alertBarHeight = $('#alertApp').outerHeight(); 317 if ($('#wpadminbar').length) 318 wpAdminBarHeight = $('#wpadminbar').outerHeight(); 319 if ($('#wsManagementBar').length) 320 wsMgmtBarHeight = $('#wsManagementBar').outerHeight(); 321 322 headerHeight = header.outerHeight(); 323 delta = headerHeight + alertBarHeight; 324 initialHeaderOffset = alertBarHeight; 325 topPosition = 0 + wpAdminBarHeight + wsMgmtBarHeight; 326 327 if (prevScrollPosition > scrollPosition) { 328 partialScrollUp = true; 329 if (!partialScrollDown) 330 header.addClass("sticky-header").css({"top": headerTopOffset}); 331 332 var headerPosition = header.offset().top - $(this).scrollTop(); 333 if (headerPosition >= topPosition) { 334 header.css({"position": "fixed", "top": topPosition + "px"}); 335 firstScroll = partialScrollUp = partialScrollDown = false; 336 distanceFromTop = window.pageYOffset; 337 } 338 } 339 340 else if (firstScroll && scrollPosition > delta + buffer) { 341 headerTopOffset = scrollPosition - (delta + buffer); 342 header.css({"position": "absolute", "top": headerTopOffset}); 343 body.css({"padding-top": header.outerHeight()}); 344 firstScroll = false; 345 partialScrollDown = true; 346 } 347 348 else if (!firstScroll && !partialScrollUp) { 349 partialScrollDown = "true"; 350 header.css({"position": "absolute", "top": distanceFromTop + wpAdminBarHeight + wsMgmtBarHeight}); 351 body.css({"padding-top": header.outerHeight()}); 352 353 if ((header.offset().top + headerHeight + buffer) < scrollPosition) { 354 headerTopOffset = scrollPosition - (delta + buffer); 355 header.removeClass("sticky-header"); 356 partialScrollDown = false; 357 } 358 } 359 360 if (scrollPosition <= initialHeaderOffset) { 361 header.css({"position": "relative", "top": ""}).removeClass("sticky-header"); 362 body.css({"padding-top": 0}); 363 firstScroll = partialScrollDown = true; 364 partialScrollUp = false; 365 } 366 prevScrollPosition = scrollPosition; 367 } 368 }); 369 370 371 // 372 // PAGE SCROLL ANIMATIONS 373 // 374 $(function() { 375 var elements = $(".fade-in, .translate-left, .translate-right, .translate-up, .translate-down, .flip-in"); 376 var theWindow = $(window), offset; 377 378 function animate() { 379 offset = $("#mobilenav").is(":visible") ? 25 : 75; // different offsets for mobile/desktop 380 elements.each(function(i, elem) { 381 if ($(this).hasClass('animated')) 382 return; 383 animateMe($(this), offset); 384 }); 385 } 386 387 function animateMe(elem, offset) { 388 var winTop = theWindow.scrollTop() - offset; 389 var winBottom = winTop + theWindow.height(); 390 var elemTop = $(elem).offset().top + offset; 391 if ((elemTop <= winBottom) && (elemTop >= winTop)) { 392 $(elem).addClass('animated'); 393 } 394 } 395 396 theWindow.on('resize scroll', function(e) { 397 animate(); 398 }); 399 400 animate(); 401 402 }); 403 404 405 406 407 // 408 // Detect short interior pages and add a class to remove the bottom left/right 409 // square embellishments so they don't overlap the ones on the top left/right 410 // 411 $(window).on('load resize', function() { 412 setInteriorPageClass(); 413 }); 414 415 setInteriorPageClass(); 416 417 function setInteriorPageClass() { 418 var interior_page = $("main.interior"); 419 if(interior_page.outerHeight() < 800) { 420 interior_page.addClass('short-page'); 421 } else { 422 interior_page.removeClass('short-page'); 423 } 424 } 425 426 // Add <span class="sr-only">(Opens in new window)</span> to any link with target="_blank" 427 const links = document.querySelectorAll( 'a[target="_blank"]' ); 428 links.forEach( link => { 429 if ( !link.querySelector( '.sr-only' ) ) { 430 const sr_span = document.createElement( 'span' ); 431 sr_span.className = 'sr-only'; 432 sr_span.textContent = ' (Opens in new window)'; 433 link.appendChild( sr_span ); 434 } 435 }); 436 437});
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.