1/** 2 * Theme JS 3 * 4 * @package bd 5 */ 6 7var $ = jQuery; 8 9$( function() { 10 11 // Shareable images â copy link buttons. 12 ( function() { 13 $( document ).on( 'click', '.js--copy-link', function() { 14 var $btn = $( this ); 15 var $input = $btn.siblings( '.js--text-to-copy' ); 16 var $text = $btn.find( '.js--copy-button-text' ); 17 18 if ( navigator.clipboard && $input.length ) { 19 navigator.clipboard.writeText( $input.val() ).then( function() { 20 $text.text( 'Copied!' ); 21 setTimeout( function() { 22 $text.text( 'Copy Link' ); 23 }, 2000 ); 24 } ); 25 } 26 } ); 27 } )(); 28 29 // Announcement banner dismiss with session cookie. 30 ( function() { 31 var banner = $( '#alert-banner' ); 32 var closeBtn = $( '#alert-banner-close' ); 33 34 if ( ! banner.length ) { 35 return; 36 } 37 38 // Check if already dismissed this session. 39 if ( 'true' === sessionStorage.getItem( 'bd_banner_dismissed' ) ) { 40 banner.addClass( 'alert-banner--hidden' ); 41 return; 42 } 43 44 closeBtn.on( 'click', function( e ) { 45 e.preventDefault(); 46 e.stopPropagation(); 47 banner.addClass( 'alert-banner--hidden' ); 48 sessionStorage.setItem( 'bd_banner_dismissed', 'true' ); 49 } ); 50 } )(); 51 52 var navigation = { 53 $nav: $( '.nav' ), 54 $navTrigger: $( '.nav__trigger' ), 55 $navContent: $( '.nav__content' ), 56 $navList: $( '.nav__list' ), 57 58 init: function() { 59 var self = this; 60 61 self.$navTrigger.on( 'click', function() { 62 var isExpanded = self.$navTrigger.attr( 'aria-expanded' ) === 'true'; 63 self.$navTrigger.attr( 'aria-expanded', ! isExpanded ); 64 self.$nav.attr( 'aria-hidden', isExpanded ); 65 66 if ( ! self.$navTrigger.hasClass( 'is-active' ) ) { 67 self.$navTrigger.addClass( 'is-active' ); 68 69 if ( ! self.$nav.hasClass( 'is-active' ) ) { 70 self.$nav.addClass( 'is-active' ); 71 self.$nav.on( 'transitionend', function( e ) { 72 if ( 73 e.originalEvent.propertyName === 'width' && 74 self.$navTrigger.hasClass( 'is-active' ) 75 ) { 76 self.$navContent.addClass( 'is-active' ); 77 } 78 } ); 79 } else { 80 self.$navContent.addClass( 'is-active' ); 81 } 82 83 if ( $( 'html' ).hasClass( 'no-csstransitions' ) ) { 84 self.$navContent.addClass( 'is-active' ); 85 } 86 } else { 87 self.$navTrigger.removeClass( 'is-active' ); 88 89 if ( self.$navContent.hasClass( 'is-active' ) ) { 90 self.$navContent.removeClass( 'is-active' ); 91 self.$navContent.on( 'transitionend', function( e ) { 92 if ( 93 e.originalEvent.propertyName === 'opacity' && 94 ! self.$navTrigger.hasClass( 'is-active' ) 95 ) { 96 self.$nav.removeClass( 'is-active' ); 97 } 98 } ); 99 } else { 100 self.$nav.removeClass( 'is-active' ); 101 } 102 103 if ( $( 'html' ).hasClass( 'no-csstransitions' ) ) { 104 self.$nav.removeClass( 'is-active' ); 105 } 106 } 107 } ); 108 }, 109 }; 110 navigation.init(); 111 112 var current = location.pathname; 113 if ( '/' !== current ) { 114 $( '.bd_main_menu li a' ).each( function() { 115 var $this = $( this ); 116 if ( -1 !== $this.attr( 'href' ).indexOf( current ) ) { 117 $this.addClass( 'active' ); 118 } 119 } ); 120 } 121 122 $( '.Trigger' ).on( 'click', function() { 123 if ( $( '.Trigger-top' ).hasClass( 'no-animation' ) ) { 124 $( '.Trigger-top' ).removeClass( 'no-animation' ); 125 $( '.Trigger-bottom' ).removeClass( 'no-animation' ); 126 } 127 128 $( '.bd_header' ).toggleClass( 'staticHeader' ); 129 130 if ( $( '.Trigger' ).hasClass( 'is-active' ) ) { 131 $( '.bd_page' ).addClass( 'bd_no_scroll' ); 132 } else { 133 $( '.bd_page' ).removeClass( 'bd_no_scroll' ); 134 } 135 } ); 136 137 // Keyboard support for mobile nav submenu chevrons. 138 $( '.nav__item i' ).attr( 'role', 'button' ).attr( 'tabindex', '0' ).attr( 'aria-label', 'Toggle submenu' ); 139 $( '.nav__item i' ).on( 'keydown', function( e ) { 140 if ( 13 === e.keyCode || 32 === e.keyCode ) { 141 e.preventDefault(); 142 $( this ).trigger( 'click' ); 143 } 144 } ); 145 146 $( '.nav__item i' ).on( 'click', function( e ) { 147 e.stopPropagation(); 148 149 var subMenu = $( this ).closest( 'li' ).find( '.sub-menu' ); 150 if ( subMenu.hasClass( 'bd_visible' ) ) { 151 subMenu.removeClass( 'bd_visible' ); 152 } else { 153 $( '.sub-menu' ).removeClass( 'bd_visible' ); 154 subMenu.addClass( 'bd_visible' ); 155 } 156 } ); 157 158 // Interstitial â outbound redirect modal for allowlisted domains. 159 ( function() { 160 var interstitialSites = [ 161 'everytownresearch.org', 162 'everytownsupportfund.org', 163 'wearorange.org', 164 'nrawatch.org', 165 'everystat.org', 166 'ask.everytownresearch.org', 167 'gunsensevoter.org', 168 'besmartforkids.org', 169 'onethingyoucando.org', 170 'momentsthatsurvive.org', 171 'smokinggun.org', 172 'demandaseat.org', 173 'citygrip.org' 174 ]; 175 176 var $modal = $( '.js--interstitial' ); 177 var $currentLink = $( '.js--interstitial-current-link' ); 178 179 if ( ! $modal.length ) { 180 return; 181 } 182 183 function matchesDomain( host, domain ) { 184 return host === domain || host.slice( -domain.length - 1 ) === '.' + domain; 185 } 186 187 function isInterstitialHost( host ) { 188 for ( var i = 0; i < interstitialSites.length; i++ ) { 189 if ( matchesDomain( host, interstitialSites[i] ) ) { 190 return true; 191 } 192 } 193 return false;
194 } 195 196 function shouldShowInterstitial( linkHost ) { 197 if ( linkHost === window.location.hostname ) { 198 return false; 199 } 200 return isInterstitialHost( linkHost ); 201 } 202 203 $( 'a[href]' ).each( function() { 204 if ( shouldShowInterstitial( this.hostname ) ) { 205 $( this ).addClass( 'js--interstitial-link' ); 206 } 207 } ); 208 209 $( document ).on( 'click', '.js--interstitial-link', function( e ) { 210 e.preventDefault(); 211 e.stopImmediatePropagation(); 212 213 var href = this.href; 214 var $clicked = $( this ); 215 216 $currentLink.text( window.location.hostname ); 217 $modal.attr( 'aria-hidden', 'false' ).addClass( 'active' ); 218 $( 'body' ).addClass( 'bd_no_scroll' ); 219 220 setTimeout( function() { 221 window.open( href, '_blank' ) || window.location.assign( href ); 222 223 setTimeout( function() { 224 $modal.attr( 'aria-hidden', 'true' ).removeClass( 'active' ); 225 $( 'body' ).removeClass( 'bd_no_scroll' ); 226 $clicked.focus(); 227 }, 3000 ); 228 }, 3000 ); 229 } ); 230 } )(); 231 232 $( 'a[href^="http"]' ).click( function( e ) { 233 if ( undefined !== $( this ).attr( 'data-lity' ) ) { 234 return; 235 } 236 237 if ( $( this ).hasClass( 'js--interstitial-link' ) ) { 238 return; 239 } 240 241 var a = new RegExp( '/' + window.location.host + '/' ); 242 if ( ! a.test( this.href ) ) { 243 if ( ! $( this ).hasClass( 'self-blank' ) ) { 244 e.preventDefault(); 245 window.open( this.href ); 246 } 247 } 248 } ); 249 250 // Social sharing functions (single post). 251 window.copyFunction = function( textToCopy, button ) { 252 var textArea = document.createElement( 'textarea' ); 253 textArea.value = textToCopy; 254 document.body.appendChild( textArea ); 255 textArea.select(); 256 document.execCommand( 'copy' ); 257 document.body.removeChild( textArea ); 258 button.innerHTML = 'Copied! <span><i class="fa-solid fa-check"></i></span>'; 259 button.disabled = true; 260 261 setTimeout( function() { 262 button.innerHTML = 'Copy Link <span><i class="fa-solid fa-link"></i></span>'; 263 button.disabled = false; 264 }, 2000 ); 265 }; 266 267 window.fbShare = function( url, winWidth, winHeight ) { 268 var winTop = ( screen.height / 2 ) - ( winHeight / 2 ); 269 var winLeft = ( screen.width / 2 ) - ( winWidth / 2 ); 270 window.open( 271 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent( url ), 272 'share', 273 'height=' + winHeight + ',top=' + winTop + ',left=' + winLeft + ',width=' + winWidth + ',resizable=1' 274 ); 275 }; 276 277 window.tweetWindow = function( url, winWidth, winHeight ) { 278 var winTop = ( screen.height / 2 ) - ( winHeight / 2 ); 279 var winLeft = ( screen.width / 2 ) - ( winWidth / 2 ); 280 window.open( 281 'https://twitter.com/intent/tweet?text=' + encodeURIComponent( url ), 282 'tweet', 283 'height=' + winHeight + ',top=' + winTop + ',left=' + winLeft + ',width=' + winWidth + ',resizable=1' 284 ); 285 }; 286 287 window.linkedInWindow = function( url, winWidth, winHeight ) { 288 var winTop = ( screen.height / 2 ) - ( winHeight / 2 ); 289 var winLeft = ( screen.width / 2 ) - ( winWidth / 2 ); 290 window.open( 291 'https://www.linkedin.com/shareArticle?mini=true&url=' + encodeURIComponent( url ), 292 'share', 293 'height=' + winHeight + ',top=' + winTop + ',left=' + winLeft + ',width=' + winWidth + ',resizable=1' 294 ); 295 }; 296 297 // Archive AJAX news loading. 298 if ( $( '#filters' ).length ) { 299 var page = 1; 300 301 var loadNews = function( pageNum ) { 302 pageNum = pageNum || 1; 303 jQuery.ajax( { 304 type: 'POST', 305 url: _wpUtilSettings.ajax.url, 306 data: $.extend( {}, $( '#filters' ).serializeJSON(), { 307 page: pageNum, 308 } ), 309 success: function( res ) { 310 if ( 1 === pageNum ) { 311 $( '#ajaxnews' ).html( res ); 312 } else { 313 $( '#ajaxnews' ).append( res ); 314 if ( '' === res ) { 315 $( '#loadmore' ).hide(); 316 } 317 } 318 319 $( '#loadmore' ).removeAttr( 'disabled' ); 320 $( '#loadmore' ).html( 'Load More' ); 321 }, 322 } ); 323 }; 324 325 loadNews(); 326 327 $( '#loadmore' ).click( function( e ) { 328 $( this ).attr( 'disabled', true ); 329 $( this ).html( 'Load More...' ); 330 e.preventDefault(); 331 page++; 332 loadNews( page ); 333 } ); 334 } 335 336 // Function to check if animations should be disabled 337 function shouldDisableAnimations() { 338 const isMobile = window.innerWidth <= 768; 339 const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; 340 const isMobileDevice = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); 341 342 return isMobile || prefersReducedMotion || isMobileDevice; 343 } 344 345 // Function to check if element is in viewport (partial visibility is enough) 346 function isInViewport(element) { 347 const rect = element.getBoundingClientRect(); 348 const windowHeight = window.innerHeight || document.documentElement.clientHeight; 349 350 // Element is visible if any part is in viewport 351 return ( 352 rect.top < windowHeight && 353 rect.bottom > 0 354 ); 355 } 356 357 // Initialize AOS with proper error handling 358 function initializeAOS() { 359 // Check if AOS is available 360 if (typeof AOS === 'undefined') {
361 console.warn('AOS library not loaded'); 362 return; 363 } 364 365 // Set up staggered items (always get delays) 366 if (!shouldDisableAnimations()) { 367 document.querySelectorAll('.staggered-item').forEach((element, index) => { 368 element.setAttribute('data-aos-delay', index * 250); 369 }); 370 } 371 372 // Handle elements that need conditional delays based on viewport 373 const delayedElements = document.querySelectorAll('.aos-delayed-animation[data-aos]'); 374 375 delayedElements.forEach((element) => { 376 const inView = isInViewport(element); 377 378 if (inView) { 379 // IN VIEWPORT (portrait/first fold): Keep the staggered delay 380 // The delay should already be set, so we don't remove it 381 console.log('Element in view, keeping delay:', element.getAttribute('data-aos-delay')); 382 } else { 383 // NOT IN VIEWPORT (landscape/below fold): Remove delay for instant animation 384 console.log('Element below fold, removing delay from:', element.className); 385 element.setAttribute('data-aos-delay', '0'); 386 } 387 }); 388 389 // Initialize AOS 390 try { 391 AOS.init({ 392 disable: shouldDisableAnimations(), 393 once: true, 394 duration: 1000, 395 offset: 50, // Start animation when element is 50px from viewport 396 }); 397 398 // Small delay to catch any late elements 399 setTimeout(() => { 400 AOS.refresh(); 401 }, 100); 402 403 } catch (error) { 404 console.error('AOS initialization error:', error); 405 } 406 } 407 408 // Initialize when DOM is ready (not waiting for all images to load) 409 if (document.readyState === 'loading') { 410 document.addEventListener('DOMContentLoaded', initializeAOS); 411 } else { 412 // DOM already loaded, init immediately 413 initializeAOS(); 414 } 415 416 417 // Subnav â auto-build sidebar links from anchor headings. 418 ( function() { 419 var navContainers = document.querySelectorAll( '.js--subnav' ); 420 if ( ! navContainers.length ) { 421 return; 422 } 423 424 var headings = document.querySelectorAll( 'h2.heading-with-anchor' ); 425 426 if ( headings.length ) { 427 navContainers.forEach( function( nav ) { 428 nav.classList.add( 'has-items' ); 429 } ); 430 } 431
432 headings.forEach( function( heading ) { 433 var li = document.createElement( 'li' ); 434 var a = document.createElement( 'a' ); 435 a.textContent = heading.textContent; 436 a.setAttribute( 'href', '#' + heading.id ); 437 438 a.addEventListener( 'click', function( e ) { 439 e.preventDefault(); 440 var target = document.getElementById( heading.id ); 441 if ( target ) { 442 var headerHeight = document.querySelector( 'header' ) ? document.querySelector( 'header' ).getBoundingClientRect().height : 0; 443 var offset = target.getBoundingClientRect().top + window.pageYOffset - headerHeight - 40; 444 window.scrollTo( { top: offset, behavior: 'smooth' } ); 445 } 446 447 // Update active link. 448 var prev = document.querySelector( '.subnav__selected-link' ); 449 if ( prev ) { 450 prev.classList.remove( 'subnav__selected-link' ); 451 } 452 a.classList.add( 'subnav__selected-link' ); 453 454 // Update URL hash. 455 if ( history.pushState ) { 456 history.pushState( null, null, '#' + heading.id ); 457 } 458 } ); 459 460 li.appendChild( a ); 461 navContainers.forEach( function( nav ) { 462 nav.appendChild( li.cloneNode( true ) ); 463 } ); 464 } ); 465 } )(); 466 467 const header = $('header'); 468 var current = false; // false = not scrolled, true = scrolled 469 470 $(window).on('scroll', function () { 471 requestAnimationFrame(function () { 472 let s = $(window).scrollTop(); 473// console.log('s', s); 474// console.log('current', current); 475 // switch ON only after >110px 476 if (!current && s > 50) { 477 header.addClass('scrolled'); 478 current = true; 479 } 480 481 // switch OFF only after <90px 482 if (s === 0) { 483 header.removeClass('scrolled'); 484 current = false; 485 } 486 }); 487 }); 488} );
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.