class ThemeManager {
constructor() {
this.currentTheme = localStorage.getItem('theme') || 'dark';
this.themeToggle = document.getElementById('theme-toggle');
this.themeIcon = document.getElementById('theme-icon');
this.init();
}
init() {
this.applyTheme(this.currentTheme);
this.bindEvents();
}
bindEvents() {
if (this.themeToggle) {
this.themeToggle.addEventListener('click', () => {
this.toggleTheme();
});
}
}
toggleTheme() {
this.currentTheme = this.currentTheme === 'dark' ? 'light' : 'dark';
this.applyTheme(this.currentTheme);
localStorage.setItem('theme', this.currentTheme);
}
applyTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
this.updateThemeIcon(theme);
// Trigger theme change event for other components
document.dispatchEvent(new CustomEvent('themeChanged', {
detail: { theme }
}));
}
updateThemeIcon(theme) {
if (!this.themeIcon) return;
if (theme === 'light') {
// Moon icon for light theme (to switch to dark)
this.themeIcon.innerHTML = `
`;
} else {
// Sun icon for dark theme (to switch to light)
this.themeIcon.innerHTML = `
`;
}
}
}
// Login System Functions
function showLoginModal() {
const modal = document.getElementById('login-modal');
modal.classList.add('active');
}
function hideLoginModal() {
const modal = document.getElementById('login-modal');
modal.classList.remove('active');
}
function showResetModal() {
const modal = document.getElementById('reset-modal');
modal.classList.add('active');
}
function hideResetModal() {
const modal = document.getElementById('reset-modal');
modal.classList.remove('active');
}
function sendResetCode() {
const nickname = document.getElementById("login-nickname").value.trim();
if (!nickname) {
showNotification("Введите игровой ник для восстановления.", 'error');
return;
}
fetch("/api/send_reset_code.php?nickname=" + encodeURIComponent(nickname))
.then(res => res.json())
.then(data => {
if (data.success) {
document.getElementById("reset-nickname").value = nickname;
hideLoginModal();
showResetModal();
showNotification("Код отправлен в Telegram!", 'success');
} else {
showNotification(data.message || "Ошибка при отправке кода.", 'error');
}
})
.catch(() => showNotification("Сервер недоступен.", 'error'));
}
function login(nickname, password, remember) {
// Validate inputs
if (!nickname || !password) {
showNotification('Пожалуйста, заполните все поля', 'error');
return;
}
// Create form data
const formData = new FormData();
formData.append('action', 'login');
formData.append('nickname', nickname);
formData.append('password', password);
formData.append('remember', remember ? '1' : '0');
// Show loading notification
showNotification('Выполняется вход...', 'info');
// Send request to server with improved error handling
fetch('family-handler.php', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error('Ошибка сервера: ' + response.status + ' ' + response.statusText);
}
return response.json();
})
.then(data => {
if (data.success) {
hideLoginModal();
showNotification('Вы успешно вошли в систему!', 'success');
// Save user data
if (remember) {
localStorage.setItem('user', JSON.stringify({
nickname: data.nickname,
token: data.token
}));
} else {
sessionStorage.setItem('user', JSON.stringify({
nickname: data.nickname,
token: data.token
}));
}
updateUserInterface(data.nickname);
} else {
showNotification(data.message || 'Ошибка при входе в систему', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('Произошла ошибка при отправке данных: ' + error.message, 'error');
});
}
function logout() {
// Create form data
const formData = new FormData();
formData.append('action', 'logout');
// Send request to server
fetch('family-handler.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('Вы успешно вышли из системы', 'success');
// Clear stored user data
localStorage.removeItem('user');
sessionStorage.removeItem('user');
updateUserInterface(null);
} else {
showNotification(data.message || 'Ошибка при выходе из системы', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showNotification('Произошла ошибка при отправке данных', 'error');
});
}
function updateUserInterface(nickname) {
const loginButton = document.getElementById('login-button');
const userInfo = document.getElementById('user-info');
const userAvatar = document.getElementById('user-avatar');
const userName = document.getElementById('user-name');
if (nickname) {
loginButton.style.display = 'none';
userInfo.style.display = 'flex';
userAvatar.textContent = nickname.charAt(0).toUpperCase();
userName.textContent = nickname;
// Add click event to avatar and username
userAvatar.onclick = function() {
window.location.href = 'user-account.php?nickname=' + encodeURIComponent(nickname);
};
userName.onclick = function() {
window.location.href = 'user-account.php?nickname=' + encodeURIComponent(nickname);
};
} else {
loginButton.style.display = 'flex';
userInfo.style.display = 'none';
}
}
function checkLoggedIn() {
// Check if user is logged in
const user = JSON.parse(localStorage.getItem('user') || sessionStorage.getItem('user') || 'null');
if (user) {
// Verify token with server
const formData = new FormData();
formData.append('action', 'verify_token');
formData.append('nickname', user.nickname);
formData.append('token', user.token);
fetch('family-handler.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
updateUserInterface(user.nickname);
} else {
// Token invalid, clear stored data
localStorage.removeItem('user');
sessionStorage.removeItem('user');
}
})
.catch(error => {
console.error('Error:', error);
});
}
}
document.addEventListener('DOMContentLoaded', function() {
const audio = document.getElementById('background-audio');
audio.volume = 0.06;
// Попытка автозапуска
const playPromise = audio.play();
if (playPromise !== undefined) {
playPromise.catch(() => {
// Если заблокировано, запускаем при клике
document.addEventListener('click', function startAudio() {
audio.play();
document.removeEventListener('click', startAudio);
}, { once: true });
});
}
const video = document.getElementById('background-video');
if (video) {
// Устанавливаем громкость
video.volume = 0.06; // 30% громкости, чтобы не было слишком громко
// Принудительно запускаем видео со звуком
const playPromise = video.play();
if (playPromise !== undefined) {
playPromise.then(() => {
console.log('Видео запущено со звуком');
}).catch(error => {
console.log('Автовоспроизведение заблокировано, пытаемся запустить при первом клике');
// Запускаем при первом взаимодействии пользователя
document.addEventListener('click', function startVideo() {
video.play();
document.removeEventListener('click', startVideo);
}, { once: true });
});
}
}
});
// Mobile menu toggle
function initMobileMenu() {
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
const mobileMenu = document.querySelector('.mobile-menu');
const mobileNavLinks = document.querySelectorAll('.mobile-nav-link');
if (mobileMenuToggle && mobileMenu) {
mobileMenuToggle.addEventListener('click', () => {
mobileMenu.classList.toggle('active');
});
mobileNavLinks.forEach(link => {
link.addEventListener('click', () => {
mobileMenu.classList.remove('active');
});
});
// Close mobile menu when clicking outside
document.addEventListener('click', (e) => {
if (!mobileMenuToggle.contains(e.target) && !mobileMenu.contains(e.target)) {
mobileMenu.classList.remove('active');
}
});
}
}
// Screenshot slider
function initScreenshotSlider() {
const screenshots = document.querySelectorAll('.screenshot-item');
const dots = document.querySelectorAll('.dot');
const prevBtn = document.querySelector('.slider-arrow.prev');
const nextBtn = document.querySelector('.slider-arrow.next');
let currentSlide = 0;
function showSlide(index) {
screenshots.forEach((screenshot, i) => {
screenshot.style.display = i === index ? 'block' : 'none';
});
dots.forEach((dot, i) => {
dot.classList.toggle('active', i === index);
});
}
function nextSlide() {
currentSlide = (currentSlide + 1) % screenshots.length;
showSlide(currentSlide);
}
function prevSlide() {
currentSlide = (currentSlide - 1 + screenshots.length) % screenshots.length;
showSlide(currentSlide);
}
if (nextBtn && prevBtn) {
nextBtn.addEventListener('click', nextSlide);
prevBtn.addEventListener('click', prevSlide);
}
dots.forEach((dot, index) => {
dot.addEventListener('click', () => {
currentSlide = index;
showSlide(currentSlide);
});
});
// Auto-slide
setInterval(nextSlide, 5000);
// Initialize
showSlide(0);
}
// Countdown timer
function initCountdown() {
const targetDate = new Date('2025-06-01T23:59:59').getTime();
function updateCountdown() {
const now = new Date().getTime();
const distance = targetDate - now;
if (distance < 0) {
document.getElementById('countdown-days').textContent = '00';
document.getElementById('countdown-hours').textContent = '00';
document.getElementById('countdown-minutes').textContent = '00';
document.getElementById('countdown-seconds').textContent = '00';
return;
}
const days = Math.floor(distance / (1000 * 60 * 60 * 24));
const hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
document.getElementById('countdown-days').textContent = days.toString().padStart(2, '0');
document.getElementById('countdown-hours').textContent = hours.toString().padStart(2, '0');
document.getElementById('countdown-minutes').textContent = minutes.toString().padStart(2, '0');
document.getElementById('countdown-seconds').textContent = seconds.toString().padStart(2, '0');
}
updateCountdown();
setInterval(updateCountdown, 1000);
}
// Notification system
function showNotification(message, type = 'success') {
// Remove existing notifications
const existingNotifications = document.querySelectorAll('.glass-notification');
existingNotifications.forEach(notification => notification.remove());
const notification = document.createElement('div');
notification.className = `glass-notification ${type}`;
const iconSvg = type === 'success'
? ''
: '';
notification.innerHTML = `
`;
document.body.appendChild(notification);
// Show notification
setTimeout(() => {
notification.classList.add('active');
}, 100);
// Auto-hide after 3 seconds
setTimeout(() => {
notification.classList.remove('active');
setTimeout(() => {
notification.remove();
}, 300);
}, 3000);
// Close button
const closeBtn = notification.querySelector('.notification-close');
closeBtn.addEventListener('click', () => {
notification.classList.remove('active');
setTimeout(() => {
notification.remove();
}, 300);
});
}
// Registration Form Handler
class RegistrationForm {
constructor() {
this.form = document.getElementById('pre-register');
this.registerBtn = document.getElementById('register-button');
this.sliderThumb = document.getElementById('slider-thumb');
this.sliderTrack = document.getElementById('slider-track');
this.sliderProgress = document.getElementById('slider-progress');
this.sliderText = document.getElementById('slider-text');
this.isSliderVerified = false;
this.isDragging = false;
this.currentCaptcha = null;
this.init();
}
init() {
this.setupCaptcha();
this.setupSlider();
this.setupFormValidation();
this.setupFormSubmission();
}
setupCaptcha() {
const captchaQuestions = [
{ question: 'Сколько будет 5 + 3?', answer: '8' },
{ question: 'Сколько будет 7 - 2?', answer: '5' },
{ question: 'Сколько будет 4 × 2?', answer: '8' },
{ question: 'Сколько будет 9 ÷ 3?', answer: '3' },
{ question: 'Сколько будет 6 + 4?', answer: '10' },
{ question: 'Сколько будет 8 - 3?', answer: '5' }
];
this.currentCaptcha = captchaQuestions[Math.floor(Math.random() * captchaQuestions.length)];
const captchaQuestion = document.getElementById('captcha-question');
if (captchaQuestion) {
captchaQuestion.textContent = this.currentCaptcha.question;
}
}
setupSlider() {
if (!this.sliderThumb || !this.sliderTrack) return;
// Mouse events
this.sliderThumb.addEventListener('mousedown', (e) => {
this.isDragging = true;
this.sliderThumb.style.cursor = 'grabbing';
e.preventDefault();
});
document.addEventListener('mousemove', (e) => {
if (this.isDragging) {
this.updateSlider(e.clientX);
}
});
document.addEventListener('mouseup', () => {
if (this.isDragging) {
this.isDragging = false;
this.sliderThumb.style.cursor = 'grab';
}
});
// Touch events
this.sliderThumb.addEventListener('touchstart', (e) => {
this.isDragging = true;
e.preventDefault();
});
document.addEventListener('touchmove', (e) => {
if (this.isDragging) {
e.preventDefault();
this.updateSlider(e.touches[0].clientX);
}
});
document.addEventListener('touchend', () => {
if (this.isDragging) {
this.isDragging = false;
}
});
}
updateSlider(clientX) {
const trackRect = this.sliderTrack.getBoundingClientRect();
const thumbWidth = this.sliderThumb.offsetWidth;
const maxX = trackRect.width - thumbWidth;
const currentX = Math.max(0, Math.min(clientX - trackRect.left, maxX));
const percentage = (currentX / maxX) * 100;
this.sliderThumb.style.left = currentX + 'px';
this.sliderProgress.style.width = percentage + '%';
if (percentage >= 95) {
this.isSliderVerified = true;
this.sliderText.textContent = 'Проверка пройдена!';
this.sliderTrack.parentElement.classList.add('verified');
this.checkFormValidity();
}
}
setupFormValidation() {
const inputs = ['nickname', 'password', 'captcha-answer'];
const termsCheckbox = document.getElementById('terms');
inputs.forEach(id => {
const input = document.getElementById(id);
if (input) {
input.addEventListener('input', () => this.checkFormValidity());
}
});
if (termsCheckbox) {
termsCheckbox.addEventListener('change', () => this.checkFormValidity());
}
}
checkFormValidity() {
const nickname = document.getElementById('nickname')?.value.trim() || '';
const password = document.getElementById('password')?.value.trim() || '';
const captchaAnswer = document.getElementById('captcha-answer')?.value.trim() || '';
const termsAccepted = document.getElementById('terms')?.checked || false;
const isValid = nickname.length >= 3 &&
password.length >= 6 &&
captchaAnswer === this.currentCaptcha.answer &&
termsAccepted &&
this.isSliderVerified;
if (this.registerBtn) {
this.registerBtn.disabled = !isValid;
}
}
setupFormSubmission() {
if (this.registerBtn) {
this.registerBtn.addEventListener('click', (e) => {
e.preventDefault();
this.handleSubmission();
});
}
}
async handleSubmission() {
const nickname = document.getElementById('nickname')?.value.trim();
const password = document.getElementById('password')?.value.trim();
const discord = document.getElementById('discord')?.value.trim();
// Show loading state
this.registerBtn.innerHTML = 'Регистрация...';
this.registerBtn.disabled = true;
try {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 2000));
// Success
showNotification('Регистрация завершена успешно!', 'success');
this.resetForm();
this.updateUserCount();
} catch (error) {
showNotification('Ошибка при регистрации. Попробуйте снова.', 'error');
} finally {
this.registerBtn.innerHTML = 'Зарегистрироваться';
this.registerBtn.disabled = false;
}
}
resetForm() {
if (this.form) {
this.form.reset();
}
// Reset slider
this.isSliderVerified = false;
if (this.sliderThumb) this.sliderThumb.style.left = '0px';
if (this.sliderProgress) this.sliderProgress.style.width = '0%';
if (this.sliderText) this.sliderText.textContent = 'Перетащите ползунок вправо';
if (this.sliderTrack) this.sliderTrack.parentElement.classList.remove('verified');
// Reset captcha
this.setupCaptcha();
}
updateUserCount() {
const userCountElement = document.getElementById('user-count-value');
if (!userCountElement) return;
const currentCount = parseInt(localStorage.getItem('userCount') || '1247');
const newCount = currentCount + 1;
localStorage.setItem('userCount', newCount.toString());
// Animate count
let start = currentCount;
const duration = 1000;
const startTime = Date.now();
function animate() {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const current = Math.floor(start + (newCount - start) * progress);
userCountElement.textContent = `330 игроков уже с нами`;
if (progress < 1) {
requestAnimationFrame(animate);
}
}
animate();
}
}
// Payment Form Handler
class PaymentForm {
constructor() {
this.form = document.getElementById('paymentForm');
this.init();
}
init() {
if (this.form) {
this.form.addEventListener('submit', (e) => {
e.preventDefault();
this.handlePayment();
});
}
}
handlePayment() {
const amount = document.getElementById('amount')?.value;
if (!amount || amount < 1) {
showNotification('Введите корректную сумму', 'error');
return;
}
// Redirect to payment page
const paymentUrl = `https://pay.cryptomus.com/pay/${amount}`;
window.open(paymentUrl, '_blank');
showNotification('Переход на страницу оплаты...', 'success');
}
}
// Steps animation
function initStepsAnimation() {
const steps = document.querySelectorAll('.step-item');
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate');
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
steps.forEach(step => {
observer.observe(step);
});
}
// Smooth scrolling
function initSmoothScrolling() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// Page switcher functionality
function initPageSwitcher() {
const pageSwitchBtns = document.querySelectorAll('.page-switch-btn');
const mainContent = document.getElementById('main-content');
const adminPage = document.getElementById('admin-page');
pageSwitchBtns.forEach(btn => {
btn.addEventListener('click', function() {
const targetPage = this.getAttribute('data-page');
// Remove active class from all buttons
pageSwitchBtns.forEach(b => b.classList.remove('active'));
// Add active class to clicked button
this.classList.add('active');
// Switch pages
if (targetPage === 'main') {
mainContent.style.display = 'block';
adminPage.classList.remove('active');
} else if (targetPage === 'other') {
mainContent.style.display = 'none';
adminPage.classList.add('active');
// Trigger admin card animations
setTimeout(() => {
const adminCards = document.querySelectorAll('.admin-card');
adminCards.forEach((card, index) => {
setTimeout(() => {
card.style.animationDelay = (index * 0.1) + 's';
card.style.animation = 'fadeInUp 0.8s ease forwards';
}, index * 100);
});
}, 100);
}
});
});
}
// Initialize user count
function initUserCount() {
const userCountElement = document.getElementById('user-count-value');
if (userCountElement) {
const currentCount = parseInt(localStorage.getItem('userCount') || '1247');
userCountElement.textContent = `330 игроков уже с нами`;
}
}
// Reset password form handler
document.getElementById("reset-form").addEventListener("submit", function(e) {
e.preventDefault();
const nickname = document.getElementById("reset-nickname").value;
const code = document.getElementById("reset-code").value.trim();
const newPassword = document.getElementById("new-password").value;
const confirmPassword = document.getElementById("confirm-password").value;
if (newPassword !== confirmPassword) {
showNotification("Пароли не совпадают.", 'error');
return;
}
fetch("/api/confirm_reset.php", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ nickname, code, newPassword })
})
.then(res => res.json())
.then(data => {
showNotification(data.message, data.success ? 'success' : 'error');
if (data.success) {
hideResetModal();
}
});
});
// Initialize everything when DOM is loaded
document.addEventListener('DOMContentLoaded', function() {
// Initialize theme manager first
new ThemeManager();
// Initialize other components
initMobileMenu();
initScreenshotSlider();
initCountdown();
new RegistrationForm();
new PaymentForm();
initStepsAnimation();
initSmoothScrolling();
initPageSwitcher();
initUserCount();
// Login button event listener
const loginButton = document.getElementById('login-button');
if (loginButton) {
loginButton.addEventListener('click', showLoginModal);
}
// Login form submission
const loginForm = document.getElementById('login-form');
if (loginForm) {
loginForm.addEventListener('submit', function(e) {
e.preventDefault();
const nickname = document.getElementById('login-nickname').value;
const password = document.getElementById('login-password').value;
const remember = document.getElementById('remember-me').checked;
login(nickname, password, remember);
});
}
// Close modal when clicking outside
const loginModal = document.getElementById('login-modal');
if (loginModal) {
loginModal.addEventListener('click', function(e) {
if (e.target === loginModal) {
hideLoginModal();
}
});
}
const resetModal = document.getElementById('reset-modal');
if (resetModal) {
resetModal.addEventListener('click', function(e) {
if (e.target === resetModal) {
hideResetModal();
}
});
}
// Logout button
const logoutButton = document.getElementById('logout-button');
if (logoutButton) {
logoutButton.addEventListener('click', logout);
}
// Check if user is already logged in
checkLoggedIn();
// Theme change listener for canvas updates
document.addEventListener('themeChanged', (e) => {
// Reinitialize sphere with new theme colors if needed
const canvas = document.getElementById('sphere-canvas');
if (canvas) {
const ctx = canvas.getContext('2d');
// Update particle colors based on theme
// This could be enhanced to change particle colors dynamically
}
});
});
// Handle window resize
window.addEventListener('resize', () => {
// Reinitialize components that depend on window size
const canvas = document.getElementById('sphere-canvas');
if (canvas) {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}
});
// Handle page visibility change
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// Pause animations when page is not visible
} else {
// Resume animations when page becomes visible
}
});
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Ctrl/Cmd + Shift + T to toggle theme
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'T') {
e.preventDefault();
const themeManager = new ThemeManager();
themeManager.toggleTheme();
}
// Escape to close mobile menu and modals
if (e.key === 'Escape') {
const mobileMenu = document.querySelector('.mobile-menu');
if (mobileMenu && mobileMenu.classList.contains('active')) {
mobileMenu.classList.remove('active');
}
const loginModal = document.getElementById('login-modal');
if (loginModal && loginModal.classList.contains('active')) {
hideLoginModal();
}
const resetModal = document.getElementById('reset-modal');
if (resetModal && resetModal.classList.contains('active')) {
hideResetModal();
}
}
});
// Performance optimization: Lazy load images
function initLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
}
// Error handling
window.addEventListener('error', (e) => {
console.error('JavaScript error:', e.error);
// Could send error reports to analytics service
});
// Service Worker registration for PWA capabilities
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered: ', registration);
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}