More vue updates

Former-commit-id: ec29aa7447e3ad1c90dea24e94072d51f212333e [formerly d370cb523dde06fb6d5aab614bf6ec1d94097363] [formerly bbabc4da0a6128b3bd8ecfa226156c9c2be7297e [formerly 2789ff2161]]
Former-commit-id: fb3ae368534aa2bd7cc08283463ac9d8e0d1d825 [formerly efbb60cc4dd2856bbc004fcbdc0567540c72f5b3]
Former-commit-id: d0c3699029c88a3ed2a8f5ab72c70d0f5289292d
pull/726/head
Henrique Dias 2017-06-30 10:25:35 +01:00
parent effa906f7c
commit 4f964faf6e
21 changed files with 421 additions and 136 deletions

View File

@ -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}}"
}
</script>
<div id="app"></div>

View File

@ -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",

View File

@ -22,11 +22,11 @@
<span>My Files</span>
</a>
<div v-if="user.allowNew">
<button @click="showNewDir = true" aria-label="New directory" title="New directory" class="action">
<button @click="$store.commit('showNewDir', true)" aria-label="New directory" title="New directory" class="action">
<i class="material-icons">create_new_folder</i>
<span>New folder</span>
</button>
<button @click="showNewFile = true" aria-label="New file" title="New file" class="action">
<button @click="$store.commit('showNewFile', true)" aria-label="New file" title="New file" class="action">
<i class="material-icons">note_add</i>
<span>New file</span>
</button>
@ -43,15 +43,14 @@
<preview v-if="req.kind === 'preview'"></preview>
</main>
<new-file-prompt v-if="showNewFile" :class="{ active: showNewFile }"></new-file-prompt>
<new-dir-prompt v-if="showNewDir" :class="{ active: showNewDir }"></new-dir-prompt>
<rename-prompt v-if="showRename" :class="{ active: showRename }"></rename-prompt>
<delete-prompt v-if="showDelete" :class="{ active: showDelete }"></delete-prompt>
<info-prompt v-if="showInfo" :class="{ active: showInfo }"></info-prompt>
<move-prompt v-if="showMove" :class="{ active: showMove }"></move-prompt>
<help v-show="showHelp" :class="{ active: showHelp }"></help>
<div v-show="showOverlay()" @click="resetPrompts" class="overlay" :class="{ active: showOverlay() }"></div>
<new-file-prompt v-if="$store.state.showNewFile" :class="{ active: $store.state.showNewFile }"></new-file-prompt>
<new-dir-prompt v-if="$store.state.showNewDir" :class="{ active: $store.state.showNewDir }"></new-dir-prompt>
<rename-prompt v-if="$store.state.showRename" :class="{ active: $store.state.showRename }"></rename-prompt>
<delete-prompt v-if="$store.state.showDelete" :class="{ active: $store.state.showDelete }"></delete-prompt>
<info-prompt v-if="$store.state.showInfo" :class="{ active: $store.state.showInfo }"></info-prompt>
<move-prompt v-if="$store.state.showMove" :class="{ active: $store.state.showMove }"></move-prompt>
<help v-show="$store.state.showHelp" :class="{ active: $store.state.showHelp }"></help>
<div v-show="$store.getters.showOverlay" @click="resetPrompts" class="overlay" :class="{ active: $store.getters.showOverlay }"></div>
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
</div>
@ -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')
}
}
}
</script>

View File

@ -10,7 +10,7 @@ export default {
name: 'delete-button',
methods: {
show: function (event) {
window.info.showDelete = true
this.$store.commit('showDelete', true)
}
}
}

View File

@ -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') {

View File

@ -10,7 +10,7 @@ export default {
name: 'info-button',
methods: {
show: function (event) {
window.info.showInfo = true
this.$store.commit('showInfo', true)
}
}
}

View File

@ -102,7 +102,7 @@ export default {
request.send()
},
close: function () {
this.showInfo = false
this.$store.commit('showInfo', false)
}
}
}

View File

@ -10,7 +10,7 @@ export default {
name: 'move-button',
methods: {
show: function (event) {
window.info.showMove = true
this.$store.commit('showMove', true)
}
}
}

View File

@ -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(() => {

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -10,7 +10,7 @@ export default {
name: 'rename-button',
methods: {
show: function (event) {
window.info.showRename = true
this.$store.commit('showRename', true)
}
}
}

View File

@ -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
}
}

View File

@ -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`)

View File

@ -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: '<App/>',
components: { App }
})

View File

@ -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')
} */

View File

@ -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
})

View File

@ -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
}

28
_assets/src/utils/css.js Normal file
View File

@ -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
}

50
_assets/src/utils/page.js Normal file
View File

@ -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
}

109
_assets/src/utils/webdav.js Normal file
View File

@ -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
}