Improvements :)

Former-commit-id: c1c1881302a241fdc7140e6aabeb9b49977bd7c6 [formerly 84bb454c2f34baffd9dfa91645b8aff149e52620] [formerly 29e258c7a16db1ca8a3fde7c5e4e3cffc47899a6 [formerly 84ddad027f]]
Former-commit-id: 0018a51df5bc801b783a3ffe17d9f33c504ce094 [formerly 0072c425cd4754e38f30007ab9f5272ea4b40370]
Former-commit-id: d298f006e58ef9e4987def4bc354818062b30fcd
pull/726/head
Henrique Dias 2017-06-28 22:20:28 +01:00
parent 7f5a361bc1
commit 346412eb2a
21 changed files with 467 additions and 445 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

139
_assets/src/css/prompts.css Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
View File

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

View File

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