PageSourceSearch

https://blog.utc.edu/app/themes/utc-tailwind-genesis-theme/js/global.js?ver=2.13

js utc.edu collected 2026-09-24 08:39:02 UTC 12,344 bytes, 437 lines download raw bytes

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 = '&nbsp;';
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.