// Wait for DOM to be ready (function() { 'use strict'; // Ensure scoresConfig is available if (typeof window.scoresConfig === 'undefined') { console.warn('scoresConfig not found'); return; } const config = window.scoresConfig; function datePicker() { return { init() { this.fp = flatpickr(this.$refs.flatpickr, { dateFormat: "Y-m-d", defaultDate: config.selectedDate, onChange: (selectedDates, dateStr) => { const url = new URL(window.location.href); url.searchParams.set('date', dateStr); // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location window.location.href = url.toString(); } }); // Populate search field with the previously entered value // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location const searchParams = new URLSearchParams(window.location.search); const searchValue = searchParams.get('search'); if (searchValue) { document.getElementById('search-input').value = decodeURIComponent(searchValue); } }, openCalendar() { this.fp.open(); }, redirectToDate(url) { // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location window.location.href = url; } } } function searchRedirect(event) { event.preventDefault(); const searchInput = document.getElementById('search-input').value; const url = new URL(window.location.href); url.searchParams.set('search', encodeURIComponent(searchInput)); // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location window.location.href = url.toString(); } function scoreViewToggle() { return { view: localStorage.getItem('scoresView') || 'grid', changeView(view) { this.view = view; localStorage.setItem('scoresView', view); }, loadEditGameForm(gameId) { const container = document.getElementById('edit-game-form-container'); if (!container) return; // Show loading state container.innerHTML = '
Loading...
'; // Load the form via AJAX fetch(config.ajaxUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: new URLSearchParams({ action: 'load_edit_game_form', game_id: gameId, nonce: config.loadEditGameFormNonce }) }) .then(response => response.text()) .then(html => { container.innerHTML = html; if (typeof Alpine !== 'undefined') { Alpine.initTree(container); } }) .catch(error => { console.error('Error loading edit game form:', error); container.innerHTML = '
Error loading form. Please try again.
'; }); } } } function scoresData() { return { ...scoreViewToggle(), showGameModal: null, filters: { class: config.urlFilters.class || [], conference: config.urlFilters.conference || [], section: config.urlFilters.section || [], district: config.urlFilters.district || [], hasStream: config.urlFilters.hasStream || false, isTop25Matchup: config.urlFilters.isTop25Matchup || false }, init() { this.view = localStorage.getItem('scoresView') || 'grid'; this.$watch('showGameModal', (gameId) => { if (gameId) { loadEditGameForm(gameId); } }); // Watch filters and apply filtering + sorting this.$watch('filters', () => { this.filterAndSortGames(); this.updateURL(); }, { deep: true }); // Handle browser back/forward buttons window.addEventListener('popstate', () => { this.loadFiltersFromURL(); }); // Initial sort on page load this.$nextTick(() => { this.filterAndSortGames(); }); }, loadFiltersFromURL() { // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location const url = new URL(window.location.href); this.filters = { class: url.searchParams.getAll('class'), conference: url.searchParams.getAll('conference'), section: url.searchParams.getAll('section'), district: url.searchParams.getAll('district'), hasStream: url.searchParams.get('stream') === '1', isTop25Matchup: url.searchParams.get('top25matchup') === '1' }; }, updateURL() { // Safe internal redirect to same domain // eslint-disable-next-line WordPressVIPMinimum.JS.Window.location const url = new URL(window.location.href); // Remove existing filter params ['class', 'conference', 'section', 'district', 'stream', 'top25matchup'].forEach(key => { url.searchParams.delete(key); }); // Add current filter values Object.keys(this.filters).forEach(key => { if (key === 'hasStream') { // Add stream param if checkbox is checked if (this.filters.hasStream) { url.searchParams.set('stream', '1'); } } else if (key === 'isTop25Matchup') { // Add top25matchup param if checkbox is checked if (this.filters.isTop25Matchup) { url.searchParams.set('top25matchup', '1'); } } else if (this.filters[key].length > 0) { this.filters[key].forEach(value => { // Safe: appending URL parameters, not injecting HTML // eslint-disable-next-line WordPressVIPMinimum.JS.HTMLExecutingFunctions.append url.searchParams.append(key, value); }); } }); // Update URL without page reload window.history.pushState({}, '', url); }, hasActiveFilters() { return this.filters.class.length > 0 || this.filters.conference.length > 0 || this.filters.section.length > 0 || this.filters.district.length > 0 || this.filters.hasStream || this.filters.isTop25Matchup; }, clearFilters() { this.filters = { class: [], conference: [], section: [], district: [], hasStream: false, isTop25Matchup: false }; // URL will be updated by the watcher }, filterAndSortGames() { const gridContainer = document.querySelector('#scores-grid'); const tableBody = document.querySelector('#scores-table tbody'); const container = gridContainer || tableBody; if (!container) return; const gameElements = Array.from(container.children).filter(el => el.classList.contains('game-row') || el.classList.contains('game-card') ); // Filter games gameElements.forEach(gameEl => { const shouldShow = this.shouldShowGame(gameEl); gameEl.style.display = shouldShow ? '' : 'none'; }); // Sort visible games: Top 25 matchups first, then single Top 25 team, then others const visibleGames = gameElements.filter(el => el.style.display !== 'none'); visibleGames.sort((a, b) => { const aTop25Matchup = a.dataset.top25Matchup === 'true'; const bTop25Matchup = b.dataset.top25Matchup === 'true'; const aHasTop25 = a.dataset.hasTop25 === 'true'; const bHasTop25 = b.dataset.hasTop25 === 'true'; // Both are Top 25 matchups or neither - maintain order if (aTop25Matchup && bTop25Matchup) return 0; if (!aTop25Matchup && !bTop25Matchup) { // Check single Top 25 team if (aHasTop25 && bHasTop25) return 0; if (aHasTop25) return -1; if (bHasTop25) return 1; return 0; } // One is Top 25 matchup return aTop25Matchup ? -1 : 1; }); // Re-append in sorted order visibleGames.forEach(el => container.appendChild(el)); // Update game count this.updateGameCount(visibleGames.length); }, shouldShowGame(gameEl) { // If no filters active, show all games if (!this.hasActiveFilters()) return true; // Helper function to check if a filter matches home/away values const matchesFilter = (filterType) => { const filterValues = this.filters[filterType]; if (filterValues.length === 0) return true; const homeKey = `home${filterType.charAt(0).toUpperCase() + filterType.slice(1)}`; const awayKey = `away${filterType.charAt(0).toUpperCase() + filterType.slice(1)}`; return filterValues.some(value => { const homeValue = gameEl.dataset[homeKey] || ''; const awayValue = gameEl.dataset[awayKey] || ''; return homeValue === value || awayValue === value; }); }; // Check each filter type const classMatch = matchesFilter('class'); const conferenceMatch = matchesFilter('conference'); const sectionMatch = matchesFilter('section'); const districtMatch = matchesFilter('district'); // Check stream filter const streamMatch = !this.filters.hasStream || gameEl.dataset.hasStream === 'true'; // Check Top 25 Matchup filter const top25MatchupMatch = !this.filters.isTop25Matchup || gameEl.dataset.top25Matchup === 'true'; return classMatch && conferenceMatch && sectionMatch && districtMatch && streamMatch && top25MatchupMatch; }, updateGameCount(count) { const countDisplay = document.querySelector('.game-count-display'); if (countDisplay) { countDisplay.textContent = `${count} ${count === 1 ? 'game' : 'games'}`; } } } } function loadEditGameForm(gameId) { const container = document.getElementById('edit-game-form-container'); console.log('container', container); if (!container) return; // Prevent duplicate calls if (window.loadingForm) return; window.loadingForm = true; // Show loading state container.innerHTML = '
Loading...
'; const formData = new URLSearchParams({ action: 'loadEditGameForm', game_id: gameId, nonce: config.loadEditGameFormNonce, // Safe: passing URL as parameter, not injecting HTML // eslint-disable-next-line WordPressVIPMinimum.JS.HTMLExecutingFunctions.append current_url: window.location.href }); fetch(config.ajaxUrl, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, body: formData }) .then(response => response.text()) .then(html => { // Safe: setting innerHTML, not injecting HTML // eslint-disable-next-line WordPressVIPMinimum.JS.InnerHTML.Found container.innerHTML = html; // Initialize ACF form after AJAX load if (typeof acf !== 'undefined') { const $form = jQuery(container).find('.acf-form'); if ($form.length) { acf.doAction('append', $form); setTimeout(() => { acf.doAction('ready', $form); acf.doAction('show_field', $form); }, 100); } } window.loadingForm = false; }) .catch(error => { console.error('Error loading edit game form:', error); container.innerHTML = '
Error loading form. Please try again.
'; window.loadingForm = false; }); } function gameFormData() { return { confirmDelete: false, successMessage: null, loading: false, submitting: false, handleFormSubmit(event) { if (this.submitting) { event.preventDefault(); return; } this.submitting = true; const submitButton = event.target.querySelector('input[type="submit"]'); if (submitButton) { submitButton.disabled = true; submitButton.value = 'Updating...'; submitButton.classList.add('opacity-50', 'cursor-not-allowed'); } } }; } function deleteGame(gameID, state) { state.confirmDelete = false; state.loading = true; state.successMessage = null; fetch(`${window.wpApiSettings.root}wp/v2/games/${gameID}?force=false`, { method: 'DELETE', headers: { 'X-WP-Nonce': window.wpApiSettings.nonce, 'Content-Type': 'application/json', }, }) .then(res => res.json()) .then(data => { if (data.deleted || data.status === 'trash') { state.successMessage = 'Game deleted successfully, refreshing page...'; setTimeout(() => { window.location.reload(); }, 1000); } else { alert(data.data || 'Could not trash game.'); state.loading = false; } }) .catch(() => { alert('Request failed.'); state.loading = false; }); } // Make functions globally available for Alpine.js window.datePicker = datePicker; window.searchRedirect = searchRedirect; window.scoreViewToggle = scoreViewToggle; window.scoresData = scoresData; window.loadEditGameForm = loadEditGameForm; window.gameFormData = gameFormData; window.deleteGame = deleteGame; })(); ; const prevIcon = ''; const nextIcon = ''; function featureSlider() { return { sliderInitialized: false, sliderInView: false, initSlider() { const self = this; // Store reference for use in callbacks // Use IntersectionObserver to detect when slider container is in view const sliderContainer = document.querySelector('.feature-slider'); if (!sliderContainer) return; const sliderObserver = new IntersectionObserver((entries) => { if (entries[0].isIntersecting && !self.sliderInView) { self.sliderInView = true; // If slider is already initialized, load images immediately if (self.sliderInitialized) { self.lazyLoadVisibleSlides(); } } }, { rootMargin: '300px', // Start loading well before slider is visible threshold: 0 }); sliderObserver.observe(sliderContainer); Alpine.nextTick(() => { const $slider = jQuery('.feature-slider'); $slider.slick({ arrows: true, prevArrow: '', nextArrow: '', autoplay: true, autoplaySpeed: 6000, infinite: false, speed: 300, slidesToShow: 2.5, slidesToScroll: 1, responsive: [ { breakpoint: 1024, settings: { slidesToShow: 2, slidesToScroll: 1, } }, { breakpoint: 768, settings: { slidesToShow: 1, slidesToScroll: 1, } } ] }); self.sliderInitialized = true; // Load images immediately if slider is already in view if (self.sliderInView) { self.lazyLoadVisibleSlides(); } // Load images when slider initializes $slider.on('init', function () { self.lazyLoadVisibleSlides(); }); // Preload images BEFORE slide change to ensure they're ready $slider.on('beforeChange', function () { self.lazyLoadVisibleSlides(); }); // Also load on afterChange as backup $slider.on('afterChange', function () { self.lazyLoadVisibleSlides(); }); }); }, lazyLoadVisibleSlides() { const $slider = jQuery('.feature-slider'); if (!$slider.length || !$slider.slick('getSlick')) return; try { const currentIndex = $slider.slick('slickCurrentSlide') || 0; const slidesToShow = $slider.slick('getSlick').options.slidesToShow || 1; const totalSlides = $slider.slick('getSlick').slideCount || 0; // Load images for visible slides plus 3 ahead for aggressive preloading const slidesToLoad = Math.ceil(slidesToShow) + 3; for (let i = 0; i < slidesToLoad; i++) { const slideIndex = currentIndex + i; if (slideIndex >= totalSlides) break; const slideElement = $slider.find('[data-slide-index="' + slideIndex + '"]').first()[0]; if (!slideElement) continue; // Access Alpine component data to trigger lazy loading if (slideElement._x_dataStack && slideElement._x_dataStack.length > 0) { const alpineData = slideElement._x_dataStack[0]; if (alpineData && typeof alpineData.loadBackground === 'function' && !alpineData.loaded) { alpineData.loadBackground(); } } } } catch (e) { console.warn('Error loading slider images:', e); } } } };