/* 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 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) { 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 = `
${filter}
` const controls = `
${all ? '' : ` `} Bulk delete
` let allSelected = true if (page.views[page.currentView].type === 'thumbs') { page.dom.innerHTML = ` ${pagination} ${extraControls} ${controls}

${pagination} ` page.fadeIn() const table = document.getElementById('table') for (let i = 0; i < response.data.files.length; i++) { const upload = response.data.files[i] const selected = page.selected[page.currentView].includes(upload.id) if (!selected && allSelected) allSelected = false page.cache.uploads[upload.id] = { name: upload.name, thumb: upload.thumb, original: upload.file } // Prettify upload.prettyBytes = page.getPrettyBytes(parseInt(upload.size)) upload.prettyDate = page.getPrettyDate(new Date(upload.timestamp * 1000)) let displayAlbumOrUser = upload.album if (all) displayAlbumOrUser = upload.username || '' const div = document.createElement('div') div.className = 'image-container column is-narrow' div.dataset.id = upload.id if (upload.thumb !== undefined) div.innerHTML = `${upload.name}` else div.innerHTML = `

${upload.extname || 'N/A'}

` div.innerHTML += `

${upload.name}

${displayAlbumOrUser ? `${displayAlbumOrUser} – ` : ''}${upload.prettyBytes}

` table.appendChild(div) page.checkboxes[page.currentView] = Array.from(table.querySelectorAll('.checkbox[data-action="select"]')) page.lazyLoad.update() } } else { let albumOrUser = 'Album' if (all) albumOrUser = 'User' page.dom.innerHTML = ` ${pagination} ${extraControls} ${controls}
File ${albumOrUser} Size Date

${pagination} ` page.fadeIn() const table = document.getElementById('table') for (let i = 0; i < response.data.files.length; i++) { const upload = response.data.files[i] const selected = page.selected[page.currentView].includes(upload.id) if (!selected && allSelected) allSelected = false page.cache.uploads[upload.id] = { name: upload.name, thumb: upload.thumb, original: upload.file } // Prettify upload.prettyBytes = page.getPrettyBytes(parseInt(upload.size)) upload.prettyDate = page.getPrettyDate(new Date(upload.timestamp * 1000)) let displayAlbumOrUser = upload.album if (all) displayAlbumOrUser = upload.username || '' const tr = document.createElement('tr') tr.dataset.id = upload.id tr.innerHTML = ` ${upload.name} ${displayAlbumOrUser} ${upload.prettyBytes} ${upload.prettyDate} ${all ? '' : ` `} ` table.appendChild(tr) page.checkboxes[page.currentView] = Array.from(table.querySelectorAll('.checkbox[data-action="select"]')) } } if (allSelected && response.data.files.length) { const selectAll = document.getElementById('selectAll') if (selectAll) selectAll.checked = true } if (page.currentView === 'uploads') page.views.uploads.album = album if (page.currentView === 'uploadsAll') page.views.uploadsAll.uploader = uploader page.views[page.currentView].pageNum = response.data.files.length ? pageNum : 0 }).catch(function (error) { if (element) page.isLoading(element, false) console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.setUploadsView = function (view, element) { localStorage[lsKeys.viewType[page.currentView]] = view page.views[page.currentView].type = view page.getUploads(page.views[page.currentView], element) } page.displayThumbnail = function (id) { const file = page.cache.uploads[id] if (!file.thumb) return const div = document.createElement('div') div.innerHTML = `
` if (file.original) { div.innerHTML += `
Load original
` div.querySelector('#swalOriginal').addEventListener('click', function () { const button = this const original = button.dataset.original button.classList.add('is-loading') const thumb = div.querySelector('#swalThumb') const exec = /.[\w]+(\?|$)/.exec(original) const isimage = exec && exec[0] && page.imageExtensions.includes(exec[0].toLowerCase()) if (isimage) { thumb.src = file.original thumb.onload = function () { button.style.display = 'none' document.body.querySelector('.swal-overlay .swal-modal:not(.is-expanded)').classList.add('is-expanded') } thumb.onerror = function () { button.className = 'button is-danger' button.innerHTML = 'Unable to load original' } } else { thumb.style.display = 'none' const video = document.createElement('video') video.id = 'swalVideo' video.controls = true video.src = file.original thumb.insertAdjacentElement('afterend', video) button.style.display = 'none' document.body.querySelector('.swal-overlay .swal-modal:not(.is-expanded)').classList.add('is-expanded') } }) } return swal({ content: div, buttons: false }).then(function () { const video = div.querySelector('#swalVideo') if (video) video.remove() // Restore modal size document.body.querySelector('.swal-overlay .swal-modal.is-expanded').classList.remove('is-expanded') }) } page.selectAll = function (element) { const checkboxes = page.checkboxes[page.currentView] const selected = page.selected[page.currentView] for (let i = 0; i < checkboxes.length; i++) { const id = page.getItemID(checkboxes[i]) if (isNaN(id)) continue if (checkboxes[i].checked !== element.checked) { checkboxes[i].checked = element.checked if (checkboxes[i].checked) selected.push(id) else selected.splice(selected.indexOf(id), 1) } } localStorage[lsKeys.selected[page.currentView]] = JSON.stringify(selected) page.selected[page.currentView] = selected element.title = element.checked ? 'Unselect all uploads' : 'Select all uploads' } page.selectInBetween = function (element, lastElement) { if (!element || !lastElement) return if (element === lastElement) return const checkboxes = page.checkboxes[page.currentView] if (!checkboxes || !checkboxes.length) return const thisIndex = checkboxes.indexOf(element) const lastIndex = checkboxes.indexOf(lastElement) const distance = thisIndex - lastIndex if (distance >= -1 && distance <= 1) return for (let i = 0; i < checkboxes.length; i++) if ((thisIndex > lastIndex && i > lastIndex && i < thisIndex) || (thisIndex < lastIndex && i > thisIndex && i < lastIndex)) { checkboxes[i].checked = true page.selected[page.currentView].push(page.getItemID(checkboxes[i])) } localStorage[lsKeys.selected[page.currentView]] = JSON.stringify(page.selected[page.currentView]) page.checkboxes[page.currentView] = checkboxes } page.select = function (element, event) { const lastSelected = page.lastSelected[page.currentView] if (event.shiftKey && lastSelected) page.selectInBetween(element, lastSelected) else page.lastSelected[page.currentView] = element const id = page.getItemID(element) if (isNaN(id)) return const selected = page.selected[page.currentView] if (!selected.includes(id) && element.checked) selected.push(id) else if (selected.includes(id) && !element.checked) selected.splice(selected.indexOf(id), 1) localStorage[lsKeys.selected[page.currentView]] = JSON.stringify(selected) page.selected[page.currentView] = selected } page.clearSelection = function () { const selected = page.selected[page.currentView] const type = page.currentView === 'users' ? 'users' : 'uploads' const count = selected.length if (!count) return swal('An error occurred!', `You have not selected any ${type}.`, 'error') const suffix = count === 1 ? type.substring(0, type.length - 1) : type return swal({ title: 'Are you sure?', text: `You are going to unselect ${count} ${suffix}.`, buttons: true }).then(function (proceed) { if (!proceed) return const checkboxes = page.checkboxes[page.currentView] for (let i = 0; i < checkboxes.length; i++) if (checkboxes[i].checked) checkboxes[i].checked = false localStorage[lsKeys.selected[page.currentView]] = '[]' page.selected[page.currentView] = [] const selectAll = document.getElementById('selectAll') if (selectAll) selectAll.checked = false return swal('Cleared selection!', `Unselected ${count} ${suffix}.`, 'success') }) } page.filterByUploader = function (element) { const uploader = document.getElementById('uploader').value page.getUploads({ all: true, uploader }, element) } page.viewUserUploads = function (id) { const user = page.cache.users[id] if (!user) return page.setActiveMenu(document.getElementById('itemManageUploads')) page.getUploads({ all: true, uploader: user.username }) } page.deleteFile = function (id) { // TODO: Share function with bulk delete, just like 'add selected uploads to album' and 'add single file to album' swal({ title: 'Are you sure?', text: 'You won\'t be able to recover the file!', icon: 'warning', dangerMode: true, buttons: { cancel: true, confirm: { text: 'Yes, delete it!', closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/upload/delete', { 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('Deleted!', 'The file has been deleted.', 'success') page.getUploads(page.views[page.currentView]) }).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.deleteSelectedFiles = function () { const count = page.selected[page.currentView].length if (!count) return swal('An error occurred!', 'You have not selected any uploads.', 'error') const suffix = `upload${count === 1 ? '' : 's'}` let text = `You won't be able to recover ${count} ${suffix}!` if (page.currentView === 'uploadsAll') text += '\nBe aware, you may be nuking uploads by other users!' swal({ title: 'Are you sure?', text, icon: 'warning', dangerMode: true, buttons: { cancel: true, confirm: { text: `Yes, nuke the ${suffix}!`, closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/upload/bulkdelete', { field: 'id', values: page.selected[page.currentView] }).then(function (bulkdelete) { if (!bulkdelete) return if (bulkdelete.data.success === false) if (bulkdelete.data.description === 'No token provided') { return page.verifyToken(page.token) } else { return swal('An error occurred!', bulkdelete.data.description, 'error') } let deleted = count if (bulkdelete.data.failed && bulkdelete.data.failed.length) { deleted -= bulkdelete.data.failed.length page.selected[page.currentView] = page.selected[page.currentView].filter(function (id) { return bulkdelete.data.failed.includes(id) }) } else { page.selected[page.currentView] = [] } localStorage[lsKeys.selected[page.currentView]] = JSON.stringify(page.selected[page.currentView]) swal('Deleted!', `${deleted} file${deleted === 1 ? ' has' : 's have'} been deleted.`, 'success') return page.getUploads(page.views[page.currentView]) }).catch(function (error) { console.log(error) swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) }) } page.deleteByNames = function () { page.dom.innerHTML = `

Delete by names

Separate each entry with a new line.

Bulk delete
` page.fadeIn() } page.deleteFileByNames = function () { const names = document.getElementById('names').value .split(/\r?\n/) .filter(function (n) { return n.trim().length }) const count = names.length if (!count) return swal('An error occurred!', 'You have not entered any file names.', 'error') const suffix = `file${count === 1 ? '' : 's'}` swal({ title: 'Are you sure?', text: `You won't be able to recover ${count} ${suffix}!`, icon: 'warning', dangerMode: true, buttons: { cancel: true, confirm: { text: `Yes, nuke the ${suffix}!`, closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/upload/bulkdelete', { field: 'name', values: names }).then(function (bulkdelete) { if (!bulkdelete) return if (bulkdelete.data.success === false) if (bulkdelete.data.description === 'No token provided') { return page.verifyToken(page.token) } else { return swal('An error occurred!', bulkdelete.data.description, 'error') } let deleted = count if (bulkdelete.data.failed && bulkdelete.data.failed.length) deleted -= bulkdelete.data.failed.length document.getElementById('names').value = bulkdelete.data.failed.join('\n') swal('Deleted!', `${deleted} file${deleted === 1 ? ' has' : 's have'} been deleted.`, 'success') }).catch(function (error) { console.log(error) swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) }) } page.addSelectedFilesToAlbum = function () { if (page.currentView !== 'uploads') return const count = page.selected[page.currentView].length if (!count) return swal('An error occurred!', 'You have not selected any uploads.', 'error') page.addFilesToAlbum(page.selected[page.currentView], function (failed) { if (!failed) return if (failed.length) page.selected[page.currentView] = page.selected[page.currentView].filter(function (id) { return failed.includes(id) }) else page.selected[page.currentView] = [] localStorage[lsKeys.selected[page.currentView]] = JSON.stringify(page.selected[page.currentView]) page.getUploads(page.views[page.currentView]) }) } page.addSingleFileToAlbum = function (id) { page.addFilesToAlbum([id], function (failed) { if (!failed) return page.getUploads(page.views[page.currentView]) }) } page.addFilesToAlbum = function (ids, callback) { const count = ids.length const content = document.createElement('div') content.innerHTML = `

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.

` swal({ icon: 'warning', content, buttons: { cancel: true, confirm: { text: 'OK', closeModal: false } } }).then(function (choose) { if (!choose) return const albumid = parseInt(document.getElementById('swalAlbum').value) if (isNaN(albumid)) return swal('An error occurred!', 'You did not choose an album.', 'error') axios.post('api/albums/addfiles', { ids, albumid }).then(function (add) { if (!add) return if (add.data.success === false) { if (add.data.description === 'No token provided') page.verifyToken(page.token) else swal('An error occurred!', add.data.description, 'error') return } let added = ids.length if (add.data.failed && add.data.failed.length) added -= add.data.failed.length const suffix = `file${ids.length === 1 ? '' : 's'}` if (!added) return swal('An error occurred!', `Could not add the ${suffix} to the album.`, 'error') swal('Woohoo!', `Successfully ${albumid < 0 ? 'removed' : 'added'} ${added} ${suffix} ${albumid < 0 ? 'from' : 'to'} the album.`, 'success') return callback(add.data.failed) }).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') }) }).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') }) // Get albums list then update content of swal axios.get('api/albums').then(function (list) { if (list.data.success === false) { if (list.data.description === 'No token provided') page.verifyToken(page.token) else swal('An error occurred!', list.data.description, 'error') return } const select = document.getElementById('swalAlbum') // If the prompt was replaced, the container would be missing if (!select) return select.innerHTML += list.data.albums .map(function (album) { return `` }) .join('\n') select.getElementsByTagName('option')[1].innerHTML = 'Choose an album' select.removeAttribute('disabled') }).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.getAlbums = function () { axios.get('api/albums').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') } page.cache.albums = {} page.dom.innerHTML = `

Create new album

Create

List of albums

ID Name Files Created at Public link
` page.fadeIn() const homeDomain = response.data.homeDomain const table = document.getElementById('table') for (let i = 0; i < response.data.albums.length; i++) { const album = response.data.albums[i] const albumUrl = `${homeDomain}/a/${album.identifier}` // Prettify album.prettyDate = page.getPrettyDate(new Date(album.timestamp * 1000)) page.cache.albums[album.id] = { name: album.name, download: album.download, public: album.public, description: album.description } const tr = document.createElement('tr') tr.innerHTML = ` ${album.id} ${album.name} ${album.files} ${album.prettyDate} ${albumUrl} ` table.appendChild(tr) } }).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.editAlbum = function (id) { const album = page.cache.albums[id] if (!album) return const div = document.createElement('div') div.innerHTML = `
` swal({ title: 'Edit album', icon: 'info', content: div, buttons: { cancel: true, confirm: { closeModal: false } } }).then(function (value) { if (!value) return axios.post('api/albums/edit', { id, name: document.getElementById('swalName').value, description: document.getElementById('swalDescription').value, download: document.getElementById('swalDownload').checked, public: document.getElementById('swalPublic').checked, requestLink: document.getElementById('swalRequestLink').checked }).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') } if (response.data.identifier) swal('Success!', `Your album's new identifier is: ${response.data.identifier}.`, 'success') else if (response.data.name !== album.name) swal('Success!', `Your album was renamed to: ${response.data.name}.`, 'success') else swal('Success!', 'Your album was edited!', 'success') page.getAlbumsSidebar() page.getAlbums() }).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.deleteAlbum = function (id) { swal({ title: 'Are you sure?', text: 'This won\'t delete your uploads, only the album!', icon: 'warning', dangerMode: true, buttons: { cancel: true, confirm: { text: 'Yes, delete it!', closeModal: false }, purge: { text: 'Umm, delete the uploads too please?', value: 'purge', className: 'swal-button--danger', closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/albums/delete', { id, purge: proceed === 'purge' }).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') } swal('Deleted!', 'Your album has been deleted.', 'success') page.getAlbumsSidebar() page.getAlbums() }).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.submitAlbum = function (element) { page.isLoading(element, true) axios.post('api/albums', { name: document.getElementById('albumName').value, description: document.getElementById('albumDescription').value }).then(function (response) { if (!response) return page.isLoading(element, false) 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('Woohoo!', 'Album was created successfully', 'success') page.getAlbumsSidebar() page.getAlbums() }).catch(function (error) { console.log(error) page.isLoading(element, false) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.getAlbumsSidebar = function () { axios.get('api/albums/sidebar').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') } const albumsContainer = document.getElementById('albumsContainer') albumsContainer.innerHTML = '' if (response.data.albums === undefined) return for (let i = 0; i < response.data.albums.length; i++) { const album = response.data.albums[i] const li = document.createElement('li') const a = document.createElement('a') a.id = album.id a.innerHTML = album.name a.addEventListener('click', function () { page.getAlbum(this) }) li.appendChild(a) albumsContainer.appendChild(li) } }).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.getAlbum = function (album) { page.setActiveMenu(album) page.getUploads({ album: album.id }) } page.changeFileLength = function () { axios.get('api/filelength/config').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') } // Shorter vars const { max, min } = response.data.config const [ chg, def ] = [ response.data.config.userChangeable, response.data.config.default ] const len = response.data.fileLength page.dom.innerHTML = `

File name length

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.')}

` page.fadeIn() document.getElementById('setFileLength').addEventListener('click', function () { page.setFileLength(document.getElementById('fileLength').value, this) }) }).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.setFileLength = function (fileLength, element) { page.isLoading(element, true) axios.post('api/filelength/change', { fileLength }).then(function (response) { page.isLoading(element, false) 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({ title: 'Woohoo!', text: 'Your file length was successfully changed.', icon: 'success' }).then(function () { page.changeFileLength() }) }).catch(function (error) { console.log(error) page.isLoading(element, false) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.changeToken = function () { axios.get('api/tokens').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') } page.dom.innerHTML = `

Manage your token

` page.fadeIn() }).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.getNewToken = function (element) { page.isLoading(element, true) axios.post('api/tokens/change').then(function (response) { page.isLoading(element, false) 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({ title: 'Woohoo!', text: 'Your token was successfully changed.', icon: 'success' }).then(function () { axios.defaults.headers.common.token = response.data.token localStorage[lsKeys.token] = response.data.token page.token = response.data.token page.changeToken() }) }).catch(function (error) { console.log(error) page.isLoading(element, false) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.changePassword = function () { page.dom.innerHTML = `

Change your password

` page.fadeIn() document.getElementById('sendChangePassword').addEventListener('click', function () { if (document.getElementById('password').value === document.getElementById('passwordConfirm').value) page.sendNewPassword(document.getElementById('password').value, this) else swal({ title: 'Password mismatch!', text: 'Your passwords do not match, please try again.', icon: 'error' }) }) } page.sendNewPassword = function (pass, element) { page.isLoading(element, true) axios.post('api/password/change', { password: pass }).then(function (response) { page.isLoading(element, false) 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({ title: 'Woohoo!', text: 'Your password was successfully changed.', icon: 'success' }).then(function () { page.changePassword() }) }).catch(function (error) { console.log(error) page.isLoading(element, false) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.setActiveMenu = function (activeItem) { const menu = document.getElementById('menu') const items = menu.getElementsByTagName('a') for (let i = 0; i < items.length; i++) items[i].classList.remove('is-active') activeItem.classList.add('is-active') } page.getUsers = function ({ pageNum } = {}, element) { if (element) page.isLoading(element, true) if (pageNum === undefined) pageNum = 0 if (!page.permissions.admin) return swal('An error occurred!', 'You can not do this!', 'error') const url = `api/users/${pageNum}` axios.get(url).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.users.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 users to populate page ${pageNum + 1}.`, 'error') } page.currentView = 'users' page.cache.users = {} const pagination = page.paginate(response.data.count, 25, pageNum) const extraControls = ` ` const controls = ` ` let allSelected = true page.dom.innerHTML = ` ${pagination} ${extraControls} ${controls}
ID Username Uploads Usage File length Group

${pagination} ` page.fadeIn() const table = document.getElementById('table') for (let i = 0; i < response.data.users.length; i++) { const user = response.data.users[i] const selected = page.selected.users.includes(user.id) if (!selected && allSelected) allSelected = false let displayGroup = null for (const group of Object.keys(user.groups)) { if (!user.groups[group]) break displayGroup = group } // Server-side explicitly expects either of these two values to consider a user as disabled const enabled = user.enabled !== false && user.enabled !== 0 page.cache.users[user.id] = { username: user.username, groups: user.groups, enabled, displayGroup } const tr = document.createElement('tr') tr.dataset.id = user.id tr.innerHTML = ` ${user.id} ${user.username} ${user.uploadsCount} ${page.getPrettyBytes(user.diskUsage)} ${user.fileLength || 'default'} ${displayGroup} ` table.appendChild(tr) // page.checkboxes.users = Array.from(table.getElementsByClassName('checkbox')) page.checkboxes.users = Array.from(table.querySelectorAll('.checkbox[data-action="select"]')) } if (allSelected && response.data.users.length) { const selectAll = document.getElementById('selectAll') if (selectAll) selectAll.checked = true } page.views.users.pageNum = response.data.users.length ? pageNum : 0 }).catch(function (error) { if (element) page.isLoading(element, false) console.log(error) return swal('An error occurred!', 'There was an error with the request, please check the console for more information.', 'error') }) } page.editUser = function (id) { const user = page.cache.users[id] if (!user) return const groupOptions = Object.keys(page.permissions).map(function (g, i, a) { const selected = g === user.displayGroup const disabled = !(a[i + 1] && page.permissions[a[i + 1]]) return `` }).join('\n') const div = document.createElement('div') div.innerHTML = `
` swal({ title: 'Edit user', icon: 'info', content: div, buttons: { cancel: true, confirm: { closeModal: false } } }).then(function (proceed) { if (!proceed) return axios.post('api/users/edit', { id, username: document.getElementById('swalUsername').value, group: document.getElementById('swalGroup').value, enabled: document.getElementById('swalEnabled').checked, resetPassword: document.getElementById('swalResetPassword').checked }).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') } if (response.data.password) { const div = document.createElement('div') div.innerHTML = `

${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 += `
  • ${i}
  • ` }, startDots () { template += `
  • 1
  • ` }, endDots () { template += `
  • ${numPages}
  • ` } } if (elementsToShow >= numPages) { add.pageNum(1, numPages) } else if (currentPage < elementsToShow) { add.pageNum(1, elementsToShow) add.endDots() } else if (currentPage > numPages - elementsToShow) { add.startDots() add.pageNum(numPages - elementsToShow, numPages) } else { add.startDots() add.pageNum(currentPage - step, currentPage, step) add.endDots() } return ` ` } page.getPrettyDate = function (date) { return date.getFullYear() + '-' + (date.getMonth() < 9 ? '0' : '') + // month's index starts from zero (date.getMonth() + 1) + '-' + (date.getDate() < 10 ? '0' : '') + date.getDate() + ' ' + (date.getHours() < 10 ? '0' : '') + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':' + (date.getSeconds() < 10 ? '0' : '') + date.getSeconds() } page.getPrettyBytes = function (num, si) { // MIT License // Copyright (c) Sindre Sorhus (sindresorhus.com) if (!Number.isFinite(num)) return num const neg = num < 0 ? '-' : '' const scale = si ? 1000 : 1024 if (neg) num = -num if (num < scale) return `${neg}${num} B` const exponent = Math.min(Math.floor(Math.log10(num) / 3), 8) // 8 is count of KMGTPEZY const numStr = Number((num / Math.pow(scale, exponent)).toPrecision(3)) const pre = (si ? 'kMGTPEZY' : 'KMGTPEZY').charAt(exponent - 1) + (si ? '' : 'i') return `${neg}${numStr} ${pre}B` } window.onload = function () { // Add 'no-touch' class to non-touch devices if (!('ontouchstart' in document.documentElement)) document.documentElement.classList.add('no-touch') const selectedKeys = ['uploads', 'uploadsAll', 'users'] for (const selectedKey of selectedKeys) { const ls = localStorage[lsKeys.selected[selectedKey]] if (ls) page.selected[selectedKey] = JSON.parse(ls) } page.preparePage() page.clipboardJS = new ClipboardJS('.clipboard-js') page.clipboardJS.on('success', function () { return swal('Copied!', 'The link has been copied to clipboard.', 'success') }) page.clipboardJS.on('error', function (event) { console.error(event) return swal('An error occurred!', 'There was an error when trying to copy the link to clipboard, please check the console for more information.', 'error') }) page.lazyLoad = new LazyLoad() }