let trendingBooks = [];
let currentHeroIndex = 0;
let heroInterval;
window.addEventListener('scroll', () => {
const nav = document.getElementById('navbar');
if (window.scrollY > 50) nav.classList.add('scrolled');
else nav.classList.remove('scrolled');
});
const searchInput = document.getElementById('search-input');
const searchResults = document.getElementById('search-results');
let searchTimeout;
searchInput.addEventListener('input', (e) => {
const query = e.target.value;
clearTimeout(searchTimeout);
if (query.length < 2) {
searchResults.classList.remove('active');
searchResults.innerHTML = '';
searchInput.style.borderRadius = '99px';
return;
}
searchTimeout = setTimeout(() => {
fetchBookSearch(query);
}, 300);
});
document.addEventListener('click', (e) => {
if (!e.target.closest('.search-wrapper')) {
searchResults.classList.remove('active');
searchInput.style.borderRadius = '99px';
}
});
async function fetchBookSearch(query) {
try {
const res = await fetch(`/api/search/books?q=${encodeURIComponent(query)}`);
const data = await res.json();
renderSearchResults(data.results || []);
} catch (err) {
console.error("Search Error:", err);
renderSearchResults([]);
}
}
function createSlug(text) {
if (!text) return '';
return text
.toLowerCase()
.trim()
.replace(/-/g, '--')
.replace(/\s+/g, '-')
.replace(/[^a-z0-9\-]/g, '');
}
function renderSearchResults(results) {
searchResults.innerHTML = '';
if (!results || results.length === 0) {
searchResults.innerHTML = '
No results found
';
} else {
results.forEach(book => {
const title = book.title.english || book.title.romaji || "Unknown";
const img = (book.coverImage && (book.coverImage.medium || book.coverImage.large)) || '';
const rating = book.averageScore ? `${book.averageScore}%` : 'N/A';
const year = book.seasonYear || (book.startDate ? book.startDate.year : '') || '????';
const format = book.format || 'MANGA';
let href;
if (book.isExtensionResult) {
const titleSlug = createSlug(title);
href = `/book/${book.extensionName}/${titleSlug}`;
} else {
href = `/book/${book.id}`;
}
const extName = book.extensionName.charAt(0).toUpperCase() + book.extensionName.slice(1);
const extPill = book.isExtensionResult
? `${extName}`
: '';
const item = document.createElement('a');
item.className = 'search-item';
item.href = href;
item.innerHTML = `
${title}
${rating}
• ${year}
• ${format}
${extPill}
`;
searchResults.appendChild(item);
});
}
searchResults.classList.add('active');
searchInput.style.borderRadius = '12px 12px 0 0';
}
function scrollCarousel(id, direction) {
const container = document.getElementById(id);
if(container) {
const scrollAmount = container.clientWidth * 0.75;
container.scrollBy({ left: direction * scrollAmount, behavior: 'smooth' });
}
}
async function init() {
try {
const res = await fetch('/api/books/trending');
const data = await res.json();
if (data.results && data.results.length > 0) {
trendingBooks = data.results;
updateHeroUI(trendingBooks[0]);
renderList('trending', trendingBooks);
startHeroCycle();
}
const resPop = await fetch('/api/books/popular');
const dataPop = await resPop.json();
if (dataPop.results) renderList('popular', dataPop.results);
} catch (e) {
console.error("Books Error:", e);
}
}
function startHeroCycle() {
if(heroInterval) clearInterval(heroInterval);
heroInterval = setInterval(() => {
if(trendingBooks.length > 0) {
currentHeroIndex = (currentHeroIndex + 1) % trendingBooks.length;
updateHeroUI(trendingBooks[currentHeroIndex]);
}
}, 8000);
}
function updateHeroUI(book) {
if(!book) return;
const title = book.title.english || book.title.romaji;
const desc = book.description || "No description available.";
const poster = (book.coverImage && (book.coverImage.extraLarge || book.coverImage.large)) || '';
const banner = book.bannerImage || poster;
document.getElementById('hero-title').innerText = title;
document.getElementById('hero-desc').innerHTML = desc;
document.getElementById('hero-score').innerText = (book.averageScore || '?') + '% Score';
document.getElementById('hero-year').innerText = (book.startDate && book.startDate.year) ? book.startDate.year : '????';
document.getElementById('hero-type').innerText = book.format || 'MANGA';
const heroPoster = document.getElementById('hero-poster');
if(heroPoster) heroPoster.src = poster;
const bg = document.getElementById('hero-bg-media');
if(bg) bg.src = banner;
const readBtn = document.getElementById('read-btn');
if (readBtn) {
readBtn.onclick = () => window.location.href = `/book/${book.id}`;
}
}
function renderList(id, list) {
const container = document.getElementById(id);
container.innerHTML = '';
list.forEach(book => {
const title = book.title.english || book.title.romaji;
const cover = book.coverImage ? book.coverImage.large : '';
const score = book.averageScore || '--';
const type = book.format || 'Book';
const el = document.createElement('div');
el.className = 'card';
el.onclick = () => {
window.location.href = `/book/${book.id}`;
};
el.innerHTML = `
${title}
${score}% • ${type}
`;
container.appendChild(el);
});
}
init();