diff --git a/.eslintignore b/.eslintignore index 5367759..a0c23a2 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,4 +1,5 @@ **/*.min.js -dist/js/* +dist/* +dist-dev/* public/libs/* src/libs/* diff --git a/.gitignore b/.gitignore index 99c5c60..ae44739 100644 --- a/.gitignore +++ b/.gitignore @@ -51,6 +51,9 @@ package-lock.json # Custom pages directory /pages/custom +# Dist dev +/dist-dev + # User files .DS_Store .nvmrc diff --git a/.stylelintignore b/.stylelintignore index 770e0ed..0275719 100644 --- a/.stylelintignore +++ b/.stylelintignore @@ -1,3 +1,4 @@ -dist/css/* +dist/* +dist-dev/* public/libs/* src/libs/* diff --git a/controllers/albumsController.js b/controllers/albumsController.js index c42838d..f30e048 100644 --- a/controllers/albumsController.js +++ b/controllers/albumsController.js @@ -10,6 +10,11 @@ const logger = require('./../logger') const db = require('knex')(config.database) const self = { + // Don't forget to update max length of text inputs in + // home.js & dashboard.js when changing these values + titleMaxLength: 280, + descMaxLength: 4000, + onHold: new Set() } @@ -109,7 +114,7 @@ self.create = async (req, res, next) => { if (!user) return const name = typeof req.body.name === 'string' - ? utils.escape(req.body.name.trim()) + ? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength)) : '' if (!name) @@ -140,7 +145,7 @@ self.create = async (req, res, next) => { download: (req.body.download === false || req.body.download === 0) ? 0 : 1, public: (req.body.public === false || req.body.public === 0) ? 0 : 1, description: typeof req.body.description === 'string' - ? utils.escape(req.body.description.trim()) + ? utils.escape(req.body.description.trim().substring(0, self.descMaxLength)) : '' }) utils.invalidateStatsCache('albums') @@ -159,7 +164,7 @@ self.delete = async (req, res, next) => { const id = req.body.id const purge = req.body.purge - if (id === undefined || id === '') + if (!Number.isFinite(id)) return res.json({ success: false, description: 'No album specified.' }) try { @@ -184,7 +189,7 @@ self.delete = async (req, res, next) => { userid: user.id }) .update('enabled', 0) - utils.invalidateStatsCache('albums') + utils.invalidateAlbumsCache([id]) const identifier = await db.table('albums') .select('identifier') @@ -215,7 +220,7 @@ self.edit = async (req, res, next) => { return res.json({ success: false, description: 'No album specified.' }) const name = typeof req.body.name === 'string' - ? utils.escape(req.body.name.trim()) + ? utils.escape(req.body.name.trim().substring(0, self.titleMaxLength)) : '' if (!name) @@ -245,13 +250,14 @@ self.edit = async (req, res, next) => { }) .update({ name, + editedAt: Math.floor(Date.now() / 1000), download: Boolean(req.body.download), public: Boolean(req.body.public), description: typeof req.body.description === 'string' - ? utils.escape(req.body.description.trim()) + ? utils.escape(req.body.description.trim().substring(0, self.descMaxLength)) : '' }) - utils.invalidateStatsCache('albums') + utils.invalidateAlbumsCache([id]) if (!req.body.requestLink) return res.json({ success: true, name }) diff --git a/controllers/authController.js b/controllers/authController.js index 0f41074..85b4c37 100644 --- a/controllers/authController.js +++ b/controllers/authController.js @@ -1,4 +1,3 @@ -const { promisify } = require('util') const bcrypt = require('bcrypt') const randomstring = require('randomstring') const perms = require('./permissionController') @@ -8,11 +7,27 @@ const config = require('./../config') const logger = require('./../logger') const db = require('knex')(config.database) +// Don't forget to update min/max length of text inputs in auth.njk +// when changing these values. const self = { - compare: promisify(bcrypt.compare), - hash: promisify(bcrypt.hash) + user: { + min: 4, + max: 32 + }, + pass: { + min: 6, + // Should not be more than 72 characters + // https://github.com/kelektiv/node.bcrypt.js#security-issues-and-concerns + max: 64, + // Length of randomized password + // when resetting passwordthrough Dashboard's Manage Users. + rand: 16 + } } +// https://github.com/kelektiv/node.bcrypt.js#a-note-on-rounds +const saltRounds = 10 + self.verify = async (req, res, next) => { const username = typeof req.body.username === 'string' ? req.body.username.trim() @@ -37,7 +52,7 @@ self.verify = async (req, res, next) => { if (user.enabled === false || user.enabled === 0) return res.json({ success: false, description: 'This account has been disabled.' }) - const result = await self.compare(password, user.password) + const result = await bcrypt.compare(password, user.password) if (result === false) return res.json({ success: false, description: 'Wrong password.' }) else @@ -55,14 +70,14 @@ self.register = async (req, res, next) => { const username = typeof req.body.username === 'string' ? req.body.username.trim() : '' - if (username.length < 4 || username.length > 32) - return res.json({ success: false, description: 'Username must have 4-32 characters.' }) + if (username.length < self.user.min || username.length > self.user.max) + return res.json({ success: false, description: `Username must have ${self.user.min}-${self.user.max} characters.` }) const password = typeof req.body.password === 'string' ? req.body.password.trim() : '' - if (password.length < 6 || password.length > 64) - return res.json({ success: false, description: 'Password must have 6-64 characters.' }) + if (password.length < self.pass.min || password.length > self.pass.max) + return res.json({ success: false, description: `Password must have ${self.pass.min}-${self.pass.max} characters.` }) try { const user = await db.table('users') @@ -72,7 +87,7 @@ self.register = async (req, res, next) => { if (user) return res.json({ success: false, description: 'Username already exists.' }) - const hash = await self.hash(password, 10) + const hash = await bcrypt.hash(password, saltRounds) const token = await tokens.generateUniqueToken() if (!token) @@ -103,11 +118,11 @@ self.changePassword = async (req, res, next) => { const password = typeof req.body.password === 'string' ? req.body.password.trim() : '' - if (password.length < 6 || password.length > 64) - return res.json({ success: false, description: 'Password must have 6-64 characters.' }) + if (password.length < self.pass.min || password.length > self.pass.max) + return res.json({ success: false, description: `Password must have ${self.pass.min}-${self.pass.max} characters.` }) try { - const hash = await self.hash(password, 10) + const hash = await bcrypt.hash(password, saltRounds) await db.table('users') .where('id', user.id) @@ -144,8 +159,11 @@ self.editUser = async (req, res, next) => { if (req.body.username !== undefined) { update.username = String(req.body.username).trim() - if (update.username.length < 4 || update.username.length > 32) - return res.json({ success: false, description: 'Username must have 4-32 characters.' }) + if (update.username.length < self.user.min || update.username.length > self.user.max) + return res.json({ + success: false, + description: `Username must have ${self.user.min}-${self.user.max} characters.` + }) } if (req.body.enabled !== undefined) @@ -159,8 +177,8 @@ self.editUser = async (req, res, next) => { let password if (req.body.resetPassword) { - password = randomstring.generate(16) - update.password = await self.hash(password, 10) + password = randomstring.generate(self.pass.rand) + update.password = await bcrypt.hash(password, saltRounds) } await db.table('users') diff --git a/controllers/pathsController.js b/controllers/pathsController.js index 3b7102e..8559653 100644 --- a/controllers/pathsController.js +++ b/controllers/pathsController.js @@ -33,7 +33,9 @@ self.thumbPlaceholder = path.resolve(config.uploads.generateThumbs.placeholder | self.logs = path.resolve(config.logsFolder) self.customPages = path.resolve('pages/custom') -self.dist = path.resolve('dist') +self.dist = process.env.NODE_ENV === 'development' + ? path.resolve('dist-dev') + : path.resolve('dist') self.public = path.resolve('public') self.errorRoot = path.resolve(config.errorPages.rootDir) diff --git a/controllers/uploadController.js b/controllers/uploadController.js index 5bd0949..eec6406 100644 --- a/controllers/uploadController.js +++ b/controllers/uploadController.js @@ -51,8 +51,18 @@ const initChunks = async uuid => { } const executeMulter = multer({ + // Guide: https://github.com/expressjs/multer#limits limits: { - fileSize: maxSizeBytes + fileSize: maxSizeBytes, + // Maximum number of non-file fields. + // Dropzone.js will add 6 extra fields for chunked uploads. + // We don't use them for anything else. + fields: 6, + // Maximum number of file fields. + // Chunked uploads still need to provide only 1 file field. + // Otherwise, only one of the files will end up being properly stored, + // and that will also be as a chunk. + files: 20 }, fileFilter (req, file, cb) { file.extname = utils.extname(file.originalname) @@ -101,7 +111,8 @@ const executeMulter = multer({ return cb(null, name) } }) -}).array('files[]') +}).array('files[]', { +}) self.isExtensionFiltered = extname => { // If empty extension needs to be filtered @@ -621,10 +632,12 @@ self.storeFilesToDb = async (req, res, user, infoMap) => { utils.invalidateStatsCache('uploads') // Update albums' timestamp - if (authorizedIds.length) + if (authorizedIds.length) { await db.table('albums') .whereIn('id', authorizedIds) .update('editedAt', Math.floor(Date.now() / 1000)) + utils.invalidateAlbumsCache(authorizedIds) + } } return files.concat(exists) diff --git a/controllers/utilsController.js b/controllers/utilsController.js index da2b8fc..d383f12 100644 --- a/controllers/utilsController.js +++ b/controllers/utilsController.js @@ -25,7 +25,9 @@ const self = { imageExts: ['.webp', '.jpg', '.jpeg', '.gif', '.png', '.tiff', '.tif', '.svg'], videoExts: ['.webm', '.mp4', '.wmv', '.avi', '.mov', '.mkv'], - ffprobe: promisify(ffmpeg.ffprobe) + ffprobe: promisify(ffmpeg.ffprobe), + + albumsCache: {} } const statsCache = { @@ -57,7 +59,7 @@ const statsCache = { } } -const cloudflareAuth = config.cloudflare.apiKey && config.cloudflare.email && config.cloudflare.zoneId +const cloudflareAuth = config.cloudflare && config.cloudflare.apiKey && config.cloudflare.email && config.cloudflare.zoneId self.mayGenerateThumb = extname => { return (config.uploads.generateThumbs.image && self.imageExts.includes(extname)) || @@ -504,6 +506,14 @@ self.bulkDeleteExpired = async (dryrun) => { return result } +self.invalidateAlbumsCache = albumids => { + for (const albumid of albumids) { + delete self.albumsCache[albumid] + delete self.albumsCache[`${albumid}-nojs`] + } + self.invalidateStatsCache('albums') +} + self.invalidateStatsCache = type => { if (!['albums', 'users', 'uploads'].includes(type)) return statsCache[type].invalidatedAt = Date.now() @@ -660,6 +670,8 @@ self.stats = async (req, res, next) => { stats.uploads = statsCache.uploads.cache } else { statsCache.uploads.generating = true + statsCache.uploads.generatedAt = Date.now() + stats.uploads = { _types: { number: ['total', 'images', 'videos', 'others'] @@ -700,7 +712,6 @@ self.stats = async (req, res, next) => { // Update cache statsCache.uploads.cache = stats.uploads - statsCache.uploads.generatedAt = Date.now() statsCache.uploads.generating = false } @@ -711,6 +722,8 @@ self.stats = async (req, res, next) => { stats.users = statsCache.users.cache } else { statsCache.users.generating = true + statsCache.users.generatedAt = Date.now() + stats.users = { _types: { number: ['total', 'disabled'] @@ -742,7 +755,6 @@ self.stats = async (req, res, next) => { // Update cache statsCache.users.cache = stats.users - statsCache.users.generatedAt = Date.now() statsCache.users.generating = false } @@ -753,6 +765,8 @@ self.stats = async (req, res, next) => { stats.albums = statsCache.albums.cache } else { statsCache.albums.generating = true + statsCache.albums.generatedAt = Date.now() + stats.albums = { _types: { number: ['total', 'active', 'downloadable', 'public', 'generatedZip'] @@ -789,7 +803,6 @@ self.stats = async (req, res, next) => { // Update cache statsCache.albums.cache = stats.albums - statsCache.albums.generatedAt = Date.now() statsCache.albums.generating = false } diff --git a/dist/css/dashboard.css b/dist/css/dashboard.css index dd86caa..cf250a8 100644 --- a/dist/css/dashboard.css +++ b/dist/css/dashboard.css @@ -1,2 +1,2 @@ -body{-webkit-animation:none;animation:none}#dashboard{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}.section{background:none}.menu-list a{color:#3794d2}.menu-list a:hover{color:#60a8dc;background-color:#4d4d4d}.menu-list a.is-active{color:#eff0f1;background-color:#3794d2}.menu-list a[disabled]{color:#7a7a7a;cursor:not-allowed}.menu-list a[disabled]:hover{background:none}ul#albumsContainer{border-left:0;padding-left:0}ul#albumsContainer li{border-left:1px solid #898b8d;padding-left:.75em}#page.fade-in,ul#albumsContainer li{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}.pagination{margin-bottom:1.25rem}.pagination a:not([disabled]){color:#eff0f1;border-color:#4d4d4d;background-color:#31363b}a.pagination-link:not(.is-current):hover,a.pagination-next:not([disabled]):hover,a.pagination-previous:not([disabled]):hover{color:#eff0f1;border-color:#60a8dc;background-color:#31363b}a.pagination-link.is-current{background-color:#3794d2;border-color:#3794d2}a.pagination-link.is-current:hover{border-color:#60a8dc}li[data-action=page-ellipsis]{cursor:pointer}.label{color:#bdc3c7}.menu-list li ul{border-left-color:#898b8d}.image-container .checkbox{position:absolute;top:12px;left:12px}.no-touch .image-container .checkbox{opacity:.5}.no-touch .image-container .controls,.no-touch .image-container .details{opacity:0}.no-touch .image-container:hover .checkbox,.no-touch .image-container:hover .controls,.no-touch .image-container:hover .details{opacity:1}#page{min-width:0}.table{color:#bdc3c7;background-color:#31363b;font-size:.75rem}.table.is-striped tbody tr:nth-child(2n),.table tr:hover{background:none}.table.is-striped tbody tr:hover,.table.is-striped tbody tr:nth-child(2n):hover,.tag{background-color:#4d4d4d}.table td,.table th{border:0;white-space:nowrap}.table th{color:#eff0f1;height:2.25em}.table thead td,.table thead th{color:#eff0f1;background-color:#ff3860}.table .cell-indent{padding-left:2.25em}.is-linethrough{text-decoration:line-through}#menu.is-loading li a{cursor:progress}#statistics tr :nth-child(2){min-width:50%}.expirydate{color:#bdc3c7} +body{-webkit-animation:none;animation:none}#dashboard{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}.section{background:none}.menu-list a{color:#3794d2}.menu-list a:hover{color:#60a8dc;background-color:#4d4d4d}.menu-list a.is-active{color:#eff0f1;background-color:#3794d2}.menu-list a[disabled]{color:#7a7a7a;cursor:not-allowed}.menu-list a[disabled]:hover{background:none}.menu-list a.is-loading:after{-webkit-animation:spinAround .5s linear infinite;animation:spinAround .5s linear infinite;border-radius:290486px;border-color:transparent transparent #dbdbdb #dbdbdb;border-style:solid;border-width:2px;content:"";display:block;height:1em;width:1em;right:.5em;top:calc(50% - .5em);position:absolute!important}ul#albumsContainer{border-left:0;padding-left:0}ul#albumsContainer li{border-left:1px solid #898b8d;padding-left:.75em}#page.fade-in,ul#albumsContainer li{-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}.pagination{margin-bottom:1.25rem}.pagination a:not([disabled]){color:#eff0f1;border-color:#4d4d4d;background-color:#31363b}a.pagination-link:not(.is-current):hover,a.pagination-next:not([disabled]):hover,a.pagination-previous:not([disabled]):hover{color:#eff0f1;border-color:#60a8dc;background-color:#31363b}a.pagination-link.is-current{background-color:#3794d2;border-color:#3794d2}a.pagination-link.is-current:hover{border-color:#60a8dc}li[data-action=page-ellipsis]{cursor:pointer}.label{color:#bdc3c7}.menu-list li ul{border-left-color:#898b8d}.image-container .checkbox{position:absolute;top:12px;left:12px}.no-touch .image-container .checkbox{opacity:.5}.no-touch .image-container .controls,.no-touch .image-container .details{opacity:0}.no-touch .image-container:hover .checkbox,.no-touch .image-container:hover .controls,.no-touch .image-container:hover .details{opacity:1}#page{min-width:0}.table{color:#bdc3c7;background-color:#31363b;font-size:.75rem}.table.is-striped tbody tr:nth-child(2n),.table tr:hover{background:none}.table.is-striped tbody tr:hover,.table.is-striped tbody tr:nth-child(2n):hover,.tag{background-color:#4d4d4d}.table td,.table th{border:0;white-space:nowrap}.table th{color:#eff0f1;height:2.25em}.table thead td,.table thead th{color:#eff0f1;background-color:#ff3860}.table .cell-indent{padding-left:2.25em}.is-linethrough{text-decoration:line-through}#menu.is-loading .menu-list a{cursor:progress}#statistics tr :nth-child(2){min-width:50%}.expirydate{color:#bdc3c7} /*# sourceMappingURL=dashboard.css.map */ diff --git a/dist/css/dashboard.css.map b/dist/css/dashboard.css.map index ca99152..67985e7 100644 --- a/dist/css/dashboard.css.map +++ b/dist/css/dashboard.css.map @@ -1 +1 @@ -{"version":3,"sources":["css/dashboard.css"],"names":[],"mappings":"AAAA,KACE,sBAAc,CAAd,cACF,CAEA,WACE,mCAA4B,CAA5B,2BACF,CAEA,SACE,eACF,CAEA,aACE,aACF,CAEA,mBACE,aAAc,CACd,wBACF,CAEA,uBACE,aAAc,CACd,wBACF,CAEA,uBACE,aAAc,CACd,kBACF,CAEA,6BACE,eACF,CAEA,mBACE,aAAc,CACd,cACF,CAEA,sBACE,6BAA8B,CAC9B,kBAEF,CAEA,oCAHE,mCAA4B,CAA5B,2BAKF,CAEA,YACE,qBACF,CAEA,8BACE,aAAc,CACd,oBAAqB,CACrB,wBACF,CAEA,6HAGE,aAAc,CACd,oBAAqB,CACrB,wBACF,CAEA,6BACE,wBAAyB,CACzB,oBACF,CAEA,mCACE,oBACF,CAEA,8BACE,cACF,CAEA,OACE,aACF,CAEA,iBACE,yBACF,CAEA,2BACE,iBAAkB,CAClB,QAAS,CACT,SACF,CAEA,qCACE,UACF,CAEA,yEAEE,SACF,CAEA,gIAGE,SACF,CAEA,MAEE,WACF,CAEA,OACE,aAAc,CACd,wBAAyB,CACzB,gBACF,CAEA,yDAEE,eACF,CAEA,qFAGE,wBACF,CAEA,oBAEE,QAAS,CACT,kBACF,CAEA,UACE,aAAc,CACd,aACF,CAEA,gCAEE,aAAc,CACd,wBACF,CAEA,oBACE,mBACF,CAEA,gBACE,4BACF,CAEA,sBACE,eACF,CAEA,6BACE,aACF,CAEA,YACE,aACF","file":"dashboard.css","sourcesContent":["body {\n animation: none\n}\n\n#dashboard {\n animation: fadeInOpacity 0.5s\n}\n\n.section {\n background: none\n}\n\n.menu-list a {\n color: #3794d2\n}\n\n.menu-list a:hover {\n color: #60a8dc;\n background-color: #4d4d4d\n}\n\n.menu-list a.is-active {\n color: #eff0f1;\n background-color: #3794d2\n}\n\n.menu-list a[disabled] {\n color: #7a7a7a;\n cursor: not-allowed\n}\n\n.menu-list a[disabled]:hover {\n background: none\n}\n\nul#albumsContainer {\n border-left: 0;\n padding-left: 0\n}\n\nul#albumsContainer li {\n border-left: 1px solid #898b8d;\n padding-left: 0.75em;\n animation: fadeInOpacity 0.5s\n}\n\n#page.fade-in {\n animation: fadeInOpacity 0.5s\n}\n\n.pagination {\n margin-bottom: 1.25rem\n}\n\n.pagination a:not([disabled]) {\n color: #eff0f1;\n border-color: #4d4d4d;\n background-color: #31363b\n}\n\na.pagination-link:not(.is-current):hover,\na.pagination-next:not([disabled]):hover,\na.pagination-previous:not([disabled]):hover {\n color: #eff0f1;\n border-color: #60a8dc;\n background-color: #31363b\n}\n\na.pagination-link.is-current {\n background-color: #3794d2;\n border-color: #3794d2\n}\n\na.pagination-link.is-current:hover {\n border-color: #60a8dc\n}\n\nli[data-action=\"page-ellipsis\"] {\n cursor: pointer\n}\n\n.label {\n color: #bdc3c7\n}\n\n.menu-list li ul {\n border-left-color: #898b8d\n}\n\n.image-container .checkbox {\n position: absolute;\n top: 12px;\n left: 12px\n}\n\n.no-touch .image-container .checkbox {\n opacity: 0.5\n}\n\n.no-touch .image-container .controls,\n.no-touch .image-container .details {\n opacity: 0\n}\n\n.no-touch .image-container:hover .checkbox,\n.no-touch .image-container:hover .controls,\n.no-touch .image-container:hover .details {\n opacity: 1\n}\n\n#page {\n /* fix overflow issue with flex */\n min-width: 0\n}\n\n.table {\n color: #bdc3c7;\n background-color: #31363b;\n font-size: 0.75rem\n}\n\n.table tr:hover,\n.table.is-striped tbody tr:nth-child(2n) {\n background: none\n}\n\n.table.is-striped tbody tr:hover,\n.table.is-striped tbody tr:nth-child(2n):hover,\n.tag {\n background-color: #4d4d4d\n}\n\n.table td,\n.table th {\n border: 0;\n white-space: nowrap\n}\n\n.table th {\n color: #eff0f1;\n height: 2.25em\n}\n\n.table thead td,\n.table thead th {\n color: #eff0f1;\n background-color: #ff3860\n}\n\n.table .cell-indent {\n padding-left: 2.25em\n}\n\n.is-linethrough {\n text-decoration: line-through\n}\n\n#menu.is-loading li a {\n cursor: progress\n}\n\n#statistics tr *:nth-child(2) {\n min-width: 50%\n}\n\n.expirydate {\n color: #bdc3c7\n}\n"]} \ No newline at end of file +{"version":3,"sources":["css/dashboard.css"],"names":[],"mappings":"AAAA,KACE,sBAAc,CAAd,cACF,CAEA,WACE,mCAA4B,CAA5B,2BACF,CAEA,SACE,eACF,CAEA,aACE,aACF,CAEA,mBACE,aAAc,CACd,wBACF,CAEA,uBACE,aAAc,CACd,wBACF,CAEA,uBACE,aAAc,CACd,kBACF,CAEA,6BACE,eACF,CAEA,8BACE,gDAA0C,CAA1C,wCAA0C,CAE1C,sBAAuB,CAEvB,oDAA6B,CAA7B,kBAA6B,CAA7B,gBAA6B,CAC7B,UAAW,CACX,aAAc,CACd,UAAW,CACX,SAAU,CACV,UAA2B,CAC3B,oBAA0B,CAC1B,2BACF,CAEA,mBACE,aAAc,CACd,cACF,CAEA,sBACE,6BAA8B,CAC9B,kBAEF,CAEA,oCAHE,mCAA4B,CAA5B,2BAKF,CAEA,YACE,qBACF,CAEA,8BACE,aAAc,CACd,oBAAqB,CACrB,wBACF,CAEA,6HAGE,aAAc,CACd,oBAAqB,CACrB,wBACF,CAEA,6BACE,wBAAyB,CACzB,oBACF,CAEA,mCACE,oBACF,CAEA,8BACE,cACF,CAEA,OACE,aACF,CAEA,iBACE,yBACF,CAEA,2BACE,iBAAkB,CAClB,QAAS,CACT,SACF,CAEA,qCACE,UACF,CAEA,yEAEE,SACF,CAEA,gIAGE,SACF,CAEA,MAEE,WACF,CAEA,OACE,aAAc,CACd,wBAAyB,CACzB,gBACF,CAEA,yDAEE,eACF,CAEA,qFAGE,wBACF,CAEA,oBAEE,QAAS,CACT,kBACF,CAEA,UACE,aAAc,CACd,aACF,CAEA,gCAEE,aAAc,CACd,wBACF,CAEA,oBACE,mBACF,CAEA,gBACE,4BACF,CAEA,8BACE,eACF,CAEA,6BACE,aACF,CAEA,YACE,aACF","file":"dashboard.css","sourcesContent":["body {\n animation: none\n}\n\n#dashboard {\n animation: fadeInOpacity 0.5s\n}\n\n.section {\n background: none\n}\n\n.menu-list a {\n color: #3794d2\n}\n\n.menu-list a:hover {\n color: #60a8dc;\n background-color: #4d4d4d\n}\n\n.menu-list a.is-active {\n color: #eff0f1;\n background-color: #3794d2\n}\n\n.menu-list a[disabled] {\n color: #7a7a7a;\n cursor: not-allowed\n}\n\n.menu-list a[disabled]:hover {\n background: none\n}\n\n.menu-list a.is-loading::after {\n animation: spinAround 0.5s infinite linear;\n border: 2px solid #dbdbdb;\n border-radius: 290486px;\n border-right-color: transparent;\n border-top-color: transparent;\n content: \"\";\n display: block;\n height: 1em;\n width: 1em;\n right: calc(0% + (1em / 2));\n top: calc(50% - (1em / 2));\n position: absolute !important\n}\n\nul#albumsContainer {\n border-left: 0;\n padding-left: 0\n}\n\nul#albumsContainer li {\n border-left: 1px solid #898b8d;\n padding-left: 0.75em;\n animation: fadeInOpacity 0.5s\n}\n\n#page.fade-in {\n animation: fadeInOpacity 0.5s\n}\n\n.pagination {\n margin-bottom: 1.25rem\n}\n\n.pagination a:not([disabled]) {\n color: #eff0f1;\n border-color: #4d4d4d;\n background-color: #31363b\n}\n\na.pagination-link:not(.is-current):hover,\na.pagination-next:not([disabled]):hover,\na.pagination-previous:not([disabled]):hover {\n color: #eff0f1;\n border-color: #60a8dc;\n background-color: #31363b\n}\n\na.pagination-link.is-current {\n background-color: #3794d2;\n border-color: #3794d2\n}\n\na.pagination-link.is-current:hover {\n border-color: #60a8dc\n}\n\nli[data-action=\"page-ellipsis\"] {\n cursor: pointer\n}\n\n.label {\n color: #bdc3c7\n}\n\n.menu-list li ul {\n border-left-color: #898b8d\n}\n\n.image-container .checkbox {\n position: absolute;\n top: 12px;\n left: 12px\n}\n\n.no-touch .image-container .checkbox {\n opacity: 0.5\n}\n\n.no-touch .image-container .controls,\n.no-touch .image-container .details {\n opacity: 0\n}\n\n.no-touch .image-container:hover .checkbox,\n.no-touch .image-container:hover .controls,\n.no-touch .image-container:hover .details {\n opacity: 1\n}\n\n#page {\n /* fix overflow issue with flex */\n min-width: 0\n}\n\n.table {\n color: #bdc3c7;\n background-color: #31363b;\n font-size: 0.75rem\n}\n\n.table tr:hover,\n.table.is-striped tbody tr:nth-child(2n) {\n background: none\n}\n\n.table.is-striped tbody tr:hover,\n.table.is-striped tbody tr:nth-child(2n):hover,\n.tag {\n background-color: #4d4d4d\n}\n\n.table td,\n.table th {\n border: 0;\n white-space: nowrap\n}\n\n.table th {\n color: #eff0f1;\n height: 2.25em\n}\n\n.table thead td,\n.table thead th {\n color: #eff0f1;\n background-color: #ff3860\n}\n\n.table .cell-indent {\n padding-left: 2.25em\n}\n\n.is-linethrough {\n text-decoration: line-through\n}\n\n#menu.is-loading .menu-list a {\n cursor: progress\n}\n\n#statistics tr *:nth-child(2) {\n min-width: 50%\n}\n\n.expirydate {\n color: #bdc3c7\n}\n"]} \ No newline at end of file diff --git a/dist/css/style.css b/dist/css/style.css index 75749c9..91467ae 100644 --- a/dist/css/style.css +++ b/dist/css/style.css @@ -1,2 +1,2 @@ -html{background-color:#232629;overflow-y:auto}body{color:#eff0f1;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}@-webkit-keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}@keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}a{color:#3794d2}a:hover{color:#60a8dc}hr{background-color:#898b8d}.message-body code,code{background-color:#222528;border-radius:5px}.title{color:#eff0f1}.subtitle,.subtitle strong{color:#bdc3c7}.input::-moz-placeholder,.textarea::-moz-placeholder{color:#7f8c8d}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:#7f8c8d}.input:-moz-placeholder,.textarea:-moz-placeholder{color:#7f8c8d}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:#7f8c8d}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3794d2}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#4d4d4d}.table td,.table th{vertical-align:middle}.help{color:#7f8c8d}.button.is-info.is-hovered [class*=" icon-"]:before,.button.is-info.is-hovered [class^=icon-]:before,.button.is-info:hover [class*=" icon-"]:before,.button.is-info:hover [class^=icon-]:before{fill:#fff}.checkbox:hover,.radio:hover{color:#7f8c8d}.message{background-color:#31363b}.message-body{color:#eff0f1;border:0;box-shadow:0 20px 60px rgba(10,10,10,.05),0 5px 10px rgba(10,10,10,.1),0 1px 1px rgba(10,10,10,.2)}.menu-list a.is-loading:after{-webkit-animation:spinAround .5s linear infinite;animation:spinAround .5s linear infinite;border-radius:290486px;border-color:transparent transparent #dbdbdb #dbdbdb;border-style:solid;border-width:2px;content:"";display:block;height:1em;width:1em;right:.5em;top:calc(50% - .5em);position:absolute!important}.hero.is-fullheight>.hero-body{min-height:100vh;height:100%}.hero.is-fullheight>.hero-body>.container{width:100%} +html{background-color:#232629;overflow-y:auto}body{color:#eff0f1;-webkit-animation:fadeInOpacity .5s;animation:fadeInOpacity .5s}@-webkit-keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}@keyframes fadeInOpacity{0%{opacity:0}to{opacity:1}}a{color:#3794d2}a:hover{color:#60a8dc}hr{background-color:#898b8d}.message-body code,code{background-color:#222528;border-radius:5px}.title{color:#eff0f1}.subtitle,.subtitle strong{color:#bdc3c7}.input::-moz-placeholder,.textarea::-moz-placeholder{color:#7f8c8d}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:#7f8c8d}.input:-moz-placeholder,.textarea:-moz-placeholder{color:#7f8c8d}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:#7f8c8d}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3794d2}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#4d4d4d}.table td,.table th{vertical-align:middle}.help{color:#7f8c8d}.button.is-info.is-hovered [class*=" icon-"]:before,.button.is-info.is-hovered [class^=icon-]:before,.button.is-info:hover [class*=" icon-"]:before,.button.is-info:hover [class^=icon-]:before{fill:#fff}.checkbox:hover,.radio:hover{color:#7f8c8d}.message{background-color:#31363b}.message-body{color:#eff0f1;border:0;box-shadow:0 20px 60px rgba(10,10,10,.05),0 5px 10px rgba(10,10,10,.1),0 1px 1px rgba(10,10,10,.2)}.hero.is-fullheight>.hero-body{min-height:100vh;height:100%}.hero.is-fullheight>.hero-body>.container{width:100%} /*# sourceMappingURL=style.css.map */ diff --git a/dist/css/style.css.map b/dist/css/style.css.map index ab6b908..22ee2d0 100644 --- a/dist/css/style.css.map +++ b/dist/css/style.css.map @@ -1 +1 @@ -{"version":3,"sources":["css/style.css"],"names":[],"mappings":"AAAA,KACE,wBAAyB,CACzB,eACF,CAEA,KACE,aAAc,CACd,mCAA4B,CAA5B,2BACF,CAEA,iCACE,GACE,SACF,CAEA,GACE,SACF,CACF,CAEA,yBACE,GACE,SACF,CAEA,GACE,SACF,CACF,CAEA,EACE,aACF,CAEA,QACE,aACF,CAEA,GACE,wBACF,CAEA,wBAEE,wBAAyB,CACzB,iBACF,CAEA,OACE,aACF,CAMA,2BACE,aACF,CAEA,qDAEE,aACF,CAEA,uEAEE,aACF,CAEA,mDAEE,aACF,CAEA,6DAEE,aACF,CAEA,wIAQE,oBACF,CAEA,qDACE,wBACF,CAEA,oBAEE,qBACF,CAEA,MACE,aACF,CAEA,gMAIE,SACF,CAEA,6BAEE,aACF,CAEA,SACE,wBACF,CAEA,cACE,aAAc,CACd,QAAS,CACT,kGACF,CAEA,8BACE,gDAA0C,CAA1C,wCAA0C,CAE1C,sBAAuB,CAEvB,oDAA6B,CAA7B,kBAA6B,CAA7B,gBAA6B,CAC7B,UAAW,CACX,aAAc,CACd,UAAW,CACX,SAAU,CACV,UAA2B,CAC3B,oBAA0B,CAC1B,2BACF,CAGA,+BACE,gBAAiB,CACjB,WACF,CAGA,0CACE,UACF","file":"style.css","sourcesContent":["html {\n background-color: #232629;\n overflow-y: auto\n}\n\nbody {\n color: #eff0f1;\n animation: fadeInOpacity 0.5s\n}\n\n@-webkit-keyframes fadeInOpacity {\n 0% {\n opacity: 0\n }\n\n 100% {\n opacity: 1\n }\n}\n\n@keyframes fadeInOpacity {\n 0% {\n opacity: 0\n }\n\n 100% {\n opacity: 1\n }\n}\n\na {\n color: #3794d2\n}\n\na:hover {\n color: #60a8dc\n}\n\nhr {\n background-color: #898b8d\n}\n\ncode,\n.message-body code {\n background-color: #222528;\n border-radius: 5px\n}\n\n.title {\n color: #eff0f1\n}\n\n.subtitle {\n color: #bdc3c7\n}\n\n.subtitle strong {\n color: #bdc3c7\n}\n\n.input::-moz-placeholder,\n.textarea::-moz-placeholder {\n color: #7f8c8d\n}\n\n.input::-webkit-input-placeholder,\n.textarea::-webkit-input-placeholder {\n color: #7f8c8d\n}\n\n.input:-moz-placeholder,\n.textarea:-moz-placeholder {\n color: #7f8c8d\n}\n\n.input:-ms-input-placeholder,\n.textarea:-ms-input-placeholder {\n color: #7f8c8d\n}\n\n.input.is-active,\n.input.is-focused,\n.input:active,\n.input:focus,\n.textarea.is-active,\n.textarea.is-focused,\n.textarea:active,\n.textarea:focus {\n border-color: #3794d2\n}\n\n.table.is-hoverable tbody tr:not(.is-selected):hover {\n background-color: #4d4d4d\n}\n\n.table td,\n.table th {\n vertical-align: middle\n}\n\n.help {\n color: #7f8c8d\n}\n\n.button.is-info.is-hovered [class^=\"icon-\"]::before,\n.button.is-info.is-hovered [class*=\" icon-\"]::before,\n.button.is-info:hover [class^=\"icon-\"]::before,\n.button.is-info:hover [class*=\" icon-\"]::before {\n fill: #fff\n}\n\n.checkbox:hover,\n.radio:hover {\n color: #7f8c8d\n}\n\n.message {\n background-color: #31363b\n}\n\n.message-body {\n color: #eff0f1;\n border: 0;\n box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2)\n}\n\n.menu-list a.is-loading::after {\n animation: spinAround 0.5s infinite linear;\n border: 2px solid #dbdbdb;\n border-radius: 290486px;\n border-right-color: transparent;\n border-top-color: transparent;\n content: \"\";\n display: block;\n height: 1em;\n width: 1em;\n right: calc(0% + (1em / 2));\n top: calc(50% - (1em / 2));\n position: absolute !important\n}\n\n/* https://github.com/philipwalton/flexbugs#flexbug-3 */\n.hero.is-fullheight > .hero-body {\n min-height: 100vh;\n height: 100%\n}\n\n/* https://github.com/philipwalton/flexbugs#flexbug-2 */\n.hero.is-fullheight > .hero-body > .container {\n width: 100%\n}\n"]} \ No newline at end of file +{"version":3,"sources":["css/style.css"],"names":[],"mappings":"AAAA,KACE,wBAAyB,CACzB,eACF,CAEA,KACE,aAAc,CACd,mCAA4B,CAA5B,2BACF,CAEA,iCACE,GACE,SACF,CAEA,GACE,SACF,CACF,CAEA,yBACE,GACE,SACF,CAEA,GACE,SACF,CACF,CAEA,EACE,aACF,CAEA,QACE,aACF,CAEA,GACE,wBACF,CAEA,wBAEE,wBAAyB,CACzB,iBACF,CAEA,OACE,aACF,CAMA,2BACE,aACF,CAEA,qDAEE,aACF,CAEA,uEAEE,aACF,CAEA,mDAEE,aACF,CAEA,6DAEE,aACF,CAEA,wIAQE,oBACF,CAEA,qDACE,wBACF,CAEA,oBAEE,qBACF,CAEA,MACE,aACF,CAEA,gMAIE,SACF,CAEA,6BAEE,aACF,CAEA,SACE,wBACF,CAEA,cACE,aAAc,CACd,QAAS,CACT,kGACF,CAGA,+BACE,gBAAiB,CACjB,WACF,CAGA,0CACE,UACF","file":"style.css","sourcesContent":["html {\n background-color: #232629;\n overflow-y: auto\n}\n\nbody {\n color: #eff0f1;\n animation: fadeInOpacity 0.5s\n}\n\n@-webkit-keyframes fadeInOpacity {\n 0% {\n opacity: 0\n }\n\n 100% {\n opacity: 1\n }\n}\n\n@keyframes fadeInOpacity {\n 0% {\n opacity: 0\n }\n\n 100% {\n opacity: 1\n }\n}\n\na {\n color: #3794d2\n}\n\na:hover {\n color: #60a8dc\n}\n\nhr {\n background-color: #898b8d\n}\n\ncode,\n.message-body code {\n background-color: #222528;\n border-radius: 5px\n}\n\n.title {\n color: #eff0f1\n}\n\n.subtitle {\n color: #bdc3c7\n}\n\n.subtitle strong {\n color: #bdc3c7\n}\n\n.input::-moz-placeholder,\n.textarea::-moz-placeholder {\n color: #7f8c8d\n}\n\n.input::-webkit-input-placeholder,\n.textarea::-webkit-input-placeholder {\n color: #7f8c8d\n}\n\n.input:-moz-placeholder,\n.textarea:-moz-placeholder {\n color: #7f8c8d\n}\n\n.input:-ms-input-placeholder,\n.textarea:-ms-input-placeholder {\n color: #7f8c8d\n}\n\n.input.is-active,\n.input.is-focused,\n.input:active,\n.input:focus,\n.textarea.is-active,\n.textarea.is-focused,\n.textarea:active,\n.textarea:focus {\n border-color: #3794d2\n}\n\n.table.is-hoverable tbody tr:not(.is-selected):hover {\n background-color: #4d4d4d\n}\n\n.table td,\n.table th {\n vertical-align: middle\n}\n\n.help {\n color: #7f8c8d\n}\n\n.button.is-info.is-hovered [class^=\"icon-\"]::before,\n.button.is-info.is-hovered [class*=\" icon-\"]::before,\n.button.is-info:hover [class^=\"icon-\"]::before,\n.button.is-info:hover [class*=\" icon-\"]::before {\n fill: #fff\n}\n\n.checkbox:hover,\n.radio:hover {\n color: #7f8c8d\n}\n\n.message {\n background-color: #31363b\n}\n\n.message-body {\n color: #eff0f1;\n border: 0;\n box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2)\n}\n\n/* https://github.com/philipwalton/flexbugs#flexbug-3 */\n.hero.is-fullheight > .hero-body {\n min-height: 100vh;\n height: 100%\n}\n\n/* https://github.com/philipwalton/flexbugs#flexbug-2 */\n.hero.is-fullheight > .hero-body > .container {\n width: 100%\n}\n"]} \ No newline at end of file diff --git a/dist/css/thumbs.css b/dist/css/thumbs.css index 170c939..d2fc5c4 100644 --- a/dist/css/thumbs.css +++ b/dist/css/thumbs.css @@ -1,2 +1,2 @@ -.image-container{display:flex;width:224px;height:224px;margin:9px;padding:12px;background-color:#31363b;overflow:hidden;align-items:center;box-shadow:0 20px 60px rgba(10,10,10,.05),0 5px 10px rgba(10,10,10,.1),0 1px 1px rgba(10,10,10,.2)}.image-container .title{font-weight:400;word-break:break-all}.image-container .image{display:flex;height:100%;width:100%;align-items:center;justify-content:center}.image-container .image img{max-height:100%;max-width:100%;height:auto;width:auto}.image-container .controls{display:flex;position:absolute;top:12px;right:12px}.image-container .controls .button{border-radius:0}.image-container .controls .button:not(:active):not(:hover){color:#fff;background-color:rgba(49,54,59,.75)}.image-container .details{position:absolute;left:12px;bottom:12px;right:12px;background-color:rgba(49,54,59,.75);color:#eff0f1;padding:3px;font-size:.75rem}.image-container .details p{display:block;text-overflow:ellipsis;overflow:hidden}.image-container .details p span{font-weight:700} +.image-container{flex:none;position:relative;width:224px;height:224px;margin:.75rem;padding:12px;background-color:#31363b;overflow:hidden;align-items:center;box-shadow:0 20px 60px rgba(10,10,10,.05),0 5px 10px rgba(10,10,10,.1),0 1px 1px rgba(10,10,10,.2)}.image-container .title{font-weight:400;word-break:break-all}.image-container .image{display:flex;height:100%;width:100%;align-items:center;justify-content:center}.image-container .image img{max-height:100%;max-width:100%;height:auto;width:auto}.image-container .controls{display:flex;position:absolute;top:12px;right:12px}.image-container .controls .button{border-radius:0}.image-container .controls .button:not(:active):not(:hover){color:#fff;background-color:rgba(49,54,59,.75)}.image-container .details{position:absolute;left:12px;bottom:12px;right:12px;background-color:rgba(49,54,59,.75);color:#eff0f1;padding:3px;font-size:.75rem}.image-container .details p{display:block;text-overflow:ellipsis;overflow:hidden}.image-container .details p span{font-weight:700} /*# sourceMappingURL=thumbs.css.map */ diff --git a/dist/css/thumbs.css.map b/dist/css/thumbs.css.map index 2eb9c6b..06eb477 100644 --- a/dist/css/thumbs.css.map +++ b/dist/css/thumbs.css.map @@ -1 +1 @@ -{"version":3,"sources":["css/thumbs.css"],"names":[],"mappings":"AAAA,iBACE,YAAa,CACb,WAAY,CACZ,YAAa,CACb,UAAW,CACX,YAAa,CACb,wBAAyB,CACzB,eAAgB,CAChB,kBAAmB,CACnB,kGACF,CAEA,wBACE,eAAmB,CACnB,oBACF,CAEA,wBACE,YAAa,CACb,WAAY,CACZ,UAAW,CACX,kBAAmB,CACnB,sBACF,CAEA,4BACE,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UACF,CAEA,2BACE,YAAa,CACb,iBAAkB,CAClB,QAAS,CACT,UACF,CAEA,mCACE,eACF,CAEA,4DACE,UAAW,CACX,mCACF,CAEA,0BACE,iBAAkB,CAClB,SAAU,CACV,WAAY,CACZ,UAAW,CACX,mCAAwC,CACxC,aAAc,CACd,WAAY,CACZ,gBACF,CAEA,4BACE,aAAc,CACd,sBAAuB,CACvB,eACF,CAEA,iCACE,eACF","file":"thumbs.css","sourcesContent":[".image-container {\n display: flex;\n width: 224px;\n height: 224px;\n margin: 9px;\n padding: 12px;\n background-color: #31363b;\n overflow: hidden;\n align-items: center;\n box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2)\n}\n\n.image-container .title {\n font-weight: normal;\n word-break: break-all\n}\n\n.image-container .image {\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center\n}\n\n.image-container .image img {\n max-height: 100%;\n max-width: 100%;\n height: auto;\n width: auto\n}\n\n.image-container .controls {\n display: flex;\n position: absolute;\n top: 12px;\n right: 12px\n}\n\n.image-container .controls .button {\n border-radius: 0\n}\n\n.image-container .controls .button:not(:active):not(:hover) {\n color: #fff;\n background-color: rgba(49, 54, 59, 0.75)\n}\n\n.image-container .details {\n position: absolute;\n left: 12px;\n bottom: 12px;\n right: 12px;\n background-color: rgba(49, 54, 59, 0.75);\n color: #eff0f1;\n padding: 3px;\n font-size: 0.75rem\n}\n\n.image-container .details p {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden\n}\n\n.image-container .details p span {\n font-weight: bold\n}\n"]} \ No newline at end of file +{"version":3,"sources":["css/thumbs.css"],"names":[],"mappings":"AAAA,iBACE,SAAU,CACV,iBAAkB,CAClB,WAAY,CACZ,YAAa,CACb,aAAe,CACf,YAAa,CACb,wBAAyB,CACzB,eAAgB,CAChB,kBAAmB,CACnB,kGACF,CAEA,wBACE,eAAmB,CACnB,oBACF,CAEA,wBACE,YAAa,CACb,WAAY,CACZ,UAAW,CACX,kBAAmB,CACnB,sBACF,CAEA,4BACE,eAAgB,CAChB,cAAe,CACf,WAAY,CACZ,UACF,CAEA,2BACE,YAAa,CACb,iBAAkB,CAClB,QAAS,CACT,UACF,CAEA,mCACE,eACF,CAEA,4DACE,UAAW,CACX,mCACF,CAEA,0BACE,iBAAkB,CAClB,SAAU,CACV,WAAY,CACZ,UAAW,CACX,mCAAwC,CACxC,aAAc,CACd,WAAY,CACZ,gBACF,CAEA,4BACE,aAAc,CACd,sBAAuB,CACvB,eACF,CAEA,iCACE,eACF","file":"thumbs.css","sourcesContent":[".image-container {\n flex: none;\n position: relative;\n width: 224px;\n height: 224px;\n margin: 0.75rem;\n padding: 12px;\n background-color: #31363b;\n overflow: hidden;\n align-items: center;\n box-shadow: 0 20px 60px rgba(10, 10, 10, 0.05), 0 5px 10px rgba(10, 10, 10, 0.1), 0 1px 1px rgba(10, 10, 10, 0.2)\n}\n\n.image-container .title {\n font-weight: normal;\n word-break: break-all\n}\n\n.image-container .image {\n display: flex;\n height: 100%;\n width: 100%;\n align-items: center;\n justify-content: center\n}\n\n.image-container .image img {\n max-height: 100%;\n max-width: 100%;\n height: auto;\n width: auto\n}\n\n.image-container .controls {\n display: flex;\n position: absolute;\n top: 12px;\n right: 12px\n}\n\n.image-container .controls .button {\n border-radius: 0\n}\n\n.image-container .controls .button:not(:active):not(:hover) {\n color: #fff;\n background-color: rgba(49, 54, 59, 0.75)\n}\n\n.image-container .details {\n position: absolute;\n left: 12px;\n bottom: 12px;\n right: 12px;\n background-color: rgba(49, 54, 59, 0.75);\n color: #eff0f1;\n padding: 3px;\n font-size: 0.75rem\n}\n\n.image-container .details p {\n display: block;\n text-overflow: ellipsis;\n overflow: hidden\n}\n\n.image-container .details p span {\n font-weight: bold\n}\n"]} \ No newline at end of file diff --git a/dist/js/auth.js b/dist/js/auth.js index 755f70e..0647355 100644 --- a/dist/js/auth.js +++ b/dist/js/auth.js @@ -1,2 +1,2 @@ -var lsKeys={token:"token"},page={token:localStorage[lsKeys.token],user:null,pass:null,do:function(e,r){var o=page.user.value.trim();if(!o)return swal("An error occurred!","You need to specify a username.","error");var t=page.pass.value.trim();if(!t)return swal("An error occurred!","You need to specify a password.","error");r.classList.add("is-loading"),axios.post("api/"+e,{username:o,password:t}).then((function(o){if(!1===o.data.success)return r.classList.remove("is-loading"),swal("Unable to "+e+"!",o.data.description,"error");localStorage.token=o.data.token,window.location="dashboard"})).catch((function(e){return console.error(e),r.classList.remove("is-loading"),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},verify:function(){page.token&&axios.post("api/tokens/verify",{token:page.token}).then((function(e){if(!1===e.data.success)return swal("An error occurred!",e.data.description,"error");window.location="dashboard"})).catch((function(e){console.error(e);var r=e.response.data&&e.response.data.description?e.response.data.description:"There was an error with the request, please check the console for more information.";return swal(e.response.status+" "+e.response.statusText,r,"error")}))}};window.onload=function(){page.verify(),page.user=document.querySelector("#user"),page.pass=document.querySelector("#pass"),document.querySelector("#authForm").addEventListener("submit",(function(e){e.preventDefault()})),document.querySelector("#loginBtn").addEventListener("click",(function(e){page.do("login",e.currentTarget)})),document.querySelector("#registerBtn").addEventListener("click",(function(e){page.do("register",e.currentTarget)}))}; +var lsKeys={token:"token"},page={token:localStorage[lsKeys.token],user:null,pass:null,do:function(e,r){var o=page.user.value.trim();if(!o)return swal("An error occurred!","You need to specify a username.","error");var t=page.pass.value.trim();if(!t)return swal("An error occurred!","You need to specify a password.","error");r.classList.add("is-loading"),axios.post("api/"+e,{username:o,password:t}).then((function(o){if(!1===o.data.success)return r.classList.remove("is-loading"),swal("Unable to "+e+"!",o.data.description,"error");localStorage.token=o.data.token,window.location="dashboard"})).catch((function(e){return console.error(e),r.classList.remove("is-loading"),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},verify:function(){page.token&&axios.post("api/tokens/verify",{token:page.token}).then((function(e){if(!1===e.data.success)return swal("An error occurred!",e.data.description,"error");window.location="dashboard"})).catch((function(e){console.error(e);var r=e.response.data&&e.response.data.description?e.response.data.description:"There was an error with the request, please check the console for more information.";return swal(e.response.status+" "+e.response.statusText,r,"error")}))}};window.onload=function(){page.verify(),page.user=document.querySelector("#user"),page.pass=document.querySelector("#pass");var e=document.querySelector("#authForm");e.addEventListener("submit",(function(e){e.preventDefault()})),document.querySelector("#loginBtn").addEventListener("click",(function(r){e.checkValidity()&&page.do("login",r.currentTarget)})),document.querySelector("#registerBtn").addEventListener("click",(function(r){e.checkValidity()&&page.do("register",r.currentTarget)}))}; //# sourceMappingURL=auth.js.map diff --git a/dist/js/auth.js.map b/dist/js/auth.js.map index c650109..b6c9a99 100644 --- a/dist/js/auth.js.map +++ b/dist/js/auth.js.map @@ -1 +1 @@ -{"version":3,"sources":["auth.js"],"names":["const","lsKeys","token","page","localStorage","user","pass","do","dest","trigger","value","trim","swal","classList","add","axios","post","username","password","then","response","data","success","remove","description","window","location","catch","error","console","verify","onload","document","querySelector","addEventListener","event","preventDefault","currentTarget"],"mappings":"AAEAA,IAAMC,OAAS,CACbC,MAAO,SAGHC,KAAO,CAEXD,MAAOE,aAAaH,OAAOC,OAG3BG,KAAM,KACNC,KAAM,KAGRC,GAAO,SAAIC,EAAMC,GACfT,IAAMK,EAAOF,KAAKE,KAAKK,MAAMC,OAC7B,IAAKN,EACH,OAAOO,KAAK,qBAAsB,kCAAmC,SAEvEZ,IAAMM,EAAOH,KAAKG,KAAKI,MAAMC,OAC7B,IAAKL,EACH,OAAOM,KAAK,qBAAsB,kCAAmC,SAEvEH,EAAQI,UAAUC,IAAI,cACtBC,MAAMC,KAAK,OAAOR,EAAQ,CACxBS,SAAUZ,EACVa,SAAUZ,IACTa,MAAI,SAACC,GACN,IAA8B,IAA1BA,EAASC,KAAKC,QAEhB,OADAb,EAAQI,UAAUU,OAAO,cAClBX,KAAK,aAAaJ,EAAI,IAAKY,EAASC,KAAKG,YAAa,SAG/DpB,aAAaF,MAAQkB,EAASC,KAAKnB,MACnCuB,OAAOC,SAAW,eACjBC,OAAK,SAACC,GAGP,OAFAC,QAAQD,MAAMA,GACdnB,EAAQI,UAAUU,OAAO,cAClBX,KAAK,qBAAsB,sFAAuF,aAI7HkB,OAAW,WACJ3B,KAAKD,OAEVa,MAAMC,KAAK,oBAAqB,CAC9Bd,MAAOC,KAAKD,QACXiB,MAAI,SAACC,GACN,IAA8B,IAA1BA,EAASC,KAAKC,QAChB,OAAOV,KAAK,qBAAsBQ,EAASC,KAAKG,YAAa,SAE/DC,OAAOC,SAAW,eACjBC,OAAK,SAACC,GACPC,QAAQD,MAAMA,GACd5B,IAAMwB,EAAcI,EAAMR,SAASC,MAAQO,EAAMR,SAASC,KAAKG,YAC3DI,EAAMR,SAASC,KAAKG,YACpB,sFACJ,OAAOZ,KAAQgB,EAAMR,SAAS,OAAM,IAAIQ,EAAMR,SAAmB,WAAII,EAAa,cAItFC,OAAOM,OAAM,WACX5B,KAAK2B,SAEL3B,KAAKE,KAAO2B,SAASC,cAAc,SACnC9B,KAAKG,KAAO0B,SAASC,cAAc,SAGnCD,SAASC,cAAc,aAAaC,iBAAiB,UAAQ,SAAEC,GAC7DA,EAAMC,oBAGRJ,SAASC,cAAc,aAAaC,iBAAiB,SAAO,SAAEC,GAC5DhC,KAAKI,GAAG,QAAS4B,EAAME,kBAGzBL,SAASC,cAAc,gBAAgBC,iBAAiB,SAAO,SAAEC,GAC/DhC,KAAKI,GAAG,WAAY4B,EAAME","file":"auth.js","sourcesContent":["/* global swal, axios */\n\nconst lsKeys = {\n token: 'token'\n}\n\nconst page = {\n // user token\n token: localStorage[lsKeys.token],\n\n // HTML elements\n user: null,\n pass: null\n}\n\npage.do = (dest, trigger) => {\n const user = page.user.value.trim()\n if (!user)\n return swal('An error occurred!', 'You need to specify a username.', 'error')\n\n const pass = page.pass.value.trim()\n if (!pass)\n return swal('An error occurred!', 'You need to specify a password.', 'error')\n\n trigger.classList.add('is-loading')\n axios.post(`api/${dest}`, {\n username: user,\n password: pass\n }).then(response => {\n if (response.data.success === false) {\n trigger.classList.remove('is-loading')\n return swal(`Unable to ${dest}!`, response.data.description, 'error')\n }\n\n localStorage.token = response.data.token\n window.location = 'dashboard'\n }).catch(error => {\n console.error(error)\n trigger.classList.remove('is-loading')\n return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')\n })\n}\n\npage.verify = () => {\n if (!page.token) return\n\n axios.post('api/tokens/verify', {\n token: page.token\n }).then(response => {\n if (response.data.success === false)\n return swal('An error occurred!', response.data.description, 'error')\n\n window.location = 'dashboard'\n }).catch(error => {\n console.error(error)\n const description = error.response.data && error.response.data.description\n ? error.response.data.description\n : 'There was an error with the request, please check the console for more information.'\n return swal(`${error.response.status} ${error.response.statusText}`, description, 'error')\n })\n}\n\nwindow.onload = () => {\n page.verify()\n\n page.user = document.querySelector('#user')\n page.pass = document.querySelector('#pass')\n\n // Prevent default form's submit action\n document.querySelector('#authForm').addEventListener('submit', event => {\n event.preventDefault()\n })\n\n document.querySelector('#loginBtn').addEventListener('click', event => {\n page.do('login', event.currentTarget)\n })\n\n document.querySelector('#registerBtn').addEventListener('click', event => {\n page.do('register', event.currentTarget)\n })\n}\n"]} \ No newline at end of file +{"version":3,"sources":["auth.js"],"names":["const","lsKeys","token","page","localStorage","user","pass","do","dest","trigger","value","trim","swal","classList","add","axios","post","username","password","then","response","data","success","remove","description","window","location","catch","error","console","verify","onload","document","querySelector","form","addEventListener","event","preventDefault","checkValidity","currentTarget"],"mappings":"AAEAA,IAAMC,OAAS,CACbC,MAAO,SAGHC,KAAO,CAEXD,MAAOE,aAAaH,OAAOC,OAG3BG,KAAM,KACNC,KAAM,KAGRC,GAAO,SAAIC,EAAMC,GACfT,IAAMK,EAAOF,KAAKE,KAAKK,MAAMC,OAC7B,IAAKN,EACH,OAAOO,KAAK,qBAAsB,kCAAmC,SAEvEZ,IAAMM,EAAOH,KAAKG,KAAKI,MAAMC,OAC7B,IAAKL,EACH,OAAOM,KAAK,qBAAsB,kCAAmC,SAEvEH,EAAQI,UAAUC,IAAI,cACtBC,MAAMC,KAAK,OAAOR,EAAQ,CACxBS,SAAUZ,EACVa,SAAUZ,IACTa,MAAI,SAACC,GACN,IAA8B,IAA1BA,EAASC,KAAKC,QAEhB,OADAb,EAAQI,UAAUU,OAAO,cAClBX,KAAK,aAAaJ,EAAI,IAAKY,EAASC,KAAKG,YAAa,SAG/DpB,aAAaF,MAAQkB,EAASC,KAAKnB,MACnCuB,OAAOC,SAAW,eACjBC,OAAK,SAACC,GAGP,OAFAC,QAAQD,MAAMA,GACdnB,EAAQI,UAAUU,OAAO,cAClBX,KAAK,qBAAsB,sFAAuF,aAI7HkB,OAAW,WACJ3B,KAAKD,OAEVa,MAAMC,KAAK,oBAAqB,CAC9Bd,MAAOC,KAAKD,QACXiB,MAAI,SAACC,GACN,IAA8B,IAA1BA,EAASC,KAAKC,QAChB,OAAOV,KAAK,qBAAsBQ,EAASC,KAAKG,YAAa,SAE/DC,OAAOC,SAAW,eACjBC,OAAK,SAACC,GACPC,QAAQD,MAAMA,GACd5B,IAAMwB,EAAcI,EAAMR,SAASC,MAAQO,EAAMR,SAASC,KAAKG,YAC3DI,EAAMR,SAASC,KAAKG,YACpB,sFACJ,OAAOZ,KAAQgB,EAAMR,SAAS,OAAM,IAAIQ,EAAMR,SAAmB,WAAII,EAAa,cAItFC,OAAOM,OAAM,WACX5B,KAAK2B,SAEL3B,KAAKE,KAAO2B,SAASC,cAAc,SACnC9B,KAAKG,KAAO0B,SAASC,cAAc,SAGnCjC,IAAMkC,EAAOF,SAASC,cAAc,aACpCC,EAAKC,iBAAiB,UAAQ,SAAEC,GAC9BA,EAAMC,oBAGRL,SAASC,cAAc,aAAaE,iBAAiB,SAAO,SAAEC,GACvDF,EAAKI,iBACVnC,KAAKI,GAAG,QAAS6B,EAAMG,kBAGzBP,SAASC,cAAc,gBAAgBE,iBAAiB,SAAO,SAAEC,GAC1DF,EAAKI,iBACVnC,KAAKI,GAAG,WAAY6B,EAAMG","file":"auth.js","sourcesContent":["/* global swal, axios */\n\nconst lsKeys = {\n token: 'token'\n}\n\nconst page = {\n // user token\n token: localStorage[lsKeys.token],\n\n // HTML elements\n user: null,\n pass: null\n}\n\npage.do = (dest, trigger) => {\n const user = page.user.value.trim()\n if (!user)\n return swal('An error occurred!', 'You need to specify a username.', 'error')\n\n const pass = page.pass.value.trim()\n if (!pass)\n return swal('An error occurred!', 'You need to specify a password.', 'error')\n\n trigger.classList.add('is-loading')\n axios.post(`api/${dest}`, {\n username: user,\n password: pass\n }).then(response => {\n if (response.data.success === false) {\n trigger.classList.remove('is-loading')\n return swal(`Unable to ${dest}!`, response.data.description, 'error')\n }\n\n localStorage.token = response.data.token\n window.location = 'dashboard'\n }).catch(error => {\n console.error(error)\n trigger.classList.remove('is-loading')\n return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error')\n })\n}\n\npage.verify = () => {\n if (!page.token) return\n\n axios.post('api/tokens/verify', {\n token: page.token\n }).then(response => {\n if (response.data.success === false)\n return swal('An error occurred!', response.data.description, 'error')\n\n window.location = 'dashboard'\n }).catch(error => {\n console.error(error)\n const description = error.response.data && error.response.data.description\n ? error.response.data.description\n : 'There was an error with the request, please check the console for more information.'\n return swal(`${error.response.status} ${error.response.statusText}`, description, 'error')\n })\n}\n\nwindow.onload = () => {\n page.verify()\n\n page.user = document.querySelector('#user')\n page.pass = document.querySelector('#pass')\n\n // Prevent default form's submit action\n const form = document.querySelector('#authForm')\n form.addEventListener('submit', event => {\n event.preventDefault()\n })\n\n document.querySelector('#loginBtn').addEventListener('click', event => {\n if (!form.checkValidity()) return\n page.do('login', event.currentTarget)\n })\n\n document.querySelector('#registerBtn').addEventListener('click', event => {\n if (!form.checkValidity()) return\n page.do('register', event.currentTarget)\n })\n}\n"]} \ No newline at end of file diff --git a/dist/js/dashboard.js b/dist/js/dashboard.js index 199d1e5..43ccbab 100644 --- a/dist/js/dashboard.js +++ b/dist/js/dashboard.js @@ -1,2 +1,2 @@ -var lsKeys={token:"token",viewType:{uploads:"viewTypeUploads",uploadsAll:"viewTypeUploadsAll"},selected:{uploads:"selectedUploads",uploadsAll:"selectedUploadsAll",users:"selectedUsers"}},page={dom:null,token:localStorage[lsKeys.token],username:null,permissions:null,menusContainer:null,menus:[],currentView:null,views:{uploads:{type:localStorage[lsKeys.viewType.uploads],album:null,pageNum:null},uploadsAll:{type:localStorage[lsKeys.viewType.uploadsAll],filters:null,pageNum:null,all:!0},users:{pageNum:null}},selected:{uploads:[],uploadsAll:[],users:[]},checkboxes:{uploads:[],uploadsAll:[],users:[]},lastSelected:{upload:null,uploadsAll:null,user:null},selectAlbumContainer:null,cache:{uploads:{},albums:{},users:{}},clipboardJS:null,lazyLoad:null,imageExts:[".webp",".jpg",".jpeg",".gif",".png",".tiff",".tif",".svg"],videoExts:[".webm",".mp4",".wmv",".avi",".mov",".mkv"],isTriggerLoading:null,fadingIn:null,preparePage:function(){page.token?page.verifyToken(page.token,!0):window.location="auth"},verifyToken:function(e,a){axios.post("api/tokens/verify",{token:e}).then((function(n){if(!1===n.data.success)return swal({title:"An error occurred!",text:n.data.description,icon:"error"}).then((function(){a&&(localStorage.removeItem(lsKeys.token),window.location="auth")}));axios.defaults.headers.common.token=e,localStorage[lsKeys.token]=e,page.token=e,page.username=n.data.username,page.permissions=n.data.permissions,page.prepareDashboard()})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},prepareDashboard:function(){page.dom=document.querySelector("#page"),page.dom.addEventListener("click",page.domClick,!0),page.dom.addEventListener("submit",(function(e){if(e.target&&e.target.classList.contains("prevent-default"))return e.preventDefault()}),!0),page.menusContainer=document.querySelector("#menu");for(var e=[{selector:"#itemUploads",onclick:page.getUploads},{selector:"#itemDeleteUploadsByNames",onclick:page.deleteUploadsByNames},{selector:"#itemManageAlbums",onclick:page.getAlbums},{selector:"#itemManageToken",onclick:page.changeToken},{selector:"#itemChangePassword",onclick:page.changePassword},{selector:"#itemLogout",onclick:page.logout,inactive:!0},{selector:"#itemManageUploads",onclick:page.getUploads,params:{all:!0},group:"moderator"},{selector:"#itemStatistics",onclick:page.getStatistics,group:"admin"},{selector:"#itemManageUsers",onclick:page.getUsers,group:"admin"}],a=function(a){if(!e[a].group||page.permissions[e[a].group]){var n=document.querySelector(e[a].selector);n.addEventListener("click",(function(n){page.menusContainer.classList.contains("is-loading")||e[a].onclick.call(null,Object.assign({trigger:n.currentTarget},e[a].params||{}))})),n.classList.remove("is-hidden"),page.menus.push(n)}},n=0;n';e.all&&(l='\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n \n
\n
\n
\n
\n ');for(var o='\n
\n '+l+'\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n ',c='\n
\n
\n \n \n
\n ',d=!1,u=n.some((function(e){return void 0!==e.expirydate})),p=0;p\n \n
\n '+i+"\n ";for(var g=document.querySelector("#table"),m=0;m'+h.name+'':b.innerHTML='

'+(h.extname||"N/A")+"

",b.innerHTML+='\n \n
\n '+(h.thumb?'\n \n \n \n \n ':"")+'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n
\n

'+h.name+"

\n

"+(h.appendix?""+h.appendix+" – ":"")+h.prettyBytes+"

\n "+(u&&h.prettyExpiryDate?'\n

EXP: '+h.prettyExpiryDate+"

":"")+"\n
\n ",g.appendChild(b),page.checkboxes[page.currentView]=g.querySelectorAll('.checkbox[data-action="select"]'),page.lazyLoad.update()}}else{page.dom.innerHTML="\n "+i+"\n "+o+"\n "+c+'\n
\n \n \n \n \n \n '+(void 0===e.album?"":"")+"\n \n "+(e.all?"":"")+"\n \n "+(u?"":"")+'\n \n \n \n \n \n
File"+(e.all?"User":"Album")+"SizeIPDateExpiry date
\n
\n
\n '+i+"\n ";for(var f=document.querySelector("#table"),v=0;v\n '+w.name+"\n "+(void 0===e.album?""+w.appendix+"":"")+"\n "+w.prettyBytes+"\n "+(e.all?""+(w.ip||"")+"":"")+"\n "+w.prettyDate+"\n "+(u?""+(w.prettyExpiryDate||"-")+"":"")+'\n \n \n \n \n \n \n \n \n \n \n \n '+(e.all?"":'\n \n \n \n \n ')+'\n \n \n \n \n \n \n ',f.appendChild(y),page.checkboxes[page.currentView]=f.querySelectorAll('.checkbox[data-action="select"]')}}var k=document.querySelector("#selectAll");k&&!d&&(k.checked=!0,k.title="Unselect all"),page.fadeAndScroll(),page.updateTrigger(e.trigger,"active"),"uploads"===page.currentView&&(page.views.uploads.album=e.album),"uploadsAll"===page.currentView&&(page.views.uploadsAll.filters=e.filters),page.views[page.currentView].pageNum=n.length?e.pageNum:0})).catch((function(a){return console.error(a),page.updateTrigger(e.trigger),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},setUploadsView:function(e,a){localStorage[lsKeys.viewType[page.currentView]]=e,page.views[page.currentView].type=e,page.getUploads(Object.assign({trigger:a},page.views[page.currentView]))},displayThumbnail:function(e){var a=page.cache.uploads[e];if(a.thumb){var n=document.createElement("div");if(n.innerHTML='\n
\n \n
\n \n
\n
\n ',a.original){var t=/.[\w]+(\?|$)/.exec(a.original),s=t&&t[0]?t[0].toLowerCase():null,r=page.imageExts.includes(s),i=!r&&page.videoExts.includes(s);(r||i)&&(n.innerHTML+='\n \n ',n.querySelector("#swalOriginal").addEventListener("click",(function(e){var t=e.currentTarget;if(!t.classList.contains("is-danger")){t.classList.add("is-loading");var s=n.querySelector("#swalThumb");if(r)s.src=a.original,s.onload=function(){t.classList.add("is-hidden"),document.body.querySelector(".swal-overlay .swal-modal:not(.is-expanded)").classList.add("is-expanded")},s.onerror=function(e){e.currentTarget.classList.add("is-hidden"),t.className="button is-danger is-fullwidth",t.innerHTML='\n \n \n \n Unable to load original\n '};else if(i){s.classList.add("is-hidden");var l=document.createElement("video");l.id="swalVideo",l.controls=!0,l.autoplay=!0,l.src=a.original,s.insertAdjacentElement("afterend",l),t.classList.add("is-hidden"),document.body.querySelector(".swal-overlay .swal-modal:not(.is-expanded)").classList.add("is-expanded")}}})))}return swal({content:n,buttons:!1}).then((function(){var e=n.querySelector("#swalVideo");e&&e.remove(),document.body.querySelector(".swal-overlay .swal-modal").classList.remove("is-expanded")}))}},selectAll:function(e){for(var a=0;at&&s>t&&sn&&s"),swal({content:a})},filterUploads:function(e){var a=document.querySelector("#filters").value;page.getUploads({all:!0,filters:a},e)},viewUserUploads:function(e,a){var n=page.cache.users[e];n&&(a.classList.add("is-loading"),page.getUploads({all:!0,filters:"user:"+n.username.replace(/ /g,"\\ "),trigger:document.querySelector("#itemManageUploads")}))},deleteUpload:function(e){page.postBulkDeleteUploads({all:"uploadsAll"===page.currentView,field:"id",values:[e],cb:function(a){!a.length&&page.selected[page.currentView].includes(e)&&page.selected[page.currentView].splice(page.selected[page.currentView].indexOf(e),1),page.selected[page.currentView].length?localStorage[lsKeys.selected[page.currentView]]=JSON.stringify(page.selected[page.currentView]):delete localStorage[lsKeys.selected[page.currentView]],page.getUploads(Object.assign({autoPage:!0},page.views[page.currentView]))}})},bulkDeleteUploads:function(){if(!page.selected[page.currentView].length)return swal("An error occurred!","You have not selected any uploads.","error");page.postBulkDeleteUploads({all:"uploadsAll"===page.currentView,field:"id",values:page.selected[page.currentView],cb:function(e){e.length?page.selected[page.currentView]=page.selected[page.currentView].filter((function(a){return e.includes(a)})):page.selected[page.currentView]=[],page.selected[page.currentView].length?localStorage[lsKeys.selected[page.currentView]]=JSON.stringify(page.selected[page.currentView]):delete localStorage[lsKeys.selected[page.currentView]],page.getUploads(Object.assign({autoPage:!0},page.views[page.currentView]))}})},deleteUploadsByNames:function(e){void 0===e&&(e={});var a="";page.permissions.moderator&&(a="
Hint: You can use this feature to delete uploads by other users."),page.dom.innerHTML='\n
\n
\n \n
\n \n
\n

Separate each entry with a new line.'+a+'

\n
\n
\n
\n \n
\n
\n
\n ',page.fadeAndScroll(),page.updateTrigger(e.trigger,"active"),document.querySelector("#submitBulkDelete").addEventListener("click",(function(){var e=document.querySelector("#bulkDeleteNames"),a={},n=e.value.split(/\r?\n/).map((function(e){var a=e.trim();return/^[^\s]+$/.test(a)?a:""})).filter((function(e){return!(!e||Object.prototype.hasOwnProperty.call(a,e))&&(a[e]=!0)}));if(e.value=n.join("\n"),!n.length)return swal("An error occurred!","You have not entered any upload names.","error");page.postBulkDeleteUploads({all:!0,field:"name",values:n,cb:function(a){e.value=a.join("\n")}})}))},postBulkDeleteUploads:function(e){void 0===e&&(e={});var a=e.values.length,n=e.values.length+" upload"+(1===a?"":"s"),t="

You won't be able to recover "+n.replace(/^(\d*)(.*)/,"$1$2")+"!

";e.all&&(t+="\n

Warning: You may be nuking "+(1===a?"an upload":"some uploads")+" by "+(1===a?"another user":"other users")+"!

");var s=document.createElement("div");s.innerHTML=t,swal({title:"Are you sure?",content:s,icon:"warning",dangerMode:!0,buttons:{cancel:!0,confirm:{text:"Yes, nuke "+(1===e.values.length?"it":"them")+"!",closeModal:!1}}}).then((function(t){t&&axios.post("api/upload/bulkdelete",{field:e.fields,values:e.values}).then((function(t){if(t){if(!1===t.data.success)return"No token provided"===t.data.description?page.verifyToken(page.token):swal("An error occurred!",t.data.description,"error");var s=Array.isArray(t.data.failed)?t.data.failed:[];s.length===e.values.length?swal("An error occurred!","Unable to delete any of the "+n+".","error"):s.length&&s.length\n

You are about to add '+n+" upload"+(1===n?"":"s")+' to an album.

\n

If an upload is already in an album, it will be moved.

\n \n
\n
\n
\n \n
\n
\n
\n ',swal({icon:"warning",content:t,buttons:{cancel:!0,confirm:{text:"OK",closeModal:!1}}}).then((function(n){if(n){var t=parseInt(document.querySelector("#swalAlbum").value);if(isNaN(t))return swal("An error occurred!","You did not choose an album.","error");axios.post("api/albums/addfiles",{ids:e,albumid:t}).then((function(n){if(n)if(!1!==n.data.success){var s=e.length;n.data.failed&&n.data.failed.length&&(s-=n.data.failed.length);var r="upload"+(1===e.length?"":"s");if(!s)return swal("An error occurred!","Could not add the "+r+" to the album.","error");swal("Woohoo!","Successfully "+(t<0?"removed":"added")+" "+s+" "+r+" "+(t<0?"from":"to")+" the album.","success"),a(n.data.failed)}else"No token provided"===n.data.description?page.verifyToken(page.token):swal("An error occurred!",n.data.description,"error")})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))}})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")})),axios.get("api/albums").then((function(e){if(!1!==e.data.success){var a=document.querySelector("#swalAlbum");a&&(a.innerHTML+=e.data.albums.map((function(e){return'"})).join("\n"),a.getElementsByTagName("option")[1].innerHTML="Choose an album",a.removeAttribute("disabled"))}else"No token provided"===e.data.description?page.verifyToken(page.token):swal("An error occurred!",e.data.description,"error")})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},getAlbums:function(e){void 0===e&&(e={}),page.updateTrigger(e.trigger,"loading"),axios.get("api/albums").then((function(a){if(a){if(!1===a.data.success)return"No token provided"===a.data.description?page.verifyToken(page.token):(page.updateTrigger(e.trigger),swal("An error occurred!",a.data.description,"error"));page.cache.albums={},page.dom.innerHTML='\n

Create new album

\n
\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n

List of albums

\n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n
IDNameFilesCreated atPublic link
\n
\n ';for(var n=a.data.homeDomain,t=document.querySelector("#table"),s=0;s"+r.id+"\n "+r.name+"\n "+r.files+"\n "+r.prettyDate+"\n '+i+'\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ',t.appendChild(l)}page.fadeAndScroll(),page.updateTrigger(e.trigger,"active")}})).catch((function(a){return console.error(a),page.updateTrigger(e.trigger),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},editAlbum:function(e){var a=page.cache.albums[e];if(a){var n=document.createElement("div");n.innerHTML='\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n
\n
\n \n
\n
\n ',swal({title:"Edit album",icon:"info",content:n,buttons:{cancel:!0,confirm:{closeModal:!1}}}).then((function(n){n&&axios.post("api/albums/edit",{id:e,name:document.querySelector("#swalName").value.trim(),description:document.querySelector("#swalDescription").value.trim(),download:document.querySelector("#swalDownload").checked,public:document.querySelector("#swalPublic").checked,requestLink:document.querySelector("#swalRequestLink").checked}).then((function(e){if(e){if(!1===e.data.success)return"No token provided"===e.data.description?page.verifyToken(page.token):swal("An error occurred!",e.data.description,"error");e.data.identifier?swal("Success!","Your album's new identifier is: "+e.data.identifier+".","success"):e.data.name!==a.name?swal("Success!","Your album was renamed to: "+e.data.name+".","success"):swal("Success!","Your album was edited!","success"),page.getAlbumsSidebar(),page.getAlbums()}})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))}))}},deleteAlbum:function(e){swal({title:"Are you sure?",text:"This won't delete your uploads, only the album!",icon:"warning",dangerMode:!0,buttons:{cancel:!0,confirm:{text:"Yes, delete it!",closeModal:!1},purge:{text:"Umm, delete the uploads too please?",value:"purge",className:"swal-button--danger",closeModal:!1}}}).then((function(a){a&&axios.post("api/albums/delete",{id:e,purge:"purge"===a}).then((function(e){if(!1===e.data.success)return"No token provided"===e.data.description?page.verifyToken(page.token):Array.isArray(e.data.failed)&&e.data.failed.length?swal("An error occurred!","Unable to delete ","error"):swal("An error occurred!",e.data.description,"error");swal("Deleted!","Your album has been deleted.","success"),page.getAlbumsSidebar(),page.getAlbums()})).catch((function(e){return console.error(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))}))},submitAlbum:function(e){page.updateTrigger(e,"loading"),axios.post("api/albums",{name:document.querySelector("#albumName").value,description:document.querySelector("#albumDescription").value}).then((function(a){if(a){if(page.updateTrigger(e),!1===a.data.success)return"No token provided"===a.data.description?page.verifyToken(page.token):swal("An error occurred!",a.data.description,"error");swal("Woohoo!","Album was created successfully.","success"),page.getAlbumsSidebar(),page.getAlbums()}})).catch((function(a){return console.error(a),page.updateTrigger(e),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},getAlbumsSidebar:function(){axios.get("api/albums/sidebar").then((function(e){if(e){if(!1===e.data.success)return"No token provided"===e.data.description?page.verifyToken(page.token):swal("An error occurred!",e.data.description,"error");var a=document.querySelector("#albumsContainer"),n=a.querySelectorAll("li > a");if(n.length){for(var t=0;t\n \n \n \n \n ',page.fadeAndScroll(),page.updateTrigger(e.trigger,"active"),document.querySelector("#getNewToken").addEventListener("click",(function(e){var a=e.currentTarget;page.updateTrigger(a,"loading"),axios.post("api/tokens/change").then((function(e){if(!1===e.data.success)return"No token provided"===e.data.description?page.verifyToken(page.token):(page.updateTrigger(a),swal("An error occurred!",e.data.description,"error"));page.updateTrigger(a),swal({title:"Woohoo!",text:"Your token was successfully changed.",icon:"success"}).then((function(){axios.defaults.headers.common.token=e.data.token,localStorage[lsKeys.token]=e.data.token,page.token=e.data.token,page.changeToken()}))})).catch((function(e){return console.error(e),page.updateTrigger(a),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))}))})).catch((function(a){return console.error(a),page.updateTrigger(e.trigger),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},changePassword:function(e){void 0===e&&(e={}),page.dom.innerHTML='\n
\n
\n \n
\n \n
\n
\n
\n \n
\n \n
\n
\n
\n
\n \n
\n
\n
\n ',page.fadeAndScroll(),page.updateTrigger(e.trigger,"active"),document.querySelector("#sendChangePassword").addEventListener("click",(function(e){document.querySelector("#password").value===document.querySelector("#passwordConfirm").value?page.sendNewPassword(document.querySelector("#password").value,e.currentTarget):swal({title:"Password mismatch!",text:"Your passwords do not match, please try again.",icon:"error"})}))},sendNewPassword:function(e,a){page.updateTrigger(a,"loading"),axios.post("api/password/change",{password:e}).then((function(e){if(page.updateTrigger(a),!1===e.data.success)return"No token provided"===e.data.description?page.verifyToken(page.token):swal("An error occurred!",e.data.description,"error");swal({title:"Woohoo!",text:"Your password was successfully changed.",icon:"success"}).then((function(){page.changePassword()}))})).catch((function(e){return console.error(e),page.updateTrigger(a),swal("An error occurred!","There was an error with the request, please check the console for more information.","error")}))},getUsers:function(e){if(void 0===e&&(e={}),page.updateTrigger(e.trigger,"loading"),void 0===e.pageNum&&(e.pageNum=0),!page.permissions.admin)return swal("An error occurred!","You can not do this!","error");var a="api/users/"+e.pageNum;axios.get(a).then((function(a){if(!1===a.data.success)return"No token provided"===a.data.description?page.verifyToken(page.token):(page.updateTrigger(e.trigger),swal("An error occurred!",a.data.description,"error"));if(e.pageNum&&0===a.data.users.length)return page.updateTrigger(e.trigger),swal("An error occurred!","There are no more users to populate page "+(e.pageNum+1)+".","error");page.currentView="users",page.cache.users={};var n=page.paginate(a.data.count,25,e.pageNum),t='\n
\n
\n
\n
\n
\n
\n \n
\n
\n \n
\n
\n
\n
\n
\n ',s=!1;page.dom.innerHTML="\n "+n+"\n "+t+'\n \n \n \n
\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
IDUsernameUploadsUsageGroup
\n
\n
\n '+n+"\n ";for(var r=document.querySelector("#table"),i=0;i