jQuery(document).ready(function($) {
// Update player counts every 5 minutes
function updatePlayerCounts() {
const countElements = document.querySelectorAll('.fh-global-player-count');
if (!countElements.length) return;
fetch(fh_ajax.ajax_url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({
action: 'fh_get_global_player_count',
nonce: fh_ajax.nonce
})
})
.then(response => response.json())
.then(data => {
countElements.forEach(element => {
if (data.success && data.data.count > 0) {
const formattedCount = new Intl.NumberFormat().format(data.data.count);
const lastUpdated = new Date(data.data.timestamp * 1000);
const timeAgo = timeSince(lastUpdated);
element.innerHTML = `
${formattedCount}
${fh_ajax.i18n.players_online}
${fh_ajax.i18n.updated_ago.replace('%s', timeAgo)}
`;
element.classList.remove('error');
} else {
element.innerHTML = `
${data.data?.error || fh_ajax.i18n.unable_to_fetch}
`;
element.classList.add('error');
}
});
})
.catch(error => {
console.error('Error updating player count:', error);
countElements.forEach(element => {
element.innerHTML = `
${fh_ajax.i18n.update_error}
`;
element.classList.add('error');
});
});
}
function timeSince(date) {
const seconds = Math.floor((new Date() - date) / 1000);
let interval = seconds / 31536000;
if (interval > 1) return Math.floor(interval) + ' ' + fh_ajax.i18n.years;
interval = seconds / 2592000;
if (interval > 1) return Math.floor(interval) + ' ' + fh_ajax.i18n.months;
interval = seconds / 86400;
if (interval > 1) return Math.floor(interval) + ' ' + fh_ajax.i18n.days;
interval = seconds / 3600;
if (interval > 1) return Math.floor(interval) + ' ' + fh_ajax.i18n.hours;
interval = seconds / 60;
if (interval > 1) return Math.floor(interval) + ' ' + fh_ajax.i18n.minutes;
return Math.floor(seconds) + ' ' + fh_ajax.i18n.seconds;
}
// Update player counts every 5 minutes
setInterval(updatePlayerCounts, 300000);
// Initial update
document.addEventListener('DOMContentLoaded', updatePlayerCounts);
// Add click handler for manual refresh
document.addEventListener('click', function(e) {
if (e.target.closest('.fh-global-player-count')) {
const element = e.target.closest('.fh-global-player-count');
element.classList.add('updating');
updatePlayerCounts();
setTimeout(() => element.classList.remove('updating'), 1000);
}
});
// Improved copy code functionality
$('.fh-copy-code').on('click touchstart', function(e) {
if (e.type === 'touchstart') {
e.preventDefault();
e.stopPropagation();
}
var $button = $(this);
var code = $button.data('code');
var postId = $button.data('post-id');
var nonce = $button.data('nonce');
var originalText = $button.text();
// Create a temporary input element
var $tempInput = $('');
$('body').append($tempInput);
$tempInput.val(code).select();
try {
// Try to copy using both methods
var successful = document.execCommand('copy');
if (!successful) {
navigator.clipboard.writeText(code).then(function() {
successful = true;
}).catch(function() {
successful = false;
});
}
if (successful) {
// Update button text
$button.text('Copied!');
// Track code copy
$.ajax({
url: fh_ajax.ajax_url,
type: 'POST',
data: {
action: 'track_code_copy',
post_id: postId,
nonce: nonce
}
});
} else {
$button.text('Failed to copy');
}
} catch (err) {
console.error('Failed to copy code: ', err);
$button.text('Failed to copy');
}
// Remove temporary input
$tempInput.remove();
// Reset button text after 2 seconds
setTimeout(function() {
$button.text(originalText);
}, 2000);
});
// Add tooltips for player counts
$('.fh-player-count').hover(
function() {
$(this).append('
Click to refresh count
');
},
function() {
$(this).find('.fh-tooltip').remove();
}
);
// Manual refresh of player count
$('.fh-player-count').on('click', function() {
var $count = $(this);
var postId = $count.closest('.fh-page-card').data('post-id');
$.ajax({
url: fh_ajax.ajax_url,
type: 'POST',
data: {
action: 'get_island_player_count',
post_id: postId,
nonce: fh_ajax.nonce
},
success: function(response) {
if (response.success && response.data.count) {
$count.find('.value').html(' ' + response.data.count.toLocaleString());
}
}
});
});
// Image gallery lightbox (if gallery exists)
const galleryImages = document.querySelectorAll('.fh-island-gallery img');
if (galleryImages.length > 0) {
galleryImages.forEach(image => {
image.addEventListener('click', function() {
const lightbox = document.createElement('div');
lightbox.className = 'fh-lightbox';
lightbox.innerHTML = `
`;
document.body.appendChild(lightbox);
// Close lightbox
lightbox.addEventListener('click', function(e) {
if (e.target === lightbox || e.target.className === 'fh-lightbox-close') {
lightbox.remove();
}
});
});
});
}
// Add smooth scrolling for filter form
const filterForm = document.querySelector('.fh-filter-form');
if (filterForm) {
filterForm.addEventListener('submit', function() {
setTimeout(() => {
window.scrollTo({
top: document.querySelector('.fh-islands-grid').offsetTop - 100,
behavior: 'smooth'
});
}, 100);
});
}
// Add loading state for filter submissions
const filterSubmit = document.querySelector('.fh-filter-submit');
if (filterSubmit) {
filterSubmit.addEventListener('click', function() {
this.classList.add('loading');
this.textContent = 'Filtering...';
});
}
// Add hover effect for island cards
const islandCards = document.querySelectorAll('.fh-island-card');
islandCards.forEach(card => {
card.addEventListener('mouseenter', function() {
this.style.transform = 'translateY(-5px)';
});
card.addEventListener('mouseleave', function() {
this.style.transform = 'translateY(0)';
});
});
// Handle island interactions
const interactionButtons = document.querySelectorAll('.interaction-buttons button');
interactionButtons.forEach(button => {
button.addEventListener('click touchstart', function(e) {
if (e.type === 'touchstart') {
e.preventDefault();
e.stopPropagation();
}
const postId = this.getAttribute('data-post-id');
const nonce = this.getAttribute('data-nonce');
const action = this.classList.contains('like-button') ? 'like' :
(this.classList.contains('dislike-button') ? 'dislike' : 'favorite');
const $button = $(this);
// Disable button during request
$button.prop('disabled', true);
$.ajax({
url: fh_ajax.ajax_url,
type: 'POST',
data: {
action: 'update_island_interaction',
post_id: postId,
interaction: action,
nonce: nonce
},
success: function(response) {
if (response.success) {
// Update button state
$button.toggleClass('active');
// Update count display
const $countElement = $button.find('.count');
const newCount = response.data.count;
$countElement.text(newCount.toLocaleString());
// Update corresponding metric if it's favorites
if (action === 'favorite') {
$('#favorites-count').text(newCount.toLocaleString());
}
// Update other buttons if needed
if (action === 'like') {
$('.dislike-button').removeClass('active');
} else if (action === 'dislike') {
$('.like-button').removeClass('active');
}
}
},
error: function(jqXHR, textStatus, errorThrown) {
console.error('AJAX Request Failed:', textStatus, errorThrown);
},
complete: function() {
// Re-enable button after request
$button.prop('disabled', false);
}
});
});
});
// Add scroll-to-top button
// Create scroll-to-top button
const scrollButton = document.createElement('button');
scrollButton.className = 'scroll-to-top';
scrollButton.innerHTML = '↑';
scrollButton.setAttribute('aria-label', 'Scroll to top');
document.body.appendChild(scrollButton);
// Show/hide scroll button based on scroll position
window.addEventListener('scroll', function() {
if (window.pageYOffset > 300) {
scrollButton.classList.add('visible');
} else {
scrollButton.classList.remove('visible');
}
});
// Scroll to top when clicked
scrollButton.addEventListener('click', function() {
window.scrollTo({
top: 0,
behavior: 'smooth'
});
});
// Add loading state to grids during AJAX operations
const grids = document.querySelectorAll('.fh-islands-grid');
grids.forEach(grid => {
grid.addEventListener('loading', () => {
grid.classList.add('loading');
});
grid.addEventListener('loaded', () => {
grid.classList.remove('loading');
});
});
// Add keyboard navigation support
document.addEventListener('keydown', function(e) {
if (e.key === 'Tab') {
document.body.classList.add('keyboard-navigation');
}
});
document.addEventListener('mousedown', function() {
document.body.classList.remove('keyboard-navigation');
});
// Carousel functionality
const carousels = document.querySelectorAll('.fh-carousel-container');
carousels.forEach(container => {
const carousel = container.querySelector('.fh-carousel');
const prevBtn = container.querySelector('.fh-carousel-prev');
const nextBtn = container.querySelector('.fh-carousel-next');
const cards = carousel.querySelectorAll('.fh-page-card');
if (cards.length === 0) return;
const gap = parseInt(getComputedStyle(carousel).gap);
const cardWidth = cards[0].offsetWidth;
const cardsPerView = window.innerWidth <= 768 ? (window.innerWidth <= 480 ? 1 : 2) : 3;
let currentPosition = 0;
const maxPosition = Math.max(0, cards.length - cardsPerView);
function updateButtons() {
prevBtn.style.display = currentPosition <= 0 ? 'none' : 'flex';
nextBtn.style.display = currentPosition >= maxPosition ? 'none' : 'flex';
}
function moveCarousel() {
const offset = currentPosition * (cardWidth + gap);
carousel.style.transform = `translateX(-${offset}px)`;
updateButtons();
}
prevBtn.addEventListener('click', () => {
if (currentPosition > 0) {
currentPosition--;
moveCarousel();
}
});
nextBtn.addEventListener('click', () => {
if (currentPosition < maxPosition) {
currentPosition++;
moveCarousel();
}
});
// Handle window resize
let resizeTimer;
window.addEventListener('resize', () => {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
const newCardWidth = cards[0].offsetWidth;
const newCardsPerView = window.innerWidth <= 768 ? (window.innerWidth <= 480 ? 1 : 2) : 3;
const newMaxPosition = Math.max(0, cards.length - newCardsPerView);
if (currentPosition > newMaxPosition) {
currentPosition = newMaxPosition;
}
moveCarousel();
}, 250);
});
// Initialize
updateButtons();
moveCarousel();
});
});
// Add focus styles for keyboard navigation
const style = document.createElement('style');
style.textContent = `
.keyboard-navigation *:focus {
outline: 2px solid var(--border-color);
outline-offset: 2px;
}
`;
document.head.appendChild(style);