initial planning
This commit is contained in:
256
frontend/files/index.html
Normal file
256
frontend/files/index.html
Normal file
@@ -0,0 +1,256 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Library</title>
|
||||
<script type="text/javascript" src="js/jquery.js"></script>
|
||||
<script type="text/javascript" src="js/mustache.js"></script>
|
||||
<script type="text/javascript" src="js/lodash.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/tabletop.js/1.5.1/tabletop.min.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 publicSpreadsheetUrl =
|
||||
"https://docs.google.com/spreadsheets/d/1w5Dc57wV0_rrKFsG7KM-qdPWEpqYk6lFu3JzAA0cSv0/pubhtml";
|
||||
var sortState = {
|
||||
sortBy: "authorLast",
|
||||
sortOrder: "asc",
|
||||
};
|
||||
|
||||
function init() {
|
||||
Tabletop.init({
|
||||
key: publicSpreadsheetUrl,
|
||||
callback: showInfo,
|
||||
simpleSheet: true,
|
||||
});
|
||||
}
|
||||
|
||||
function showInfo(data, tabletop) {
|
||||
$("#reloadLink").unbind("click");
|
||||
$("#reloadLink").on("click", function () {
|
||||
init();
|
||||
});
|
||||
|
||||
$("#search").unbind("input");
|
||||
$("#search").on("input", function (e) {
|
||||
search(data, e.target.value);
|
||||
});
|
||||
|
||||
$.each(data, function (key, value) {
|
||||
value.sortTitle = titleCleaner(value.title);
|
||||
if (!value["isbn-10"] && value["isbn-13"]) {
|
||||
value["isbn-10"] = generateISBNfromEAN(value["isbn-13"]);
|
||||
}
|
||||
if (!value.coverurl && value["isbn-10"]) {
|
||||
value.coverurl = generateAmazonCoverUrl(value["isbn-10"]);
|
||||
}
|
||||
});
|
||||
|
||||
renderTable(data);
|
||||
}
|
||||
|
||||
function search(data, searchString) {
|
||||
searchBy = searchString
|
||||
.toLowerCase()
|
||||
.replace('"', "")
|
||||
.replace(":", "")
|
||||
.replace("'", "")
|
||||
.replace(" ", "");
|
||||
relevantFields = [
|
||||
"title",
|
||||
"author",
|
||||
"genre",
|
||||
"publisher",
|
||||
"series",
|
||||
"year",
|
||||
];
|
||||
|
||||
if (!searchString) {
|
||||
renderTable(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
renderTable(
|
||||
_.filter(data, function (book) {
|
||||
return _.find(_.pick(book, relevantFields), function (field) {
|
||||
return (
|
||||
field
|
||||
.toLowerCase()
|
||||
.replace('"', "")
|
||||
.replace(":", "")
|
||||
.replace("'", "")
|
||||
.replace(" ", "")
|
||||
.indexOf(searchBy) !== -1
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
function renderTable(data, sortField) {
|
||||
if (sortField) {
|
||||
if (sortState.sortBy === sortField) {
|
||||
sortState.sortOrder =
|
||||
sortState.sortOrder === "asc" ? "desc" : "asc"; // swap if we're looping
|
||||
} else {
|
||||
sortState.sortOrder = "asc"; // reset if we've changed columns
|
||||
}
|
||||
sortState.sortBy = sortField;
|
||||
}
|
||||
data = _.orderBy(
|
||||
data,
|
||||
function (o) {
|
||||
return (
|
||||
o[sortState.sortBy].toLowerCase() + o["sortTitle"].toLowerCase()
|
||||
);
|
||||
},
|
||||
sortState.sortOrder
|
||||
);
|
||||
|
||||
$.each(data, function (key, value) {
|
||||
value.rowNumber = key; // re-key for new sort
|
||||
});
|
||||
|
||||
var template = $("#Table").html();
|
||||
var rendered = Mustache.render(template, { books: data });
|
||||
$("#books").html(rendered);
|
||||
$("#books tbody tr")
|
||||
.not(":first")
|
||||
.on("click", function () {
|
||||
updateCurrentBook(data[$(this)[0].id]); // ignore the headers
|
||||
});
|
||||
$("#books tbody tr th[data-sort-by]").on("click", function () {
|
||||
renderTable(data, $(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 updateCurrentBook(book) {
|
||||
var template = $("#View").html();
|
||||
var rendered = Mustache.render(template, { book: book });
|
||||
$("#current").html(rendered);
|
||||
}
|
||||
|
||||
function titleCleaner(title) {
|
||||
return title
|
||||
.replace('"', "")
|
||||
.replace(":", "")
|
||||
.replace(/^(An?|The)\s/i, "");
|
||||
}
|
||||
|
||||
function generateAmazonCoverUrl(ISBN) {
|
||||
return (
|
||||
"https://images-na.ssl-images-amazon.com/images/P/" +
|
||||
ISBN +
|
||||
".01.LZZ.jpg"
|
||||
);
|
||||
}
|
||||
|
||||
function generateISBNfromEAN(EAN) {
|
||||
ISBN = EAN.slice(3, 12);
|
||||
var checkdigit =
|
||||
(11 -
|
||||
(_.reduce(
|
||||
ISBN.split(""),
|
||||
function (sum, num, key) {
|
||||
return sum + num * (10 - key);
|
||||
},
|
||||
0
|
||||
) %
|
||||
11)) %
|
||||
11;
|
||||
return ISBN + (checkdigit == 10 ? "X" : checkdigit);
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
<script id="Table" type="text/html">
|
||||
<table class="bookTable">
|
||||
<tr>
|
||||
<th data-sort-by="sortTitle" class="tHeader title">Title</th>
|
||||
<th data-sort-by="authorLast" 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}}
|
||||
<tr class="tRow {{#onLoan}}onLoan{{/onLoan}}" id="{{rowNumber}}">
|
||||
<td class="title">
|
||||
{{title}} {{#signed}}<span
|
||||
class="signed"
|
||||
title="Signed by the author"
|
||||
>✒</span
|
||||
>︎{{/signed}}
|
||||
</td>
|
||||
<td class="author">{{author}}</td>
|
||||
<td class="publisher">{{publisher}}</td>
|
||||
<td class="year">{{year}}</td>
|
||||
<td class="isbn">{{isbn-13}}</td>
|
||||
</tr>
|
||||
{{/books}}
|
||||
</table>
|
||||
</script>
|
||||
|
||||
<script id="View" type="text/html">
|
||||
{{#book}}
|
||||
{{#coverurl}}
|
||||
<img src="{{coverurl}}"/>
|
||||
{{/coverurl}}
|
||||
<h1 {{#onLoan}}class="onLoan" {{/onLoan}}>{{title}}</h1>
|
||||
<h2>{{author}}</h2>
|
||||
<span>{{isbn-13}}</span><br/>
|
||||
<span>{{publisher}}, {{year}}</span><br/>
|
||||
{{#series}}
|
||||
<span>{{series}}{{#volume}}, Volume {{volume}}</span>{{/volume}}<br/>
|
||||
{{/series}}
|
||||
{{#signed}}
|
||||
<span>Signed by the author ✒</span><br/>
|
||||
{{/signed}}
|
||||
<span>{{format}}</span>
|
||||
{{#onLoan}}
|
||||
<h2 class="onLoan">On loan to {{onLoan}}</h2>
|
||||
{{/onLoan}}
|
||||
<div class="description">
|
||||
<p>{{description}}</p>
|
||||
{{#notes}}
|
||||
<span>Notes:</span>
|
||||
<p>{{notes}}</p>
|
||||
{{/notes}}
|
||||
</div>
|
||||
{{/book}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Reference in New Issue
Block a user