/* global swal, axios, ClipboardJS, LazyLoad */ // keys for localStorage const lsKeys = { token: 'token', viewType: { uploads: 'viewTypeUploads', uploadsAll: 'viewTypeUploadsAll' }, selected: { uploads: 'selectedUploads', uploadsAll: 'selectedUploadsAll', users: 'selectedUsers' } } const page = { // #page dom: null, // user token token: localStorage[lsKeys.token], // from api/tokens/verify username: null, permissions: null, currentView: null, views: { // config of uploads view uploads: { type: localStorage[lsKeys.viewType.uploads], album: null, // album's id pageNum: null // page num }, // config of uploads view (all) uploadsAll: { type: localStorage[lsKeys.viewType.uploadsAll], uploader: null, // uploader's name pageNum: null, // page num all: true }, // config of users view users: { pageNum: null } }, // id of selected items (shared across pages and will be synced with localStorage) selected: { uploads: [], uploadsAll: [], users: [] }, checkboxes: { uploads: [], uploadsAll: [], users: [] }, lastSelected: { upload: null, uploadsAll: null, user: null }, // select album dom for dialogs/modals selectAlbumContainer: null, // cache for dialogs/modals cache: { uploads: {}, albums: {}, users: {} }, clipboardJS: null, lazyLoad: null, imageExtensions: ['.webp', '.jpg', '.jpeg', '.bmp', '.gif', '.png'], fadingIn: null } page.preparePage = function () { if (!page.token) { window.location = 'auth' return } page.verifyToken(page.token, true) } page.verifyToken = function (token, reloadOnError) { axios.post('api/tokens/verify', { token }).then(function (response) { if (response.data.success === false) return swal({ title: 'An error occurred!', text: response.data.description, icon: 'error' }).then(function () { if (!reloadOnError) return localStorage.removeItem(lsKeys.token) location.location = 'auth' }) axios.defaults.headers.common.token = token localStorage[lsKeys.token] = token page.token = token page.username = response.data.username page.permissions = response.data.permissions page.prepareDashboard() }).catch(function (error) { console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.prepareDashboard = function () { page.dom = document.getElementById('page') page.dom.addEventListener('click', page.domClick, true) // document.getElementById('auth').style.display = 'none' document.getElementById('dashboard').style.display = 'block' if (page.permissions.moderator) { const itemManageUploads = document.getElementById('itemManageUploads') itemManageUploads.removeAttribute('disabled') itemManageUploads.addEventListener('click', function () { page.setActiveMenu(this) page.getUploads({ all: true }) }) } if (page.permissions.admin) { const itemServerStats = document.getElementById('itemServerStats') itemServerStats.removeAttribute('disabled') itemServerStats.addEventListener('click', function () { page.setActiveMenu(this) page.getServerStats() }) const itemManageUsers = document.getElementById('itemManageUsers') itemManageUsers.removeAttribute('disabled') itemManageUsers.addEventListener('click', function () { page.setActiveMenu(this) page.getUsers() }) } document.getElementById('itemUploads').addEventListener('click', function () { page.setActiveMenu(this) page.getUploads({ all: false }) }) document.getElementById('itemDeleteByNames').addEventListener('click', function () { page.setActiveMenu(this) page.deleteByNames() }) document.getElementById('itemManageGallery').addEventListener('click', function () { page.setActiveMenu(this) page.getAlbums() }) document.getElementById('itemFileLength').addEventListener('click', function () { page.setActiveMenu(this) page.changeFileLength() }) document.getElementById('itemTokens').addEventListener('click', function () { page.setActiveMenu(this) page.changeToken() }) document.getElementById('itemPassword').addEventListener('click', function () { page.setActiveMenu(this) page.changePassword() }) const logoutBtn = document.getElementById('itemLogout') logoutBtn.addEventListener('click', function () { page.logout() }) logoutBtn.innerHTML = `Logout ( ${page.username} )` page.getAlbumsSidebar() if (typeof page.prepareShareX === 'function') page.prepareShareX() } page.logout = function () { localStorage.removeItem(lsKeys.token) location.reload('.') } page.getItemID = function (element) { // This expects the item's parent to have the item's ID let parent = element.parentNode // If the element is part of a set of controls, use the container's parent instead if (element.parentNode.classList.contains('controls')) parent = parent.parentNode return parseInt(parent.dataset.id) } page.domClick = function (event) { // We are processing clicks this way to avoid using "onclick" attribute // Apparently we will need to use "unsafe-inline" for "script-src" directive // of Content Security Policy (CSP), if we want ot use "onclick" attribute // Though I think that only applies to some browsers (?) // Either way, I personally would rather not // Of course it wouldn't have mattered if we didn't use CSP to begin with let element = event.target if (!element) return // If the clicked element is an icon, delegate event to its A parent; hacky if (element.tagName === 'I' && element.parentNode.tagName === 'SPAN') element = element.parentNode if (element.tagName === 'SPAN' && element.parentNode.tagName === 'A') element = element.parentNode // Skip elements that have no action data if (!element.dataset || !element.dataset.action) return // Skip disabled elements if (element.hasAttribute('disabled')) return event.stopPropagation() // maybe necessary const id = page.getItemID(element) const action = element.dataset.action switch (action) { case 'view-list': return page.setUploadsView('list', element) case 'view-thumbs': return page.setUploadsView('thumbs', element) case 'clear-selection': return page.clearSelection() case 'add-selected-files-to-album': return page.addSelectedFilesToAlbum() case 'bulk-delete': return page.deleteSelectedFiles() case 'select': return page.select(element, event) case 'add-to-album': return page.addSingleFileToAlbum(id) case 'delete-file': return page.deleteFile(id) case 'select-all': return page.selectAll(element) case 'display-thumbnail': return page.displayThumbnail(id) case 'delete-file-by-names': return page.deleteFileByNames() case 'submit-album': return page.submitAlbum(element) case 'edit-album': return page.editAlbum(id) case 'delete-album': return page.deleteAlbum(id) case 'get-new-token': return page.getNewToken(element) case 'edit-user': return page.editUser(id) case 'disable-user': return page.disableUser(id) case 'filter-by-uploader': return page.filterByUploader(element) case 'view-user-uploads': return page.viewUserUploads(id) case 'page-prev': case 'page-next': case 'page-goto': case 'jump-to-page': return page.switchPage(action, element) } } page.isLoading = function (element, state) { if (!element) return if (state) return element.classList.add('is-loading') element.classList.remove('is-loading') } page.fadeIn = function (content) { if (page.fadingIn) { clearTimeout(page.fadingIn) page.dom.classList.remove('fade-in') } page.dom.classList.add('fade-in') page.fadingIn = setTimeout(function () { page.dom.classList.remove('fade-in') }, 500) } page.switchPage = function (action, element) { const views = {} let func = null if (page.currentView === 'users') { func = page.getUsers } else { func = page.getUploads views.album = page.views[page.currentView].album views.all = page.views[page.currentView].all views.uploader = page.views[page.currentView].uploader } switch (action) { case 'page-prev': views.pageNum = page.views[page.currentView].pageNum - 1 if (views.pageNum < 0) return swal('An error occurred!', 'This is already the first page.', 'error') return func(views, element) case 'page-next': views.pageNum = page.views[page.currentView].pageNum + 1 return func(views, element) case 'page-goto': views.pageNum = parseInt(element.dataset.goto) return func(views, element) case 'jump-to-page': const jumpToPage = parseInt(document.getElementById('jumpToPage').value) views.pageNum = isNaN(jumpToPage) ? 0 : (jumpToPage - 1) if (views.pageNum < 0) views.pageNum = 0 return func(views, element) } } page.getUploads = function ({ pageNum, album, all, uploader } = {}, element) { if (element) page.isLoading(element, true) if ((all || uploader) && !page.permissions.moderator) return swal('An error occurred!', 'You can not do this!', 'error') if (typeof pageNum !== 'number' || pageNum < 0) pageNum = 0 let url = `api/uploads/${pageNum}` if (typeof album === 'string') url = `api/album/${album}/${pageNum}` const headers = {} if (all) headers.all = '1' if (uploader) headers.uploader = uploader axios.get(url, { headers }).then(function (response) { if (response.data.success === false) if (response.data.description === 'No token provided') { return page.verifyToken(page.token) } else { return swal('An error occurred!', response.data.description, 'error') } if (pageNum && (response.data.files.length === 0)) { // Only remove loading class here, since beyond this the entire page will be replaced anyways if (element) page.isLoading(element, false) return swal('An error occurred!', `There are no more uploads to populate page ${pageNum + 1}.`, 'error') } page.currentView = all ? 'uploadsAll' : 'uploads' page.cache.uploads = {} const pagination = page.paginate(response.data.count, 25, pageNum) let filter = '' if (all) filter = `
` const extraControls = ` ` const controls = `${upload.name}
${displayAlbumOrUser ? `${displayAlbumOrUser} – ` : ''}${upload.prettyBytes}
File | ${albumOrUser} | Size | Date |
---|
Separate each entry with a new line.${appendix}
You are about to add ${count} file${count === 1 ? '' : 's'} to an album.
If a file is already in an album, it will be moved.
ID | Name | Files | Created at | Public link |
---|
Default file name length is ${def} characters. ${(chg ? `Range allowed for user is ${min} to ${max} characters.` : 'Changing file name length is disabled at the moment.')}
ID | Username | Uploads | Usage | File length | Group |
---|
${user.username}'s new password is:
${response.data.password}
` swal({ title: 'Success!', icon: 'success', content: div }) } else if (response.data.update && response.data.update.username !== user.username) { swal('Success!', `${user.username} was renamed into: ${response.data.update.name}.`, 'success') } else { swal('Success!', 'The user was edited!', 'success') } page.getUsers(page.views.users) }).catch(function (error) { console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) }) } page.disableUser = function (id) { const user = page.cache.users[id] if (!user || !user.enabled) return const content = document.createElement('div') content.innerHTML = `You will be disabling a user with the username ${page.cache.users[id].username}!` swal({ title: 'Are you sure?', icon: 'warning', content, dangerMode: true, buttons: { cancel: true, confirm: { text: 'Yes, disable them!', closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/users/disable', { id }).then(function (response) { if (!response) return if (response.data.success === false) if (response.data.description === 'No token provided') return page.verifyToken(page.token) else return swal('An error occurred!', response.data.description, 'error') swal('Success!', 'The user has been disabled.', 'success') page.getUsers(page.views.users) }).catch(function (error) { console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) }) } page.paginate = function (totalItems, itemsPerPage, currentPage) { // Roughly based on https://github.com/mayuska/pagination/blob/master/index.js currentPage = currentPage + 1 const step = 3 const numPages = Math.ceil(totalItems / itemsPerPage) let template = '' const elementsToShow = step * 2 const add = { pageNum (start, end) { for (let i = start; i <= end; ++i) template += `${key.toUpperCase()} |
---|