1var mq = matchMedia("(min-width: 750px)"); 2 3document.addEventListener("DOMContentLoaded", () => { 4 5 // Accessible dropdowns - Event listener for buttons 6 document.querySelectorAll( '#mainnav>ul>li>button' ).forEach( el => { 7 el.addEventListener( 'click', (e) => { 8 e.preventDefault(); 9 let parent = el.parentNode; 10 parent.classList.toggle( 'expand' ); 11 parent.querySelector( 'button' ).setAttribute( 'aria-expanded', parent.classList.contains( 'expand' ) ? true : false ); 12 }); 13 }); 14 15 16 if (window.mq.matches) { 17 // do desktop stuff // 18 // Accessible dropdowns - Focus event listeners for megamenus 19 // Only needed if the megamenu is not a <ul> (#mainnav>ul>li>ul.megamenu) i.e. it's a div (#mainnav>ul>li>div.megamenu) 20 document.querySelectorAll( '#mainnav>ul>li.has-megamenu' ).forEach( el => { 21 let megamenu = el.querySelector( '.megamenu' ); 22 23 el.querySelector( 'a' ).addEventListener( 'focus', e => { 24 ws_close_element( el ); 25 }); 26 27 megamenu.addEventListener( 'focusout', e => { 28 if ( !megamenu.contains( e.relatedTarget ) ) { 29 ws_close_element( el ); 30 } 31 }); 32 }); 33 34 35 // Accessible dropdowns - Focus event listeners for normal dropdowns 36 document.querySelectorAll( '#mainnav>ul>li.menu-item-has-children' ).forEach( el => { 37 el.querySelector( 'a' ).addEventListener( 'focus', e => { 38 ws_close_element( el ); 39 }); 40 41 el.querySelector( 'ul' ).addEventListener( 'focusout', function (e) { 42 if ( !this.contains( e.relatedTarget ) ) { 43 ws_close_element( el ); 44 } 45 }); 46 }); 47 48 49 // Accessible dropdowns - Helper class to close expanded elements 50 function ws_close_element( el ) { 51 el.classList.remove( 'expand' ); 52 el.querySelector( 'button' ).setAttribute( 'aria-expanded', false ); 53 } 54 55 56 // Toggle aria-expanded on hover (desktop only) 57 document.querySelectorAll( '#mainnav>ul>li:is(.menu-item-has-children, .has-megamenu)' ).forEach( el => { 58 let button = el.querySelector( 'button' ); 59 if ( button.classList.contains( 'expand' ) ) { 60 return; 61 } 62 63 el.addEventListener( 'mouseover', (e) => { 64 el.querySelector( 'button' ).setAttribute( 'aria-expanded', true ); 65 }); 66 67 el.addEventListener( 'mouseout', (e) => { 68 el.querySelector( 'button' ).setAttribute( 'aria-expanded', false ); 69 }); 70 }); 71 72 73 74 // Add class to header when page scrolled to toggle alternate styles 75 var header = document.querySelector('body>header'); 76 window.addEventListener( 'scroll', () => { 77 if(window.pageYOffset > 0) { 78 header.classList.add('scrolled'); 79 } else { 80 header.classList.remove('scrolled'); 81 } 82 }, { passive: true } ); 83 84 85 86 87 function is_touch_device() { 88 var prefixes = ' -webkit- -moz- -o- -ms- '.split(' '); 89 var mq = function (query) { 90 return window.matchMedia(query).matches; 91 } 92 if(('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints > 0) || window.DocumentTouch && document instanceof DocumentTouch) { 93 return true; 94 } 95 var query = ['(', prefixes.join('touch-enabled),('), 'heartz', ')'].join(''); 96 return mq(query); 97 } 98 99 // On touch devices, expand dropdowns on first tap 100 if( is_touch_device() ) { 101 document.querySelectorAll("#mainnav>div>ul>li>a").forEach(elem => { 102 elem.addEventListener('touchstart', () => { 103 var parent = elem.parentElement; 104 if( parent.contains( parent.querySelector('ul') ) ) { 105 if( !parent.classList.contains('tapped') ) { 106 event.preventDefault(); 107 document.querySelector("#mainnav>ul>li.tapped")?.classList.remove('tapped'); 108 parent.classList.add('tapped'); 109 } 110 } 111 }); 112 }); 113 } 114 115 116 } else { 117 // do mobile stuff // 118 119 // Set Initial states on pageLoad: 120 navContainer.setAttribute( 'role', 'dialog' ); 121 navContainer.inert = true; 122 navContainer.querySelectorAll( 'li.menu-item-has-children>ul' ).forEach( item => { 123 item.inert = true; 124 }); 125 126 // Handle toggleNav button clicks (#mobilenav Menu and navContainer "Close menu") 127 document.querySelectorAll( '.toggleNav' ).forEach( toggleNav => { 128 toggleNav.addEventListener( 'click', ws_toggleNav ); 129 }); 130 131 // Close slidenav with ESC key 132 navContainer.addEventListener( 'keydown', function(event) { 133 if (event.key === 'Escape' || event.key === 'Esc') { 134 ws_toggleNav(); 135 } 136 }); 137 138 139 // Close slideNav when clicked outside navContainer 140 document.addEventListener( 'click', checkTarget, false ); 141 document.addEventListener( 'touchstart', checkTarget, { passive: true, capture: false } ); 142 143 function checkTarget( event ) { 144 const mobile_toggle = document.querySelector( '#mobilenav .toggleNav') ; 145 146 if ( 147 document.body.classList.contains('slideNav') && 148 !navContainer.contains(event.target) && 149 !mobile_toggle.contains(event.target) 150 ) { 151 ws_toggleNav(); 152 } 153 } 154 155 156 // Toggle the slideNav open/close and update aria attributes 157 function ws_toggleNav() { 158 159 document.body.classList.toggle( 'slideNav' ); 160 let expanded = document.body.classList.contains( 'slideNav' ); 161 let main = document.querySelector( 'main' ); 162 let footer = document.querySelector( 'body>footer' ); 163 let skipnav = document.getElementById( 'skipNavigation' ); 164 165 // Update ARIA states: 166 if ( expanded ) { 167 main.inert = true; 168 footer.inert = true; 169 skipnav.inert = true; 170 navContainer.inert = false;
171 navContainer.setAttribute( 'aria-modal', true ); 172 } else { 173 main.inert = false; 174 footer.inert = false; 175 skipnav.inert = false; 176 navContainer.inert = true; 177 navContainer.setAttribute( 'aria-modal', false ); 178 } 179 180 document.querySelectorAll( '.toggleNav').forEach( button => { 181 button.setAttribute( 'aria-expanded', expanded ? 'true' : 'false' ); 182 }); 183 184 // Focus management with 250ms delay 185 setTimeout( () => { 186 if ( expanded ) { 187 navContainer.querySelector( 'button.toggleNav' ).focus(); 188 } else { 189 document.querySelector( '#mobilenav button.toggleNav' ).focus(); 190 } 191 }, 250); 192 193 } 194 195 196 // Trap focus within navContainer 197 navContainer.addEventListener( 'focusout', e => { 198 if ( !navContainer.contains( e.relatedTarget ) ) { 199 navContainer.querySelector( 'button.toggleNav' ).focus(); 200 } 201 }); 202 203 204 // Expand T1 dropdowns when + clicked 205 document.querySelectorAll( '#navContainer #mainnav>ul>li>button' ).forEach( button => { 206 button.addEventListener( 'click', () => { 207 208 let dropdown = button.nextElementSibling; 209 210 if ( !button.parentElement.classList.contains( 'expand') ) { 211 let scroll_height = button.nextElementSibling.scrollHeight; 212 dropdown.inert = true; 213 } else { 214 let scroll_height = button.nextElementSibling.scrollHeight; 215 dropdown.inert = false; 216 } 217 218 }); 219 }); 220 221 222 223 // Expand current page / current page ancestor T1 items in mobile nav 224 document.querySelectorAll('#mainnav li.current_page_item, #mainnav li.current_page_parent, #mainnav li.current_page_ancestor, #mainnav li.current-page-ancestor').forEach(nav_item => { 225 nav_item.classList.add( 'expand' ); 226 var dropdown = nav_item.querySelector( ':scope>ul' ); 227 if ( dropdown ) { 228 dropdown.inert = false; 229 dropdown.previousElementSibling.setAttribute( 'aria-expanded', true ); 230 } 231 }); 232 233 234 235 } // end of mobile stuff // 236 237 // do desktop and mobile stuff // 238 mq.addListener( () => { 239 location.reload(); 240 }); 241 242 243 // Smooth scroll to anchor tag 244 document.querySelectorAll('a[href^="#"]:not([href="#"])').forEach(anchor => { 245 anchor.addEventListener('click', (e) => { 246 e.preventDefault(); 247 document.querySelector(anchor.getAttribute('href')).scrollIntoView({ 248 behavior: 'smooth' 249 }); 250 anchor.blur(); 251 }); 252 }); 253 254 255 // 256 // Skip Link Focus 257 // 258 var is_webkit = navigator.userAgent.toLowerCase().indexOf( 'webkit' ) > -1, 259 is_opera = navigator.userAgent.toLowerCase().indexOf( 'opera' ) > -1, 260 is_ie = navigator.userAgent.toLowerCase().indexOf( 'msie' ) > -1; 261 262 if ( ( is_webkit || is_opera || is_ie ) && document.getElementById && window.addEventListener ) { 263 window.addEventListener( 'hashchange', function() { 264 var id = location.hash.substring( 1 ), 265 element; 266 if ( ! ( /^[A-z0-9_-]+$/.test( id ) ) ) { 267 return; 268 } 269 element = document.getElementById( id ); 270 if ( element ) { 271 if ( ! ( /^(?:a|select|input|button|textarea)$/i.test( element.tagName ) ) ) { 272 element.tabIndex = -1; 273 } 274 element.focus(); 275 } 276 }, false ); 277 } 278 279 280 // Animations 281 const elements = document.querySelectorAll('.circle-section, .translate-up li'); // define animated elements 282 var offset = window.mq.matches ? 150 : 50; // desktop offset / mobile offset 283 284 function intersectionObserver() { 285 var theRootMargin = '-' + offset + 'px 0px -' + offset + 'px 0px'; 286 const config = { rootMargin: theRootMargin, threshold: 0 }; 287 let observer = new IntersectionObserver(entries => { 288 entries.forEach(entry => { 289 if (entry.intersectionRatio > 0) { 290 entry.target.classList.add("animated"); 291 observer.unobserve(entry.target); 292 } 293 }); 294 }, config); 295 elements.forEach(element => { 296 observer.observe(element); 297 }); 298 } 299 intersectionObserver(); 300 301 302 // Stop animations during window resize event 303 var timer; 304 window.addEventListener('resize', () => { 305 document.body.classList.add( 'stop-transitions' ); 306 clearTimeout(timer); 307 timer = setTimeout( () => { 308 document.body.classList.remove( 'stop-transitions' ); 309 }, 500); 310 }, true); 311 312 313 // Add <span class="sr-only">(Opens in new window)</span> to any link with target="_blank" 314 const links = document.querySelectorAll( 'a[target="_blank"]' ); 315 links.forEach( link => { 316 if ( !link.querySelector( '.sr-only' ) ) { 317 const sr_span = document.createElement( 'span' ); 318 sr_span.className = 'sr-only'; 319 sr_span.textContent = ' (Opens in new window)'; 320 link.appendChild( sr_span ); 321 } 322 }); 323 324});
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.