1/** 2 * Below is a document-ready function that allows for standard jQuery syntax in WordPress by mapping jQuery() to '$'. 3 * 4 * If jQuery is not being used in this project, remove the function below and remove the jQuery dependency from the 5 * load_scripts_styles() function in inc/enqueue.php 6 * 7 * * */ 8 9 jQuery(document).ready(function ($) { 10 11 /** 12 * Helper Functions 13 * */ 14 15 /*Compensate for :hover states on touch devices*/ 16 function isTouchDevice() { 17 return (('ontouchstart' in window) || 18 (navigator.maxTouchPoints > 0) || 19 (navigator.msMaxTouchPoints > 0)); 20 } 21 22 /*Check if element is in viewport*/ 23 function isElementVisible($element) { 24 if ($element.length) { 25 let top_of_element = $element.offset().top, 26 bottom_of_element = $element.offset().top + $element.outerHeight(), 27 bottom_of_screen = $(window).scrollTop() + $(window).innerHeight(), 28 top_of_screen = $(window).scrollTop(); 29 if ((bottom_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) { 30 // the element is visible 31 return true; 32 } else { 33 // the element is not visible 34 return false; 35 } 36 } else { 37 return false; 38 } 39 } 40 41 /* doubletaptogo.js: by Osvaldas Valutis, www.osvaldas.info */ 42 $.fn.doubleTapToGo = function (params) { 43 if (!('ontouchstart' in window) && 44 !navigator.msMaxTouchPoints && 45 !navigator.userAgent.toLowerCase().match(/windows phone os 7/i)) return false; 46 this.each(function () { 47 var curItem = false; 48 $(this).on('click', function (e) { 49 var item = $(this); 50 if (item[0] != curItem[0]) { 51 e.preventDefault(); 52 curItem = item; 53 } 54 }); 55 $(document).on('click touchstart MSPointerDown', function (e) { 56 var resetItem = true, 57 parents = $(e.target).parents(); 58 for (var i = 0; i < parents.length; i++) 59 if (parents[i] == curItem[0]) 60 resetItem = false; 61 if (resetItem) 62 curItem = false; 63 }); 64 }); 65 return this; 66 }; 67 68 /* Keyboard Character Code Reverse compatibility: use the legacy properties if they're available */ 69 function getCharacterCode(e) { 70 if (e.which || e.charCode || e.keyCode) { 71 return e.which || e.charCode || e.keyCode; 72 //otherwise use new properties 73 } else if (e.key != undefined) { 74 return charCodeArr[e.key] || e.key.charCodeAt(0); 75 } else { 76 return 0; 77 } 78 } 79 80 81 /** 82 * Scroll Events 83 * */ 84 85 var didScroll; 86 $(window).scroll(function (event) { 87 // on scroll, let the interval function know the user has scrolled 88 didScroll = true; 89 90 /*NOT Throttled Scroll Event*/ 91 toggleStickyNav(); 92 }); 93 setInterval(function () { 94 if (didScroll) { 95 // run hasScrolled() and reset didScroll status 96 hasScrolled(); 97 didScroll = false; 98 } 99 }, 250); 100 101 /*Throttled Scroll Event*/ 102 function hasScrolled() { 103 104 } 105 106 /** 107 * On Resize and On Load 108 * */ 109 $(window).on("resize", function () { 110 //wait for resize to end 111 if (this.resizeTO) clearTimeout(this.resizeTO); 112 this.resizeTO = setTimeout(function () { 113 $(this).trigger('resizeEnd'); 114 }, 500); 115 // Invoke the resize event immediately on load 116 }).resize(); 117 118 //After resize 119 $(window).bind('resizeEnd', function () { 120 //fix accordion tabs for desktop/mobile 121 if ($('.accordion-tabs').length) { 122 fixAccordionTabs(); 123 } 124 125 //fix slider content height for desktop/mobile 126 if ($('.fixed-height-slider').length) { 127 fixSliderHeight(); 128 } 129 130 //fix filters for desktop/mobile 131 if ($('.mobile-filters-wrapper').length) { 132 fixMobileFiltersVisibility(); 133 } 134 });
135 136 137 /** 138 * Prevent page from scrolling behind modals/overlays 139 * */ 140 141 function freezeScroll() { 142 let $body = $('body'), 143 scrollY = $(window).scrollTop(), 144 offsetTop = scrollY * -1; 145 $body.addClass('no-body-scroll').css('top', offsetTop + 'px'); 146 if (scrollY > 0) { 147 $body.addClass('has-sticky-header'); 148 } 149 } 150 151 function unfreezeScroll() { 152 let $body = $('body'), 153 scrollY = $body.css('top'); 154 if ($body.hasClass('no-body-scroll')) { 155 $body.removeClass('no-body-scroll').css('top', ''); 156 $(window).scrollTop(parseInt(scrollY || '0') * -1); 157 } 158 } 159 160 /** 161 * Smooth Scroll to Anchors 162 * */ 163 164 $(document).on('click', 'a[href^="#"]', function (e) { 165 if ($(this).hasClass('smooth-scroll')) { 166 e.preventDefault(); 167 let $header = $('#masthead'); 168 $('html, body').animate({ 169 scrollTop: $($.attr(this, 'href')).offset().top - $header.height() 170 }, 2500); 171 } 172 }); 173 174 /** 175 * Desktop Header & Navigation 176 * */ 177 178 /*Sticky Nav*/ 179 180 //toggle sticky nav 181 function toggleStickyNav() { 182 let $header = $('#masthead'), 183 headerHeight = 150, 184 offset = $('html').css('margin-top'), 185 st = $(this).scrollTop(); 186 //Show sticky header 187 if ((st > (headerHeight))) { 188 $('body').addClass('has-sticky-header'); 189 $header.css('top', offset); 190 } else { 191 if (!$('body').hasClass('no-body-scroll')) { 192 $('body').removeClass('has-sticky-header'); 193 $header.css('top', offset); 194 } 195 } 196 } 197 198 /*Mega Menu*/ 199 200 //test for mobile menu 201 function isMobileMenu() { 202 return $('.header-mobile-btns').css('display') != 'none'; 203 } 204 205 //fade in menu 206 $.fn.menuFadeIn = function () { 207 if (!isMobileMenu()) { 208 $('body').addClass('desktop-nav-active').removeClass('search-modal-active'); 209 freezeScroll(); 210 } 211 this.activateMenu(); 212 } 213 214 //fade out menu 215 $.fn.menuFadeOut = function (fullFadeOut = false) { 216 //only fade out overlay if mouse has left the nav completely 217 if (($('#menu-primary-navigation').is(":hover") && !fullFadeOut) || isMobileMenu()) { 218 this.deactivateMenu(); 219 } else { 220 $('body').removeClass('desktop-nav-active'); 221 unfreezeScroll(); 222 this.deactivateMenu(); 223 } 224 } 225 226 //activate sub-menu 227 $.fn.activateMenu = function () { 228 if (isMobileMenu()) { 229 let $menu = this.children('.dropdown-panel').first(); 230 $menu.height($menu.get(0).scrollHeight).parent('.menu-item').addClass('active'); // get height and open 231 $menu.one('transitionend', function () { // after transition complete 232 $menu.height(''); // revert to CSS-set height 233 }); 234 } else { 235 this.addClass('active'); 236 } 237 this.find('[aria-expanded]').attr('aria-expanded', 'true'); 238 } 239 240 //deactivate sub-menu 241 $.fn.deactivateMenu = function () { 242 if (isMobileMenu()) { 243 let $menu = this.children('.dropdown-panel').first(); 244 $menu.height('').parent('.menu-item').removeClass('active'); // remove height and close 245 } else { 246 this.removeClass('active'); 247 } 248 this.find('[aria-expanded]').attr('aria-expanded', 'false'); 249 } 250 251 //toggle menu dropdown on hover 252 $('#menu-primary-navigation > .menu-item-has-children').hoverIntent({ 253 over: function () { 254 if (!isMobileMenu()) { 255 $(this).menuFadeIn(); 256 } 257 }, 258 out: function () { 259 if (!isMobileMenu()) { 260 $(this).menuFadeOut(); 261 } 262 }, 263 timeout: 500 264 }); 265 266 //fade out menu by clicking "Close Dropdown Menu" button 267 $('.primary-navigation .dropdown-close').on('click keypress', function (e) { 268 e.preventDefault(); 269 $(this).closest('.menu-item.active').menuFadeOut(true); 270 });
271 272 /*Desktop Menu Keyboard Navigation*/ 273 274 // dropdown-trigger button utility menu 275 $('#menu-utility-menu > .menu-item-has-children .dropdown-trigger').on('click keypress', function (e) { 276 e.preventDefault(); 277 let $li = $(this).closest('.menu-item-has-children'), 278 $alreadyActive = $li.siblings('.menu-item-has-children.active'); 279 280 if ($li.hasClass('active')) { 281 $li.removeClass('active'); 282 } else { 283 if ($alreadyActive.length) { 284 $alreadyActive.removeClass('active'); 285 } 286 $li.addClass('active'); 287 } 288 }); 289 290 //fade out menu on focus of top level menu items 291 $('#menu-utility-menu .nav-link').on("focus", function () { 292 let $li = $(this).closest('.menu-item'); 293 if (!isMobileMenu()) { 294 $li.siblings('.menu-item-has-children').removeClass('active'); 295 } 296 }); 297 298 // first click on utility mobile link opens the submenu 299 $('#menu-utility-menu .nav-link').on("click", function (e) { 300 let $parent = $(this).closest('.menu-item'), 301 $trigger = $(this).next('.dropdown-trigger'); 302 if (isMobileMenu() && !$parent.hasClass('active') && $parent.hasClass('menu-item-has-children')) { 303 e.preventDefault(); 304 $trigger.click(); 305 } 306 }); 307 308 //toggle menu with .dropdown-trigger button 309 $('#menu-primary-navigation > .menu-item-has-children .dropdown-trigger').on('click keypress', function (e) { 310 e.preventDefault(); 311 let $li = $(this).closest('.menu-item-has-children'), 312 $alreadyActive = $li.siblings('.menu-item-has-children.active'); 313 314 if ($li.hasClass('active')) { 315 $li.menuFadeOut(); 316 } else { 317 if ($alreadyActive.length) { 318 $alreadyActive.menuFadeOut(); 319 } 320 $li.menuFadeIn(); 321 } 322 }); 323 324 // close utility submenu on mouseleave 325 $('#menu-utility-menu .nav-item').on("mouseout", function () { 326 if (!isMobileMenu()) { 327 $(this).removeClass('active'); 328 } 329 }); 330 331 //fade out menu on focus of top level menu items 332 $('#menu-primary-navigation .menu-item-top a').on("focus", function () { 333 let $li = $(this).closest('.menu-item'); 334 if (!isMobileMenu()) { 335 $li.siblings('.menu-item-has-children').menuFadeOut(); 336 } 337 }); 338 339 $('#menu-primary-navigation .menu-item-top a').on("click", function (e) { 340 let $parent = $(this).closest('.menu-item'), 341 $trigger = $(this).next('.dropdown-trigger'); 342 if (isMobileMenu() && !$parent.hasClass('active')) { 343 e.preventDefault(); 344 $trigger.click(); 345 } 346 ; 347 }); 348 349 /** 350 * Mobile Header & Navigation 351 * */ 352 353 $('.mobile-nav-trigger').on('click', function () { 354 let expanded = $(this).attr('aria-expanded'); 355 if (expanded === 'true') { 356 unfreezeScroll(); 357 $(this).attr('aria-expanded', 'false'); 358 $('body').removeClass('mobile-nav-active'); 359 } else { 360 freezeScroll(); 361 $(this).attr('aria-expanded', 'true'); 362 $('body').addClass('mobile-nav-active'); 363 } 364 }); 365 366 367 /** 368 * Page Templates 369 * */ 370 //Random Dots in corner 371 function randomDots() { 372 let rando = Math.floor((Math.random() * 2) + 1), 373 classAttr = 'has-bkgd-dots' + rando, 374 $body = $('body'), 375 templatesWithBackground = '.single-event_group, .page-template-tours-events-listing, .page-template-majors-and-minors'; 376 if ($body.is(templatesWithBackground)) { 377 $body.find('.site-main').first().addClass(classAttr); 378 } 379 } 380 381 randomDots(); 382 383 //Add arrow SVG to preset buttons 384 function insertArrowIcon() { 385 if ($('.btn-arrow--preset').length) { 386 let icon = '<svg class="icon-arrow-long" role="img" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 46 22" stroke-width="2" fill="none">\n' + 387 ' <path class="line" d="M 42 9 L 0 9" />\n' + 388 ' <path class="arrow" d="M 32 0 L 42 9 L 32 18" />\n' + 389 '</svg>'; 390 $('.btn-arrow--preset').each(function () { 391 let wrappedContent = '<span>' + $(this).text() + '</span>'; 392 $(this).html(icon + wrappedContent); 393 }); 394 } 395 } 396 397 insertArrowIcon(); 398 399 /** 400 * Video/Slider Controls 401 * */ 402 403 //Toggle Play/Pause Button 404 $.fn.togglePlayPause = function ($target, label = 'Video') { 405 let $button = $(this), 406 isPaused = $button.attr('data-paused') === 'true', 407 newLabel = isPaused ? 'Pause' + ' ' + label : 'Play' + ' ' + label, 408 newState = isPaused ? 'false' : 'true'; 409 if (typeof $target !== 'undefined') { 410 411 if ($target.is('video')) { 412 let $videos = $target; 413 //loop through videos 414 $videos.each(function () { 415 if (isPaused) { 416 $(this).get(0).play(); 417 } else { 418 $(this).get(0).pause(); 419 } 420 }); 421 } else if ($target.hasClass('slick-initialized')) { 422 let $slider = $target; 423 if (isPaused) { 424 $slider.slick('slickPlay'); 425 } else { 426 $slider.slick('slickPause'); 427 } 428 } 429 } 430 //update button 431 $button.toggleClass('playing').attr({ 432 'aria-label': newLabel, 433 'data-paused': newState 434 }).find('.screen-reader-text').text(newLabel); 435 } 436 437 //Home Hero Play/Pause 438 $('.home-hero .play-pause-button').click(function () { 439 let $video = $('.home-hero .video-background');
440 $(this).togglePlayPause($video, 'Background Video'); 441 }); 442 443 //L1 Hero Play/Pause 444 $('.l1-header .play-pause-button').click(function () { 445 let $video = $('.l1-header .video-background'); 446 $(this).togglePlayPause($video, 'Background Video'); 447 }); 448 449 //Life in Baltimore Play/Pause 450 $('.life-baltimore-header .play-pause-button').click(function () { 451 let $video = $('.life-baltimore-header .video-background'); 452 $(this).togglePlayPause($video, 'Background Video'); 453 }); 454 455 //Hopkins Insider 456 $('.insider-landing-column-header-video .play-pause-button').click(function () { 457 let $video = $('.insider-landing-column-header-video .video-background'); 458 $(this).togglePlayPause($video, 'Background Videos'); 459 }); 460 $('#slider_studentVideo .play-pause-button').click(function () { 461 let $video = $('#slider_studentVideo .video-background'); 462 $(this).togglePlayPause($video, 'Preview Videos'); 463 }); 464 $('#slider_advisorAdviceVideo .play-pause-button').click(function () { 465 let $video = $('#slider_advisorAdviceVideo .video-background'); 466 $(this).togglePlayPause($video, 'Preview Videos'); 467 }); 468 469 470 //Insert Play/Pause Button into slick slider 471 $.fn.insertPlayPause = function (label = 'Slider', selector) { 472 let slideCount = $(this).slick("getSlick").slideCount; 473 if (slideCount > 1) { 474 let button = '<button type="button" class="play-pause-button playing" data-paused="false" aria-label="Pause ' + label + '"><span class="screen-reader-text">Pause ' + label + '</span></button>', 475 $target = $(this), 476 $sliderControls; 477 if (typeof selector !== 'undefined') { 478 $sliderControls = $(selector); 479 } else { 480 $sliderControls = $target; 481 } 482 $sliderControls.append(button).on('click', '.play-pause-button', function () { 483 $(this).togglePlayPause($target, label); 484 }); 485 } 486 } 487 488 function findSlider($element) { 489 let sliderID = $element.closest('.slider-controls').attr('aria-controls'); 490 return $('#' + sliderID); 491 } 492 493 $('.slider-prev').on('click', function () { 494 let $slider = findSlider($(this)); 495 $slider.slick('slickPrev'); 496 }); 497 498 $('.slider-next').on('click', function () { 499 let $slider = findSlider($(this)); 500 $slider.slick('slickNext'); 501 }); 502 503 $('.slider-controls .play-pause-button').click(function () { 504 let $slider = findSlider($(this)); 505 $(this).togglePlayPause($slider, 'Slider'); 506 }); 507 508 //Fix "jumping" between first and last slides 509 $.fn.fixSlickJump = function (){ 510 this.on('beforeChange', function (event, slick, currentSlide, nextSlide) { 511 let direction,
512 slideCountZeroBased = slick.slideCount - 1, 513 extraSlidesShown = slick.options.slidesToShow - 1; 514 if (nextSlide == currentSlide) { 515 direction = "same"; 516 } else if (Math.abs(nextSlide - currentSlide) == 1) { 517 direction = (nextSlide - currentSlide > 0) ? "right" : "left"; 518 } else { 519 direction = (nextSlide - currentSlide > 0) ? "left" : "right"; 520 } 521 522 // Add a temp CSS class for the slide animation (.slick-current-clone-animate) 523 if (direction == 'right') { 524 $(this).find('.slick-cloned[data-slick-index="' + (nextSlide + slideCountZeroBased + 1) + '"]').addClass('slick-current-clone-animate').nextAll().slice(0,extraSlidesShown).addClass('slick-current-clone-animate'); 525 } 526 if (direction == 'left') { 527 $(this).find('.slick-cloned[data-slick-index="' + (nextSlide - slideCountZeroBased - 1) + '"]').addClass('slick-current-clone-animate').nextAll().slice(0,extraSlidesShown).addClass('slick-current-clone-animate'); 528 } 529 530 }); 531 this.on('afterChange', function (event, slick, currentSlide, nextSlide) { 532 $(this).find('.slick-current-clone-animate').removeClass('slick-current-clone-animate'); 533 }); 534 } 535 536 537 /** 538 * Sliders 539 * */ 540 541 //Home Slider 542 $('#homeSlider').slick({ 543 infinite: true, 544 autoplay: true, 545 arrows: false, 546 dots: true, 547 appendDots: $('#homeSliderControls'), 548 }).insertPlayPause('Slider', '#homeSliderControls'); 549 550 //50-50 Carousel 551 if($('.carousel-50-50-section').length){ 552 $('.carousel-50-50-section').each(function(){ 553 let sectionID = '#' + $(this).attr('id'), 554 imageSliderID = sectionID + '-slider-images', 555 contentSliderID = sectionID + '-slider'; 556 557 $(contentSliderID).slick({ 558 infinite: true, 559 autoplay: true, 560 autoplaySpeed: 10000, 561 arrows: false, 562 dots: false, 563 speed: 500, 564 fade: true, 565 cssEase: 'linear', 566 asNavFor: imageSliderID, 567 }); 568 $(imageSliderID).slick({ 569 infinite: true, 570 arrows: false, 571 dots: false, 572 speed: 500, 573 fade: true, 574 cssEase: 'linear', 575 asNavFor: contentSliderID, 576 }); 577 578 }); 579 } 580 581 582 //Full-Width Slider 583 $('.full-width-slider').slick({ 584 infinite: true, 585 autoplay: true, 586 autoplaySpeed: 7000, 587 arrows: false, 588 dots: false, 589 speed: 500, 590 fade: true, 591 cssEase: 'linear' 592 }); 593 594 let full_width_slider_count = $('.full-width-slider').find('.slick-slide').length; 595 if (full_width_slider_count <= 1) { 596 $('.full-width-slider .slider-controls').hide(); 597 } 598 599 //Featured Events Slider 600 $('#featuredEventsSlider').slick({ 601 infinite: true, 602 autoplay: true, 603 autoplaySpeed: 7000, 604 arrows: false, 605 dots: true, 606 fade: true, 607 appendDots: $('#featuredEventsSliderControls'), 608 }).insertPlayPause('Featured Events Carousel', '#featuredEventsSliderControls'); 609 610 //Listing A Slider 611 $('.listing-a-slider').slick({ 612 infinite: true, 613 autoplay: true, 614 autoplaySpeed: 7000, 615 arrows: false, 616 dots: false, 617 speed: 500, 618 fade: true, 619 cssEase: 'linear' 620 }); 621 622 //Interests Slider (Life in Baltimore) 623 var interstsSliderOptions = { 624 infinite: true, 625 arrows: false, 626 dots: false, 627 speed: 500, 628 variableWidth: true, 629 } 630 //only initialize visible Interests Slider on initial pageload 631 $('.tabs-panel.is-open .interests-slider').slick(interstsSliderOptions); 632 633 //initialize new Interests Slider when new tab opens
634 $('.interests-section .tabs-panel').on('tab:open', function () { 635 let $slider = $(this).find('.interests-slider'); 636 if (!$slider.hasClass('slick-initialized')) { 637 $slider.slick(interstsSliderOptions); 638 } 639 }); 640 641 //Hopkins Insider Categories Slider 642 $('#slider_otherCategories').slick({ 643 infinite: true, 644 arrows: false, 645 dots: false, 646 speed: 500, 647 variableWidth: true, 648 responsive: [ 649 { 650 breakpoint: 640, 651 settings: { 652 infinite: true, 653 arrows: false, 654 dots: false, 655 speed: 500, 656 variableWidth: true, 657 centerMode: true, 658 slidesToShow: 1 659 } 660 }, 661 ] 662 }).fixSlickJump(); 663 664 //Hopkins Insider Video Sliders 665 if($('.hi-video-slider-video').length) { 666 667 let unslicked; 668 669 //remove loaders when responsive slider is initialized 670 $('#slider_studentVideo').on('init.slick', function (event, slick) { 671 $('.hi-block-video-slider--student').removeClass('loading'); 672 }); 673 $('#slider_advisorAdviceVideo').on('init.slick', function (event, slick) { 674 $('.hi-block-video-slider--admin').removeClass('loading'); 675 }); 676 677 //default slider options 678 function hi_videoSliderVideoOptions(navSlider) { 679 let centermode = false; 680 if(isMobileMenu()){ 681 centermode = true; 682 } 683 return { 684 infinite: true, 685 arrows: false, 686 dots: false, 687 speed: 500, 688 variableWidth: true, 689 asNavFor: navSlider, 690 centerMode: centermode, 691 slidesToShow: 1, 692 }; 693 } 694 function hi_videoSliderTextOptions(navSlider) { 695 return { 696 infinite: true, 697 arrows: false, 698 dots: false, 699 speed: 500, 700 slidesToShow: 1, 701 slidesToScroll: 1, 702 fade: true, 703 asNavFor: navSlider, 704 slidesToShow: 1, 705 }; 706 } 707 708 //Initialize video sliders 709 function buildVideoSliders() { 710 711 //Hopkins Insider Student Video Sliders 712 $('#slider_studentVideo').slick(hi_videoSliderVideoOptions('#slider_studentText')).fixSlickJump(); 713 $('#slider_studentText').slick(hi_videoSliderTextOptions('#slider_studentVideo')); 714 715 //Hopkins Insider Admin Video Sliders 716 $('#slider_advisorAdviceVideo').slick(hi_videoSliderVideoOptions('#slider_advisorAdviceText')).fixSlickJump(); 717 $('#slider_advisorAdviceText').slick(hi_videoSliderTextOptions('#slider_advisorAdviceVideo')); 718 719 720 unslicked = false; 721 } 722 723 function destroyVideoSliders() { 724 725 if ($('#slider_studentVideo').hasClass('slick-initialized')) { 726 $('#slider_studentVideo').slick('unslick'); 727 } 728 if ($('#slider_studentText').hasClass('slick-initialized')) { 729 $('#slider_studentText').slick('unslick'); 730 } 731 if ($('#slider_advisorAdviceVideo').hasClass('slick-initialized')) { 732 $('#slider_advisorAdviceVideo').slick('unslick'); 733 } 734 if ($('#slider_advisorAdviceText').hasClass('slick-initialized')) { 735 $('#slider_advisorAdviceText').slick('unslick'); 736 } 737 } 738 739 //Build on initial page load 740 buildVideoSliders(); 741 742 //Rebuild home header sliders on resize 743 let videoSliderResizeTimer, 744 windowWidth = $(window).width(), 745 windowHeight = $(window).height(); 746 $(window).on('resize', function () { 747 748 if ($(this).width() !== windowWidth || $(this).height() !== windowHeight) { 749 750 //reset windowWidth to new width 751 windowWidth = $(this).width(); 752 windowHeight = $(window).height(); 753 754 if (unslicked === false) { 755 unslicked = true; 756 //"destroy" sliders and display loader on initial resize 757 $('.hi-block-video-slider').addClass('loading'); 758 destroyVideoSliders(); 759 } 760 761 clearTimeout(videoSliderResizeTimer); 762 videoSliderResizeTimer = setTimeout(function () { 763 // Run code here, resizing has "stopped" 764 buildVideoSliders(); 765 $('.hi-block-video-slider').removeClass('loading'); 766 }, 250); 767 } 768 }); 769 770 } 771 772 773 //Fixed Height Slider 774 function fixSliderHeight() { 775 $('.fixed-height-slider').each(function () { 776 let slideContents = $(this).find('.slide-content-inner'), 777 minHeight = 0; 778 slideContents.css('min-height', 0); 779 slideContents.each(function () { 780 if ($(this).outerHeight() > minHeight) { 781 minHeight = $(this).outerHeight(); 782 } 783 }); 784 if (minHeight > 0) { 785 slideContents.css('min-height', minHeight + 'px'); 786 } 787 }); 788 } 789 790 /** 791 * Accordions & Tabs 792 * */ 793 794 //open accordion 795 $.fn.openAccordion = function () { 796 let $accordion = $(this), 797 $trigger = $accordion.find('.accordion-trigger').first(), 798 $content = $accordion.find('.accordion-content').first(), 799 isAccordionTabs = $(this).closest('.accordion-tabs').length; 800 801 $accordion.addClass('is-open'); 802 $trigger.attr('aria-expanded', 'true'); 803 804 if ($trigger.css('display') != 'none') { 805 //animate accordion content unless its desktop accordion tabs 806 $content.height($content.get(0).scrollHeight).attr('aria-hidden', 'false').addClass('is-open'); // get height and open 807 $content.one('transitionend', function () { // after transition complete 808 $content.height(''); // revert to CSS-set height 809 }); 810 } else { 811 $content.attr('aria-hidden', 'false').addClass('is-open'); 812 } 813 } 814 815 //close accordion 816 $.fn.closeAccordion = function () { 817 let $accordion = $(this), 818 $trigger = $accordion.find('.accordion-trigger').first(), 819 $content = $accordion.find('.accordion-content').first(); 820 821 $accordion.removeClass('is-open'); 822 $trigger.attr('aria-expanded', 'false').blur(); 823 824 if ($trigger.css('display') != 'none') { 825 //animate accordion content unless its desktop accordion tabs 826 $content.height($content.get(0).scrollHeight).height('').attr('aria-hidden', 'true').removeClass('is-open'); // get height and close 827 } else { 828 $content.attr('aria-hidden', 'true').removeClass('is-open'); 829 } 830 } 831 832 //toggle accordion 833 $('.accordion-trigger').on('click', function (e) { 834 if(!$(e.target).is('a, a *')){ //allow links inside accordion titles to work 835 e.preventDefault(); 836 let isOpen = $(this).attr('aria-expanded') === 'true', 837 target = $(this).attr('aria-controls'), 838 $accordion = $('#' + target), 839 $accordionTabs = $(this).closest('.accordion-tabs'); 840 if (isOpen) { 841 //close accordion 842 $accordion.closeAccordion(); 843 844 //open tab if this is also a tab panel 845 if ($accordionTabs.length) { 846 $accordionTabs.find('.tabs-list a').each(function () { 847 if ($(this).attr('aria-controls') === target) { 848 $(this).closeTab(); 849 } 850 }); 851 } 852 } else { 853 $accordion.siblings('.accordion.is-open').each(function () { 854 $(this).closeAccordion(); 855 }); 856 //open accordion 857 $accordion.openAccordion(); 858 859 //open/close appropriate tabs if this is also a tab panel 860 if ($accordionTabs.length) { 861 $accordionTabs.find('.tabs-list a').each(function () { 862 if ($(this).attr('aria-controls') === target) { 863 $(this).openTab(); 864 } else { 865 $(this).closeTab(); 866 } 867 }); 868 } 869 } 870 } 871 });
872 873 //accordion keyboard accessibility 874 $('.accordion-trigger').keydown(function (e) { 875 if (e.code === 'Enter' || e.code === 'Space') { 876 e.preventDefault(); 877 $(this).click(); 878 } 879 }); 880 881 //open tab 882 $.fn.openTab = function (withAccordion = false) { 883 let target = $(this).attr('aria-controls'), 884 $panel = $('#' + target); 885 $panel.trigger('tab:open'); 886 $(this).attr('aria-selected', 'true').addClass('is-selected').removeAttr('tabindex'); 887 $panel.addClass('is-open').attr('tabindex', '0'); 888 889 //open accordion if this is also an accordion 890 if ($panel.hasClass('accordion') && withAccordion) { 891 $panel.openAccordion(); 892 } 893 } 894 895 //close tab 896 $.fn.closeTab = function (withAccordion = false) { 897 let target = $(this).attr('aria-controls'), 898 $panel = $('#' + target); 899 $panel.trigger('tab:close'); 900 $(this).attr('aria-selected', 'false').removeClass('is-selected').attr('tabindex', '-1'); 901 $panel.removeClass('is-open').attr('tabindex', '-1'); 902 903 //close accordion if this is also an accordion 904 if ($panel.hasClass('accordion') && withAccordion) { 905 $panel.closeAccordion(); 906 } 907 } 908 909 //tab trigger click 910 $('.tabs-list .tabs-trigger').on('click', function (e) { 911 e.preventDefault(); 912 let isSelected = $(this).hasClass('is-selected'); 913 if (!isSelected) { 914 //close other tab 915 $(this).closest('li').siblings().find('.is-selected').each(function () { 916 $(this).closeTab(true); 917 }); 918 //open this tab 919 $(this).openTab(true); 920 } 921 }) 922 923 //accordion keyboard accessibility 924 $('.tabs-trigger').keyup(function (e) { 925 let keyCode = getCharacterCode(e), 926 keys = { 927 end: 35, 928 home: 36, 929 left: 37, 930 up: 38, 931 right: 39, 932 down: 40, 933 tab: 9, 934 }; 935 if (keyCode != keys.tab) { 936 e.preventDefault(); 937 938 let $tablist = $(this).closest('.tabs-list'), 939 $tablistItem = $(this).closest('li'), 940 $firstTab = $tablist.find('.tabs-trigger').first(), 941 $lastTab = $tablist.find('.tabs-trigger').last(), 942 $nextTab = $tablistItem.next().find('.tabs-trigger'), 943 $prevTab = $tablistItem.prev().find('.tabs-trigger'); 944 if (keyCode === keys.left || keyCode === keys.up) { 945 if ($tablistItem.is(':first-child')) { 946 $lastTab.click().focus(); 947 } else { 948 $prevTab.click().focus(); 949 } 950 } else if (keyCode === keys.right || keyCode === keys.down) { 951 if ($tablistItem.is(':last-child')) { 952 $firstTab.click().focus(); 953 } else { 954 $nextTab.click().focus(); 955 } 956 } else if (keyCode === keys.home) { 957 $firstTab.click().focus(); 958 } else if (keyCode === keys.end) { 959 $lastTab.click().focus(); 960 } 961 } 962 }); 963 964 965 //start with first tab open on desktop 966 function fixAccordionTabs() { 967 if ($('.tabs-list').css('display') != 'none') { 968 $('.accordion-tabs').each(function () { 969 if (!$(this).find('.tabs-trigger.is-selected').length) { 970 $(this).find('.tabs-trigger').first().openTab(true); 971 } 972 }); 973 } 974 } 975 976 /** 977 * Filter Functions 978 * */ 979 980 //filters accordion on mobile. Add aria-hidden for mobile only. 981 function fixMobileFiltersVisibility() { 982 let $wrapper = $('.mobile-filters-wrapper'), 983 $filterList = $wrapper.find('.filter-list').first(); 984 if (!isMobileMenu()) { 985 $filterList.removeAttr('aria-hidden'); 986 } else { 987 if ($wrapper.hasClass('is-open')) { 988 $filterList.attr('aria-hidden', 'false'); 989 } else { 990 $filterList.attr('aria-hidden', 'true'); 991 } 992 } 993 } 994 995 $('.filter-box--select select').on('change', function () { 996 $(this).closest('.filter-box--select').addClass('has-option-selected'); 997 });
998 999 /** 1000 * Modal Functions 1001 * */ 1002 1003 //Trap Focus 1004 $.fn.trapFocus = function ($trigger, autoFocus = false) { 1005 let $this = $(this), 1006 hasTabbed = false, 1007 tabbable = $this.find('select, input, textarea, button, a, [tabindex]:not([tabindex^="-"]), iframe').filter(':visible'), 1008 firstTabbable = tabbable.first(), 1009 lastTabbable = tabbable.last(); 1010 1011 if (autoFocus) { 1012 firstTabbable.focus(); 1013 hasTabbed = true; 1014 } else { 1015 /*set focus on first tab press*/ 1016 $(document).on('keydown', function (e) { 1017 let keyCode = getCharacterCode(e); 1018 if ((keyCode === 9 && !hasTabbed) && $this.hasClass('active')) { 1019 e.preventDefault(); 1020 firstTabbable.focus(); 1021 hasTabbed = true; 1022 } 1023 }); 1024 } 1025 1026 /*redirect last tab to first focus item*/ 1027 lastTabbable.on('keydown', function (e) { 1028 let keyCode = getCharacterCode(e); 1029 if ((keyCode === 9 && !e.shiftKey)) { 1030 e.preventDefault(); 1031 firstTabbable.focus(); 1032 } 1033 }); 1034 1035 /*redirect first shift+tab to last focus item*/ 1036 firstTabbable.on('keydown', function (e) { 1037 let keyCode = getCharacterCode(e); 1038 if ((keyCode === 9 && e.shiftKey)) { 1039 e.preventDefault(); 1040 lastTabbable.focus(); 1041 } 1042 }); 1043 1044 /* 1045 * Escape the Trap 1046 * */ 1047 1048 /* escape key pressed */ 1049 $(document).on('keyup', function (e) { 1050 let keyCode = getCharacterCode(e); 1051 if (keyCode === 27) { 1052 closeModal(); 1053 if ($trigger) { 1054 //focus on link that opened modal 1055 $trigger.focus(); 1056 } 1057 } 1058 ; 1059 }); 1060 1061 /* close button pressed */ 1062 $('.popup-modal-close').on('keypress', function (e) { 1063 e.preventDefault(); 1064 closeModal(); 1065 if ($trigger) { 1066 //focus on link that opened modal 1067 $trigger.focus(); 1068 } 1069 }); 1070 } 1071 1072 //Close Modals 1073 function closeModal() { 1074 unfreezeScroll(); 1075 //Close dynamic popup 1076 $('.popup-modal').removeClass('active').fadeOut(); 1077 1078 //stop video and remove aspect ratio class 1079 $('.popup-modal .video-wrapper').html('').closest('.wp-has-aspect-ratio').removeClass (function (index, className) { 1080 return (className.match (/\bwp-embed-aspect-\S+/g) || []).join(' '); 1081 }); 1082 } 1083 1084 //Open Modals 1085 function openModal($target, $trigger = false) { 1086 freezeScroll(); 1087 //Open dynamic popup 1088 $target.addClass('active').fadeIn().scrollTop(0).trapFocus($trigger); 1089 } 1090 1091 /** 1092 * Popup Modal Click Events 1093 * */ 1094 1095 $('.popup-modal-close').on('click', function (e) { 1096 e.preventDefault(); 1097 closeModal(); 1098 }); 1099 1100 //Youtube Modal 1101 $('.youtube-lightbox').on('click keypress', function (e) { 1102 e.preventDefault(); 1103 let $trigger = $(this), 1104 href = $trigger.attr('href'), 1105 jsonEndpoint = 'https://www.youtube.com/oembed?url=' + href + '&format=json', 1106 embedHtml = '', 1107 aspectRatioClass = 'wp-embed-aspect-'; 1108 aspectRatio = $trigger.attr('data-aspect-ratio'); 1109 1110 //use 16:9 aspect ratio if not specified otherwise 1111 if (typeof aspectRatio !== 'undefined' && aspectRatio !== false) { 1112 aspectRatioClass += aspectRatio 1113 }else{ 1114 aspectRatioClass += '16-9' 1115 } 1116 $.getJSON(jsonEndpoint, function (data) { 1117 // JSON result in `data` variable 1118 embedHtml = data.html; 1119 if (embedHtml != '') { 1120 1121 //make autoplay with no related videos 1122 embedHtml = embedHtml.replace('?feature=oembed', '?feature=oembed&rel=0&autoplay=1'); 1123 1124 let $modal = $('#overlayVideo'); 1125 1126 //add aspect ratio class 1127 $modal.find('.wp-has-aspect-ratio').addClass(aspectRatioClass); 1128 1129 //append the video 1130 $modal.find('.video-wrapper').html(embedHtml); 1131 1132 //Fade In Modal 1133 openModal($modal, $trigger); 1134 } 1135 }); 1136 });
1137 1138 //Search Modal 1139 $('.search-trigger').on('click keypress', function (e) { 1140 e.preventDefault(); 1141 let $trigger = $(this), 1142 $modal = $('#overlaySearch'); 1143 1144 //Fade In Modal 1145 openModal($modal, $trigger); 1146 1147 //focus searchbar after fade-in 1148 setTimeout(function () { 1149 $modal.find('.searchform-input').first().focus(); 1150 }, 600); 1151 }); 1152 1153 /** 1154 * Parallax 1155 * */ 1156 1157 //Parallax movement 1158 $.fn.parallaxOffset = function (maxOffset = 0, $viewport = $(window)) { 1159 let $wrapper = this.closest('.parallax-container'), 1160 totalHeight = $wrapper.height() + $viewport.height(), 1161 wrapperTop = $wrapper.offset().top, 1162 offset = (wrapperTop - $viewport.scrollTop()) + $wrapper.height(), 1163 percentage = (totalHeight - offset) / totalHeight; 1164 if (maxOffset === 0) { 1165 maxOffset = $wrapper.height() - this.outerHeight(true); 1166 } 1167 let parallaxPosition = maxOffset * percentage; 1168 this.css('transform', 'translateY(-' + parallaxPosition + 'px)'); 1169 } 1170 1171 //Virtual Tour Block Background Parallax
1172 if ($('.virtual-tour-block').length) { 1173 let $viewport = $(window); 1174 $viewport.on({ 1175 scroll: function () { 1176 //Move the Parallax Item 1177 $('.virtual-tour-block-bkgd.parallax').each(function () { 1178 if (isElementVisible($(this))) { 1179 //Move element if visible 1180 $(this).parallaxOffset(200); 1181 } 1182 }); 1183 }, 1184 }); 1185 } 1186 1187 1188 /** 1189 * Homepage 1190 * */ 1191 1192 //Disable Profile Panel part of Link on Mobile 1193 $('.home-profile-grid-item').on('click', function (e) { 1194 if (isMobileMenu()) { 1195 if ($('.profile-panel').find(e.target).length) { 1196 e.preventDefault(); 1197 } 1198 } 1199 }); 1200 1201 /** 1202 * Life in Baltimore 1203 * */ 1204 1205 $.fn.isHoverColumn = function () { 1206 let $link = $(this).find('.hover-column-link').first() 1207 if ($link.css('position') === 'absolute') { 1208 return true; 1209 } else { 1210 return false; 1211 } 1212 } 1213 1214 $.fn.activateHoverColumnLeftAlign = function () { 1215 if ($(this).isHoverColumn()) { 1216 let $top = $(this).find('.hover-column-top').first(), 1217 $bottom = $(this).find('.hover-column-bottom').first(), 1218 offset = $bottom.height(); 1219 $top.css('transform', 'translateY(-' + offset + 'px)'); 1220 } 1221 } 1222 1223 $.fn.deactivateHoverColumnLeftAlign = function () { 1224 if ($(this).isHoverColumn() && !$(this).is(':focus-within')) { 1225 let $top = $(this).find('.hover-column-top').first(); 1226 $top.removeAttr('style'); 1227 } 1228 } 1229 1230 $('.hover-column--left-align').hover( 1231 function () { 1232 $(this).activateHoverColumnLeftAlign(); 1233 }, function () { 1234 $(this).deactivateHoverColumnLeftAlign(); 1235 } 1236 ); 1237 1238 $('.hover-column--left-align .hover-column-link').focus(function () { 1239 let $column = $(this).closest('.hover-column--left-align'); 1240 $column.activateHoverColumnLeftAlign(); 1241 }).blur(function () { 1242 let $column = $(this).closest('.hover-column--left-align'); 1243 $column.deactivateHoverColumnLeftAlign(); 1244 }); 1245 1246 1247 $('.tabs-list-wrapper').on('click', function (e) { 1248 if (isMobileMenu() && !$(this).hasClass('is-open')) { 1249 e.preventDefault(); 1250 $(this).addClass('is-open'); 1251 } else if (isMobileMenu()) { 1252 $(this).removeClass('is-open'); 1253 } 1254 }); 1255 1256 function closeTabsListWrapperDropdown() { 1257 $('.tabs-list-wrapper.is-open').removeClass('is-open'); 1258 } 1259 1260 $(document).on('click', function (e) { 1261 let $list = $('.tabs-list-wrapper.is-open'); 1262 if ($list !== e.target && !$list.has(e.target).length) { 1263 closeTabsListWrapperDropdown(); 1264 } 1265 }); 1266 1267 $('.tabs-list-wrapper').on('focusin', function (e) { 1268 if (isMobileMenu() && !$(this).hasClass('is-open')) { 1269 $(this).addClass('is-open'); 1270 } 1271 }); 1272 1273 1274 /** 1275 * Hopkins Insider 1276 * */ 1277 1278 //HI Landing Page Prevent automatic browser scroll on page refresh 1279 $(window).on('beforeunload', function() { 1280 fixInsiderWindowScroll(); 1281 }); 1282 function fixInsiderWindowScroll(){ 1283 if($('body').hasClass('page-template-hopkins-insider-landing') && !isMobileMenu()){ 1284 history.scrollRestoration = "manual"; 1285 }else{ 1286 history.scrollRestoration = "auto"; 1287 } 1288 } 1289 1290 //HI Landing Page Header (push up header content on column/page scroll) 1291 setHITitlePosition(); //onload 1292 $('.insider-landing-column').on('scroll', setHITitlePosition); //column scroll 1293 $(document).on('scroll', setHITitlePosition); //page scroll 1294 1295 function setHITitlePosition(){ 1296 if($('body').hasClass('page-template-hopkins-insider-landing') && !isMobileMenu()) { 1297 let documentScrollY = document.documentElement.scrollTop, 1298 offsetLeft = $('#splitLayoutLeft').scrollTop(), 1299 offsetRight = $('#splitLayoutRight').scrollTop(), 1300 offset = (Math.max(offsetLeft, offsetRight) + documentScrollY) * -1; 1301 $('#splitLayoutHeader').css("transform", "translateY(" + offset + "px)"); 1302 } 1303 } 1304 1305 //HI Landing Page faux scrollbar on scroll 1306 $('.insider-landing-column').scroll(function (event) { 1307 let scrollTop = $(this).scrollTop(), 1308 scrollBottom = $(this).prop("scrollHeight") - $(this).height(), 1309 scrollPercent = scrollTop / scrollBottom * 100 + "%"; 1310 1311 //move scrollbar gradient 1312 $(this).find('.insider-landing-column-progress').css("--scroll", scrollPercent); 1313 });
1314 1315 //Allow the faux scrollbar to be clickable 1316 $('.insider-landing-column-progress-placeholder').on('mouseup', function(e) { 1317 let $parent = $(this).closest('.insider-landing-column'), 1318 height = $parent.height(), 1319 posY = $('#cols-container').offset().top, 1320 percent = (e.pageY) / height, 1321 parentHeight = $parent.prop("scrollHeight") - $parent.height(), 1322 newScrollPos = parentHeight * percent; 1323 //scroll to new position 1324 $parent.scrollTop(newScrollPos); 1325 }); 1326 1327 //Expand faux scrollbar on hover 1328 $('.insider-landing-column-progress-placeholder').hover(function(){ 1329 //hover in 1330 $(this).closest('.insider-landing-column').addClass('has-scrollbar-active'); 1331 },function(){ 1332 //hover out 1333 $(this).closest('.insider-landing-column').removeClass('has-scrollbar-active'); 1334 }); 1335 1336 //Move a scrollbar position indicator on hover 1337 $('.insider-landing-column-progress-placeholder').on('mousemove touchstart touchmove', function(e){ 1338 let points = [], 1339 $target = $(this).closest('.insider-landing-column').find('.insider-landing-column-progress-inner').first(); 1340 if ((e.clientX)&&(e.clientY)) { 1341 points[0] = e; 1342 } else if (e.targetTouches) { 1343 points = e.targetTouches; 1344 e.preventDefault(); 1345 } 1346 for (let i = 0; i<points.length; i++) { 1347 $target.css('transform','translateY(' + points[i].clientY + 'px)'); 1348 } 1349 }); 1350 1351 //HI category selection form 1352 $('.hi-block-category-search-form').on('submit', function (e) { 1353 e.preventDefault(); 1354 1355 // redirect to selected category 1356 let selectedCategory = $(this).find('[name="category"]').val(); 1357 if (selectedCategory) { 1358 location.href = selectedCategory; 1359 } 1360 }); 1361 1362 1363 /** 1364 * Datepicker Accessibility 1365 * */ 1366 1367 //Send message to Datepicker Reader 1368 function readDatepicker(message) { 1369 let $datepickerReader = $('#datepicker-reader'), 1370 oldMessage = $datepickerReader.html(); 1371 if (oldMessage != message) { 1372 $datepickerReader.html(message); 1373 } 1374 } 1375 1376 //Read Floating Datepicker 1377 function readFloatingDatepicker() { 1378 let $datepicker = $('#ui-datepicker-div'); 1379 if ($datepicker.css('display') === 'block') { 1380 $('body').addClass('keyboard-in-use'); 1381 let day = $datepicker.find('.ui-state-hover'), 1382 month = $datepicker.find('.ui-datepicker-month'), 1383 year = $datepicker.find('.ui-datepicker-year'), 1384 message = 1385 ' ' + day.html() + 1386 ' ' + month.html() + 1387 ' ' + year.html(); 1388 readDatepicker(message); 1389 } 1390 } 1391 1392 $(document).bind('keyup', function (e) { 1393 readFloatingDatepicker(); 1394 }); 1395 1396 //Read Inline Datepicker 1397 $.fn.readInlineDatepicker = function () { 1398 let $datepicker = $(this).closest('.ui-datepicker-inline'), 1399 day = $(this).attr('data-date'), 1400 month = $datepicker.find('.ui-datepicker-month').html(), 1401 year = $datepicker.find('.ui-datepicker-year').html(), 1402 message = 1403 ' ' + day + 1404 ' ' + month + 1405 ' ' + year; 1406 readDatepicker(message); 1407 } 1408 1409 $(document).on('focus', '.ui-datepicker-inline td a', function () { 1410 $(this).readInlineDatepicker(); 1411 }); 1412 1413 1414 /** 1415 * Weglot Accessibility 1416 * */ 1417 1418 //Make weglot dropdown keyboard accessible 1419 $('.weglot-dropdown .wg-li').each(function () { 1420 $(this).attr('tabindex', '0'); 1421 }).on('keypress', function (e) { 1422 e.preventDefault(); 1423 let code = e.keyCode || e.which, 1424 $link = $(this).find('a').first(); 1425 if (code == 13) { 1426 if ($link.length) { 1427 $link.get(0).click(); 1428 } else { 1429 $(this).trigger('click'); 1430 } 1431 } 1432 });
1433 1434 //close weglot dropdown on focusout 1435 $('.weglot-dropdown').on('focusout', function (e) { 1436 if (!$(this).is(':focus-within')) { 1437 $(this).find('input[type="checkbox"]').first().prop("checked", false); 1438 } 1439 }); 1440 1441 /** 1442 * Keypress Events 1443 * */ 1444 1445 $(document).bind('keydown', function (e) { 1446 //console.log(document.activeElement); 1447 let code = getCharacterCode(e); 1448 if (code == 27) { 1449 //ESC Pressed 1450 $('#menu-primary-navigation > .active').menuFadeOut(); //close nav dropdown 1451 closeModal(); //close popup modal 1452 } 1453 if (code == 9) { 1454 //Tab Pressed 1455 $('body').addClass('keyboard-in-use'); //add a11y buttons to nav 1456 } 1457 }); 1458 1459});
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.