diff --git a/_assets/index.html b/_assets/index.html index 6b19b129..6c540a9f 100644 --- a/_assets/index.html +++ b/_assets/index.html @@ -99,17 +99,7 @@ user: JSON.parse('{{ Marshal .User }}'), req: JSON.parse('{{ Marshal . }}'), webdavURL: "{{ .WebDavURL }}", - baseURL: "{{.BaseURL}}", - ssl: (window.location.protocol === 'https:'), - showInfo: false, - showHelp: false, - showDelete: false, - showRename: false, - showMove: false, - showNewFile: false, - showNewDir: false, - selected: [], - multiple: false + baseURL: "{{.BaseURL}}" }
diff --git a/_assets/package.json b/_assets/package.json index bac6a1fd..0ac07c70 100644 --- a/_assets/package.json +++ b/_assets/package.json @@ -12,7 +12,8 @@ "dependencies": { "filesize": "^3.5.10", "moment": "^2.18.1", - "vue": "^2.3.3" + "vue": "^2.3.3", + "vuex": "^2.3.1" }, "devDependencies": { "autoprefixer": "^6.7.2", diff --git a/_assets/src/App.vue b/_assets/src/App.vue index 84121ede..b5811259 100644 --- a/_assets/src/App.vue +++ b/_assets/src/App.vue @@ -22,11 +22,11 @@ My Files
- - @@ -43,15 +43,14 @@ - - - - - - - - -
+ + + + + + + +
@@ -89,16 +88,6 @@ function updateColumnSizes () { items.style.width = `calc(${100 / columns}% - 1em)` } -function resetPrompts () { - $.showHelp = false - $.showInfo = false - $.showDelete = false - $.showRename = false - $.showMove = false - $.showNewFile = false - $.showNewDir = false -} - function showRenameButton () { if ($.req.kind === 'listing') { if ($.selected.length === 1) { @@ -123,78 +112,6 @@ function showDeleteButton () { return $.user.allowEdit } -function keydown (event) { - // Esc! - if (event.keyCode === 27) { - resetPrompts() - - // Unselect all files and folders. - if ($.req.kind === 'listing') { - let items = document.getElementsByClassName('item') - Array.from(items).forEach(link => { - link.setAttribute('aria-selected', false) - }) - - $.selected = [] - } - - return - } - - // Del! - if (event.keyCode === 46) { - if (showDeleteButton()) { - $.showDelete = true - } - } - - // F1! - if (event.keyCode === 112) { - event.preventDefault() - $.showHelp = true - } - - // F2! - if (event.keyCode === 113) { - if (showRenameButton()) { - $.showRename = true - } - } - - // CTRL + S - if (event.ctrlKey || event.metaKey) { - switch (String.fromCharCode(event.which).toLowerCase()) { - case 's': - event.preventDefault() - - if ($.req.kind !== 'editor') { - window.location = '?download=true' - return - } - - // TODO: save file on editor! - } - } -} - -function startup () { - updateColumnSizes() - window.addEventListener('resize', updateColumnSizes) - window.history.replaceState({ - url: window.location.pathname, - name: document.title - }, document.title, window.location.pathname) - - window.addEventListener('keydown', keydown) - - let loading = document.getElementById('loading') - loading.classList.add('done') - - setTimeout(function () { - loading.parentNode.removeChild(loading) - }, 1000) -} - export default { name: 'app', components: { @@ -218,21 +135,78 @@ export default { NewDirPrompt }, mounted: function () { - startup() + updateColumnSizes() + window.addEventListener('resize', updateColumnSizes) + window.history.replaceState({ + url: window.location.pathname, + name: document.title + }, document.title, window.location.pathname) + + window.addEventListener('keydown', (event) => { + // Esc! + if (event.keyCode === 27) { + this.$store.commit('resetPrompts') + + // Unselect all files and folders. + if ($.req.kind === 'listing') { + let items = document.getElementsByClassName('item') + Array.from(items).forEach(link => { + link.setAttribute('aria-selected', false) + }) + + $.selected = [] + } + + return + } + + // Del! + if (event.keyCode === 46) { + if (showDeleteButton()) { + $.showDelete = true + } + } + + // F1! + if (event.keyCode === 112) { + event.preventDefault() + $.showHelp = true + } + + // F2! + if (event.keyCode === 113) { + if (showRenameButton()) { + $.showRename = true + } + } + + // CTRL + S + if (event.ctrlKey || event.metaKey) { + switch (String.fromCharCode(event.which).toLowerCase()) { + case 's': + event.preventDefault() + + if ($.req.kind !== 'editor') { + window.location = '?download=true' + return + } + + // TODO: save file on editor! + } + } + }) + + let loading = document.getElementById('loading') + loading.classList.add('done') + + setTimeout(function () { + loading.parentNode.removeChild(loading) + }, 1000) }, data: function () { return window.info }, methods: { - showOverlay: function () { - return $.showInfo || - $.showHelp || - $.showDelete || - $.showRename || - $.showMove || - $.showNewFile || - $.showNewDir - }, showUpload: function () { if (this.req.kind === 'editor') return false return $.user.allowNew @@ -250,7 +224,9 @@ export default { return false }, - resetPrompts: resetPrompts + resetPrompts: function () { + this.$store.commit('resetPrompts') + } } } diff --git a/_assets/src/components/DeleteButton.vue b/_assets/src/components/DeleteButton.vue index 559b6fb4..9624110e 100644 --- a/_assets/src/components/DeleteButton.vue +++ b/_assets/src/components/DeleteButton.vue @@ -10,7 +10,7 @@ export default { name: 'delete-button', methods: { show: function (event) { - window.info.showDelete = true + this.$store.commit('showDelete', true) } } } diff --git a/_assets/src/components/DeletePrompt.vue b/_assets/src/components/DeletePrompt.vue index 8c97dd14..ea99eb40 100644 --- a/_assets/src/components/DeletePrompt.vue +++ b/_assets/src/components/DeletePrompt.vue @@ -23,10 +23,10 @@ export default { }, methods: { cancel: function (event) { - $.showDelete = false + this.$store.commit('showDelete', false) }, submit: function (event) { - $.showDelete = false + this.$store.commit('showDelete', false) // buttons.setLoading('delete') if ($.req.kind !== 'listing') { diff --git a/_assets/src/components/InfoButton.vue b/_assets/src/components/InfoButton.vue index 1704d887..0d821380 100644 --- a/_assets/src/components/InfoButton.vue +++ b/_assets/src/components/InfoButton.vue @@ -10,7 +10,7 @@ export default { name: 'info-button', methods: { show: function (event) { - window.info.showInfo = true + this.$store.commit('showInfo', true) } } } diff --git a/_assets/src/components/InfoPrompt.vue b/_assets/src/components/InfoPrompt.vue index c552e737..e43875ec 100644 --- a/_assets/src/components/InfoPrompt.vue +++ b/_assets/src/components/InfoPrompt.vue @@ -102,7 +102,7 @@ export default { request.send() }, close: function () { - this.showInfo = false + this.$store.commit('showInfo', false) } } } diff --git a/_assets/src/components/MoveButton.vue b/_assets/src/components/MoveButton.vue index d15ef264..3b6c100b 100644 --- a/_assets/src/components/MoveButton.vue +++ b/_assets/src/components/MoveButton.vue @@ -10,7 +10,7 @@ export default { name: 'move-button', methods: { show: function (event) { - window.info.showMove = true + this.$store.commit('showMove', true) } } } diff --git a/_assets/src/components/MovePrompt.vue b/_assets/src/components/MovePrompt.vue index 60a58369..30dd7b7b 100644 --- a/_assets/src/components/MovePrompt.vue +++ b/_assets/src/components/MovePrompt.vue @@ -53,7 +53,7 @@ export default { }, methods: { cancel: function (event) { - $.showMove = false + this.$store.commit('showMove', false) }, move: function (event) { event.preventDefault() @@ -76,7 +76,7 @@ export default { promises.push(webdav.move(from, to)) } - $.showMove = false + this.$store.commit('showMove', false) Promise.all(promises) .then(() => { diff --git a/_assets/src/components/NewDirPrompt.vue b/_assets/src/components/NewDirPrompt.vue index acf23e24..4b4fa271 100644 --- a/_assets/src/components/NewDirPrompt.vue +++ b/_assets/src/components/NewDirPrompt.vue @@ -14,8 +14,6 @@ import page from '../page' import webdav from '../webdav' -var $ = window.info - export default { name: 'new-dir-prompt', data: function () { @@ -25,7 +23,7 @@ export default { }, methods: { cancel: function () { - $.showNewDir = false + this.$store.commit('showNewDir', false) }, submit: function (event) { event.preventDefault() @@ -45,7 +43,7 @@ export default { console.log(e) }) - $.showNewDir = false + this.$store.commit('showNewDir', false) } } } diff --git a/_assets/src/components/NewFilePrompt.vue b/_assets/src/components/NewFilePrompt.vue index 173dd738..da89b4d8 100644 --- a/_assets/src/components/NewFilePrompt.vue +++ b/_assets/src/components/NewFilePrompt.vue @@ -14,8 +14,6 @@ import page from '../page' import webdav from '../webdav' -var $ = window.info - export default { name: 'new-file-prompt', data: function () { @@ -25,7 +23,7 @@ export default { }, methods: { cancel: function () { - $.showNewFile = false + this.$store.commit('showNewFile', false) }, submit: function (event) { event.preventDefault() @@ -42,7 +40,7 @@ export default { console.log(e) }) - $.showNewFile = false + this.$store.commit('showNewFile', false) } } } diff --git a/_assets/src/components/RenameButton.vue b/_assets/src/components/RenameButton.vue index 875dd5fa..db8bb44d 100644 --- a/_assets/src/components/RenameButton.vue +++ b/_assets/src/components/RenameButton.vue @@ -10,7 +10,7 @@ export default { name: 'rename-button', methods: { show: function (event) { - window.info.showRename = true + this.$store.commit('showRename', true) } } } diff --git a/_assets/src/components/RenamePrompt.vue b/_assets/src/components/RenamePrompt.vue index c51c3f45..898d2d67 100644 --- a/_assets/src/components/RenamePrompt.vue +++ b/_assets/src/components/RenamePrompt.vue @@ -25,7 +25,7 @@ export default { }, methods: { cancel: function (event) { - $.showRename = false + this.$store.commit('showRename', false) }, oldName: function () { if ($.req.kind !== 'listing') { @@ -68,7 +68,7 @@ export default { }) this.name = '' - $.showRename = false + this.$store.commit('showRename', false) return } } diff --git a/_assets/src/components/Search.vue b/_assets/src/components/Search.vue index 9fd17783..b8d5bfb9 100644 --- a/_assets/src/components/Search.vue +++ b/_assets/src/components/Search.vue @@ -85,7 +85,7 @@ export default { url = page.removeLastDir(url) } - let protocol = $.ssl ? 'wss:' : 'ws:' + let protocol = this.$store.state.ssl ? 'wss:' : 'ws:' if (this.supported() && $.user.allowCommands) { let conn = new window.WebSocket(`${protocol}//${url}?command=true`) diff --git a/_assets/src/main.js b/_assets/src/main.js index eb848086..91e337d2 100644 --- a/_assets/src/main.js +++ b/_assets/src/main.js @@ -2,6 +2,7 @@ // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' +import store from './store/store' // simport page from './page.js' Vue.config.productionTip = false @@ -46,6 +47,7 @@ window.addEventListener('popstate', (event) => { /* eslint-disable no-new */ new Vue({ el: '#app', + store, template: '', components: { App } }) diff --git a/_assets/src/mixins/buttons.js b/_assets/src/mixins/buttons.js new file mode 100644 index 00000000..57a4c57c --- /dev/null +++ b/_assets/src/mixins/buttons.js @@ -0,0 +1,50 @@ +export default { + data: function () { + return { + buttonState: '' + } + }, + methods: { + setLoading: function () { + let i = this.$el.querySelector('i') + i.style.opacity = 0 + + this.buttonState = i.innerHTML + + setTimeout(() => { + i.classList.add('spin') + i.innerHTML = 'autorenew' + i.style.opacity = 1 + }, 200) + }, + setDone: function (success = true) { + let i = this.$el.querySelector('i') + i.style.opacity = 0 + + let thirdStep = () => { + i.innerHTML = this.buttonState + i.style.opacity = null + } + + 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) + } + } +} +/* // third step ? +if (selectedItems.length === 0 && document.getElementById('listing')) { + document.sendCostumEvent('changed-selected') +} */ diff --git a/_assets/src/store/store.js b/_assets/src/store/store.js new file mode 100644 index 00000000..03903c02 --- /dev/null +++ b/_assets/src/store/store.js @@ -0,0 +1,59 @@ +import Vue from 'vue' +import Vuex from 'vuex' + +Vue.use(Vuex) + +const state = { + ssl: (window.location.protocol === 'https:'), + selected: [], + multiple: false, + showInfo: false, + showHelp: false, + showDelete: false, + showRename: false, + showMove: false, + showNewFile: false, + showNewDir: false +} + +const getters = { + showOverlay: state => { + return state.showInfo || + state.showHelp || + state.showDelete || + state.showRename || + state.showMove || + state.showNewFile || + state.showNewDir + } +} + +const mutations = { + showInfo: (state, value) => (state.showInfo = value), + showHelp: (state, value) => (state.showHelp = value), + showDelete: (state, value) => (state.showDelete = value), + showRename: (state, value) => (state.showRename = value), + showMove: (state, value) => (state.showMove = value), + showNewFile: (state, value) => (state.showNewFile = value), + showNewDir: (state, value) => (state.showNewDir = value), + resetPrompts: (state) => { + state.showHelp = false + state.showInfo = false + state.showDelete = false + state.showRename = false + state.showMove = false + state.showNewFile = false + state.showNewDir = false + }, + multiple: (state, value) => (state.multiple = value), + resetSelected: (state) => { + state.selected.length = 0 + } +} + +export default new Vuex.Store({ + strict: process.env.NODE_ENV !== 'production', + state, + getters, + mutations +}) diff --git a/_assets/src/utils/array.js b/_assets/src/utils/array.js new file mode 100644 index 00000000..b06b5f7a --- /dev/null +++ b/_assets/src/utils/array.js @@ -0,0 +1,24 @@ +// Removes an element, if exists, from an array +function removeElement (array, element) { + var i = array.indexOf(element) + if (i !== -1) { + array.splice(i, 1) + } + + return array +} + +// Replaces an element inside an array by another +function replaceElement (array, oldElement, newElement) { + var i = array.indexOf(oldElement) + if (i !== -1) { + array[i] = newElement + } + + return array +} + +export default { + remove: removeElement, + replace: replaceElement +} diff --git a/_assets/src/utils/css.js b/_assets/src/utils/css.js new file mode 100644 index 00000000..15ab99fe --- /dev/null +++ b/_assets/src/utils/css.js @@ -0,0 +1,28 @@ +export default function getRule (rules) { + for (let i = 0; i < rules.length; i++) { + rules[i] = rules[i].toLowerCase() + } + + let result = null + let find = Array.prototype.find + + find.call(document.styleSheets, styleSheet => { + result = find.call(styleSheet.cssRules, cssRule => { + let found = false + + if (cssRule instanceof window.CSSStyleRule) { + for (let i = 0; i < rules.length; i++) { + if (cssRule.selectorText.toLowerCase() === rules[i]) { + found = true + } + } + } + + return found + }) + + return result != null + }) + + return result +} diff --git a/_assets/src/utils/page.js b/_assets/src/utils/page.js new file mode 100644 index 00000000..344995a9 --- /dev/null +++ b/_assets/src/utils/page.js @@ -0,0 +1,50 @@ +var $ = window.info + +function open (url, history) { + // Reset info + $.selected = [] + $.multiple = false + $.req.kind = '' + + let request = new window.XMLHttpRequest() + request.open('GET', url, true) + request.setRequestHeader('Accept', 'application/json') + + request.onload = () => { + if (request.status === 200) { + $.req = JSON.parse(request.responseText) + + if (history) { + window.history.pushState({ + name: $.req.data.name, + url: url + }, $.req.data.name, url) + + document.title = $.req.data.name + } + } else { + console.log(request.responseText) + } + } + + request.onerror = (error) => { console.log(error) } + request.send() +} + +function removeLastDir (url) { + var arr = url.split('/') + if (arr.pop() === '') { + arr.pop() + } + return (arr.join('/')) +} + +export default { + reload: () => { + open(window.location.pathname, false) + }, + open: (url) => { + open(url, true) + }, + removeLastDir: removeLastDir +} diff --git a/_assets/src/utils/webdav.js b/_assets/src/utils/webdav.js new file mode 100644 index 00000000..bd40f3a4 --- /dev/null +++ b/_assets/src/utils/webdav.js @@ -0,0 +1,109 @@ +var $ = window.info + +function convertURL (url) { + return window.location.origin + url.replace($.baseURL + '/', $.webdavURL + '/') +} + +function move (oldLink, newLink) { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + + oldLink = convertURL(oldLink) + newLink = newLink.replace($.baseURL + '/', $.webdavURL + '/') + newLink = window.location.origin + newLink.substring($.baseURL.length) + + request.open('MOVE', oldLink, true) + request.setRequestHeader('Destination', newLink) + request.onload = () => { + if (request.status === 201 || request.status === 204) { + resolve() + } else { + reject(request.statusText) + } + } + request.onerror = () => reject(request.statusText) + request.send() + }) +} + +function put (link, body, headers = {}) { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open('PUT', 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) + }) +} + +function propfind (link, body, headers = {}) { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open('PROPFIND', convertURL(link), true) + + for (let key in headers) { + request.setRequestHeader(key, headers[key]) + } + + request.onload = () => { + if (request.status < 300) { + resolve(request.responseText) + } else { + reject(request.statusText) + } + } + request.onerror = () => reject(request.statusText) + request.send(body) + }) +} + +function trash (link) { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open('DELETE', convertURL(link), true) + request.onload = () => { + if (request.status === 204) { + resolve() + } else { + reject(request.statusText) + } + } + request.onerror = () => reject(request.statusText) + request.send() + }) +} + +function create (link) { + return new Promise((resolve, reject) => { + let request = new window.XMLHttpRequest() + request.open((link.endsWith('/') ? 'MKCOL' : 'PUT'), convertURL(link), true) + request.onload = () => { + if (request.status === 201) { + resolve() + } else { + reject(request.statusText) + } + } + request.onerror = () => reject(request.statusText) + request.send() + }) +} + +export default { + create: create, + trash: trash, + propfind: propfind, + put: put, + move: move +}