diff --git a/frontend/files/app.js b/frontend/files/app.js new file mode 100644 index 0000000..b7ada36 --- /dev/null +++ b/frontend/files/app.js @@ -0,0 +1,195 @@ +var sortState = { + sortBy: "sortAuthor", + sortOrder: "asc", +}; + +function init() { + fetch("/api") + .then((response) => response.json()) + .then((books) => { + // prepare response + books.forEach(apiResponseParsing); + document.getElementById("search").addEventListener("input", (e) => { + renderTable(search(books, e.target.value)); + }); + renderTable(books); + }); +} + +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 renderTable(books, sortField) { + if (sortField) { + if (sortState.sortBy === sortField && sortState.sortOrder === "asc") { + sortState.sortOrder = "desc"; + } else { + sortState.sortOrder = "asc"; + } + sortState.sortBy = sortField; + } + books.sort((one, two) => + (one[sortState.sortBy] + one["sortTitle"]).localeCompare( + two[sortState.sortBy] + two["sortTitle"] + ) + ); + if (sortState.sortOrder === "desc") { + books.reverse(); + } + books.forEach((e, i) => (e.rowNumber = i)); // re-key + + // rendering + var bookElement = document.getElementById("books"); + bookElement.innerHTML = TableTemplate(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( + books[e.currentTarget.id] + ); + }); + }); + // add sorting callbacks + Array.from(bookElement.querySelectorAll("tbody tr th[data-sort-by]")).forEach( + (row) => { + row.addEventListener("click", function (e) { + renderTable(books, e.target.dataset.sortBy); // only add callback when there's a sortBy attribute + }); + } + ); + // mark currently active column + bookElement + .querySelector("tbody tr th[data-sort-by=" + sortState.sortBy + "]") + .classList.add(sortState.sortOrder); +} + +function search(books, searchBy) { + searchBy = searchCleaner(searchBy); + if (searchBy !== "") { + books = books.filter( + ({ title, authors, genre, publisher, series, year }) => { + return Object.values({ + title, + authors: authors.join(" "), + genre, + publisher, + series, + year, + }).find((field) => searchCleaner(field).indexOf(searchBy) !== -1); + } + ); + } + return books; +} + +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 BookTemplate({ + "isbn-13": isbn13, + authors, + coverurl, + description, + format, + notes, + onLoan, + publisher, + series, + signed, + title, + volume, + year, +}) { + return `${coverurl ? `` : ""} +
${description}
+ ${notes ? `Notes:${notes}
` : ""} +Title | +Author | +Publisher | +Year | +ISBN | +
---|