161 lines
6.2 KiB
JavaScript
161 lines
6.2 KiB
JavaScript
|
|
const SETTINGS_KEY = "warpbox.upload.settings.v1";
|
||
|
|
|
||
|
|
const el = {
|
||
|
|
form: document.querySelector("#upload-form"),
|
||
|
|
fileInput: document.querySelector("#file-upload"),
|
||
|
|
dropSurface: document.querySelector("#drop-surface"),
|
||
|
|
dropzone: document.querySelector("#dropzone"),
|
||
|
|
fileList: document.querySelector("#file-list"),
|
||
|
|
queueLabel: document.querySelector("#queue-label"),
|
||
|
|
queueSize: document.querySelector("#queue-size"),
|
||
|
|
limitHint: document.querySelector("#limit-hint"),
|
||
|
|
boxSpaceText: document.querySelector("#box-space-text"),
|
||
|
|
boxSpaceBar: document.querySelector("#box-space-bar"),
|
||
|
|
overallBar: document.querySelector("#overall-bar"),
|
||
|
|
overallPercent: document.querySelector("#overall-percent"),
|
||
|
|
shareLink: document.querySelector("#share-link"),
|
||
|
|
copyButton: document.querySelector("#copy-button"),
|
||
|
|
startButton: document.querySelector("#start-button"),
|
||
|
|
statusText: document.querySelector("#status-text"),
|
||
|
|
toast: document.querySelector("#toast"),
|
||
|
|
terminal: document.querySelector("#terminal-box"),
|
||
|
|
copyCurlButton: document.querySelector("#copy-curl-button"),
|
||
|
|
docPopup: document.querySelector("#doc-popup"),
|
||
|
|
modalBackdrop: document.querySelector("#modal-backdrop"),
|
||
|
|
docPopupTitle: document.querySelector("#doc-popup-title"),
|
||
|
|
docPopupBody: document.querySelector("#doc-popup-body"),
|
||
|
|
docPopupClose: document.querySelector("#doc-popup-close"),
|
||
|
|
expiry: document.querySelector("#expiry-select"),
|
||
|
|
password: document.querySelector("#password-input"),
|
||
|
|
optionsForm: document.querySelector("#box-options-form"),
|
||
|
|
maxViews: document.querySelector("#max-views"),
|
||
|
|
boxName: document.querySelector("#box-name"),
|
||
|
|
customSlug: document.querySelector("#custom-slug"),
|
||
|
|
downloadPage: document.querySelector("#download-page"),
|
||
|
|
allowZip: document.querySelector("#allow-zip"),
|
||
|
|
allowPreview: document.querySelector("#allow-preview"),
|
||
|
|
keepFilenames: document.querySelector("#keep-filenames"),
|
||
|
|
privateBox: document.querySelector("#private-box"),
|
||
|
|
apiKeyMode: document.querySelector("#api-key-mode"),
|
||
|
|
apiKeyInput: document.querySelector("#api-key-input"),
|
||
|
|
apiKeyRow: document.querySelector("#api-key-row"),
|
||
|
|
apiKeyState: document.querySelector("#api-key-state"),
|
||
|
|
};
|
||
|
|
|
||
|
|
const uploadsEnabled = el.form?.dataset.uploadsEnabled === "true";
|
||
|
|
const defaultRetention = el.form?.dataset.defaultRetention || "10s";
|
||
|
|
const maxFileBytes = numberFromDataset(el.form?.dataset.maxFileBytes);
|
||
|
|
const maxBoxBytes = numberFromDataset(el.form?.dataset.maxBoxBytes);
|
||
|
|
const oneTimeRetentionKey = "one-time";
|
||
|
|
|
||
|
|
let files = [];
|
||
|
|
let shareUrl = "";
|
||
|
|
let uploadLocked = false;
|
||
|
|
let statusTimer = null;
|
||
|
|
let pendingDuplicateFiles = [];
|
||
|
|
let apiKeyTimer = null;
|
||
|
|
let completedImpactKeys = new Set();
|
||
|
|
let overallImpactDone = false;
|
||
|
|
|
||
|
|
function numberFromDataset(value) {
|
||
|
|
const number = Number.parseInt(value || "0", 10);
|
||
|
|
return Number.isFinite(number) && number > 0 ? number : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function formatBytes(bytes) {
|
||
|
|
if (!bytes) return "0 B";
|
||
|
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
||
|
|
let value = bytes;
|
||
|
|
let unit = 0;
|
||
|
|
while (value >= 1024 && unit < units.length - 1) {
|
||
|
|
value /= 1024;
|
||
|
|
unit += 1;
|
||
|
|
}
|
||
|
|
return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
|
||
|
|
}
|
||
|
|
|
||
|
|
const htmlEscape = window.WarpBoxUI.htmlEscape;
|
||
|
|
|
||
|
|
function shellQuote(value) {
|
||
|
|
return `'${String(value).replaceAll("'", "'\\''")}'`;
|
||
|
|
}
|
||
|
|
|
||
|
|
function totalBytes() {
|
||
|
|
return files.reduce((sum, item) => sum + item.file.size, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
function uploadedBytes() {
|
||
|
|
return files.reduce((sum, item) => sum + item.loaded, 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
function overallProgress() {
|
||
|
|
const total = totalBytes();
|
||
|
|
return total ? Math.round((uploadedBytes() / total) * 100) : 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function oversizedFiles() {
|
||
|
|
return maxFileBytes ? files.filter((item) => item.file.size > maxFileBytes) : [];
|
||
|
|
}
|
||
|
|
|
||
|
|
function isOverBoxQuota() {
|
||
|
|
return maxBoxBytes ? totalBytes() > maxBoxBytes : false;
|
||
|
|
}
|
||
|
|
|
||
|
|
function hasQuotaError() {
|
||
|
|
return isOverBoxQuota() || oversizedFiles().length > 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
function normalizedFileName(name) {
|
||
|
|
return String(name || "").trim().toLowerCase();
|
||
|
|
}
|
||
|
|
|
||
|
|
function splitNameForIncrement(name) {
|
||
|
|
const value = String(name || "file");
|
||
|
|
const dot = value.lastIndexOf(".");
|
||
|
|
if (dot > 0 && dot < value.length - 1) return [value.slice(0, dot), value.slice(dot)];
|
||
|
|
return [value, ""];
|
||
|
|
}
|
||
|
|
|
||
|
|
function nextIncrementedFileName(name, usedNames) {
|
||
|
|
const [base, ext] = splitNameForIncrement(name);
|
||
|
|
let index = 2;
|
||
|
|
let candidate = `${base} (${index})${ext}`;
|
||
|
|
while (usedNames.has(normalizedFileName(candidate))) {
|
||
|
|
index += 1;
|
||
|
|
candidate = `${base} (${index})${ext}`;
|
||
|
|
}
|
||
|
|
usedNames.add(normalizedFileName(candidate));
|
||
|
|
return candidate;
|
||
|
|
}
|
||
|
|
|
||
|
|
function makeQueuedFile(file, displayName = file.name) {
|
||
|
|
return {
|
||
|
|
file,
|
||
|
|
displayName,
|
||
|
|
loaded: 0,
|
||
|
|
uploaded: false,
|
||
|
|
failed: false,
|
||
|
|
error: "",
|
||
|
|
row: null,
|
||
|
|
boxID: "",
|
||
|
|
boxFile: null,
|
||
|
|
previewURL: file.type?.startsWith("image/") ? URL.createObjectURL(file) : "",
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function iconForFile(file) {
|
||
|
|
const filename = file.name || "";
|
||
|
|
const mimeType = file.type || "";
|
||
|
|
const extension = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
|
||
|
|
|
||
|
|
if (extension === ".exe") return "/static/img/icons/Program Files Icons - PNG/MSONSEXT.DLL_14_6-0.png";
|
||
|
|
if (mimeType.startsWith("image/")) return "/static/img/sprites/bitmap.png";
|
||
|
|
if (mimeType.startsWith("video/") || mimeType.startsWith("audio/")) return "/static/img/icons/netshow_notransm-1.png";
|
||
|
|
if (mimeType.startsWith("text/") || extension === ".md") return "/static/img/sprites/notepad_file-1.png";
|
||
|
|
if (mimeType.includes("zip") || mimeType.includes("compressed") || [".rar", ".7z", ".tar", ".gz"].includes(extension)) return "/static/img/icons/Windows Icons - PNG/zipfldr.dll_14_101-0.png";
|
||
|
|
if ([".ttf", ".otf", ".woff", ".woff2"].includes(extension)) return "/static/img/sprites/font.png";
|
||
|
|
if (extension === ".pdf") return "/static/img/sprites/journal.png";
|
||
|
|
if ([".html", ".css", ".js"].includes(extension)) return "/static/img/sprites/frame_web-0.png";
|
||
|
|
return "/static/img/icons/Windows Icons - PNG/ole2.dll_14_DEFICON.png";
|
||
|
|
}
|