Update lufi to only use Lufi API

This commit is contained in:
Booteille 2024-11-12 13:34:50 +01:00
parent e372e660cb
commit 51f3181b4a
No known key found for this signature in database
GPG Key ID: 0AB6C6CA01272646
22 changed files with 24038 additions and 470 deletions

4
.gitignore vendored
View File

@ -13,3 +13,7 @@ stop-upload
themes/*
!themes/default
!themes/default/*
.stignore
.stfolder/
TODO

View File

@ -307,7 +307,7 @@
# Set to '' to disable CSP header
# https://content-security-policy.com/ provides a good documentation about CSP.
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
# optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
# optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'"
#csp => "",
# X-Frame-Options header that will be sent by Lufi

View File

@ -1,6 +1,10 @@
Revision history for Lufi
0.08.0 ????-??-??
- Use Lufi API
- Password encryption is now done client side, through Lufi API
- Update default CSP to allow "blob:"
0.07.0 2023-12-25
- ⬆️ — Update jQuery

0
README.md Normal file → Executable file
View File

View File

@ -11,7 +11,7 @@ sub register {
if (!defined($app->config('csp')) || (defined($app->config('csp')) && $app->config('csp') ne '')) {
my $directives = {
'default-src' => "'none'",
'script-src' => "'self' 'unsafe-inline' 'unsafe-eval'",
'script-src' => "'self' 'unsafe-inline' 'unsafe-eval' blob:",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' blob:",
'media-src' => "blob:",

0
lufi-provisioning.lock Normal file
View File

View File

@ -337,7 +337,7 @@
# Set to '' to disable CSP header
# https://content-security-policy.com/ provides a good documentation about CSP.
# https://report-uri.com/home/generate provides a tool to generate a CSP header.
# optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'"
# optional, default is "base-uri 'self'; connect-src 'self' ws://YOUR_HOST; default-src 'none'; font-src 'self'; form-action 'self'; frame-ancestors 'none'; img-src 'self' blob:; media-src blob:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'"
#csp => "",
# X-Frame-Options header that will be sent by Lufi

File diff suppressed because one or more lines are too long

View File

@ -4,9 +4,9 @@ var entityMap = {
"&": "&",
"<": "&lt;",
">": "&gt;",
'"': '&quot;',
"'": '&#39;',
"/": '&#x2F;'
'"': "&quot;",
"'": "&#39;",
"/": "&#x2F;",
};
function escapeHtml(string) {
@ -18,17 +18,20 @@ function changeLang() {
window.location = langUrl + $(this).val();
}
function formatDate(unixTimestamp) {
return new Date(unixTimestamp * 1000).toLocaleString(window.navigator.language, {
year: 'numeric',
month: 'long',
day: 'numeric',
weekday: 'long',
hour: '2-digit',
minute: '2-digit',
})
return new Date(unixTimestamp * 1000).toLocaleString(
window.navigator.language,
{
year: "numeric",
month: "long",
day: "numeric",
weekday: "long",
hour: "2-digit",
minute: "2-digit",
}
);
}
$(document).ready(function () {
$('select').material_select();
$(".select-lang select").on('change', changeLang);
$(".select-lang-mobile select").on('change', changeLang);
$("select").material_select();
$(".select-lang select").on("change", changeLang);
$(".select-lang-mobile select").on("change", changeLang);
});

View File

@ -17,14 +17,17 @@ const filesizeDOM = document.getElementById("filesize");
document.addEventListener("DOMContentLoaded", () => {
let go = true;
filesizeDOM.innerHTML = filesize(filesizeDOM.attributes.getNamedItem("data-filesize").value, {
filesizeDOM.innerHTML = filesize(
filesizeDOM.attributes.getNamedItem("data-filesize").value,
{
base: 10,
});
}
);
if (isPasswordNeeded()) {
go = false;
passwordDOM.focus()
passwordDOM.focus();
onPasswordEvents();
}
@ -34,12 +37,12 @@ document.addEventListener("DOMContentLoaded", () => {
}
});
const isPasswordNeeded = () => document.querySelectorAll("#file_pwd").length === 1
const isPasswordNeeded = () =>
document.querySelectorAll("#file_pwd").length === 1;
const startDownload = () => {
warnOnReload();
lufi
.download(window.location, passwordDOM?.value)
.andThen((job) => {
@ -53,7 +56,7 @@ const startDownload = () => {
job.terminate();
filesizeDOM.parentElement.append(abortedDOM);
warnOnReload(false)
warnOnReload(false);
document.getElementById("reloadLocation").onclick = (e) => {
e.preventDefault();
@ -65,8 +68,8 @@ const startDownload = () => {
})
.mapErr((error) => {
addAlert(error.message);
warnOnReload(false)
remove(["abort"])
warnOnReload(false);
remove(["abort"]);
})
.andThen((job) => {
notify(i18n.fileDownloaded, job.lufiFile.name);
@ -115,20 +118,22 @@ const remove = (elements) => {
if (document.getElementById(id)) {
document.getElementById(id).remove();
} else {
console.error(`${id} does not exist`)
console.error(`${id} does not exist`);
}
});
};
const onPasswordEvents = () => {
const callback = () => {
document.getElementsByClassName("file-progress")[0].classList.remove("hide");
document
.getElementsByClassName("file-progress")[0]
.classList.remove("hide");
document.getElementsByClassName("file-abort")[0].classList.remove("hide");
passwordDOM.parentElement.parentElement.classList.add("hide");
startDownload();
}
};
document.getElementById("go").onclick = () => {
callback();
@ -138,8 +143,8 @@ const onPasswordEvents = () => {
if (event.key === "Enter") {
callback();
}
}
}
};
};
// Something's wring
const addAlert = (msg) => {
@ -156,7 +161,7 @@ const addAlert = (msg) => {
<strong>${msg}</strong>
</div>
</div>`;
}
};
const warnOnReload = (toWarn = true) => {
if (toWarn) {
@ -164,14 +169,15 @@ const warnOnReload = (toWarn = true) => {
} else {
window.onbeforeunload = null;
}
}
};
const updateProgress = (lufiFile) => {
// Update loading text
loadingDOM.textContent = i18n.loading.replace(/XX1/, lufiFile.chunksReady);
// Update progress bar
const percent = Math.round((1000 * lufiFile.chunksReady) / lufiFile.totalChunks) / 10;
const percent =
Math.round((1000 * lufiFile.chunksReady) / lufiFile.totalChunks) / 10;
const wClass = percent.toString().replace(".", "-");
const pb = document.getElementById("pb");
@ -179,59 +185,61 @@ const updateProgress = (lufiFile) => {
pb.attributes.getNamedItem("aria-valuenow").value = percent;
document.getElementById("pbt").innerHTML = `${percent}%`;
}
};
const showZipContent = (blob) => {
const showZipContentDOM = document.getElementById('showZipContent');
const showZipContentDOM = document.getElementById("showZipContent");
const showZipContentDOMListener = () => {
JSZip.loadAsync(blob)
.then((zip) => {
JSZip.loadAsync(blob).then((zip) => {
const newElement = document.createElement("div");
let innerHTML = `<h3>${i18n.zipContent}</h3><ul>`;
zip.forEach(function (_relativePath, zipEntry) {
innerHTML += `<li>
${escapeHtml(zipEntry.name)}
(${filesize(zipEntry._data.uncompressedSize, { base: 10 })})
(${filesize(zipEntry._data.uncompressedSize, {
base: 10,
})})
<a href="#"
download="${escapeHtml(zipEntry.name)}"
class="download-zip-content"
title="${i18n.download}">
<i class="mdi-file-file-download"></i>
</a>
</li>`
</li>`;
});
innerHTML += '</ul>';
innerHTML += "</ul>";
newElement.innerHTML = innerHTML
newElement.innerHTML = innerHTML;
pbd.append(newElement);
console.debug()
console.debug();
document.querySelectorAll('.download-zip-content').forEach((element) => {
document.querySelectorAll(".download-zip-content").forEach((element) => {
const elementListener = (e) => {
e.preventDefault();
var filename = element.getAttribute('download');
zip.files[filename].async('blob').then((blob) => {
element.removeEventListener('click', elementListener);
element.setAttribute('href', URL.createObjectURL(blob));
var filename = element.getAttribute("download");
zip.files[filename].async("blob").then((blob) => {
element.removeEventListener("click", elementListener);
element.setAttribute("href", URL.createObjectURL(blob));
element.click();
});
};
element.addEventListener('click', elementListener);
element.addEventListener("click", elementListener);
showZipContentDOM.style.display = "none";
showZipContentDOM.removeEventListener('click', showZipContentDOMListener);
showZipContentDOM.removeEventListener(
"click",
showZipContentDOMListener
);
});
});
})
};
showZipContentDOM.onclick = showZipContentDOMListener
}
showZipContentDOM.onclick = showZipContentDOMListener;
};

View File

@ -1,39 +1,32 @@
// vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
// Add item to localStorage
function addItem(item) {
var files = localStorage.getItem(`${window.prefix}files`);
if (files === null) {
files = new Array();
} else {
files = JSON.parse(files);
}
const addItem = (item) => {
const files = JSON.parse(localStorage.getItem(`${window.prefix}files`)) || [];
files.push(item);
localStorage.setItem(`${window.prefix}files`, JSON.stringify(files));
}
};
function delItem(name) {
var files = localStorage.getItem(`${window.prefix}files`);
if (files === null) {
files = new Array();
} else {
files = JSON.parse(files);
}
var i;
const delItem = (name) => {
const files = JSON.parse(localStorage.getItem(`${window.prefix}files`)) || [];
let i;
for (i = 0; i < files.length; i++) {
if (files[i].short === name) {
files.splice(i, 1);
}
}
localStorage.setItem(`${window.prefix}files`, JSON.stringify(files));
}
};
function itemExists(name) {
var files = localStorage.getItem(`${window.prefix}files`);
const itemExists = (name) => {
let files = localStorage.getItem(`${window.prefix}files`);
if (files === null) {
return false;
} else {
files = JSON.parse(files);
var i;
let i;
for (i = 0; i < files.length; i++) {
if (files[i].short === name) {
return true;
@ -41,72 +34,96 @@ function itemExists(name) {
}
return false;
}
}
};
function invertSelection(event) {
const invertSelection = (event) => {
event.preventDefault();
$('input[type="checkbox"]').each(function() {
var el = $(this);
el.click();
if (el.attr('data-checked') && el.attr('data-checked') === 'data-checked') {
el.attr('data-checked', null);
document.querySelectorAll('input[type="checkbox"]').forEach((element) => {
element.click();
if (element.getAttribute("data-checked") === "data-checked") {
element.setAttribute("data-checked", null);
} else {
el.attr('data-checked', 'data-checked');
element.setAttribute("data-checked", "data-checked");
}
});
evaluateMassDelete();
}
};
function purgeExpired(event) {
const purgeExpired = (event) => {
event.preventDefault();
var files = JSON.parse(localStorage.getItem(`${window.prefix}files`));
files.forEach(function(element, index, array) {
$.ajax({
url: counterURL,
method: 'POST',
dataType: 'json',
data: {
short: element.short,
token: element.token
const files = JSON.parse(localStorage.getItem(`${window.prefix}files`));
files.forEach(function (element) {
fetch(counterURL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
success: function(data, textStatus, jqXHR) {
body: new URLSearchParams({
short: element.short,
token: element.token,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("Request error.");
}
return response.json();
})
.then((data) => {
if (data.success) {
if (data.deleted) {
$(`#count-${data.short}`).parent().remove();
const elementToRemove = document.querySelector(
`#count-${data.short}`
);
if (elementToRemove) {
elementToRemove.parentElement.remove();
}
delItem(data.short);
}
}
}
});
});
}
};
function exportStorage(event) {
const exportStorage = (event) => {
event.preventDefault();
var a = $('<a id="data-json">');
a.hide();
$('body').append(a);
var storageData = [localStorage.getItem(`${window.prefix}files`)];
var exportFile = new Blob(storageData, {type : 'application/json'});
var url = window.URL.createObjectURL(exportFile);
const a = document.createElement("a");
a.id = "data-json";
a.attr('href', url);
a.attr('download', 'data.json');
$('#data-json')[0].click();
$('#data-json').remove();
}
a.style.display = "none";
function importStorage(f) {
var reader = new FileReader();
reader.addEventListener("loadend", function() {
document.body.append(a);
const storageData = [localStorage.getItem(`${window.prefix}files`)];
const exportFile = new Blob(storageData, { type: "application/json" });
const url = window.URL.createObjectURL(exportFile);
a.setAttribute("href", url);
a.setAttribute("download", "data.json");
a.click();
a.remove();
};
const importStorage = (f) => {
let reader = new FileReader();
reader.addEventListener("loadend", () => {
try {
var newFiles = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(reader.result)));
var i;
var hasImported = 0;
const newFiles = JSON.parse(
String.fromCharCode.apply(null, new Uint8Array(reader.result))
);
let i;
let hasImported = 0;
for (i = 0; i < newFiles.length; i++) {
var item = newFiles[i];
const item = newFiles[i];
if (validURL(item.url) && !itemExists(item.short)) {
addItem(item);
hasImported++;
@ -115,103 +132,117 @@ function importStorage(f) {
populateFilesTable();
Materialize.toast(i18n.importProcessed);
} catch(err) {
} catch (err) {
alert(err);
}
});
reader.readAsArrayBuffer(f[0]);
}
};
function validURL(str) {
const validURL = (str) => {
try {
var url = new URL(str);
if (url.host) {
return true;
} else {
return new URL(str).host ? true : false;
} catch (e) {
return false;
}
} catch(e) {
return false;
}
}
};
function delFile() {
var dlink = $(this).attr('data-dlink');
var short = $(this).attr('data-short');
$.ajax({
url: dlink,
method: 'GET',
data: {
_format: 'json'
},
success: function(data) {
const delFile = (element) => {
const deleteUrl = new URL(element.getAttribute("data-dlink"));
const short = element.getAttribute("data-short");
deleteUrl.searchParams.append("_format", "json");
fetch(deleteUrl, {
method: "GET",
})
.then((response) => {
if (!response.ok) {
throw new Error("Network error while deleting file");
}
return response.json();
})
.then((data) => {
if (data.success) {
$(`#row-${short}`).remove();
document.getElementById(`row-${short}`).remove();
delItem(short);
} else {
alert(data.msg);
}
evaluateMassDelete();
},
error: function() {
},
complete: function() {
}
});
}
};
function evaluateMassDelete() {
if ($('input[data-checked="data-checked"]').length > 0) {
$('#mass-delete').removeAttr('disabled');
$('#mass-delete').removeClass('disabled');
const evaluateMassDelete = () => {
const massDeleteDOM = document.getElementById("mass-delete");
if (
document.querySelectorAll('input[data-checked="data-checked"]').length > 0
) {
massDeleteDOM.removeAttribute("disabled");
massDeleteDOM.classList.remove("disabled");
} else {
$('#mass-delete').attr('disabled');
$('#mass-delete').addClass('disabled');
massDeleteDOM.setAttribute("disabled", "disabled");
massDeleteDOM.classList.add("disabled");
}
}
};
function massDelete(event) {
const massDelete = (event) => {
event.preventDefault();
$('input[data-checked="data-checked"]').each(delFile);
}
document
.querySelectorAll('input[data-checked="data-checked"]')
.forEach(delFile);
};
function populateFilesTable() {
$('#myfiles').empty();
const populateFilesTable = () => {
const myFilesDOM = document.getElementById("myfiles");
var files = localStorage.getItem(`${window.prefix}files`);
myFilesDOM.innerHTML = "";
let files = localStorage.getItem(`${window.prefix}files`);
if (files === null) {
var filesWithoutPrefix = localStorage.getItem('files');
var filesWithoutPrefix = localStorage.getItem("files");
if (filesWithoutPrefix !== null) {
if (window.confirm(i18n.importFilesWithoutPrefix)) {
localStorage.setItem(`${window.prefix}files`, filesWithoutPrefix);
files = JSON.parse(filesWithoutPrefix);
} else {
localStorage.setItem(`${window.prefix}files`, JSON.stringify([]));
files = new Array();
files = [];
}
} else {
files = new Array();
files = [];
}
} else {
files = JSON.parse(files);
}
files.sort(function(a, b) {
files.sort(function (a, b) {
if (a.created_at < b.created_at) {
return -1;
} else if (a.created_at > b.created_at) {
return 1;
} else {
return 0
return 0;
}
});
files.forEach(function(element, index, array) {
var del_view = (element.del_at_first_view) ? '<i class="small mdi-action-done"></i>' : '<i class="small mdi-navigation-close"></i>';
var dlink = `${actionURL}d/${element.short}/${element.token}`;
var limit = (element.delay === 0) ? i18n.noExpiration : formatDate(element.delay * 86400 + element.created_at);
var created_at = formatDate(element.created_at);
var tr = $(`<tr id="row-${element.short}">`);
tr.html(`<td class="center-align">
files.forEach(function (element) {
const del_view = element.del_at_first_view
? '<i class="small mdi-action-done"></i>'
: '<i class="small mdi-navigation-close"></i>';
const dlink = `${actionURL}d/${element.short}/${element.token}`;
const limit =
element.delay === 0
? i18n.noExpiration
: formatDate(element.delay * 86400 + element.created_at);
const created_at = formatDate(element.created_at);
const tr = document.createElement("tr");
tr.id = `row-${element.short}`;
tr.innerHTML = `<td class="center-align">
<input type="checkbox"
id="check-${element.short}"
data-short="${element.short}"
@ -249,47 +280,72 @@ function populateFilesTable() {
</a>
</td>
<td class="center-align">
<a href="${actionURL}m?links=[&quot;${element.short}&quot;]"
<a href="${actionURL}m?links=[&quot;${
element.short
}&quot;]"
class="classic"><i class="small mdi-communication-email"></i></a>
</td>`);
$('#myfiles').append(tr);
$(`#del-${element.short}`).on('click', delFile);
$(`label[for="check-${element.short}"]`).on('click', function(){
if ($(`#check-${element.short}`).attr('data-checked') && $(`#check-${element.short}`).attr('data-checked') === 'data-checked') {
$(`#check-${element.short}`).attr('data-checked', null);
} else {
$(`#check-${element.short}`).attr('data-checked', 'data-checked');
}
evaluateMassDelete();
});
</td>`;
$.ajax({
url: counterURL,
method: 'POST',
dataType: 'json',
data: {
short: element.short,
token: element.token
},
success: function(data, textStatus, jqXHR) {
if (data.success) {
$(`#count-${data.short}`).html(data.counter);
if (data.deleted) {
$(`#count-${data.short}`).parent().addClass('purple lighten-4');
myFilesDOM.append(tr);
document.getElementById(`del-${element.short}`).onclick = (event) =>
delFile(event.target.parentElement);
document.querySelector(`label[for="check-${element.short}"]`).onclick = (
event
) => {
const checkDOM = document.getElementById(`check-${element.short}`);
if (checkDOM.getAttribute("data-checked") === "data-checked") {
checkDOM.setAttribute("data-checked", null);
} else {
checkDOM.setAttribute("data-checked", "data-checked");
}
evaluateMassDelete();
};
fetch(counterURL, {
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
},
body: new URLSearchParams({
short: element.short,
token: element.token,
}),
})
.then((response) => {
if (!response.ok) {
throw new Error("Request error.");
}
return response.json();
})
.then((data) => {
if (data.success) {
if (data.deleted) {
const countDOM = document.getElementById(`count-${data.short}`);
countDOM.innerHTML = data.counter;
if (data.deleted) {
countDOM.parentElement.classList.add("purple", "lighten-4");
} else {
alert(data.msg);
$(`#count-${data.short}`).parent().remove();
countDOM.parentElement.remove();
if (data.missing) {
delItem(data.short);
}
}
}
}
});
});
}
};
function clickImport(event) {
const clickImport = (event) => {
event.preventDefault();
$('#import').click();
}
document.getElementById("import").click();
};

View File

@ -1,24 +1,25 @@
// vim:set sw=4 ts=4 sts=4 ft=javascript expandtab:
import { lufi, okAsync, errAsync } from "/js/lufi.js";
import {
lufi,
okAsync,
ResultAsync,
isSecureContext,
CryptoAlgorithm,
} from "/js/lufi.js";
// Cancelled files indexes
window.cancelled = [];
// Set websocket
// window.ws = spawnWebsocket(0, function () {
// return null;
// });
// Use slice of 0.75MB
window.sliceLength = 750000;
// Global zip objects for currently created zip file
window.zip = null;
window.zipSize = 0;
// Init the list of files (used by LDAP invitation feature)
window.filesURLs = [];
let archiveEntries;
// Copy a link to clipboard
function copyToClipboard(txt) {
var textArea = document.createElement("textarea");
const textArea = document.createElement("textarea");
textArea.className = "textarea-hidden";
textArea.value = txt;
@ -56,7 +57,16 @@ function copyAllToClipboard(event) {
}
// Add item to localStorage
function addItem(name, url, size, del_at_first_view, created_at, delay, short, token) {
function addItem(
name,
url,
size,
del_at_first_view,
created_at,
delay,
short,
token
) {
let files = localStorage.getItem(`${window.prefix}files`);
files = JSON.parse(files) || [];
@ -79,7 +89,10 @@ function destroyBlock(clientKey) {
if (document.querySelectorAll(".link-input").length === 0) {
document.getElementById("misc").innerHTML = "";
if (document.querySelectorAll("#results li").length === 0 && window.fileList === null) {
if (
document.querySelectorAll("#results li").length === 0 &&
window.fileList === null
) {
document.getElementById("results").style.display = "none";
}
} else {
@ -93,7 +106,8 @@ function firstViewClicking() {
.getElementById("first-view")
.setAttribute(
"data-checked",
document.getElementById("first-view").getAttribute("data-checked") === "data-checked"
document.getElementById("first-view").getAttribute("data-checked") ===
"data-checked"
? null
: "data-checked"
);
@ -101,9 +115,11 @@ function firstViewClicking() {
// When clicking on zip checkbox
function zipClicking() {
if (document.getElementById("zip-files").getAttribute("data-checked") === "data-checked") {
if (
document.getElementById("zip-files").getAttribute("data-checked") ===
"data-checked"
) {
window.zipSize = 0;
window.zip = null;
document.getElementById("zip-files").removeAttribute("data-checked");
document.getElementById("zipname").value = "documents.zip";
document.getElementById("zipname-input").classList.add("hide");
@ -112,7 +128,9 @@ function zipClicking() {
document.getElementById("zip-parts").innerHTML = "";
document.getElementById("delete-day").removeAttribute("disabled");
} else {
document.getElementById("zip-files").setAttribute("data-checked", "data-checked");
document
.getElementById("zip-files")
.setAttribute("data-checked", "data-checked");
document.getElementById("zipname-input").classList.remove("hide");
document.getElementById("zip-size").innerText = filesize(window.zipSize);
}
@ -120,14 +138,10 @@ function zipClicking() {
// Get the zip file name
const getZipname = () => {
var zipname = document.getElementById("zipname").value || "documents.zip";
let zipname = document.getElementById("zipname").value || "documents.zip";
if (!zipname.endsWith(".zip")) {
if (zipname.endsWith(".")) {
zipname += "zip";
} else {
zipname += ".zip";
}
zipname += zipname.endsWith(".") ? "zip" : ".zip";
}
return escapeHtml(zipname);
@ -141,8 +155,8 @@ const updateZipname = () => {
// Create blob from zip
const uploadZip = (e) => {
e.preventDefault();
var delay = document.getElementById("delete-day");
var del_at_first_view = document.getElementById("first-view");
const delay = document.getElementById("delete-day");
const del_at_first_view = document.getElementById("first-view");
document.getElementById("zip-files").disabled = true;
document.getElementById("file-browser-button").disabled = true;
document.getElementById("file-browser-span").classList.add("disabled");
@ -150,10 +164,14 @@ const uploadZip = (e) => {
document.getElementById("zip-parts").textContent = "";
document.getElementById("zip-compressing").classList.remove("hide");
window.zip.generateAsync({ type: "blob" }).then((zipFile) => {
const zipname = getZipname();
lufi
.compress(archiveEntries, zipname)
.andThen((zipFile) => {
// if '#zipping' is hidden, the zipping has been aborted
if (!document.getElementById("zipping").classList.contains("hide")) {
window.zip = null;
document.getElementById("zipping").classList.add("hide");
document.getElementById("files").classList.remove("m6");
document.getElementById("files").classList.add("m12");
@ -162,21 +180,36 @@ const uploadZip = (e) => {
document.getElementById("uploadZip").classList.remove("hide");
document.getElementById("results").style.display = "block";
document.getElementById("zip-files").disabled = false;
document.getElementById("zip-files").removeAttribute("data-checked");
document.getElementById("zip-files").checked = false;
var zipname = getZipname();
const password = document.getElementById("file_pwd").value;
var file = new File([zipFile], zipname, { type: "application/zip" });
Materialize.toast(
i18n.enqueued.replace("XXX", zipname),
3000,
"teal accent-3"
);
Materialize.toast(i18n.enqueued.replace("XXX", zipname), 3000, "teal accent-3");
window.fileList = window.fileList || [zipFile];
window.fileList = window.fileList || [file];
startUpload(
[zipFile],
delay.value,
del_at_first_view.checked,
true,
zipname,
password
);
startUpload(file, delay.value, del_at_first_view.checked, true, password);
archiveEntries = undefined;
}
document.getElementById("file-browser-button").disabled = false;
document.getElementById("file-browser-span").classList.remove("disabled");
});
return okAsync(undefined);
})
.orElse((error) => console.error(error.message));
};
// Update the mail link
@ -239,8 +272,7 @@ const handleDrop = (evt) => {
evt.stopPropagation();
evt.preventDefault();
var f = evt.dataTransfer.files; // FileList object
handleFiles(f);
handleFiles(evt.dataTransfer.files);
};
const handleDragOver = (evt) => {
@ -259,7 +291,9 @@ const bindDropZone = () => {
document.getElementById("file-browser-span").classList.add("cyan");
document.getElementById("file-browser-button").disabled = false;
document.getElementById("file-browser-button").addEventListener("change", (e) => {
document
.getElementById("file-browser-button")
.addEventListener("change", (e) => {
handleFiles(e.target.files);
});
};
@ -278,28 +312,51 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
document.querySelector('label[for="first-view"]').addEventListener("click", firstViewClicking);
document.querySelector('label[for="zip-files"]').addEventListener("click", zipClicking);
document
.querySelector('label[for="first-view"]')
.addEventListener("click", firstViewClicking);
document
.querySelector('label[for="zip-files"]')
.addEventListener("click", zipClicking);
document.getElementById("zipname").addEventListener("input", updateZipname);
document.getElementById("uploadZip").addEventListener("click", uploadZip);
document.getElementById("reset-zipping").addEventListener("click", () => {
window.zip = null;
archiveEntries = undefined;
document.querySelector('label[for="zip-files"]').click();
document.getElementById("zip-files").disabled = false;
document.getElementById("zip-files").removeAttribute("data-checked");
document.getElementById("zip-compressing").classList.add("hide");
document.getElementById("file-browser-button").disabled = false;
document.getElementById("file-browser-span").classList.remove("disabled");
document.getElementById("files").classList.remove("m6").classList.add("m12");
document.getElementById("files").classList.replace("m6", "m12");
});
});
const startUpload = (file, delay, delAtFirstView, isZipped, password) => {
const startUpload = (
files,
delay,
delAtFirstView,
isZipped,
zipName,
password
) => {
let clientKey;
lufi
.upload(window.location, file, delay, delAtFirstView, isZipped, password)
.andThen((job) => {
return lufi
.upload(
window.location,
files,
delay,
delAtFirstView,
isZipped,
zipName,
password,
isSecureContext() ? CryptoAlgorithm.WebCrypto : CryptoAlgorithm.Sjcl
)
.andThen((jobs) =>
ResultAsync.combine(
jobs.map((job) => {
clientKey = job.lufiFile.keys.client;
createUploadBox(job);
@ -308,8 +365,8 @@ const startUpload = (file, delay, delAtFirstView, isZipped, password) => {
updateProgressBar(job.lufiFile);
});
return job.waitForCompletion();
})
return job
.waitForCompletion()
.andThen((job) => {
notify(i18n.fileUploaded, job.lufiFile.name);
@ -360,17 +417,24 @@ const startUpload = (file, delay, delAtFirstView, isZipped, password) => {
sendFilesURLs();
}
});
})
)
);
};
const handleFiles = (files = []) => {
const filesArray = Array.from(files);
const isZipped =
document.getElementById("zip-files").getAttribute("data-checked") === "data-checked";
document.getElementById("zip-files").getAttribute("data-checked") ===
"data-checked";
document.body.style.cursor = "wait";
if (!isZipped) {
const delay = document.getElementById("delete-day").value;
const delAtFirstView =
document.getElementById("first-view").getAttribute("data-checked") === "data-checked";
document.getElementById("first-view").getAttribute("data-checked") ===
"data-checked";
const password = document.getElementById("file_pwd").value;
if (window.fileList === undefined || window.fileList === null) {
@ -392,43 +456,43 @@ const handleFiles = (files = []) => {
window.fileList = window.fileList.concat(filesArray); // Concatenate new files
}
filesArray.forEach((file) => {
startUpload(file, delay, delAtFirstView, isZipped, password);
});
document.body.style.cursor = "default";
startUpload(filesArray, delay, delAtFirstView, isZipped, password);
} else {
window.zip = window.zip || new JSZip();
lufi
.addFilesToArchive(filesArray, archiveEntries)
.andThen((entries) => {
archiveEntries = entries;
document.getElementById("zipping").classList.remove("hide");
const filesElement = document.getElementById("files");
filesElement.classList.remove("m12");
filesElement.classList.add("m6");
filesElement.classList.replace("m12", "m6");
for (let i = 0; i < files.length; i++) {
const element = files.item(i);
let filename = element.name;
const originalName = filename;
let counter = 0;
const zipPartsDOM = document.getElementById("zip-parts");
// Ensure unique filename
while (window.zip.files[filename] !== undefined) {
counter++;
const extensionIndex = originalName.lastIndexOf(".");
const baseName = originalName.substring(0, extensionIndex);
const extension = originalName.substring(extensionIndex);
filename = `${baseName}_(${counter})${extension}`;
zipPartsDOM.replaceChildren();
for (const [name, file] of Object.entries(archiveEntries)) {
window.zipSize += file.length;
const listItemDOM = document.createElement("li");
listItemDOM.innerHTML = `${escapeHtml(name)} (${filesize(
file.length
)})`;
zipPartsDOM.appendChild(listItemDOM);
}
// Add the file to the zip
window.zip.file(filename, element);
window.zipSize += element.size;
document.getElementById("zip-size").textContent = filesize(
window.zipSize
);
// Update DOM
document.getElementById("zip-size").textContent = filesize(window.zipSize);
const zipPartsElement = document.getElementById("zip-parts");
const listItem = document.createElement("li");
listItem.innerHTML = `${escapeHtml(filename)} (${filesize(element.size)})`;
zipPartsElement.appendChild(listItem);
}
document.body.style.cursor = "default";
return okAsync(undefined);
})
.orElse((error) => console.error(error.message));
}
};
@ -450,7 +514,9 @@ const createUploadBox = (job) => {
<div class="card-content">
<span class="card-title"
id="name-${clientKey}">${job.lufiFile.name}</span>
<span id="size-${clientKey}">(${filesize(job.lufiFile.size)})</span>
<span id="size-${clientKey}">(${filesize(
job.lufiFile.size
)})</span>
<p id="parts-${clientKey}"></p>
</div>
<div class="progress">
@ -501,7 +567,9 @@ const uploadBoxComplete = (lufiFile) => {
const limit =
lufiFile.delay === 0
? i18n.noLimit
: `${i18n.expiration} ${formatDate(lufiFile.delay * 86400 + lufiFile.createdAt)}`;
: `${i18n.expiration} ${formatDate(
lufiFile.delay * 86400 + lufiFile.createdAt
)}`;
if (!isGuest) {
nameDOM.innerHTML += `${sizeDOM.innerHTML} <a href="${actionURL}m?links=${links}"><i class="mdi-communication-email"></i></a><br>${limit}`;
@ -542,7 +610,9 @@ const uploadBoxComplete = (lufiFile) => {
p1.appendChild(newDivDOM);
// Copy URL to clipboard
document.getElementById(`copyurl-${clientKey}`).addEventListener("click", (e) => {
document
.getElementById(`copyurl-${clientKey}`)
.addEventListener("click", (e) => {
e.preventDefault();
copyToClipboard(downloadUrl);
});
@ -562,14 +632,17 @@ const uploadBoxComplete = (lufiFile) => {
<a href="#" id="copyall" class="btn btn-info">${i18n.copyAll}</a>
<a id="mailto" href="${actionURL}m?links=${links}" class="btn btn-info">${i18n.mailTo}</a>
`;
document.getElementById("copyall").addEventListener("click", copyAllToClipboard);
document
.getElementById("copyall")
.addEventListener("click", copyAllToClipboard);
} else {
updateMailLink();
}
};
const updateProgressBar = (lufiFile) => {
const percent = Math.round((1000 * lufiFile.chunksReady) / lufiFile.totalChunks) / 10;
const percent =
Math.round((1000 * lufiFile.chunksReady) / lufiFile.totalChunks) / 10;
const wClass = percent.toString().replace(".", "-");
const dp = document.getElementById(`progress-${lufiFile.keys.client}`);
@ -578,5 +651,7 @@ const updateProgressBar = (lufiFile) => {
dp.classList.add(`width-${wClass}`);
dp.setAttribute("aria-valuenow", percent);
document.getElementById(`parts-${lufiFile.keys.client}`).innerHTML = `${percent.toFixed(1)}%`;
document.getElementById(
`parts-${lufiFile.keys.client}`
).innerHTML = `${percent.toFixed(1)}%`;
};

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,3 @@
$(document).ready(function() {
$(document).ready(function () {
$(".button-collapse").sideNav();
});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -13,10 +13,10 @@ var i18n = {
};
$(document).ready(function() {
populateFilesTable();
$('#invertSelection').on('click', invertSelection);
$('#exportStorage').on('click', exportStorage);
$('#purgeExpired').on('click', purgeExpired);
$('#clickImport').on('click', clickImport);
$('#mass-delete').on('click', massDelete);
document.getElementById('invertSelection').onclick = (event) => invertSelection(event);
document.getElementById('exportStorage').onclick = (event) => exportStorage(event);
document.getElementById('purgeExpired').onclick = (event) => purgeExpired(event);
document.getElementById('clickImport').onclick = (event) => clickImport(event);
document.getElementById('mass-delete').onclick = (event) => massDelete(event);
});