PageSourceSearch

https://pasadena.edu/_resources/js/main.js?v=1.4

js pasadena.edu collected 2026-09-24 06:24:01 UTC 14,722 bytes, 526 lines download raw bytes

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.