<!DOCTYPE html> <html> <head> <title>Library</title> <script type="text/javascript" src="js/jquery.js"></script> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" /> <link rel="stylesheet" href="css/reset.css" /> <link rel="stylesheet" href="css/style.css" /> <link rel="icon" href="favicon.ico" type="image/x-icon" /> <link href="https://fonts.googleapis.com/css?family=Libre+Baskerville:400,700" rel="stylesheet" /> <script type="text/javascript"> var sortState = { sortBy: "sortAuthor", sortOrder: "asc", }; function init() { fetch("/api") .then((response) => response.json()) .then((books) => { // prepare response books.forEach((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 books; }) .then((books) => { document.getElementById("search").addEventListener("input", (e) => { search(books, e.target.value); }); return books; }) .then(renderTable); } 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); } ); } renderTable(books); } 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 document.getElementById("books").innerHTML = TableTemplate(books); $("#books tbody tr") .not(":first") // ignore the headers .on("click", (e) => { document.getElementById("current").innerHTML = BookTemplate( books[e.currentTarget.id] ); }); $("#books tbody tr th[data-sort-by]").on("click", function (e) { console.log(e); renderTable(books, $(this).data("sortBy")); // only add callback when there's a sortBy attribute }); $("#books tbody tr th[data-sort-by=" + sortState.sortBy + "]").addClass( sortState.sortOrder ); } function titleCleaner(title) { return title .replace('"', "") .replace(":", "") .replace(/^(An?|The)\s/i, ""); } function searchCleaner(str) { return str .toLowerCase() .replace('"', "") .replace(":", "") .replace("'", "") .replace(" ", ""); } 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 ? `<img src="${coverurl}"/>` : ""} <h1 ${onLoan ? "class='onLoan' " : ""}>${title}</h1> <h2>${authors}</h2> <span>${isbn13}</span><br/> <span>${publisher}, ${year}</span><br/> ${ series ? `<span>${series}${volume ? `, Volume ${volume}` : ""}</span><br/>` : "" } ${signed ? "<span>Signed by the author ✒</span><br/>" : ""} <span>${format}</span> ${onLoan ? `<h2 class="onLoan">On loan to ${onLoan}</h2>` : ""} <div class="description"> <p>${description}</p> ${notes ? `<span>Notes:</span><p>${notes}</p>` : ""} </div>`; } function TableRowTemplate({ "isbn-13": isbn13, authors, onLoan, publisher, rowNumber, signed, title, year, }) { return `<tr class="tRow ${onLoan ? "onLoan" : ""}" id="${rowNumber}"> <td class="title"> ${title} ${ signed ? '<span class="signed" title="Signed by the author" >✒</span>︎' : "" } </td> <td class="author">${authors}</td> <td class="publisher">${publisher}</td> <td class="year">${year}</td> <td class="isbn">${isbn13}</td> </tr>`; } function TableTemplate(books) { return `<table class="bookTable"> <tr> <th data-sort-by="sortTitle" class="tHeader title">Title</th> <th data-sort-by="sortAuthor" class="tHeader author">Author</th> <th data-sort-by="publisher" class="tHeader publisher">Publisher</th> <th data-sort-by="year" class="tHeader year">Year</th> <th class="tHeader isbn">ISBN</th> </tr>${books.reduce((acc, book) => { return acc.concat(TableRowTemplate(book)); }, "")} </table>`; } window.addEventListener("DOMContentLoaded", () => { init(); }); </script> </head> <body> <div class="wrapper"> <div id="header"> <h1>Library</h1> <a target="_blank" href="https://git.yetaga.in/alazyreader/library" >git</a > <div id="searchBox"> <input id="search" type="text" name="search" placeholder="Search..." /> </div> </div> <div id="current">No Book Selected</div> <div id="books"></div> <!-- Table goes here --> </div> </body> </html>