327 lines
12 KiB
JavaScript
327 lines
12 KiB
JavaScript
document.addEventListener('DOMContentLoaded', () => {
|
|
// Basic interaction for toasts
|
|
try {
|
|
const toasts = document.querySelectorAll('.toast');
|
|
toasts.forEach(toastEl => {
|
|
if (window.bootstrap && bootstrap.Toast) {
|
|
const toast = new bootstrap.Toast(toastEl, { delay: 5000 });
|
|
toast.show();
|
|
}
|
|
});
|
|
} catch (e) {
|
|
console.error('Toast error:', e);
|
|
}
|
|
|
|
const html = document.documentElement;
|
|
|
|
const updateIcons = (theme) => {
|
|
const icons = document.querySelectorAll('#theme-toggle i, #theme-toggle-mobile i');
|
|
icons.forEach(icon => {
|
|
if (theme === 'dark') {
|
|
icon.className = 'fa-solid fa-sun';
|
|
} else {
|
|
icon.className = 'fa-solid fa-moon';
|
|
}
|
|
});
|
|
|
|
const textLabels = document.querySelectorAll('.theme-status-text');
|
|
textLabels.forEach(label => {
|
|
label.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode';
|
|
});
|
|
};
|
|
|
|
// Theme Toggle Logic
|
|
const initThemeToggle = (btnId) => {
|
|
const themeToggle = document.getElementById(btnId);
|
|
if (!themeToggle) return;
|
|
|
|
themeToggle.addEventListener('click', () => {
|
|
const currentTheme = html.getAttribute('data-theme') || 'light';
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
html.setAttribute('data-theme', newTheme);
|
|
updateIcons(newTheme);
|
|
document.cookie = `theme=${newTheme}; path=/; max-age=${365 * 24 * 60 * 60}`;
|
|
localStorage.setItem('theme', newTheme);
|
|
});
|
|
};
|
|
|
|
// Unified AJAX Content Loader
|
|
const loadContent = (url, updateUrl = true) => {
|
|
const gridContainer = document.getElementById('apk-grid-container');
|
|
if (!gridContainer) return;
|
|
|
|
gridContainer.style.opacity = '0.5';
|
|
gridContainer.style.pointerEvents = 'none';
|
|
|
|
fetch(url, {
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
})
|
|
.then(response => response.text())
|
|
.then(data => {
|
|
gridContainer.innerHTML = data;
|
|
gridContainer.style.opacity = '1';
|
|
gridContainer.style.pointerEvents = 'all';
|
|
|
|
if (updateUrl) {
|
|
window.history.pushState({}, '', url);
|
|
}
|
|
|
|
// Scroll to grid top on mobile if needed
|
|
if (window.innerWidth < 992) {
|
|
const latestSection = document.getElementById('latest');
|
|
if (latestSection) {
|
|
window.scrollTo({
|
|
top: latestSection.offsetTop - 100,
|
|
behavior: 'smooth'
|
|
});
|
|
}
|
|
}
|
|
})
|
|
.catch(err => {
|
|
console.error('Fetch error:', err);
|
|
gridContainer.style.opacity = '1';
|
|
gridContainer.style.pointerEvents = 'all';
|
|
});
|
|
};
|
|
|
|
// AJAX Category Filtering
|
|
const initCategoryAjax = () => {
|
|
document.addEventListener('click', (e) => {
|
|
const filter = e.target.closest('.category-filter, .ajax-cat-link');
|
|
if (!filter) return;
|
|
|
|
e.preventDefault();
|
|
const url = filter.getAttribute('href');
|
|
const categoryName = filter.textContent.trim();
|
|
const dropdownBtn = document.getElementById('category-dropdown-btn');
|
|
|
|
if (dropdownBtn && filter.classList.contains('category-filter')) {
|
|
dropdownBtn.innerHTML = `${categoryName} <i class="bi bi-chevron-down ms-1 small"></i>`;
|
|
}
|
|
|
|
// Update active state for chips if they are chips
|
|
if (filter.classList.contains('ajax-cat-link')) {
|
|
document.querySelectorAll('.ajax-cat-link').forEach(btn => {
|
|
btn.classList.remove('btn-success');
|
|
btn.classList.add('btn-light');
|
|
});
|
|
filter.classList.remove('btn-light');
|
|
filter.classList.add('btn-success');
|
|
}
|
|
|
|
loadContent(url);
|
|
|
|
// Close search overlay if open
|
|
const searchOverlay = document.getElementById('search-overlay');
|
|
if (searchOverlay) searchOverlay.classList.remove('active');
|
|
});
|
|
};
|
|
|
|
// AJAX Search
|
|
const initSearchAjax = () => {
|
|
const searchForm = document.getElementById('ajax-search-form');
|
|
if (!searchForm) return;
|
|
|
|
searchForm.addEventListener('submit', (e) => {
|
|
e.preventDefault();
|
|
const formData = new FormData(searchForm);
|
|
const query = formData.get('search');
|
|
const url = `/?search=${encodeURIComponent(query)}`;
|
|
|
|
loadContent(url);
|
|
|
|
const searchOverlay = document.getElementById('search-overlay');
|
|
if (searchOverlay) searchOverlay.classList.remove('active');
|
|
});
|
|
};
|
|
|
|
// Newsletter AJAX
|
|
const initNewsletterAjax = () => {
|
|
document.addEventListener('click', (e) => {
|
|
const btn = e.target.closest('.newsletter-submit-btn');
|
|
if (!btn) return;
|
|
|
|
const form = btn.closest('.newsletter-form') || btn.parentElement;
|
|
const emailInput = form.querySelector('.newsletter-email');
|
|
const msg = form.querySelector('.newsletter-msg') || form.nextElementSibling;
|
|
|
|
if (!emailInput) return;
|
|
|
|
const email = emailInput.value;
|
|
if (!email || !email.includes('@')) return;
|
|
|
|
btn.disabled = true;
|
|
const originalText = btn.innerHTML;
|
|
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
|
|
|
|
fetch('/api/newsletter/subscribe', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ email: email })
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = originalText;
|
|
if (data.success) {
|
|
if (msg) msg.innerHTML = `<span class="text-success">${data.success}</span>`;
|
|
else alert(data.success);
|
|
emailInput.value = '';
|
|
} else {
|
|
if (msg) msg.innerHTML = `<span class="text-danger">${data.error}</span>`;
|
|
else alert(data.error);
|
|
}
|
|
})
|
|
.catch(err => {
|
|
btn.disabled = false;
|
|
btn.innerHTML = originalText;
|
|
if (msg) msg.innerHTML = '<span class="text-danger">An error occurred.</span>';
|
|
});
|
|
});
|
|
|
|
// Also handle form submit for the one in home.php
|
|
document.addEventListener('submit', (e) => {
|
|
if (e.target.classList.contains('newsletter-form')) {
|
|
e.preventDefault();
|
|
const btn = e.target.querySelector('.newsletter-submit-btn');
|
|
if (btn) btn.click();
|
|
}
|
|
});
|
|
};
|
|
|
|
// AI Chat Assistant Logic
|
|
const initAIChat = () => {
|
|
const toggleBtn = document.getElementById('toggle-ai-chat');
|
|
const closeBtn = document.getElementById('close-ai-chat');
|
|
const chatWindow = document.getElementById('ai-chat-window');
|
|
const chatInput = document.getElementById('ai-chat-input');
|
|
const sendBtn = document.getElementById('send-ai-chat');
|
|
const messagesContainer = document.getElementById('ai-chat-messages');
|
|
|
|
if (!toggleBtn || !chatWindow) return;
|
|
|
|
toggleBtn.addEventListener('click', () => {
|
|
chatWindow.classList.toggle('d-none');
|
|
if (!chatWindow.classList.contains('d-none')) {
|
|
chatInput.focus();
|
|
}
|
|
});
|
|
|
|
closeBtn.addEventListener('click', () => {
|
|
chatWindow.classList.add('d-none');
|
|
});
|
|
|
|
const appendMessage = (message, isUser = false) => {
|
|
const div = document.createElement('div');
|
|
div.className = 'mb-3 d-flex ' + (isUser ? 'justify-content-end' : '');
|
|
|
|
const content = document.createElement('div');
|
|
content.className = (isUser ? 'bg-success text-white' : 'bg-white') + ' p-3 rounded-4 shadow-sm small';
|
|
content.style.maxWidth = '85%';
|
|
content.style.borderBottomRightRadius = isUser ? '0' : 'inherit';
|
|
content.style.borderBottomLeftRadius = isUser ? 'inherit' : '0';
|
|
content.textContent = message;
|
|
|
|
div.appendChild(content);
|
|
messagesContainer.appendChild(div);
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
};
|
|
|
|
const sendMessage = () => {
|
|
const message = chatInput.value.trim();
|
|
if (!message) return;
|
|
|
|
appendMessage(message, true);
|
|
chatInput.value = '';
|
|
|
|
const loadingDiv = document.createElement('div');
|
|
loadingDiv.className = 'mb-3 d-flex';
|
|
loadingDiv.innerHTML = '<div class="bg-white p-3 rounded-4 shadow-sm small" style="border-bottom-left-radius: 0 !important;"><div class="spinner-border spinner-border-sm text-success" role="status"></div> Thinking...</div>';
|
|
messagesContainer.appendChild(loadingDiv);
|
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
|
|
fetch('/api/ai/chat', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: message })
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
messagesContainer.removeChild(loadingDiv);
|
|
if (data.reply) {
|
|
appendMessage(data.reply);
|
|
} else {
|
|
appendMessage(data.error || 'Sorry, something went wrong.');
|
|
}
|
|
})
|
|
.catch(err => {
|
|
if (messagesContainer.contains(loadingDiv)) messagesContainer.removeChild(loadingDiv);
|
|
appendMessage('Error connecting to AI assistant.');
|
|
console.error(err);
|
|
});
|
|
};
|
|
|
|
sendBtn.addEventListener('click', sendMessage);
|
|
chatInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') sendMessage();
|
|
});
|
|
};
|
|
|
|
// Mobile Overlays & Utils
|
|
const initMobileUtils = () => {
|
|
const searchTrigger = document.getElementById('mobile-search-trigger');
|
|
const searchOverlay = document.getElementById('search-overlay');
|
|
const closeSearch = document.getElementById('close-search-overlay');
|
|
|
|
if (searchTrigger && searchOverlay) {
|
|
searchTrigger.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
searchOverlay.classList.add('active');
|
|
const input = searchOverlay.querySelector('input');
|
|
if (input) setTimeout(() => input.focus(), 300);
|
|
});
|
|
}
|
|
|
|
if (closeSearch && searchOverlay) {
|
|
closeSearch.addEventListener('click', () => {
|
|
searchOverlay.classList.remove('active');
|
|
});
|
|
}
|
|
|
|
// Back to Top
|
|
const backToTop = document.getElementById('back-to-top');
|
|
if (backToTop) {
|
|
window.addEventListener('scroll', () => {
|
|
if (window.pageYOffset > 300) {
|
|
backToTop.classList.add('show');
|
|
} else {
|
|
backToTop.classList.remove('show');
|
|
}
|
|
});
|
|
backToTop.addEventListener('click', (e) => {
|
|
e.preventDefault();
|
|
window.scrollTo({ top: 0, behavior: 'smooth' });
|
|
});
|
|
}
|
|
};
|
|
|
|
// Initial Sync
|
|
const currentTheme = html.getAttribute('data-theme') || 'light';
|
|
updateIcons(currentTheme);
|
|
|
|
initThemeToggle('theme-toggle');
|
|
initThemeToggle('theme-toggle-mobile');
|
|
initCategoryAjax();
|
|
initSearchAjax();
|
|
initNewsletterAjax();
|
|
initAIChat();
|
|
initMobileUtils();
|
|
|
|
// Handle browser back/forward
|
|
window.addEventListener('popstate', () => {
|
|
loadContent(window.location.href, false);
|
|
});
|
|
|
|
console.log('ApkNusa AJAX Engine active.');
|
|
});
|