'use strict' var tempID = '_fm_internal_temporary_id', ssl = (window.location.protocol === 'https:'), templates = {}, selectedItems = [], overlay, clickOverlay // Removes an element, if exists, from an array Array.prototype.removeElement = function (element) { var i = this.indexOf(element) if (i !== -1) this.splice(i, 1) } // Replaces an element inside an array by another Array.prototype.replaceElement = function (oldElement, newElement) { var i = this.indexOf(oldElement) if (i != -1) this[i] = newElement } // Sends a costum event to itself Document.prototype.sendCostumEvent = function (text) { this.dispatchEvent(new CustomEvent(text)) } // Gets the content of a cookie Document.prototype.getCookie = function (name) { var re = new RegExp('(?:(?:^|.*;\\s*)' + name + '\\s*\\=\\s*([^;]*).*$)|^.*$') return document.cookie.replace(re, '$1') } // Remove the last directory of an url var removeLastDirectoryPartOf = function (url) { var arr = url.split('/') if (arr.pop() === '') { arr.pop() } return (arr.join('/')) } function getCSSRule (rules) { for (let i = 0; i < rules.length; i++) { rules[i] = rules[i].toLowerCase() } var result = null, find = Array.prototype.find find.call(document.styleSheets, styleSheet => { result = find.call(styleSheet.cssRules, cssRule => { let found = false if (cssRule instanceof CSSStyleRule) { for (let i = 0; i < rules.length; i++) { if (cssRule.selectorText.toLowerCase() == rules[i]) found = true } } return found }) return result != null }) return result } /* * * * * * * * * * * * * * * * * * * BUTTONS * * * * * * * * * * * * * * * * * * */ var buttons = { previousState: {} } buttons.setLoading = function (name) { if (typeof this[name] === 'undefined') return let i = this[name].querySelector('i') this.previousState[name] = i.innerHTML i.style.opacity = 0 setTimeout(function () { i.classList.add('spin') i.innerHTML = 'autorenew' i.style.opacity = 1 }, 200) } // Changes an element to done animation buttons.setDone = function (name, success = true) { let i = this[name].querySelector('i') i.style.opacity = 0 let thirdStep = () => { i.innerHTML = this.previousState[name] i.style.opacity = null if (selectedItems.length == 0 && document.getElementById('listing')) document.sendCostumEvent('changed-selected') } let secondStep = () => { i.style.opacity = 0 setTimeout(thirdStep, 200) } let firstStep = () => { i.classList.remove('spin') i.innerHTML = success ? 'done' : 'close' i.style.opacity = 1 setTimeout(secondStep, 1000) } setTimeout(firstStep, 200) return false } /* * * * * * * * * * * * * * * * * * * WEBDAV * * * * * * * * * * * * * * * * * * */ var webdav = {} webdav.convertURL = function (url) { return window.location.origin + url.replace(baseURL + '/', webdavURL + '/') } webdav.move = function (oldLink, newLink) { return new Promise((resolve, reject) => { let request = new XMLHttpRequest(), destination = newLink.replace(baseURL + '/', webdavURL + '/') destination = window.location.origin + destination.substring(prefixURL.length) request.open('MOVE', webdav.convertURL(oldLink), true) request.setRequestHeader('Destination', destination) request.onload = () => { if (request.status == 201 || request.status == 204) { resolve() } else { reject(request.statusText) } } request.onerror = () => reject(request.statusText) request.send() }) } webdav.put = function (link, body, headers = {}) { return new Promise((resolve, reject) => { let request = new XMLHttpRequest() request.open('PUT', webdav.convertURL(link), true) for (let key in headers) { request.setRequestHeader(key, headers[key]) } request.onload = () => { if (request.status == 201) { resolve() } else { reject(request.statusText) } } request.onerror = () => reject(request.statusText) request.send(body) }) } webdav.delete = function (link) { return new Promise((resolve, reject) => { let request = new XMLHttpRequest() request.open('DELETE', webdav.convertURL(link), true) request.onload = () => { if (request.status == 204) { resolve() } else { reject(request.statusText) } } request.onerror = () => reject(request.statusText) request.send() }) } webdav.new = function (link) { return new Promise((resolve, reject) => { let request = new XMLHttpRequest() request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), webdav.convertURL(link), true) request.onload = () => { if (request.status == 201) { resolve() } else { reject(request.statusText) } } request.onerror = () => reject(request.statusText) request.send() }) } /* * * * * * * * * * * * * * * * * * * EVENTS * * * * * * * * * * * * * * * * * * */ function closePrompt (event) { let prompt = document.querySelector('.prompt') if (!prompt) return if (typeof event != 'undefined') { event.preventDefault() } document.querySelector('.overlay').classList.remove('active') prompt.classList.remove('active') setTimeout(() => { prompt.remove() }, 100) } function notImplemented (event) { event.preventDefault() clickOverlay.click() let clone = document.importNode(templates.info.content, true) clone.querySelector('h3').innerHTML = 'Not implemented' clone.querySelector('p').innerHTML = "Sorry, but this feature wasn't implemented yet." document.querySelector('body').appendChild(clone) document.querySelector('.overlay').classList.add('active') document.querySelector('.prompt').classList.add('active') } // Prevent Default event var preventDefault = function (event) { event.preventDefault() } function logoutEvent (event) { let request = new XMLHttpRequest() request.open('GET', window.location.pathname, true, 'username', 'password') request.send() request.onreadystatechange = function () { if (request.readyState == 4) { window.location = '/' } } } function openEvent (event) { if (event.currentTarget.classList.contains('disabled')) return false let link = '?raw=true' if (selectedItems.length) { link = document.getElementById(selectedItems[0]).dataset.url + link } else { link = window.location.pathname + link } window.open(link) return false } function deleteOnSingleFile () { closePrompt() buttons.setLoading('delete') let promises = [] webdav.delete(window.location.pathname) .then(() => { window.location.pathname = removeLastDirectoryPartOf(window.location.pathname) }) .catch(e => { buttons.setDone('delete', false) console.log(e) }) } function deleteOnListing () { closePrompt() buttons.setLoading('delete') let promises = [] for (let id of selectedItems) { promises.push(webdav.delete(document.getElementById(id).dataset.url)) } Promise.all(promises) .then(() => { listing.reload() buttons.setDone('delete') }) .catch(e => { console.log(e) buttons.setDone('delete', false) }) } // Handles the delete button event function deleteEvent (event) { let single = false if (!selectedItems.length) { selectedItems = ['placeholder'] single = true } let clone = document.importNode(templates.question.content, true) clone.querySelector('h3').innerHTML = 'Delete files' if (single) { clone.querySelector('form').addEventListener('submit', deleteOnSingleFile) clone.querySelector('p').innerHTML = `Are you sure you want to delete this file/folder?` } else { clone.querySelector('form').addEventListener('submit', deleteOnListing) clone.querySelector('p').innerHTML = `Are you sure you want to delete ${selectedItems.length} file(s)?` } clone.querySelector('input').remove() clone.querySelector('.ok').innerHTML = 'Delete' document.body.appendChild(clone) document.querySelector('.overlay').classList.add('active') document.querySelector('.prompt').classList.add('active') return false } function resetSearchText () { let box = document.querySelector('#search > div div') if (user.AllowCommands) { box.innerHTML = `Search or use one of your supported commands: ${user.Commands.join(", ")}.` } else { box.innerHTML = 'Type and press enter to search.' } } function searchEvent (event) { if (this.value.length == 0) { resetSearchText() return } let value = this.value, search = document.getElementById('search'), scrollable = document.querySelector('#search > div'), box = document.querySelector('#search > div div'), pieces = value.split(' '), supported = false user.Commands.forEach(function (cmd) { if (cmd == pieces[0]) { supported = true } }) if (!supported || !user.AllowCommands) { box.innerHTML = 'Press enter to search.' } else { box.innerHTML = 'Press enter to execute.' } if (event.keyCode == 13) { box.innerHTML = '' search.classList.add('ongoing') let url = window.location.host + window.location.pathname if (document.getElementById('editor')) { url = removeLastDirectoryPartOf(url) } let protocol = ssl ? 'wss:' : 'ws:' if (supported && user.AllowCommands) { let conn = new WebSocket(`${protocol}//${url}?command=true`) conn.onopen = function () { conn.send(value) } conn.onmessage = function (event) { box.innerHTML = event.data scrollable.scrollTop = scrollable.scrollHeight } conn.onclose = function (event) { search.classList.remove('ongoing') listing.reload() } return } box.innerHTML = '' let ul = box.querySelector('ul'), conn = new WebSocket(`${protocol}//${url}?search=true`) conn.onopen = function () { conn.send(value) } conn.onmessage = function (event) { ul.innerHTML += '
  • ' + event.data + '
  • ' scrollable.scrollTop = scrollable.scrollHeight } conn.onclose = function (event) { search.classList.remove('ongoing') } } } function setupSearch () { let search = document.getElementById('search'), searchInput = search.querySelector('input'), searchDiv = search.querySelector('div'), hover = false, focus = false resetSearchText() searchInput.addEventListener('focus', event => { focus = true search.classList.add('active') }) searchDiv.addEventListener('mouseover', event => { hover = true search.classList.add('active') }) searchInput.addEventListener('blur', event => { focus = false if (hover) return search.classList.remove('active') }) search.addEventListener('mouseleave', event => { hover = false if (focus) return search.classList.remove('active') }) search.addEventListener('click', event => { search.classList.add('active') search.querySelector('input').focus() }) searchInput.addEventListener('keyup', searchEvent) } function closeHelp (event) { event.preventDefault() document.querySelector('.help').classList.remove('active') document.querySelector('.overlay').classList.remove('active') } function openHelp (event) { closePrompt(event) document.querySelector('.help').classList.add('active') document.querySelector('.overlay').classList.add('active') } window.addEventListener('keydown', (event) => { if (event.keyCode == 27) { if (document.querySelector('.help.active')) { closeHelp(event) } } if (event.keyCode == 46) { deleteEvent(event) } if (event.keyCode == 112) { event.preventDefault() openHelp(event) } }) /* * * * * * * * * * * * * * * * * * * BOOTSTRAP * * * * * * * * * * * * * * * * * * */ document.addEventListener('DOMContentLoaded', function (event) { overlay = document.querySelector('.overlay') clickOverlay = document.querySelector('#click-overlay') buttons.logout = document.getElementById('logout') buttons.open = document.getElementById('open') buttons.delete = document.getElementById('delete') buttons.previous = document.getElementById('previous') // Attach event listeners buttons.logout.addEventListener('click', logoutEvent) buttons.open.addEventListener('click', openEvent) templates.question = document.querySelector('#question-template') templates.info = document.querySelector('#info-template') templates.move = document.querySelector('#move-template') if (user.AllowEdit) { buttons.delete.addEventListener('click', deleteEvent) } let dropdownButtons = document.querySelectorAll('.action[data-dropdown]') Array.from(dropdownButtons).forEach(button => { button.addEventListener('click', event => { button.querySelector('ul').classList.toggle('active') clickOverlay.classList.add('active') clickOverlay.addEventListener('click', event => { button.querySelector('ul').classList.remove('active') clickOverlay.classList.remove('active') }) }) }) overlay.addEventListener('click', event => { if (document.querySelector('.help.active')) { closeHelp(event) return } closePrompt(event) }) let mainActions = document.getElementById('main-actions') document.getElementById('more').addEventListener('click', event => { event.preventDefault() event.stopPropagation() clickOverlay.classList.add('active') mainActions.classList.add('active') clickOverlay.addEventListener('click', event => { mainActions.classList.remove('active') clickOverlay.classList.remove('active') }) }) setupSearch() return false })