Improve templates; Add navbar; Show book details
This commit is contained in:
		
							parent
							
								
									c0b154e1f6
								
							
						
					
					
						commit
						14c0dddf93
					
				
					 9 changed files with 139 additions and 44 deletions
				
			
		| 
						 | 
				
			
			@ -17,7 +17,7 @@
 | 
			
		|||
    "filenamify": "^2.1.0",
 | 
			
		||||
    "helmet": "^3.15.0",
 | 
			
		||||
    "jquery": "^3.3.1",
 | 
			
		||||
    "slugify": "^1.3.4",
 | 
			
		||||
    "snarkdown": "^1.2.2",
 | 
			
		||||
    "socket.io": "^2.2.0",
 | 
			
		||||
    "socket.io-client": "^2.2.0",
 | 
			
		||||
    "unused-filename": "^1.0.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,13 @@
 | 
			
		|||
$(document).ready(function() {
 | 
			
		||||
  var socket = io();
 | 
			
		||||
  
 | 
			
		||||
  $('.modal-background, .modal-close').click(function() {
 | 
			
		||||
    $(this).parent('.modal').removeClass('is-active');
 | 
			
		||||
  $('.modal-background, .modal-close, .modal-card-head .delete, .modal-card-foot .close').click(function() {
 | 
			
		||||
    $(this).closest('.modal').removeClass('is-active');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('.modal-button').click(function() {
 | 
			
		||||
    var modal = $(this).data('modal');
 | 
			
		||||
    $('#' + modal).addClass('is-active');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('#book').change(function() {
 | 
			
		||||
| 
						 | 
				
			
			@ -16,9 +21,4 @@ $(document).ready(function() {
 | 
			
		|||
    }
 | 
			
		||||
    $('#bookFileName').text(fileName ? fileName : 'None Selected');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  $('.book').click(function() {
 | 
			
		||||
    var modal = $(this).data('modal');
 | 
			
		||||
    $('#' + modal).addClass('is-active');
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										64
									
								
								server.js
									
										
									
									
									
								
							
							
						
						
									
										64
									
								
								server.js
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -8,6 +8,7 @@ const bodyParser = require('body-parser');
 | 
			
		|||
const fileUpload = require('express-fileupload');
 | 
			
		||||
const filenamify = require('filenamify');
 | 
			
		||||
const unusedFilename = require('unused-filename');
 | 
			
		||||
const snarkdown = require('snarkdown');
 | 
			
		||||
 | 
			
		||||
const settings = require('./settings.json');
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,21 +45,7 @@ function Server () {
 | 
			
		|||
  this.server.use('/css', express.static(path.join(__dirname, './public/css/')));
 | 
			
		||||
 | 
			
		||||
  this.server.get('/', (req, res) => {
 | 
			
		||||
    const files = fs.readdirSync(this.fileLocation).filter(fileName => fileName.includes('.json'));
 | 
			
		||||
    const books = files.map(fileName => {
 | 
			
		||||
      const bookData = JSON.parse(fs.readFileSync(path.resolve(this.fileLocation, fileName), 'utf8'));
 | 
			
		||||
      const id = fileName.replace('.json', '');
 | 
			
		||||
      const content = '<div class="box">' + bookData.summary + '</div>';
 | 
			
		||||
      const modal = this.fillTemplate('./templates/elements/modal.html', { content, id });
 | 
			
		||||
      return this.fillTemplate('./templates/elements/book.html', {
 | 
			
		||||
        id,
 | 
			
		||||
        title: bookData.title,
 | 
			
		||||
        author: bookData.author,
 | 
			
		||||
        modal,
 | 
			
		||||
      })
 | 
			
		||||
    }).join('');
 | 
			
		||||
    const body = this.fillTemplate('./templates/pages/booksList.html', { books });
 | 
			
		||||
    const html = this.fillTemplate('./templates/htmlContainer.html', { body });
 | 
			
		||||
    const html = this.generateHomePage();
 | 
			
		||||
    if (html) {
 | 
			
		||||
      res.send(html);
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,14 +53,19 @@ function Server () {
 | 
			
		|||
    }
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  this.server.get('/give', (req, res) => {
 | 
			
		||||
    const body = this.fillTemplate('./templates/pages/uploadForm.html');
 | 
			
		||||
    const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body });
 | 
			
		||||
    res.send(html);
 | 
			
		||||
  });
 | 
			
		||||
  this.server.post('/give', (req, res) => {
 | 
			
		||||
    if (Object.keys(req.files).length > 0
 | 
			
		||||
      && req.body.hasOwnProperty('title') && req.body.title.trim() !== ''
 | 
			
		||||
      && req.body.hasOwnProperty('summary') && req.body.summary.trim() !== '') {
 | 
			
		||||
      const { book } = req.files;
 | 
			
		||||
      const { title, author, summary } = req.body;
 | 
			
		||||
      const { title, author, summary, contributor } = req.body;
 | 
			
		||||
      const fileType = book.name.substr(book.name.lastIndexOf('.'));
 | 
			
		||||
      this.addBook({ book, title, author, summary, fileType }, () => {
 | 
			
		||||
      this.addBook({ book, title, author, summary, contributor, fileType }, () => {
 | 
			
		||||
        const messageBox = this.fillTemplate('./templates/elements/messageBox.html', {
 | 
			
		||||
          style: 'is-success',
 | 
			
		||||
          header: 'Upload Successful',
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +76,7 @@ function Server () {
 | 
			
		|||
          content: messageBox,
 | 
			
		||||
        });
 | 
			
		||||
        const body = this.fillTemplate('./templates/pages/uploadForm.html');
 | 
			
		||||
        const html = this.fillTemplate('./templates/htmlContainer.html', { body, modal });
 | 
			
		||||
        const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, modal });
 | 
			
		||||
        res.send(html);
 | 
			
		||||
      }, (err) => {
 | 
			
		||||
        const messageBox = this.fillTemplate('./templates/elements/messageBox.html', {
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +89,7 @@ function Server () {
 | 
			
		|||
          content: messageBox,
 | 
			
		||||
        });
 | 
			
		||||
        const body = this.fillTemplate('./templates/pages/uploadForm.html');
 | 
			
		||||
        const html = this.fillTemplate('./templates/htmlContainer.html', { body, modal });
 | 
			
		||||
        const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, modal });
 | 
			
		||||
        res.send(html);
 | 
			
		||||
      });
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -117,7 +109,7 @@ function Server () {
 | 
			
		|||
        message: errorMessage,
 | 
			
		||||
      });
 | 
			
		||||
      const body = this.fillTemplate('./templates/pages/uploadForm.html');
 | 
			
		||||
      const html = this.fillTemplate('./templates/htmlContainer.html', { body, message });
 | 
			
		||||
      const html = this.fillTemplate('./templates/htmlContainer.html', { title: 'Give a Book', body, message });
 | 
			
		||||
      res.send(html);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
| 
						 | 
				
			
			@ -130,7 +122,7 @@ function Server () {
 | 
			
		|||
      if (fileLocation) {
 | 
			
		||||
        console.log(socket.id + ' removed ' + bookId);
 | 
			
		||||
        const downloadLocation = fileLocation.substr(fileLocation.lastIndexOf('/'));
 | 
			
		||||
        socket.emit('./files' + downloadLocation);
 | 
			
		||||
        socket.emit('get book', './files' + downloadLocation);
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +145,9 @@ Server.prototype.fillTemplate = function (file, templateVars = {}) {
 | 
			
		|||
      this.templateCache[file] = data;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let filledTemplate = data.replace(/\{\{allowedFormats\}\}/g, settings.allowedFormats.join(','))
 | 
			
		||||
    let filledTemplate = data.replace(/\{\{siteTitle\}\}/g, settings.siteTitle)
 | 
			
		||||
      .replace(/\{\{titleSeparator\}\}/g, settings.titleSeparator)
 | 
			
		||||
      .replace(/\{\{allowedFormats\}\}/g, settings.allowedFormats.join(','))
 | 
			
		||||
      .replace(/\{\{maxFileSize\}\}/g, (settings.maxFileSize > 0 ? settings.maxFileSize + 'MB' : 'no'));
 | 
			
		||||
 | 
			
		||||
    for (let templateVar in templateVars) {
 | 
			
		||||
| 
						 | 
				
			
			@ -170,6 +164,31 @@ Server.prototype.fillTemplate = function (file, templateVars = {}) {
 | 
			
		|||
  return data;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Server.prototype.generateHomePage = function () {
 | 
			
		||||
  const files = fs.readdirSync(this.fileLocation).filter(fileName => fileName.includes('.json'));
 | 
			
		||||
  const books = files.map(fileName => {
 | 
			
		||||
    const bookData = JSON.parse(fs.readFileSync(path.resolve(this.fileLocation, fileName), 'utf8'));
 | 
			
		||||
    const id = fileName.replace('.json', '');
 | 
			
		||||
    const header = '<h2 class="title">' + bookData.title + '</h2><h4 class="subtitle">' + bookData.author + '</h4>';
 | 
			
		||||
    const content = '<div class="content"><h4>Contributed by ' + bookData.contributor + '</h4>' + snarkdown(bookData.summary) + '</div>';
 | 
			
		||||
    const footer = '<a class="button close">Close</a> <a class="button is-success take-book">Take Book</a>';
 | 
			
		||||
    const modal = this.fillTemplate('./templates/elements/modalCard.html', {
 | 
			
		||||
      id,
 | 
			
		||||
      header,
 | 
			
		||||
      content,
 | 
			
		||||
      footer,
 | 
			
		||||
    });
 | 
			
		||||
    return this.fillTemplate('./templates/elements/book.html', {
 | 
			
		||||
      id,
 | 
			
		||||
      title: bookData.title,
 | 
			
		||||
      author: bookData.author,
 | 
			
		||||
      modal,
 | 
			
		||||
    });
 | 
			
		||||
  }).join('');
 | 
			
		||||
  const body = this.fillTemplate('./templates/pages/booksList.html', { books });
 | 
			
		||||
  return this.fillTemplate('./templates/htmlContainer.html', { title: 'View', body });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Server.prototype.broadcastVisitors = function () {
 | 
			
		||||
  const numberConnected = this.io.of('/').clients().connected.length;
 | 
			
		||||
  this.io.emit('connected', numberConnected);
 | 
			
		||||
| 
						 | 
				
			
			@ -190,6 +209,7 @@ Server.prototype.addBook = function (uploadData = {}, success = () => {}, error
 | 
			
		|||
    title: uploadData.title.trim(),
 | 
			
		||||
    author: uploadData.author.trim(),
 | 
			
		||||
    summary: uploadData.summary.trim(),
 | 
			
		||||
    contributor: uploadData.contributor.trim(),
 | 
			
		||||
    fileType: book.name.substr(book.name.lastIndexOf('.')),
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
  "port": 3000,
 | 
			
		||||
  "siteTitle": "Little Library",
 | 
			
		||||
  "titleSeparator": " | ",
 | 
			
		||||
  "fileLocation": "./public/files/",
 | 
			
		||||
  "historyLocation": "./public/history/",
 | 
			
		||||
  "maxLibrarySize": 0,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<div class="column is-one-quarter">
 | 
			
		||||
  <div class="box book has-text-centered" data-modal="{{id}}">
 | 
			
		||||
  <div class="box modal-button has-text-centered" data-modal="{{id}}">
 | 
			
		||||
    <h2 class="title">{{title}}</h2>
 | 
			
		||||
    <h4 class="subtitle">{{author}}</h4>
 | 
			
		||||
  </div>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								templates/elements/modalCard.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								templates/elements/modalCard.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,15 @@
 | 
			
		|||
<div class="modal {{isActive}}" id="{{id}}">
 | 
			
		||||
  <div class="modal-background"></div>
 | 
			
		||||
  <div class="modal-card">
 | 
			
		||||
    <header class="modal-card-head">
 | 
			
		||||
      <div class="modal-card-title">{{header}}</div>
 | 
			
		||||
      <button class="delete" aria-label="close"></button>
 | 
			
		||||
    </header>
 | 
			
		||||
    <section class="modal-card-body">
 | 
			
		||||
      {{content}}
 | 
			
		||||
    </section>
 | 
			
		||||
    <footer class="modal-card-foot">
 | 
			
		||||
      {{footer}}
 | 
			
		||||
    </footer>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
| 
						 | 
				
			
			@ -6,7 +6,7 @@
 | 
			
		|||
  <meta charset="utf-8">
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1">
 | 
			
		||||
 | 
			
		||||
  <title>{{title}}Little Library</title>
 | 
			
		||||
  <title>{{title}}{{titleSeparator}}{{siteTitle}}</title>
 | 
			
		||||
  <meta name="description" content="A digital give-a-book, take-a-book library for ebooks">
 | 
			
		||||
 | 
			
		||||
  <link rel="stylesheet" href="./css/bulma.min.css?v=1.0">
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +17,57 @@
 | 
			
		|||
</head>
 | 
			
		||||
 | 
			
		||||
<body>
 | 
			
		||||
  {{message}}
 | 
			
		||||
  <nav class="navbar" role="navigation" aria-label="main navigation">
 | 
			
		||||
    <div class="navbar-brand">
 | 
			
		||||
      <a class="navbar-item" href="/">
 | 
			
		||||
        <h1 class="title">{{siteTitle}}</h1>
 | 
			
		||||
      </a>
 | 
			
		||||
  
 | 
			
		||||
      <a role="button" class="navbar-burger burger" aria-label="menu" aria-expanded="false" data-target="navbarBasicExample">
 | 
			
		||||
        <span aria-hidden="true"></span>
 | 
			
		||||
        <span aria-hidden="true"></span>
 | 
			
		||||
        <span aria-hidden="true"></span>
 | 
			
		||||
      </a>
 | 
			
		||||
    </div>
 | 
			
		||||
  
 | 
			
		||||
    <div id="navbarBasicExample" class="navbar-menu">
 | 
			
		||||
      <div class="navbar-start">
 | 
			
		||||
        <a class="navbar-item" href="/">
 | 
			
		||||
          View
 | 
			
		||||
        </a>
 | 
			
		||||
 | 
			
		||||
  {{body}}
 | 
			
		||||
        <a class="navbar-item" href="/give">
 | 
			
		||||
          Give
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
  
 | 
			
		||||
      <div class="navbar-end">
 | 
			
		||||
        <a class="navbar-item" href="/about">
 | 
			
		||||
          About
 | 
			
		||||
        </a>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </nav>
 | 
			
		||||
 | 
			
		||||
  <section class="section">
 | 
			
		||||
    <div class="container">
 | 
			
		||||
 | 
			
		||||
      {{message}}
 | 
			
		||||
 | 
			
		||||
      {{body}}
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
  </section>
 | 
			
		||||
 | 
			
		||||
  <footer class="footer">
 | 
			
		||||
    <div class="content has-text-centered">
 | 
			
		||||
      <p>
 | 
			
		||||
        <strong>Little Library</strong> by <a href="https://robbie.antenesse.net">Robbie Antenesse</a>. The source code is licensed
 | 
			
		||||
        <a href="http://opensource.org/licenses/mit-license.php">MIT</a>. The website content
 | 
			
		||||
        is licensed <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/">CC BY NC SA 4.0</a>.
 | 
			
		||||
      </p>
 | 
			
		||||
    </div>
 | 
			
		||||
  </footer>
 | 
			
		||||
 | 
			
		||||
  {{modal}}
 | 
			
		||||
  <script src="./js/little-library.js"></script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,28 @@
 | 
			
		|||
<form action="./give" method="post" enctype="multipart/form-data">
 | 
			
		||||
  <div class="field">
 | 
			
		||||
    <label class="label">Book Title:
 | 
			
		||||
    <label class="label" for="title">Book Title:</label>
 | 
			
		||||
    <div class="control">
 | 
			
		||||
      <input type="text" class="input" name="title" id="title">
 | 
			
		||||
    </label>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="field">
 | 
			
		||||
    <label class="label">Book Author:
 | 
			
		||||
    <label class="label" for="author"><span class="is-italic has-text-weight-normal has-text-grey">(Optional)</span> Book Author:</label>
 | 
			
		||||
    <div class="control">
 | 
			
		||||
      <input type="text" class="input" name="author" id="author">
 | 
			
		||||
    </label>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="field">
 | 
			
		||||
    <label class="label">Why you're sharing it:<br>
 | 
			
		||||
    <label class="label" for="summary">Why you're sharing it:</label>
 | 
			
		||||
    <div class="control">
 | 
			
		||||
      <div class="help">Markdown Enabled</div>
 | 
			
		||||
      <textarea class="textarea" name="summary" id="summary"></textarea>
 | 
			
		||||
    </label>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="field">
 | 
			
		||||
    <label class="label" for="contributor"><span class="is-italic has-text-weight-normal has-text-grey">(Optional)</span> Your name:</label>
 | 
			
		||||
    <div class="control">
 | 
			
		||||
      <input type="text" class="input" name="contributor" id="contributor">
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
 | 
			
		||||
  <div class="field">
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -690,10 +690,10 @@ setprototypeof@1.1.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656"
 | 
			
		||||
  integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==
 | 
			
		||||
 | 
			
		||||
slugify@^1.3.4:
 | 
			
		||||
  version "1.3.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.4.tgz#78d2792d7222b55cd9fc81fa018df99af779efeb"
 | 
			
		||||
  integrity sha512-KP0ZYk5hJNBS8/eIjGkFDCzGQIoZ1mnfQRYS5WM3273z+fxGWXeN0fkwf2ebEweydv9tioZIHGZKoF21U07/nw==
 | 
			
		||||
snarkdown@^1.2.2:
 | 
			
		||||
  version "1.2.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/snarkdown/-/snarkdown-1.2.2.tgz#0cfe2f3012b804de120fc0c9f7791e869c59cc74"
 | 
			
		||||
  integrity sha1-DP4vMBK4BN4SD8DJ93kehpxZzHQ=
 | 
			
		||||
 | 
			
		||||
socket.io-adapter@~1.1.0:
 | 
			
		||||
  version "1.1.1"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue