// Swiss Football Blocks Frontend JavaScript (function($) { 'use strict'; // Team selector functionality for blocks window.swiFootSelectTeam = function(teamId, blockType) { if (!teamId) return; // This could be enhanced to use AJAX for dynamic loading const url = new URL(window.location); url.searchParams.set('swi_foot_team_' + blockType, teamId); window.location.href = url.toString(); }; // Auto-refresh functionality for events shortcodes $(document).ready(function() { initEventRefresh(); setupShortcodeHelpers(); }); /** * Initialize event refresh polling for all match event shortcodes on page * * Finds all `.swi-foot-events[data-match-id][data-refresh]` elements and sets up * automatic polling based on the configured refresh interval. Respects match status * (does not update UI until match starts, stops polling when match ends). * * @returns {void} */ function initEventRefresh() { const $eventContainers = $('.swi-foot-events[data-match-id][data-refresh]'); if ($eventContainers.length === 0) return; $eventContainers.each(function() { const $container = $(this); const matchId = $container.data('match-id'); const interval = parseInt($container.data('refresh')) || 30; if (!matchId) return; // Initial check: determine if match has started and if it should refresh checkMatchStatusAndRefresh($container, matchId, interval); }); } /** * Initialize polling interval for match events * * Sets up a setInterval that periodically fetches the latest events for a match. * Automatically clears the interval when the match ends. * * @param {jQuery} $container - jQuery element for the event container * @param {string} matchId - ID of the match to fetch events for * @param {number} interval - Refresh interval in seconds (e.g., 30) * @returns {void} */ function checkMatchStatusAndRefresh($container, matchId, interval) { fetchMatchEvents($container, matchId); // Set up polling - will stop automatically once match ends const pollInterval = setInterval(function() { const hasEnded = $container.data('match-ended'); // Stop polling if match has ended if (hasEnded) { clearInterval(pollInterval); return; } fetchMatchEvents($container, matchId); }, interval * 1000); } /** * Fetch latest match events from REST API * * Makes an authenticated request to `/wp-json/swi-foot/v1/events/{matchId}` to * retrieve the current list of match events. Updates container data with match * status (started/ended). Only updates the DOM if the match has started. * * @param {jQuery} $container - jQuery element for the event container * @param {string} matchId - Match ID to fetch events for * @returns {void} */ function fetchMatchEvents($container, matchId) { const restUrl = window.swiFootRest ? window.swiFootRest.rest_url : '/wp-json'; const nonce = window.swiFootRest ? window.swiFootRest.rest_nonce : ''; // Get event order from data attribute const eventOrder = $container.data('event-order') || 'dynamic'; // Build URL with event_order parameter let url = restUrl + 'swi-foot/v1/events/' + encodeURIComponent(matchId); if (eventOrder && eventOrder !== 'dynamic') { url += '?event_order=' + encodeURIComponent(eventOrder); } fetch(url, { method: 'GET', credentials: 'same-origin', headers: { 'X-WP-Nonce': nonce } }) .then(function(resp) { if (!resp.ok) throw new Error('Failed to fetch events'); return resp.json(); }) .then(function(data) { if (!data) return; // Track match status for polling control $container.data('match-started', data.hasMatchStarted); $container.data('match-ended', data.hasMatchEnded); // Only update if match has started if (!data.hasMatchStarted) { return; // Don't update UI, match hasn't started yet } // Update events list updateEventsList($container, data.events); }) .catch(function(err) { console.error('Error fetching match events:', err); }); } /** * Update the events timeline display with new events * * Renders an HTML list of match events from the API response, sorted newest first. * Handles empty event lists with a localized message. Updates the `.events-timeline` * element within the container. * * @param {jQuery} $container - jQuery element for the event container * @param {Array} events - Array of event objects with matchMinute, eventTypeName, playerName, teamName * @returns {void} */ function updateEventsList($container, events) { const $timeline = $container.find('.events-timeline'); if ($timeline.length === 0) return; // Build HTML for events let html = ''; if (events && events.length > 0) { events.forEach(function(event) { html += '
'; html += '
' + (event.matchMinute || '') + '\'
'; html += '
'; html += '
' + (event.eventTypeName || '') + '
'; html += '
'; if (event.playerName) { html += '' + event.playerName + ''; } if (event.teamName) { html += '(' + event.teamName + ')'; } html += '
'; }); } else { html = '

' + (window.swiFootNoEvents || 'No events recorded yet.') + '

'; } $timeline.html(html); } function setupShortcodeHelpers() { // Add copy functionality to shortcode examples in admin if ($('.shortcode-group code').length > 0) { $('.shortcode-group code').each(function() { const $code = $(this); $code.css('cursor', 'pointer'); $code.attr('title', 'Click to copy shortcode'); $code.on('click', function() { const shortcode = $(this).text(); copyToClipboard(shortcode); }); }); } } function copyToClipboard(text) { if (navigator.clipboard && window.isSecureContext) { navigator.clipboard.writeText(text).then(function() { showNotification('Shortcode copied to clipboard!', 'success'); }).catch(function() { fallbackCopyTextToClipboard(text); }); } else { fallbackCopyTextToClipboard(text); } } function fallbackCopyTextToClipboard(text) { const textArea = document.createElement('textarea'); textArea.value = text; textArea.style.position = 'fixed'; textArea.style.left = '-999999px'; textArea.style.top = '-999999px'; document.body.appendChild(textArea); textArea.focus(); textArea.select(); try { document.execCommand('copy'); showNotification('Shortcode copied to clipboard!', 'success'); } catch (err) { showNotification('Failed to copy shortcode', 'error'); } document.body.removeChild(textArea); } function showNotification(message, type) { const $notification = $('
' + message + '
'); $notification.css({ position: 'fixed', top: '20px', right: '20px', padding: '10px 15px', borderRadius: '4px', color: '#fff', fontSize: '14px', zIndex: '10000', opacity: '0', transform: 'translateY(-20px)', transition: 'all 0.3s ease' }); if (type === 'success') { $notification.css('backgroundColor', '#28a745'); } else { $notification.css('backgroundColor', '#dc3545'); } $('body').append($notification); // Animate in setTimeout(function() { $notification.css({ opacity: '1', transform: 'translateY(0)' }); }, 100); // Remove after 3 seconds setTimeout(function() { $notification.css({ opacity: '0', transform: 'translateY(-20px)' }); setTimeout(function() { $notification.remove(); }, 300); }, 3000); } // Initialize tooltips for match elements $(document).ready(function() { $('.swi-foot-match-container [title]').each(function() { const $element = $(this); $element.on('mouseenter', function() { // Simple tooltip implementation const title = $element.attr('title'); if (title) { const $tooltip = $('
' + title + '
'); $tooltip.css({ position: 'absolute', background: '#333', color: '#fff', padding: '5px 8px', borderRadius: '4px', fontSize: '12px', zIndex: '1000', whiteSpace: 'nowrap' }); $('body').append($tooltip); const rect = this.getBoundingClientRect(); $tooltip.css({ left: rect.left + (rect.width / 2) - ($tooltip.outerWidth() / 2), top: rect.top - $tooltip.outerHeight() - 5 }); } }).on('mouseleave', function() { $('.swi-foot-tooltip').remove(); }); }); }); })(jQuery);