1/** 2 * @file 3 * A JavaScript file for the theme. 4 */ 5 6// Inspired by Zen theme https://www.drupal.org/project/zen. 7// JavaScript should be made compatible with libraries other than jQuery by 8// wrapping it with an "anonymous closure". See: 9// - https://drupal.org/node/1446420 10// - http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth 11(function ($, Drupal) { 12 13 'use strict'; 14 15 // To understand behaviors, see https://drupal.org/node/756722#behaviors 16 Drupal.behaviors.topPageLink = { 17 attach: function (context, settings) { 18 19 /* 20 * Creates a link back to the top of the page. 21 * A link appears when the user has scrolled sufficiently which returns 22 * them back to the top of the page. 23 */ 24 $.fn.topLink = function (settings) { 25 settings = $.extend({ 26 min: 1, 27 fadeSpeed: 200 28 }, settings); 29 30 return this.each(function () { 31 // Listen for scroll. 32 var el = $(this); 33 // In case the user forgot. 34 el.hide(); 35 $(window).scroll(function () { 36 if ($(window).scrollTop() >= settings.min) { 37 el.fadeIn(settings.fadeSpeed); 38 } 39 else { 40 el.fadeOut(settings.fadeSpeed); 41 } 42 }); 43 }); 44 }; 45 46 // Assign anchor element. 47 var $element = $('a#js-top-page-link'); 48 49 // Check if element exist. 50 if ($element.length) { 51 52 // Settings for anchor. 53 $element.topLink({ 54 min: 1200, 55 fadeSpeed: 500 56 }); 57 58 // Animate smooth scroll. 59 $element.click(function (e) { 60 e.preventDefault(); 61 $('html, body').animate({ scrollTop: 0 }, 'slow'); 62 }); 63 } 64 } 65 }; 66 67})(jQuery, Drupal); 68; 69/** 70 * @file mcgill_js/plugins.js 71 */ 72 73/** 74* hoverIntent is similar to jQuery's built-in "hover" function except that 75* instead of firing the onMouseOver event immediately, hoverIntent checks 76* to see if the user's mouse has slowed down (beneath the sensitivity 77* threshold) before firing the onMouseOver event. 78* 79* hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+ 80* <http://cherne.net/brian/resources/jquery.hoverIntent.html> 81* 82* hoverIntent is currently available for use in all personal or commercial 83* projects under both MIT and GPL licenses. This means that you can choose 84* the license that best suits your project, and use it accordingly. 85* 86* // basic usage (just like .hover) receives onMouseOver and onMouseOut functions 87* $("ul li").hoverIntent( showNav , hideNav ); 88* 89* // advanced usage receives configuration object only 90* $("ul li").hoverIntent({ 91* sensitivity: 7, // number = sensitivity threshold (must be 1 or higher) 92* interval: 100, // number = milliseconds of polling interval 93* over: showNav, // function = onMouseOver callback (required) 94* timeout: 0, // number = milliseconds delay before onMouseOut function call 95* out: hideNav // function = onMouseOut callback (required) 96* }); 97* 98* @param f onMouseOver function || An object with configuration options 99* @param g onMouseOut function || Nothing (use configuration options object) 100* @author Brian Cherne brian(at)cherne(dot)net 101* @name jQuery.fn.hoverIntent 102* @method 103*/ 104(function($) { 105 $.fn.hoverIntent = function(f,g) { 106 // default configuration options 107 var cfg = { 108 sensitivity: 7, 109 interval: 250, 110 timeout: 20 111 }; 112 // override configuration options with user supplied object 113 cfg = $.extend(cfg, g ? { over: f, out: g } : f ); 114 115 // instantiate variables 116 // cX, cY = current X and Y position of mouse, updated by mousemove event 117 // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval 118 var cX, cY, pX, pY; 119 120 // A private function for getting mouse position 121 var track = function(ev) { 122 cX = ev.pageX; 123 cY = ev.pageY; 124 }; 125 126 // A private function for comparing current and previous mouse position 127 var compare = function(ev,ob) { 128 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 129 // compare mouse positions to see if they've crossed the threshold 130 if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) { 131 $(ob).unbind("mousemove",track); 132 // set hoverIntent state to true (so mouseOut can be called) 133 ob.hoverIntent_s = 1; 134 return cfg.over.apply(ob,[ev]); 135 } else {
136 // set previous coordinates for next time 137 pX = cX; pY = cY; 138 // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) 139 ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); 140 } 141 }; 142 143 // A private function for delaying the mouseOut function 144 var delay = function(ev,ob) { 145 ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); 146 ob.hoverIntent_s = 0; 147 return cfg.out.apply(ob,[ev]); 148 }; 149 150 // A private function for handling mouse 'hovering' 151 var handleHover = function(e) { 152 // copy objects to be passed into t (required for event object to be passed in IE) 153 var ev = jQuery.extend({},e); 154 var ob = this; 155 156 // cancel hoverIntent timer if it exists 157 if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } 158 159 // if e.type == "mouseenter" 160 if (e.type == "mouseenter") { 161 // set "previous" X and Y position based on initial entry point 162 pX = ev.pageX; pY = ev.pageY; 163 // update "current" X and Y position based on mousemove 164 $(ob).bind("mousemove",track); 165 // start polling interval (self-calling timeout) to compare mouse coordinates over time 166 if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} 167 168 // else e.type == "mouseleave" 169 } else { 170 // unbind expensive mousemove event 171 $(ob).unbind("mousemove",track); 172 // if hoverIntent state is true, then call the mouseOut function after the specified delay 173 if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} 174 } 175 }; 176 177 // bind the function to the two event listeners 178 return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover); 179 }; 180})(jQuery); 181 182 183(function($) { 184 185 /** 186 * Measures Height of an object and then sets all items to the Max height 187 * @author Jason DelMarr 188 * @name jQuery.fn.equalHeights 189 * @method 190 */ 191 $.fn.equalHeights = function(){ 192 193 var heights = $(this).map(function(){ 194 return $(this).height(); 195 }).get(), 196 197 maxHeight = Math.max.apply(null, heights); 198 if (screen.width > 600) { 199 $(this).height(maxHeight); 200 } 201 }; 202 203 204 /** 205 * Sets a Boolean if element displays in viewport. 206 * @author Jason DelMarr 207 * @name jQuery.fn.isInViewport 208 * @boolean 209 */ 210 $.fn.isInViewport = function() { 211 212 // Element measurements. 213 var elementTop = $(this).offset().top; 214 var elementBottom = elementTop + $(this).outerHeight(); 215 216 // Viewport measurements. 217 var viewportTop = $(window).scrollTop(); 218 var viewportBottom = viewportTop + $(window).height(); 219 220 // Return TRUE or FALSE; 221 return elementBottom > viewportTop && elementTop < viewportBottom; 222 }; 223 224 225})(jQuery); 226; 227(function ($) { 228 229 230/** 231 * Toggle explore McGill menu 232 * 233 */ 234Drupal.behaviors.exploremcgill = { 235 attach: function() { 236 // establish some useful vars... 237 var toggle = $('#explore-mcgill'); 238 var nav = $('#global-nav'); 239 240 //logic for click, dont wanna keep adding events to same UI element since we're in a repeating behaviour here... 241 //checking to see if the toggle has already had an event attached (should probably see if its a click too) and if not, add it, if it does, nothing. Similar to run-once logic elsewhere... 242 if (!toggle.data('events')) { 243 244 //if explore-mcgill button clicked 245 toggle.css('cursor', 'pointer').click(function() { 246 //toggle global nav 247 nav.slideToggle(function() { 248 if ($('#explore-mcgill').hasClass('open')) { 249 $('.homepage-cta').removeClass('moveit').addClass('hide'); 250 $('body').addClass('tool-open'); 251 } else { 252 $('.homepage-cta').addClass('moveit show').removeClass('hide'); 253 $('body').removeClass('tool-open'); 254 } 255 }); 256 toggle.toggleClass('open'); 257 return false; 258 }); 259 } 260 } 261}; 262 263/** 264 * quickStyle maintains equal heights for menu columns 265 * Strips out ahref and replaces with <h2 /> 266 * 267 */ 268Drupal.behaviors.quickStyle = { 269 attach: function(){ 270 271 $('#block-menu-menu-quicklinks ul ul li a').not('#block-menu-menu-quicklinks ul ul ul li a').each(function(i, e){ 272 $(this).wrap('<h3></h3>'); 273 var text = $(this).replaceWith( this.childNodes ); 274 }); 275 276 //Wraps equalHeights call in a name-spaced click handler, which is then 277 $('#explore-mcgill').bind('click.menuReceiver', function(){ 278 $('#block-menu-menu-quicklinks ul li').not("#block-menu-menu-quicklinks ul li ul li").equalHeights(); 279 //unbound, so it only happens once per page. 280 $('#explore-mcgill').unbind('click.menuReceiver'); 281 }); 282 } 283}; 284 285 286/** 287noToolbar cleans up extra padding present when the Toolbar module is turned off.
288*/ 289function noToolbar (){ 290 if (!$('body').hasClass('toolbar')) { //&& $('body').hasClass('tool-open pin pin-processed')) { 291 $('body').css({'background': 'red'}); 292 } 293 alert("something"); 294}; 295 296})(jQuery); 297; 298(function ($) { 299/** 300 * Drupal accordions 301 * 302 */ 303 304 Drupal.mcgillEditorWidgets = { 305 accordionWidgets: { 306 create: function() { 307 //Hide all accordion divs. 308 $('#container .accordion > div').hide(); 309 //Show any divs with an open class. 310 $('#container .accordion .open').show(); 311 //Add the active class to headers with open divs. 312 $('#container .accordion > div').prev().addClass('active'); 313 }, 314 interact: function() { 315 $('#container .accordion :header').click(function(event) { 316 event.preventDefault(); // this will prevent the default navigation action 317 if ($(this).next().is(":visible")) { 318 $(this).next().hide(); 319 $(this).siblings('div').removeClass('open'); 320 $(this).addClass('active'); 321 } else { 322 $(this).next().show(); 323 $(this).siblings('div').removeClass('open'); 324 $(this).removeClass('active'); 325 } 326 }); 327 } 328 } 329 }; 330 331Drupal.behaviors.accordion = { 332 attach: function() { 333 var accordions = Drupal.mcgillEditorWidgets.accordionWidgets; 334 accordions.create(); 335 accordions.interact(Drupal.mcgillEditorWidgets.accordionWidgets.lockableElement); 336 337 // // Get all the accordions 338 var allAccordions = $('.content div.accordion'); 339 340 // Check if accordion was selected in URL and open if exists 341 allAccordions.each( function() { 342 343 var accordionLinks = $(this).find('h3 > a'); 344 345 accordionLinks.each ( function () { 346 347 // If href exist than match href in accordion title with anchor specified in URL 348 if ( $(this).attr('href') && $(this).attr('href').substring(1) == window.location.hash.substring(1) ) { 349 350 // Variable created to be accessed by window.setTimeout(). 351 Drupal.accessLink = this; 352 353 /* Gotta wait till other the other stuff is rendered, 354 it'd be better to have a proper callback for this kinda thing... */ 355 356 window.setTimeout(function() { 357 358 // Initiate accordion click associated to url ('#') argument. 359 // Has the effect of opening the accordion. 360 $(Drupal.accessLink).click(); 361 362 }, 1000); 363 } 364 }); 365 366 }); 367 } 368}; 369 370/** 371 * Drupal advanced slide and toggle accordions 372 * 373 */ 374Drupal.behaviors.advanced_accordion = { 375 attach: function() { 376 //Hide all accordion slider divs. 377 $('#content .advanced-accordion .slider').hide(); 378 //Show any slider divs with an open class. 379 $('#content .advanced-accordion .open').show(); 380 //Add the active class to toggler divs with open .slider divs. 381 $('#content .advanced-accordion .open').prev().addClass('active'); 382 $('#content .advanced-accordion .toggler').css('cursor','pointer').click(function() { 383 //If clicked .toggler div has a hidden slider div below it. 384 if ($(this).next('.slider').is(':hidden')) { 385 //Hide sibling slider divs. 386 $(this).siblings('.slider').slideUp(); 387 //Remove the open class from sibling slider divs. 388 $(this).siblings('.slider').removeClass('open'); 389 //Remove the active class from toggler divs with hidden slider divs. 390 $(this).siblings('.toggler').removeClass('active'); 391 //Reveal this toggler div's slider div. 392 $(this).next('.slider').slideDown(); 393 //Add the open class to this toggler div's slider div. 394 $(this).next('.slider').addClass('open'); 395 //Add the active class to this toggler div. 396 $(this).addClass('active'); 397 } else { 398 //Hide this this toggler div's slider div. 399 $(this).next('.slider').slideUp(); 400 //Remove the open class from this toggler div's slider div. 401 $(this).next('.slider').removeClass('open'); 402 //Remove the active class from this toggler div. 403 $(this).removeClass('active'); 404 } 405 }); 406 } 407}; 408 409/** 410 * Drupal tabs 411 */ 412Drupal.behaviors.tabs = { 413 attach: function() { 414 415 // Get the parent of the tabs. 416 var tabParent = $('.content div.tabs'); 417 418 // Initialize array of all tab containers. 419 var arrayContent = []; 420 421 // Get the tab selecting UI. If this exist there must be tabs. 422 var tabLinks = $('.content div.tabs ul.tab-nav li > a'); 423 var tabReady = tabLinks; 424 425 var tabContent = tabParent.find('>div'); 426 427 // If there isn't tabs for tab content, do nothing. 428 if (tabReady.length) { 429 430 // Keeping tab functionality encapsulated for multiple tabs on a page. 431 tabParent.each(function(index) { 432 433 var links = $(this).find('ul li > a'); 434 435 // Get the tab selecting UI. 436 var tabContent = $(this).find('>div'); 437 var i = parseInt(index) + 1; 438 439 // Add class to Parent Container. 440 $(this).addClass('tab-parent-' + index); 441 442 // Make a big list of all the tabs contents containers. 443 $.merge(arrayContent, tabContent); 444 445 // Keeps track of which tab is active and it's associatted content. 446 var $active = $(this).find('a'); 447 448 // Sets active dependent from url else use first tab. 449 $active = $( links.filter('[href="'+location.hash+'"]')[0] || links[0] ); 450 $active.addClass('selected'); 451 452 // Collect index of active tab 453 $activeIndex = links.index($active); 454 455 // Show associated content and hide siblings. 456 tabContent.eq($activeIndex).show().siblings('div').hide(); 457 458 459 }); 460 461 // Clicking tabs 462 tabLinks.click(function(e) { 463 var target = e.target, 464 // Records index that was clicked. 465 activeIndex = tabLinks.index(target), 466 467 // Sets associated container. 468 $targetContainer = $(arrayContent[activeIndex]); 469 $targetContainer.show(); 470 471 // Make sure the siblings (i.e. other tab contents are hidden). 472 $targetContainer.siblings('.container').hide(); 473 474 // Remove the selected class from the other tabs. 475 $(e.target).parent('li').siblings('li').children('a').removeClass('selected'); 476 477 // Add the selected class to the clicked tab. 478 $(this).addClass('selected'); 479 e.preventDefault(); 480 }); 481 482 // Mark tab content with index id's and classes. 483 tabContent.each(function(index) { 484 index++; 485 $(this).attr('id', 'tab-'+ $(this).index()); 486 $(this).addClass('container tab-set-'+ $(this).index()); 487 }) 488 489 490 }; 491 } 492}; 493 494})(jQuery); 495; 496/** 497 * Moriarty Mega Menu. 498 * Creates a mega menu in the right contexts. 499 * 500 */ 501( function( $ ) { 502 /**
503 * To Be used when an accordion menu comes to mobile 504 */ 505 Drupal.behaviors.mobileScroll = { 506 attach: function() { 507 508 if ( Modernizr.mq( "only all and (max-width: 680px)" ) && $("#nav-column").length ) { 509 510 /* 511 * Sets vertical navigation for mobile. 512 */ 513 var navCol = $("#nav-column").insertBefore('div#top-page'); 514 var position = navCol.offset().top; 515 516 var lastScrollTop = 0; 517 var delta = 5; 518 var navHeight = $('#nav-menu').outerHeight(); 519 520 /* 521 * Records when scroll has happened in relation to button location. 522 * & secondary nav will stick to top. 523 */ 524 $(window).scroll(function(event){ 525 hasScrolled(); 526 }); 527 528 // Actions performed on up and down scrolling. 529 var hasScrolled = function() { 530 531 var scrLocation = $(window).scrollTop(); 532 533 if (scrLocation >= position) { 534 navCol.addClass("fixed"); 535 } 536 537 if(Math.abs(lastScrollTop - scrLocation) <= delta) { 538 return; 539 } 540 541 if(scrLocation < position){ 542 navCol.removeClass("fixed"); 543 navCol.removeClass('nav-up').addClass('nav-down'); 544 } 545 else if(scrLocation > lastScrollTop && scrLocation > navHeight) { 546 547 // Hide section navigation on scroll down. 548 navCol.removeClass('nav-down').addClass('nav-up'); 549 550 } 551 else if(scrLocation + $(window).height() < $(document).height()) { 552 553 // Show section navigation on scroll up. 554 $('#nav-column .menu').removeClass('state-open').addClass('state-hide'); 555 // Close section nav when scrolling up 556 navCol.removeClass('nav-up').addClass('nav-down'); 557 558 } 559 560 // Updates last scrolled location. 561 lastScrollTop = scrLocation; 562 }; 563 564 565 // When clicking on the Main menu check state of section menu. 566 $('#superfish-1-toggle').click(function(){ 567 568 // Close section nav when we open Main menu. 569 if ($('#nav-column .menu').hasClass('state-open')) { 570 $('#nav-column .menu').removeClass('state-open').addClass('state-hide'); 571 } 572 }); 573 574 575 // Display a menu for section navigation. 576 var sectionMenu = function () { 577 // First hide section menu. 578 $('#nav-column .menu').addClass('state-hide'); 579 580 // When the menu button clicked, it opens the menu and closes the 581 // superfish menu if open. 582 $('#nav-menu .btn-nav').click( function(){ 583 584 // Toggle open and closed states. 585 $('#nav-column .menu').toggleClass('state-hide state-open'); 586 587 if ( $('#superfish-1-accordion').hasClass('sf-expanded') ) { 588 // Set click to close superfish menu. 589 $('#superfish-1-toggle').click(); 590 } 591 }); 592 }; 593 594 // Executes section menu functions. 595 sectionMenu(); 596 597 } // mobile check. 598 } 599 }; 600 601 602 Drupal.behaviors.mobile = { 603 attach: function() { 604 if ( Modernizr.mq( "only all and (max-width: 680px)" ) ) { 605 606 // Moves search bar into superfish element. 607 $( "#strap-search" ).prependTo( $( "#superfish-1-accordion" ) ); 608 609 // Checks if McGill logo exist. 610 if ( $('#title-box').length) { 611 612 // Adds superfish menu block below logo. 613 $( "#navigation" ).find( ".block-superfish" ).insertAfter( $( "#title-box" ) ); 614 615 } else { 616 617 // Adds superfish menu at top of body. 618 $( "#navigation" ).find( ".block-superfish" ).prependTo( $( "body" ) ); 619 } 620 621 $( "body" ).addClass( "mb" ); 622 } 623 } 624 }; 625 626 /** 627 * If the menu is too wide for one line, hide overflowing items. 628 */ 629 Drupal.behaviors.longMainMenu = { 630 631 attach: function( context ) { 632 633 /* 634 * The .expanded-nav class is set by the site administrator 635 * to display all the menu items in the horizontal menu to help 636 * organize their site structure since we wil no longer be hiding 637 * menu items in Drupal 9. 638 */ 639 640 var longMenuEl = $( "body:not(.expanded-nav) .sf-main-menu > li.first"); 641 642 /* 643 * Page loads completely before measuring which menu items are on the line. 644 * If other menu items are displayed below (i.e. in second line) hide 645 * them. 646 */ 647 setTimeout(function(){ 648 649 // Execute only if menu has more than one item. 650 if ( longMenuEl.length > 0 ) { 651 652 // Get top position of regular menu items 653 var topLocation = longMenuEl.position().top; 654 655 // Make sure loop has only run once. 656 var hasLooped = false;
657 658 if(!hasLooped) { 659 660 $( ".sf-main-menu > li" ).each( function() { 661 662 if ( $( this ).position().top != topLocation ) { 663 $( this ).hide(); 664 } 665 }); 666 667 // Prevents multiple loops. 668 hasLooped = true; 669 } 670 } 671 }, 600); 672 } 673 }; 674 675 /** 676 * Kill links for footer headers 677 * 678 */ 679 Drupal.behaviors.footerMenu = { 680 attach: function() { 681 $( "#footer-nav > ul > li > ul.menu > li:not('.leaf') > a" ).css( "top", "0" ).click( function( e ) { 682 e.preventDefault(); 683 } ); 684 } 685 }; 686 687} )( jQuery ); 688; 689/** 690 * @file moriarty/js/presentation.js 691 */ 692 693(function ($) { 694 695 696 697 Drupal.behaviors.blocksActions = { 698 699 attach: function() { 700 // Look for all the call to action blocks in this region that have enhanced 701 // mode. 702 var cTAs = $('.region-highlighted .block-bean.mode-enhanced'); 703 704 // Wraps and sets position of CTA's dynamically, so more than one can fit on 705 // the right. 706 function setWrapPosition(cTAs) { 707 var wrapper = $('<div class="cta-dynamic-wrapper" />'); 708 cTAs.wrapAll(wrapper); 709 } 710 711 // Toggles hide/show of CTA 712 function toggleCTA() { 713 // Change the 'X' icon to an arrow and vice-versa. 714 $(this).toggleClass('fa-times fa-arrow-left'); 715 // Hide/Show the parent CTA. 716 $(this).parents('.block-bean').toggleClass('hidden'); 717 } 718 719 if(cTAs) { 720 // Creates show/hide toggle. 721 $.each(cTAs, function(i, el) { 722 $(el).find('.field-name-field-single-link') 723 .after('<i class="fa fa-times toggle corner"></i>'); 724 var toggle = $(el).find('.toggle'); 725 toggle.bind('click', toggleCTA); 726 }); 727 // Position all of the cTAs appropriately while wrapping them. 728 setWrapPosition(cTAs); 729 } 730 } 731 }; 732 733 734 /* 735 * Preview displays a small amount of text 736 * allowing the visitor a button to see more 737 */ 738 Drupal.behaviors.preview = { 739 attach: function() { 740 741 // Find all instances of Preview. 742 // Check if the class preview exists. 743 $('.preview-container .preview').each(function(){ 744 745 746 // Creates indicator. 747 var $indicator = $('<div class="indicator"></div>'); 748 749 $indicator.addClass('fa fa-chevron-down'); 750 751 // Adds indicator. 752 $(this).children().first().before($indicator); 753 754 755 // Hide and reveal functions 756 showPreview = function(obj){ 757 obj.show(); 758 }; 759 760 hidePreview = function(obj){ 761 obj.hide(); 762 }; 763 764 // Find siblings. 765 $previewExtra = $(this).siblings('.preview-content'); 766 767 // Hide siblings on load. 768 hidePreview($previewExtra); 769 770 // Combines indicator and clickable title. 771 var expandsPreview = $indicator.add($(this).find('h2')); 772 773 // On click or hover reveal siblings. 774 expandsPreview.click(function(event){ 775 776 event.preventDefault(); 777 // Toggle indicator. 778 $indicator.toggleClass("fa-chevron-down fa-chevron-up"); 779 780 /** 781 * Retrieves the siblings of the parent element. 782 * 783 * @returns {jQuery} A jQuery object containing the siblings of the parent element. 784 */ 785 var $siblings = $(this).parent().siblings(); 786 var cssDisplay = $siblings.css('display'); 787 788 $siblings.css('display', cssDisplay !== 'none' ? 'none' : 'block'); 789 790 }); 791 }); 792 } 793 }; 794 795 796 /* 797 * TAG display 798 */ 799 800 Drupal.behaviors.tag = { 801 attach: function() { 802 //Container used as a wrapper div 803 var container = $('.mcgill-tags'); 804 //Items within wrapper 805 var items = container.children(); 806 807 //Sets handle to empty to avaoid duplicates 808 var handle = ''; 809 810 //If handle hasn't been created generate clickable handle 811 if (handle.length < 1) { 812 813 handle = $('<div class="fa fa-tags toggle"></div>'); 814 815 //Creates handle inside of container 816 container.prepend(handle); 817 } 818 819 //Sets items to be invisible 820 items.addClass('hide'); 821 822 //Toggle between show and hide states 823 handle.click(function(){ 824 items.toggleClass('hide'); 825 }); 826 } 827 }; 828 829 Drupal.behaviors.tag.attach = null; 830 831 832 /** 833 * Shortens login names. 834 * 835 */ 836 Drupal.behaviors.login_short = { 837 attach: function() { 838 var username = $('.block-user h3').text().replace('.',' '); 839 // Username replacing . with a space for wrapping. 840 var i = username.indexOf("@"); 841 // Remove text including and after @. 842 if ( i >= 0 ) { 843 username = username.substring(0, i); 844 } 845 $('.block-user h3').replaceWith('<h3>'+ username + '</h3>'); 846 } 847 }; 848 849 850 /** 851 * Center Media when container is wider than element. 852 * 853 */ 854 Drupal.behaviors.centerMedia = { 855 attach: function() { 856 857 // Check if Media nivo is available. 858 if(Drupal.behaviors.mediaNivoSlider) { 859 860 var containerWidth = $('.block-media-nivo-slider').parent().width(); 861 // Checks image width that was uploaded my content creator. 862 var imageWidth = $('.block-media-nivo-slider').find('img.nivo-main-image').attr('width'); 863 864 $('.block-media-nivo-slider').each(function() { 865 866 if(imageWidth <= containerWidth ) { 867 // Add class that applies a max-width determined my legacy region sizes 868 // to legacy created slideshows. 869 $(this).addClass('js-center-media'); 870 } 871 }); 872 } 873 } 874 }; 875 876 /** 877 * Evenly spaced grids. 878 * Spaces out grids on the horizontal plane. 879 */ 880 Drupal.behaviors.views_grid = { 881 attach: function(){ 882 // Setting some default variables. sGrid, a specific grid to use, rather than 883 // defaultClass or defaultGrid. The most specific option. Null value should 884 // NOT be permanent default value for sGrid. sGrid should ideally be set 885 // elsewhere, a module, as should defaultClass. 886 var sGrid = null; 887 /// defaultClass, a string used to evaluate whether we should use this 888 /// function on a generic basis. Somewhat general. 889 var defaultClass = null; 890 891 // DefaultGrid, a JQ object or array of objects that are used if nothing 892 // else is set; the most general option. Current defaultGrid is only for 893 // community site grid to be changed. 894 var defaultGrid = $('.equal table.views-view-grid'); 895 896 var grid = null; 897 if (sGrid || defaultClass) { 898 // grid value is either sGrid or if not available, defaultClass. 899 grid = sGrid? sGrid : defaultClass; 900 } 901 else if (defaultGrid) { 902 // Only fire defaultGrid as a last resort, if neither sGrid or 903 // defaultClass have values. Perhaps not the fall-back we'd like. 904 grid = defaultGrid; 905 } 906 var tableWidth = grid.width(); 907 var tdNum = grid.each(function() { 908 // Find number of TDs in table. 909 var NumNum = $(this).find('td').length; 910 // Divide table width by number of TDs. 911 var equal = parseInt(tableWidth / NumNum, 10); 912 grid.find('td').width(equal); 913 }); 914 } 915 }; 916 917 918 /** 919 * Highlights block corners. 920 * 921 */ 922 Drupal.behaviors.highlight_block = { 923 attach: function(){ 924 $('#sidebar-column .block.highlight').append('<span class="deco"></span>'); 925 } 926 }; 927 928 929 /** 930 * Measures quote width. 931 * 932 */ 933 Drupal.behaviors.quote_block = { 934 attach: function(){ 935 var length = $('.block.quote p').css('float','left').width() + 60; 936 $('.block.quote .views-field-body').width(length); 937 $('.block.quote p').css('float','none'); 938 } 939 }; 940 941 942 /** 943 * Handles pull quotes. 944 * Adds ellipsis, removes images. 945 */ 946 Drupal.behaviors.pull_quote = { 947 attach: function(){ 948 $('span.pull-quote').each(function(i){ 949 var $parentParagraph = $(this).parent('p'); 950 $parentParagraph.css('position', 'relative'); 951 var $clonedCopy = $(this).clone(); 952 // Adds ... for dropped text. 953 $clonedCopy.addClass('pulled').find('span.drop').html('…').end().prependTo($parentParagraph); 954 // Removes images and other tags. 955 var clonedText = $clonedCopy.text(); 956 $clonedCopy.html(clonedText); 957 }); 958 } 959 }; 960 961 /** 962 * Fade in Big Numbers in viewport. 963 */ 964 Drupal.behaviors.bigNumbersAnimation = { 965 attach: function() { 966 967 // Get the BN's components. 968 var bNS = $('bignumber') || []; 969 970 // Set a reasonable interval to check conditions. 971 var checkInterval = 400; 972 973 // If we have no BNs, do nothin. 974 if (bNS.length < 1) { 975 return null; 976 } 977 // If we have some, set up animation... 978 else { 979 980 // Custom event for BN components to listen to. Could be anything. 981 // var fadeInEvent = new Event('fadein'); IE11 incompatible. 982 var fadeInEvent = jQuery.Event('fadein'); 983 984 // Counter for seeing how many BN's have animated. 985 var completedBns = 0; 986 bNS.each(function(){ 987 // Listen for custom event. 988 // this.addEventListener('fadein', function(){; IE11 incompatible. 989 $(this).bind("fadein", function(){ 990 var t = $(this); 991 // Check if the fade-in class is already applied and if the item is in the viewport. 992 if (!t.hasClass("fade-in") && jQuery.fn.isInViewport && t.isInViewport()) { 993 t.addClass("fade-in"); 994 completedBns++; 995 } 996 }); 997 }); 998 999 // This seems better than scroll directly... 1000 // One because I thinks less burdensome. 1001 // 2 I think its more modular. 1002 // 3 Because it works better with content if you haven't scrolled - i.e. it appears soon. 1003 // Dispatches a custom event at checkInterval interval so BN's can respond. 1004 var windowTimer = window.setInterval(function() {
1005 // Only dispatch event as long as it needs to be dispatched. 1006 if (completedBns < bNS.length) { 1007 bNS.each(function(){ 1008 $(this).trigger(fadeInEvent); 1009 // this.dispatchEvent(fadeInEvent); IE11 incompatible. 1010 }); 1011 } else { 1012 // Clear window interval when done. 1013 window.clearInterval(windowTimer); 1014 } 1015 }, 1016 checkInterval 1017 ); 1018 } 1019 } 1020 }; 1021 1022 1023 /** 1024 * Makes blocks the same height 1025 * Calculates heights and makes them all the same 1026 * equalHeights is a custom method in [plugin.js]{@link 1027 * http://www.google.com}. 1028 * 1029 */ 1030 Drupal.behaviors.blockHandle = { 1031 attach: function() { 1032 //We should probably check somewhere that this plugin exists before 1033 //implementing it, unless we add it as a Drupal dependency to the theme. 1034 if(jQuery.fn.equalHeights) { 1035 // Remove margins on the last block. 1036 $('.block-block.half-width').equalHeights(); 1037 $('.block-block.third-width').equalHeights(); 1038 1039 // Adds class on last block in a row. 1040 $('.region').each(function(){ 1041 1042 // Add last-block class to every second item. 1043 $(this).find('.mode-half, .half-width').filter(function(index){ 1044 return index % 2 == 1; 1045 }).addClass("last-block"); 1046 1047 // Add last-block class to every third item. 1048 $(this).find('.mode-third, .third-width').filter(function(index){ 1049 return index % 3 == 2; 1050 }).addClass("last-block"); 1051 1052 // Add last-block class to every fourth item. 1053 $(this).find('.quarter-width').filter(function(index){ 1054 return index % 4 == 3; 1055 }).addClass("last-block"); 1056 1057 }); 1058 } 1059 } 1060 }; 1061 1062 1063 /** 1064 * Translates the text in #explore-mcgill based on language. 1065 * 1066 */ 1067 Drupal.behaviors.translater = { 1068 attach: function(){ 1069 $('.i18n-fr #explore-mcgill').text('Liens rapides'); 1070 } 1071 }; 1072 1073 1074 /** 1075 * Enables a number of theme features. 1076 * Sets up the top link, some vertical nav stuff, sectional sites stuff, etc. 1077 * 1078 */ 1079 Drupal.behaviors.enabler = { 1080 attach: function() { 1081 1082 // Vertical Nav. 1083 var currentPage = 'li.expanded.active-trail a.active:last'; 1084 1085 $('li.expanded.active-trail > a').addClass('parent-select'); 1086 $(currentPage).addClass('nav-select'); 1087 1088 // Hack to remove empty h3 in channels. 1089 $('#sidebar-column .block h3').filter(function() { 1090 return $.trim($(this).text()) === ''; 1091 }).remove(); 1092 1093 // Sectional sites. 1094 $('.section-site #nav-column').children('ul').children('li.first').addClass('home-section'); 1095 } 1096 }; 1097 1098 1099 /** 1100 * Clear search bar on focus 1101 * 1102 */ 1103 Drupal.behaviors.searchquery = { 1104 attach: function() { 1105 $('#searchquery').bind('focus', function(){ 1106 if( this.value == 'Search McGill' ) { 1107 this.value = ''; 1108 } 1109 }); 1110 } 1111 }; 1112 1113 1114})(jQuery); 1115; 1116/** 1117 * Make table sortable 1118 * Uses tablesorter library 1119 */ 1120(function ($) { 1121 1122 Drupal.behaviors.tableSorter = { 1123 1124 attach: function (context, settings) { 1125 // If Jquery plugin tablesorter library exist, apply sort functionality. 1126 if($.tablesorter) { 1127 1128 $('table.tablesorter', context).each(function() { 1129 1130 $(this).tablesorter({ 1131 1132 textExtraction: function(s) { 1133 1134 var text = $(s).text(); 1135 1136 if (/^\$?([0-9]{1,3}[,\s]([0-9]{3}[,\s])*[0-9]{3}|[0-9]+)(\.[0-9][0-9])?$/.test(text)) { 1137 return text.replace(/[,\s]/g, ''); 1138 } else { 1139 return text; 1140 } 1141 } 1142 }); 1143 }); 1144 } 1145 } 1146 }; 1147}(jQuery)); 1148; 1149'use strict'; 1150 1151/** 1152 * Drupal.mcgillDropDown 1153 * 1154 * Takes config {} that contains groups of existing DOM elements to hide/show, 1155 * using <select /> element to allow user to pick which group to display. 1156 * 1157 * Requires jQuery & Drupal root object & Drupal.t translate method. 1158 */ 1159(function ($) { 1160 Drupal.mcgillDropDown = function(config) { 1161 1162 // Specify default configuration for Dropdown. 1163 var defaultConfig = { 1164 1165 // Name used to identify instance and provide ID to select for a11y etc. 1166 // By default will provide a semi-random string. 1167 name: 'DropDown-' + Math.random().toString(30).substring(2,8), 1168 1169 // Array of objects with properties: 1170 // * elements 1171 // * groupName 1172 // e.g. 1173 // [ 1174 // {groupName: "Foos", elements: $('.foo')}, 1175 // {groupName: "Bars", elements: $('.bar')} 1176 // ] 1177 // OR just jQuery array-like obj. 1178 // e.g. 1179 // $('.foo'); 1180 // to display depending on selection. 1181 groups: [], 1182 1183 // DOM element/jQuery obj. to add the DropDown after. 1184 insertAfter: {}, 1185 1186 // DOM element/jQuery obj. to add the DropDown before. 1187 insertBefore: {}, 1188 1189 // String to use as the DropDown label. 1190 label: Drupal.t('Select an option:') 1191 }; 1192 1193 // Shorthand for config. 1194 var c = config; 1195 1196 // If no config, bail! Can't do anything. 1197 if (!c) { 1198 1199 // No DropDown config, return nothing. 1200 return; 1201 } else { 1202 // Build the DropDown contents and select box and options using config. 1203
1204 // Merge config from args over defaultConfig. 1205 c = $.extend(defaultConfig, c); 1206 1207 // Create select element, add id attr from name in config. 1208 var selectElement = $('<select />', {'id': c.name}); 1209 1210 // Create label element using supplied config options and link label to 1211 // select using 'for' attribute. 1212 var labelElement = $('<label/>', { 1213 text: c.label, 1214 1215 // Optional Class. 1216 class: '', 1217 for: c.name 1218 }); 1219 1220 // General root container for DropDown contents, label, select etc. 1221 var container = $('<div class="d-content"></div>'); 1222 1223 // HTML element template string to wrap around each group. 1224 // NB! Not a jQuery object yet, a string template for one! 1225 // Going to be used to create more than one, so don't want it 1226 // instantiated as jQ obj yet! 1227 var groupWrapper = '<div class="d-group" />'; 1228 1229 // Get the groups to show/hide via select from the configuration. 1230 var groups = c.groups; 1231 1232 // An array of group elements to add to template to be selected by 1233 // select and options. 1234 var groupElements = []; 1235 1236 // An array of group options to add to the template, to be used in the 1237 // select. 1238 var groupOptions = []; 1239 1240 // Filter out bad group data from groups and make uniform. I.e. empty 1241 // elements property, create a uniform array of objects to loop over. 1242 // Returns a new array of uniform groups. 1243 var cleanGroups = $.map(groups, function(g, i) { 1244 1245 // Check format of group. 1246 // See if it us an object w/ elements property. 1247 if (g.elements && $.isEmptyObject(g.elements) !== true) { 1248 return g; 1249 } 1250 1251 // Check it doesn't have any properties and its a valid DOM object. 1252 if (!g.elements && !g.groupName && (g instanceof Element)) { 1253 1254 // Inspect for data attributes. 1255 var data = $(g).data(); 1256 1257 // Check for defaultGroup settings. 1258 var defaulted; 1259 1260 // If sessionStorage.savedGroup has not yet been set, try the data attribute from HTML. 1261 if (!sessionStorage.savedGroup) { 1262 defaulted = data['defaultGroup'] ? data['defaultGroup'] : false; 1263 } else { 1264 1265 // If the sessionStorage.savedGroup is set, then try to match that. 1266 defaulted = slugify(data.name) === sessionStorage.savedGroup; 1267 } 1268 1269 1270 // Turn it into a standard object for better organization. 1271 return { 1272 elements: g, 1273 groupName: data.name ? data.name : null, 1274 default: defaulted 1275 }; 1276 } 1277 }); 1278 1279 // Follow up to check if a default group exists, to be initially 1280 // displayed and option pre-selected. Returns an array that contains 1281 // default group if exists. 1282 var defaultGroup = $.grep(cleanGroups, function(g) { 1283 1284 // Return the group with truthy default property. 1285 return g.default; 1286 }); 1287 1288 // If a default doesn't already exist, assign default to 1st group. 1289 if (defaultGroup.length <= 0) { 1290 cleanGroups[0].default = true; 1291 } 1292 1293 // Loop through each group supplied in config, if it has content, wrap 1294 // and add to the groupElements array. 1295 $.each(cleanGroups, function(i, g) { 1296 1297 // Local variable to hold contents of group for each loop. 1298 var groupContent = g.elements; 1299 1300 // Make a slug of each groupName or if none, a unique key based 1301 // on 'Section ' + index. 1302 var slug = g.groupName ? slugify(g.groupName) : Drupal.t('Section ') + Math.abs(i+1); 1303 1304 // Wrap each group of elements in a groupWrapper element. 1305 var wrapped = $(groupWrapper) 1306 1307 // Add data attributes to it. 1308 .data({ 1309 1310 // Add slug key for identifying which groups are 1311 // selected. 1312 key: slug, 1313 1314 // Add the default flag. 1315 isDefault: g.default 1316 }) 1317 1318 // Add 'active' class for default, 'inactive' if not. Uses CSS 1319 // to style. 1320 .addClass(g.default ? 'active' : 'inactive') 1321 1322 // Add the actual group content to wrapper. 1323 .append(groupContent); 1324 1325 // Push newly wrapped groups to the groupElements array. 1326 groupElements.push(wrapped); 1327 1328 // While looping, also handle options for select. 1329 // Add options to the array used in the select. 1330 var option = $('<option />', { 1331 1332 // Uses the groupName property, or if none, slug 1333 text: Drupal.t(g.groupName) || slug, 1334 value: slug, 1335 1336 // Default is also initially selected using that attr. 1337 selected: g.default 1338 }); 1339 1340 // Push new options to array of options. 1341 groupOptions.push(option); 1342 }); 1343 1344 // Build templates; (append all elements of DropDown to container). 1345 var groupContainerTemplate = $('<div class="groups" />'); 1346 var template = container.append( 1347 1348 // Append label. 1349 labelElement, 1350 1351 // Append select and all its options. Use apply to handle array as arguments. 1352 selectElement.append.apply(selectElement, groupOptions), 1353 1354 // Append all groups to the end of the container in a parent (for CSS). 1355 groupContainerTemplate.append.apply(groupContainerTemplate, groupElements) 1356 1357 ); 1358 1359 // Add entire template after or before insertion point specified in config. 1360 1361 // Check to see if value was added to config, if not, throw a helpful message. 1362 // TODO: review if any of the below conditionals are necessary: 1363 // Providing a default configuration value and not throwing an error, 1364 // but rather a warning (console.warn('warning'), is preferrable to 1365 // throwing but not catching an error. 1366 if(jQuery.isEmptyObject(c.insertAfter) && jQuery.isEmptyObject(c.insertBefore)) { 1367 throw 'You will need to add a jQuery selector value to your config for either "insertAfter" or "insertBefore".'; 1368 } 1369 else if (!jQuery.isEmptyObject(c.insertAfter)){ 1370 // If insertAfter is used position template. 1371 c.insertAfter.after(template); 1372 } 1373 else if (!jQuery.isEmptyObject(c.insertBefore)){ 1374 // If insertBefore is used position template. 1375 c.insertBefore.before(template); 1376 } 1377 1378 1379 // Add interactivity to select. 1380 selectElement.bind('change', function(e) { 1381 1382 // Find the group w/ the label that matches the selection. 1383 var targetGroup = groupElements.find(function(g) { 1384 1385 // Update sessionStorage to 'save' selection, so its retained next page load. 1386 sessionStorage.setItem('savedGroup', e.target.value); 1387 1388 return g.data('key') === e.target.value; 1389 }); 1390 1391 // Find the group that was previously active (has active class). 1392 var oldTargetGroup = groupElements.find(function(g) { 1393 return g.hasClass('active'); 1394 }); 1395 1396 // Remove active class and add inactive to old group. 1397 oldTargetGroup.removeClass('active').addClass('inactive'); 1398 1399 // Remove inactive class and add active to new selected group. 1400 targetGroup.removeClass('inactive').addClass('active'); 1401 1402 }); 1403 1404 // Helper slugify function borrowed from: 1405 // https://gist.github.com/codeguy/6684588#gistcomment-3361909 1406 function slugify(text) { 1407 return text.toString().normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase().trim().replace(/\s+/g, '-').replace(/[^\w-]+/g, '').replace(/--+/g, '-'); 1408 } 1409 } 1410 }; 1411 1412}(jQuery)); 1413;
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.