245 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			245 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
<!DOCTYPE html>
 | 
						||
<html>
 | 
						||
  <head>
 | 
						||
    <title>Library</title>
 | 
						||
    <link
 | 
						||
      rel="stylesheet"
 | 
						||
      href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"
 | 
						||
    />
 | 
						||
    <link rel="stylesheet" href="style.css" />
 | 
						||
    <link rel="icon" href="favicon.ico" type="image/x-icon" />
 | 
						||
    <link
 | 
						||
      href="https://fonts.googleapis.com/css?family=Libre+Baskerville:400,700&display=swap"
 | 
						||
      rel="stylesheet"
 | 
						||
      as="font"
 | 
						||
      rel="preload"
 | 
						||
    />
 | 
						||
    <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
 | 
						||
        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 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"
 | 
						||
          rel="noreferrer"
 | 
						||
          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>
 |