refactor: no inline scripts in HTML (#391)
* Moved 'ready' func call from index.html Inline script moved to index.js * Moved <script> out from index.html * moved inline-styling to css * minor formatting changes * changed ratio from const to let * refactor * fix tests --------- Co-authored-by: sigoden <sigoden@gmail.com>
This commit is contained in:
parent
ce740b1fb1
commit
dc7a7cbb3f
6 changed files with 81 additions and 59 deletions
|
@ -6,10 +6,6 @@
|
|||
<meta name="viewport" content="width=device-width" />
|
||||
<link rel="icon" type="image/x-icon" href="__ASSETS_PREFIX__favicon.ico">
|
||||
<link rel="stylesheet" href="__ASSETS_PREFIX__index.css">
|
||||
<script>
|
||||
DATA = __INDEX_DATA__
|
||||
</script>
|
||||
<script src="__ASSETS_PREFIX__index.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -74,7 +70,8 @@
|
|||
d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001c.03.04.062.078.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1.007 1.007 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0z" />
|
||||
</svg>
|
||||
</div>
|
||||
<input id="search" title="Searching for folders or files" name="q" type="text" maxlength="128" autocomplete="off" tabindex="1">
|
||||
<input id="search" title="Searching for folders or files" name="q" type="text" maxlength="128" autocomplete="off"
|
||||
tabindex="1">
|
||||
<input type="submit" hidden />
|
||||
</form>
|
||||
<div class="toolbox-right">
|
||||
|
@ -125,9 +122,8 @@
|
|||
<textarea id="editor" class="editor hidden" aria-label="Editor" cols="10"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
window.addEventListener("DOMContentLoaded", ready);
|
||||
</script>
|
||||
<template id="index-data">__INDEX_DATA__</template>
|
||||
<script src="__ASSETS_PREFIX__index.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
104
assets/index.js
104
assets/index.js
|
@ -29,6 +29,11 @@ var DUFS_MAX_UPLOADINGS = 1;
|
|||
*/
|
||||
var DATA;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
var DIR_EMPTY_NOTE;
|
||||
|
||||
/**
|
||||
* @type {PARAMS}
|
||||
* @typedef {object} PARAMS
|
||||
|
@ -45,8 +50,6 @@ const IFRAME_FORMATS = [
|
|||
".mp3", ".ogg", ".wav", ".m4a",
|
||||
];
|
||||
|
||||
const dirEmptyNote = PARAMS.q ? 'No results' : DATA.dir_exists ? 'Empty folder' : 'Folder will be created when a file is uploaded';
|
||||
|
||||
const ICONS = {
|
||||
dir: `<svg height="16" viewBox="0 0 14 16" width="14"><path fill-rule="evenodd" d="M13 4H7V3c0-.66-.31-1-1-1H1c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1V5c0-.55-.45-1-1-1zM6 4H1V3h5v1z"></path></svg>`,
|
||||
symlinkFile: `<svg height="16" viewBox="0 0 12 16" width="12"><path fill-rule="evenodd" d="M8.5 1H1c-.55 0-1 .45-1 1v12c0 .55.45 1 1 1h10c.55 0 1-.45 1-1V4.5L8.5 1zM11 14H1V2h7l3 3v9zM6 4.5l4 3-4 3v-2c-.98-.02-1.84.22-2.55.7-.71.48-1.19 1.25-1.45 2.3.02-1.64.39-2.88 1.13-3.73.73-.84 1.69-1.27 2.88-1.27v-2H6z"></path></svg>`,
|
||||
|
@ -97,8 +100,22 @@ let $userBtn;
|
|||
*/
|
||||
let $userName;
|
||||
|
||||
function ready() {
|
||||
$pathsTable = document.querySelector(".paths-table")
|
||||
// Produce table when window loads
|
||||
window.addEventListener("DOMContentLoaded", async () => {
|
||||
const $indexData = document.getElementById('index-data');
|
||||
if (!$indexData) {
|
||||
alert("No data");
|
||||
return;
|
||||
}
|
||||
|
||||
DATA = JSON.parse($indexData.innerHTML);
|
||||
DIR_EMPTY_NOTE = PARAMS.q ? 'No results' : DATA.dir_exists ? 'Empty folder' : 'Folder will be created when a file is uploaded';
|
||||
|
||||
await ready();
|
||||
});
|
||||
|
||||
async function ready() {
|
||||
$pathsTable = document.querySelector(".paths-table");
|
||||
$pathsTableHead = document.querySelector(".paths-table thead");
|
||||
$pathsTableBody = document.querySelector(".paths-table tbody");
|
||||
$uploadersTable = document.querySelector(".uploaders-table");
|
||||
|
@ -109,25 +126,24 @@ function ready() {
|
|||
|
||||
addBreadcrumb(DATA.href, DATA.uri_prefix);
|
||||
|
||||
if (DATA.kind == "Index") {
|
||||
if (DATA.kind === "Index") {
|
||||
document.title = `Index of ${DATA.href} - Dufs`;
|
||||
document.querySelector(".index-page").classList.remove("hidden");
|
||||
|
||||
setupIndexPage();
|
||||
} else if (DATA.kind == "Edit") {
|
||||
await setupIndexPage();
|
||||
} else if (DATA.kind === "Edit") {
|
||||
document.title = `Edit ${DATA.href} - Dufs`;
|
||||
document.querySelector(".editor-page").classList.remove("hidden");;
|
||||
document.querySelector(".editor-page").classList.remove("hidden");
|
||||
|
||||
setupEditorPage();
|
||||
} else if (DATA.kind == "View") {
|
||||
await setupEditorPage();
|
||||
} else if (DATA.kind === "View") {
|
||||
document.title = `View ${DATA.href} - Dufs`;
|
||||
document.querySelector(".editor-page").classList.remove("hidden");;
|
||||
document.querySelector(".editor-page").classList.remove("hidden");
|
||||
|
||||
setupEditorPage();
|
||||
await setupEditorPage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Uploader {
|
||||
/**
|
||||
*
|
||||
|
@ -221,7 +237,7 @@ class Uploader {
|
|||
uploadOffset = parseInt(value) || 0;
|
||||
}
|
||||
this.uploadOffset = uploadOffset;
|
||||
this.ajax()
|
||||
this.ajax();
|
||||
}
|
||||
|
||||
progress(event) {
|
||||
|
@ -230,7 +246,7 @@ class Uploader {
|
|||
const [speedValue, speedUnit] = formatSize(speed);
|
||||
const speedText = `${speedValue} ${speedUnit}/s`;
|
||||
const progress = formatPercent(((event.loaded + this.uploadOffset) / this.file.size) * 100);
|
||||
const duration = formatDuration((event.total - event.loaded) / speed)
|
||||
const duration = formatDuration((event.total - event.loaded) / speed);
|
||||
this.$uploadStatus.innerHTML = `<span style="width: 80px;">${speedText}</span><span>${progress} ${duration}</span>`;
|
||||
this.uploaded = event.loaded;
|
||||
this.lastUptime = now;
|
||||
|
@ -274,7 +290,7 @@ Uploader.runQueue = async () => {
|
|||
if (!Uploader.auth) {
|
||||
Uploader.auth = true;
|
||||
try {
|
||||
await checkAuth()
|
||||
await checkAuth();
|
||||
} catch {
|
||||
Uploader.auth = false;
|
||||
}
|
||||
|
@ -319,7 +335,7 @@ function addBreadcrumb(href, uri_prefix) {
|
|||
}
|
||||
}
|
||||
|
||||
function setupIndexPage() {
|
||||
async function setupIndexPage() {
|
||||
if (DATA.allow_archive) {
|
||||
const $download = document.querySelector(".download");
|
||||
$download.href = baseUrl() + "?zip";
|
||||
|
@ -335,11 +351,11 @@ function setupIndexPage() {
|
|||
}
|
||||
|
||||
if (DATA.auth) {
|
||||
setupAuth();
|
||||
await setupAuth();
|
||||
}
|
||||
|
||||
if (DATA.allow_search) {
|
||||
setupSearch()
|
||||
setupSearch();
|
||||
}
|
||||
|
||||
renderPathsTableHead();
|
||||
|
@ -402,7 +418,7 @@ function renderPathsTableBody() {
|
|||
addPath(DATA.paths[i], i);
|
||||
}
|
||||
} else {
|
||||
$emptyFolder.textContent = dirEmptyNote;
|
||||
$emptyFolder.textContent = DIR_EMPTY_NOTE;
|
||||
$emptyFolder.classList.remove("hidden");
|
||||
}
|
||||
}
|
||||
|
@ -414,7 +430,7 @@ function renderPathsTableBody() {
|
|||
*/
|
||||
function addPath(file, index) {
|
||||
const encodedName = encodedStr(file.name);
|
||||
let url = newUrl(file.name)
|
||||
let url = newUrl(file.name);
|
||||
let actionDelete = "";
|
||||
let actionDownload = "";
|
||||
let actionMove = "";
|
||||
|
@ -455,7 +471,7 @@ function addPath(file, index) {
|
|||
${actionMove}
|
||||
${actionDelete}
|
||||
${actionEdit}
|
||||
</td>`
|
||||
</td>`;
|
||||
|
||||
$pathsTableBody.insertAdjacentHTML("beforeend", `
|
||||
<tr id="addPath${index}">
|
||||
|
@ -468,7 +484,7 @@ function addPath(file, index) {
|
|||
<td class="cell-mtime">${formatMtime(file.mtime)}</td>
|
||||
<td class="cell-size">${formatSize(file.size).join(" ")}</td>
|
||||
${actionCell}
|
||||
</tr>`)
|
||||
</tr>`);
|
||||
}
|
||||
|
||||
function setupDropzone() {
|
||||
|
@ -480,7 +496,7 @@ function setupDropzone() {
|
|||
});
|
||||
document.addEventListener("drop", async e => {
|
||||
if (!e.dataTransfer.items[0].webkitGetAsEntry) {
|
||||
const files = e.dataTransfer.files.filter(v => v.size > 0);
|
||||
const files = Array.from(e.dataTransfer.files).filter(v => v.size > 0);
|
||||
for (const file of files) {
|
||||
new Uploader(file, []).upload();
|
||||
}
|
||||
|
@ -490,12 +506,12 @@ function setupDropzone() {
|
|||
for (let i = 0; i < len; i++) {
|
||||
entries.push(e.dataTransfer.items[i].webkitGetAsEntry());
|
||||
}
|
||||
addFileEntries(entries, [])
|
||||
addFileEntries(entries, []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setupAuth() {
|
||||
async function setupAuth() {
|
||||
if (DATA.user) {
|
||||
$userBtn.classList.remove("hidden");
|
||||
$userName.textContent = DATA.user;
|
||||
|
@ -504,7 +520,7 @@ function setupAuth() {
|
|||
$loginBtn.classList.remove("hidden");
|
||||
$loginBtn.addEventListener("click", async () => {
|
||||
try {
|
||||
await checkAuth()
|
||||
await checkAuth();
|
||||
location.reload();
|
||||
} catch (err) {
|
||||
alert(err.message);
|
||||
|
@ -585,7 +601,7 @@ async function setupEditorPage() {
|
|||
await doDeletePath(name, url, () => {
|
||||
location.href = location.href.split("/").slice(0, -1).join("/");
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
const $saveBtn = document.querySelector(".save-btn");
|
||||
$saveBtn.classList.remove("hidden");
|
||||
|
@ -599,7 +615,7 @@ async function setupEditorPage() {
|
|||
const url = baseUrl();
|
||||
const ext = extName(baseName(url));
|
||||
if (IFRAME_FORMATS.find(v => v === ext)) {
|
||||
$notEditable.insertAdjacentHTML("afterend", `<iframe src="${url}" sandbox width="100%" height="${window.innerHeight - 100}px"></iframe>`)
|
||||
$notEditable.insertAdjacentHTML("afterend", `<iframe src="${url}" sandbox width="100%" height="${window.innerHeight - 100}px"></iframe>`);
|
||||
} else {
|
||||
$notEditable.classList.remove("hidden");
|
||||
$notEditable.textContent = "Cannot edit because file is too large or binary.";
|
||||
|
@ -616,8 +632,8 @@ async function setupEditorPage() {
|
|||
$editor.value = await res.text();
|
||||
} else {
|
||||
const bytes = await res.arrayBuffer();
|
||||
const dataView = new DataView(bytes)
|
||||
const decoder = new TextDecoder(encoding)
|
||||
const dataView = new DataView(bytes);
|
||||
const decoder = new TextDecoder(encoding);
|
||||
$editor.value = decoder.decode(dataView);
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -638,10 +654,10 @@ async function deletePath(index) {
|
|||
DATA.paths[index] = null;
|
||||
if (!DATA.paths.find(v => !!v)) {
|
||||
$pathsTable.classList.add("hidden");
|
||||
$emptyFolder.textContent = dirEmptyNote;
|
||||
$emptyFolder.textContent = DIR_EMPTY_NOTE;
|
||||
$emptyFolder.classList.remove("hidden");
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async function doDeletePath(name, url, cb) {
|
||||
|
@ -674,13 +690,13 @@ async function movePath(index) {
|
|||
}
|
||||
|
||||
async function doMovePath(fileUrl) {
|
||||
const fileUrlObj = new URL(fileUrl)
|
||||
const fileUrlObj = new URL(fileUrl);
|
||||
|
||||
const prefix = DATA.uri_prefix.slice(0, -1);
|
||||
|
||||
const filePath = decodeURIComponent(fileUrlObj.pathname.slice(prefix.length));
|
||||
|
||||
let newPath = prompt("Enter new path", filePath)
|
||||
let newPath = prompt("Enter new path", filePath);
|
||||
if (!newPath) return;
|
||||
if (!newPath.startsWith("/")) newPath = "/" + newPath;
|
||||
if (filePath === newPath) return;
|
||||
|
@ -803,7 +819,7 @@ function baseUrl() {
|
|||
}
|
||||
|
||||
function baseName(url) {
|
||||
return decodeURIComponent(url.split("/").filter(v => v.length > 0).slice(-1)[0])
|
||||
return decodeURIComponent(url.split("/").filter(v => v.length > 0).slice(-1)[0]);
|
||||
}
|
||||
|
||||
function extName(filename) {
|
||||
|
@ -830,7 +846,7 @@ function getPathSvg(path_type) {
|
|||
}
|
||||
|
||||
function formatMtime(mtime) {
|
||||
if (!mtime) return ""
|
||||
if (!mtime) return "";
|
||||
const date = new Date(mtime);
|
||||
const year = date.getFullYear();
|
||||
const month = padZero(date.getMonth() + 1, 2);
|
||||
|
@ -841,17 +857,17 @@ function formatMtime(mtime) {
|
|||
}
|
||||
|
||||
function padZero(value, size) {
|
||||
return ("0".repeat(size) + value).slice(-1 * size)
|
||||
return ("0".repeat(size) + value).slice(-1 * size);
|
||||
}
|
||||
|
||||
function formatSize(size) {
|
||||
if (size == null) return [0, "B"]
|
||||
if (size == null) return [0, "B"];
|
||||
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
if (size == 0) return [0, "B"];
|
||||
const i = parseInt(Math.floor(Math.log(size) / Math.log(1024)));
|
||||
ratio = 1
|
||||
let ratio = 1;
|
||||
if (i >= 3) {
|
||||
ratio = 100
|
||||
ratio = 100;
|
||||
}
|
||||
return [Math.round(size * ratio / Math.pow(1024, i), 2) / ratio, sizes[i]];
|
||||
}
|
||||
|
@ -860,7 +876,7 @@ function formatDuration(seconds) {
|
|||
seconds = Math.ceil(seconds);
|
||||
const h = Math.floor(seconds / 3600);
|
||||
const m = Math.floor((seconds - h * 3600) / 60);
|
||||
const s = seconds - h * 3600 - m * 60
|
||||
const s = seconds - h * 3600 - m * 60;
|
||||
return `${padZero(h, 2)}:${padZero(m, 2)}:${padZero(s, 2)}`;
|
||||
}
|
||||
|
||||
|
@ -889,8 +905,8 @@ function getEncoding(contentType) {
|
|||
if (/charset/i.test(charset)) {
|
||||
let encoding = charset.split("=")[1];
|
||||
if (encoding) {
|
||||
return encoding.toLowerCase()
|
||||
return encoding.toLowerCase();
|
||||
}
|
||||
}
|
||||
return 'utf-8'
|
||||
return 'utf-8';
|
||||
}
|
||||
|
|
|
@ -115,7 +115,7 @@ fn assets_override(tmpdir: TempDir, port: u16) -> Result<(), Error> {
|
|||
let url = format!("http://localhost:{port}");
|
||||
let resp = reqwest::blocking::get(&url)?;
|
||||
assert!(resp.text()?.starts_with(&format!(
|
||||
"/__dufs_v{}__/index.js;DATA",
|
||||
"/__dufs_v{}__/index.js;<template id=\"index-data\">",
|
||||
env!("CARGO_PKG_VERSION")
|
||||
)));
|
||||
let resp = reqwest::blocking::get(&url)?;
|
||||
|
|
|
@ -65,7 +65,7 @@ pub fn tmpdir() -> TempDir {
|
|||
if *directory == DIR_ASSETS {
|
||||
tmpdir
|
||||
.child(format!("{}{}", directory, "index.html"))
|
||||
.write_str("__ASSETS_PREFIX__index.js;DATA = __INDEX_DATA__")
|
||||
.write_str("__ASSETS_PREFIX__index.js;<template id=\"index-data\">__INDEX_DATA__</template>")
|
||||
.unwrap();
|
||||
} else {
|
||||
for file in FILES {
|
||||
|
|
|
@ -62,8 +62,18 @@ pub fn encode_uri(v: &str) -> String {
|
|||
#[allow(dead_code)]
|
||||
pub fn retrieve_json(content: &str) -> Option<Value> {
|
||||
let lines: Vec<&str> = content.lines().collect();
|
||||
let line = lines.iter().find(|v| v.contains("DATA ="))?;
|
||||
let line_col = line.find("DATA =").unwrap() + 6;
|
||||
let value: Value = line[line_col..].parse().unwrap();
|
||||
let start_tag = "<template id=\"index-data\">";
|
||||
let end_tag = "</template>";
|
||||
|
||||
let line = lines.iter().find(|v| v.contains(start_tag))?;
|
||||
|
||||
let start_index = line.find(start_tag)?;
|
||||
let start_content_index = start_index + start_tag.len();
|
||||
|
||||
let end_index = line[start_content_index..].find(end_tag)?;
|
||||
let end_content_index = start_content_index + end_index;
|
||||
|
||||
let value = line[start_content_index..end_content_index].parse().ok()?;
|
||||
|
||||
Some(value)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue