1/** 2 * This script adds the jquery effects to the BHM Custom Theme Theme. 3 * 4 * @package BHM Custom Child\JS 5 * @author Bet Hannon Business Websites 6 * @license GPL-2.0-or-later 7 */ 8( function( $ ) { 9 10 // Cache DOM selectors. 11 var $container = $( '.site-container' ), 12 $header = $( '.site-header' ), 13 $hsToggle = $( '.toggle-header-search' ), 14 $hsWrap = $( '#header-search-wrap' ), 15 $hsInput = $hsWrap.find( 'input[type="search"]' ); 16 17 // Make sure JS class is added. 18 $( document ).ready( function() { 19 let activeSubmenu = null; // Track the currently active submenu 20 21 $( 'body' ).addClass( 'js' ); 22 23 // Initialize all submenus as hidden 24 $('.sub-menu').each(function () { 25 $(this).hide().attr('aria-expanded', 'false'); 26 }); 27 28 // Hover Behavior for Mouse Users 29 $('.menu-item').on('mouseenter', function () { 30 const $this = $(this); 31 const $submenu = $this.find('.sub-menu:first'); 32 const $button = $this.find('.sub-menu-toggle'); 33 34 if ($submenu.length) { 35 // Close other submenus 36 closeAllSubmenus(); 37 38 // Open this submenu 39 $submenu.stop(true, true).slideDown(200).attr('aria-expanded', 'true'); 40 $button.attr('aria-expanded', 'true').attr('aria-pressed', 'true'); 41 activeSubmenu = $submenu; // Set this submenu as active 42 } 43 }); 44 45 $('.menu-item').on('mouseleave', function () { 46 // Do nothing here to let submenus remain open 47 }); 48 49 // Dismiss submenus when clicking outside 50 $(document).on('click', function (e) { 51 if (!$(e.target).closest('.menu-item').length) { 52 closeAllSubmenus(); 53 } 54 }); 55 56 // Keyboard Accessibility: Toggle submenu with Enter or Space 57 $(document).on('keydown', '.sub-menu-toggle', function (e) { 58 if (e.key === 'Enter' || e.key === ' ') { 59 e.preventDefault(); 60 61 const $button = $(this); 62 const $submenu = $button.siblings('.sub-menu'); 63 const isExpanded = $button.attr('aria-expanded') === 'true'; 64 65 if (isExpanded) { 66 // Close submenu 67 $submenu.slideUp(200).attr('aria-expanded', 'false'); 68 $button.attr('aria-expanded', 'false').attr('aria-pressed', 'false'); 69 activeSubmenu = null; // Clear active submenu 70 } else { 71 // Close other submenus and open this one 72 closeAllSubmenus(); 73 $submenu.stop(true, true).slideDown(200).attr('aria-expanded', 'true'); 74 $button.attr('aria-expanded', 'true').attr('aria-pressed', 'true'); 75 activeSubmenu = $submenu; // Set this submenu as active 76 } 77 } 78 }); 79 80 // Keyboard Accessibility: Close submenu with Escape key 81 $(document).on('keydown', function (e) { 82 if (e.key === 'Escape') { 83 // Check if focus is inside an active submenu 84 if (activeSubmenu && activeSubmenu.find(':focus').length) { 85 const $toggleButton = activeSubmenu.siblings('.sub-menu-toggle'); 86 activeSubmenu.slideUp(200).attr('aria-expanded', 'false'); 87 $toggleButton.attr('aria-expanded', 'false').attr('aria-pressed', 'false').focus(); 88 activeSubmenu = null; // Clear active submenu 89 } else { 90 // Fallback to closing all submenus 91 closeAllSubmenus(); 92 } 93 } 94 }); 95 96 // Helper function: Close all submenus 97 function closeAllSubmenus() { 98 $('.menu-item .sub-menu').slideUp(200).attr('aria-expanded', 'false'); 99 $('.menu-item .sub-menu-toggle').attr('aria-expanded', 'false').attr('aria-pressed', 'false'); 100 activeSubmenu = null; // Clear active submenu 101 } 102 103 $('.kt-blocks-modal-link-inner').each(function () { 104 let html = $(this).html(); 105 106 // Look for text like: Transcript* for video* 107 html = html.replace(/\*(.*?)\*/g, '<span class="sr-only">$1</span>'); 108 109 $(this).html(html); 110 }); 111 112 113 }); 114 115 // Run on page scroll. 116 $( window ).scroll( function() { 117 118 // Toggle header class after threshold point. 119 if ( 50 < $( document ).scrollTop() ) { 120 $( '.site-container' ).addClass( 'shadow' ); 121 } else { 122 $( '.site-container' ).removeClass( 'shadow' ); 123 } 124 125 }); 126 127 // Handler for click a show/hide button. 128 $hsToggle.on( 'click', function( event ) { 129 130 event.preventDefault(); 131 132 if ( $( this ).hasClass( 'close' ) ) { 133 hideSearch(); 134 } else { 135 showSearch(); 136 } 137 138 }); 139 140 $( '.gb-block-post-grid-excerpt:empty' ).parent().parent().addClass( 'no-excerpt' ); 141 142 // Handler for pressing show/hide button. 143 $hsToggle.on( 'keydown', function( event ) { 144 145 // If tabbing from toggle button, and search is hidden, exit early. 146 if ( 9 === event.keyCode && ! $header.hasClass( 'search-visible' ) ) { 147 return; 148 } 149 150 event.preventDefault(); 151 handleKeyDown( event ); 152 153 }); 154 155 // Helper function to show the search form. 156 function showSearch() { 157 158 $header.addClass( 'search-visible' ); 159 $hsWrap.fadeIn( 'fast' ).find( 'input[type="search"]' ).focus(); 160 $hsToggle.attr( 'aria-expanded', true ); 161 162 } 163 164 // Helper function to hide the search form. 165 function hideSearch() { 166 167 $hsWrap.fadeOut( 'fast' ).parents( '.site-header' ).removeClass( 'search-visible' ); 168 $hsToggle.attr( 'aria-expanded', false ); 169 170 } 171
172 // Keydown handler function for toggling search field visibility. 173 function handleKeyDown( event ) { 174 175 // Enter/Space, respectively. 176 if ( 13 === event.keyCode || 32 === event.keyCode ) { 177 178 event.preventDefault(); 179 180 if ( $( event.target ).hasClass( 'close' ) ) { 181 hideSearch(); 182 } else { 183 showSearch(); 184 } 185 186 } 187 188 } 189 190 // Adding parent prefix to submenu toggles for context 191 192 setTimeout(function() { 193 194 var submenuButtons = $('.sub-menu-toggle'); 195 $.each(submenuButtons, function (buttonIdx, btn) { 196 var me = $(this); 197 var myText = me.find('span').text(); 198 var myParent = me.parent(); 199 var menuText = myParent.find(' > a').text(); 200 var newText = menuText + ' ' + myText; 201 me.find('span').text(newText); 202 }); 203 }, 250); 204 205 206 // Add additional wrappers to blockquote block 207 208 $( ".wp-block-quote:not(.is-style-tlc-standard-blockquote)" ).wrap( "<div class='blockquote-wrapper'></div>" ); 209 $( ".wp-block-quote:not(.is-style-tlc-standard-blockquote)" ).wrap( "<div class='blockquote-inner-wrapper'></div>" ); 210 $( ".wp-block-quote:not(.is-style-tlc-standard-blockquote)" ).wrapInner( "<div class='blockquote-text-wrapper'></div>"); 211 212 // Add additional wrapper to form field 213 214 $( ".comment-respond" ).wrapInner( "<div class='comment-respond-inner'></div>"); 215 $( ".entry-comments" ).wrapInner( "<div class='comments-wrapper'></div>"); 216 217 218 // Re-wrap the media-text link 219 220 $(document).ready(function() { 221 $('.thisisatest .wp-block-media-text__content').prev().wrap('<a href="#"></a>'); 222 $('.thisisatest .wp-block-media-text__content').prev().appendTo($('.thisisatest .wp-block-media-text__content').prev().parent()); 223 $('.thisisatest .wp-block-media-text__content').appendTo($('.thisisatest .wp-block-media-text__content').prev().parent()); 224 }); 225 226 227 /* Adds/edits Spanish attribute */ 228 229 $(document).ready(function($) { 230 $('.lang-spanish').attr('lang', 'es'); 231 }); 232 233 /* Change the buttons text for Spanish posts */ 234 235$(document).ready(function($) { 236 $('.category-espanol').each(function() { 237 var postTitle = $(this).find('.entry-title').text(); 238 $(this).find('.more-link').html('Seguir Leyendo<span class="sr-only"> sobre ' + postTitle + '</span>'); 239 }); 240}); 241 242 243 244 245 /* Change Heroes Assemble heading levels 246 247 ******************************************/ 248 /* Staff */ 249 $('h4.lhea-member-name').replaceWith(function(){ 250 return $('<h2>', {class: 'lhea-member-name', html: $(this).html()}); 251 }); 252 253 /* Modal */ 254 $('h4.lhea-modal-position').replaceWith(function(){ 255 return $('<div>', {class: 'lhea-modal-position', html: $(this).html()}); 256 }); 257 258 /* Accessibility optimizations to the modal close button */ 259 $('button.lhea-close-modal i').after(' Close<span class="sr-only"> profile modal</span>'); 260 261 /* wrap the image */ 262 $('div.lhea-member-picture img.wp-post-image').wrap('<div class="lhea-img-wrapper"></div>'); 263 264 $(document).ready(function() { 265 $('ul.lhea-heroes > li.lhea-hero').each(function() { 266 var $li = $(this); 267 $li.removeClass('lhea-hero').addClass('tlc-team-member-wrapper'); 268 }); 269 }); 270 271 /* Reduce the font size for long titles */ 272 273 $(document).ready(function() { 274 $('h1').each(function() { 275 if ($(this).text().length > 85) { 276 $(this).addClass('small-h1'); 277 } 278 }); 279 }); 280 281 282$(document).ready(function() { 283 function applyCheckedClass() {
284 $('.case-archive-filter .checkbox input[type="checkbox"]').each(function() { 285 if ($(this).is(':checked')) { 286 $(this).parent().addClass('checked'); 287 } else { 288 $(this).parent().removeClass('checked'); 289 } 290 }); 291 } 292 293 // Apply the checked class on initial load 294 applyCheckedClass(); 295 296 // Listen for changes and apply the checked class 297 $('.case-archive-filter').on('change', '.checkbox input[type="checkbox"]', applyCheckedClass); 298 299 // Reapply the checked class after the AJAX refresh 300 $(document).on('js_event_wpv_pagination_completed', applyCheckedClass); 301}); 302 303 /* Add #top to header */ 304 jQuery(document).ready(function($) { 305 $('header').attr('id', 'top'); 306 }); 307 308}( jQuery ) ); 309 310 311/* Smooth scroll with jQuery animate */ 312jQuery(document).ready(function($) { 313 $('a[href^="#"]').on('click keydown', function(event) { 314 // Check for click or keydown (Enter or Space) 315 if (event.type === 'keydown' && (event.which !== 13 && event.which !== 32)) { 316 return; 317 } 318 319 event.preventDefault(); 320 321 var targetId = $(this).attr('href'); 322 var targetElement = $(targetId); 323 324 if (targetElement.length) { 325 $('html, body').animate({ 326 scrollTop: targetElement.offset().top 327 }, 1000, function() { 328 // Callback after animation 329 window.location.hash = targetId; 330 }); 331 } 332 }); 333}); 334 335 336// fast action 337 338jQuery(document).ready(function($) { 339 340 function fixFastActionAccessibility() { 341 342 $('img.profile-link-fa-image:not([alt])').attr( 343 'alt', 344 'Fast Action log in' 345 ); 346 347 $('a#fastaction-whatsthis:not([aria-label])').attr( 348 'aria-label', 349 'What is Fast Action (opens new window)' 350 ); 351 352 } 353 354 // Run once in case FastAction already exists. 355 fixFastActionAccessibility(); 356 357 // Watch for FastAction being injected later. 358 const observer = new MutationObserver(function() { 359 fixFastActionAccessibility(); 360 }); 361 362 observer.observe(document.body, { 363 childList: true, 364 subtree: true 365 }); 366 367}); 368 369// Cases accordion 370 371 372 373jQuery(document).ready(function($) { 374 // When an accordion button is clicked
375 $('.case-accordion-button').on('click', function() { 376 // Toggle the "active" class on the button 377 $(this).toggleClass('active'); 378 // Change the "aria-expanded" attribute 379 var isExpanded = $(this).attr('aria-expanded') === 'true'; 380 $(this).attr('aria-expanded', !isExpanded); 381 // Show/hide the associated accordion content 382 var $content = $(this).next('.case-accordion-content'); 383 $content.slideToggle(200, function() { 384 // This callback function runs after the slideToggle animation is complete 385 if ($content.is(':visible')) { 386 adjustLineHeights($content); 387 appendPdfIconsAndSize($content); 388 } else { 389 // Remove the styles when the accordion is closed 390 $content.find('.cd-child-list').each(function() { 391 var $list = $(this); 392 var uniqueClass = $list.data('unique-class'); 393 if (uniqueClass) { 394 $('head style[data-unique-class="' + uniqueClass + '"]').remove(); 395 $list.removeData('unique-class'); 396 } 397 }); 398 } 399 }); 400 }); 401 402 function appendPdfIconsAndSize($context) { 403 console.log('Searching for PDFs in', $context); 404 $context.find('a[href$=".pdf"]').each(function() { 405 var $link = $(this); 406 if (!$link.data('pdf-icon-appended')) { // Check if the icon and size have already been appended 407 var fileSize = $link.data('filesize'); // Assuming file size is in a data-filesize attribute 408 409 if (fileSize) { 410 appendIconAndSize($link, fileSize); 411 } else { 412 // Try to fetch the file size via a HEAD request 413 $.ajax({ 414 url: $link.attr('href'), 415 type: 'HEAD', 416 success: function(data, status, xhr) { 417 var contentLength = xhr.getResponseHeader('Content-Length'); 418 if (contentLength) { 419 var fileSize = formatFileSize(contentLength); 420 appendIconAndSize($link, fileSize); 421 } 422 } 423 }); 424 } 425 } 426 }); 427 } 428 429 function appendIconAndSize($link, fileSize) { 430 // Create a visually hidden span for screen readers 431 var $screenReaderText = $('<span class="sr-only"> (PDF, ' + fileSize + ')</span>'); 432 $link.append($screenReaderText); 433 434 // Append the icon and visible text for sighted users 435 $link.append(' <i class="fas fa-file-pdf"></i> (' + fileSize + ')'); 436 437 $link.data('pdf-icon-appended', true); // Set a flag to indicate that the icon and size have been appended 438 } 439 440 function formatFileSize(bytes) { 441 var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; 442 if (bytes === 0) return '0 Byte'; 443 var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024))); 444 return Math.round(bytes / Math.pow(1024, i), 2) + ' ' + sizes[i]; 445 } 446 447 448 449 function adjustLineHeights($context) { 450 $context.find('.cd-child-list').each(function(index) { 451 var $list = jQuery(this); 452 var $items = $list.find('> li'); 453 454 if ($items.length > 0) { 455 var lastItemHeight = $items.last().outerHeight(); 456 var listHeight = $list.outerHeight(); 457 458 // Adding a unique class to each list 459 var uniqueClass = 'unique-list-' + index + '-' + new Date().getTime(); 460 $list.data('unique-class', uniqueClass); 461 462 // Remove old style rule if it exists 463 $('head style[data-unique-class="' + uniqueClass + '"]').remove(); 464 465 // Creating the dynamic style rule 466 var styleRule = '.' + uniqueClass + ':before { height: ' + (listHeight - (lastItemHeight / 2)) + 'px !important; }'; 467 $('<style>' + styleRule + '</style>') 468 .attr('data-unique-class', uniqueClass) 469 .appendTo('head'); 470 471 // Add the unique class to the list 472 $list.addClass(uniqueClass); 473 } 474 }); 475 } 476 477 // fixing the breadcrumbs 478 const $breadcrumbNav = $('.tlc-breadcrumbs > nav'); 479 const $breadcrumbWrapper = $breadcrumbNav.children('span'); 480 481 if ($breadcrumbWrapper.length) { 482 // create a new <ul> 483 const $ul = $('<ul class="kv-breadcrumb-list"></ul>'); 484 485 // iterate through the children of the wrapper span 486 $breadcrumbWrapper.contents().each(function() { 487 if (this.nodeType === 1 && this.nodeName === 'SPAN') { 488 const $li = $('<li class="kv-breadcrumb-item"></li>').append($(this).contents()); 489 if ($(this).hasClass('breadcrumb_last')) { 490 $li.attr('aria-current', 'page').addClass('kv-breadcrumb-current'); 491 } 492 $ul.append($li); 493 } 494 }); 495 496 497 // replace the wrapper <span> with the new <ul> 498 $breadcrumbWrapper.replaceWith($ul); 499 } 500 501 502 503});
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.