1/** 2 * This script adds the jquery effects to the UTC Tailwind Genesis Theme. 3 * 4 * @package UTC\JS 5 * @author StudioPress/Bridget Hornsby 6 * @license GPL-2.0-or-later 7 */ 8 9//updated 3/22/2026 10//console.log("globlal JS loaded."); 11 12(function ($) { 13 // Cache DOM selectors. 14 var $container = $('.site-container'), 15 $header = $('.site-header'), 16 $hsToggle = $('.toggle-header-search'), 17 $hsWrap = $('#header-search-wrap'), 18 $hsInput = $hsWrap.find('input[type="search"]'); 19 20 // Make sure JS class is added. 21 $(document).ready(function () { 22 $('body').addClass('js'); 23 }); 24 25 // Run on page scroll. 26 $(window).scroll(function () { 27 // Toggle header class after threshold point. 28 if (50 < $(document).scrollTop()) { 29 $('.site-container').addClass('shadow'); 30 } else { 31 $('.site-container').removeClass('shadow'); 32 } 33 }); 34 35 /*****************Important Search functionality****************/ 36 // Handler for click a show/hide button. 37 38 $hsToggle.on('click', function (event) { 39 event.preventDefault(); 40 41 if ($(this).hasClass('close')) { 42 hideSearch(); 43 } else { 44 showSearch(); 45 } 46 }); 47 48 // Handler for pressing show/hide button. 49 $hsToggle.on('keydown', function (event) { 50 // If tabbing from toggle button, and search is hidden, exit early. 51 if (9 === event.keyCode && !$header.hasClass('search-visible')) { 52 return; 53 } 54 event.preventDefault(); 55 handleKeyDown(event); 56 }); 57 58 // Hide search when tabbing or escaping out of the search bar. 59 $hsInput.on('keydown', function (event) { 60 // Tab: 9, Esc: 27. 61 if (9 === event.keyCode || 27 === event.keyCode) { 62 hideSearch(event.target); 63 } 64 }); 65 66 // Hide search on blur, such as when clicking outside it. 67 $hsInput.on('blur', hideSearch); 68 69 // Helper function to show the search form. 70 function showSearch() { 71 $header.addClass('search-visible'); 72 $hsWrap.slideDown('fast').find('input[type="search"]').focus(); 73 $hsToggle.attr('aria-expanded', true); 74 } 75 76 // Helper function to hide the search form. 77 function hideSearch() { 78 $hsWrap 79 .slideUp('fast') 80 .parents('.site-header') 81 .removeClass('search-visible'); 82 $hsToggle.attr('aria-expanded', false); 83 } 84
85 // Keydown handler function for toggling search field visibility. 86 function handleKeyDown(event) { 87 // Enter/Space, respectively. 88 if (13 === event.keyCode || 32 === event.keyCode) { 89 event.preventDefault(); 90 91 if ($(event.target).hasClass('close')) { 92 hideSearch(); 93 } else { 94 showSearch(); 95 } 96 } 97 } 98 99 /*****************Important Main Menu functionality****************/ 100 101 $('.genesis-nav-menu.menu-primary').addClass('level-1'); 102 $( 103 '.genesis-nav-menu.menu-primary > li.menu-item-has-children > .sub-menu', 104 ).addClass('level-2'); 105 106 $('.genesis-nav-menu.menu-primary > li.menu-item-has-children').each( 107 function () { 108 $(this).click(function () { 109 if ($(this).hasClass('active')) { 110 $(this).removeClass('active'); 111 $(this).parent().removeClass('open-sub-menu'); 112 } else { 113 $(this).parent().find('li.active').removeClass('active'); 114 $(this).addClass('active'); 115 if ($(this).is(':last-child')) { 116 $(this).parent().addClass('open-sub-menu'); 117 } else { 118 $(this).parent().removeClass('open-sub-menu'); 119 } 120 } 121 }); 122 }, 123 ); 124 $('#genesis-mobile-nav-primary').click(function () { 125 $(this).toggleClass('activated'); 126 }); 127 128 /*****************Convert categories and archive lists into ul menus****************/ 129 $(function () { 130 const $widget = $('.widget_categories'); 131 const $ul = $widget.find('.widget-wrap > ul').first(); 132 133 if (!$ul.length) return; 134 135 // Wrap UL 136 $ul.wrap('<div class="category-toggle-panel" style="display:none;"></div>'); 137 138 // Add button 139 const $btn = $( 140 '<button class="category-toggle-btn" aria-expanded="false">' + 141 '<span class="label">Select one...</span>' + 142 '<span class="arrow"></span>' + 143 '</button>', 144 ); 145 146 $btn.insertAfter($widget.find('.widget-title')); 147 148 // Toggle main panel 149 $widget.on('click', '.category-toggle-btn', function () { 150 const $panel = $(this).next('.category-toggle-panel'); 151 const isOpen = $panel.is(':visible'); 152 153 $panel.slideToggle(150); 154 $(this).attr('aria-expanded', !isOpen).toggleClass('is-open', !isOpen); 155 }); 156 157 // ===== SUBMENU LOGIC ===== 158 159 // Find only categories with a DIRECT child submenu. 160 $widget 161 .find('li') 162 .filter(function () { 163 return $(this).children('ul.children').length > 0; 164 }) 165 .each(function (index) { 166 const $li = $(this); 167 const $link = $li.children('a').first(); 168 const $submenu = $li.children('ul.children').first(); 169 170 $li.addClass('has-submenu'); 171 $submenu.hide(); 172 173 // Give submenu an ID for accessibility. 174 if (!$submenu.attr('id')) { 175 $submenu.attr('id', 'category-submenu-' + index); 176 } 177 178 // Add a real arrow button after the category link. 179 if (!$li.children('.category-submenu-toggle').length) { 180 const categoryName = $link.text().trim(); 181 182 const $button = $('<button>', { 183 type: 'button', 184 class: 'category-submenu-toggle', 185 'aria-expanded': 'false', 186 'aria-controls': $submenu.attr('id'), 187 'aria-label': 'Show subcategories for ' + categoryName, 188 }); 189 190 $button.insertAfter($link); 191 } 192 }); 193 194 // ONLY the arrow button toggles the submenu. 195 $widget.on('click', '.category-submenu-toggle', function (e) { 196 e.preventDefault(); 197 e.stopPropagation(); 198 199 const $button = $(this); 200 const $li = $button.closest('li.has-submenu'); 201 const $submenu = $li.children('ul.children').first(); 202 203 const isOpen = $button.attr('aria-expanded') === 'true'; 204 205 if (isOpen) { 206 $submenu.stop(true, true).slideUp(150); 207 } else { 208 $submenu.stop(true, true).slideDown(150); 209 } 210 211 $li.toggleClass('submenu-open', !isOpen); 212 $button.attr('aria-expanded', String(!isOpen)); 213 214 const categoryName = $li.children('a').first().text().trim(); 215 216 $button.attr( 217 'aria-label', 218 (isOpen ? 'Show' : 'Hide') + ' subcategories for ' + categoryName, 219 ); 220 }); 221 }); 222 223 /*****************Make sidebar menus reponsive****************/ 224 $('#left-footer-menu').parent().parent().parent().attr('id', 'footer-menus'); 225 $('#left-footer-map').parent().parent().parent().attr('id', 'footer-map'); 226 $('aside #footer-menus, aside #footer-map').remove(); 227
228 /*****************Modernize the share button****************/ 229 $( 230 '<i class="fa fa-share-alt-square utc-share" title="Share this article."></i>', 231 ).appendTo('a.addtoany_share'); 232 233 /*****************Add back to top eased scrolling functionality****************/ 234 jQuery(document).ready(function ($) { 235 // Scroll (in pixels) after which the "To Top" link is shown 236 var offset = 300, 237 //Scroll (in pixels) after which the "back to top" link opacity is reduced 238 offset_opacity = 1200, 239 //Duration of the top scrolling animation (in ms) 240 scroll_top_duration = 700, 241 //Get the "To Top" link 242 $back_to_top = $('.to-top'); 243 244 //Visible or not "To Top" link 245 $(window).scroll(function () { 246 $(this).scrollTop() > offset 247 ? $back_to_top.addClass('top-is-visible') 248 : $back_to_top.removeClass('top-is-visible top-fade-out'); 249 if ($(this).scrollTop() > offset_opacity) { 250 $back_to_top.addClass('top-fade-out'); 251 } 252 }); 253 254 //Smoothy scroll to top 255 //$back_to_top.css('background-image','../images/to-top.svg'); 256 $back_to_top.on('click', function (event) { 257 event.preventDefault(); 258 $('body,html').animate( 259 { 260 scrollTop: 0, 261 }, 262 scroll_top_duration, 263 ); 264 }); 265 }); 266 //Another class added with a shorter scroll 267 $(window).scroll(function () { 268 if ($(this).scrollTop() > 50) { 269 $('body').addClass('scrolled'); 270 } else { 271 $('body').removeClass('scrolled'); 272 } 273 }); 274 $('.sidebar .widget_nav_menu .widget-wrap').each(function () { 275 if (!($(this).find('h3').length > 0)) { 276 $('<h3 class="widgettitle widget-title">Section Menu</h3>').prependTo( 277 this, 278 ); 279 } 280 }); 281 /*****************Fix OEmbed issues that OCM makes frequently****************/ 282 283 const bodyHasFirstParagraphTrue = document.body.classList.contains( 284 'first-paragraph-true', 285 ); 286 const entryContent = document.querySelector('.entry-content'); 287 288 $('.entry-content:has(iframe) p:first-child').addClass('first-child'); 289 $('.entry-content p:has(iframe)').parent().addClass('first-has-iframe'); 290 291 /****B/C Safari won't play nice with the Apply Now transitions, we're going to add the browser as a body class****/ 292 var BrowserDetect = { 293 init: function () { 294 this.browser = this.searchString(this.dataBrowser) || 'Other'; 295 this.version = 296 this.searchVersion(navigator.userAgent) || 297 this.searchVersion(navigator.appVersion) || 298 'Unknown'; 299 }, 300 searchString: function (data) { 301 for (var i = 0; i < data.length; i++) { 302 var dataString = data[i].string; 303 this.versionSearchString = data[i].subString; 304 if (dataString.indexOf(data[i].subString) !== -1) { 305 return data[i].identity; 306 } 307 } 308 }, 309 searchVersion: function (dataString) { 310 var index = dataString.indexOf(this.versionSearchString); 311 if (index === -1) { 312 return; 313 } 314 var rv = dataString.indexOf('rv:'); 315 if (this.versionSearchString === 'Trident' && rv !== -1) { 316 return parseFloat(dataString.substring(rv + 3)); 317 } else { 318 return parseFloat( 319 dataString.substring(index + this.versionSearchString.length + 1), 320 ); 321 } 322 }, 323 dataBrowser: [ 324 { 325 string: navigator.userAgent, 326 subString: 'Edge', 327 identity: 'MS Edge', 328 }, 329 { 330 string: navigator.userAgent, 331 subString: 'MSIE', 332 identity: 'Explorer', 333 }, 334 { 335 string: navigator.userAgent, 336 subString: 'Trident', 337 identity: 'Explorer', 338 }, 339 { 340 string: navigator.userAgent, 341 subString: 'Firefox', 342 identity: 'Firefox', 343 }, 344 { 345 string: navigator.userAgent, 346 subString: 'Opera', 347 identity: 'Opera', 348 }, 349 { 350 string: navigator.userAgent, 351 subString: 'OPR', 352 identity: 'OPR', 353 }, 354 { 355 string: navigator.userAgent, 356 subString: 'Netscape', 357 identity: 'Netscape', 358 }, 359 { 360 string: navigator.userAgent, 361 subString: 'Chrome', 362 identity: 'Chrome', 363 }, 364 { 365 string: navigator.userAgent, 366 subString: 'Safari', 367 identity: 'Safari', 368 }, 369 ], 370 }; 371 BrowserDetect.init(); 372 var bv = BrowserDetect.browser; 373 if (bv == 'Chrome') { 374 $('body').addClass('chrome'); 375 } else if (bv == 'MS Edge') { 376 $('body').addClass('edge'); 377 } else if (bv == 'Explorer') { 378 $('body').addClass('ie'); 379 } else if (bv == 'Firefox') { 380 $('body').addClass('firefox'); 381 } else if (bv == 'Safari') { 382 $('body').addClass('safari'); 383 } else if (bv == 'Netscape') { 384 $('body').addClass('opera'); 385 } else { 386 $('body').addClass('browser-unknown'); 387 } 388})(jQuery); 389
390/*****************Move any department info block from content into the footer*******/ 391if (document.querySelector('#department-footer')) { 392 let moveThisDiv = document.querySelector('#department-footer'); 393 let destinationFooter = document.querySelector('.site-footer'); 394 deptFooter = destinationFooter.prepend(moveThisDiv); 395 moveThisDiv.style.visibility = 'visible'; 396} 397 398/*****************Make sidebar menus reponsive****************/ 399const bodyHasClass = document.body.classList.contains('sidebar-content'); 400const sidebarHasMenu = document.querySelector('.navigation_widget'); 401const hamburgerMenu = document.createElement('i'); 402hamburgerMenu.className = 'fa fa-bars float-right text-xl'; 403 404if (bodyHasClass && sidebarHasMenu) { 405 document 406 .querySelector('.sidebar .navigation_widget .widget-title') 407 .appendChild(hamburgerMenu); 408 hamburgerMenu.innerHTML = ' '; 409} 410 411/***************** Update video iframes for custom responsive application 3.19.26 *****************/ 412 413document.querySelectorAll('iframe').forEach((iframe) => { 414 const src = iframe.getAttribute('src') || ''; 415 416 if (src.includes('youtu') || src.includes('vimeo')) { 417 const parent = iframe.parentElement; 418 419 // If already wrapped correctly, skip 420 if (parent.classList.contains('video-wrapper')) return; 421 422 // Create new wrapper 423 const wrapper = document.createElement('div'); 424 wrapper.classList.add('video-wrapper'); 425 426 // Replace <p> (or any parent) with wrapper 427 parent.parentNode.insertBefore(wrapper, parent); 428 429 // Move iframe into wrapper 430 wrapper.appendChild(iframe); 431 432 // Remove old parent if it's just a container (like <p>) 433 if (parent.tagName.toLowerCase() === 'p') { 434 parent.remove(); 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.