diff --git a/package.json b/package.json index 211a9f5..ddcbf84 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "body-parser": "^1.18.3", "bulma": "^0.7.2", + "cookie-parser": "^1.4.3", "express": "^4.16.4", "express-fileupload": "^1.0.0", "fecha": "^3.0.2", @@ -23,6 +24,7 @@ "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", "striptags": "^3.1.1", + "tinycolor2": "^1.4.1", "unused-filename": "^1.0.0" } } diff --git a/public/css/styles.css b/public/css/styles.css index 66fb60a..ff586ec 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -1,4 +1,115 @@ .is-clickable, .modal-button { cursor: pointer; -} \ No newline at end of file +} + +/* Bookshelf Styling */ +.bookshelf { + border: 4px solid saddlebrown; + background: sienna; +} + +.book-slot { + position: relative; + overflow: visible; + width: 100px; + height: 300px; /* The tallest a book could be */ + border-bottom: 4px solid saddlebrown; + margin: 10px 1px -4px !important; +} +.book-slot.is-thin { + width: 80px; +} +.book-slot.is-thick { + width: 120px; +} + +.book { + position: absolute; + overflow: visible; + top: 30px; + right: 0; + bottom: 0; + left: 0; +} +.book.is-short { + top: 60px; +} +.book.is-tall { + top: 0; +} + +.book .spine { + position: relative; + box-sizing: border-box; + background: #c0ffee; + border: 1px solid #aaa; + width: 100%; + height: 100%; + margin-bottom: 4px; + z-index: 1; + transition: all 0.25s; +} +.book:hover .spine { + width: 120%; + margin-left: -10%; + height: 120%; + margin-top: -20%; + z-index: 2; +} + +.spine .text-container { + width: 260px; + height: 100px; + transform: rotate(90deg) translateX(82px) translateY(80px); + overflow: hidden; + word-break: break-all; +} +.spine .title { + font-size: 1.3em; +} +.spine .subtitle { + font-size: 1.1em; +} + +.book-slot.is-thin .book .spine .text-container { + height: 80px; + transform: rotate(90deg) translateX(92px) translateY(92px); +} +.book-slot.is-thick .book .spine .text-container { + height: 120px; + transform: rotate(90deg) translateX(72px) translateY(72px); +} + +.book.is-short .spine .text-container { + width: 230px; + transform: rotate(90deg) translateX(68px) translateY(66px); +} +.book-slot.is-thin .book.is-short .spine .text-container { + transform: rotate(90deg) translateX(78px) translateY(76px); +} +.book-slot.is-thick .book.is-short .spine .text-container { + transform: rotate(90deg) translateX(58px) translateY(56px); +} +.book.is-short .spine .title { + font-size: 1.1em; +} +.book.is-short .spine .subtitle { + font-size: 0.9em; +} +.book.is-tall .spine .text-container { + width: 290px; + transform: rotate(90deg) translateX(98px) translateY(95px); +} +.book-slot.is-thin .book.is-tall .spine .text-container { + transform: rotate(90deg) translateX(108px) translateY(106px); +} +.book-slot.is-thick .book.is-tall .spine .text-container { + transform: rotate(90deg) translateX(88px) translateY(86px); +} +.book.is-tall .spine .title { + font-size: 1.3em; +} +.book.is-tall .spine .subtitle { + font-size: 1.1em; +} diff --git a/public/js/little-library.js b/public/js/little-library.js index c9389f6..c4c0566 100644 --- a/public/js/little-library.js +++ b/public/js/little-library.js @@ -33,6 +33,12 @@ $(document).ready(function() { } }); + $('#readableToggle').click(function() { + var useReadable = getCookieValue('useReadable'); + document.cookie = 'useReadable=' + (useReadable !== 'yes' ? 'yes' : 'no'); + window.location.reload(); + }); + $('.modal-background, .modal-close, .modal-card-head .delete, .modal .close').click(function() { $(this).closest('.modal').removeClass('is-active'); downloadButton = undefined; @@ -64,4 +70,9 @@ $(document).ready(function() { } $('#bookFileName').text(fileName ? fileName : 'None Selected'); }); -}); \ No newline at end of file +}); + +function getCookieValue(key) { + var matches = document.cookie.match('(^|;)\\s*' + key + '\\s*=\\s*([^;]+)'); + return matches ? matches.pop() : '' +} \ No newline at end of file diff --git a/routes/get_home.js b/routes/get_home.js index 3456ae3..b1a85c3 100644 --- a/routes/get_home.js +++ b/routes/get_home.js @@ -2,21 +2,30 @@ const path = require('path'); const fs = require('fs'); const snarkdown = require('snarkdown'); const fecha = require('fecha'); +const tinycolor = require('tinycolor2'); + +const settings = require('../settings.json'); module.exports = function (app) { app.server.get('/', (req, res) => { + const useReadable = req.cookies['useReadable'] === 'yes'; const files = fs.readdirSync(app.fileLocation).filter(fileName => fileName.includes('.json')) .map(fileName => { // Cache the file data so sorting doesn't need to re-check each file - return { name: fileName, time: fs.statSync(path.resolve(app.fileLocation, fileName)).mtime.getTime() }; - }).sort((a, b) => a.time - b.time).map(v => v.name); // Sort from oldest to newest. + const stats = fs.statSync(path.resolve(app.fileLocation, fileName)); + return { + name: fileName, + size: stats.size / (1000 * 1000), + time: stats.mtime.getTime(), + }; + }).sort((a, b) => a.time - b.time); // Sort from oldest to newest. - let books = files.map(fileName => { - const bookData = JSON.parse(fs.readFileSync(path.resolve(app.fileLocation, fileName), 'utf8')); + let books = files.map(fileDetails => { + const bookData = JSON.parse(fs.readFileSync(path.resolve(app.fileLocation, fileDetails.name), 'utf8')); if (bookData.hasOwnProperty('fileName')) return ''; bookData.author = bookData.author ? bookData.author : 'author not provided'; bookData.contributor = bookData.contributor ? bookData.contributor : 'Anonymous'; - const id = fileName.replace('.json', ''); + const id = fileDetails.name.replace('.json', ''); const confirmId = 'confirm_' + id; const added = fecha.format(new Date(bookData.added), 'hh:mm:ssA on dddd MMMM Do, YYYY'); const modal = app.templater.fill('./templates/elements/modalCard.html', { @@ -37,10 +46,20 @@ module.exports = function (app) { }), footer: 'Close Take Book', }); - return app.templater.fill('./templates/elements/book.html', { + const maxSize = settings.maxFileSize > 0 ? settings.maxFileSize : 10; + let spineColor = tinycolor('#' + id.substr(0, 6)); + if (!spineColor.isValid()) { + spineColor = tinycolor.random(); + } + return app.templater.fill(useReadable ? './templates/elements/book_readable.html' : './templates/elements/book.html', { id, title: bookData.title, author: bookData.author, + thickness: (fileDetails.size > (maxSize * 0.3)) || (bookData.title.length > 28) + ? 'is-thick' : (fileDetails.size < (maxSize * 0.6) ? 'is-thin' : ''), + tallness: bookData.title.length > 16 ? 'is-tall' : (bookData.title.length < 8 ? 'is-short' : ''), + spineColor: spineColor.toString(), + textColor: spineColor.isLight() ? '#000000' : '#ffffff', fileType: bookData.fileType, modal, }); @@ -50,7 +69,11 @@ module.exports = function (app) { books = '
The shelf is empty. Would you like to add a book?
'; } - const body = '

Available Books

' + books + '
'; + const body = '

Available Books

' + + '' + (!useReadable ? 'Make it readable' : 'Make it look cool') + '' + + '
' + + books + + '
'; const html = app.templater.fill('./templates/htmlContainer.html', { title: 'View', resourcePath: (req.url.substr(-1) === '/' ? '../' : './'), diff --git a/routes/middleware.js b/routes/middleware.js index 68a2d46..3f4559e 100644 --- a/routes/middleware.js +++ b/routes/middleware.js @@ -2,6 +2,7 @@ const path = require('path'); const fs = require('fs'); const express = require('express'); const helmet = require('helmet'); +const cookieParser = require('cookie-parser'); const bodyParser = require('body-parser'); const fileUpload = require('express-fileupload'); @@ -9,6 +10,8 @@ const settings = require('../settings.json'); module.exports = function (app) { app.server.use(helmet()); + + app.server.use(cookieParser()); app.server.use(bodyParser.json()); // support json encoded bodies app.server.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies diff --git a/templates/elements/book.html b/templates/elements/book.html index f082172..f670dfc 100644 --- a/templates/elements/book.html +++ b/templates/elements/book.html @@ -1,10 +1,14 @@ -
-