PageSourceSearch

https://www.forcewrestling.com/javascripts/themes/itasca/theme.js?16737052261

js forcewrestling.com collected 2026-09-28 17:31:26 UTC 62,596 bytes, 1,975 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  $.fn.scrollGuard = function() {
114    this.each(function() {
115      var el = this;
116      var lastTouchY = null;
117
118      // Wheel event (desktop)
119      el.addEventListener('wheel', function(e) {
120        var d = -e.deltaY;
121        var atTop = el.scrollTop === 0;
122        var atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight;
123        if ((atTop && atBottom) || (d < 0 && atBottom) || (d > 0 && atTop)) {
124          e.preventDefault();
125        }
126      }, {passive: false});
127
128      // Touch events (mobile)
129      if (hasTouch()) {
130        setTouchScrolling(0, el);
131
132        el.addEventListener('touchstart', function(e) {
133          lastTouchY = e.touches[0].pageY;
134        }, {passive: true});
135
136        el.addEventListener('touchmove', function(e) {
137          var currentY = e.touches[0].pageY;
138          var d = currentY - lastTouchY;
139          lastTouchY = currentY;
140          var atTop = el.scrollTop === 0;
141          var atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight;
142          if ((atTop && atBottom) || (d < 0 && atBottom) || (d > 0 && atTop)) {
143            e.preventDefault();
144          }
145        }, {passive: false});
146
147        el.addEventListener('touchend', function() {
148          lastTouchY = null;
149        }, {passive: true});
150      }
151    });
152    return this;
153  };
154
155})(jQuery);
156;(function($){
157
158  var NAR = NGIN.AdResizer = function(selector, config) {
159    this.$el = $(selector); // this might need to be a direct reference to the ad-wrapper element
160    this.el = this.$el[0];
161    this.bindEvents();
162  }
163
164  $.extend(NAR, {
165    setup: function() {
166      googletag.pubads().addEventListener('slotRenderEnded', function(e) {
167        var adElement = $('#' + e.slot.getSlotElementId());
168        adElement.trigger('adSlotRendered', e);
169      });
170    }
171  });
172
173  $.extend(NAR.prototype, {
174
175    bindEvents: function() {
176      $(window).on('resize', this.resize.bind(this));
177      this.$el.on('adSlotRendered', this.resize.bind(this));
178      setTimeout(this.resize.bind(this), 0);
179    },
180
181    resize: function() {
182      this.setSize(1, 0); // reset
183      var scale = Math.min(this.el.clientWidth / this.el.scrollWidth, 1);
184      var marginBottom = this.el.scrollHeight * (scale - 1);
185      this.setSize(scale, marginBottom);
186    },
187
188    setSize: function(scale, marginBottom) {
189      this.$el.children().css('transform', 'scale(' + scale + ')');
190      this.$el.css('marginBottom', marginBottom + 'px');
191    }
192
193  });
194
195  if (typeof googletag == 'object') googletag.cmd.push(NGIN.AdResizer.setup);
196
197})(jQuery);
198;(function($) {
199
200  if (!NGIN) NGIN = {}
201  if (!NGIN.Modules) NGIN.Modules = {}
202
203  var $win = $(window);
204
205  var NS = NGIN.Modules.NavService = {
206
207    promises: {},
208    modules: {},
209
210    register: function(className, methods) {
211      var initialize = methods.initialize || $.noop;
212      var module = this.modules[className] = function(opts) {
213        this._setup(initialize, opts) // shared setup method
214      }
215      $.extend(module.prototype, this.sharedMethods, methods);
216      delete module.prototype.initialize;
217    },
218
219    create: function(className, opts) {
220      var Module = this.modules[className];
221      return Module && new Module(opts);
222    },
223
224    fixedNav: function(el) {
225      var $nav = $(el);
226      var $els = $nav.add('html');
227      var navTop = $nav.offset().top;
228      var wasFixed = false;
229
230      $(window).scroll(function(){
231        var scrollTop = window.scrollY || window.pageYOffset || 0;
232        var isFixed = navTop < scrollTop;
233        if (!wasFixed && isFixed) {
234          $els.addClass('nav-fixed nav-fixed-start');
235          setTimeout(function(){
236            $els.removeClass('nav-fixed-start');
237          }, 1)
238        }
239        else if (wasFixed && !isFixed) {
240          $els.removeClass('nav-fixed');
241        }
242        wasFixed = isFixed;
243      });
244    },
245
246    sharedMethods: {
247
248      // PUBLIC METHODS
249
250      getNavData: function(url) {
251        var nav = this;
252        return this.preload((this.nav_url = url));
253      },
254
255      preload: function(e) {
256        var url = typeof e === 'string' ? e : $(e.currentTarget).attr('data-nav-url');
257        return NS.promises[url] = NS.promises[url] || $.ajax({
258          dataType: "json",
259          url: url,
260          error: this._preloadError.bind(this, url)
261        });
262      },
263
264      // PRIVATE
265
266      _setup: function(initialize, opts) {
267        this.$html = $('html');
268        this.$el = $(opts.selector).first();
269        this.$navLinks = $($('#navLinksTemplate').html());
270        this.$pageSearch = $($('#pageSearchTemplate').html());
271
272        this.activeClass = opts.activeClass || '';
273        this.collapsedClass = opts.collapsedClass || '';
274        this.navUrl = opts.data.nav_url;
275        this.paywallImage = '<img src="' + opts.paywallImage + '" class="paywall_image">';
276        this.minWidth = opts.minWidth || 0;
277        this.collapseWidth = opts.collapseWidth || 0;
278        this.maxWidth = opts.maxWidth || Infinity;
279        this.currentId = opts.currentId;
280        this.opts = opts;
281
282        this._cache(opts.data);
283        this._bindEvents();
284
285        initialize.call(this, opts);
286
287        // render should come last
288        this.getNavData(this.navUrl)
289          .then(this._render.bind(this));
290      },
291
292      _bindEvents: function() {
293        $win.on('resize', this._resize.bind(this));
294      },
295
296      _render: function(navData, opts) {
297        this.render(navData, opts);
298        this.rendered = this.visible = true;
299        this._resize();
300      },
301
302      _okToShow: function() {
303        return window.innerWidth >= this.minWidth && window.innerWidth <= this.maxWidth;
304      },
305
306      _resize: function() {
307        if (!this.rendered) return;
308
309        // note, you can specify a collapse class in your config OR in the nav module
310        var collapse = this.collapsedClass && window.innerWidth <= this.collapseWidth;
311        var show = this._okToShow();
312
313        // set html class if visible
314        this.$html.toggleClass(this.activeClass, show);
315        if (this.collapsedClass) this.$html.toggleClass(this.collapsedClass, show && collapse);
316
317        // show or hide as needed
318        if (show && !this.visible) {
319          this.visible = true;
320          this.$el.show();
321        } else if (!show && this.visible) {
322          this.visible = false;
323          this.$el.hide();
324        }
325
326        if (this.resize) this.resize();
327      },
328
329      _cache: function(navData) {
330        var dfd = $.Deferred();
331        dfd.resolve(navData);
332        NS.promises[navData.nav_url] = dfd.promise();
333      },
334
335      _preloadError: function(url) {
336        delete NS.promises[url];
337      }
338    }
339
340  }
341
342})(jQuery);
343;(function($) {
344
345  var NEXT_TICK = 30;
346  var OPEN_DELAY = 200;
347  var CLOSE_DELAY = 500;
348  var OPENING_CLASS = 'dropdown-opening';
349  var OPEN_CLASS = 'dropdown-open';
350  var OPEN_CLASSES = OPEN_CLASS + ' ' + OPENING_CLASS;
351  var DROPDOWN_MARGIN_TOP = 0;
352  var DROPDOWN_MARGIN_BOTTOM = 30;
353  var DROPDOWN_WIDTH = 200;
354  var DROPDOWN_ALIGN_LEFT = 'dropdown-align-left';
355  var DROPDOWN_ALIGN_RIGHT = 'dropdown-align-right';
356  var DROPDOWN_ALIGN_CLASSES = DROPDOWN_ALIGN_LEFT + ' ' + DROPDOWN_ALIGN_RIGHT;
357  var LOAD_ERROR_RETRY_DELAY = 5000;
358  var RATE_LIMIT_RETRY_DELAY = 30000;
359
360  function realOuterWidth(el) {
361    if (!el || !el.style) return 0;
362    var s = window.getComputedStyle(el);
363    return parseFloat(s.width) + parseFloat(s.paddingRight) + parseFloat(s.paddingLeft) + parseFloat(s.borderRightWidth) + parseFloat(s.borderLeftWidth);
364  }
365
366  NGIN.Modules.NavService.register('DropdownNav', {
367
368    initialize: function(opts) {
369      this.openDelay = opts.openDelay || OPEN_DELAY;
370      this.closeDelay = opts.closeDelay || CLOSE_DELAY;
371      this.$navItems = $('<ul class="theme-nav theme-nav-style-dropdown" data-nav-level="1" />').appendTo(this.$el);
372      this.$moreButton = $('<li class="theme-nav-item theme-nav-more hasChild"><a class="theme-nav-link">More</a></li>');
373      this.$moreMenu = $('<ul class="theme-nav-dropdown" data-nav-level="2" />').width(DROPDOWN_WIDTH).scrollGuard();
374      this.$moreButton.data('dropdown', this.$moreMenu);
375      this.$moreMenu.data('navItem', this.$moreButton);
376      this.bindEvents();
377    },
378
379    bindEvents: function() {
380      $(window).one('load', this.checkOverflowAfterLoad.bind(this));
381      $(document).on('scroll', this.closeAllDropdowns.bind(this));
382      this.$el
383        .on('mouseenter', '.theme-nav-dropdown', this.dropdownOver.bind(this))
384        .on('mouseleave', '.theme-nav-dropdown', this.dropdownOut.bind(this))
385        .on('mouseenter', '.theme-nav-item', this.itemOver.bind(this))
386        .on('mouseleave', '.theme-nav-item', this.itemOut.bind(this))
387    },
388
389    itemOver: function(e) {
390      var $navItem = $(e.currentTarget);
391      $navItem.data('over', true);
392      if ($navItem.hasClass('hasChild')) this.showDropdown($navItem);
393      this.keepDropdownOpen($navItem.data('parentDropdown'));
394    },
395
396    itemOut: function(e) {
397      var $navItem = $(e.currentTarget);
398      var $dropdown = $navItem.data('dropdown');
399      $navItem.data('over', false);
400      this.allowDropdownToClose($dropdown);
401    },
402
403    dropdownOver: function(e) {
404      var $dropdown = $(e.currentTarget);
405      $dropdown.data('over', true);
406      this.keepDropdownOpen($dropdown);
407    },
408
409    dropdownOut: function(e) {
410      var $dropdown = $(e.currentTarget);
411      $dropdown.data('over', false);
412      this.allowDropdownToClose($dropdown);
413    },
414
415    closeAllDropdowns: function() {
416      this.closeItems(this.$navItems.children());
417    },
418
419    render: function(navData) {
420      var showChildren = this.opts.showChildren && navData.children.length;
421      var items = showChildren ? navData.children : navData.siblings;
422      var renderOpts = {
423        items: items,
424        level: 1
425      }
426
427      if (this.opts.showTitle) {
428        var titleObj = showChildren ? navData : navData.parent;
429        renderOpts.title = titleObj.name;
430        renderOpts.titleLink = titleObj.url;
431      }
432
433      this.renderNavItems(this.$navItems, renderOpts);
434      this.$el.append(this.$moreMenu);
435      this.handleOverflow();
436    },
437
438    resize: function() {
439      // need to only run this when showing the first time???
440      this.resetOverflow();
441    },
442
443    showDropdown: function($navItem) {
444      var nav = this;
445      var $dropdown = this.renderDropdown($navItem);
446      var $itemAndDropdown = $navItem.add($dropdown);
447      var openDelay = $dropdown.is(':visible') ? 0 : this.openDelay;
448
449      // keep this drop open
450      clearTimeout($dropdown.data('openTimeout'));
451      clearTimeout($dropdown.data('closeTimeout'));
452
453      this.positionDropdown($dropdown);
454
455      function startOpening() {
456        if (!$navItem.data('over') && !$dropdown.data('over')) return;
457        nav.loadDropdown($navItem, $dropdown);
458        nav.closeItems($navItem.siblings());
459        $itemAndDropdown.addClass(OPENING_CLASS);
460        $dropdown.data('openTimeout', setTimeout(finishOpening, NEXT_TICK));
461      }
462
463      function finishOpening() {
464        if ($navItem.hasClass(OPENING_CLASS)) {
465          $itemAndDropdown
466            .addClass(OPEN_CLASS)
467            .removeClass(OPENING_CLASS);
468        }
469      }
470
471      $dropdown.data('openTimeout', setTimeout(startOpening, openDelay));
472    },
473
474    keepDropdownOpen: function($dropdown) {
475      if (!$dropdown) return
476      $dropdown.data('over', true);
477      clearTimeout($dropdown.data('closeTimeout'));
478      var $parentItem = $dropdown.data('navItem');
479      if ($parentItem) this.keepDropdownOpen($parentItem.data('parentDropdown'));
480    },
481
482    allowDropdownToClose: function($dropdown) {
483      if (!$dropdown) return
484      $dropdown.data('over', false);
485      $dropdown.data('closeTimeout', setTimeout(this.closeDropdown.bind(this, $dropdown, true), this.closeDelay));
486      var $parentItem = $dropdown.data('navItem');
487      if ($parentItem) this.allowDropdownToClose($parentItem.data('parentDropdown'));
488    },
489
490    closeItems: function($items) {
491      var nav = this;
492      $items.each(function(i, item) {
493        nav.closeDropdown($(item).data('dropdown'));
494      });
495    },
496
497    closeDropdown: function($dropdown, bubble) {
498      if (!$dropdown) return
499      var $navItem = $dropdown.data('navItem');
500      if ($navItem.data('over') || $dropdown.data('over')) return
501      $navItem.removeClass(OPEN_CLASSES);
502      this.closeItems($dropdown.removeClass(OPEN_CLASSES).children());
503      if (bubble) this.closeDropdown($navItem.data('parentDropdown'));
504    },
505
506    positionDropdown: function($dropdown) {
507      // start by resetiing the dropdown state
508      $dropdown
509        .css('maxHeight', '')
510        .removeClass(DROPDOWN_ALIGN_CLASSES);
511
512      var $navItem = $dropdown.data('navItem');
513      var $parentDropdown = $navItem.data('parentDropdown');
514      var renderBelow = $dropdown.data('nav-level') < 3;
515      var navOffset = this.$el.offset();
516      var navItemsOffset = this.$navItems.offset();
517      var itemOffset = $navItem.offset();
518      var windowHeight = $(window).height();
519      var windowScroll = $(window).scrollTop();
520      var maxX = this.$navItems.outerWidth() + navItemsOffset.left - navOffset.left;
521      var navHeight = this.$navItems.outerHeight();
522      var navItemWidth = $navItem.outerWidth();
523      var width = navItemWidth > DROPDOWN_WIDTH ? navItemWidth : DROPDOWN_WIDTH;
524      var height = $dropdown.outerHeight();
525      var minTop = navHeight;
526      var fixedBottom = navOffset.top + navHeight - windowScroll;
527      var maxHeight = windowHeight - fixedBottom - DROPDOWN_MARGIN_BOTTOM;
528
529      // allow second level menues to shift down
530      if (!renderBelow) minTop += DROPDOWN_MARGIN_TOP;
531
532      var maxBottom = minTop + maxHeight;
533      var left;
534      var top;
535      var alignmentClass = DROPDOWN_ALIGN_LEFT;
536
537      // first level drop downs
538      if (renderBelow) {
539        top = minTop;
540        left = itemOffset.left - navOffset.left;
541
542        if (left + width > maxX) {
543          alignmentClass = DROPDOWN_ALIGN_RIGHT;
544          left = left - width + navItemWidth;
545        }
546      }
547
548      // second level dropdowns
549      else {
550        var widthOffset = $parentDropdown ? $parentDropdown.outerWidth() : navItemWidth;
551        top = Math.max(itemOffset.top - navOffset.top, minTop);
552        left = itemOffset.left - navOffset.left + widthOffset;
553
554        if (left + width > maxX) {
555          alignmentClass = DROPDOWN_ALIGN_RIGHT;
556          left = left - width - navItemWidth;
557        }
558      }
559
560      // Shift menu up if is scrolls and there is space to do so
561      if (top + height > maxBottom) {
562        top = Math.max(maxBottom - height, minTop);
563      }
564
565      // update dropdown state
566      $dropdown
567        .addClass(alignmentClass)
568        .css({
569          top: top,
570          left: left,
571          maxHeight: maxHeight,
572          width: width,
573          overflow: 'auto'
574        });
575    },
576
577    showNextLevel: function(level) {
578      var checkDepth = 'maxDepth' in this.opts && level;
579      return checkDepth && level <= this.opts.maxDepth || false;
580    },
581
582    loadDropdown: function($navItem, $dropdown) {
583      if ($dropdown.data('loadRequested') || !$dropdown.hasClass('theme-nav-dropdown') || !$dropdown.hasClass('loading')) return;
584      if (this.dropdownLoadCoolingDown($dropdown)) return;
585
586      var nav = this;
587      var navUrl = $navItem.data('nav-url');
588      var level = $dropdown.data('nav-level');
589
590      if (!navUrl) return;
591
592      $dropdown.data('loadRequested', true);
593
594      function renderItems(navData) {
595        $dropdown.removeData('loadRetryAt');
596        nav.renderNavItems($dropdown, {
597          items: navData.children,
598          level: level
599        });
600        var items = $dropdown.children();
601        items.data('parentItem', $navItem);
602        items.data('parentDropdown', $dropdown);
603        // add scroll binding to hide submenus
604        $dropdown.on('scroll', nav.closeItems.bind(nav, $dropdown.children()));
605        nav.positionDropdown($dropdown);
606      }
607
608      function handleLoadError(xhr) {
609        $dropdown.removeData('loadRequested');
610        $dropdown.data('loadRetryAt', nav.nextDropdownLoadRetryAt(xhr));
611      }
612
613      nav.getNavData(navUrl).then(renderItems, handleLoadError);
614    },
615
616    dropdownLoadCoolingDown: function($dropdown) {
617      var retryAt = $dropdown.data('loadRetryAt');
618      if (!retryAt) return false;
619      if (retryAt > new Date().getTime()) return true;
620      $dropdown.removeData('loadRetryAt');
621      return false;
622    },
623
624    nextDropdownLoadRetryAt: function(xhr) {
625      var retryAfter = xhr && xhr.getResponseHeader && parseInt(xhr.getResponseHeader('Retry-After'), 10);
626      var delay = retryAfter ? retryAfter * 1000 : xhr && xhr.status === 429 ? RATE_LIMIT_RETRY_DELAY : LOAD_ERROR_RETRY_DELAY;
627      return new Date().getTime() + delay;
628    },
629
630    renderDropdown: function($navItem) {
631      var $dropdown = $navItem.data('dropdown');
632      if ($dropdown) return $dropdown;
633
634      var level = ($navItem.parent().data('nav-level') || 1) + 1
635
636      $dropdown = $('<ul class="loading theme-nav-dropdown" data-nav-level="' + level + '" />');
637      $dropdown.width(DROPDOWN_WIDTH);
638      $dropdown.scrollGuard(); // prevents overflow scrolling
639      $navItem.data('dropdown', $dropdown);
640      $dropdown.data('navItem', $navItem);
641
642      return $dropdown.appendTo(this.$el);
643    },
644
645    renderNavItems: function($container, opts) {
646      var nav = this;
647      $container.removeClass('loading');
648
649      if (opts.title) {
650        $container.append('<li class="theme-nav-title"><a href="' + opts.titleLink + '">' + opts.title + '</a></li>');
651      }
652
653      opts.items.forEach(function(navItem){
654        var classes = [].concat(navItem.css_classes||[]);
655        var $navItem = $('<li class="theme-nav-item" />');
656
657        if (navItem.id === nav.currentId) classes.push('selected');
658        if (navItem.display_status) classes.push(navItem.display_status);
659
660        if (nav.showNextLevel(opts.level)) {
661          if (navItem.has_child) classes.push('hasChild');
662          if (navItem.nav_url) $navItem.attr('data-nav-url', navItem.nav_url);
663        }
664
665        $navItem.addClass(classes.join(' '));
666
667        var $link = $('<a class="theme-nav-link" />')
668          .appendTo($navItem)
669          .text(navItem.name)
670          .attr({
671            href: navItem.url,
672            title: navItem.title,
673            target: navItem.target || '_self'
674          });
675
676        if (navItem.display_status === 'paywalled' && nav.paywallImage) {
677          $link.prepend(nav.paywallImage + ' ');
678        }
679
680        $container.append($navItem);
681      })
682    },
683
684    checkOverflowAfterLoad: function() {
685      var overflowWidths = this.overflowWidths || [];
686      var firstItemWidth = this.$navItems.children().first().outerWidth();
687      this.resetOverflow();
688    },
689
690    resetOverflow: function() {
691      delete this.overflowWidths;
692      this.$moreButton.detach();
693      this.$navItems.append(this.$moreMenu.children());
694      this.handleOverflow();
695    },
696
697    handleOverflow: function() {
698      var navItems = this.$navItems;
699      var moreMenu = this.$moreMenu;
700      var moreButton = this.$moreButton;
701      var visibleItems = navItems.children().not(moreButton);
702      var oldExtraItems = moreMenu.children();
703      var allItems = visibleItems.add(oldExtraItems);
704
705      var overflowWidths = this.getOverflowWidths();
706      var moreButtonWidth = this.moreButtonWidth;
707      var maxWidth = this.$navItems.width();
708      var totalWidth = 0;
709      var hasExtraItems = false;
710      var index = 0;
711
712      // fit as many items in as possible
713      while (index < overflowWidths.length) {
714        var itemWidth = overflowWidths[index];
715        if (totalWidth + itemWidth > maxWidth) {
716          hasExtraItems = true;
717          break;
718        }
719        totalWidth += itemWidth;
720        index++;
721      }
722
723      // backtrack if more button won't fit
724      while (hasExtraItems && totalWidth + moreButtonWidth > maxWidth) {
725        index--;
726        totalWidth -= overflowWidths[index];
727      }
728
729      var newExtraItems = hasExtraItems && allItems.slice(index).not(oldExtraItems);
730      var newVisibleItems = hasExtraItems && oldExtraItems.not(newExtraItems);
731
732      function changeNavLevel(delta) {
733        return function(i, navItem) {
734          navItem = $(navItem);
735          navItem.data('nav-level', delta + navItem.data('nav-level'));
736        }
737      }
738
739      function setParents(parentItem, parentDropdown) {
740        return function(i, navItem) {
741          navItem = $(navItem);
742          navItem.data('parentItem', parentItem);
743          navItem.data('parentDropdown', parentDropdown);
744        }
745      }
746
747      if (newVisibleItems) {
748        newVisibleItems.each(changeNavLevel(-1))
749        moreButton.before(newVisibleItems);
750        newVisibleItems.each(setParents(null, null))
751      }
752
753      if (newExtraItems) {
754        newExtraItems.each(changeNavLevel(1))
755        moreMenu.prepend(newExtraItems);
756        newExtraItems.each(setParents(moreButton, moreMenu))
757      }
758
759      if (!hasExtraItems) moreButton.detach();
760    },
761
762    getOverflowWidths: function() {
763      if (this.overflowWidths) return this.overflowWidths;
764
765      var overflowWidths = this.overflowWidths = [];
766      var navItems = this.$navItems.children();
767      var moreButton = this.$moreButton.appendTo(this.$navItems);
768      this.moreButtonWidth = Math.ceil(realOuterWidth(moreButton.get(0)));
769
770      navItems.each(function(i, item) {
771        overflowWidths[i] = Math.ceil(realOuterWidth(item));
772      });
773
774      return overflowWidths;
775    }
776
777  });
778
779})(jQuery);
780;(function($) {
781  var MEGA_NAV_SELECTOR = '.mega-nav, .mega_nav';
782
783  function installMegaNavPatch() {
784    if (!window.NGIN || !NGIN.Modules || !NGIN.Modules.NavService) return false;
785
786    var NS = NGIN.Modules.NavService;
787    if (NS.__sharedMegaNavPatchInstalled__) return true;
788
789    function patchMethods(methods) {
790      if (!methods || methods.__sharedMegaNavMethodsPatched__) return methods;
791
792      var patched = $.extend({}, methods);
793      var originalBindEvents = patched.bindEvents;
794      var originalCloseDropdown = patched.closeDropdown;
795      var originalPositionDropdown = patched.positionDropdown;
796      var originalRenderDropdown = patched.renderDropdown;
797      var originalRenderNavItems = patched.renderNavItems;
798      var originalResize = patched.resize;
799
800      patched.bindEvents = function() {
801        originalBindEvents.call(this);
802        this.$el
803          .on('mouseenter', '.mega-nav-dropdown', this.dropdownOver.bind(this))
804          .on('mouseleave', '.mega-nav-dropdown', this.dropdownOut.bind(this));
805      };
806
807      patched.renderDropdown = function($navItem) {
808        var $dropdown = $navItem.data('dropdown');
809        if ($dropdown) return $dropdown;
810
811        if (!$navItem.data('parentItem') && isMegaNavItem($navItem)) {
812          return this.renderMegaDropdown($navItem);
813        }
814
815        return originalRenderDropdown.call(this, $navItem);
816      };
817
818      patched.positionDropdown = function($dropdown) {
819        if ($dropdown && $dropdown.hasClass('mega-nav-dropdown')) {
820          return this.positionMegaDropdown($dropdown);
821        }
822
823        return originalPositionDropdown.call(this, $dropdown);
824      };
825
826      patched.closeDropdown = function($dropdown, bubble) {
827        var result = originalCloseDropdown.call(this, $dropdown, bubble);
828
829        if ($dropdown && $dropdown.hasClass('mega-nav-dropdown') && !isDropdownOpen($dropdown)) {
830          resetCollapsedItems($dropdown);
831        }
832
833        return result;
834      };
835
836      patched.resize = function() {
837        if (originalResize) originalResize.call(this);
838
839        this.$el.find('.mega-nav-dropdown').each(function(i, dropdown) {
840          var $dropdown = $(dropdown);
841          if (isDropdownOpen($dropdown)) this.positionDropdown($dropdown);
842        }.bind(this));
843      };
844
845      patched.renderMegaDropdown = function($navItem) {
846        var nav = this;
847        var $dropdown = $('<div class="mega-nav-dropdown" data-nav-level="2" />').scrollGuard();
848        var megaChildren = $navItem.data('mega-nav-children');
849        var maxItems = parseInt($navItem.data('mega-nav-max-items'), 10) || 5;
850
851        $navItem.data('dropdown', $dropdown);
852        $dropdown
853          .data('navItem', $navItem)
854          .appendTo(this.$el);
855
856        if (megaChildren) {
857          this.renderMegaColumns($dropdown, megaChildren, maxItems);
858          this.positionDropdown($dropdown);
859        } else {
860          $dropdown.addClass('loading');
861          nav.getNavData($navItem.data('nav-url')).then(function(navData) {
862            $dropdown.removeClass('loading');
863            nav.renderMegaColumns($dropdown, navData.children, maxItems);
864            nav.positionDropdown($dropdown);
865          });
866        }
867
868        return $dropdown;
869      };
870
871      patched.renderMegaColumns = function($dropdown, children, maxItems) {
872        var maxVisible = maxItems || 5;
873        var generalItems = [];
874        var $columnsWrap = $('<div class="mega-nav-columns-wrap" />');
875
876        applyDropdownStyles(this, $dropdown);
877
878        $.each(children || [], function(i, child) {
879          if (!canAccess(child)) return;
880
881          var childNodes = $.grep(child.children || [], canAccess);
882          if (!childNodes.length) {
883            generalItems.push(child);
884            return;
885          }
886
887          var $column = $('<div class="mega-nav-column" />')
888            .append(buildMegaLink('mega-nav-column-header', child));
889
890          appendMegaItems($column, childNodes, maxVisible);
891          $columnsWrap.append($column);
892        });
893
894        setMegaColumnCount($columnsWrap, maxMegaColumnCount($columnsWrap));
895
896        if (generalItems.length) {
897          var $generalColumn = $('<div class="mega-nav-column mega-nav-column-general" />');
898          appendMegaItems($generalColumn, generalItems, Infinity);
899          $dropdown.append($generalColumn);
900        }
901
902        $dropdown.append($columnsWrap);
903        fitGeneralColumn($dropdown, generalItems);
904      };
905
906      patched.positionMegaDropdown = function($dropdown) {
907        var $navItem = $dropdown.data('navItem');
908        var navHeight = this.$navItems.outerHeight();
909        var elOffsetLeft = this.$el.offset().left;
910        var windowLeft = $(window).scrollLeft();
911        var windowRight = windowLeft + $(window).width();
912        var elWidth = this.$el.outerWidth();
913        var visibleLeft = Math.max(0, windowLeft - elOffsetLeft);
914        var visibleRight = Math.min(elWidth, windowRight - elOffsetLeft);
915        var availableWidth = Math.max(0, visibleRight - visibleLeft);
916        var navBottom = this.$el.offset().top + navHeight - $(window).scrollTop();
917        var maxHeight = $(window).height() - navBottom - 20;
918        var itemLeft = $navItem.offset().left - elOffsetLeft;
919        var itemRight = itemLeft + $navItem.outerWidth();
920        fitMegaColumnsToWidth($dropdown, availableWidth);
921        var measuredWidth = measureMegaWidth($dropdown);
922        var dropdownWidth = Math.min(measuredWidth || availableWidth, availableWidth);
923        var maxLeft = Math.max(visibleLeft, visibleRight - dropdownWidth);
924        var left = itemLeft + dropdownWidth > visibleRight ? itemRight - dropdownWidth : itemLeft;
925
926        $dropdown.css({
927          top: navHeight - 1,
928          width: dropdownWidth,
929          maxWidth: availableWidth,
930          maxHeight: maxHeight,
931          overflowX: 'auto',
932          overflowY: 'auto',
933          left: Math.max(visibleLeft, Math.min(left, maxLeft)),
934          transform: ''
935        });
936      };
937
938      patched.renderNavItems = function($container, opts) {
939        var existingCount = $container.children('.theme-nav-item').length;
940        originalRenderNavItems.call(this, $container, opts);
941
942        var items = opts.items || [];
943        $container.children('.theme-nav-item').slice(existingCount).each(function(index) {
944          var navItem = items[index];
945          if (!navItem) return;
946
947          var $navItem = $(this);
948          if (navItem.mega_nav_children) $navItem.data('mega-nav-children', navItem.mega_nav_children);
949          if (navItem.mega_nav_max_items) $navItem.data('mega-nav-max-items', navItem.mega_nav_max_items);
950
951          if (isMegaNavItem($navItem)) {
952            $navItem.addClass('hasChild');
953            if (navItem.nav_url) $navItem.attr('data-nav-url', navItem.nav_url);
954          }
955        });
956      };
957
958      patched.__sharedMegaNavMethodsPatched__ = true;
959      return patched;
960    }
961
962    function patchPrototype(Module) {
963      if (!Module || !Module.prototype || Module.prototype.__sharedMegaNavPrototypePatched__) return;
964
965      $.extend(Module.prototype, patchMethods(Module.prototype));
966      Module.prototype.__sharedMegaNavPrototypePatched__ = true;
967    }
968
969    if (!NS.__sharedMegaNavRegisterWrapped__) {
970      var originalRegister = NS.register;
971      NS.register = function(className, methods) {
972        if (className === 'DropdownNav') methods = patchMethods(methods);
973        var result = originalRegister.call(this, className, methods);
974        if (className === 'DropdownNav') patchPrototype(this.modules && this.modules.DropdownNav);
975        return result;
976      };
977      NS.__sharedMegaNavRegisterWrapped__ = true;
978    }
979
980    patchPrototype(NS.modules && NS.modules.DropdownNav);
981    NS.__sharedMegaNavPatchInstalled__ = true;
982    return true;
983  }
984
985  function appendMegaItems($column, items, maxVisible) {
986    $.each(items, function(i, item) {
987      var $item = buildMegaLink('mega-nav-column-item', item);
988      if (items.length > maxVisible + 1 && i >= maxVisible) $item.addClass('mega-nav-hidden-item').hide();
989      $column.append($item);
990    });
991
992    if (items.length > maxVisible + 1) appendMoreLink($column);
993  }
994
995  function appendMoreLink($column) {
996    $('<a class="mega-nav-column-item mega-nav-more-link" href="#" />')
997      .text('More...')
998      .on('click', function(e) {
999        e.preventDefault();
1000        $(this).siblings('.mega-nav-hidden-item').show();
1001        $(this).hide();
1002      })
1003      .appendTo($column);
1004  }
1005
1006  function buildMegaLink(className, item) {
1007    var $link = $('<a class="' + className + '" />')
1008      .text(item.name)
1009      .attr({
1010        href: item.url || item.nav_url || item.path || item.full_url || '#',
1011        title: item.title || item.name,
1012        target: item.target || '_self'
1013      });
1014
1015    if (isDisabled(item)) $link.addClass('mega-nav-muted');
1016    return $link;
1017  }
1018
1019  function applyDropdownStyles(nav, $dropdown) {
1020    var $source = findSourceDropdown(nav);
1021    var removeSource = !$source.length;
1022
1023    if (removeSource) {
1024      $source = $(
1025        '<div class="theme-nav-dropdown" data-nav-level="2" style="display:block;visibility:hidden;position:absolute;
1025left:-9999px;top:-9999px;">' +
1026          '<div class="theme-nav-item"><a class="theme-nav-link" href="#">Sample</a></div>' +
1027          '<div class="theme-nav-item disabled"><a class="theme-nav-link" href="#">Sample</a></div>' +
1028        '</div>'
1029      ).appendTo(nav.$el);
1030    }
1031
1032    copyStyleProperty($dropdown, $source[0], 'zIndex', 'z-index');
1033    copyStyleVar($dropdown, $source[0], 'backgroundColor', '--mega-dropdown-bg');
1034    copyStyleVar($dropdown, $source.find('.theme-nav-item').first()[0], 'borderBottomColor', '--mega-separator-color');
1035    copyStyleVar($dropdown, $source.find('.theme-nav-link').first()[0], 'color', '--mega-link-color');
1036    copyStyleVar($dropdown, $source.find('.theme-nav-link').first()[0], 'color', '--mega-link-hover-color');
1037    copyStyleVar($dropdown, $source.find('.disabled .theme-nav-link, .disabled a').first()[0], 'color', '--mega-link-disabled-color');
1038
1039    if (removeSource) $source.remove();
1040  }
1041
1042  function copyStyleProperty($target, source, property, cssProperty) {
1043    if (!source) return;
1044
1045    var value = window.getComputedStyle(source)[property];
1046    if (value && value !== 'auto') $target[0].style.setProperty(cssProperty, value);
1047  }
1048
1049  function copyStyleVar($target, source, property, cssVar) {
1050    if (!source) return;
1051
1052    var value = window.getComputedStyle(source)[property];
1053    if (value && value !== 'transparent' && value !== 'rgba(0, 0, 0, 0)') {
1054      $target[0].style.setProperty(cssVar, value);
1055    }
1056  }
1057
1058  function canAccess(item) {
1059    if (!item) return false;
1060    if (item.hasOwnProperty('can_access')) return !!item.can_access;
1061    if (item.hasOwnProperty('can_view')) return !!item.can_view;
1062    if (item.hasOwnProperty('accessible')) return !!item.accessible;
1063    if (item.hasOwnProperty('authorized')) return !!item.authorized;
1064    return true;
1065  }
1066
1067  function fitGeneralColumn($dropdown, generalItems) {
1068    var $columnsWrap = $dropdown.find('.mega-nav-columns-wrap');
1069    if (!generalItems.length || !$columnsWrap.children().length) return;
1070
1071    var wasHidden = !$dropdown.is(':visible');
1072    if (wasHidden) $dropdown.css({ display: 'flex', visibility: 'hidden', opacity: 0 });
1073
1074    var columnsHeight = $columnsWrap.outerHeight();
1075    var $items = $dropdown.find('.mega-nav-column-general .mega-nav-column-item');
1076    var totalHeight = 0;
1077    var visibleCount = 0;
1078
1079    $items.each(function() {
1080      totalHeight += $(this).outerHeight(true);
1081      if (totalHeight <= columnsHeight) visibleCount++;
1082      else return false;
1083    });
1084
1085    if (visibleCount < generalItems.length && generalItems.length - visibleCount > 1) {
1086      visibleCount = Math.max(visibleCount - 1, 3);
1087      $items.each(function(i) {
1088        if (i >= visibleCount) $(this).addClass('mega-nav-hidden-item').hide();
1089      });
1090      appendMoreLink($dropdown.find('.mega-nav-column-general'));
1091    }
1092
1093    if (wasHidden) $dropdown.css({ display: '', visibility: '', opacity: '' });
1094  }
1095
1096  function findSourceDropdown(nav) {
1097    return nav.$el.find('.theme-nav-dropdown:not(.mega-nav-dropdown)').filter(function() {
1098      return $(this).find('.theme-nav-item').not(MEGA_NAV_SELECTOR).length > 0;
1099    }).first();
1100  }
1101
1102  function isDisabled(item) {
1103    if (!item) return false;
1104    if (item.disabled === true || item.is_disabled === true) return true;
1105
1106    var status = ((item.display_status || '') + '').toLowerCase();
1107    return !!status && status !== 'enabled' && status !== 'public' && status !== 'paywalled';
1108  }
1109
1110  function isMegaNavItem($navItem) {
1111    return $navItem.is(MEGA_NAV_SELECTOR);
1112  }
1113
1114  function isDropdownOpen($dropdown) {
1115    return $dropdown.hasClass('dropdown-open') || $dropdown.hasClass('dropdown-opening');
1116  }
1117
1118  function fitMegaColumnsToWidth($dropdown, availableWidth) {
1119    var $columnsWrap = $dropdown.find('.mega-nav-columns-wrap');
1120    var maxCols = maxMegaColumnCount($columnsWrap);
1121    var minCols = maxCols > 1 ? 2 : 1;
1122    var cols;
1123
1124    if (!$columnsWrap.length || !availableWidth) return maxCols;
1125
1126    for (cols = maxCols; cols > minCols; cols--) {
1127      setMegaColumnCount($columnsWrap, cols);
1128      if (measureMegaWidth($dropdown) <= availableWidth) return cols;
1129    }
1130
1131    setMegaColumnCount($columnsWrap, minCols);
1132    return minCols;
1133  }
1134
1135  function maxMegaColumnCount($columnsWrap) {
1136    if (!$columnsWrap.length) return 1;
1137
1138    return Math.max(1, Math.min(4, $columnsWrap.children().length));
1139  }
1140
1141  function setMegaColumnCount($columnsWrap, columnCount) {
1142    if ($columnsWrap.length) {
1143      $columnsWrap[0].style.setProperty('--mega-nav-cols-per-row', columnCount);
1144    }
1145  }
1146
1147  function measureMegaWidth($dropdown) {
1148    var wasHidden = !$dropdown.is(':visible');
1149    var el = $dropdown[0];
1150    var display = el.style.display;
1151    var maxWidth = el.style.maxWidth;
1152    var opacity = el.style.opacity;
1153    var visibility = el.style.visibility;
1154    var widthStyle = el.style.width;
1155
1156    $dropdown.css({ width: '', maxWidth: '' });
1157    if (wasHidden) $dropdown.css({ display: 'flex', visibility: 'hidden', opacity: 0 });
1158
1159    var width = $dropdown.outerWidth() || 0;
1160
1161    el.style.display = display;
1162    el.style.maxWidth = maxWidth;
1163    el.style.opacity = opacity;
1164    el.style.visibility = visibility;
1165    el.style.width = widthStyle;
1166
1167    return width;
1168  }
1169
1170  function resetCollapsedItems($dropdown) {
1171    $dropdown.find('.mega-nav-hidden-item').hide();
1172    $dropdown.find('.mega-nav-more-link').show();
1173  }
1174
1175  function watchProperty(obj, key, onSet) {
1176    if (!obj) return false;
1177
1178    var desc = Object.getOwnPropertyDescriptor(obj, key);
1179    if (desc && !desc.configurable) return false;
1180
1181    var value = obj[key];
1182    Object.defineProperty(obj, key, {
1183      configurable: true,
1184      enumerable: desc ? desc.enumerable : true,
1185      get: function() {
1186        return value;
1187      },
1188      set: function(next) {
1189        value = next;
1190        onSet(next);
1191      }
1192    });
1193
1194    if (value !== undefined) onSet(value);
1195    return true;
1196  }
1197
1198  function watchModules(modules) {
1199    if (!modules) return;
1200
1201    if (installMegaNavPatch()) return;
1202    watchProperty(modules, 'NavService', installMegaNavPatch);
1203  }
1204
1205  function watchNavServiceBootstrap() {
1206    if (installMegaNavPatch()) return;
1207
1208    watchProperty(window, 'NGIN', function(ngin) {
1209      if (!ngin) return;
1210
1211      if (installMegaNavPatch()) return;
1212      watchModules(ngin.Modules);
1213      watchProperty(ngin, 'Modules', watchModules);
1214    });
1215
1216    if (window.NGIN) {
1217      watchModules(window.NGIN.Modules);
1218      watchProperty(window.NGIN, 'Modules', watchModules);
1219    }
1220
1221    $(window).one('load', installMegaNavPatch);
1222    $(document).one('DOMContentLoaded', installMegaNavPatch);
1223  }
1224
1225  watchNavServiceBootstrap();
1226})(jQuery);
1227;(function($) {
1228
1229  var DEFAULT_DURATION = 300;
1230
1231  NGIN.Modules.NavService.register('SlideNav', {
1232
1233    initialize: function(opts) {
1234      // Allow custom collapsed classes as well as the required one (for css integration)
1235      this.collapsedClass = 'slideNavCollapsed ' + (this.collapsedClass || '');
1236      this.duration = opts.duration || DEFAULT_DURATION;
1237      this.collapseWidth = opts.collapseWidth || 0;
1238      this.listCount = 0;
1239      this.currentNavData = opts.data;
1240      this.$html = $('html');
1241      this.$nav = $('<div class="theme-nav theme-nav-style-slide" />');
1242      this.$toggle = $('<span class="theme-slide-nav-toggle" />').appendTo(this.$nav);
1243      this.$title = $('<div class="theme-nav-title">').appendTo(this.$nav);
1244      this.$menus = $('<div class="theme-nav-menus">').appendTo(this.$nav).scrollGuard();
1245      this.$overlay = $('<div class="theme-slide-nav-overlay" />').appendTo(this.$nav);
1246
1247      this.appendContent();
1248      this.bindEvents();
1249
1250      this.$el.trigger('SlideNavInitialize', { navElement: this.$nav });
1251    },
1252
1253    // Injects any pre-rendered content into the nav element in the correct location
1254    appendContent: function() {
1255      var content = this.$el.children().detach();
1256      if (content.length) $('<div class="theme-slide-nav-content" />')
1257        .append(content)
1258        .insertBefore(this.$title);
1259    },
1260
1261    bindEvents: function() {
1262      this.$nav.on('hover', '[data-nav-url]', this.preload.bind(this));
1263      this.$menus.on('click', '[data-nav-url]', this.loadChildNav.bind(this));
1264      this.$title.on('click', '[data-nav-url]', this.loadParentNav.bind(this));
1265      this.$toggle.add(this.$overlay).on('click', this.toggleMenu.bind(this));
1266
1267      // unbind mclose -- Removes old ngin JS menu close handler
1268      $(document).unbind('click', mclose());
1269    },
1270
1271    toggleMenu: function(e) {
1272      var open = !this.$html.hasClass('slide-nav-open');
1273      this.$html.toggleClass('slide-nav-open', open);
1274      this.$el.trigger(open ? 'SlideNavOpen' : 'SlideNavClose', { navElement: this.$nav });
1275      this.scrollToTop();
1276    },
1277
1278    scrollToTop: function() {
1279      // Doing this in an interval to fix an issue where iOS would NOT end up at
1280      // the top when closing the menu. Maybe something to do with CSS animation?
1281      var scrollInterval = setInterval(function(){
1282        window.scrollTo(0, 0);
1283      }, 20);
1284      setTimeout(function(){
1285        clearInterval(scrollInterval);
1286      }, 300);
1287    },
1288
1289    resize: function() {
1290      this.$html.not('.slideNavCollapsed').removeClass('slide-nav-open');
1291    },
1292
1293    renderParentContent: function(data) {
1294      var topLevel = data.parent_id === null && data.node_type === null;
1295
1296      if (topLevel) {
1297        this.$title.html(
1298          '<span class="theme-nav-title-text">Main Menu</span>'
1299        );
1300      } else {
1301        this.$title.html(
1302          '<span class="theme-nav-back" data-nav-url="' + data.nav_url + '"></span>' +
1303          '<a href="' + data.url + '" class="theme-nav-title-text">' + data.name + '</a>'
1304        );
1305      }
1306    },
1307
1308    render: function(navData, opts) {
1309      if (!this.$nav.parent(this.$el).length) this.$el.append(this.$nav);
1310      var navDir = 'up';
1311
1312      // navDir bound as first arg when rendering after navigation
1313      if (typeof navData === 'string') {
1314        navDir = navData;
1315        navData = opts;
1316      }
1317
1318      var renderParent = navDir !== 'down';
1319      var newMenu = $('<ul class="theme-nav-menu" />');
1320      var listData = renderParent ? navData.siblings : navData.children;
1321      var parentData = renderParent ? navData.parent : navData;
1322
1323      // Build Lists
1324      for (var i = 0; i < listData.length; i++) {
1325        var navItem = $('<li class="theme-nav-item"/>')
1326        var data = listData[i];
1327        var linkContent = data.name;
1328
1329        if (data.display_status == "paywalled") linkContent = this.paywallImage + ' ' + linkContent
1330
1331        var $link = $('<a class="theme-nav-link"/>')
1332          .html(linkContent)
1333          .attr({
1334             href: data.url,
1335             title: data.title,
1336             id: data.id,
1337             target: data.target || '_self'
1338          });
1339        var forwardArrow = '';
1340
1341        if (data.display_status) navItem.addClass(data.display_status);
1342        if (data.id === this.currentId) navItem.addClass('selected');
1343        if (data.has_child) {
1344          navItem.addClass('hasChild');
1345          forwardArrow = $('<span class="theme-nav-forward ' + data.id + '"></span>');
1346          if (data.nav_url) forwardArrow.attr('data-nav-url', data.nav_url);
1347        }
1348
1349        navItem
1350          .append($link, forwardArrow)
1351          .appendTo(newMenu)
1352      }
1353
1354      // render/preload parent data if needed (do this after preloading
1355      // children since child pages are more likely to be needed first).
1356      this.renderParentContent(parentData);
1357
1358      // Place List Content
1359      this.$menus.append(newMenu);
1360
1361      // Sets the first menu as current (will animate to next menu if needed)
1362      this.$menus.children().first();
1363
1364      // Animate if we have rendered more than one list
1365      if (this.listCount++) {
1366        if (renderParent) this.transitionToParent();
1367        else this.transitionToChildren();
1368      }
1369    },
1370
1371    transitionToParent: function() {
1372      var menus = this.$menus.children();
1373      var newMenu = menus.last().addClass('theme-nav-parent-in');
1374      var oldMenu = menus.first().addClass('theme-nav-children-out');
1375      newMenu.insertBefore(oldMenu);
1376      setTimeout(function() {
1377        newMenu.removeClass('theme-nav-parent-in');
1378      }, 50);
1379      setTimeout(function() {
1380        oldMenu.remove();
1381      }, this.duration);
1382    },
1383
1384    transitionToChildren: function() {
1385      var menus = this.$menus.children();
1386      var newMenu = menus.last().addClass('theme-nav-children-in');
1387      var oldMenu = menus.first().addClass('theme-nav-parent-out');
1388      setTimeout(function() {
1389        newMenu.removeClass('theme-nav-children-in');
1390      }, 50);
1391      setTimeout(function() {
1392        oldMenu.remove();
1393      }, this.duration);
1394    },
1395
1396    loadParentNav: function(e) {
1397      this.getNavData($(e.currentTarget).attr('data-nav-url'))
1398        .then(this.render.bind(this, 'up'));
1399    },
1400
1401    loadChildNav: function(e) {
1402      this.getNavData($(e.currentTarget).attr('data-nav-url'))
1403        .then(this.render.bind(this, 'down'));
1404    }
1405
1406  });
1407
1408})(jQuery);
1409;(function($){
1410
1411  // Requires teh $.fn.scrollGuard module.
1412
1413  var COMPACT_CLASS = 'compact-style';
1414
1415  var NNB = NGIN.NetworkNavBar = function(selector, config) {
1416    this.dropDownStyle = config.displayStyle === 'drop_down_menu'
1417    this.$el = $(selector);
1418    this.el = this.$el[0];
1419    this.$title = this.$el.find('.theme-network-bar-title');
1420    this.$sites = this.$el.find('.theme-network-bar-sites').scrollGuard();
1421    this.$body = $('body');
1422    this.$doc = $(document);
1423    this.bindEvents();
1424  }
1425
1426  $.extend(NNB.prototype, {
1427
1428    bindEvents: function() {
1429      $(window).on('resize', this.resize.bind(this));
1430      setTimeout(this.resize.bind(this), 0);
1431    },
1432
1433    renderDropDown: function() {
1434      this.$el.addClass(COMPACT_CLASS);
1435      this.$sites.detach();
1436      this.tip = NGIN.Modules.Tip(this.$sites, {
1437        style: 'theme-network-bar-dropdown',
1438        width: 'target',
1439        target: this.$title,
1440        target_anchor: 'bl',
1441        tip_anchor: 'tl',
1442        show_on: ['.theme-network-bar-title', 'click'],
1443        hide_on: [['.theme-network-bar-title', 'click'], [document, 'click'], [window, 'scroll']],
1444      });
1445    },
1446
1447    renderIconList: function() {
1448      this.$sites.detach();
1449      if (this.tip) this.tip.destroy();
1450      this.$sites.insertAfter(this.$title).show();
1451      this.$el.removeClass(COMPACT_CLASS);
1452    },
1453
1454    resize: function() {
1455      this.renderIconList();
1456      var dropDown = this.dropDownStyle || this.el.scrollWidth > this.el.offsetWidth;
1457      if (dropDown) this.renderDropDown();
1458    }
1459
1460  });
1461
1462})(jQuery);
1463;(function($){
1464
1465  var EXPANDED_CLASS = 'network-nav-expanded';
1466
1467  var NNM = NGIN.NetworkNavMenu = function(config) {
1468    this.$title = $(config.title);
1469    this.$sites = $(config.sites);
1470    this.$mobileNav = this.$title.closest('.theme-mobile-nav');
1471
1472    this.$sites.find('.theme-nav-menus').scrollGuard();
1473    this.$sites.detach();
1474
1475    this.bindEvents();
1476  }
1477
1478  $.extend(NNM.prototype, {
1479
1480    bindEvents: function() {
1481      var self = this;
1482      this.$title.on('click', '.theme-nav-back', this.transitionToNetwork.bind(this));
1483      this.$sites.on('click', '.theme-nav-forward', this.transitionToSite.bind(this));
1484      this.$mobileNav.on('SlideNavInitialize', this.moveSites.bind(this));
1485      this.$mobileNav.on('SlideNavClose', (function() {
1486        var timer;
1487        return function() {
1488          clearTimeout(timer);
1489          timer = setTimeout(function() { self.transitionToSite(); }, 200);
1490        };
1491      })());
1492    },
1493
1494    // do this only after the slide nav inits since this gets initialized before the slide nav module
1495    moveSites: function(event, eventData) {
1496      this.$sites.appendTo(eventData.navElement);
1497    },
1498
1499    transitionToSite: function() {
1500      this.$mobileNav.removeClass(EXPANDED_CLASS);
1501    },
1502
1503    transitionToNetwork: function() {
1504      this.$mobileNav.addClass(EXPANDED_CLASS);
1505    }
1506
1507  });
1508
1509})(jQuery);
1510;(function($) {
1511
1512  var $html = $('html');
1513  var htmlClass = 'page-manager-visible';
1514
1515  var PM = NGIN.Modules.PageManager = function(opts) {
1516    this.config = PM.CONFIG || {}
1517    this.opts = opts || {};
1518    this.$pageManager = $('.sn-pageManager');
1519    this.$pageManagerToggle = this.$pageManager.find('.sn-togglePages');
1520    this.$pageManagerContent = $('.sn-structure.sn-sidebarItem');
1521    this.$tabs = $('.sn-nav.sn-nav-tabs li');
1522    this.$tabContent = $('.sn-tabContainer');
1523    this.$pageLinks = $('.sn-pages li a').not('.cmsNavButton a');
1524    this.$editModeDisabled = $('.sn-edit-mode-disabled');
1525    this.$editModeDisabledTip = this.$editModeDisabled.find('.sn-tooltip');
1526    this.bindEvents();
1527  }
1528
1529  PM.config = function(config) {
1530    PM.CONFIG = config;
1531  }
1532
1533  // Use jQuery UI sortable interaction to handle page manager's fixed position.
1534  // Use the sortable callbacks for backward compatability.
1535  PM.sortable = function(elementId, pageId) {
1536    var $el = $('#' + elementId);
1537    var childSelector = '.drag';
1538    var url = '/page/order/' + pageId;
1539    var csrfToken = $('meta[name="csrf-token"]').attr('content');
1540
1541    function updateOrder(event, ui) {
1542      var serializeRule = /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/;
1543      var params = { authenticity_token: csrfToken };
1544
1545      params[elementId + '[]'] = $el.children(childSelector).map(function() {
1546        return (this.id.match(serializeRule) || [])[1];
1547      }).get();
1548
1549      $.ajax({
1550        url: url,
1551        type: 'POST',
1552        data: params,
1553        dataType: 'script'
1554      });
1555    }
1556
1557    $el.sortable({
1558      axis: 'y',
1559      items: childSelector,
1560      update: updateOrder
1561    });
1562  }
1563
1564  PM.prototype.bindEvents = function() {
1565    this.$pageManagerToggle
1566      .on('click', this.toggle.bind(this));
1567    this.$editModeDisabled
1568      .on('mouseover', this.showEditModeDisabledTip.bind(this))
1569      .on('mouseout', this.hideEditModeDisabledTip.bind(this));
1570    this.$tabs
1571      .on('click', this.showTab.bind(this));
1572
1573    // prevent click of links in pages & navigation tab
1574    this.$pageLinks.each(function() {
1575      $(this).replaceWith($('<p>' + this.innerHTML + '</p>'));
1576    });
1577
1578    // decide to show or not based on window size
1579    if (this.config.minWidth) {
1580      this.resize()
1581      $(window).on('resize', this.resize.bind(this));
1582    }
1583  }
1584
1585  PM.prototype.toggle = function() {
1586    var content = this.$pageManagerContent;
1587    var activate = !content.hasClass('active');
1588    content.toggleClass('active', activate);
1589    if (activate) this.resetTabs();
1590  }
1591
1592  PM.prototype.resize = function() {
1593    var visible = $html.hasClass(htmlClass);
1594    var hide = window.innerWidth < this.config.minWidth;
1595    if (hide && visible) $html.removeClass(htmlClass);
1596    else if (!hide && !visible) $html.addClass(htmlClass);
1597  }
1598
1599  PM.prototype.showTab = function(e) {
1600    this.$tabs.removeClass('active');
1601    this.$tabContent.hide();
1602    $(e.currentTarget)
1603      .addClass('active')
1604      .find('.sn-tabContainer').show();
1605  }
1606
1607  PM.prototype.resetTabs = function() {
1608    this.$tabs.removeClass('active').first().addClass('active');
1609    this.$tabContent.hide().first().show();
1610  }
1611
1612  PM.prototype.showEditModeDisabledTip = function() {
1613    this.$editModeDisabledTip.stop().fadeIn();
1614  }
1615
1616  PM.prototype.hideEditModeDisabledTip = function() {
1617    var tip = this.$editModeDisabledTip
1618    tip.stop().fadeOut(200, function() {
1619      tip.css({ opacity : "1" });
1620    });
1621  }
1622
1623})(jQuery);
1624;(function($) {
1625
1626  NGIN.Modules.GamesSlider = {
1627    init: function() {
1628      return new GamesSlider();
1629    }
1630  };
1631
1632  function GamesSlider() {
1633    this.$slider = $('.games-slider');
1634    this.$selected = this.$slider.find('.selected');
1635    this.scrollToSelected();
1636  }
1637
1638  // Set scrollLeft of slider so that selected game is visible
1639  GamesSlider.prototype.scrollToSelected = function() {
1640    if (this.$selected.length) {
1641      var scrollLeft = this.$slider.scrollLeft();
1642
1643      var left = Math.round(
1644        this.$selected.offset().left
1645        - this.$slider.offset().left
1646      );
1647      var leftWidth = left + this.$selected.outerWidth();
1648
1649      // Selected game is off to left
1650      if (left < 0) {
1651        scrollLeft += left;
1652      }
1653      // Selected game is off to right
1654      else if (leftWidth > this.$slider.outerWidth()) {
1655        scrollLeft += leftWidth - this.$slider.outerWidth();
1656      }
1657
1658      if (scrollLeft) this.$slider.scrollLeft(scrollLeft);
1659    }
1660  }
1661
1662})(jQuery);
1663;(function($){
1664
1665  // SHARED VARIABLES
1666
1667  var searchModal;
1668  var searchTerm;
1669  var loadSearchData;
1670
1671  // Kyes that don't change the search value are ignored and include:
1672  // shift, ctrl, alt/opt, cmd, win/cmd, left, up, right, down
1673  var ignoredKeys = [16, 17, 18, 91, 93, 37, 38, 39, 40];
1674
1675  // Some events run on click or certain key press. This check
1676  // will pass if the event was not a key event, or if the key event
1677  // matches the given key code.
1678  function allowKeys(e, code) {
1679    if (e.type.indexOf('key') === -1) return true;
1680    return e.which === code;
1681  }
1682
1683  function ignoredKeyEvent(e) {
1684    return e && e.type.indexOf('key') !== -1 && ignoredKeys.indexOf(e.which) !== -1;
1685  }
1686
1687  // PAGE SEARCH BOX (one or more per page)
1688
1689  var PSB = NGIN.PageSearchBox = function(selector, config) {
1690    this.$el = $(selector);
1691    this.$input = this.$el.find(config.inputElement);
1692    this.$submit = this.$el.find(config.submitElement);
1693    this.$clear = this.$el.find(config.clearElement).hide();
1694    this.bindEvents();
1695  }
1696
1697  $.extend(PSB.prototype, {
1698
1699    bindEvents: function() {
1700      var self = this;
1701      this.$input
1702        .on('focus', this.load.bind(this))
1703        .on('keyup', this.update.bind(this))
1704        .on('keydown', this.submit.bind(this));
1705      this.$submit
1706        .on('click', function(event) {
1707          self.load();
1708          self.submit(event);
1709        });
1710      this.$clear
1711        .on('click', this.clear.bind(this));
1712    },
1713
1714    val: function(query) {
1715      var setting = !!arguments.length;
1716      if (setting) {
1717        this.$clear.toggle(!!query);
1718        return this.$input.val(query);
1719      } else {
1720        return this.$input.val();
1721      }
1722    },
1723
1724    update: function(e) {
1725      if (searchModal && !ignoredKeyEvent(e)) {
1726        var val = this.$input.val();
1727        var empty = !val.trim();
1728        this.$clear.toggle(!empty);
1729        searchModal.update(val);
1730      }
1731    },
1732
1733    submit: function(e) {
1734      if (!searchModal || !allowKeys(e, 13)) return
1735
1736      this.update();
1737      searchModal.show();
1738      e.preventDefault();
1739    },
1740
1741    clear: function(e) {
1742      e.preventDefault();
1743      this.$input.val('');
1744      this.update();
1745    },
1746
1747    load: function() {
1748      if (!loadSearchData) {
1749        loadSearchData = $.ajax('/navigation/page_search')
1750      }
1751    }
1752
1753  });
1754
1755  // PAGE SEARCH MODAL (only one per page)
1756
1757  var PSM = NGIN.PageSearchModal = function(selector, config) {
1758    if (searchModal) return searchModal;
1759
1760    searchModal = this;
1761
1762    this.$el = $(selector).appendTo('body');
1763    this.$results = this.$el.find(config.resultsElement).scrollGuard();
1764    this.$status = this.$el.find(config.statusElement);
1765    this.$spinner = this.$el.find(config.spinnerElement);
1766    this.$close = this.$el.find(config.closeElement);
1767
1768    this.bindEvents(config);
1769
1770    this.searchBox = new PSB(this.$el, {
1771      inputElement: config.inputElement,
1772      clearElement: config.clearElement
1773    });
1774  }
1775
1776  $.extend(PSM.prototype, {
1777
1778    sortBy: 'page_title',
1779
1780    resultTmpl: function(item) {
1781      return '<div class="theme-search-result" data-url="/page/nav_links_redirect/' + item.page_node_id + '">' +
1782        '<div class="theme-search-result-info">' +
1783          '<span class="theme-search-result-primary">' + item.page_title + '</span>' +
1784          '<span class="theme-search-result-secondary">' + item.parent_button_title + '</span>' +
1785        '</div>' +
1786      '</div>';
1787    },
1788
1789    bindEvents: function(config) {
1790      this.$el.on('click', this.clickToHide.bind(this));
1791      this.$close.on('click', this.clickToHide.bind(this));
1792      this.$el.on('click', '[data-url]', this.navigate.bind(this));
1793      $(document).on('keydown', this.hide.bind(this));
1794    },
1795
1796    show: function() {
1797      this.visible = true;
1798      this.render();
1799      this.$el.fadeIn();
1800      this.searchBox.$input.focus();
1801    },
1802
1803    clickToHide: function(e) {
1804      if (e.target === e.currentTarget) this.hide(e);
1805    },
1806
1807    navigate: function(e) {
1808      var url = $(e.currentTarget).data('url');
1809      if (url) $.get(url);
1810    },
1811
1812    hide: function(e) {
1813      if (!allowKeys(e, 27)) return;
1814      this.visible = false;
1815      this.$el.fadeOut();
1816    },
1817
1818    update: function(query) {
1819      this.query = query;
1820      this.searchBox.val(query);
1821      if (this.visible) this.render();
1822    },
1823
1824    filteredResults: function(data) {
1825      var query = this.query && this.query.trim();
1826      if (!query) return data;
1827
1828      var sortBy = this.sortBy;
1829      var queryRx = new RegExp(query, 'i');
1830
1831      return data.filter(function(item) {
1832        return queryRx.test(item[sortBy]);
1833      });
1834    },
1835
1836    setStatus: function(matches, total) {
1837      var statusText = matches ? 'Showing ' + matches + ' of ' + total : 'No results.';
1838      this.$status.text(statusText);
1839    },
1840
1841    render: function() {
1842      var search = this;
1843      if (search.waitingToRender) return;
1844
1845      search.$results.empty().append(search.$spinner);
1846      search.waitingToRender = true;
1847
1848      loadSearchData.then(function(data) {
1849        var results = data.result || [];
1850        var filteredResults = search.filteredResults(results);
1851        var markup = filteredResults.map(search.resultTmpl).join('');
1852
1853        search.waitingToRender = false;
1854        search.setStatus(filteredResults.length, results.length);
1855        search.$results
1856          .empty()
1857          .append(this.$status)
1858          .append(markup);
1859      })
1860    }
1861
1862  });
1863
1864})(jQuery);
1865;(function($){
1866
1867  var searchModal
1868
1869  // SEARCH BOX
1870
1871  var GSB = NGIN.GoogleSearchBox = function(selector, config) {
1872    this.$el = $(selector);
1873    this.$input = this.$el.find(config.inputElement);
1874    this.$submit = this.$el.find(config.submitElement);
1875    this.$clear = this.$el.find(config.clearElement).hide();
1876    this.bindEvents();
1877  }
1878
1879  $.extend(GSB.prototype, {
1880
1881    bindEvents: function() {
1882      this.$input
1883        .on('keydown', this.submitIfEnter.bind(this))
1884        .on('keyup', this.update.bind(this));
1885      this.$submit.on('click', this.submit.bind(this));
1886      this.$clear.on('click', this.clear.bind(this));
1887    },
1888
1889    update: function() {
1890      var val = this.$input.val().trim();
1891      this.$clear.toggle(!!val);
1892    },
1893
1894    submit: function(e) {
1895      if (!searchModal) return
1896      var api = searchModal.api();
1897      var query = this.$input.val().trim();
1898
1899      e.preventDefault();
1900      e.stopPropagation();
1901
1902      if (api && query) {
1903        api.execute(query);
1904        this.showModal();
1905      }
1906    },
1907
1908    clear: function(e) {
1909      e.preventDefault();
1910      this.$input.val('');
1911    },
1912
1913    submitIfEnter: function(e) {
1914      if (e.which == 13) this.submit(e);
1915    },
1916
1917    showModal: function() {
1918      if (searchModal) searchModal.show();
1919    },
1920
1921    hideModal: function() {
1922      if (searchModal) searchModal.hide();
1923    }
1924
1925  });
1926
1927  // SEARCH MODAL
1928
1929  var GSM = NGIN.GoogleSearchModal = function(selector, config) {
1930    if (searchModal) return searchModal;
1931    searchModal = this;
1932    this.$el = $(selector);
1933    this.$close = this.$el.find(config.closeElement);
1934    this.bindEvents();
1935  }
1936
1937  $.extend(GSM.prototype, {
1938
1939    api: function() {
1940      return google.search.cse.element.getElement('GoogleSearch');
1941    },
1942
1943    bindEvents: function() {
1944      this.$close.on('click', this.hide.bind(this));
1945      this.$el.on('click', this.clickToHide.bind(this));
1946    },
1947
1948    show: function() {
1949      this.$el.fadeIn();
1950    },
1951
1952    hide: function() {
1953      var api = searchModal.api();
1954      if (api) api.clearAllResults();
1955      this.$el.fadeOut();
1956    },
1957
1958
1959    clickToHide: function(e) {
1960      if (e.target === e.currentTarget) this.hide(e);
1961    }
1962
1963  });
1964
1965})(jQuery);
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975

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.