1// Hack for removing any preprod- instances if exist on production 2const OLD_URL = 'https://preprod-omniweb.pasadena.edu'; 3const NEW_URL = 'https://pasadena.edu'; 4 5document.querySelectorAll('html a[href]').forEach(link => { 6 const href = link.getAttribute('href'); 7 8 if (href && href.startsWith(OLD_URL)) { 9 link.setAttribute('href', href.replace(OLD_URL, NEW_URL)); 10 } 11}); 12 13// Main Site JS -- Originally created June 2024 14 15document.addEventListener('DOMContentLoaded', () => { 16 // TOOLTIPs (BS5 + legacy attr support) 17 document.querySelectorAll('[data-bs-toggle="tooltip"], [data-toggle="tooltip"]').forEach(el => { 18 // Must have a title/data-bs-title 19 const hasTitle = (el.getAttribute('title') || el.getAttribute('data-bs-title') || '').trim().length > 0; 20 if (hasTitle) { 21 bootstrap.Tooltip.getOrCreateInstance(el, { 22 container: 'body', 23 popperConfig: { strategy: 'fixed' } // â key for Translate banner/positioned ancestors 24 }); 25 } 26 }); 27 28 // POPOVERs (BS5 + legacy attr support) 29 document.querySelectorAll('[data-bs-toggle="popover"], [data-toggle="popover"]').forEach(el => { 30 // Needs data-bs-content (or provide content via options) 31 const hasContent = (el.getAttribute('data-bs-content') || '').trim().length > 0; 32 if (hasContent) { 33 bootstrap.Popover.getOrCreateInstance(el, { 34 container: 'body', 35 popperConfig: { strategy: 'fixed' } // â key for Translate banner/positioned ancestors 36 }); 37 } 38 }); 39}); 40 41// Notification Snippet Block Notification Hack to Ensure Hidden if display: none; 42document.addEventListener('DOMContentLoaded', () => { 43 document.querySelectorAll('.page-content-body .block-notification.d-flex').forEach(el => { 44 if (el.style.display === 'none') { 45 console.log('Removing hidden .block-notification:', el); 46 el.remove(); 47 } 48 }); 49}); 50 51// Page Titles 52jQuery('h1.section-title').addClass('section-title-loaded'); 53jQuery('h1.section-title-page').addClass('section-title-loaded'); 54 55/********************* 56Mobile Menu button, on show add class 57**********************/ 58document.addEventListener('DOMContentLoaded', function () { 59 const offcanvasEl = document.getElementById('offcanvas'); 60 const toggleBtn = document.getElementById('mobile-offcanvas-menu-button'); 61 62 if (offcanvasEl && toggleBtn) { 63 const bsOffcanvas = new bootstrap.Offcanvas(offcanvasEl); 64 65 offcanvasEl.addEventListener('shown.bs.offcanvas', function () { 66 toggleBtn.classList.add('active'); // or any custom class 67 }); 68 69 offcanvasEl.addEventListener('hidden.bs.offcanvas', function () { 70 toggleBtn.classList.remove('active'); 71 }); 72 } 73}); 74 75/************************* 76Hide Offcanvas Menu at greater than 992px 77**************************/ 78document.addEventListener('DOMContentLoaded', function () { 79 const offcanvasEl = document.querySelector('#offcanvas'); 80 if (offcanvasEl) { 81 const bsOffcanvas = bootstrap.Offcanvas.getOrCreateInstance(offcanvasEl); 82 const mql = window.matchMedia("(min-width: 992px)"); 83 84 function handleWidthChange(e) { 85 if (e.matches) { 86 bsOffcanvas.hide(); 87 } 88 } 89 90 // Run immediately 91 handleWidthChange(mql); 92 // Watch for resize 93 mql.addEventListener("change", handleWidthChange); 94 } 95}); 96 97/************************* 98Move all Modals to just before body 99*************************/ 100$(document).ready(function () { 101 $('.modal').appendTo('body'); 102 $('.mfp-hide').appendTo('body'); 103}); 104 105/******************************** 106Subnavigation 107********************************/ 108// Subnavigation update to BS 5.2 109$(document).ready(function() { 110 $('.subnavigation nav li:not(#subnav-more)').each(function() { 111 $(this).find('a.dropdown-toggle').attr('data-bs-toggle','dropdown'); 112 }); 113}); 114// Subnavigation Show on load 115$(window).load(function() { 116 $('.subnavigation').addClass('show-subnav'); 117}); 118 119// Subnavigation dropdown within overflow menu 120function subnavToggleFix() { 121 $('#subnav-overflow-container li.dropdown a.dropdown-toggle').on('click', function(event) { 122 event.stopImmediatePropagation(); 123 124 if($(this).hasClass('show')) { 125 $(this).attr('aria-expanded', 'false'); 126 } else { 127 $(this).attr('aria-expanded', 'true'); 128 } 129 $(this).closest('dropdown-menu').toggleClass('show'); 130 131 }); 132} 133 134// Subnavigation if items exceeds space 135$(document).ready(function () { 136 var subnavContainerWidth = $('.subnavigation-menu').width(); 137 var subnavMenuWidth = $('.subnavigation-menu nav').outerWidth(); 138 var subnavCtaWidth = $('.subnavigation-menu .btn').outerWidth(); 139 var moreButtonWidth = $('#subnav-more').outerWidth(); 140 141 var overflowDiv = $('#subnav-overflow-container'); 142 143 var total = 0; 144 var exceeded = false;
145 146 // Determine if we show or hide elements based on the available horz space 147 $('.subnavigation nav > ul > li:not(#subnav-more)').each(function(){ 148 var width = $(this).outerWidth(); 149 if(width + total + subnavCtaWidth > subnavMenuWidth - moreButtonWidth || exceeded == true) { 150 $(this).clone().appendTo(overflowDiv); 151 $(this).hide(); 152 exceeded = true; 153 } else { 154 $(this).show(); 155 total += width; 156 } 157 }); 158 159 // Show / Hide More Drop down 160 if(exceeded == true) { 161 $('#subnav-more').addClass('subnav-more-show'); 162 } else { 163 $('#subnav-more').removeClass('subnav-more-show'); 164 } 165 166 subnavToggleFix(); 167 168}); 169// Subnavigation if items exceeds space 170$(window).resize(function () { 171 var subnavContainerWidth = $('.subnavigation-menu').width(); 172 var subnavMenuWidth = $('.subnavigation-menu nav').outerWidth(); 173 var subnavCtaWidth = $('.subnavigation-menu .btn').outerWidth(); 174 var moreButtonWidth = $('#subnav-more').outerWidth(); 175 176 // clear out the overflow-container 177 var overflowDiv = $('#subnav-overflow-container'); 178 $('#subnav-overflow-container').html(''); 179 180 var total = 0; 181 var exceeded = false; 182 183 // Determine if we show or hide elements based on the available horz space 184 $('.subnavigation nav > ul > li:not(#subnav-more)').each(function(){ 185 var width = $(this).outerWidth(); 186 if(width + total + subnavCtaWidth > subnavMenuWidth - moreButtonWidth || exceeded == true) { 187 $(this).clone().appendTo(overflowDiv); 188 $(this).hide(); 189 exceeded = true; 190 } else { 191 $(this).show(); 192 total += width; 193 } 194 }); 195 196 // Show / Hide More Drop down 197 if(exceeded == true) { 198 $('#subnav-more').addClass('subnav-more-show'); 199 } else { 200 $('#subnav-more').removeClass('subnav-more-show'); 201 } 202 203 subnavToggleFix(); 204 205}); 206 207// Subnav Current Page 208document.addEventListener("DOMContentLoaded", () => { 209 const currentPath = window.location.pathname.replace(/\/+$/, ""); 210 const links = document.querySelectorAll(".subnavigation-menu a"); 211 212 links.forEach(link => { 213 const href = link.getAttribute("href"); 214 215 // Skip empty, hash, or JS pseudo links 216 if ( 217 !href || 218 href === "#" || 219 href.startsWith("#") || 220 href.startsWith("javascript:") 221 ) { 222 return; 223 } 224 225 const url = new URL(link.href, window.location.origin); 226 const linkPath = url.pathname.replace(/\/+$/, ""); 227 228 if (linkPath === currentPath) { 229 link.classList.add("current-page"); 230 } 231 }); 232}); 233 234// Mobile Subnav / Side Nav Menu Item Toggle 235/*$(document).ready(function() { 236 $('#mobile-side-sub-nav .dropdown-toggle').on('click', function(event) { 237 event.preventDefault; 238 $(this).closest('.dropdown').find('ul.dropdown-menu').toggleClass('show'); 239 $(this).toggleClass('shown'); 240 }); 241}); 242$(document).ready(function() { 243 $('#mobile-side-sub-nav a[data-toggle="collapse"]').on('click', function(event) { 244 event.preventDefault; 245 $(this).closest('li').find('ul.list-group-submenu').toggleClass('show'); 246 $(this).toggleClass('shown'); 247 }); 248});*/ 249 250// On scroll up, sticky nav on desktop 251let lastScrollTop = 0; 252const header = document.getElementById('site-header'); 253const subnavigation = document.querySelector('.subnavigation'); 254if (header) { 255 if(!subnavigation){ 256 window.addEventListener("scroll", function() { 257 let currentScroll = window.pageYOffset || document.documentElement.scrollTop; 258 259 if (currentScroll > 120) { // Trigger after scrolling 100px from top 260 if (currentScroll > lastScrollTop) { 261 // Scroll down 262 header.classList.remove('sticky-lg-top'); 263 header.classList.add('unstuck'); 264 } else { 265 // Scroll up 266 header.classList.add('sticky-lg-top'); 267 header.classList.remove('unstuck'); 268 } 269 } 270 lastScrollTop = currentScroll; 271 }); 272 } 273} 274 275// Match Height Utility 276$(document).ready(function(){ 277 $('.match-height-all').matchHeight({ 278 byRow: false, 279 property: 'height' 280 }); 281 282 $('.match-height-row').matchHeight({ 283 byRow: true, 284 property: 'height' 285 }); 286}); 287 288// Data AOS Init 289AOS.init(); 290 291 292/*------------------Magnific Popup-------------------*/ 293 294// Single image lightbox 295$('.image-link').magnificPopup({ 296 type: 'image', 297 mainClass: 'mfp-with-zoom', // this class is for CSS animation below 298 299 zoom: { 300 enabled: true, // By default it's false, so don't forget to enable it 301 302 duration: 300, // duration of the effect, in milliseconds 303 easing: 'ease-in-out', // CSS transition easing function 304 305 // The "opener" function should return the element from which popup will be zoomed in 306 // and to which popup will be scaled down 307 // By defailt it looks for an image tag: 308 opener: function(openerElement) { 309 // openerElement is the element on which popup was initialized, in this case its <a> tag 310 // you don't need to add "opener" option if this code matches your needs, it's defailt one. 311 return openerElement.is('img') ? openerElement : openerElement.find('img'); 312 } 313 }, 314 image: { 315 // options for image content type 316 titleSrc: 'alt' 317 } 318}); 319 320// Single image lightbox 321$('.link-to-image').magnificPopup({
322 type: 'image', 323 mainClass: 'mfp-with-zoom', // this class is for CSS animation below 324}); 325 326// Image gallery lightbox 327$('.popup-gallery').each(function() { // the containers for all your galleries 328 $(this).find('a').magnificPopup({ 329 type: 'image', 330 gallery: { 331 enabled:true 332 }, 333 image: { 334 // options for image content type 335 titleSrc: function(item) { 336 return item.img.attr('alt'); 337 } 338 } 339 }); 340}); 341 342/* Video lightbox */ 343$('.popup-youtube, .popup-vimeo, .popup-gmaps').magnificPopup({ 344 disableOn: 700, 345 type: 'iframe', 346 mainClass: 'mfp-fade', 347 removalDelay: 160, 348 preloader: false, 349 fixedContentPos: false 350}); 351 352/* YouTube API v3 lightbox */ 353$('.vid-thumb').magnificPopup({ 354 type:'inline', 355 midClick: true, // Allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source in href. 356 closeBtnInside: true 357}); 358 359/* Modals for Magnific Popup */ 360$(function () { 361 $('.popup-modal').magnificPopup({ 362 type: 'inl
362ine', 363 preloader: false, 364 modal: true 365 }); 366 $(document).on('click', '.popup-modal-dismiss', function (e) { 367 e.preventDefault(); 368 $.magnificPopup.close(); 369 }); 370}); 371 372/* Modal with content */ 373$(document).ready(function() { 374 375 $('.popup-modal-content').magnificPopup({ 376 type: 'inline', 377 preloader: false, 378 379 // When elemened is focused, some mobile browsers in some cases zoom in 380 // It looks not nice, so we disable it: 381 callbacks: { 382 beforeOpen: function() { 383 if($(window).width() < 700) { 384 this.st.focus = false; 385 } else { 386 this.st.focus = '#name'; 387 } 388 } 389 } 390 }); 391 $(document).on('click', '.popup-modal-dismiss', function (e) { 392 e.preventDefault(); 393 $.magnificPopup.close(); 394 }); 395}); 396 397/*------------------------------/ Magnific Popup ------------------------------*/ 398 399// CSV to Table Datatables Init 400document.addEventListener('DOMContentLoaded', function () { 401 document.querySelectorAll('.js-datatable').forEach(function (table) { 402 const hideSearch = table.classList.contains('dt-hide-search'); 403 404 new DataTable(table, { 405 paging: false, 406 info: false, 407 responsive: true, 408 pageLength: 25, 409 autoWidth: false, 410 411 layout: { 412 topStart: 'pageLength', 413 topEnd: hideSearch ? null : 'search', 414 bottomStart: 'info', 415 bottomEnd: 'paging' 416 } 417 }); 418 }); 419}); 420 421// Table to Datatable 422document.addEventListener('DOMContentLoaded', function () { 423 document 424 .querySelectorAll('.table-to-datatable table') 425 .forEach(function (table) { 426 427 if (table.dataset.datatableInitialized === 'true') return; 428 429 const container = table.closest('.table-to-datatable'); 430 431 const searchHide = 432 container?.getAttribute('data-td-search-hide')?.trim().toLowerCase() === 'true'; 433 434 const searchLabel = 435 container?.getAttribute('data-td-search-label')?.trim() || 'Search'; 436 437 table.classList.add('table', 'display'); 438 439 normalizeTableStructure(table); 440 441 new DataTable(table, { 442 paging: false, 443 info: false, 444 responsive: true, 445 pageLength: 25, 446 autoWidth: false, 447 448 language: { 449 search: searchLabel + ':' 450 }, 451 452 layout: { 453 topStart: null, 454 topEnd: searchHide ? null : 'search', 455 bottomStart: null, 456 bottomEnd: null 457 } 458 }); 459 460 table.dataset.datatableInitialized = 'true'; 461 }); 462}); 463 464function normalizeTableStructure(table) { 465 let thead = table.querySelector(':scope > thead'); 466 let tbody = table.querySelector(':scope > tbody'); 467 468 // If thead exists, make sure tbody exists 469 if (thead) { 470 if (!tbody) { 471 tbody = document.createElement('tbody'); 472 473 Array.from(table.querySelectorAll(':scope > tr')).forEach(function (row) { 474 tbody.appendChild(row); 475 }); 476 477 table.appendChild(tbody); 478 } 479 480 return; 481 } 482 483 // Get rows from existing tbody or direct table children 484 const rows = tbody 485 ? Array.from(tbody.querySelectorAll(':scope > tr')) 486 : Array.from(table.querySelectorAll(':scope > tr')); 487 488 if (!rows.length) return; 489 490 const firstRow = rows.shift(); 491 492 // Create thead 493 thead = document.createElement('thead'); 494 495 // Convert first row cells to th 496 Array.from(firstRow.children).forEach(function (cell) { 497 if (cell.tagName.toLowerCase() !== 'th') { 498 const th = document.createElement('th'); 499 500 th.innerHTML = cell.innerHTML; 501 502 Array.from(cell.attributes).forEach(function (attr) { 503 th.setAttribute(attr.name, attr.value); 504 }); 505 506 cell.replaceWith(th); 507 } 508 }); 509 510 thead.appendChild(firstRow); 511 512 // Create/rebuild tbody 513 const newTbody = document.createElement('tbody'); 514 515 rows.forEach(function (row) { 516 newTbody.appendChild(row); 517 }); 518 519 if (tbody) { 520 tbody.replaceWith(newTbody); 521 } else { 522 table.appendChild(newTbody); 523 } 524 525 table.insertBefore(thead, table.firstChild); 526}
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.