PageSourceSearch

https://www.mcgill.ca/student-accounts/files/student-accounts/js/j…72DT0WwKU-6NEoa6GyKnaZ2i1vHa6sl5m5W4v0Mg.js

js mcgill.ca collected 2026-09-24 07:14:39 UTC 43,965 bytes, 1,413 lines download raw bytes

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('&hellip;').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.