This commit is contained in:
Henrique Dias 2016-12-29 22:50:36 +00:00
parent 72e65e85fb
commit ebbe370ebb
8 changed files with 202 additions and 394 deletions

View File

@ -395,7 +395,7 @@ textarea {
body {
font-family: 'Roboto', sans-serif;
padding-top: 5em;
padding-top: 9em;
background-color: #f8f8f8;
text-rendering: optimizespeed;
}
@ -602,11 +602,15 @@ pre {
/* HEADER */
header {
z-index: 999;
padding: 1.7em 0;
z-index: 1000;
background-color: #fff;
border-bottom: 1px solid rgba(0, 0, 0, 0.075);
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
position: fixed;
top: 0;
left: 0;
width: 100%;
padding: 0;
}
header h1 {
@ -621,10 +625,7 @@ header a:hover,
color: inherit;
}
header p {
font-size: 1.5em;
max-width: calc(100% - 3em);
}
header p {}
header p i {
font-size: 1em !important;
@ -632,14 +633,10 @@ header p i {
}
header #logout {
background-color: rgba(0, 0, 0, 0.05);
/* background-color: rgba(0, 0, 0, 0.05); */
border-radius: 0;
margin: -0.5em -0.5em -0.5em 0;
padding: .5em;
}
header p i {
vertical-align: middle;
margin: 0 0 0 auto;
padding: .2em;
}
#search {
@ -771,29 +768,46 @@ header p i {
color: rgba(255, 255, 255, .5);
}
#toolbar,
header {
position: fixed;
top: 0;
left: 0;
display: -webkit-box;
display: -ms-flexbox;
header>div {
display: flex;
width: 100%;
padding: 0.5em;
max-height: 4em;
padding: 0.5em 0.5em 0.5em 1em;
}
#toolbar div,
header div {
header>div:first-child>div:nth-child(1) {
margin-right: 2em;
font-weight: 500;
font-size: 1.5em;
line-height: 2;
}
header>div:last-child {
background-color: #fafafa;
border-top: 1px solid rgba(0, 0, 0, 0.075);
border-bottom: 1px solid rgba(0, 0, 0, 0.075);
}
header>div div {
vertical-align: middle;
-webkit-box-flex: 1;
-ms-flex-positive: 1;
flex-grow: 1;
position: relative;
}
#toolbar p,
header .actions {
margin-left: auto;
}
header #file-only {
display: inline-block;
border-right: 1px solid rgba(0, 0, 0, 0.075);
padding-right: .3em;
margin-right: .3em;
transition: .2s ease all;
}
#file-only.disabled {
opacity: 0;
}
header p {
display: inline-block;
margin: 0;
@ -804,49 +818,12 @@ header #open-nav {
display: none;
}
#toolbar p a,
#toolbar p a:hover,
header p a,
header p a:hover {
color: inherit;
}
#toolbar {
z-index: 1000;
top: -4em;
-webkit-transition: 0.2s ease-in-out all;
transition: 0.2s ease-in-out all;
opacity: 0;
color: #fff;
background-color: #2196f3;
}
#toolbar.enabled {
top: 0;
opacity: 1;
}
#toolbar div:nth-child(2),
header div:nth-child(2) {
text-align: right;
}
header #overlay {
visibility: hidden;
opacity: 0;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 999;
transition: .2s ease all;
background-color: rgba(0, 0, 0, 0.35);
}
header .only-side {
display: none;
}
header>div div:nth-child(2) {}
.action:hover ul {
display: flex;
@ -908,11 +885,12 @@ header .only-side {
.action {
display: inline-block;
margin: 0 0.2em;
cursor: pointer;
-webkit-transition: 0.2s ease all;
transition: 0.2s ease all;
border: 0;
margin: 0;
color: #546E7A;
border-radius: 50%;
}
@ -922,7 +900,7 @@ header .only-side {
}
.action i {
padding: 0.5em;
padding: 0.4em;
-webkit-transition: 0.2s ease-in-out all;
transition: 0.2s ease-in-out all;
border-radius: 50%;
@ -932,7 +910,6 @@ header .only-side {
background-color: rgba(0, 0, 0, .1);
}
#toolbar .action span,
header .action span {
display: none;
}
@ -983,8 +960,6 @@ header .action span {
justify-content: flex-start;
max-width: calc(100% - 2.2em);
width: 100%;
opacity: 0;
transition: .1s ease all;
}
#listing.list {
@ -1000,7 +975,7 @@ header .action span {
border: 0;
box-shadow: none;
border-radius: 0;
border-bottom: 1px solid rgba(0,0,0,0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
padding: 1em;
}
@ -1053,7 +1028,6 @@ header .action span {
vertical-align: bottom;
}
#listing.list .item div:first-of-type {
width: 3em;
}
@ -1066,6 +1040,7 @@ header .action span {
width: calc(100% - 3em);
}
/* ANIMATIONS */
i.spin {
@ -1237,130 +1212,4 @@ i.spin {
display: inline-block;
text-align: right;
float: right;
}
@media screen and (max-width: 1024px) {
header .only-side {
display: block;
padding: .5em;
background-color: #2196f3;
color: #fff;
max-height: 4em;
}
header>div:first-child #prev {
display: none;
}
header #open-nav {
display: inline-block;
}
/* SIDEBAR */
header>div:nth-child(2) {
position: fixed;
top: 0;
z-index: 999999;
background-color: #fff;
height: 100%;
width: 95%;
max-width: 20em;
text-align: left;
color: #212121;
left: -100%;
transition: .2s ease-in-out all;
}
header>div:nth-child(2).active {
left: 0;
}
header>div:nth-child(2).active+#overlay {
opacity: 1;
visibility: visible;
}
header #search {
height: auto;
background-color: transparent;
color: #212121;
border-bottom: 1px solid #eee;
border-radius: 0;
display: block;
width: 100%;
padding: .5em 0;
text-align: left;
}
header #search input {
color: #212121;
display: inline-block;
width: auto;
min-width: 16em;
}
header #search i {
color: #6f6f6f;
display: inline-block;
padding: .5em;
margin: 0;
}
header>div:nth-child(2)>div {
display: block;
}
header>div:nth-child(2) .action {
border-radius: 0 !important;
padding: .5em 0 !important;
margin: 0 !important;
text-align: left;
background-color: transparent !important;
}
header>div:nth-child(2) #prev {
border-radius: 50% !important;
padding: 0 !important;
}
header>div:nth-child(2) #prev i {
color: #fff;
}
header>div:nth-child(2) .action:hover {
background-color: rgba(0, 0, 0, .1) !important;
}
header>div:nth-child(2) .action:hover i {
background-color: transparent;
}
header>div:nth-child(2) .action i {
border-radius: 0;
color: #6f6f6f;
}
header>div:nth-child(2) .action span {}
header>div:nth-child(2) .action i,
header>div:nth-child(2) .action span {
vertical-align: middle;
display: inline-block;
}
}
@media screen and (max-width: 800px) {
#listing .item {
width: calc(50% - 1em);
}
}
@media screen and (max-width: 700px) {
header>div:first-child p a,
header>div:first-child p i {
display: none !important;
}
header>div:first-child p {
font-size: 1em;
}
#editor .frontmatter {
column-count: 1;
column-gap: 0;
}
}
@media screen and (max-width: 650px) {
#listing .item {
width: 100%;
margin: 0 0 1em;
}
}
@media screen and (max-width: 450px) {
#toolbar p {
display: none;
}
}

View File

@ -85,6 +85,21 @@ Element.prototype.changeToDone = function(error, html) {
return false;
}
function getCSSRule(ruleName) {
ruleName = ruleName.toLowerCase();
var result = null;
var find = Array.prototype.find;
find.call(document.styleSheets, styleSheet => {
result = find.call(styleSheet.cssRules, cssRule => {
return cssRule instanceof CSSStyleRule &&
cssRule.selectorText.toLowerCase() == ruleName;
});
return result != null;
});
return result;
}
var toWebDavURL = function(url) {
url = url.replace(baseURL + "/", webdavURL + "/");
return window.location.origin + url
@ -171,8 +186,7 @@ var reloadListing = function(callback) {
if (request.status == 200) {
document.querySelector('body main').innerHTML = request.responseText;
addNewDirEvents();
document.getElementById("listing").style.opacity = 1;
if (typeof callback == 'function') {
callback();
}
@ -193,23 +207,24 @@ var renameEvent = function(event) {
location.refresh();
}
let link = selectedItems[0];
let item = document.getElementById(link);
let span = item.getElementsByTagName('span')[0];
let name = span.innerHTML;
let item = document.getElementById(selectedItems[0]),
link = item.dataset.url,
span = item.getElementsByTagName('span')[0],
name = span.innerHTML;
span.setAttribute('contenteditable', 'true');
span.focus();
let keyDownEvent = (event) => {
if (event.keyCode == 13) {
let newName = span.innerHTML;
let newLink = toWebDavURL(link).replace(name, newName)
let html = document.getElementById('rename').changeToLoading();
let request = new XMLHttpRequest();
let newName = span.innerHTML,
newLink = RemoveLastDirectoryPartOf(toWebDavURL(link)) + newName,
html = document.getElementById('rename').changeToLoading(),
request = new XMLHttpRequest();
request.open('MOVE', toWebDavURL(link));
request.setRequestHeader('Destination', newLink);
request.setRequestHeader('Content-type', 'text/plain; charset=utf-8');
request.send();
request.onreadystatechange = function() {
// TODO: redirect if it's moved to another folder
@ -221,11 +236,10 @@ var renameEvent = function(event) {
let newLink = encodeURI(link.replace(name, newName));
console.log(request.body)
reloadListing(() => {
let newLink = encodeURI(link.replace(name, newName));
selectedItems = [newLink];
document.getElementById(newLink).classList.add("selected")
var event = new CustomEvent('changed-selected');
document.dispatchEvent(event);
newName = btoa(newName);
selectedItems = [newName];
document.getElementById(newName).setAttribute("aria-selected", true);
document.sendCostumEvent('changed-selected');
});
}
@ -376,12 +390,13 @@ var newDirEvent = function(event) {
// Handles the event when there is change on selected elements
document.addEventListener("changed-selected", function(event) {
var toolbar = document.getElementById("toolbar");
var selectedNumber = selectedItems.length;
document.getElementById("selected-number").innerHTML = selectedNumber;
redefineDownloadURLs();
let selectedNumber = selectedItems.length,
fileAction = document.getElementById("file-only");
if (selectedNumber) {
toolbar.classList.add("enabled");
fileAction.classList.remove("disabled");
if (selectedNumber > 1) {
document.getElementById("open").classList.add("disabled");
@ -393,12 +408,10 @@ document.addEventListener("changed-selected", function(event) {
document.getElementById("rename").classList.remove("disabled");
}
redefineDownloadURLs();
return false;
}
toolbar.classList.remove("enabled");
fileAction.classList.add("disabled");
return false;
});
@ -406,7 +419,8 @@ var redefineDownloadURLs = function() {
let files = "";
for (let i = 0; i < selectedItems.length; i++) {
files += selectedItems[i].replace(window.location.pathname, "") + ",";
let url = document.getElementById(selectedItems[i]).dataset.url;
files += url.replace(window.location.pathname, "") + ",";
}
files = files.substring(0, files.length - 1);
@ -484,13 +498,26 @@ var searchEvent = function(event) {
}
}
document.addEventListener('listing', event => {
// Handles the current view mode and adds the event to the button
handleViewType(document.getCookie("view-list"));
document.getElementById("view").addEventListener("click", viewEvent);
let updateColumns = () => {
let columns = Math.floor(document.getElementById('listing').offsetWidth / 300),
itens = getCSSRule('#listing .item');
itens.style.width = `calc(${100/columns}% - 1em)`;
}
updateColumns();
window.addEventListener("resize", () => {
updateColumns();
});
// Add event to back button and executes back event on ESC
document.getElementById("back").addEventListener("click", backEvent)
document.addEventListener('keydown', (event) => {
if (event.keyCode == 27) {
backEvent(event);
@ -628,6 +655,7 @@ function itemDrop(e) {
let el = e.target,
id = e.dataTransfer.getData("id"),
name = e.dataTransfer.getData("name");
if (id == "" || name == "") return;
for (let i = 0; i < 5; i++) {
@ -638,10 +666,10 @@ function itemDrop(e) {
if (el.id === id) return;
let oldLink = toWebDavURL(id);
let newLink = toWebDavURL(el.id + name);
let oldLink = toWebDavURL(document.getElementById(id).dataset.url),
newLink = toWebDavURL(el.dataset.url + name),
request = new XMLHttpRequest();
let request = new XMLHttpRequest();
request.open('MOVE', oldLink);
request.setRequestHeader('Destination', newLink);
request.send();
@ -660,20 +688,18 @@ function openItem(event) {
}
function selectItem(event) {
let el = event.currentTarget,
url = el.dataset.url;
let el = event.currentTarget;
if (selectedItems.length != 0) event.preventDefault();
if (selectedItems.indexOf(url) == -1) {
if (selectedItems.indexOf(el.id) == -1) {
el.setAttribute("aria-selected", true);
selectedItems.push(url);
selectedItems.push(el.id);
} else {
el.setAttribute("aria-selected", false);
selectedItems.removeElement(url);
selectedItems.removeElement(el.id);
}
var event = new CustomEvent('changed-selected');
document.dispatchEvent(event);
document.sendCostumEvent("changed-selected");
return false;
}
@ -969,13 +995,6 @@ document.addEventListener("DOMContentLoaded", function(event) {
document.getElementById("delete").addEventListener("click", deleteEvent);
}
document.getElementById("open-nav").addEventListener("click", event => {
document.querySelector("header > div:nth-child(2)").classList.toggle("active");
});
document.getElementById("overlay").addEventListener("click", event => {
document.querySelector("header > div:nth-child(2)").classList.toggle("active");
});
if (document.getElementById('listing')) {
document.sendCostumEvent('listing');
}
@ -985,36 +1004,4 @@ document.addEventListener("DOMContentLoaded", function(event) {
}
return false;
});
(function() {
let columns = Math.floor(document.getElementById('listing').offsetWidth / 300);
var header = getCSSRule('#listing .item');
header.style.width = `calc(${100/columns}% - 1em)`;
document.getElementById("listing").style.opacity = 1;
}());
window.addEventListener("resize", () => {
let columns = Math.floor(document.getElementById('listing').offsetWidth / 300);
var itens = getCSSRule('#listing .item');
itens.style.width = `calc(${100/columns}% - 1em)`;
});
function getCSSRule(ruleName) {
ruleName = ruleName.toLowerCase();
var result = null;
var find = Array.prototype.find;
find.call(document.styleSheets, styleSheet => {
result = find.call(styleSheet.cssRules, cssRule => {
return cssRule instanceof CSSStyleRule &&
cssRule.selectorText.toLowerCase() == ruleName;
});
return result != null;
});
return result;
}
});

View File

@ -1,33 +0,0 @@
{{ define "actions" }}
<div class="action" id="open">
<i class="material-icons" title="See raw">open_in_new</i> <span>See raw</span>
</div>
{{ if and .IsDir .User.AllowEdit }}
<div class="action" id="rename">
<i class="material-icons" title="Edit">mode_edit</i>
</div>
{{ end }}
<!-- {{ if .IsDir }}
<div class="action" id="info">
<i class="material-icons">info</i>
</div>
{{ end }}-->
<div class="action" id="download">
<a href="?download=true">
<i class="material-icons" title="Download">file_download</i> <span>Download</span>
</a>
{{ if .IsDir }}
<ul class="prev-links">
<a data-format="tarbz2" href="?download=tarbz2"><li>tar.bz2</li></a>
<a data-format="targz" href="?download=targz"><li>tar.gz</li></a>
<a data-format="tar" href="?download=tar"><li>tar</li></a>
<a data-format="zip" href="?download=zip"><li>zip</li></a>
</ul>
{{ end }}
</div>
{{ if .User.AllowEdit }}
<div class="action" id="delete">
<i class="material-icons" title="Delete">delete</i> <span>Delete</span>
</div>
{{ end }}
{{ end }}

View File

@ -4,6 +4,7 @@
<head>
<title>{{.Name}}</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta charset="utf-8">
<meta name="token" content="{{ .Token }}">
<link rel="stylesheet" href="{{ .Config.AbsoluteURL }}/_filemanagerinternal/css/styles.css">
{{ if ne .User.StyleSheet "" }}
@ -19,103 +20,96 @@
</head>
<body>
<header>
<!-- TOP BAR -->
<div>
{{ $lnk := .PreviousLink }}
<div class="action{{ if eq $lnk ""}} disabled{{ end }}" id="prev">
{{ if ne $lnk ""}}<a href="{{ $lnk }}">{{ end }}
<i class="material-icons" title="Previous">subdirectory_arrow_left</i>
{{ if ne $lnk ""}}</a>{{ end }}
{{ if ne $lnk ""}}
<ul class="prev-links">
{{ range $link, $name := .BreadcrumbMap }}<a href="{{ $absURL }}{{ $link }}"><li>{{ $name }}</li></a>{{ end }}
</ul>
{{ end }}
<div><p>File Manager</p></div>
{{ if .User.AllowCommands }}
<div id="search">
<i class="material-icons" title="Storage">storage</i>
<input type="text" placeholder="Search or execute a command...">
<div>
<div>Write your git, mercurial or svn command and press enter.</div>
<p><i class="material-icons spin">autorenew</i></p>
</div>
</div>
<div class="action" id="open-nav">
<i class="material-icons" title="Menu">menu</i>
</div>
{{ if ne .Name "/"}}<p>{{ .Name }}</p>{{ end }}
</div>
<div>
<div class="only-side">
{{ $lnk := .PreviousLink }}
{{ if ne $lnk ""}}<a href="{{ $lnk }}">{{ end }}
<div class="action{{ if eq $lnk ""}} disabled{{ end }}" id="prev">
<i class="material-icons" title="Previous">subdirectory_arrow_left</i>
</div>
{{ if ne $lnk ""}}</a>{{ end }}
<p><a href="{{ if eq .Config.AbsoluteURL "" }}/{{ else }}{{ .Config.AbsoluteURL }}{{ end }}">File Manager</a></p>
</div>
{{ if .IsDir}}
{{ if .User.AllowCommands }}
<div id="search">
<i class="material-icons" title="Storage">storage</i>
<input type="text" placeholder="Search or execute a command...">
<div>
<div>Write your git, mercurial or svn command and press enter.</div>
<p><i class="material-icons spin">autorenew</i></p>
</div>
</div>
{{ end }}
<div class="action" id="view">
<i class="material-icons" title="Switch view">view_headline</i> <span>Switch view</span>
</div>
{{ if .User.AllowNew }}
<div class="action" id="upload">
<i class="material-icons" title="Upload">file_upload</i> <span>Upload</span>
</div>
{{ end }}
<div class="action">
<a href="?download=true">
<i class="material-icons" title="Download">file_download</i> <span>Download</span>
</a>
<ul class="prev-links">
<a href="?download=tarbz2"><li>tar.bz2</li></a>
<a href="?download=targz"><li>tar.gz</li></a>
<a href="?download=tar"><li>tar</li></a>
<a href="?download=zip"><li>zip</li></a>
</ul>
</div>
{{ else }}
{{ template "actions" . }}
{{ end }}
<div class="action" id="logout">
<i class="material-icons" title="Logout">exit_to_app</i> <span>Logout</span>
</div>
</div>
<div id="overlay"></div>
</header>
{{ if .IsDir }}
<div id="toolbar">
<!-- BOTTOM BAR -->
<div>
<div class="action" id="back">
<i class="material-icons" title="Back">arrow_back</i>
<div>
{{ $lnk := .PreviousLink }}
<div class="action{{ if eq $lnk ""}} disabled{{ end }}" id="prev">
{{ if ne $lnk ""}}<a href="{{ $lnk }}">{{ end }}
<i class="material-icons" title="Previous">subdirectory_arrow_left</i>
{{ if ne $lnk ""}}</a>{{ end }}
{{ if ne $lnk ""}}
<ul class="prev-links">
{{ range $link, $name := .BreadcrumbMap }}<a href="{{ $absURL }}{{ $link }}"><li>{{ $name }}</li></a>{{ end }}
</ul>
{{ end }}
</div>
{{ if ne .Name "/"}}<p>{{ .Name }}</p>{{ end }}
</div>
<!-- ACTIONS -->
<div class="actions">
<div id="file-only" {{ if .IsDir }}class="disabled"{{ end }}>
<div class="action" id="open">
<i class="material-icons" title="See raw">open_in_new</i> <span>See raw</span>
</div>
{{ if and .IsDir .User.AllowEdit }}
<div class="action" id="rename">
<i class="material-icons" title="Edit">mode_edit</i>
</div>
{{ end }}
{{ if .User.AllowEdit }}
<div class="action" id="delete">
<i class="material-icons" title="Delete">delete</i> <span>Delete</span>
</div>
{{ end }}
</div>
{{ if and (.User.AllowNew) (.IsDir) }}
<div class="action" id="upload">
<i class="material-icons" title="Upload">file_upload</i> <span>Upload</span>
</div>
{{ end }}
<div class="action" id="download">
<a href="?download=true">
<i class="material-icons" title="Download">file_download</i> <span>Download</span>
</a>
{{ if .IsDir }}
<ul class="prev-links">
<a data-format="tarbz2" href="?download=tarbz2"><li>tar.bz2</li></a>
<a data-format="targz" href="?download=targz"><li>tar.gz</li></a>
<a data-format="tar" href="?download=tar"><li>tar</li></a>
<a data-format="zip" href="?download=zip"><li>zip</li></a>
</ul>
{{ end }}
</div>
{{ if .IsDir }}
<div class="action" id="view">
<i class="material-icons" title="Switch view">view_headline</i> <span>Switch view</span>
</div>
{{ end }}
</div>
<p><span id="selected-number">0</span> selected.</p>
</div>
<div>
{{ template "actions" . }}
</div>
</div>
{{ end }}
</header>
<main>
{{ template "content" . }}
</main>
<footer>Served with <a rel="noopener noreferrer" href="https://caddyserver.com">Caddy</a> and <a rel="noopener noreferrer" href="https://github.com/hacdias/caddy-filemanager">File Manager</a>.</footer>
</body>
</html>

View File

@ -12,7 +12,7 @@
ondblclick="openItem(event)"
data-dir="{{ .IsDir }}"
data-url="{{ .URL }}"
id="{{.URL}}">
id="{{ EncodeBase64 .Name }}">
<div>
{{- if .IsDir}}
<i class="material-icons">folder</i>

View File

@ -4,7 +4,7 @@
{{ if eq .Type "image" }}
<img src="{{ .URL }}?raw=true">
{{ else if eq .Type "audio" }}
<audio src="{{ .URL }}?raw=true"></audio>
<audio src="{{ .URL }}?raw=true" controls></audio>
{{ else if eq .Type "video" }}
<video src="{{ .URL }}?raw=true" controls>
Sorry, your browser doesn't support embedded videos,

View File

@ -4,6 +4,7 @@ import (
"io"
"io/ioutil"
"net/http"
"net/url"
"os"
"path/filepath"
"strings"
@ -29,6 +30,12 @@ func Download(w http.ResponseWriter, r *http.Request, c *config.Config, i *file.
if len(names) != 0 {
for _, name := range names {
name, err := url.QueryUnescape(name)
if err != nil {
return http.StatusInternalServerError, err
}
files = append(files, filepath.Join(i.Path, name))
}
@ -78,7 +85,7 @@ func Download(w http.ResponseWriter, r *http.Request, c *config.Config, i *file.
}
name := i.Name()
if name == "" {
if name == "." || name == "" {
name = "download"
}

View File

@ -3,6 +3,7 @@ package page
import (
"bytes"
"encoding/base64"
"encoding/json"
"html/template"
"log"
@ -90,12 +91,15 @@ func (p Page) PrintAsHTML(w http.ResponseWriter, templates ...string) (int, erro
a, _ := json.Marshal(v)
return template.JS(a)
},
"EncodeBase64": func(s string) string {
return base64.StdEncoding.EncodeToString([]byte(s))
},
}
if p.Minimal {
templates = append(templates, "actions", "minimal")
templates = append(templates, "minimal")
} else {
templates = append(templates, "actions", "base")
templates = append(templates, "base")
}
var tpl *template.Template