const bookId = window.location.pathname.split('/').pop();
let allChapters = []; // Stores all fetched chapters
let filteredChapters = []; // Stores currently displayed chapters (filtered)
let currentPage = 1;
const itemsPerPage = 12;
async function init() {
try {
// 1. Load Metadata
const res = await fetch(`/api/book/${bookId}`);
const data = await res.json();
if (data.error) {
const titleEl = document.getElementById('title');
if (titleEl) titleEl.innerText = "Book Not Found";
return;
}
// Populate Hero Elements
const title = data.title.english || data.title.romaji;
document.title = `${title} | WaifuBoard Books`;
const titleEl = document.getElementById('title');
if (titleEl) titleEl.innerText = title;
const descEl = document.getElementById('description');
if (descEl) descEl.innerHTML = data.description || "No description available.";
const scoreEl = document.getElementById('score');
if (scoreEl) scoreEl.innerText = (data.averageScore || '?') + '% Score';
const pubEl = document.getElementById('published-date');
if (pubEl) {
if (data.startDate && data.startDate.year) {
const y = data.startDate.year;
const m = data.startDate.month ? `-${data.startDate.month.toString().padStart(2, '0')}` : '';
const d = data.startDate.day ? `-${data.startDate.day.toString().padStart(2, '0')}` : '';
pubEl.innerText = `${y}${m}${d}`;
} else {
pubEl.innerText = '????';
}
}
const statusEl = document.getElementById('status');
if (statusEl) statusEl.innerText = data.status || 'Unknown';
const formatEl = document.getElementById('format');
if (formatEl) formatEl.innerText = data.format || 'MANGA';
const chaptersEl = document.getElementById('chapters');
if (chaptersEl) chaptersEl.innerText = data.chapters || '?';
const genresEl = document.getElementById('genres');
if(genresEl && data.genres) {
genresEl.innerText = data.genres.slice(0, 3).join(' • ');
}
const img = data.coverImage.extraLarge || data.coverImage.large;
const posterEl = document.getElementById('poster');
if (posterEl) posterEl.src = img;
const heroBgEl = document.getElementById('hero-bg');
if (heroBgEl) heroBgEl.src = data.bannerImage || img;
// 2. Load Chapters
loadChapters();
} catch (err) {
console.error("Metadata Error:", err);
}
}
async function loadChapters() {
const tbody = document.getElementById('chapters-body');
if (!tbody) return;
tbody.innerHTML = '
| Searching extensions for chapters... |
';
try {
const res = await fetch(`/api/book/${bookId}/chapters`);
const data = await res.json();
allChapters = data.chapters || [];
filteredChapters = [...allChapters]; // Initially, show all
const totalEl = document.getElementById('total-chapters');
if (allChapters.length === 0) {
tbody.innerHTML = '| No chapters found on loaded extensions. |
';
if (totalEl) totalEl.innerText = "0 Found";
return;
}
if (totalEl) totalEl.innerText = `${allChapters.length} Found`;
// Populate Provider Filter
populateProviderFilter();
// Read Button Action (Start at filtered Ch 1)
const readBtn = document.getElementById('read-start-btn');
if (readBtn && filteredChapters.length > 0) {
readBtn.onclick = () => openReader(filteredChapters[0].id);
}
renderTable();
} catch (err) {
tbody.innerHTML = '| Error loading chapters. |
';
console.error(err);
}
}
function populateProviderFilter() {
const select = document.getElementById('provider-filter');
if (!select) return;
// Extract unique providers
const providers = [...new Set(allChapters.map(ch => ch.provider))];
// Only show filter if there are actual providers found
if (providers.length > 0) {
select.style.display = 'inline-block';
// Clear existing options except "All"
select.innerHTML = '';
providers.forEach(prov => {
const opt = document.createElement('option');
opt.value = prov;
opt.innerText = prov;
select.appendChild(opt);
});
// Attach Event Listener
select.onchange = (e) => {
const selected = e.target.value;
if (selected === 'all') {
filteredChapters = [...allChapters];
} else {
filteredChapters = allChapters.filter(ch => ch.provider === selected);
}
currentPage = 1; // Reset to page 1 on filter change
renderTable();
};
}
}
function renderTable() {
const tbody = document.getElementById('chapters-body');
if (!tbody) return;
tbody.innerHTML = '';
if (filteredChapters.length === 0) {
tbody.innerHTML = '| No chapters match this filter. |
';
updatePagination();
return;
}
const start = (currentPage - 1) * itemsPerPage;
const end = start + itemsPerPage;
const pageItems = filteredChapters.slice(start, end);
pageItems.forEach((ch, idx) => {
const realIndex = start + idx;
const row = document.createElement('tr');
row.innerHTML = `
${ch.number} |
${ch.title || 'Chapter ' + ch.number} |
${ch.provider} |
|
`;
tbody.appendChild(row);
});
updatePagination();
}
function updatePagination() {
const totalPages = Math.ceil(filteredChapters.length / itemsPerPage);
const pagination = document.getElementById('pagination');
if (!pagination) return;
if (totalPages <= 1) {
pagination.style.display = 'none';
return;
}
pagination.style.display = 'flex';
document.getElementById('page-info').innerText = `Page ${currentPage} of ${totalPages}`;
const prevBtn = document.getElementById('prev-page');
const nextBtn = document.getElementById('next-page');
prevBtn.disabled = currentPage === 1;
nextBtn.disabled = currentPage >= totalPages;
prevBtn.onclick = () => { currentPage--; renderTable(); };
nextBtn.onclick = () => { currentPage++; renderTable(); };
}
function openReader(bookId, chapterId, provider) {
localStorage.setItem('reader_prev_url', window.location.href);
const c = encodeURIComponent(chapterId);
const p = encodeURIComponent(provider);
window.location.href = `/read/${bookId}/${c}/${p}`;
}
init();