library/frontend/files/index.html

238 lines
6.8 KiB
HTML
Raw Normal View History

2021-06-26 18:08:35 +00:00
<!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",
2021-06-26 18:08:35 +00:00
sortOrder: "asc",
};
function init() {
fetch("/api")
.then((response) => response.json())
.then(showInfo);
2021-06-26 18:08:35 +00:00
}
2021-08-03 01:31:54 +00:00
function showInfo(books) {
books.forEach((book) => {
book.sortTitle = titleCleaner(book.title);
if (!book["isbn-10"] && book["isbn-13"]) {
book["isbn-10"] = ISBNfromEAN(book["isbn-13"]);
2021-06-26 18:08:35 +00:00
}
2021-08-03 01:31:54 +00:00
if (!book.coverurl && book["isbn-10"]) {
book.coverurl = amazonCoverUrl(book["isbn-10"]);
2021-06-26 18:08:35 +00:00
}
});
2021-08-03 01:31:54 +00:00
$("#search")
.unbind("input")
.on("input", (e) => {
search(books, e.target.value);
});
2021-06-26 18:08:35 +00:00
2021-08-03 01:31:54 +00:00
$("#reloadLink")
.unbind("click")
.on("click", () => {
init();
});
2021-06-26 18:08:35 +00:00
2021-08-03 01:31:54 +00:00
renderTable(books);
}
2021-06-26 18:08:35 +00:00
2021-08-03 01:31:54 +00:00
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);
2021-06-26 18:08:35 +00:00
}
2021-08-03 01:31:54 +00:00
function renderTable(books, sortField) {
2021-06-26 18:08:35 +00:00
if (sortField) {
2021-08-03 01:31:54 +00:00
if (sortState.sortBy === sortField && sortState.sortOrder === "asc") {
sortState.sortOrder = "desc";
2021-06-26 18:08:35 +00:00
} else {
2021-08-03 01:31:54 +00:00
sortState.sortOrder = "asc";
2021-06-26 18:08:35 +00:00
}
sortState.sortBy = sortField;
}
2021-08-03 01:31:54 +00:00
books.sort((one, two) =>
(one[sortState.sortBy] + one["sortTitle"]).localeCompare(
two[sortState.sortBy] + two["sortTitle"]
)
2021-06-26 18:08:35 +00:00
);
2021-08-03 01:31:54 +00:00
if (sortState.sortOrder === "desc") {
books.reverse();
}
books.forEach((e, i) => (e.rowNumber = i)); // re-key for new sort
2021-06-26 18:08:35 +00:00
2021-08-04 00:10:24 +00:00
$("#books").html(TableTemplate(books));
2021-06-26 18:08:35 +00:00
$("#books tbody tr")
2021-08-03 01:31:54 +00:00
.not(":first") // ignore the headers
2021-06-26 18:08:35 +00:00
.on("click", function () {
2021-08-03 01:31:54 +00:00
updateCurrentBook(books[$(this)[0].id]);
2021-06-26 18:08:35 +00:00
});
$("#books tbody tr th[data-sort-by]").on("click", function () {
2021-08-03 01:31:54 +00:00
renderTable(books, $(this).data("sortBy")); // only add callback when there's a sortBy attribute
2021-06-26 18:08:35 +00:00
});
$("#books tbody tr th[data-sort-by=" + sortState.sortBy + "]").addClass(
sortState.sortOrder
);
}
function updateCurrentBook(book) {
2021-08-04 00:10:24 +00:00
$("#current").html(BookTemplate(book));
2021-06-26 18:08:35 +00:00
}
function titleCleaner(title) {
return title
.replace('"', "")
.replace(":", "")
.replace(/^(An?|The)\s/i, "");
}
2021-08-03 01:31:54 +00:00
function searchCleaner(str) {
return str
.toLowerCase()
.replace('"', "")
.replace(":", "")
.replace("'", "")
.replace(" ", "");
}
function amazonCoverUrl(ISBN) {
return `https://images-na.ssl-images-amazon.com/images/P/${ISBN}.01.LZZ.jpg`;
2021-06-26 18:08:35 +00:00
}
2021-08-03 01:31:54 +00:00
function ISBNfromEAN(EAN) {
2021-06-26 18:08:35 +00:00
ISBN = EAN.slice(3, 12);
var checkdigit =
(11 -
2021-08-03 01:31:54 +00:00
(ISBN.split("").reduce((s, n, k) => s + n * (10 - k), 0) % 11)) %
2021-06-26 18:08:35 +00:00
11;
2021-08-03 01:31:54 +00:00
return ISBN + (checkdigit === 10 ? "X" : checkdigit);
2021-06-26 18:08:35 +00:00
}
2021-08-04 00:10:24 +00:00
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>`;
}
2021-06-26 18:08:35 +00:00
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
>
<a id="reloadLink" href="#">reload</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>