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.