var sortState = { sortBy: "sortAuthor", sortOrder: "asc", }; var admin = false; var books; function checkAdminMode() { fetch("/api/mode") .then((response) => response.json()) .then((resp) => (admin = resp.Admin)) .then(() => { if (admin) { var element = document.getElementById("addBook"); element.addEventListener("click", (e) => { e.preventDefault(); renderAddBookView(); }); element.classList.remove("hidden"); } }); } function loadBookList() { fetch("/api/books") .then((response) => response.json()) .then((list) => { // prepare response list.forEach(apiResponseParsing); books = list; document.getElementById("search").addEventListener("input", rerender); document.getElementById("childrens").addEventListener("change", rerender); rerender(); }); } function rerender() { var searchValue = document.getElementById("search").value; var childrens = document.getElementById("childrens").checked; renderTable(search(searchValue, childrens)); } function init() { checkAdminMode(); loadBookList(); } function renderAddBookView() { document.getElementById("current").innerHTML = AddBookTemplate(); document.getElementById("lookup").addEventListener("click", (e) => { e.preventDefault(); if (document.getElementById("isbn-13").value.length === 13) { getPossibleBooks(document.getElementById("isbn-13").value); } else { console.log("no isbn"); } }); document.getElementById("save").addEventListener("click", (e) => { e.preventDefault(); saveBook({ title: document.getElementById("title").value, authors: document.getElementById("authors").value.split(";"), sortAuthor: document.getElementById("sortAuthor").value, "isbn-10": document.getElementById("isbn-10").value, "isbn-13": document.getElementById("isbn-13").value, publisher: document.getElementById("publisher").value, format: document.getElementById("format").value, genre: document.getElementById("genre").value, series: document.getElementById("series").value, volume: document.getElementById("volume").value, year: document.getElementById("year").value, coverURL: document.getElementById("coverURL").value, }); }); } function getPossibleBooks(isbn) { fetch("/api/query", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ "isbn-13": isbn }), }) .then((response) => response.json()) .then((json) => { Object.keys(json).forEach((key) => { var elem = document.getElementById(key); if (elem !== null) { elem.value = json[key]; } }); }); } function saveBook(book) { fetch("/api/books", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(book), }).then(() => { clearAddBookForm(); loadBookList(); }); } function renderTable(bookList, sortField) { if (sortField) { sortState.sortOrder = sortState.sortBy === sortField && sortState.sortOrder === "asc" ? "desc" : "asc"; sortState.sortBy = sortField; } bookList.sort((one, two) => (one[sortState.sortBy] + one["sortTitle"]).localeCompare( two[sortState.sortBy] + two["sortTitle"] ) ); if (sortState.sortOrder === "desc") { bookList.reverse(); } bookList.forEach((e, i) => (e.rowNumber = i)); // re-key // rendering var bookElement = document.getElementById("books"); bookElement.innerHTML = TableTemplate(bookList); document.getElementById("bookCount").innerHTML = `${bookList.length} books`; // add listeners for selecting book to view Array.from(bookElement.querySelectorAll("tbody tr")) .slice(1) // remove header from Array .forEach((row) => { row.addEventListener("click", (e) => { // add listener to swap current book document.getElementById("current").innerHTML = BookTemplate( bookList[e.currentTarget.id] ); }); }); // add sorting callbacks Array.from(bookElement.querySelectorAll("tbody tr th[data-sort-by]")).forEach( (row) => { row.addEventListener("click", function (e) { // only add callback when there's a sortBy attribute renderTable(bookList, e.target.dataset.sortBy); }); } ); // mark currently active column bookElement .querySelector("tbody tr th[data-sort-by=" + sortState.sortBy + "]") .classList.add(sortState.sortOrder); } function apiResponseParsing(book) { book.sortTitle = titleCleaner(book.title); if (!book["isbn-10"] && book["isbn-13"]) { book["isbn-10"] = ISBNfromEAN(book["isbn-13"]); } if (!book.coverURL && book["isbn-10"]) { book.coverURL = `https://images-na.ssl-images-amazon.com/images/P/` + book["isbn-10"] + `.01.LZZ.jpg`; } return book; } function search(searchBy, includeChildrensBooks) { searchBy = searchCleaner(searchBy); return books.filter( ({ title, authors, genre, publisher, series, year, childrens }) => { var inSearch = true; if (searchBy !== "") { inSearch = Object.values({ title, authors: authors.join(" "), genre, publisher, series, year, }).find((field) => searchCleaner(field).indexOf(searchBy) !== -1); } if (!includeChildrensBooks) { return inSearch && !childrens; } return inSearch; } ); } function titleCleaner(title) { return title .replace('"', "") .replace(":", "") .replace(/^(An?|The)\s/i, ""); } function searchCleaner(str) { return str .toLowerCase() .replaceAll('"', "") .replaceAll(":", "") .replaceAll("'", "") .replaceAll(" ", ""); } function ISBNfromEAN(EAN) { ISBN = EAN.slice(3, 12); var checkdigit = (11 - (ISBN.split("").reduce((s, n, k) => s + n * (10 - k), 0) % 11)) % 11; return ISBN + (checkdigit === 10 ? "X" : checkdigit); } function clearAddBookForm() { document .getElementById("newBookForm") .childNodes.forEach((node) => node.nodeName === "LABEL" ? (node.lastChild.value = "") : null ); } function BookTemplate({ "isbn-13": isbn13, "isbn-10": isbn10, authors, coverURL, format, publisher, series, signed, title, volume, year, }) { return `

${title}

${authors}

${[isbn10, isbn13].filter((v) => v != "").join(" / ")}
${publisher}, ${year}
${ series ? `${series}${volume ? `, Volume ${volume}` : ""}
` : "" } ${signed ? "Signed by the author ✒
" : ""} ${format} ${admin ? `Edit Book` : ""}
`; } function TableRowTemplate({ "isbn-13": isbn13, "isbn-10": isbn10, authors, publisher, rowNumber, signed, title, year, }) { return ` ${title} ${ signed ? '' : "" } ${authors} ${publisher} ${year} ${isbn13 ? isbn13 : isbn10} `; } function TableTemplate(books) { return `${books.reduce((acc, book) => { return acc.concat(TableRowTemplate(book)); }, "")}
Title Author Publisher Year ISBN
`; } function AddBookTemplate() { return `
${[ { name: "Title", id: "title", type: "text" }, { name: "Authors", id: "authors", type: "text" }, { name: "SortAuthor", id: "sortAuthor", type: "text" }, { name: "ISBN10", id: "isbn-10", type: "text" }, { name: "ISBN13", id: "isbn-13", type: "text" }, { name: "Publisher", id: "publisher", type: "text" }, { name: "Format", id: "format", type: "text" }, { name: "Genre", id: "genre", type: "text" }, { name: "Series", id: "series", type: "text" }, { name: "Volume", id: "volume", type: "text" }, { name: "Year", id: "year", type: "text" }, { name: "CoverURL", id: "coverURL", type: "text" }, { name: "Signed", id: "signed", type: "checkbox" }, { name: "Childrens", id: "childrens", type: "checkbox" }, ].reduce((acc, field) => { return acc.concat( `
` ); }, "")}
`; }