updates...
Former-commit-id: 2d19655aac80ec7033829b0b3d708bf86986252c [formerly b8bb006bd1a5492960c1d4793e15e80775ddc5fd] [formerly 4641277c14953bdb2ddab89ba866cf63f3dac0f9 [formerly 95b91a64a3c648e1414c7feb4b597bf0da195664]] Former-commit-id: 5a6e07758bbe67fd202effa5b2975c65227eaebd [formerly 9a161548d270750b8da949189ad6f3b641779bfc] Former-commit-id: 84e0bbcbbead53c5e91987574d5c854a9792515f
This commit is contained in:
77
_assets/src/components/Listing.vue
Normal file
77
_assets/src/components/Listing.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<template>
|
||||
<div id="listing" :class="Data.Display">
|
||||
<div>
|
||||
<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>
|
||||
<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>
|
||||
<a v-else href="?sort=size&order=desc"><i class="material-icons">arrow_downward</i></a>
|
||||
</p>
|
||||
|
||||
<p class="modified">Last modified</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2 v-if="(Data.NumDirs + Data.NumFiles) == 0" class="message">It feels lonely here :'(</h2>
|
||||
|
||||
<h2 v-if="Data.NumDirs !== 0">Folders</h2>
|
||||
<div v-if="Data.NumDirs !== 0">
|
||||
<item
|
||||
v-for="(item, index) in Data.Items"
|
||||
v-if="item.IsDir"
|
||||
:key="base64(item.Name)"
|
||||
:id="base64(item.Name)"
|
||||
v-bind:name="item.Name"
|
||||
v-bind:isDir="item.IsDir"
|
||||
v-bind:url="item.URL"
|
||||
v-bind:modified="item.ModTime"
|
||||
v-bind:type="item.Type"
|
||||
v-bind:size="item.Size">
|
||||
</item>
|
||||
</div>
|
||||
|
||||
<h2 v-if="Data.NumItems !== 0">Files</h2>
|
||||
<div v-if="Data.NumItems !== 0">
|
||||
<item
|
||||
v-for="(item, index) in Data.Items"
|
||||
v-if="!item.IsDir"
|
||||
:key="base64(item.Name)"
|
||||
:id="base64(item.Name)"
|
||||
v-bind:name="item.Name"
|
||||
v-bind:isDir="item.IsDir"
|
||||
v-bind:modified="item.ModTime"
|
||||
v-bind:url="item.URL"
|
||||
v-bind:type="item.Type"
|
||||
v-bind:size="item.Size">
|
||||
</item>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<input style="display:none" type="file" id="upload-input" onchange="listing.handleFiles(this.files, '')" value="Upload" multiple>
|
||||
-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Item from './ListingItem'
|
||||
|
||||
export default {
|
||||
name: 'preview',
|
||||
components: { Item },
|
||||
data: function () {
|
||||
return window.page
|
||||
},
|
||||
methods: {
|
||||
base64: function (name) {
|
||||
return window.btoa(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
50
_assets/src/components/ListingItem.vue
Normal file
50
_assets/src/components/ListingItem.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div class="item"
|
||||
draggable="true"
|
||||
:id="base64()"
|
||||
:data-dir="isDir"
|
||||
:data-url="url" >
|
||||
<div>
|
||||
<i class="material-icons">{{ icon() }}</i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p class="name">{{ name }}</p>
|
||||
|
||||
<p v-if="isDir" class="size" data-order="-1">—</p>
|
||||
<p v-else class="size" :data-order="humanSize()">{{ humanSize() }}</p>
|
||||
|
||||
<p class="modified">
|
||||
<time :datetime="modified">{{ humanTime() }}</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import filesize from 'filesize'
|
||||
import moment from 'moment'
|
||||
|
||||
export default {
|
||||
name: 'item',
|
||||
props: ['name', 'isDir', 'url', 'type', 'size', 'modified'],
|
||||
methods: {
|
||||
icon: function () {
|
||||
if (this.isDir) return 'folder'
|
||||
if (this.type === 'image') return 'insert_photo'
|
||||
if (this.type === 'audio') return 'volume_up'
|
||||
if (this.type === 'video') return 'movie'
|
||||
return 'insert_drive_file'
|
||||
},
|
||||
humanSize: function () {
|
||||
return filesize(this.size)
|
||||
},
|
||||
humanTime: function () {
|
||||
return moment(this.modified).fromNow()
|
||||
},
|
||||
base64: function () {
|
||||
return window.btoa(this.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
37
_assets/src/components/Preview.vue
Normal file
37
_assets/src/components/Preview.vue
Normal file
@@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div id="previewer">
|
||||
<div class="bar">
|
||||
<button class="action" aria-label="Close Preview" id="close">
|
||||
<i class="material-icons">close</i>
|
||||
</button>
|
||||
<!-- TODO: add more buttons -->
|
||||
</div>
|
||||
|
||||
<div class="preview">
|
||||
<img v-if="Data.Type == 'image'" :src="raw()">
|
||||
<audio v-else-if="Data.Type == 'audio'" :src="raw()" controls></audio>
|
||||
<video v-else-if="Data.Type == 'video'" :src="raw()" controls>
|
||||
Sorry, your browser doesn't support embedded videos,
|
||||
but don't worry, you can <a href="?download=true">download it</a>
|
||||
and watch it with your favorite video player!
|
||||
</video>
|
||||
<object v-else-if="Data.Extension == '.pdf'" class="pdf" :data="raw()"></object>
|
||||
<a v-else-if="Data.Type == 'blob'" href="?download=true"><h2 class="message">Download <i class="material-icons">file_download</i></h2></a>
|
||||
<pre v-else >{{ Data.Content }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'preview',
|
||||
data: function () {
|
||||
return window.page
|
||||
},
|
||||
methods: {
|
||||
raw: function () {
|
||||
return this.Data.URL + '?raw=true'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
140
_assets/src/components/Search.vue
Normal file
140
_assets/src/components/Search.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div id="search" v-on:mouseleave="hover = false" v-on:click="click" v-bind:class="{ active: focus || hover }">
|
||||
<i class="material-icons" title="Search">search</i>
|
||||
<input type="text"
|
||||
v-on:focus="focus = true"
|
||||
v-on:blur="focus = false"
|
||||
v-on:keyup="keyup"
|
||||
v-on:keyup.enter="submit"
|
||||
aria-label="Write here to search"
|
||||
placeholder="Search or execute a command...">
|
||||
<div v-on:mouseover="hover = true">
|
||||
<div>Loading...</div>
|
||||
<p><i class="material-icons spin">autorenew</i></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
// Remove the last directory of an url
|
||||
var removeLastDirectoryPartOf = function (url) {
|
||||
var arr = url.split('/')
|
||||
if (arr.pop() === '') {
|
||||
arr.pop()
|
||||
}
|
||||
return (arr.join('/'))
|
||||
}
|
||||
|
||||
var data = window.data
|
||||
var ssl = window.ssl
|
||||
|
||||
export default {
|
||||
name: 'search',
|
||||
data: function () {
|
||||
return {
|
||||
hover: false,
|
||||
focus: false,
|
||||
scrollable: null,
|
||||
box: null,
|
||||
input: null
|
||||
}
|
||||
},
|
||||
mounted: function () {
|
||||
this.scrollable = document.querySelector('#search > div')
|
||||
this.box = document.querySelector('#search > div div')
|
||||
this.input = document.querySelector('#search input')
|
||||
this.reset()
|
||||
},
|
||||
methods: {
|
||||
reset: function () {
|
||||
if (data.user.AllowCommands && data.user.Commands.length > 0) {
|
||||
this.box.innerHTML = `Search or use one of your supported commands: ${data.user.Commands.join(', ')}.`
|
||||
} else {
|
||||
this.box.innerHTML = 'Type and press enter to search.'
|
||||
}
|
||||
},
|
||||
supported: function () {
|
||||
let value = this.input.value
|
||||
let pieces = value.split(' ')
|
||||
|
||||
for (let i = 0; i < data.user.Commands.length; i++) {
|
||||
if (pieces[0] === data.user.Commands[0]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
},
|
||||
click: function (event) {
|
||||
event.currentTarget.classList.add('active')
|
||||
this.$el.querySelector('input').focus()
|
||||
},
|
||||
keyup: function (event) {
|
||||
let el = event.currentTarget
|
||||
|
||||
if (el.value.length === 0) {
|
||||
this.reset()
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.supported() || !data.user.AllowCommands) {
|
||||
this.box.innerHTML = 'Press enter to search.'
|
||||
} else {
|
||||
this.box.innerHTML = 'Press enter to execute.'
|
||||
}
|
||||
},
|
||||
submit: function (event) {
|
||||
this.box.innerHTML = ''
|
||||
this.$el.classList.add('ongoing')
|
||||
|
||||
let url = window.location.host + window.location.pathname
|
||||
|
||||
if (document.getElementById('editor')) {
|
||||
url = removeLastDirectoryPartOf(url)
|
||||
}
|
||||
|
||||
let protocol = ssl ? 'wss:' : 'ws:'
|
||||
|
||||
if (this.supported() && data.user.AllowCommands) {
|
||||
let conn = new window.WebSocket(`${protocol}//${url}?command=true`)
|
||||
|
||||
conn.onopen = () => {
|
||||
conn.send(this.input.value)
|
||||
}
|
||||
|
||||
conn.onmessage = (event) => {
|
||||
this.box.innerHTML = event.data
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
}
|
||||
|
||||
conn.onclose = (event) => {
|
||||
this.$el.classList.remove('ongoing')
|
||||
// TODO: if is listing!
|
||||
// listing.reload()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
this.box.innerHTML = '<ul></ul>'
|
||||
|
||||
let ul = this.box.querySelector('ul')
|
||||
let conn = new window.WebSocket(`${protocol}//${url}?search=true`)
|
||||
|
||||
conn.onopen = () => {
|
||||
conn.send(this.input.value)
|
||||
}
|
||||
|
||||
conn.onmessage = (event) => {
|
||||
ul.innerHTML += `<li><a href=".${event.data}">${event.data}</a></li>`
|
||||
this.scrollable.scrollTop = this.scrollable.scrollHeight
|
||||
}
|
||||
|
||||
conn.onclose = () => {
|
||||
this.$el.classList.remove('ongoing')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user