1jQuery(document).ready(function ($) { 2 3/* ========================================================================== 4 Toggle CSS classes for focused navigation items to improve accessibility 5 ========================================================================== */ 6 jQuery( '#masthead' ).on( 'focusin focusout', '#menu-primary-menu .sub-menu a', function(){ 7 jQuery( this ) 8 .parent( 'li' ).toggleClass( 'focused' ) 9 .closest( '#menu-primary-menu > li.menu-item-has-children' ).toggleClass( 'focused' ); 10 }); 11 12/* ========================================================================== 13 Escape closes an open desktop sub-menu 14 ========================================================================== */ 15 /* 16 * A sub-menu opens on focus and covers the page, so Escape has to dismiss it 17 * without leaving the menu (WCAG 1.4.13). The `dismissed` class holds it closed 18 * while the parent link keeps focus, since the stylesheet otherwise reopens it; 19 * the class clears when focus leaves the item or the pointer returns. 20 */ 21 jQuery( '#masthead' ).on( 'keydown', '#menu-primary-menu > li.menu-item-has-children', function( e ) { 22 23 if ( e.key !== 'Escape' || ! window.matchMedia( '(min-width: 768px)' ).matches ) { 24 return; 25 } 26 27 const $item = jQuery( this ); 28 const $subMenu = $item.children( '.sub-menu' ); 29 30 if ( ! $subMenu.length || getComputedStyle( $subMenu[0] ).visibility !== 'visible' ) { 31 return; 32 } 33 34 e.preventDefault(); 35 e.stopPropagation(); 36 37 // Focus first, so the focused-class toggles above settle before the classes are cleared. 38 $item.children( 'a' )[0].focus(); 39 $item.addClass( 'dismissed' ).find( '.focused' ).addBack().removeClass( 'focused' ); 40 } ); 41 42 jQuery( '#masthead' ).on( 'focusout mouseenter', '#menu-primary-menu > li.dismissed', function( e ) { 43 44 if ( e.type === 'focusout' && e.relatedTarget && this.contains( e.relatedTarget ) ) { 45 return; 46 } 47 48 jQuery( this ).removeClass( 'dismissed' ); 49 } ); 50 51/* ========================================================================== 52 MeanMenu init and options 53 ========================================================================== */ 54 // Shared with the expand toggle handler below so the two glyphs can't drift. 55 const expandGlyph = '+'; 56 const contractGlyph = 'â'; 57 58 jQuery('header nav').meanmenu( { 59 meanScreenWidth: '767', 60 meanMenuCloseSize: '2rem', 61 meanMenuContainer: '.mobile-nav', 62 meanExpand: expandGlyph, 63 meanContract: contractGlyph 64 }); 65 66/* ========================================================================== 67 Give MeanMenu's generated toggle a name, role and state 68 MeanMenu builds an empty anchor, so screen readers announce it as an 69 unnamed link with no indication of whether the menu is open. The names come 70 from PHP through wp_localize_script so they can be translated. 71 ========================================================================== */ 72 ( function () { 73 74 const phpLabels = window.pioneerMenuLabels || {}; 75 const labels = { 76 menu: phpLabels.menu || 'Menu', 77 toggleSubmenu: phpLabels.toggleSubmenu || 'Toggle submenu' 78 }; 79 80 var $container = jQuery( '.mobile-nav' ); 81 82 if ( ! $container.length ) { 83 return; 84 } 85 86 /* 87 * MeanMenu rebuilds its bar whenever the window is resized across the 88 * breakpoint, which throws away anything set on the generated toggle, so 89 * the attributes are re-applied whenever a new toggle appears. 90 */ 91 function enhance() { 92 93 var $reveal = $container.find( '.meanmenu-reveal' ); 94 95 if ( $reveal.length && $reveal.attr( 'role' ) !== 'button' ) { 96 97 $container.find( '.mean-nav' ).attr( 'id', 'mobile-nav-menu' ); 98 99 $reveal.attr( { 100 'role': 'button', 101 'aria-label': labels.menu, 102 'aria-controls': 'mobile-nav-menu', 103 'aria-expanded': 'false' 104 } ); 105 } 106 107 moveExpandToggles(); 108 } 109 110 /* 111 * MeanMenu appends each expand toggle after the sub-menu it opens, which 112 * puts it after every sub-item in the tab order, so move it up to follow 113 * the parent link. MeanMenu's own click handler opens prev( 'ul' ), which 114 * finds nothing after the move, so the delegated handler below takes over. 115 */ 116 function moveExpandToggles() { 117 118 $container.find( 'a.mean-expand' ).each( function () { 119 120 var $toggle = jQuery( this ); 121 var $submenu = $toggle.prev( 'ul' ); 122 123 if ( ! $submenu.length ) { 124 return; 125 } 126 127 $toggle 128 .off( 'click' ) 129 .attr( { 130 'aria-label': labels.toggleSubmenu, 131 'aria-expanded': 'false' 132 } ) 133 .insertBefore( $submenu ); 134 } ); 135 } 136 137 enhance(); 138 139 if ( window.MutationObserver ) { 140 new window.MutationObserver( enhance ).observe( $container[0], { childList: true, subtree: true } ); 141 } 142 143 /* 144 * Delegated so it survives the toggle being replaced. MeanMenu marks the 145 * open state with the meanclose class and sets it in its own click 146 * handler, so read it back on the next tick. 147 */ 148 $container.on( 'click', '.meanmenu-reveal', function () { 149 150 var $self = jQuery( this ); 151 152 setTimeout( function () { 153 $self.attr( 'aria-expanded', $self.hasClass( 'meanclose' ) ? 'true' : 'false' ); 154 }, 0 ); 155 } ); 156 157 // It announces as a button, so Space has to work as well as Enter. The 158 // generated control is a link, which only reacts to Enter on its own. 159 $container.on( 'keydown', '.meanmenu-reveal', function ( e ) { 160 161 if ( e.key === ' ' || e.key === 'Spacebar' ) { 162 e.preventDefault(); 163 jQuery( this ).trigger( 'click' ); 164 } 165 } ); 166 167 /* 168 * Replaces the per-toggle handler removed above, doing what MeanMenu did 169 * but against next( 'ul' ) now that the toggle comes first. Delegated so 170 * it survives MeanMenu rebuilding the bar on resize. 171 */ 172 $container.on( 'click', 'a.mean-expand', function ( e ) { 173 174 var $toggle = jQuery( this ); 175 var $submenu = $toggle.next( 'ul' ); 176 177 e.preventDefault(); 178 179 if ( $toggle.hasClass( 'mean-clicked' ) ) { 180 181 $toggle.text( expandGlyph ).attr( 'aria-expanded', 'false' ); 182 $submenu.slideUp( 300 ); 183 184 } else { 185 186 $toggle.text( contractGlyph ).attr( 'aria-expanded', 'true' ); 187 $submenu.slideDown( 300 ); 188 } 189 190 $toggle.toggleClass( 'mean-clicked' ); 191 } ); 192 } )(); 193 194 195 196/* ========================================================================== 197 Toggle Search Bar 198 ========================================================================== */ 199 /* 200 * Remember which toggle opened the form. There is a desktop toggle and a 201 * copy inside the mobile menu bar, and Escape hands focus back to the one 202 * the person used. 203 */ 204 let $searchToggleInUse = jQuery('.search-toggle').first(); 205 206 /* 207 * The toggle shows a magnifying glass while the form is closed and an X 208 * while it is open, so its name changes with the icon. The names come from 209 * PHP through wp_localize_script so they can be translated. 210 */ 211 const $searchPhpLabels = window.pioneerMenuLabels || {}; 212 const $searchOpenLabel = $searchPhpLabels.search || 'Search'; 213 const $searchCloseLabel = $searchPhpLabels.closeSearch || 'Close search'; 214 215 jQuery('.search-toggle').on('click', function(e) { 216 217 $searchToggleInUse = jQuery( this ); 218 219 // Open or close the search form 220 jQuery('#masthead .search-form').toggleClass('search-open'); 221 222 // The class drives the icon swap and the panel's slide, so it flips at once. 223 jQuery( '.search-toggle' ).toggleClass( 'search-open' ); 224 225 // The name and expanded state flip with the icon. On close they change right 226 // away, since focus returns to the toggle. On open they wait until focus has 227 // moved into the field, because a screen reader starts reading a change on 228 // the focused control and the move to the field cuts it off mid-word. 229 const searchIsOpen = jQuery( '.search-toggle' ).hasClass( 'search-open' ); 230 231 if ( searchIsOpen === false ) { 232 jQuery( '.search-toggle' ).attr( 'aria-expanded', 'false' ).attr( 'aria-label', $searchOpenLabel ); 233 } 234 235 // Once the search form transitions to display place the cursor inside it 236 jQuery( '#masthead .search-form' ).one( 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd', function(){ 237 var $this = $( this ); 238 239 if( $this.hasClass( 'search-open' ) === true ){ 240 $this.find( 'input[type="search"]' ).focus(); 241 jQuery( '.search-toggle' ).attr( 'aria-expanded', 'true' ).attr( 'aria-label', $searchCloseLabel ); 242 } 243 }); 244 245 e.preventDefault(); 246 }); 247 248 // It announces as a button, so Space has to work as well as Enter. The 249 // toggle is a link, which only reacts to Enter on its own. 250 jQuery( '.search-toggle' ).on( 'keydown', function ( e ) { 251 252 if ( e.key === ' ' || e.key === 'Spacebar' ) { 253 e.preventDefault(); 254 jQuery( this ).trigger( 'click' ); 255 } 256 } ); 257 258 /* 259 * Escape closes the form and returns focus to the toggle, matching the other 260 * themes. Bound to the form and the toggles so it only fires while one of 261 * them holds focus. 262 */ 263 jQuery( '#masthead .search-form, .search-toggle' ).on( 'keydown', function(e) { 264 265 const $searchForm = jQuery( '#masthead .search-form' ); 266 267 if ( e.key !== 'Escape' || ! $searchForm.hasClass( 'search-open' ) ) { 268 return; 269 } 270 271 $searchForm.removeClass( 'search-open' ); 272 jQuery( '.search-toggle' ).removeClass( 'search-open' ).attr( 'aria-expanded', 'false' ).attr( 'aria-label', $searchOpenLabel ); 273 $searchToggleInUse.focus(); 274 }); 275 276 277/* ========================================================================== 278 iOS body class 279 ========================================================================== */ 280 if (navigator.userAgent.match(/(iPod|iPhone|iPad)/)) { 281 jQuery('body').addClass('agent-ios'); 282 } 283 284 285/* ========================================================================== 286 Wrap <address> tags with Google Maps links 287 ========================================================================== */
288 jQuery('address').each(function () { 289 var link = "<a href='http://maps.google.com/maps?q=" + encodeURIComponent( jQuery(this).text() ) + "' class='map-link' target='_blank'>" + jQuery(this).html() + "</a>"; 290 jQuery(this).html(link); 291 }); 292 293 /* ========================================================================== 294 Remove Bottom Border from Anchors that contain Images 295 296 If the anchor links are designed out to have bottom borders this will remove the bottom border 297 when a user adds a linked image within the content editor. 298 ========================================================================== */ 299 $('img').parent('a').css('border-bottom', 'none'); 300 301 302 /* ========================================================================== 303 Open Default WordPress Gallery Pictures in New Tab 304 ========================================================================== */ 305 $('.gallery .gallery-item a').attr('target', '_blank'); 306 307 308 /* ========================================================================== 309 Double Tap and Go makes primary navigation dropdowns work on tablets 310 ========================================================================== */ 311 $( '#site-navigation #menu-primary-menu' ).doubleTapToGo({ 312 selectorChain: '> li:has(ul)' 313 }); 314 315 316 /* ============================================================================ 317 Homepage Statistics Block - Numbers Count Up (See https://github.com/inorganik/countUp.js) 318 ============================================================================ */ 319 const numberElements = document.querySelectorAll( '.homepage-statistics .stat span.number' ); 320 numberElements.forEach( function( numberElement ) { 321 const endNumber = parseFloat( numberElement.getAttribute( 'data-end-number' ) ); 322 const countAnimation = new countUp.CountUp( 323 numberElement, 324 endNumber, 325 { 326 duration: 2.5, // Duration in seconds 327 enableScrollSpy: true, // Animate when the element enters view 328 scrollSpyOnce: true, // Only animate once 329 decimalPlaces: parseInt( numberElement.getAttribute( 'data-num-decimal-places' ), 10 ), 330 } 331 ); 332 countAnimation.start(); 333 }); 334 335 /* ============================================================================ 336 Homepage Blocks Background Colors 337 Alternate colors but skip Tiles blocks and combine single Text Callouts that have right-aligned buttons 338 ============================================================================ */ 339 const homepageBlocks = document.querySelectorAll( 'body.home .site-main > section' ); 340 if ( homepageBlocks && homepageBlocks.length > 0 ) { 341 342 let sectionCounter = 1; 343 let previousBlockClasses = []; 344 const requiredTextCalloutClasses = [ 'text-callouts', 'callout-num-1', 'has-buttons-on-the-right' ]; 345 346 homepageBlocks.forEach( ( block ) => { 347 348 // Skip Tiles blocks so they aren't factored into the alternating background colors. 349 if ( block.classList.contains( 'homepage-tiles' ) ) { 350 351 return; 352 } 353 354 const blockClasses = block.classList.value.split( ' ' ); 355 const isSingleTextCalloutButtonRight = requiredTextCalloutClasses.every( requiredClass => blockClasses.includes( requiredClass ) ); 356 const wasPreviousSingleTextCalloutButtonRight = requiredTextCalloutClasses.every( requiredClass => previousBlockClasses.includes( requiredClass ) ); 357 const previousBlockHasColoredBackground = previousBlockClasses.includes( 'colored-background' ); 358 359 // If this block and the previous block are Text Callouts with a single callout and right aligned buttons, treat them as one block. 360 if ( isSingleTextCalloutButtonRight && wasPreviousSingleTextCalloutButtonRight ) { 361 362 if ( previousBlockHasColoredBackground ) { 363 364 block.classList.add( 'colored-background' ); 365 } 366 367 return; 368 } 369 370 // Add colored-background class to alternating blocks. 371 if ( sectionCounter % 2 === 0 ) { 372 373 block.classList.add( 'colored-background' ); 374 } 375 376 previousBlockClasses = block.classList.value.split( ' ' ); 377 378 sectionCounter++; 379 }); 380 } 381});
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.