library/frontend/files/index.html

231 lines
6.8 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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>