1$(document).ready(function () { 2 $('.searchcollapse').on('shown.bs.collapse', function () { 3 $("#text-search").focus(); 4 }); 5 6 var xy = document.getElementsByClassName(".full-search-btn"); 7 8 $(xy).click(function (event) { 9 event.stopPropagation(); 10 $('.searchcollapse').collapse('toggle'); 11 $("#text-search").focus(); 12 }); 13 14 $(document).click(function (e) { 15 var searchbar = $(".searchcollapse"); 16 var txtboxsearch = $("#text-search"); 17 var btnsearch = $(".full-search-btn"); 18 19 if (!searchbar.is(e.target) && !txtboxsearch.is(e.target) && !btnsearch.is(e.target)) { 20 $('.searchcollapse').collapse('hide') 21 } 22 }); 23}); 24 25// Main Site Nav 26$(function () { 27 $('[data-nav]').nav(); 28}); 29 30// Mobile nav 31document.addEventListener( 32 "DOMContentLoaded", () => { 33 new Mmenu("#offCanvasMenu", { 34 offCanvas: { 35 position: "right" 36 }, 37 theme: "dark", 38 counters: { 39 add: true 40 }, 41 scrollBugFix: false, 42 }); 43 } 44); 45 46// Folder Nav Script 47(function (window, document, undefined) { 48 function closeMenu() { 49 $('.main-menu__open').removeClass('main-menu__open'); 50 } 51 52 $(window).on('load', function () { 53 $('.menu-btn a').click(function (e) { 54 e.preventDefault(); 55 e.stopPropagation(); 56 $(this).toggleClass('main-menu__open') 57 $('.custom-navbar').toggleClass('main-menu__open'); 58 }); 59 60 $('.menu .menu-item--expanded').children('a').wrap("<div class='toggle_wrap'></div>").parent().append('<span class="menu-toggle icon ion-ios-plus-outline"></span>'); 61 62 $('.block-expanded-menu-block .is-active').parents().each(function () { 63 if ($(this).hasClass('menu-item--expanded')) { 64 $(this).addClass('menu-item--active-trail menu-open'); 65 $(this).children('.toggle_wrap').children('.menu-toggle').removeClass('ion-ios-plus-outline').addClass('ion-ios-minus-outline'); 66 } 67 }); 68 69 $('.menu-toggle').click(function () { 70 $parent = $(this).parent().parent(); 71 72 if ($parent.hasClass('menu-open')) { 73 $parent.removeClass('menu-open'); 74 $(this).removeClass('ion-ios-minus-outline').addClass('ion-ios-plus-outline'); 75 } 76 else { 77 $parent.addClass('menu-open'); 78 $(this).removeClass('ion-ios-plus-outline').addClass('ion-ios-minus-outline'); 79 } 80 }); 81 82 $(document).on('click', function (e) { 83 $('.menu--audience').removeClass('open'); 84 }); 85 }); 86})(this, this.document); 87 88// Details and Summary 89const details = document.querySelectorAll("details"); 90const detailsAnimationDuration = 300; 91const detailsAnimations = new WeakMap(); 92 93const detailsOptions = (detail) => ({ 94 animate: detail.dataset.detailsAnimate === "true", 95 collapseSiblings: detail.dataset.detailsCollapseSiblings !== "false", 96 scroll: detail.dataset.detailsScroll !== "false" 97}); 98 99const resetDetailsContentStyles = (content) => { 100 content.style.height = ""; 101 content.style.overflow = ""; 102}; 103 104const animateDetailsToggle = (detail, shouldOpen) => { 105 const summary = detail.querySelector("summary"); 106 107 if (!summary || !summary.nextElementSibling) { 108 detail.open = shouldOpen; 109 return; 110 } 111 112 const content = summary.nextElementSibling; 113 const currentAnimation = detailsAnimations.get(detail); 114 115 if (currentAnimation) { 116 currentAnimation.cancel(); 117 } 118 119 if (shouldOpen) { 120 detail.open = true; 121 content.style.height = "0"; 122 content.style.overflow = "hidden"; 123 } 124 125 const startHeight = shouldOpen ? 0 : content.offsetHeight; 126 const endHeight = shouldOpen ? content.scrollHeight : 0; 127 content.style.height = `${startHeight}px`; 128 content.style.overflow = "hidden"; 129 130 const animation = content.animate( 131 [ 132 { height: `${startHeight}px` }, 133 { height: `${endHeight}px` } 134 ], 135 { duration: detailsAnimationDuration, easing: "ease-in-out" } 136 ); 137 138 detailsAnimations.set(detail, animation); 139 animation.onfinish = () => { 140 detail.open = shouldOpen; 141 resetDetailsContentStyles(content);
142 detailsAnimations.delete(detail); 143 }; 144 animation.oncancel = () => { 145 detailsAnimations.delete(detail); 146 }; 147}; 148 149details.forEach((targetDetail) => { 150 targetDetail.addEventListener("click", (event) => { 151 const options = detailsOptions(targetDetail); 152 const target = event.target instanceof Element ? event.target : null; 153 const summary = target ? target.closest("summary") : null; 154 155 if (options.animate && summary && summary.parentElement === targetDetail) { 156 event.preventDefault(); 157 animateDetailsToggle(targetDetail, !targetDetail.open); 158 } 159 160 if (!options.scroll && !options.collapseSiblings) { 161 return; 162 } 163 164 details.forEach((detail) => { 165 if (detail == targetDetail && options.scroll) { 166 detail.scrollIntoView(); 167 } 168 else if (detail !== targetDetail && options.collapseSiblings) { 169 detail.removeAttribute("open"); 170 } 171 }); 172 }); 173}); 174 175// Toggle open when URL fragment matches detail-summary identifier 176const openActiveDetail = (url) => { 177 let indexOfHash = url.indexOf("#"); 178 179 if (indexOfHash !== -1) { 180 let hash = url.substring(indexOfHash).trim(); 181 182 if (hash.length > 1) { 183 let summaryId = hash.substring(1); 184 let summary = document.querySelector(`summary#${summaryId}`); 185 186 if (summary !== null) { 187 let detail = summary.parentElement; 188 const options = detailsOptions(detail); 189 190 if (options.collapseSiblings) { 191 this.document.querySelectorAll("details[open]").forEach((target) => { 192 target.removeAttribute("open"); 193 }); 194 } 195 196 if (details) { 197 detail.open = true; 198 199 if (options.scroll) { 200 detail.scrollIntoView(); 201 } 202 } 203 } 204 } 205 } 206}; 207 208window.addEventListener("load", () => openActiveDetail(window.location.href)); 209window.addEventListener("hashchange", (event) => openActiveDetail(event.newURL)); 210 211// Carousel video pause 212function onYouTubeIframeAPIReady() { 213 const videoCarousels = document.querySelectorAll(".video-carousel"); 214 videoCarousels.forEach((targetVideoCarousel) => { 215 const players = []; 216 const iframeSelector = "iframe[src^='https://www.youtube.com/embed']"; 217 const iframes = targetVideoCarousel.querySelectorAll(iframeSelector); 218 iframes.forEach((iframe, index) => { 219 players[index] = new YT.Player(iframe, { 220 events: { 221 "onReady": (event) => { 222 event.target.pauseVideo(); 223 } 224 } 225 }); 226 }); 227 targetVideoCarousel.addEventListener("slide.bs.carousel", (e) => { 228 players.forEach((player) => { 229 player.pauseVideo(); 230 }); 231 }); 232 }); 233} 234 235// Card Scroller 236function setCardScroller(uniqueId) { 237 document.getElementById(`nextButton-${uniqueId}`).addEventListener('click', function () { 238 const cardsWrapper = document.querySelector(`#cardCarousel-${uniqueId} .cards-wrapper`); 239 const cardWidth = document.querySelector(`#cardCarousel-${uniqueId} .singlecard`).offsetWidth; 240 const scrollAmount = cardWidth * 1; 241 cardsWrapper.scrollBy({ left: scrollAmount, behavior: 'smooth' }); 242 }); 243 244 document.getElementById(`prevButton-${uniqueId}`).addEventListener('click', function () { 245 const cardsWrapper = document.querySelector(`#cardCarousel-${uniqueId} .cards-wrapper`); 246 const cardWidth = document.querySelector(`#cardCarousel-${uniqueId} .singlecard`).offsetWidth; 247 const scrollAmount = -cardWidth * 1; 248 cardsWrapper.scrollBy({ left: scrollAmount, behavior: 'smooth' }); 249 }); 250} 251 252// Program Listing: initializer 253(() => { 254 // Selectors and configuration 255 const SELECTOR = Object.freeze({ 256 root: '.program-listing',
257 details: '.filter-accordion details', 258 summary: '.filter-accordion summary', 259 icon: 'summary .toggle-icon', 260 chip: '[data-program-chip]', 261 keywordInputName: 'KeyWords', 262 filterInput: 'input[data-program-filter-item]' 263 }); 264 265 // Initialization guard 266 const isInitialized = (el) => el.hasAttribute('data-program-listing-init'); 267 const markInitialized = (el) => el.setAttribute('data-program-listing-init', 'true'); 268 269 // DOM helpers 270 const getDetails = (container) => Array.from(container.querySelectorAll(SELECTOR.details)); 271 const getIcon = (detail) => detail.querySelector(SELECTOR.icon); 272 const escapeCss = (v) => (window.CSS && CSS.escape) ? CSS.escape(v) : String(v); 273 274 // Icon synchronization 275 const syncIcon = (detail) => { 276 const icon = getIcon(detail); 277 if (!icon) return; 278 icon.classList.toggle('fa-plus', !detail.open); 279 icon.classList.toggle('fa-minus', detail.open); 280 }; 281 const syncAllIcons = (container) => getDetails(container).forEach(syncIcon); 282 283 // Accordion state management 284 const closeSiblings = (container, current) => getDetails(container).forEach((d) => { if (d !== current) d.removeAttribute('open'); }); 285 286 // Prevent sitewide scroll for accordion interactions 287 function stopScrollIfAccordionClick(e, container) { 288 const target = e.target instanceof Element ? e.target : null; 289 if (!target) return false; 290 const summary = target.closest(SELECTOR.summary); 291 if (summary && container.contains(summary)) { 292 if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation(); 293 e.stopPropagation(); 294 return true; 295 } 296 const detailsEl = target.closest(SELECTOR.details); 297 if (detailsEl && container.contains(detailsEl)) { 298 if (typeof e.stopImmediatePropagation === 'function') e.stopImmediatePropagation(); 299 e.stopPropagation(); 300 return true; 301 } 302 return false; 303 } 304 305 // Clear the filter represented by a chip 306 function clearChip(container, chip) { 307 const type = chip.getAttribute('data-program-chip'); 308 const name = chip.getAttribute('data-filter-name'); 309 const value = chip.getAttribute('data-filter-value'); 310 311 if (type === 'keyword') { 312 const kw = container.querySelector(`input[name="${SELECTOR.keywordInputName}"]`); 313 if (kw) kw.value = ''; 314 return; 315 } 316 317 if (type === 'filter' && name && value) { 318 const selector = `${SELECTOR.filterInput}[name="${escapeCss(name)}"][value="${escapeCss(value)}"]`; 319 const input = container.querySelector(selector); 320 if (input) input.checked = false; 321 } 322 } 323 324 // Toggle handler (capture) 325 function onToggleCapture(e, container) { 326 const detail = e.target; 327 if (!(detail instanceof HTMLElement) || detail.tagName.toLowerCase() !== 'details') return; 328 if (!container.contains(detail)) return; 329 if (detail.open) closeSiblings(container, detail); 330 syncAllIcons(container); 331 } 332 333 // Click handler (capture) 334 function onClickCapture(e, container) { 335 if (stopScrollIfAccordionClick(e, container)) return; 336 const target = e.target instanceof Element ? e.target : null; 337 if (!target) return; 338 const chip = target.closest(SELECTOR.chip); 339 if (chip && container.contains(chip)) clearChip(container, chip); 340 } 341 342 function initProgramListing() { 343 // Query containers 344 const containers = document.querySelectorAll(SELECTOR.root); 345 346 containers.forEach((container) => { 347 // Guard: skip if already initialized 348 if (!container || isInitialized(container)) return; 349 markInitialized(container); 350 351 // Initial icon sync 352 syncAllIcons(container); 353 354 // Register delegated handlers (capture) 355 container.addEventListener('toggle', (e) => onToggleCapture(e, container), true); 356 container.addEventListener('click', (e) => onClickCapture(e, container), true); 357 }); 358 } 359 360 if (document.readyState === 'loading') { 361 // DOM-ready hook 362 document.addEventListener('DOMContentLoaded', () => initProgramListing()); 363 } else { 364 // Initialize immediately if DOM already loaded 365 initProgramListing(); 366 } 367 368 // Expose initializer for dynamic mounts 369 window.initProgramListing = initProgramListing; 370})(); 371 372// Locations Map: initializer 373(function () { 374 // Map setup 375 function initMap() { 376 // Guard: ensure map container and Google Maps are available 377 var mapElement = document.getElementById('map'); 378 if (!mapElement || !window.google || !google.maps) { 379 return; 380 } 381 382 // Parse payload 383 var locations = getLocations(mapElement); 384 if (!locations) { 385 return; 386 } 387 388 // Initialize map and shared UI state 389 var map = new google.maps.Map(mapElement, { 390 zoom: 9, 391 center: { lat: 43.263, lng: -80.330 } 392 }); 393 var infoWindow = new google.maps.InfoWindow(); 394 var markers = []; 395 var campusIcon = mapElement.dataset.campusIcon || ''; 396 var housingIcon = mapElement.dataset.housingIcon || ''; 397 var clusterIcon = mapElement.dataset.clusterIcon || ''; 398 399 // Create a marker per location 400 locations.forEach(function (location) { 401 var marker = createMarker(location, map, infoWindow, campusIcon, housingIcon); 402 if (marker) { 403 markers.push(marker); 404 } 405 }); 406
407 // Cluster markers if configured 408 createClusterer(map, markers, clusterIcon); 409 } 410 411 // Payload parsing 412 function getLocations(mapElement) { 413 // Parse the serialized payload from the DOM 414 var locationsJson = mapElement.dataset.locations; 415 if (!locationsJson) { 416 return null; 417 } 418 419 try { 420 var parsed = JSON.parse(locationsJson); 421 return Array.isArray(parsed) ? parsed : null; 422 } catch (error) { 423 console.error('Failed to parse locations map payload.', error); 424 return null; 425 } 426 } 427 428 // Marker creation 429 function createMarker(location, map, infoWindow, campusIcon, housingIcon) { 430 // Resolve marker details 431 var position = getLocationPosition(location); 432 var title = getMarkerTitle(location); 433 var icon = getMarkerIcon(location, campusIcon, housingIcon); 434 435 if (!position) { 436 console.error(`Invalid coordinates for ${title || 'unknown location'}`); 437 return null; 438 } 439 440 // Create marker and wire the click handler 441 var marker = new google.maps.Marker({ 442 position: position, 443 map: map, 444 icon: icon, 445 title: title 446 }); 447 448 marker.addListener('click', function () { 449 openInfoWindow(infoWindow, map, marker, location); 450 }); 451 452 return marker; 453 } 454 455 // Position helpers 456 function getLocationPosition(location) { 457 // Validate lat/lng before building the position 458 var lat = Number(location.latitude); 459 var lng = Number(location.longitude); 460 461 if (!Number.isFinite(lat) || !Number.isFinite(lng)) { 462 return null; 463 } 464 465 return { lat: lat, lng: lng }; 466 } 467 468 // Display helpers 469 function getMarkerTitle(location) { 470 // Favor a clean title string 471 return location.title || ''; 472 } 473 474 function getMarkerIcon(location, campusIcon, housingIcon) { 475 // Select icon based on location type 476 return location.locationType === 'Housing' ? housingIcon : campusIcon; 477 } 478 479 function openInfoWindow(infoWindow, map, marker, location) { 480 // Skip empty info windows 481 if (!location.infoWindowHtml) { 482 return; 483 } 484 485 infoWindow.setContent(location.infoWindowHtml); 486 infoWindow.open(map, marker); 487 } 488 489 // Cluster helpers 490 function createClusterer(map, markers, clusterIcon) { 491 // Guard: clusterer must exist and be configured 492 if (typeof markerClusterer === 'undefined' || markers.length === 0 || !clusterIcon) { 493 return; 494 } 495 496 var renderer = { 497 render: function (options) { 498 if (options.count === 1) { 499 return null; 500 } 501 502 return new google.maps.Marker({ 503 position: options.position, 504 icon: { 505 url: clusterIcon, 506 scaledSize: new google.maps.Size(50, 50), 507 labelOrigin: new google.maps.Point(25, 20) 508 }, 509 label: { 510 text: String(options.count), 511 color: 'white', 512 className: 'cluster-label', 513 fontSize: '14px', 514 fontWeight: 'bold' 515 }, 516 zIndex: Number(google.maps.Marker.MAX_ZINDEX) + options.count 517 }); 518 } 519 }; 520 521 new markerClusterer.MarkerClusterer({ map, markers, renderer }); 522 } 523 524 window.initMap = initMap; 525})();
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.