1const btn = document.getElementById('menu-btn') 2 3btn.addEventListener('click', navToggle) 4 5function navToggle() { 6 btn.classList.toggle('open') 7} 8 9function data() { 10 return { 11 sortBy: "", 12 sortAsc: false, 13 sortByColumn($event) { 14 if (this.sortBy === $event.target.innerText) { 15 if (this.sortAsc) { 16 this.sortBy = ""; 17 this.sortAsc = false; 18 } else { 19 this.sortAsc = !this.sortAsc; 20 } 21 } else { 22 this.sortBy = $event.target.innerText; 23 } 24 25 let rows = this.getTableRows() 26 .sort( 27 this.sortCallback( 28 Array.from($event.target.parentNode.children).indexOf( 29 $event.target 30 ) 31 ) 32 ) 33 .forEach((tr) => { 34 this.$refs.tbody.appendChild(tr); 35 }); 36 }, 37 getTableRows() { 38 return Array.from(this.$refs.tbody.querySelectorAll("tr")); 39 }, 40 getCellValue(row, index) { 41 return row.children[index].innerText; 42 }, 43 sortCallback(index) { 44 return (a, b) => 45 ((row1, row2) => { 46 return row1 !== "" && 47 row2 !== "" && 48 !isNaN(row1) && 49 !isNaN(row2) 50 ? row1 - row2 51 : row1.toString().localeCompare(row2); 52 })( 53 this.getCellValue(this.sortAsc ? a : b, index), 54 this.getCellValue(this.sortAsc ? b : a, index) 55 ); 56 } 57 }; 58} 59 60// User Menu Shrink 61 62 function navShrink() { 63 return { 64 scale: 1, 65 padding: 1, 66 fontScale: 1, 67 get navStyle() { 68 return window.innerWidth >= 768 69 ? `padding-top:${this.padding}rem;padding-bottom:${this.padding}rem;` 70 : ''; 71 }, 72 get logoStyle() { 73 return window.innerWidth >= 768 74 ? `transform:scale(${this.scale});transform-origin:center;` 75 : ''; 76 }, 77 get linkStyle() { 78 return window.innerWidth >= 768 79 ? `transform:scale(${this.fontScale});transform-origin:center;` 80 : ''; 81 }, 82 init() { 83 const maxScroll = 150; 84 const minScale = 0.70; 85 const minPadding = 0; // rem 86 const maxPadding = 1; // rem 87 const minFontScale = 0.95; 88 89 const update = () => { 90 if (window.innerWidth < 768) return; 91 92 const scrollY = Math.min(window.scrollY, maxScroll); 93 const progress = scrollY / maxScroll; 94 95 // interpolate sizes 96 this.scale = 1 - (1 - minScale) * progress; 97 this.padding = maxPadding - (maxPadding - minPadding) * progress; 98 this.fontScale = 1 - (1 - minFontScale) * progress; 99 }; 100 101 // run once 102 update(); 103 104 window.addEventListener('scroll', () => { 105 requestAnimationFrame(update); 106 }, { passive: true }); 107 108 window.addEventListener('resize', update); 109 } 110 } 111 } 112 113 114// User Menu Listener & Overflow 115 116 function navMenu() { 117 return { 118 mobileOpen: false, 119 openMenu: null, 120 overflowItems: [], 121 init() { 122 this.handleOverflow(); 123 this.setupResizeObserver(); 124 window.addEventListener('resize', () => { 125 this.handleOverflow(); 126 this.openMenu = null; 127 if(window.innerWidth < 768) this.mobileOpen = false; 128 }); 129 }, 130 131 setupResizeObserver() { 132 const menu = document.getElementById('user-menu'); 133 if (!menu) return; 134 let scheduled = false; 135 this.resizeObserver = new ResizeObserver(() => { 136 if (scheduled) return; 137 scheduled = true; 138 requestAnimationFrame(() => { 139 this.handleOverflow(); 140 scheduled = false; 141 }); 142 }); 143 this.resizeObserver.observe(menu); 144 }, 145 146 handleOverflow() { 147 const menu = document.getElementById('user-menu'); 148 if (!menu) return; 149 const moreMenu = document.getElementById('moreMenuButton'); 150 if (!moreMenu) return; 151 152 const items = Array.from(menu.children).filter(li => li.id !== 'moreMenuLi'); 153 const availableWidth = menu.clientWidth; 154 let usedWidth = 0; 155 const moreWidth = moreMenu.getBoundingClientRect().width || 80; 156 157 this.overflowItems = [];
158 items.forEach(i => i.style.display = 'inline-flex'); 159 160 let lastVisibleIndex = items.length; 161 162 for(let i=0; i < items.length; i++) { 163 const itemWidth = items[i].getBoundingClientRect().width; 164 usedWidth += itemWidth; 165 if(usedWidth + moreWidth > availableWidth) { 166 lastVisibleIndex = i; 167 break; 168 } 169 } 170 171 if(lastVisibleIndex < items.length) { 172 for(let i = lastVisibleIndex; i < items.length; i++) { 173 this.overflowItems.push(items[i].outerHTML); 174 items[i].style.display = 'none'; 175 } 176 document.getElementById('moreMenuLi').style.display = 'inline-flex'; 177 } else { 178 document.getElementById('moreMenuLi').style.display = 'none'; 179 } 180 }, 181 182 watch: { 183 mobileOpen(value) { 184 document.body.style.overflow = value ? 'hidden' : ''; 185 } 186 }, 187 188 toggleMobileMenu() { 189 this.mobileOpen = !this.mobileOpen; 190 this.openMenu = null; 191 }, 192 193 toggleSubmenu(name) { 194 if (this.openMenu === name) { 195 this.openMenu = null; 196 } else { 197 this.openMenu = name; 198 } 199 }, 200 201 closeMenus() { 202 this.openMenu = null; 203 } 204 } 205 } 206 207// Image Carousel-Lightbox Script 208 209function imageCarousel() { 210 return { 211 current: 0, 212 lightboxOpen: false, 213 activeImage: '', 214 activeTitle: '', 215 slides: [], 216 217 init() { 218 this.slides = Array.from(this.$refs.source.children).map(el => ({ 219 thumb: el.dataset.thumb, 220 title: el.dataset.title 221 })); 222 }, 223 224 next() { 225 if (this.current < this.slides.length - 1) this.current++; 226 }, 227 prev() { 228 if (this.current > 0) this.current--; 229 }, 230 goTo(i) { 231 this.current = i; 232 }, 233 234 openLightbox(slide) { 235 this.activeImage = slide.thumb; 236 this.activeTitle = slide.title; 237 this.lightboxOpen = true; 238 }, 239 closeLightbox() { 240 this.lightboxOpen = false; 241 this.activeImage = ''; 242 this.activeTitle = ''; 243 }, 244 245 swipeStartX: null, 246 swipeDeltaX: null, 247 startSwipe(e) { this.swipeStartX = e.clientX; }, 248 trackSwipe(e) { 249 if (this.swipeStartX !== null) this.swipeDeltaX = e.clientX - this.swipeStartX; 250 }, 251 endSwipe() { 252 if (this.swipeDeltaX > 50) this.prev(); 253 else if (this.swipeDeltaX < -50) this.next(); 254 this.cancelSwipe(); 255 }, 256 cancelSwipe() { 257 this.swipeStartX = null; 258 this.swipeDeltaX = null; 259 } 260 }; 261} 262 263 264// Video Carousel-Lightbox Script 265 266function vidCarousel() { 267 return { 268 current: 0, 269 lightboxOpen: false, 270 activeVideo: null, 271 embedUrl: '', 272 slides: [], 273 274 async init() { 275 this.slides = Array.from(document.querySelectorAll('[data-type]')); 276 277 // Fetch thumbnails 278 for (let el of this.slides) { 279 if (el.dataset.type === 'youtube') { 280 el.dataset.thumb = `https://img.youtube.com/vi/${el.dataset.id}/hqdefault.jpg`; 281 } else if (el.dataset.type === 'vimeo') { 282 try { 283 const res = await fetch(`https://vimeo.com/api/oembed.json?url=https://vimeo.com/${el.dataset.id}`); 284 const data = await res.json(); 285 el.dataset.thumb = data.thumbnail_url; 286 } catch { 287 el.dataset.thumb = `https://vumbnail.com/${el.dataset.id}.jpg`; 288 } 289 } 290 }; 291 }, 292 293 next() { 294 if (this.current < this.slides.length - 1) this.current++; 295 }, 296 prev() { 297 if (this.current > 0) this.current--; 298 }, 299 goTo(i) { 300 this.current = i; 301 }, 302 303 openLightbox(dataset) { 304 this.activeVideo = dataset; 305 if (dataset.type === 'youtube') { 306 this.embedUrl = `https://www.youtube.com/embed/${dataset.id}?autoplay=1`; 307 } else if (dataset.type === 'vimeo') { 308 this.embedUrl = `https://player.vimeo.com/video/${dataset.id}?autoplay=1`; 309 } 310 this.lightboxOpen = true; 311 }, 312 313 closeLightbox() { 314 this.lightboxOpen = false; 315 this.activeVideo = null; 316 this.embedUrl = ''; 317 }, 318 319 swipeStartX: null, 320 swipeDeltaX: null, 321 startSwipe(e) { this.swipeStartX = e.clientX; }, 322 trackSwipe(e) { 323 if (this.swipeStartX !== null) this.swipeDeltaX = e.clientX - this.swipeStartX; 324 }, 325 endSwipe() { 326 if (this.swipeDeltaX > 50) this.prev(); 327 else if (this.swipeDeltaX < -50) this.next(); 328 this.cancelSwipe(); 329 }, 330 cancelSwipe() { 331 this.swipeStartX = null; 332 this.swipeDeltaX = null; 333 } 334 }; 335} 336 337 338// Game Ticker 339 340function carousel() { 341 return { 342 currentIndex: 0, 343 totalCards: 0, 344 cardWidth: 0, 345 gap: 0, 346 step: 0, 347 visibleCards: 1, 348 349 init() { 350 this.syncFromDOM(); 351 window.addEventListener('resize', () => this.syncFromDOM()); 352 requestAnimationFrame(() => this.syncFromDOM()); 353 setTimeout(() => this.syncFromDOM(), 150); 354 }, 355 356 syncFromDOM() { 357 const track = this.$refs.track; 358 const viewport = this.$refs.viewport; 359 if (!track || !viewport || track.children.length === 0) return; 360 361 this.totalCards = track.children.length; 362 363 const firstCard = track.children[0]; 364 const rect = firstCard.getBoundingClientRect(); 365 const styles = getComputedStyle(firstCard); 366 this.cardWidth = rect.width; 367 this.gap = parseFloat(styles.marginRight || '0') || 0; 368 this.step = this.cardWidth + this.gap; 369 370 const viewportWidth = viewport.clientWidth; 371 this.visibleCards = Math.min( 372 Math.max(1, Math.floor((viewportWidth + this.gap) / this.step)), 373 this.totalCards 374 ); 375 376 const lastIndex = Math.max(0, this.totalCards - this.visibleCards);
377 if (this.currentIndex > lastIndex) this.currentIndex = lastIndex; 378 }, 379 380 next() { 381 const lastIndex = Math.max(0, this.totalCards - this.visibleCards); 382 if (this.currentIndex < lastIndex) { 383 this.currentIndex++; 384 } else { 385 this.currentIndex = 0; 386 } 387 }, 388 389 prev() { 390 const lastIndex = Math.max(0, this.totalCards - this.visibleCards); 391 if (this.currentIndex > 0) { 392 this.currentIndex--; 393 } else { 394 this.currentIndex = lastIndex; 395 } 396 } 397 }; 398 } 399 400// Game Ticker V2 - native momentum scroller (smooth paged arrows, mouse drag, 401// touch swipe via native overflow scrolling, scroll-snap). Modelled on the 402// old MSA4 slick slider feel. 403function gameTickerScroller() { 404 return { 405 atStart: true, 406 atEnd: false, 407 hasOverflow: false, 408 409 init() { 410 const vp = this.$refs.viewport; 411 if (!vp) return; 412 413 vp.addEventListener('scroll', () => this.update(), { passive: true }); 414 window.addEventListener('resize', () => this.update()); 415 this.update(); 416 requestAnimationFrame(() => this.update()); 417 setTimeout(() => this.update(), 150); 418 419 this.initDrag(vp); 420 }, 421 422 update() { 423 const vp = this.$refs.viewport; 424 if (!vp) return; 425 const max = vp.scrollWidth - vp.clientWidth; 426 this.hasOverflow = max > 1; 427 this.atStart = vp.scrollLeft <= 1; 428 this.atEnd = vp.scrollLeft >= max - 1; 429 }, 430 431 // Slide a full page of visible cards (slick slidesToScroll behavior) 432 page(dir) { 433 const vp = this.$refs.viewport; 434 if (!vp) return; 435 const card = vp.querySelector('.game-ticker-card'); 436 const step = card ? card.getBoundingClientRect().width : 320; 437 const cardsPerPage = Math.max(1, Math.floor(vp.clientWidth / step)); 438 vp.scrollBy({ left: dir * cardsPerPage * step, behavior: 'smooth' }); 439 }, 440 441 // Desktop mouse drag-to-scroll (touch devices scroll natively). 442 // Scrolls ONLY while the left button is held; releasing anywhere ends the 443 // drag immediately. Pointer capture keeps move/up events flowing to the 444 // viewport even when the cursor leaves it, so the drag can never get stuck. 445 initDrag(vp) { 446 let isDown = false, startX = 0, startLeft = 0, moved = false; 447 448 const endDrag = () => { 449 isDown = false; 450 vp.classList.remove('dragging'); 451 }; 452 453 // The cards are covered by anchor overlays - block native link/image 454 // dragging which would hijack the pointer stream mid-drag 455 vp.addEventListener('dragstart', (e) => e.preventDefault()); 456 457 vp.addEventListener('pointerdown', (e) => { 458 if (e.pointerType !== 'mouse' || e.button !== 0) return; 459 isDown = true; 460 moved = false; 461 startX = e.clientX; 462 startLeft = vp.scrollLeft; 463 // No capture/preventDefault here - a clean stationary click must 464 // reach the card links; the drag only starts once the mouse moves 465 }); 466 467 vp.addEventListener('pointermove', (e) => { 468 if (!isDown) return; 469 // Button no longer held (released outside, lost focus, etc.) - stop 470 if ((e.buttons & 1) !== 1) { 471 endDrag(); 472 return; 473 } 474 const dx = e.clientX - startX; 475 if (!moved && Math.abs(dx) > 5) { 476 // Threshold crossed - now it's a drag: capture the pointer so 477 // move/up keep flowing to the viewport even off-element 478 moved = true; 479 vp.setPointerCapture(e.pointerId); 480 vp.classList.add('dragging'); 481 } 482 if (moved) vp.scrollLeft = startLeft - dx; 483 }); 484 485 vp.addEventListener('pointerup', endDrag); 486 vp.addEventListener('pointercancel', endDrag); 487 vp.addEventListener('lostpointercapture', endDrag); 488 489 // Swallow the click that follows a drag so card links don't navigate 490 // (matches the old slick behavior: click-and-scroll never navigates) 491 vp.addEventListener('click', (e) => { 492 if (moved) { 493 e.preventDefault(); 494 e.stopPropagation(); 495 moved = false; 496 } 497 }, true); 498 } 499 }; 500} 501 502// Head to Head Bars 503 504function barStat(teamA, teamB, label, valueA, valueB) { 505 return { 506 teamA, 507 teamB, 508 label, 509 valueA, 510 valueB, 511 widthA: 0, 512 widthB: 0, 513 animate() { 514 setTimeout(() => { 515 this.widthA = this.valueA; 516 this.widthB = this.valueB; 517 }, 200); 518 } 519 } 520 } 521 522// Checkout: SportsPay embedded payment iframe callback (required by CheckoutPage.razor) 523window.__registerCheckoutCallback = function (dotNetRef) { 524 window.__checkoutRef = dotNetRef; 525 window.onEmbedPayResult = function (data) { 526 if (window.__checkoutRef) { 527 window.__checkoutRef.invokeMethodAsync('OnPaymentResult', JSON.stringify(data)); 528 } 529 }; 530};
531window.__checkoutCleanup = function () { 532 delete window.onEmbedPayResult; 533 window.__checkoutRef = null; 534}; 535 536// Calendar subscribe: report the browser's IANA timezone (e.g. "America/Toronto") 537// so .ics feed links can carry the user's timezone as a query param. 538// (required by AssocMasterSchedule.razor) 539window.getBrowserTimeZone = function () { 540 try { 541 return Intl.DateTimeFormat().resolvedOptions().timeZone || ''; 542 } catch (e) { 543 return ''; 544 } 545}; 546 547// Anchor Link Handler 548// <base href="/"> makes #hash links resolve to /#hash instead of /current-page#hash. 549// This intercepts hash-only clicks and handles them as same-page scroll. 550document.addEventListener('click', function (e) { 551 var link = e.target.closest('a[href]'); 552 if (!link) return; 553 var href = link.getAttribute('href'); 554 if (!href || !href.startsWith('#') || href.length < 2) return; 555 e.preventDefault(); 556 var id = href.substring(1); 557 var target = document.getElementById(id) || document.querySelector('[name="' + id.replace(/"/g, '\\"') + '"]'); 558 if (target) { 559 target.scrollIntoView({ behavior: 'smooth' }); 560 } 561 history.replaceState(null, '', window.location.pathname + window.location.search + href); 562}); 563 564// Hero Section Parallax Scrolling 565 566 (function () { 567 var reduce = window.matchMedia('(prefers-reduced-motion: reduce)'); 568 var section = document.querySelector('[data-parallax-section]'); 569 var bg = document.querySelector('[data-parallax]'); 570 if (!section || !bg) return; 571 572 var SPEED = 0.16; // background moves at ~16% of the scroll distance 573 var ticking = false; 574 575 function apply() { 576 ticking = false; 577 if (reduce.matches) { bg.style.transform = 'translate3d(0,0,0)'; return; } 578 var rect = section.getBoundingClientRect(); 579 var blockCenter = rect.top + rect.height / 2; 580 var delta = (window.innerHeight / 2) - blockCenter; // 0 when block is centered 581 bg.style.transform = 'translate3d(0,' + (delta * SPEED).toFixed(2) + 'px,0)'; 582 } 583 function onScroll() { 584 if (!ticking) { ticking = true; requestAnimationFrame(apply); } 585 } 586 587 window.addEventListener('scroll', onScroll, { passive: true }); 588 window.addEventListener('resize', onScroll); 589 if (reduce.addEventListener) reduce.addEventListener('change', apply); 590 apply(); 591 })();
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.