Improvements :)
Former-commit-id: c1c1881302a241fdc7140e6aabeb9b49977bd7c6 [formerly 84bb454c2f34baffd9dfa91645b8aff149e52620] [formerly 29e258c7a16db1ca8a3fde7c5e4e3cffc47899a6 [formerly 84ddad027f
]]
Former-commit-id: 0018a51df5bc801b783a3ffe17d9f33c504ce094 [formerly 0072c425cd4754e38f30007ab9f5272ea4b40370]
Former-commit-id: d298f006e58ef9e4987def4bc354818062b30fcd
pull/726/head
parent
7f5a361bc1
commit
346412eb2a
|
@ -125,95 +125,6 @@ function logoutEvent (event) {
|
|||
}
|
||||
}
|
||||
|
||||
function getHash (event, hash) {
|
||||
event.preventDefault()
|
||||
|
||||
let request = new window.XMLHttpRequest()
|
||||
let link
|
||||
|
||||
if (selectedItems.length) {
|
||||
link = document.getElementById(selectedItems[0]).dataset.url
|
||||
} else {
|
||||
link = window.location.pathname
|
||||
}
|
||||
|
||||
request.open('GET', `${link}?checksum=${hash}`, true)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status >= 300) {
|
||||
console.log(request.statusText)
|
||||
return
|
||||
}
|
||||
event.target.parentElement.innerHTML = request.responseText
|
||||
}
|
||||
request.onerror = (e) => console.log(e)
|
||||
request.send()
|
||||
}
|
||||
|
||||
function infoEvent (event) {
|
||||
event.preventDefault()
|
||||
if (event.currentTarget.classList.contains('disabled')) {
|
||||
return
|
||||
}
|
||||
|
||||
let dir = false
|
||||
let link
|
||||
|
||||
if (selectedItems.length) {
|
||||
link = document.getElementById(selectedItems[0]).dataset.url
|
||||
dir = document.getElementById(selectedItems[0]).dataset.dir
|
||||
} else {
|
||||
if (document.getElementById('listing') !== null) {
|
||||
dir = true
|
||||
}
|
||||
|
||||
link = window.location.pathname
|
||||
}
|
||||
|
||||
buttons.setLoading('info', false)
|
||||
|
||||
webdav.propfind(link)
|
||||
.then((text) => {
|
||||
let parser = new window.DOMParser()
|
||||
let xml = parser.parseFromString(text, 'text/xml')
|
||||
let clone = document.importNode(templates.info.content, true)
|
||||
|
||||
let value = xml.getElementsByTagName('displayname')
|
||||
if (value.length > 0) {
|
||||
clone.getElementById('display_name').innerHTML = value[0].innerHTML
|
||||
} else {
|
||||
clone.getElementById('display_name').innerHTML = xml.getElementsByTagName('D:displayname')[0].innerHTML
|
||||
}
|
||||
|
||||
value = xml.getElementsByTagName('getcontentlength')
|
||||
if (value.length > 0) {
|
||||
clone.getElementById('content_length').innerHTML = value[0].innerHTML
|
||||
} else {
|
||||
clone.getElementById('content_length').innerHTML = xml.getElementsByTagName('D:getcontentlength')[0].innerHTML
|
||||
}
|
||||
|
||||
value = xml.getElementsByTagName('getlastmodified')
|
||||
if (value.length > 0) {
|
||||
clone.getElementById('last_modified').innerHTML = value[0].innerHTML
|
||||
} else {
|
||||
clone.getElementById('last_modified').innerHTML = xml.getElementsByTagName('D:getlastmodified')[0].innerHTML
|
||||
}
|
||||
|
||||
if (dir === true || dir === 'true') {
|
||||
clone.querySelector('.file-only').style.display = 'none'
|
||||
}
|
||||
|
||||
document.querySelector('body').appendChild(clone)
|
||||
document.querySelector('.overlay').classList.add('active')
|
||||
document.querySelector('.prompt').classList.add('active')
|
||||
buttons.setDone('info', true)
|
||||
})
|
||||
.catch(e => {
|
||||
buttons.setDone('info', false)
|
||||
console.log(e)
|
||||
})
|
||||
}
|
||||
|
||||
function deleteOnSingleFile () {
|
||||
closePrompt()
|
||||
buttons.setLoading('delete')
|
||||
|
@ -279,36 +190,6 @@ function deleteEvent (event) {
|
|||
return false
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* *
|
||||
|
|
|
@ -159,29 +159,6 @@ listing.addDoubleTapEvent = function () {
|
|||
})
|
||||
}
|
||||
|
||||
// Keydown events
|
||||
window.addEventListener('keydown', (event) => {
|
||||
if (event.keyCode == 27) {
|
||||
listing.unselectAll()
|
||||
|
||||
if (document.querySelectorAll('.prompt').length) {
|
||||
closePrompt(event)
|
||||
}
|
||||
}
|
||||
|
||||
if (event.keyCode == 113) {
|
||||
listing.rename()
|
||||
}
|
||||
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
switch (String.fromCharCode(event.which).toLowerCase()) {
|
||||
case 's':
|
||||
event.preventDefault()
|
||||
window.location = '?download=true'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
listing.selectMoveFolder = function (event) {
|
||||
if (event.target.getAttribute('aria-selected') === 'true') {
|
||||
event.target.setAttribute('aria-selected', false)
|
||||
|
|
|
@ -11,26 +11,6 @@
|
|||
</form>
|
||||
</template>
|
||||
|
||||
<template id="info-template">
|
||||
<div class="prompt">
|
||||
<h3>File Information</h3>
|
||||
<p><strong>Display Name:</strong> <span id="display_name"></span></p>
|
||||
<p><strong>Size:</strong> <span id="content_length"></span> Bytes</p>
|
||||
<p><strong>Last Modified:</strong> <span id="last_modified"></span></p>
|
||||
|
||||
<section class="file-only">
|
||||
<p><strong>MD5:</strong> <code id="md5"><a href="#" onclick="getHash(event, 'md5')">show</a></code></p>
|
||||
<p><strong>SHA1:</strong> <code id="sha1"><a href="#" onclick="getHash(event, 'sha1')">show</a></code></p>
|
||||
<p><strong>SHA256:</strong> <code id="sha256"><a href="#" onclick="getHash(event, 'sha256')">show</a></code></p>
|
||||
<p><strong>SHA512:</strong> <code id="sha512"><a href="#" onclick="getHash(event, 'sha512')">show</a></code></p>
|
||||
</section>
|
||||
|
||||
<div>
|
||||
<button type="submit" onclick="closePrompt(event);" class="ok">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="message-template">
|
||||
<div class="prompt">
|
||||
<h3></h3>
|
||||
|
@ -60,28 +40,5 @@
|
|||
</form>
|
||||
</template>
|
||||
|
||||
<div class="help">
|
||||
<h3>Help</h3>
|
||||
|
||||
<ul>
|
||||
<li><strong>F1</strong> - this information</li>
|
||||
<li><strong>F2</strong> - rename file</li>
|
||||
<li><strong>DEL</strong> - delete selected items</li>
|
||||
<li><strong>ESC</strong> - clear selection and/or close the prompt</li>
|
||||
<li><strong>CTRL + S</strong> - save a file or download the directory where you are</li>
|
||||
<li><strong>CTRL + Click</strong> - select multiple files or directories</li>
|
||||
<li><strong>Double click</strong> - open a file or directory</li>
|
||||
<li><strong>Click</strong> - select file or directory</li>
|
||||
</ul>
|
||||
|
||||
<p>Not available yet</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Alt + Click</strong> - select a group of files</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<button type="submit" onclick="closeHelp(event);" class="ok">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -33,10 +33,19 @@
|
|||
<body>
|
||||
<script>
|
||||
var info = {
|
||||
user: JSON.parse('{{ Marshal .User }}'),
|
||||
page: JSON.parse('{{ Marshal . }}'),
|
||||
webdavURL: "{{ .WebDavURL }}",
|
||||
baseURL: "{{.BaseURL}}"
|
||||
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,
|
||||
listing: {
|
||||
selected: [],
|
||||
multiple: false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<div id="app">
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<header>
|
||||
<div id="first-bar">
|
||||
<div>
|
||||
<img src="./assets/logo.svg" alt="File Manager">
|
||||
<search></search>
|
||||
</div>
|
||||
<div id="second-bar">
|
||||
<div>
|
||||
<info-button></info-button>
|
||||
</div>
|
||||
<!-- <div id="click-overlay"></div> -->
|
||||
|
@ -21,12 +21,12 @@
|
|||
</div>
|
||||
</nav>
|
||||
<main>
|
||||
<listing v-if="page.kind == 'listing'"></listing>
|
||||
<listing v-if="req.kind == 'listing'"></listing>
|
||||
</main>
|
||||
|
||||
<preview v-if="page.kind == 'preview'"></preview>
|
||||
<preview v-if="req.kind == 'preview'"></preview>
|
||||
|
||||
<div class="overlay"></div>
|
||||
|
||||
<!-- TODO: show on listing and allowedit -->
|
||||
<div class="floating">
|
||||
<div tabindex="0" role="button" class="action" id="new">
|
||||
|
@ -42,15 +42,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
|
||||
<info-prompt v-show="showInfo" :class="{ active: showInfo }"></info-prompt>
|
||||
<help v-show="showHelp" :class="{ active: showHelp }"></help>
|
||||
|
||||
<div v-show="showOverlay()" class="overlay" :class="{ active: showOverlay() }"></div>
|
||||
|
||||
<footer>Served with <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Search from './components/Search'
|
||||
import Preview from './components/Preview'
|
||||
import Help from './components/Help'
|
||||
import Listing from './components/Listing'
|
||||
import InfoButton from './components/InfoButton'
|
||||
import InfoPrompt from './components/InfoPrompt'
|
||||
import css from './css.js'
|
||||
|
||||
function updateColumnSizes () {
|
||||
|
@ -60,9 +67,62 @@ function updateColumnSizes () {
|
|||
items.style.width = `calc(${100 / columns}% - 1em)`
|
||||
}
|
||||
|
||||
window.addEventListener('keydown', (event) => {
|
||||
// Esc!
|
||||
if (event.keyCode === 27) {
|
||||
window.info.showHelp = false
|
||||
window.info.showInfo = false
|
||||
window.info.showDelete = false
|
||||
window.info.showRename = false
|
||||
|
||||
// Unselect all files and folders.
|
||||
if (window.info.req.kind === 'listing') {
|
||||
let items = document.getElementsByClassName('item')
|
||||
Array.from(items).forEach(link => {
|
||||
link.setAttribute('aria-selected', false)
|
||||
})
|
||||
|
||||
window.info.listing.selected.length = 0
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Del!
|
||||
if (event.keyCode === 46) {
|
||||
window.info.showDelete = true
|
||||
}
|
||||
|
||||
// F1!
|
||||
if (event.keyCode === 112) {
|
||||
event.preventDefault()
|
||||
window.info.showHelp = true
|
||||
}
|
||||
|
||||
// F2!
|
||||
if (event.keyCode === 113) {
|
||||
window.info.showRename = true
|
||||
}
|
||||
|
||||
// CTRL + S
|
||||
if (event.ctrlKey || event.metaKey) {
|
||||
switch (String.fromCharCode(event.which).toLowerCase()) {
|
||||
case 's':
|
||||
event.preventDefault()
|
||||
|
||||
if (window.info.req.kind !== 'editor') {
|
||||
window.location = '?download=true'
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: save file on editor!
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: { Search, Preview, Listing, InfoButton },
|
||||
components: { Search, Preview, Listing, InfoButton, InfoPrompt, Help },
|
||||
mounted: function () {
|
||||
updateColumnSizes()
|
||||
window.addEventListener('resize', updateColumnSizes)
|
||||
|
@ -70,6 +130,11 @@ export default {
|
|||
},
|
||||
data: function () {
|
||||
return window.info
|
||||
},
|
||||
methods: {
|
||||
showOverlay: function () {
|
||||
return this.showInfo || this.showHelp
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<template>
|
||||
<div class="prompt help">
|
||||
<h3>Help</h3>
|
||||
|
||||
<ul>
|
||||
<li><strong>F1</strong> - this information</li>
|
||||
<li><strong>F2</strong> - rename file</li>
|
||||
<li><strong>DEL</strong> - delete selected items</li>
|
||||
<li><strong>ESC</strong> - clear selection and/or close the prompt</li>
|
||||
<li><strong>CTRL + S</strong> - save a file or download the directory where you are</li>
|
||||
<li><strong>CTRL + Click</strong> - select multiple files or directories</li>
|
||||
<li><strong>Double click</strong> - open a file or directory</li>
|
||||
<li><strong>Click</strong> - select file or directory</li>
|
||||
</ul>
|
||||
|
||||
<p>Not available yet</p>
|
||||
|
||||
<ul>
|
||||
<li><strong>Alt + Click</strong> - select a group of files</li>
|
||||
</ul>
|
||||
|
||||
<div>
|
||||
<button type="submit" @click="close" class="ok">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'help',
|
||||
methods: {
|
||||
close: function (event) {
|
||||
window.info.showHelp = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<button title="Info" aria-label="Info" class="action" id="info">
|
||||
<button @click="show" title="Info" aria-label="Info" class="action" id="info">
|
||||
<i class="material-icons">info</i>
|
||||
<span>Info</span>
|
||||
</button>
|
||||
|
@ -8,8 +8,10 @@
|
|||
<script>
|
||||
export default {
|
||||
name: 'info-button',
|
||||
data: function () {
|
||||
return window.info.page.data
|
||||
methods: {
|
||||
show: function (event) {
|
||||
window.info.showInfo = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
<template>
|
||||
<div class="prompt">
|
||||
<h3>File Information</h3>
|
||||
|
||||
<p v-show="listing.selected.length > 1">{{ listing.selected.length }} files selected.</p>
|
||||
|
||||
<p v-show="listing.selected.length < 2"><strong>Display Name:</strong> {{ name() }}</p>
|
||||
<p><strong>Size:</strong> <span id="content_length"></span>{{ humanSize() }}</p>
|
||||
<p v-show="listing.selected.length < 2"><strong>Last Modified:</strong> {{ humanTime() }}</p>
|
||||
|
||||
<section v-show="dir() && listing.selected.length === 0">
|
||||
<p><strong>Number of files:</strong> {{ req.data.numFiles }}</p>
|
||||
<p><strong>Number of directories:</strong> {{ req.data.numDirs }}</p>
|
||||
</section>
|
||||
|
||||
<section v-show="!dir()">
|
||||
<p><strong>MD5:</strong> <code><a @click="checksum($event, 'md5')">show</a></code></p>
|
||||
<p><strong>SHA1:</strong> <code><a @click="checksum($event, 'sha1')">show</a></code></p>
|
||||
<p><strong>SHA256:</strong> <code><a @click="checksum($event, 'sha256')">show</a></code></p>
|
||||
<p><strong>SHA512:</strong> <code><a @click="checksum($event, 'sha512')">show</a></code></p>
|
||||
</section>
|
||||
|
||||
<div>
|
||||
<button type="submit" @click="close" class="ok">OK</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'info-prompt',
|
||||
data: function () {
|
||||
return window.info
|
||||
},
|
||||
methods: {
|
||||
humanSize: function () {
|
||||
if (this.listing.selected.length === 0 || this.req.kind !== 'listing') {
|
||||
return filesize(this.req.data.size)
|
||||
}
|
||||
|
||||
var sum = 0
|
||||
|
||||
for (let i = 0; i < this.listing.selected.length; i++) {
|
||||
sum += this.req.data.items[this.listing.selected[i]].size
|
||||
}
|
||||
|
||||
return filesize(sum)
|
||||
},
|
||||
humanTime: function () {
|
||||
if (this.listing.selected.length === 0) {
|
||||
return moment(this.req.data.modified).fromNow()
|
||||
}
|
||||
|
||||
return moment(this.req.data.items[this.listing.selected[0]]).fromNow()
|
||||
},
|
||||
name: function () {
|
||||
if (this.listing.selected.length === 0) {
|
||||
return this.req.data.name
|
||||
}
|
||||
|
||||
return this.req.data.items[this.listing.selected[0]].name
|
||||
},
|
||||
dir: function () {
|
||||
if (this.listing.selected.length > 1) {
|
||||
// Don't show when multiple selected.
|
||||
return true
|
||||
}
|
||||
|
||||
if (this.listing.selected.length === 0) {
|
||||
return this.req.data.isDir
|
||||
}
|
||||
|
||||
return this.req.data.items[this.listing.selected[0]].isDir
|
||||
},
|
||||
checksum: function (event, hash) {
|
||||
event.preventDefault()
|
||||
|
||||
let request = new window.XMLHttpRequest()
|
||||
let link
|
||||
|
||||
if (this.listing.selected.length) {
|
||||
link = this.req.data.items[this.listing.selected[0]].url
|
||||
} else {
|
||||
link = window.location.pathname
|
||||
}
|
||||
|
||||
request.open('GET', `${link}?checksum=${hash}`, true)
|
||||
|
||||
request.onload = () => {
|
||||
if (request.status >= 300) {
|
||||
console.log(request.statusText)
|
||||
return
|
||||
}
|
||||
|
||||
event.target.innerHTML = request.responseText
|
||||
}
|
||||
|
||||
request.onerror = (e) => console.log(e)
|
||||
request.send()
|
||||
},
|
||||
close: function () {
|
||||
this.showInfo = false
|
||||
|
||||
let checksums = this.$el.querySelectorAll('a')
|
||||
for (let i = 0; i < checksums.length; i++) {
|
||||
checksums[i].innerHTML = 'show'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="listing"
|
||||
:class="data.display"
|
||||
:class="req.data.display"
|
||||
@drop="drop"
|
||||
@dragenter="dragEnter"
|
||||
@dragend="dragEnd">
|
||||
|
@ -8,13 +8,13 @@
|
|||
<div class="item header">
|
||||
<div></div>
|
||||
<div>
|
||||
<p v-bind:class="{ active: data.sort === 'name' }" class="name"><span>Name</span>
|
||||
<a v-if="data.sort === 'name' && data.order != 'asc'" href="?sort=name&order=asc"><i class="material-icons">arrow_upward</i></a>
|
||||
<p v-bind:class="{ active: req.data.sort === 'name' }" class="name"><span>Name</span>
|
||||
<a v-if="req.data.sort === 'name' && req.data.order != 'asc'" href="?sort=name&order=asc"><i class="material-icons">arrow_upward</i></a>
|
||||
<a v-else href="?sort=name&order=desc"><i class="material-icons">arrow_downward</i></a>
|
||||
</p>
|
||||
|
||||
<p v-bind:class="{ active: data.sort === 'size' }" class="size"><span>Size</span>
|
||||
<a v-if="data.sort === 'size' && data.order != 'asc'" href="?sort=size&order=asc"><i class="material-icons">arrow_upward</i></a>
|
||||
<p v-bind:class="{ active: req.data.sort === 'size' }" class="size"><span>Size</span>
|
||||
<a v-if="req.data.sort === 'size' && req.data.order != 'asc'" href="?sort=size&order=asc"><i class="material-icons">arrow_upward</i></a>
|
||||
<a v-else href="?sort=size&order=desc"><i class="material-icons">arrow_downward</i></a>
|
||||
</p>
|
||||
|
||||
|
@ -23,16 +23,15 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<h2 v-if="(data.numDirs + data.numFiles) == 0" class="message">It feels lonely here :'(</h2>
|
||||
<h2 v-if="(req.data.numDirs + req.data.numFiles) == 0" class="message">It feels lonely here :'(</h2>
|
||||
|
||||
<h2 v-if="data.numDirs > 0">Folders</h2>
|
||||
<div v-if="data.numDirs > 0">
|
||||
<h2 v-if="req.data.numDirs > 0">Folders</h2>
|
||||
<div v-if="req.data.numDirs > 0">
|
||||
<item
|
||||
v-for="(item, index) in data.items"
|
||||
v-for="(item, index) in req.data.items"
|
||||
v-if="item.isDir"
|
||||
:key="base64(item.name)"
|
||||
:id="base64(item.name)"
|
||||
v-bind:selected="selected"
|
||||
v-bind:index="index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.isDir"
|
||||
v-bind:url="item.url"
|
||||
|
@ -42,14 +41,13 @@
|
|||
</item>
|
||||
</div>
|
||||
|
||||
<h2 v-if="data.numFiles > 0">Files</h2>
|
||||
<div v-if="data.numFiles > 0">
|
||||
<h2 v-if="req.data.numFiles > 0">Files</h2>
|
||||
<div v-if="req.data.numFiles > 0">
|
||||
<item
|
||||
v-for="(item, index) in data.items"
|
||||
v-for="(item, index) in req.data.items"
|
||||
v-if="!item.isDir"
|
||||
:key="base64(item.name)"
|
||||
:id="base64(item.name)"
|
||||
v-bind:selected="selected"
|
||||
v-bind:index="index"
|
||||
v-bind:name="item.name"
|
||||
v-bind:isDir="item.isDir"
|
||||
v-bind:url="item.url"
|
||||
|
@ -73,11 +71,7 @@ import page from '../page.js'
|
|||
export default {
|
||||
name: 'preview',
|
||||
data: function () {
|
||||
return {
|
||||
data: window.info.page.data,
|
||||
selected: [],
|
||||
multiple: false
|
||||
}
|
||||
return window.info
|
||||
},
|
||||
components: { Item },
|
||||
mounted: function () {
|
||||
|
|
|
@ -33,7 +33,10 @@ import array from '../array.js'
|
|||
|
||||
export default {
|
||||
name: 'item',
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'selected'],
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified', 'index'],
|
||||
data: function () {
|
||||
return window.info.listing
|
||||
},
|
||||
methods: {
|
||||
icon: function () {
|
||||
if (this.isDir) return 'folder'
|
||||
|
@ -97,22 +100,18 @@ export default {
|
|||
})
|
||||
|
||||
this.selected.length = 0
|
||||
|
||||
// listing.handleSelectionChange()
|
||||
return false
|
||||
},
|
||||
click: function (event) {
|
||||
let el = event.currentTarget
|
||||
|
||||
if (this.selected.length !== 0) event.preventDefault()
|
||||
if (this.selected.indexOf(el.id) === -1) {
|
||||
if (this.selected.indexOf(this.index) === -1) {
|
||||
if (!event.ctrlKey && !this.multiple) this.unselectAll()
|
||||
|
||||
el.setAttribute('aria-selected', true)
|
||||
this.selected.push(el.id)
|
||||
this.$el.setAttribute('aria-selected', true)
|
||||
this.selected.push(this.index)
|
||||
} else {
|
||||
el.setAttribute('aria-selected', false)
|
||||
this.selected = array.remove(this.selected, el.id)
|
||||
this.$el.setAttribute('aria-selected', false)
|
||||
this.selected = array.remove(this.selected, this.index)
|
||||
}
|
||||
|
||||
// this.handleSelectionChange()
|
||||
|
|
|
@ -23,17 +23,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import page from '../page'
|
||||
|
||||
export default {
|
||||
name: 'preview',
|
||||
data: function () {
|
||||
return window.info.page.data
|
||||
return window.info.req.data
|
||||
},
|
||||
methods: {
|
||||
raw: function () {
|
||||
return this.url + '?raw=true'
|
||||
},
|
||||
back: function (event) {
|
||||
window.history.back()
|
||||
let url = page.removeLastDir(window.location.pathname)
|
||||
page.open(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,17 +18,7 @@
|
|||
<script>
|
||||
import page from '../page'
|
||||
|
||||
// Remove the last directory of an url
|
||||
var removeLastDirectoryPartOf = function (url) {
|
||||
var arr = url.split('/')
|
||||
if (arr.pop() === '') {
|
||||
arr.pop()
|
||||
}
|
||||
return (arr.join('/'))
|
||||
}
|
||||
|
||||
var user = window.info.user
|
||||
var ssl = window.ssl
|
||||
var $ = window.info
|
||||
|
||||
export default {
|
||||
name: 'search',
|
||||
|
@ -49,8 +39,8 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
reset: function () {
|
||||
if (user.allowCommands && user.commands.length > 0) {
|
||||
this.box.innerHTML = `Search or use one of your supported commands: ${user.commands.join(', ')}.`
|
||||
if ($.user.allowCommands && $.user.commands.length > 0) {
|
||||
this.box.innerHTML = `Search or use one of your supported commands: ${$.user.commands.join(', ')}.`
|
||||
} else {
|
||||
this.box.innerHTML = 'Type and press enter to search.'
|
||||
}
|
||||
|
@ -59,8 +49,8 @@ export default {
|
|||
let value = this.input.value
|
||||
let pieces = value.split(' ')
|
||||
|
||||
for (let i = 0; i < user.commands.length; i++) {
|
||||
if (pieces[0] === user.commands[0]) {
|
||||
for (let i = 0; i < $.user.commands.length; i++) {
|
||||
if (pieces[0] === $.user.commands[0]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +69,7 @@ export default {
|
|||
return
|
||||
}
|
||||
|
||||
if (!this.supported() || !user.allowCommands) {
|
||||
if (!this.supported() || !$.user.allowCommands) {
|
||||
this.box.innerHTML = 'Press enter to search.'
|
||||
} else {
|
||||
this.box.innerHTML = 'Press enter to execute.'
|
||||
|
@ -92,12 +82,12 @@ export default {
|
|||
let url = window.location.host + window.location.pathname
|
||||
|
||||
if (document.getElementById('editor')) {
|
||||
url = removeLastDirectoryPartOf(url)
|
||||
url = page.removeLastDir(url)
|
||||
}
|
||||
|
||||
let protocol = ssl ? 'wss:' : 'ws:'
|
||||
let protocol = $.ssl ? 'wss:' : 'ws:'
|
||||
|
||||
if (this.supported() && user.allowCommands) {
|
||||
if (this.supported() && $.user.allowCommands) {
|
||||
let conn = new window.WebSocket(`${protocol}//${url}?command=true`)
|
||||
|
||||
conn.onopen = () => {
|
||||
|
|
|
@ -16,6 +16,12 @@ header a:hover {
|
|||
color: inherit;
|
||||
}
|
||||
|
||||
header img {
|
||||
height: 2.5em;
|
||||
margin-right: 1em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
header>div {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
@ -32,27 +38,18 @@ header>div div {
|
|||
position: relative;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* TOP BAR *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
#first-bar {
|
||||
header > div:first-child {
|
||||
height: 4em;
|
||||
}
|
||||
|
||||
#first-bar img {
|
||||
height: 2.5em;
|
||||
margin-right: 1em;
|
||||
vertical-align: middle;
|
||||
header > div:last-child {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* BOTTOM BAR *
|
||||
* MORE?? *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
#second-bar {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#more {
|
||||
display: none;
|
||||
|
|
|
@ -0,0 +1,139 @@
|
|||
.prompt {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 2em;
|
||||
max-width: 25em;
|
||||
width: 90%;
|
||||
max-height: 95%;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.overlay.active,
|
||||
.prompt.active,
|
||||
.help.active {
|
||||
animation: .2s show forwards;
|
||||
}
|
||||
|
||||
.prompt h3 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.prompt p {
|
||||
font-size: .9em;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
margin: .5em 0 1em;
|
||||
}
|
||||
|
||||
.prompt input {
|
||||
width: 100%;
|
||||
border: 1px solid #dadada;
|
||||
line-height: 1;
|
||||
padding: .3em;
|
||||
}
|
||||
|
||||
.prompt code {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.prompt div {
|
||||
margin-top: 1em;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.prompt .cancel {
|
||||
background-color: #ECEFF1;
|
||||
color: #37474F;
|
||||
}
|
||||
|
||||
.prompt .cancel:hover {
|
||||
background-color: #e9eaeb;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* PROMPT - MOVE *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.prompt .file-list {
|
||||
flex-direction: initial;
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prompt .file-list ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li {
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li[aria-selected=true] {
|
||||
background: #2196f3 !important;
|
||||
color: #fff !important;
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li:hover {
|
||||
background-color: #e9eaeb;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li:before {
|
||||
content: "folder";
|
||||
color: #6f6f6f;
|
||||
vertical-align: middle;
|
||||
padding: 0 .25em;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li[aria-selected=true]:before {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.help {
|
||||
max-width: 24em;
|
||||
}
|
||||
|
||||
.help ul {
|
||||
padding: 0;
|
||||
margin: 1em 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
@keyframes show {
|
||||
0% {
|
||||
display: none;
|
||||
opacity: 0;
|
||||
}
|
||||
1% {
|
||||
display: block;
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
@import "./fonts.css";
|
||||
@import "./normalize.css";
|
||||
@import "./header.css";
|
||||
@import "./prompts.css";
|
||||
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
|
@ -726,8 +727,7 @@ main {
|
|||
#previewer .preview pre,
|
||||
#previewer .preview video,
|
||||
#previewer .preview img {
|
||||
/* max-height: 80vh; */
|
||||
height: 100%;
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
@ -778,162 +778,8 @@ main {
|
|||
* PROMPT *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.overlay,
|
||||
.prompt,
|
||||
.help {
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
transition: .1s ease opacity, z-index;
|
||||
}
|
||||
|
||||
.overlay.active,
|
||||
.prompt.active,
|
||||
.help.active {
|
||||
z-index: 9999999;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.overlay.active {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prompt,
|
||||
.help {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 99999999;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.075);
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
padding: 2em;
|
||||
max-width: 25em;
|
||||
width: 90%;
|
||||
max-height: 95%;
|
||||
}
|
||||
|
||||
.prompt h3,
|
||||
.help h3 {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
.prompt p,
|
||||
.help p {
|
||||
font-size: .9em;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
margin: .5em 0 1em;
|
||||
}
|
||||
|
||||
.prompt input {
|
||||
width: 100%;
|
||||
border: 1px solid #dadada;
|
||||
line-height: 1;
|
||||
padding: .3em;
|
||||
}
|
||||
|
||||
.prompt code {
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.prompt div,
|
||||
.help div {
|
||||
margin-top: 1em;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.prompt .cancel {
|
||||
background-color: #ECEFF1;
|
||||
color: #37474F;
|
||||
}
|
||||
|
||||
.prompt .cancel:hover {
|
||||
background-color: #e9eaeb;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* PROMPT - MOVE *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.prompt .file-list {
|
||||
flex-direction: initial;
|
||||
max-height: 50vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.prompt .file-list ul {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li {
|
||||
width: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li[aria-selected=true] {
|
||||
background: #2196f3 !important;
|
||||
color: #fff !important;
|
||||
transition: .1s ease all;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li:hover {
|
||||
background-color: #e9eaeb;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li:before {
|
||||
content: "folder";
|
||||
color: #6f6f6f;
|
||||
vertical-align: middle;
|
||||
padding: 0 .25em;
|
||||
line-height: 2em;
|
||||
}
|
||||
|
||||
.prompt .file-list ul li[aria-selected=true]:before {
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
* HELP *
|
||||
* * * * * * * * * * * * * * * */
|
||||
|
||||
.help {
|
||||
max-width: 24em;
|
||||
visibility: hidden;
|
||||
top: -100%;
|
||||
left: -100%;
|
||||
}
|
||||
|
||||
.help.active {
|
||||
visibility: visible;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
}
|
||||
|
||||
.help ul {
|
||||
padding: 0;
|
||||
margin: 1em 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * *
|
||||
|
|
|
@ -6,18 +6,19 @@ import App from './App'
|
|||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
window.info = (window.info || window.alert('Something is wrong, please refresh!'))
|
||||
window.ssl = (window.location.protocol === 'https:')
|
||||
var $ = (window.info || window.alert('Something is wrong, please refresh!'))
|
||||
|
||||
// TODO: keep this here?
|
||||
document.title = window.info.page.name
|
||||
document.title = $.req.name
|
||||
|
||||
// TODO: keep this here?
|
||||
window.addEventListener('popstate', (event) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
window.info.page.kind = ''
|
||||
$.req.kind = ''
|
||||
$.listing.selected.length = 0
|
||||
$.listing.selected.multiple = false
|
||||
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', event.state.url, true)
|
||||
|
@ -25,7 +26,7 @@ window.addEventListener('popstate', (event) => {
|
|||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
window.info.page = JSON.parse(request.responseText)
|
||||
$.req = JSON.parse(request.responseText)
|
||||
document.title = event.state.name
|
||||
} else {
|
||||
console.log(request.responseText)
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
var $ = window.info
|
||||
|
||||
function open (url, history) {
|
||||
window.info.page.kind = ''
|
||||
// Reset info
|
||||
$.listing.selected.length = 0
|
||||
$.listing.selected.multiple = false
|
||||
$.req.kind = ''
|
||||
|
||||
let request = new window.XMLHttpRequest()
|
||||
request.open('GET', url, true)
|
||||
|
@ -7,15 +12,15 @@ function open (url, history) {
|
|||
|
||||
request.onload = () => {
|
||||
if (request.status === 200) {
|
||||
window.info.page = JSON.parse(request.responseText)
|
||||
$.req = JSON.parse(request.responseText)
|
||||
|
||||
if (history) {
|
||||
window.history.pushState({
|
||||
name: window.info.page.name,
|
||||
name: $.req.name,
|
||||
url: url
|
||||
}, window.info.page.name, url)
|
||||
}, $.req.name, url)
|
||||
|
||||
document.title = window.info.page.name
|
||||
document.title = $.req.name
|
||||
}
|
||||
} else {
|
||||
console.log(request.responseText)
|
||||
|
@ -26,11 +31,20 @@ function open (url, history) {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
var info = window.info
|
||||
var $ = window.info
|
||||
|
||||
function convertURL (url) {
|
||||
return window.location.origin + url.replace(info.baseURL + '/', info.webdavURL + '/')
|
||||
return window.location.origin + url.replace($.baseURL + '/', $.webdavURL + '/')
|
||||
}
|
||||
|
||||
function move (oldLink, newLink) {
|
||||
|
@ -9,8 +9,8 @@ function move (oldLink, newLink) {
|
|||
let request = new window.XMLHttpRequest()
|
||||
|
||||
oldLink = convertURL(oldLink)
|
||||
newLink = newLink.replace(info.baseURL + '/', info.webdavURL + '/')
|
||||
newLink = window.location.origin + newLink.substring(info.baseURL.length)
|
||||
newLink = newLink.replace($.baseURL + '/', $.webdavURL + '/')
|
||||
newLink = window.location.origin + newLink.substring($.baseURL.length)
|
||||
|
||||
request.open('MOVE', oldLink, true)
|
||||
request.setRequestHeader('Destination', newLink)
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
// editor contains the information to fill the editor template.
|
||||
type editor struct {
|
||||
*fileInfo
|
||||
Class string `json:"class"`
|
||||
Mode string `json:"mode"`
|
||||
Visual bool `json:"visual"`
|
||||
|
@ -28,7 +29,7 @@ func getEditor(r *http.Request, i *fileInfo) (*editor, error) {
|
|||
var err error
|
||||
|
||||
// Create a new editor variable and set the mode
|
||||
e := &editor{}
|
||||
e := &editor{fileInfo: i}
|
||||
e.Mode = editorMode(i.Name)
|
||||
e.Class = editorClass(e.Mode)
|
||||
|
||||
|
|
11
file.go
11
file.go
|
@ -15,7 +15,6 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -56,10 +55,7 @@ type fileInfo struct {
|
|||
|
||||
// A listing is the context used to fill out a template.
|
||||
type listing struct {
|
||||
// The name of the directory (the last element of the path).
|
||||
Name string `json:"-"`
|
||||
// The full path of the request relatively to a File System.
|
||||
Path string `json:"-"`
|
||||
*fileInfo
|
||||
// The items (files and folders) in the path.
|
||||
Items []fileInfo `json:"items"`
|
||||
// The number of directories in the listing.
|
||||
|
@ -103,7 +99,7 @@ func getInfo(url *url.URL, c *FileManager, u *User) (*fileInfo, error) {
|
|||
}
|
||||
|
||||
// getListing gets the information about a specific directory and its files.
|
||||
func getListing(u *User, filePath string, baseURL string) (*listing, error) {
|
||||
func getListing(u *User, filePath string, baseURL string, i *fileInfo) (*listing, error) {
|
||||
// Gets the directory information using the Virtual File System of
|
||||
// the user configuration.
|
||||
file, err := u.fileSystem.OpenFile(context.TODO(), filePath, os.O_RDONLY, 0)
|
||||
|
@ -155,8 +151,7 @@ func getListing(u *User, filePath string, baseURL string) (*listing, error) {
|
|||
}
|
||||
|
||||
return &listing{
|
||||
Name: path.Base(filePath),
|
||||
Path: filePath,
|
||||
fileInfo: i,
|
||||
Items: fileinfos,
|
||||
NumDirs: dirCount,
|
||||
NumFiles: fileCount,
|
||||
|
|
2
serve.go
2
serve.go
|
@ -64,7 +64,7 @@ func serveListing(c *requestContext, w http.ResponseWriter, r *http.Request) (in
|
|||
|
||||
c.pg.Kind = "listing"
|
||||
|
||||
listing, err = getListing(c.us, c.fi.VirtualPath, c.fm.RootURL()+r.URL.Path)
|
||||
listing, err = getListing(c.us, c.fi.VirtualPath, c.fm.RootURL()+r.URL.Path, c.fi)
|
||||
if err != nil {
|
||||
return errorToHTTP(err, true), err
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue