PageSourceSearch

https://gainesvilleyouthsports.com/javascripts/themes/itasca/theme_16737052261.js

js gainesvilleyouthsports.com collected 2026-10-09 05:01:08 UTC 44,987 bytes, 1,465 lines download raw bytes

1(function($) {
2  $(function() {
3    var mobileBreakpoints = ["1024px", "768px", "640px","480px","360px","270px"];
4
5    function fitElementWidth(fitEl){
6      var elementLength = $(fitEl).length;
7      var elementPercent = 100 / elementLength;
8      var fitElementStyle = '<style>@media only screen and (max-width: '+mobileBreakpoints[1]+') { '+fitEl+'{ width:'+elementPercent+'%; }}</style>';
9      $(fitElementStyle).appendTo('head');
10    }
11    function setElementWidth(setEl){
12      var totalWidth = 0;
13      setEl.each(function() {
14        totalWidth =  $(this).outerWidth() + totalWidth;
15      });
16      setEl.parent().css('width',totalWidth+10);
17    }
18    function clearClassDropdowns(){
19      $('.sport-crumbs-list').removeClass('thisCrumb');
20      $('#sport-crumbs ul li').removeClass('crumbSelected');
21      $('.dropdown-wrapper').removeClass('mdd-active-open');
22    }
23    setElementWidth($('#sport-crumbs .sport-crumbs-btns > ul li'));
24    fitElementWidth('#sport-header .sport-tools li');
25
26    $('#sport-crumbs .sport-crumbs-btns a[data-id]').on( "click", function(e) {
27      thisCrumb = $(this).attr('data-id');
28      thisCrumbId = $('#'+thisCrumb);
29      parentList = $(this).parent();
30      
31      if(thisCrumb != undefined){
32        if(thisCrumbId.hasClass('thisCrumb') == true){
33          clearClassDropdowns();
34        }
35        else{
36          clearClassDropdowns();
37          parentList.addClass('crumbSelected');
38          thisCrumbId.addClass('thisCrumb');
39        }
40        thisCrumbId.mouseleave(function(e) {
41          clearClassDropdowns();
42          e.stopPropagation();
43        });
44      }
45      e.preventDefault();
46      e.stopPropagation();
47    });
48    $('.sport-crumbs-list-close a').on( "click", function(e) {
49      clearClassDropdowns();
50      e.preventDefault();
51      e.stopPropagation();
52    });
53
54    /* Mega Dropdown Season */
55    $('#megaDropDown-season').on( "click", function(e) {
56      nextDiv = $(this).next();
57      parentList = $(this).parent();
58      if(nextDiv.length == true){
59        if(parentList.hasClass('mdd-active-open') == true){
60            parentList.removeClass('mdd-active-open');
61        }
62        else{
63          parentList.addClass('mdd-active-open');
64        }
65        $('.sport-crumbs-list').removeClass('thisCrumb');
66        $('#sport-crumbs ul li').removeClass('crumbSelected');
67      }
68      e.preventDefault();
69      e.stopPropagation();
70    });
71    $('#megaDropDown-season-callout a.close').on( "click", function(e) {
72      $('.dropdown-wrapper').removeClass('mdd-active-open');
73    });
74    /* Roster Player Dropdown */
75    $('#rosterPlayerConnections-button').on( "click", function(e) {
76      $('#rosterPlayerConnections-dropdown').toggle(0, function() {
77        if($(this).is(':visible')) {
78          $('#rosterPlayerConnections-button').addClass('rpc-open');
79        }
80        else{
81          $('#rosterPlayerConnections-button').removeClass('rpc-open');
82        }
83      });
84    });
85    /* make textBlockElement liquid remove this */
86    $j('.textBlockElement .text > table').each( function() {
87         $j(this).wrap('<div class="tableWrapper"></div>');
88    });
89    // 
90    $j(document).on('topnav.loaded', 'body', function(){
91      $('#topNav ul ul ul li a.disabled').parent().addClass('disabled');      
92    });
93    /* Network Bar Select Option */
94    $('#network_sites').change(function() {
95      window.location = $(this).val();
96    });
97    /* Touch detection */
98    if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)){
99      $j("body").addClass("touch-screen")
100    }
101  });
102}(jQuery));
103;(function($) {
104  
105  function hasTouch() {
106    return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
107  }
108  
109  function setTouchScrolling(i, el) {
110    if (el.style) el.style.webkitOverflowScrolling = 'touch';
111  }
112  
113  function trackTouch(e) {
114    var el = $(e.currentTarget);
115    var currentY = e.originalEvent.pageY;
116    var lastY = el.data('scrollGuardY');
117    if (lastY === null) lastY = currentY;
118    el.data('scrollGuardY', currentY);
119    return currentY - lastY;
120  }
121  
122  function untrackTouch(e) {
123    $(e.currentTarget).data('scrollGuardY', null);
124  }
125  
126  $.fn.scrollGuard = function() {
127    var scrollEvents = 'mousewheel';
128    if (hasTouch()) {
129      scrollEvents += ' touchmove';
130      this.each(setTouchScrolling)
131        .on('touchstart', trackTouch)
132        .on('touchend', untrackTouch);
133    }
134    return this
135      .on(scrollEvents, function(e) {
136        var event = e.originalEvent;
137        var el = e.currentTarget;
138        var d = event.wheelDelta || -event.detail || trackTouch(e);
139        var atTop = el.scrollTop === 0;
140        var atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight;
141        if (atTop && atBottom || d < 0 && atBottom || d > 0 && atTop) e.preventDefault();
142      });
143  };
144
145})(jQuery);
146;(function($, _){
147
148  var NAR = NGIN.AdResizer = function(selector, config) {
149    this.$el = $(selector); // this might need to be a direct reference to the ad-wrapper element
150    this.el = this.$el[0];
151    this.bindEvents();
152  }
153
154  _.extend(NAR, {
155    setup: function() {
156      googletag.pubads().addEventListener('slotRenderEnded', function(e) {
157        var adElement = $('#' + e.slot.getSlotElementId());
158        adElement.trigger('adSlotRendered', e);
159      });
160    }
161  });
162
163  _.extend(NAR.prototype, {
164
165    bindEvents: function() {
166      $(window).on('resize', this.resize.bind(this));
167      this.$el.on('adSlotRendered', this.resize.bind(this));
168      _.delay(this.resize.bind(this));
169    },
170
171    resize: function() {
172      this.setSize(1, 0); // reset
173      var scale = Math.min(this.el.clientWidth / this.el.scrollWidth, 1);
174      var marginBottom = this.el.scrollHeight * (scale - 1);
175      this.setSize(scale, marginBottom);
176    },
177
178    setSize: function(scale, marginBottom) {
179      this.$el.children().css('transform', 'scale(' + scale + ')');
180      this.$el.css('marginBottom', marginBottom + 'px');
181    }
182
183  });
184
185  if (typeof googletag == 'object') googletag.cmd.push(NGIN.AdResizer.setup);
186
187})(jQuery, _);
188;(function($, _) {
189
190  if (!NGIN) NGIN = {}
191  if (!NGIN.Modules) NGIN.Modules = {}
192
193  var $win = $(window);
194
195  var NS = NGIN.Modules.NavService = {
196
197    promises: {},
198    modules: {},
199
200    register: function(className, methods) {
201      var initialize = methods.initialize || $.noop;
202      var module = this.modules[className] = function(opts) {
203        this._setup(initialize, opts) // shared setup method
204      }
205      _.extend(module.prototype, this.sharedMethods, methods);
206      delete module.prototype.initialize;
207    },
208
209    create: function(className, opts) {
210      var Module = this.modules[className];
211      return Module && new Module(opts);
212    },
213
214    fixedNav: function(el) {
215      var $nav = $(el);
216      var $els = $nav.add('html');
217      var navTop = $nav.offset().top;
218      var wasFixed = false;
219
220      $(window).scroll(function(){
221        var scrollTop = window.scrollY || window.pageYOffset || 0;
222        var isFixed = navTop < scrollTop;
223        if (!wasFixed && isFixed) {
224          $els.addClass('nav-fixed nav-fixed-start');
225          setTimeout(function(){
226            $els.removeClass('nav-fixed-start');
227          }, 1)
228        }
229        else if (wasFixed && !isFixed) {
230          $els.removeClass('nav-fixed');
231        }
232        wasFixed = isFixed;
233      });
234    },
235
236    sharedMethods: {
237
238      // PUBLIC METHODS
239
240      getNavData: function(url) {
241        var nav = this;
242        return this.preload((this.nav_url = url));
243      },
244
245      preload: function(e) {
246        var url = typeof e === 'string' ? e : $(e.currentTarget).attr('data-nav-url');
247        return NS.promises[url] = NS.promises[url] || $.ajax({
248          dataType: "json",
249          url: url,
250          error: this._preloadError.bind(this, url)
251        });
252      },
253
254      // PRIVATE
255
256      _setup: function(initialize, opts) {
257        this.$html = $('html');
258        this.$el = $(opts.selector).first();
259        this.$navLinks = $($('#navLinksTemplate').html());
260        this.$pageSearch = $($('#pageSearchTemplate').html());
261
262        this.activeClass = opts.activeClass || '';
263        this.collapsedClass = opts.collapsedClass || '';
264        this.navUrl = opts.data.nav_url;
265        this.paywallImage = '<img src="' + opts.paywallImage + '" class="paywall_image">';
266        this.minWidth = opts.minWidth || 0;
267        this.collapseWidth = opts.collapseWidth || 0;
268        this.maxWidth = opts.maxWidth || Infinity;
269        this.currentId = opts.currentId;
270        this.opts = opts;
271
272        this._cache(opts.data);
273        this._bindEvents();
274
275        initialize.call(this, opts);
276
277        // render should come last
278        this.getNavData(this.navUrl)
279          .then(this._render.bind(this));
280      },
281
282      _bindEvents: function() {
283        $win.on('resize', this._resize.bind(this));
284      },
285
286      _render: function(navData, opts) {
287        this.render(navData, opts);
288        this.rendered = this.visible = true;
289        this._resize();
290      },
291
292      _okToShow: function() {
293        return window.innerWidth >= this.minWidth && window.innerWidth <= this.maxWidth;
294      },
295
296      _resize: function() {
297        if (!this.rendered) return;
298
299        // note, you can specify a collapse class in your config OR in the nav module
300        var collapse = this.collapsedClass && window.innerWidth <= this.collapseWidth;
301        var show = this._okToShow();
302
303        // set html class if visible
304        this.$html.toggleClass(this.activeClass, show);
305        if (this.collapsedClass) this.$html.toggleClass(this.collapsedClass, show && collapse);
306
307        // show or hide as needed
308        if (show && !this.visible) {
309          this.visible = true;
310          this.$el.show();
311        } else if (!show && this.visible) {
312          this.visible = false;
313          this.$el.hide();
314        }
315
316        if (this.resize) this.resize();
317      },
318
319      _cache: function(navData) {
320        var dfd = $.Deferred();
321        dfd.resolve(navData);
322        NS.promises[navData.nav_url] = dfd.promise();
323      },
324
325      _preloadError: function(url) {
326        delete NS.promises[url];
327      }
328    }
329
330  }
331
332})(jQuery, _);
333;(function($, _) {
334
335  var NEXT_TICK = 30;
336  var OPEN_DELAY = 200;
337  var CLOSE_DELAY = 500;
338  var OPENING_CLASS = 'dropdown-opening';
339  var OPEN_CLASS = 'dropdown-open';
340  var OPEN_CLASSES = OPEN_CLASS + ' ' + OPENING_CLASS;
341  var DROPDOWN_MARGIN_TOP = 0;
342  var DROPDOWN_MARGIN_BOTTOM = 30;
343  var DROPDOWN_WIDTH = 200;
344  var DROPDOWN_ALIGN_LEFT = 'dropdown-align-left';
345  var DROPDOWN_ALIGN_RIGHT = 'dropdown-align-right';
346  var DROPDOWN_ALIGN_CLASSES = DROPDOWN_ALIGN_LEFT + ' ' + DROPDOWN_ALIGN_RIGHT;
347
348  function realOuterWidth(el) {
349    if (!el || !el.style) return 0;
350    var s = window.getComputedStyle(el);
351    return parseFloat(s.width) + parseFloat(s.paddingRight) + parseFloat(s.paddingLeft) + parseFloat(s.borderRightWidth) + parseFloat(s.borderLeftWidth);
352  }
353
354  NGIN.Modules.NavService.register('DropdownNav', {
355
356    initialize: function(opts) {
357      this.openDelay = opts.openDelay || OPEN_DELAY;
358      this.closeDelay = opts.closeDelay || CLOSE_DELAY;
359      this.$navItems = $('<ul class="theme-nav theme-nav-style-dropdown" data-nav-level="1" />').appendTo(this.$el);
360      this.$moreButton = $('<li class="theme-nav-item theme-nav-more hasChild"><a class="theme-nav-link">More</a></li>');
361      this.$moreMenu = $('<ul class="theme-nav-dropdown" data-nav-level="2" />').width(DROPDOWN_WIDTH).scrollGuard();
362      this.$moreButton.data('dropdown', this.$moreMenu);
363      this.$moreMenu.data('navItem', this.$moreButton);
364      this.bindEvents();
365    },
366
367    bindEvents: function() {
368      $(window).one('load', this.checkOverflowAfterLoad.bind(this));
369      $(document).on('scroll', this.closeAllDropdowns.bind(this));
370      this.$el
371        .on('mouseenter', '.theme-nav-dropdown', this.dropdownOver.bind(this))
372        .on('mouseleave', '.theme-nav-dropdown', this.dropdownOut.bind(this))
373        .on('mouseenter', '.theme-nav-item', this.itemOver.bind(this))
374        .on('mouseleave', '.theme-nav-item', this.itemOut.bind(this))
375    },
376
377    itemOver: function(e) {
378      var $navItem = $(e.currentTarget);
379      $navItem.data('over', true);
380      if ($navItem.hasClass('hasChild')) this.showDropdown($navItem);
381      this.keepDropdownOpen($navItem.data('parentDropdown'));
382    },
383
384    itemOut: function(e) {
385      var $navItem = $(e.currentTarget);
386      var $dropdown = $navItem.data('dropdown');
387      $navItem.data('over', false);
388      this.allowDropdownToClose($dropdown);
389    },
390
391    dropdownOver: function(e) {
392      var $dropdown = $(e.currentTarget);
393      $dropdown.data('over', true);
394      this.keepDropdownOpen($dropdown);
395    },
396
397    dropdownOut: function(e) {
398      var $dropdown = $(e.currentTarget);
399      $dropdown.data('over', false);
400      this.allowDropdownToClose($dropdown);
401    },
402
403    closeAllDropdowns: function() {
404      this.closeItems(this.$navItems.children());
405    },
406
407    render: function(navData) {
408      var showChildren = this.opts.showChildren && navData.children.length;
409      var items = showChildren ? navData.children : navData.siblings;
410      var renderOpts = {
411        items: items,
412        level: 1
413      }
414
415      if (this.opts.showTitle) {
416        var titleObj = showChildren ? navData : navData.parent;
417        renderOpts.title = titleObj.name;
418        renderOpts.titleLink = titleObj.url;
419      }
420
421      this.renderNavItems(this.$navItems, renderOpts);
422      this.$el.append(this.$moreMenu);
423      this.handleOverflow();
424    },
425
426    resize: function() {
427      // need to only run this when showing the first time???
428      this.resetOverflow();
429    },
430
431    showDropdown: function($navItem) {
432      var nav = this;
433      var $dropdown = this.renderDropdown($navItem);
434      var $itemAndDropdown = $navItem.add($dropdown);
435      var openDelay = $dropdown.is(':visible') ? 0 : this.openDelay;
436
437      // keep this drop open
438      clearTimeout($dropdown.data('openTimeout'));
439      clearTimeout($dropdown.data('closeTimeout'));
440
441      this.positionDropdown($dropdown);
442
443      function startOpening() {
444        if (!$navItem.data('over') && !$dropdown.data('over')) return;
445        nav.closeItems($navItem.siblings());
446        $itemAndDropdown.addClass(OPENING_CLASS);
447        $dropdown.data('openTimeout', setTimeout(finishOpening, NEXT_TICK));
448      }
449
450      function finishOpening() {
451        if ($navItem.hasClass(OPENING_CLASS)) {
452          $itemAndDropdown
453            .addClass(OPEN_CLASS)
454            .removeClass(OPENING_CLASS);
455        }
456      }
457
458      $dropdown.data('openTimeout', setTimeout(startOpening, openDelay));
459    },
460
461    keepDropdownOpen: function($dropdown) {
462      if (!$dropdown) return
463      $dropdown.data('over', true);
464      clearTimeout($dropdown.data('closeTimeout'));
465      var $parentItem = $dropdown.data('navItem');
466      if ($parentItem) this.keepDropdownOpen($parentItem.data('parentDropdown'));
467    },
468
469    allowDropdownToClose: function($dropdown) {
470      if (!$dropdown) return
471      $dropdown.data('over', false);
472      $dropdown.data('closeTimeout', setTimeout(this.closeDropdown.bind(this, $dropdown, true), this.closeDelay));
473      var $parentItem = $dropdown.data('navItem');
474      if ($parentItem) this.allowDropdownToClose($parentItem.data('parentDropdown'));
475    },
476
477    closeItems: function($items) {
478      var nav = this;
479      $items.each(function(i, item) {
480        nav.closeDropdown($(item).data('dropdown'));
481      });
482    },
483
484    closeDropdown: function($dropdown, bubble) {
485      if (!$dropdown) return
486      var $navItem = $dropdown.data('navItem');
487      if ($navItem.data('over') || $dropdown.data('over')) return
488      $navItem.removeClass(OPEN_CLASSES);
489      this.closeItems($dropdown.removeClass(OPEN_CLASSES).children());
490      if (bubble) this.closeDropdown($navItem.data('parentDropdown'));
491    },
492
493    positionDropdown: function($dropdown) {
494      // start by resetiing the dropdown state
495      $dropdown
496        .css('maxHeight', '')
497        .removeClass(DROPDOWN_ALIGN_CLASSES);
498
499      var $navItem = $dropdown.data('navItem');
500      var $parentDropdown = $navItem.data('parentDropdown');
501      var renderBelow = $dropdown.data('nav-level') < 3;
502      var navOffset = this.$el.offset();
503      var navItemsOffset = this.$navItems.offset();
504      var itemOffset = $navItem.offset();
505      var windowHeight = $(window).height();
506      var windowScroll = $(window).scrollTop();
507      var maxX = this.$navItems.outerWidth() + navItemsOffset.left - navOffset.left;
508      var navHeight = this.$navItems.outerHeight();
509      var navItemWidth = $navItem.outerWidth();
510      var width = navItemWidth > DROPDOWN_WIDTH ? navItemWidth : DROPDOWN_WIDTH;
511      var height = $dropdown.outerHeight();
512      var minTop = navHeight;
513      var fixedBottom = navOffset.top + navHeight - windowScroll;
514      var maxHeight = windowHeight - fixedBottom - DROPDOWN_MARGIN_BOTTOM;
515
516      // allow second level menues to shift down
517      if (!renderBelow) minTop += DROPDOWN_MARGIN_TOP;
518
519      var maxBottom = minTop + maxHeight;
520      var left;
521      var top;
522      var alignmentClass = DROPDOWN_ALIGN_LEFT;
523
524      // first level drop downs
525      if (renderBelow) {
526        top = minTop;
527        left = itemOffset.left - navOffset.left;
528
529        if (left + width > maxX) {
530          alignmentClass = DROPDOWN_ALIGN_RIGHT;
531          left = left - width + navItemWidth;
532        }
533      }
534
535      // second level dropdowns
536      else {
537        var widthOffset = $parentDropdown ? $parentDropdown.outerWidth() : navItemWidth;
538        top = Math.max(itemOffset.top - navOffset.top, minTop);
539        left = itemOffset.left - navOffset.left + widthOffset;
540
541        if (left + width > maxX) {
542          alignmentClass = DROPDOWN_ALIGN_RIGHT;
543          left = left - width - navItemWidth;
544        }
545      }
546
547      // Shift menu up if is scrolls and there is space to do so
548      if (top + height > maxBottom) {
549        top = Math.max(maxBottom - height, minTop);
550      }
551
552      // update dropdown state
553      $dropdown
554        .addClass(alignmentClass)
555        .css({
556          top: top,
557          left: left,
558          maxHeight: maxHeight,
559          width: width,
560          overflow: 'auto'
561        });
562    },
563
564    showNextLevel: function(level) {
565      var checkDepth = 'maxDepth' in this.opts && level;
566      return checkDepth && level <= this.opts.maxDepth || false;
567    },
568
569    renderDropdown: function($navItem) {
570      var $dropdown = $navItem.data('dropdown');
571      if ($dropdown) return $dropdown;
572
573      var nav = this;
574      var navUrl = $navItem.data('nav-url');
575      var level = ($navItem.parent().data('nav-level') || 1) + 1
576
577      $dropdown = $('<ul class="loading theme-nav-dropdown" data-nav-level="' + level + '" />');
578      $dropdown.width(DROPDOWN_WIDTH);
579      $dropdown.scrollGuard(); // prevents overflow scrolling
580      $navItem.data('dropdown', $dropdown);
581      $dropdown.data('navItem', $navItem);
582
583      function renderItems(navData) {
584        nav.renderNavItems($dropdown, {
585          items: navData.children,
586          level: level
587        });
588        var items = $dropdown.children();
589        items.data('parentItem', $navItem);
590        items.data('parentDropdown', $dropdown);
591        // add scroll binding to hide submenus
592        $dropdown.on('scroll', nav.closeItems.bind(nav, $dropdown.children()));
593      }
594
595      nav.getNavData(navUrl).then(renderItems);
596      nav.positionDropdown($dropdown);
597
598      return $dropdown.appendTo(this.$el);
599    },
600
601    renderNavItems: function($container, opts) {
602      var nav = this;
603      $container.removeClass('loading');
604
605      if (opts.title) {
606        $container.append('<li class="theme-nav-title"><a href="' + opts.titleLink + '">' + opts.title + '</a></li>');
607      }
608
609      _.each(opts.items, function(navItem){
610        var classes = [].concat(navItem.css_classes||[]);
611        var $navItem = $('<li class="theme-nav-item" />');
612
613        if (navItem.id === nav.currentId) classes.push('selected');
614        if (navItem.display_status) classes.push(navItem.display_status);
615
616        if (nav.showNextLevel(opts.level)) {
617          if (navItem.has_child) classes.push('hasChild');
618          if (navItem.nav_url) $navItem.attr('data-nav-url', navItem.nav_url);
619        }
620
621        $navItem.addClass(classes.join(' '));
622
623        var $link = $('<a class="theme-nav-link" />')
624          .appendTo($navItem)
625          .text(navItem.name)
626          .attr({
627            href: navItem.url,
628            title: navItem.title,
629            target: navItem.target || '_self'
630          });
631
632        if (navItem.display_status === 'paywalled' && nav.paywallImage) {
633          $link.prepend(nav.paywallImage + ' ');
634        }
635
636        $container.append($navItem);
637      })
638    },
639
640    checkOverflowAfterLoad: function() {
641      var overflowWidths = this.overflowWidths || [];
642      var firstItemWidth = this.$navItems.children().first().outerWidth();
643      this.resetOverflow();
644    },
645
646    resetOverflow: function() {
647      delete this.overflowWidths;
648      this.$moreButton.detach();
649      this.$navItems.append(this.$moreMenu.children());
650      this.handleOverflow();
651    },
652
653    handleOverflow: function() {
654      var navItems = this.$navItems;
655      var moreMenu = this.$moreMenu;
656      var moreButton = this.$moreButton;
657      var visibleItems = navItems.children().not(moreButton);
658      var oldExtraItems = moreMenu.children();
659      var allItems = visibleItems.add(oldExtraItems);
660
661      var overflowWidths = this.getOverflowWidths();
662      var moreButtonWidth = this.moreButtonWidth;
663      var maxWidth = this.$navItems.width();
664      var totalWidth = 0;
665      var hasExtraItems = false;
666      var index = 0;
667
668      // fit as many items in as possible
669      while (index < overflowWidths.length) {
670        var itemWidth = overflowWidths[index];
671        if (totalWidth + itemWidth > maxWidth) {
672          hasExtraItems = true;
673          break;
674        }
675        totalWidth += itemWidth;
676        index++;
677      }
678
679      // backtrack if more button won't fit
680      while (hasExtraItems && totalWidth + moreButtonWidth > maxWidth) {
681        index--;
682        totalWidth -= overflowWidths[index];
683      }
684
685      var newExtraItems = hasExtraItems && allItems.slice(index).not(oldExtraItems);
686      var newVisibleItems = hasExtraItems && oldExtraItems.not(newExtraItems);
687
688      function changeNavLevel(delta) {
689        return function(i, navItem) {
690          navItem = $(navItem);
691          navItem.data('nav-level', delta + navItem.data('nav-level'));
692        }
693      }
694
695      function setParents(parentItem, parentDropdown) {
696        return function(i, navItem) {
697          navItem = $(navItem);
698          navItem.data('parentItem', parentItem);
699          navItem.data('parentDropdown', parentDropdown);
700        }
701      }
702
703      if (newVisibleItems) {
704        newVisibleItems.each(changeNavLevel(-1))
705        moreButton.before(newVisibleItems);
706        newVisibleItems.each(setParents(null, null))
707      }
708
709      if (newExtraItems) {
710        newExtraItems.each(changeNavLevel(1))
711        moreMenu.prepend(newExtraItems);
712        newExtraItems.each(setParents(moreButton, moreMenu))
713      }
714
715      if (!hasExtraItems) moreButton.detach();
716    },
717
718    getOverflowWidths: function() {
719      if (this.overflowWidths) return this.overflowWidths;
720
721      var overflowWidths = this.overflowWidths = [];
722      var navItems = this.$navItems.children();
723      var moreButton = this.$moreButton.appendTo(this.$navItems);
724      this.moreButtonWidth = Math.ceil(realOuterWidth(moreButton.get(0)));
725
726      navItems.each(function(i, item) {
727        overflowWidths[i] = Math.ceil(realOuterWidth(item));
728      });
729
730      return overflowWidths;
731    }
732
733  });
734
735})(jQuery, _);
736;(function($) {
737
738  var DEFAULT_DURATION = 300;
739
740  NGIN.Modules.NavService.register('SlideNav', {
741
742    initialize: function(opts) {
743      // Allow custom collapsed classes as well as the required one (for css integration)
744      this.collapsedClass = 'slideNavCollapsed ' + (this.collapsedClass || '');
745      this.duration = opts.duration || DEFAULT_DURATION;
746      this.collapseWidth = opts.collapseWidth || 0;
747      this.listCount = 0;
748      this.currentNavData = opts.data;
749      this.$html = $('html');
750      this.$nav = $('<div class="theme-nav theme-nav-style-slide" />');
751      this.$toggle = $('<span class="theme-slide-nav-toggle" />').appendTo(this.$nav);
752      this.$title = $('<div class="theme-nav-title">').appendTo(this.$nav);
753      this.$menus = $('<div class="theme-nav-menus">').appendTo(this.$nav).scrollGuard();
754      this.$overlay = $('<div class="theme-slide-nav-overlay" />').appendTo(this.$nav);
755
756      this.appendContent();
757      this.bindEvents();
758
759      this.$el.trigger('SlideNavInitialize', { navElement: this.$nav });
760    },
761
762    // Injects any pre-rendered content into the nav element in the correct location
763    appendContent: function() {
764      var content = this.$el.children().detach();
765      if (content.length) $('<div class="theme-slide-nav-content" />')
766        .append(content)
767        .insertBefore(this.$title);
768    },
769
770    bindEvents: function() {
771      this.$nav.on('hover', '[data-nav-url]', this.preload.bind(this));
772      this.$menus.on('click', '[data-nav-url]', this.loadChildNav.bind(this));
773      this.$title.on('click', '[data-nav-url]', this.loadParentNav.bind(this));
774      this.$toggle.add(this.$overlay).on('click', this.toggleMenu.bind(this));
775
776      // unbind mclose -- Removes old ngin JS menu close handler
777      $(document).unbind('click', mclose());
778    },
779
780    toggleMenu: function(e) {
781      var open = !this.$html.hasClass('slide-nav-open');
782      this.$html.toggleClass('slide-nav-open', open);
783      this.$el.trigger(open ? 'SlideNavOpen' : 'SlideNavClose', { navElement: this.$nav });
784      this.scrollToTop();
785    },
786
787    scrollToTop: function() {
788      // Doing this in an interval to fix an issue where iOS would NOT end up at
789      // the top when closing the menu. Maybe something to do with CSS animation?
790      var scrollInterval = setInterval(function(){
791        window.scrollTo(0, 0);
792      }, 20);
793      setTimeout(function(){
794        clearInterval(scrollInterval);
795      }, 300);
796    },
797
798    resize: function() {
799      this.$html.not('.slideNavCollapsed').removeClass('slide-nav-open');
800    },
801
802    renderParentContent: function(data) {
803      var topLevel = data.parent_id === null && data.node_type === null;
804
805      if (topLevel) {
806        this.$title.html(
807          '<span class="theme-nav-title-text">Main Menu</span>'
808        );
809      } else {
810        this.$title.html(
811          '<span class="theme-nav-back" data-nav-url="' + data.nav_url + '"></span>' +
812          '<a href="' + data.url + '" class="theme-nav-title-text">' + data.name + '</a>'
813        );
814      }
815    },
816
817    render: function(navData, opts) {
818      if (!this.$nav.parent(this.$el).length) this.$el.append(this.$nav);
819      var navDir = 'up';
820
821      // navDir bound as first arg when rendering after navigation
822      if (typeof navData === 'string') {
823        navDir = navData;
824        navData = opts;
825      }
826
827      var renderParent = navDir !== 'down';
828      var newMenu = $('<ul class="theme-nav-menu" />');
829      var listData = renderParent ? navData.siblings : navData.children;
830      var parentData = renderParent ? navData.parent : navData;
831
832      // Build Lists
833      for (var i = 0; i < listData.length; i++) {
834        var navItem = $('<li class="theme-nav-item"/>')
835        var data = listData[i];
836        var linkContent = data.name;
837
838        if (data.display_status == "paywalled") linkContent = this.paywallImage + ' ' + linkContent
839
840        var $link = $('<a class="theme-nav-link"/>')
841          .html(linkContent)
842          .attr({
843             href: data.url,
844             title: data.title,
845             id: data.id,
846             target: data.target || '_self'
847          });
848        var forwardArrow = '';
849
850        if (data.display_status) navItem.addClass(data.display_status);
851        if (data.id === this.currentId) navItem.addClass('selected');
852        if (data.has_child) {
853          navItem.addClass('hasChild');
854          forwardArrow = $('<span class="theme-nav-forward ' + data.id + '"></span>');
855          if (data.nav_url) forwardArrow.attr('data-nav-url', data.nav_url);
856        }
857
858        navItem
859          .append($link, forwardArrow)
860          .appendTo(newMenu)
861      }
862
863      // render/preload parent data if needed (do this after preloading
864      // children since child pages are more likely to be needed first).
865      this.renderParentContent(parentData);
866
867      // Place List Content
868      this.$menus.append(newMenu);
869
870      // Sets the first menu as current (will animate to next menu if needed)
871      this.$menus.children().first();
872
873      // Animate if we have rendered more than one list
874      if (this.listCount++) {
875        if (renderParent) this.transitionToParent();
876        else this.transitionToChildren();
877      }
878    },
879
880    transitionToParent: function() {
881      var menus = this.$menus.children();
882      var newMenu = menus.last().addClass('theme-nav-parent-in');
883      var oldMenu = menus.first().addClass('theme-nav-children-out');
884      newMenu.insertBefore(oldMenu);
885      setTimeout(function() {
886        newMenu.removeClass('theme-nav-parent-in');
887      }, 50);
888      setTimeout(function() {
889        oldMenu.remove();
890      }, this.duration);
891    },
892
893    transitionToChildren: function() {
894      var menus = this.$menus.children();
895      var newMenu = menus.last().addClass('theme-nav-children-in');
896      var oldMenu = menus.first().addClass('theme-nav-parent-out');
897      setTimeout(function() {
898        newMenu.removeClass('theme-nav-children-in');
899      }, 50);
900      setTimeout(function() {
901        oldMenu.remove();
902      }, this.duration);
903    },
904
905    loadParentNav: function(e) {
906      this.getNavData($(e.currentTarget).attr('data-nav-url'))
907        .then(this.render.bind(this, 'up'));
908    },
909
910    loadChildNav: function(e) {
911      this.getNavData($(e.currentTarget).attr('data-nav-url'))
912        .then(this.render.bind(this, 'down'));
913    }
914
915  });
916
917})(jQuery);
918;(function($, _){
919
920  // Requires teh $.fn.scrollGuard module.
921
922  var COMPACT_CLASS = 'compact-style';
923
924  var NNB = NGIN.NetworkNavBar = function(selector, config) {
925    this.dropDownStyle = config.displayStyle === 'drop_down_menu'
926    this.$el = $(selector);
927    this.el = this.$el[0];
928    this.$title = this.$el.find('.theme-network-bar-title');
929    this.$sites = this.$el.find('.theme-network-bar-sites').scrollGuard();
930    this.$body = $('body');
931    this.$doc = $(document);
932    this.bindEvents();
933  }
934
935  _.extend(NNB.prototype, {
936
937    bindEvents: function() {
938      $(window).on('resize', this.resize.bind(this));
939      _.delay(this.resize.bind(this));
940    },
941
942    renderDropDown: function() {
943      this.$el.addClass(COMPACT_CLASS);
944      this.$sites.detach();
945      this.tip = NGIN.Modules.Tip(this.$sites, {
946        style: 'theme-network-bar-dropdown',
947        width: 'target',
948        target: this.$title,
949        target_anchor: 'bl',
950        tip_anchor: 'tl',
951        show_on: ['.theme-network-bar-title', 'click'],
952        hide_on: [['.theme-network-bar-title', 'click'], [document, 'click'], [window, 'scroll']],
953      });
954    },
955
956    renderIconList: function() {
957      this.$sites.detach();
958      if (this.tip) this.tip.destroy();
959      this.$sites.insertAfter(this.$title).show();
960      this.$el.removeClass(COMPACT_CLASS);
961    },
962
963    resize: function() {
964      this.renderIconList();
965      var dropDown = this.dropDownStyle || this.el.scrollWidth > this.el.offsetWidth;
966      if (dropDown) this.renderDropDown();
967    }
968
969  });
970
971})(jQuery, _);
972;(function($, _){
973
974  var EXPANDED_CLASS = 'network-nav-expanded';
975
976  var NNM = NGIN.NetworkNavMenu = function(config) {
977    this.$title = $(config.title);
978    this.$sites = $(config.sites);
979    this.$mobileNav = this.$title.closest('.theme-mobile-nav');
980
981    this.$sites.find('.theme-nav-menus').scrollGuard();
982    this.$sites.detach();
983
984    this.bindEvents();
985  }
986
987  _.extend(NNM.prototype, {
988
989    bindEvents: function() {
990      this.$title.on('click', '.theme-nav-back', this.transitionToNetwork.bind(this));
991      this.$sites.on('click', '.theme-nav-forward', this.transitionToSite.bind(this));
992      this.$mobileNav.on('SlideNavInitialize', this.moveSites.bind(this));
993      this.$mobileNav.on('SlideNavClose', _.debounce(this.transitionToSite.bind(this), 200));
994    },
995
996    // do this only after the slide nav inits since this gets initialized before the slide nav module
997    moveSites: function(event, eventData) {
998      this.$sites.appendTo(eventData.navElement);
999    },
1000
1001    transitionToSite: function() {
1002      this.$mobileNav.removeClass(EXPANDED_CLASS);
1003    },
1004
1005    transitionToNetwork: function() {
1006      this.$mobileNav.addClass(EXPANDED_CLASS);
1007    }
1008
1009  });
1010
1011})(jQuery, _);
1012;(function($, _) {
1013
1014  var $html = $('html');
1015  var htmlClass = 'page-manager-visible';
1016
1017  var PM = NGIN.Modules.PageManager = function(opts) {
1018    this.config = PM.CONFIG || {}
1019    this.opts = opts || {};
1020    this.$pageManager = $('.sn-pageManager');
1021    this.$pageManagerToggle = this.$pageManager.find('.sn-togglePages');
1022    this.$pageManagerContent = $('.sn-structure.sn-sidebarItem');
1023    this.$tabs = $('.sn-nav.sn-nav-tabs li');
1024    this.$tabContent = $('.sn-tabContainer');
1025    this.$pageLinks = $('.sn-pages li a').not('.cmsNavButton a');
1026    this.$editModeDisabled = $('.sn-edit-mode-disabled');
1027    this.$editModeDisabledTip = this.$editModeDisabled.find('.sn-tooltip');
1028    this.bindEvents();
1029  }
1030
1031  PM.config = function(config) {
1032    PM.CONFIG = config;
1033  }
1034
1035  // Use jQuery UI sortable interaction to handle page manager's fixed position.
1036  // Use the scriptaculous style sortable callbacks for backward compatability.
1037  PM.sortable = function(elementId, pageId) {
1038    var $el = $('#' + elementId);
1039    var childSelector = '.drag';
1040    var url = '/page/order/' + pageId;
1041    var csrfToken = $('meta[name="csrf-token"]').attr('content');
1042
1043    function updateOrder(event, ui) {
1044      var params = { authenticity_token: csrfToken };
1045
1046      params[elementId + '[]'] = _.map($el.children(childSelector), function(child) {
1047        return (child.id.match(Sortable.SERIALIZE_RULE) || [])[1];
1048      });
1049
1050      new Ajax.Request(url, {
1051        asynchronous: true,
1052        evalScripts: true,
1053        parameters: params
1054      });
1055    }
1056
1057    $el.sortable({
1058      axis: 'y',
1059      items: childSelector,
1060      update: updateOrder
1061    });
1062  }
1063
1064  PM.prototype.bindEvents = function() {
1065    this.$pageManagerToggle
1066      .on('click', this.toggle.bind(this));
1067    this.$editModeDisabled
1068      .on('mouseover', this.showEditModeDisabledTip.bind(this))
1069      .on('mouseout', this.hideEditModeDisabledTip.bind(this));
1070    this.$tabs
1071      .on('click', this.showTab.bind(this));
1072
1073    // prevent click of links in pages & navigation tab
1074    this.$pageLinks.each(function() {
1075      $(this).replaceWith($('<p>' + this.innerHTML + '</p>'));
1076    });
1077
1078    // decide to show or not based on window size
1079    if (this.config.minWidth) {
1080      this.resize()
1081      $(window).on('resize', this.resize.bind(this));
1082    }
1083  }
1084
1085  PM.prototype.toggle = function() {
1086    var content = this.$pageManagerContent;
1087    var activate = !content.hasClass('active');
1088    content.toggleClass('active', activate);
1089    if (activate) this.resetTabs();
1090  }
1091
1092  PM.prototype.resize = function() {
1093    var visible = $html.hasClass(htmlClass);
1094    var hide = window.innerWidth < this.config.minWidth;
1095    if (hide && visible) $html.removeClass(htmlClass);
1096    else if (!hide && !visible) $html.addClass(htmlClass);
1097  }
1098
1099  PM.prototype.showTab = function(e) {
1100    this.$tabs.removeClass('active');
1101    this.$tabContent.hide();
1102    $(e.currentTarget)
1103      .addClass('active')
1104      .find('.sn-tabContainer').show();
1105  }
1106
1107  PM.prototype.resetTabs = function() {
1108    this.$tabs.removeClass('active').first().addClass('active');
1109    this.$tabContent.hide().first().show();
1110  }
1111
1112  PM.prototype.showEditModeDisabledTip = function() {
1113    this.$editModeDisabledTip.stop().fadeIn();
1114  }
1115
1116  PM.prototype.hideEditModeDisabledTip = function() {
1117    var tip = this.$editModeDisabledTip
1118    tip.stop().fadeOut(200, function() {
1119      tip.css({ opacity : "1" });
1120    });
1121  }
1122
1123})(jQuery, _);
1124;(function($) {
1125
1126  NGIN.Modules.GamesSlider = {
1127    init: function() {
1128      return new GamesSlider();
1129    }
1130  };
1131
1132  function GamesSlider() {
1133    this.$slider = $('.games-slider');
1134    this.$selected = this.$slider.find('.selected');
1135    this.scrollToSelected();
1136  }
1137
1138  // Set scrollLeft of slider so that selected game is visible
1139  GamesSlider.prototype.scrollToSelected = function() {
1140    if (this.$selected.length) {
1141      var scrollLeft = this.$slider.scrollLeft();
1142
1143      var left = Math.round(
1144        this.$selected.offset().left
1145        - this.$slider.offset().left
1146      );
1147      var leftWidth = left + this.$selected.outerWidth();
1148
1149      // Selected game is off to left
1150      if (left < 0) {
1151        scrollLeft += left;
1152      }
1153      // Selected game is off to right
1154      else if (leftWidth > this.$slider.outerWidth()) {
1155        scrollLeft += leftWidth - this.$slider.outerWidth();
1156      }
1157
1158      if (scrollLeft) this.$slider.scrollLeft(scrollLeft);
1159    }
1160  }
1161
1162})(jQuery);
1163;(function($, _){
1164
1165  // SHARED VARIABLES
1166
1167  var searchModal;
1168  var searchTerm;
1169  var loadSearchData;
1170
1171  // Kyes that don't change the search value are ignored and include:
1172  // shift, ctrl, alt/opt, cmd, win/cmd, left, up, right, down
1173  var ignoredKeys = [16, 17, 18, 91, 93, 37, 38, 39, 40];
1174
1175  // Some events run on click or certain key press. This check
1176  // will pass if the event was not a key event, or if the key event
1177  // matches the given key code.
1178  function allowKeys(e, code) {
1179    if (e.type.indexOf('key') === -1) return true;
1180    return e.which === code;
1181  }
1182
1183  function ignoredKeyEvent(e) {
1184    return e && e.type.indexOf('key') !== -1 && ignoredKeys.indexOf(e.which) !== -1;
1185  }
1186
1187  // PAGE SEARCH BOX (one or more per page)
1188
1189  var PSB = NGIN.PageSearchBox = function(selector, config) {
1190    this.$el = $(selector);
1191    this.$input = this.$el.find(config.inputElement);
1192    this.$submit = this.$el.find(config.submitElement);
1193    this.$clear = this.$el.find(config.clearElement).hide();
1194    this.bindEvents();
1195  }
1196
1197  _.extend(PSB.prototype, {
1198
1199    bindEvents: function() {
1200      var self = this;
1201      this.$input
1202        .on('focus', this.load.bind(this))
1203        .on('keyup', this.update.bind(this))
1204        .on('keydown', this.submit.bind(this));
1205      this.$submit
1206        .on('click', function(event) {
1207          self.load();
1208          self.submit(event);
1209        });
1210      this.$clear
1211        .on('click', this.clear.bind(this));
1212    },
1213
1214    val: function(query) {
1215      var setting = !!arguments.length;
1216      if (setting) {
1217        this.$clear.toggle(!!query);
1218        return this.$input.val(query);
1219      } else {
1220        return this.$input.val();
1221      }
1222    },
1223
1224    update: function(e) {
1225      if (searchModal && !ignoredKeyEvent(e)) {
1226        var val = this.$input.val();
1227        var empty = !val.trim();
1228        this.$clear.toggle(!empty);
1229        searchModal.update(val);
1230      }
1231    },
1232
1233    submit: function(e) {
1234      if (!searchModal || !allowKeys(e, 13)) return
1235
1236      this.update();
1237      searchModal.show();
1238      e.preventDefault();
1239    },
1240
1241    clear: function(e) {
1242      e.preventDefault();
1243      this.$input.val('');
1244      this.update();
1245    },
1246
1247    load: function() {
1248      if (!loadSearchData) {
1249        loadSearchData = $.ajax('/navigation/page_search')
1250      }
1251    }
1252
1253  });
1254
1255  // PAGE SEARCH MODAL (only one per page)
1256
1257  var PSM = NGIN.PageSearchModal = function(selector, config) {
1258    if (searchModal) return searchModal;
1259
1260    searchModal = this;
1261
1262    this.$el = $(selector).appendTo('body');
1263    this.$results = this.$el.find(config.resultsElement).scrollGuard();
1264    this.$status = this.$el.find(config.statusElement);
1265    this.$spinner = this.$el.find(config.spinnerElement);
1266    this.$close = this.$el.find(config.closeElement);
1267
1268    this.bindEvents(config);
1269
1270    this.searchBox = new PSB(this.$el, {
1271      inputElement: config.inputElement,
1272      clearElement: config.clearElement
1273    });
1274  }
1275
1276  _.extend(PSM.prototype, {
1277
1278    sortBy: 'page_title',
1279
1280    resultTmpl: _.template(
1281      '<div class="theme-search-result" data-url="/page/nav_links_redirect/<%= page_node_id %>">' +
1282        '<div class="theme-search-result-info">' +
1283          '<span class="theme-search-result-primary"><%= page_title %></span>' +
1284          '<span class="theme-search-result-secondary"><%= parent_button_title %></span>' +
1285        '</div>' +
1286      '</div>'
1287    ),
1288
1289    bindEvents: function(config) {
1290      this.$el.on('click', this.clickToHide.bind(this));
1291      this.$close.on('click', this.clickToHide.bind(this));
1292      this.$el.on('click', '[data-url]', this.navigate.bind(this));
1293      $(document).on('keydown', this.hide.bind(this));
1294    },
1295
1296    show: function() {
1297      this.visible = true;
1298      this.render();
1299      this.$el.fadeIn();
1300      this.searchBox.$input.focus();
1301    },
1302
1303    clickToHide: function(e) {
1304      if (e.target === e.currentTarget) this.hide(e);
1305    },
1306
1307    navigate: function(e) {
1308      var url = $(e.currentTarget).data('url');
1309      if (url) $.get(url);
1310    },
1311
1312    hide: function(e) {
1313      if (!allowKeys(e, 27)) return;
1314      this.visible = false;
1315      this.$el.fadeOut();
1316    },
1317
1318    update: function(query) {
1319      this.query = query;
1320      this.searchBox.val(query);
1321      if (this.visible) this.render();
1322    },
1323
1324    filteredResults: function(data) {
1325      var query = this.query && this.query.trim();
1326      if (!query) return data;
1327
1328      var sortBy = this.sortBy;
1329      var queryRx = new RegExp(query, 'i');
1330
1331      return _.filter(data, function(item) {
1332        return queryRx.test(item[sortBy]);
1333      });
1334    },
1335
1336    setStatus: function(matches, total) {
1337      var statusText = matches ? 'Showing ' + matches + ' of ' + total : 'No results.';
1338      this.$status.text(statusText);
1339    },
1340
1341    render: function() {
1342      var search = this;
1343      if (search.waitingToRender) return;
1344
1345      search.$results.empty().append(search.$spinner);
1346      search.waitingToRender = true;
1347
1348      loadSearchData.then(function(data) {
1349        var results = data.result || [];
1350        var filteredResults = search.filteredResults(results);
1351        var markup = _.map(filteredResults, search.resultTmpl).join('');
1352
1353        search.waitingToRender = false;
1354        search.setStatus(filteredResults.length, results.length);
1355        search.$results
1356          .empty()
1357          .append(this.$status)
1358          .append(markup);
1359      })
1360    }
1361
1362  });
1363
1364})(jQuery, _);
1365;(function($, _){
1366
1367  var searchModal
1368
1369  // SEARCH BOX
1370
1371  var GSB = NGIN.GoogleSearchBox = function(selector, config) {
1372    this.$el = $(selector);
1373    this.$input = this.$el.find(config.inputElement);
1374    this.$submit = this.$el.find(config.submitElement);
1375    this.$clear = this.$el.find(config.clearElement).hide();
1376    this.bindEvents();
1377  }
1378
1379  _.extend(GSB.prototype, {
1380
1381    bindEvents: function() {
1382      this.$input
1383        .on('keydown', this.submitIfEnter.bind(this))
1384        .on('keyup', this.update.bind(this));
1385      this.$submit.on('click', this.submit.bind(this));
1386      this.$clear.on('click', this.clear.bind(this));
1387    },
1388
1389    update: function() {
1390      var val = this.$input.val().trim();
1391      this.$clear.toggle(!!val);
1392    },
1393
1394    submit: function(e) {
1395      if (!searchModal) return
1396      var api = searchModal.api();
1397      var query = this.$input.val().trim();
1398
1399      e.preventDefault();
1400      e.stopPropagation();
1401
1402      if (api && query) {
1403        api.execute(query);
1404        this.showModal();
1405      }
1406    },
1407
1408    clear: function(e) {
1409      e.preventDefault();
1410      this.$input.val('');
1411    },
1412
1413    submitIfEnter: function(e) {
1414      if (e.which == 13) this.submit(e);
1415    },
1416
1417    showModal: function() {
1418      if (searchModal) searchModal.show();
1419    },
1420
1421    hideModal: function() {
1422      if (searchModal) searchModal.hide();
1423    }
1424
1425  });
1426
1427  // SEARCH MODAL
1428
1429  var GSM = NGIN.GoogleSearchModal = function(selector, config) {
1430    if (searchModal) return searchModal;
1431    searchModal = this;
1432    this.$el = $(selector);
1433    this.$close = this.$el.find(config.closeElement);
1434    this.bindEvents();
1435  }
1436
1437  _.extend(GSM.prototype, {
1438
1439    api: function() {
1440      return google.search.cse.element.getElement('GoogleSearch');
1441    },
1442
1443    bindEvents: function() {
1444      this.$close.on('click', this.hide.bind(this));
1445      this.$el.on('click', this.clickToHide.bind(this));
1446    },
1447
1448    show: function() {
1449      this.$el.fadeIn();
1450    },
1451
1452    hide: function() {
1453      var api = searchModal.api();
1454      if (api) api.clearAllResults();
1455      this.$el.fadeOut();
1456    },
1457
1458
1459    clickToHide: function(e) {
1460      if (e.target === e.currentTarget) this.hide(e);
1461    }
1462
1463  });
1464
1465})(jQuery, _);

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.