<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <title>storage.clerie.de</title> <style> * { margin: 0; padding: 0; } body { font-family: Roboto; color: #222222; } header { } header > .title { padding: 3em; min-height: 3em; } header > .title > h1 { color: #222222; } header > .menu { padding: 1em 3em; } header > .menu > button, header > .menu > input { height: 3em; min-width: 3em; } main { } .listing { display: flex; width: 100%; flex-direction: column; } .listing-item { padding: 1em 3em; display: flex; flex-direction: row; flex-wrap: wrap; min-height: 3em; border-top-style: solid; border-width: 1px; border-color: #F0F0F0; } .listing-item:hover { background-color: rgb(245, 245, 245); } .listing-item > .detail { flex: auto; } .listing-item > .detail > a { display: block; height: 100%; width: 100%; } .listing-item button { height: 3em; min-width: 3em; } button, input[type="file"] { border-style: none; background-color: transparent; min-width: 3em; height: 3em; border-radius: 0.2em; } button:hover { background-color: #DDDDDD; } .loading { margin: auto; } @keyframes spinner { 0% { transform: translate3d(-50%, -50%, 0) rotate(0deg); } 100% { transform: translate3d(-50%, -50%, 0) rotate(360deg); } } .loading::before { display: block; animation: 1.5s linear infinite spinner; animation-play-state: inherit; border: solid 5px #cfd0d1; border-bottom-color: #1c87c9; border-radius: 50%; content: ""; height: 40px; width: 40px; transform: translate3d(-50%, -50%, 0); will-change: transform; } </style> </head> <body> <header> <div class="title"> <h1 id="path"></h1> </div> <div class="menu"> <button onclick="createDirectoryButton();">📁 +</button> <input id="files" type="file" multiple> <button onclick="uploadFilesButton();">⭫</button> </div> </header> <main> <div id="listing"></div> </main> <script> let path_field = document.getElementById("path"); let listing_field = document.getElementById("listing"); let base_path = "/_"; if (window.location.hash === "") { window.location.hash = "#/"; } let current_path = window.location.hash.substring(1); function deleteFile(path) { fetch(base_path + path, { method: 'DELETE', }) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Removing file failed"); } }) .then(() => { listFiles(); }); } function deleteFileButton(path) { if (confirm("Are you sure you wanna delete " + path + "?")) { deleteFile(path); } } function createDirectory(path) { fetch(base_path + path, { method: 'MKCOL', }) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Creating directory failed"); } }) .then(() => { listFiles(); }); } function createDirectoryButton() { let dir_name = prompt("Directory name"); if (dir_name !== null) { createDirectory(current_path + dir_name + "/"); } } function uploadFile(path, file) { fetch(base_path + path, { method: 'PUT', body: file, }) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Upload failed"); } }) .then(() => { listFiles(); }); } function uploadFilesButton() { let files = document.getElementById("files").files; for (let i = 0; i < files.length; i++) { uploadFile(current_path + files[i].name, files[i]); } document.getElementById("files").value = ""; } function moveFile(old_path, new_path) { fetch(base_path + old_path, { method: 'MOVE', headers: { 'Destination': base_path + new_path, }, }) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Rename file failed"); } }) .then(() => { listFiles(); }); } function moveFileButton(path) { let new_path = prompt("New file path", path); moveFile(path, new_path); } function renameFileButton(path) { let new_name = prompt("New file name"); moveFile(path, current_path + new_name); } function copyFile(path, new_path) { fetch(base_path + path, { method: 'COPY', headers: { 'Destination': base_path + new_path, }, }) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Copy file failed"); } }) .then(() => { listFiles(); }); } function duplicateFileButton(path) { let new_path = prompt("New file path", path); copyFile(path, new_path); } function listFiles() { listing_field.innerHTML = '<div class="loading"></div>'; path_field.innerHTML = '<a href="#/">⌂</a>'; let path_blocks = current_path.split('/').slice(1); for (let i = 0; i < path_blocks.length; i++) { path_field.innerHTML += ' / <a href="#/' + path_blocks.slice(0, i+1).join('/') + '/">' + path_blocks[i] + '</a>'; } fetch(base_path + current_path) .then((response) => { console.log(response); if (!response.ok) { throw new Error("Can't fetch directory"); } return response.json(); }) .then((listing) => { console.log(listing); let out = '<div class="listing">'; for (let i = 0; i < listing.length; i++) { let listing_path = current_path + encodeURIComponent(listing[i]["name"]); let listing_link = "#"; let listing_icon = ""; if (listing[i]["type"] === "directory") { listing_path += '/'; listing_link = '#' + listing_path; listing_icon = "📁"; } else { listing_link = base_path + listing_path; } out += '<div class="listing-item">' + '<div class="detail">' + '<a href="' + listing_link + '">' + listing_icon + " " + listing[i]["name"] + '</a>' + '</div>' + '<div class="options">' + '<button onclick="renameFileButton(\'' + listing_path + '\');" alt="rename">✎</button>' + '<button onclick="duplicateFileButton(\'' + listing_path + '\');">●●</button>' + '<button onclick="moveFileButton(\'' + listing_path + '\');">⇨</button>' + '<button onclick="deleteFileButton(\'' + listing_path + '\');">❌</button>' + '</div>' + '</div>'; } out += '</div>'; listing_field.innerHTML = out; }); } listFiles(); window.onhashchange = () => { current_path = window.location.hash.substring(1); listFiles(); } </script> </body> </html>