F diff --git a/.gitignore b/.gitignore
--- a/.gitignore
+++ b/.gitignore
screen
files
+ node_modules
php/custom_configuration.php
F diff --git a/build/bundle.js b/build/bundle.js
new file mode 100644
--- /dev/null
+++ b/build/bundle.js
+ // This should be only set to false for debugging purposes
+ // If it's set to false, the upload requests will be synchronous
+ // and you will be able to see PHP's echo output in the browser
+ var FORM_ASYNC = true;
+ // A FileView is an entry inside the explorer window
+ var FileView = /** @class */ (function () {
+ function FileView(filename, wnd, mimetype, is_directory, write_permissions) {
+ this.filename = filename;
+ this.wnd = wnd;
+ this.visuals = null; // The DOM object with the icon and the filenam text
+ this.mimetype = mimetype;
+ this.is_directory = is_directory;
+ this.write_permissions = write_permissions;
+ }
+ FileView.prototype.full_path = function () {
+ return path_combine(get_path(this.wnd), this.filename);
+ };
+ return FileView;
+ }());
+ // An array of all fileviews currently open
+ var OurWindow = /** @class */ (function () {
+ function OurWindow(pwd) {
+ this.pwd = pwd; // pwd = [ "Folder1", "Folder2" ] means the current directory of that window is /Folder1/Folder2
+ this.visuals = null; // The DOM object
+ this.h2 = null; // The titlebar of the window
+ this.fileview = null;
+ this.files = [];
+ this.txt_editor = null; // For editable text files, this is the DOM element the user can edit
+ }
+ return OurWindow;
+ }());
+ // An array with all the windows on the screen
+ var windows = [];
+ // The focused window
+ var focused_window = null;
+ // Those all belong to the hidden file upload form
+ var upload_form = document.getElementById("upload_form");
+ var filename_input = document.getElementById("filename");
+ var override_input = document.getElementById("override_input");
+ var upload_parent_directory = document.getElementById("upload_parent_directory");
+ var the_file = document.getElementById("the_file");
+ // If this is set to true, requests to uploads.php will be sent with the "override" flag
+ // which will override existing files with the same name
+ var override_file = false;
+ var override_file_filename = "";
+ var override_file_path = "";
+ var open_file = null;
+ // Some elements have custom right click context menus
+ // If there's a custom context menu active, this will be it
+ var context_menu = null;
+ // If we're currently dragging something (a window or a file), this is the DOM object we're dragging
+ // dragging_offset_x and dragging_offset_y are the difference between the objec's top left point and the cursor position
+ // this is then added to the cursor position when the mouse is moved
+ var dragging = null;
+ var dragging_offset_x = 0, dragging_offset_y = 0;
+ // If we have pressed down a window's title bar but haven't yet started dragging it, it's the drag candidate
+ // This is needed because we don't yet know whether we want to start dragging the window or click an element in the titlebar
+ // Once the mouse has moved sufficiently far away from dragging_candidate_x or y, we start dragging
+ var dragging_candidate = null;
+ var dragging_candidate_x, dragging_candidate_y;
+ // If we're dragging a fileview, this is set to the fileview class instance itself, because 'dragging' is just a DOM object
+ // The placeholder is a dummy DIV that we insert in the object's place on the grid to keep things nicely aligned
+ var dragging_fileview;
+ var dragging_placeholder = null;
+ // Windows have a z-index. When we click a window it is sent to the top this will be its new z-index
+ // We then increment the depth, and the next window we click will go on top of the current one
+ var depth = 20;
+ function main() {
+ // Create a window that looks at the root directory
+ var root_window = make_window([], false);
+ // Focus that window and load the directory
+ focus_window(root_window);
+ openfile(true, root_window);
+ }
+ function focus_window(wnd) {
+ // Unfocus the old window
+ if (focused_window)
+ focused_window.visuals.classList.remove('focus');
+ focused_window = wnd;
+ // And focus the new one!
+ if (wnd) {
+ wnd.visuals.classList.add('focus');
+ wnd.visuals.style.zIndex = (depth++).toString();
+ }
+ }
+ // Delete the focused window
+ function delete_window(wnd) {
+ var index = windows.indexOf(wnd);
+ if (index >= 0)
+ windows.splice(index, 1);
+ wnd.visuals.parentNode.removeChild(wnd.visuals);
+ if (wnd == focused_window)
+ focused_window = null;
+ }
+ // Create a right click context menu
+ function context(e, entries) {
+ if (context_menu)
+ context_menu.remove();
+ context_menu = mk(document.body, 'ul', 'context');
+ context_menu.onmousedown = function (e) {
+ e.stopPropagation();
+ };
+ context_menu.onclick = function (_e) {
+ context_menu.remove();
+ context_menu = null;
+ };
+ context_menu.style.left = e.clientX + "px";
+ context_menu.style.top = e.clientY + "px";
+ for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) {
+ var e_1 = entries_1[_i];
+ var li = document.createElement('li');
+ li.innerText = e_1[0];
+ li.onclick = e_1[1];
+ context_menu.appendChild(li);
+ }
+ }
+ // This is called whenever the <input type="file">'s value changes
+ function on_file_added(_e) {
+ if (the_file.files.length >= 1) {
+ if (override_file) {
+ filename_input.value = override_file_filename;
+ override_input.value = "1";
+ upload_parent_directory.value = override_file_path;
+ console.log(filename_input.value, override_input.value, upload_parent_directory.value);
+ }
+ else {
+ filename_input.value = the_file.files[0].name;
+ override_input.value = "0";
+ upload_parent_directory.value = get_path(focused_window);
+ }
+ if (!FORM_ASYNC) {
+ upload_form.submit();
+ return;
+ }
+ // Send the form asynchronously through the fetch api
+ fetch(upload_form.action, {
+ method: upload_form.method,
+ body: new FormData(upload_form)
+ }).then(function (resp) {
+ if (resp.status == 200) {
+ // Reload the directory so the user can see the newly uploaded file
+ openfile(true, focused_window);
+ }
+ else {
+ alert("Upload failed");
+ }
+ }, function () {
+ alert("Upload failed");
+ });
+ }
+ else {
+ alert("No files selected");
+ }
+ }
+ // It's honestly really sad that we need this
+ // We have an image viewer, but we load the uploaded via the XMLHttpRequest API, which gives us an array buffer
+ // We need to base64 encode the image data so we can feed it into the <img src="...">
+ // and the standart base64 encode API is shit
+ // https://stackoverflow.com/questions/7370943/retrieving-binary-file-content-using-javascript-base64-encode-it-and-reverse-de
+ function base64ArrayBuffer(arrayBuffer) {
+ var base64 = '';
+ var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+ var bytes = new Uint8Array(arrayBuffer);
+ var byteLength = bytes.byteLength;
+ var byteRemainder = byteLength % 3;
+ var mainLength = byteLength - byteRemainder;
+ var a, b, c, d;
+ var chunk;
+ // Main loop deals with bytes in chunks of 3
+ for (var i = 0; i < mainLength; i = i + 3) {
+ // Combine the three bytes into a single integer
+ chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
+ // Use bitmasks to extract 6-bit segments from the triplet
+ a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
+ b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
+ c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
+ d = chunk & 63; // 63 = 2^6 - 1
+ // Convert the raw binary segments to the appropriate ASCII encoding
+ base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
+ }
+ // Deal with the remaining bytes and padding
+ if (byteRemainder == 1) {
+ chunk = bytes[mainLength];
+ a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
+ // Set the 4 least significant bits to zero
+ b = (chunk & 3) << 4; // 3 = 2^2 - 1
+ base64 += encodings[a] + encodings[b] + '==';
+ }
+ else if (byteRemainder == 2) {
+ chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
+ a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
+ b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
+ // Set the 2 least significant bits to zero
+ c = (chunk & 15) << 2; // 15 = 2^4 - 1
+ base64 += encodings[a] + encodings[b] + encodings[c] + '=';
+ }
+ return base64;
+ }
+ // This updates the path of the window's DOM (the "Root > Folder1 > Folder2 > foo.png")
+ function update_path_visuals(wnd) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ wnd = focused_window;
+ }
+ var the_path = wnd.visuals.getElementsByClassName('path')[0];
+ // Remove the old path
+ while (the_path.children.length > 0)
+ the_path.removeChild(the_path.lastChild);
+ var _loop_1 = function (i) {
+ // For each element after the first create a separator
+ if (i >= 0) {
+ d = wnd.pwd[i];
+ separator_div = mk(the_path, 'div', 'separator');
+ separator_div.innerText = "ยป";
+ }
+ else
+ d = "Root";
+ entry = mk(the_path, 'button', 'pathentry');
+ entry.innerText = d;
+ // When we click the entry, go to its folder
+ entry.onclick = function (_e) {
+ if (length < wnd.pwd.length) {
+ wnd.pwd.length = i + 1;
+ openfile(true, wnd);
+ }
+ };
+ // We can drop files onto the path, which will omve them to teh folder
+ entry.onmouseup = function (e) {
+ if (dragging && dragging_fileview) {
+ var new_folder = get_path(wnd, i + 1);
+ move_file(dragging_fileview.wnd, wnd, new_folder, dragging_fileview.filename);
+ end_drag(e);
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ };
+ };
+ var d, separator_div, entry;
+ for (var i = -1; i < wnd.pwd.length; i++) {
+ _loop_1(i);
+ }
+ }
+ // This asks the server for the contents of the specified file
+ // The 'cb' callback is then called, which gives you the file as either text or binary
+ // depending on whether or not text is true/false
+ function read_file_contents(text, cb, folder, filename) {
+ var data = new FormData();
+ data.append('folder', folder);
+ data.append('filename', filename);
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/readfile.php', true);
+ if (text) {
+ xhr.onload = function () {
+ cb(xhr.responseText);
+ };
+ }
+ else {
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function () {
+ cb(xhr.response);
+ };
+ }
+ xhr.send(data);
+ }
+ // This opens a file.
+ // If the file has image/* mimetype, it will be displayed as an image
+ // otherwise it will be displayed as plaintext
+ function openfile_nondir(wnd) {
+ while (wnd.filecontents.children.length > 0)
+ wnd.filecontents.removeChild(wnd.filecontents.lastChild);
+ // Send a request to readfile.php, which will give us the contents
+ var data = new FormData();
+ data.append('folder', get_path(wnd, wnd.pwd.length - 1));
+ data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
+ var xhr = new XMLHttpRequest();
+ update_path_visuals(wnd);
+ xhr.open('POST', '/php/readfile.php', true);
+ wnd.filecontents.innerText = "";
+ wnd.filecontentsroot.style.display = 'flex';
+ wnd.foldercontents.style.display = 'none';
+ var is_image = open_file.mimetype.split("/")[0] == "image";
+ wnd.save_btn_container.style.display = (open_file.write_permissions && !is_image) ? "flex" : "none";
+ if (is_image) {
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function () {
+ var b = "data:image/png;base64," + base64ArrayBuffer(xhr.response);
+ wnd.filecontents.style.backgroundImage = "url('" + b + "')";
+ wnd.filecontents.classList.add('imgview');
+ };
+ }
+ else {
+ wnd.filecontents.classList.remove('imgview');
+ wnd.filecontents.style.backgroundImage = "unset";
+ wnd.txt_editor = mk(wnd.filecontents, 'pre');
+ xhr.onload = function () {
+ wnd.txt_editor.innerText = xhr.responseText;
+ if (open_file.write_permissions)
+ wnd.txt_editor.contentEditable = "true";
+ };
+ }
+ xhr.send(data);
+ }
+ // This is a tiny wrapper around the share_window.
+ function share(in_file, filename, wnd) {
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ }
+ else {
+ var folder = get_path(wnd);
+ }
+ var wnd = make_share_window(folder, filename);
+ focus_window(wnd);
+ }
+ // Replace an existing file with a new one
+ function replace_file(in_file, filename, wnd) {
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ }
+ else {
+ var folder = get_path(wnd);
+ }
+ override_file = true;
+ override_file_path = folder;
+ override_file_filename = filename;
+ the_file.click();
+ }
+ // This loads the contents of the current directory
+ function openfile_dir(wnd) {
+ update_path_visuals(wnd);
+ var data = new FormData();
+ data.append('path', get_path(wnd));
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/readdir.php', true);
+ xhr.onload = function () {
+ for (var _i = 0, _a = wnd.files; _i < _a.length; _i++) {
+ var f = _a[_i];
+ f.visuals.remove();
+ }
+ wnd.files = [];
+ var json = JSON.parse(xhr.responseText);
+ if (!json)
+ return;
+ // Create the FileViews from the json response
+ for (var _b = 0, json_1 = json; _b < json_1.length; _b++) {
+ var f = json_1[_b];
+ var view = new FileView(f.name, wnd, f.mimetype, f.is_directory && f.is_directory != "0", f.can_edit && f.can_edit != "0");
+ wnd.files.push(view);
+ }
+ // Sort the files nicely before adding their visuals
+ // Folders come first, then files, then the special trash directory
+ // Everything inside the categories is lexically sorted
+ wnd.files.sort(function (a, b) {
+ if (wnd.pwd.length == 0 && a.filename == "share")
+ return -10;
+ if (wnd.pwd.length == 0 && b.filename == "share")
+ return 10;
+ if (wnd.pwd.length == 0 && a.filename == "trash")
+ return 10;
+ if (wnd.pwd.length == 0 && b.filename == "trash")
+ return -10;
+ if (a.is_directory && !b.is_directory)
+ return -1;
+ if (!a.is_directory && b.is_directory)
+ return 1;
+ return a.filename.localeCompare(b.filename);
+ });
+ for (var _c = 0, _d = wnd.files; _c < _d.length; _c++) {
+ var f = _d[_c];
+ add_file_visuals(f, wnd);
+ }
+ };
+ xhr.send(data);
+ wnd.filecontentsroot.style.display = 'none';
+ wnd.foldercontents.style.display = 'flex';
+ wnd.foldercontents.onmouseup = function () {
+ if (dragging && dragging_fileview) {
+ move_file(dragging_fileview.wnd, wnd, get_path(wnd), dragging_fileview.filename);
+ }
+ };
+ }
+ function openfile(is_directory, wnd) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ console.trace();
+ wnd = focused_window;
+ }
+ if (is_directory) {
+ openfile_dir(wnd);
+ }
+ else {
+ openfile_nondir(wnd);
+ }
+ }
+ function move_to_trash(wnd, filename) {
+ move_file(wnd, wnd, "/trash", filename, path_combine(get_path(wnd), filename));
+ }
+ function restore_from_trash(wnd, filename) {
+ var split = filename.split("/");
+ var new_filename = split.pop();
+ var new_directory = "/" + split.join("/");
+ move_file(wnd, wnd, new_directory, filename, new_filename);
+ }
+ // This deletes the file, *for real*
+ // move_to_trash is what is actually called when the user clicks 'Delete'
+ function delete_file(wnd, filename) {
+ var data = new FormData();
+ data.append('folder', get_path(wnd));
+ data.append('filename', filename);
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/delete.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+ function rename_file(filename, wnd) {
+ var new_name = prompt("Rename " + filename + " to", filename);
+ if (!new_name)
+ return;
+ var data = new FormData();
+ data.append('folder', get_path(wnd));
+ data.append('old_filename', filename);
+ data.append('new_filename', new_name);
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/rename.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+ function move_file(srcwnd, dstwnd, new_folder, filename, new_filename) {
+ if (!new_filename)
+ new_filename = filename;
+ var data = new FormData();
+ data.append('old_folder', get_path(srcwnd));
+ data.append('new_folder', new_folder);
+ data.append('filename', filename);
+ data.append('new_filename', new_filename);
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/move.php', true);
+ xhr.onload = function () {
+ openfile(true, srcwnd);
+ openfile(true, dstwnd);
+ };
+ xhr.send(data);
+ }
+ function new_folder(wnd) {
+ var dirname = prompt("Directory name", "New Folder");
+ if (!dirname)
+ return;
+ var data = new FormData();
+ data.append('parent_directory', get_path(wnd));
+ data.append('dirname', dirname);
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/mkdir.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+ // Dragging a fileview is a bit different from dragging a window
+ // This does some setup work before calling the common begin_drag
+ function begin_drag_fileview(e, fileview) {
+ if (dragging)
+ end_drag(e);
+ // The dragging_placeholder is inserted into its place by the begin_drag function
+ dragging_placeholder = document.createElement('div');
+ dragging_fileview = fileview;
+ dragging = fileview.visuals;
+ dragging.style.zIndex = 50000;
+ begin_drag(e, fileview.visuals);
+ }
+ // Start dragging the 'obj' DOM element
+ // e is a DOM event, this should only get called in response of a DOM event
+ function begin_drag(e, obj, dont_set_width) {
+ set_iframe_enabled(false);
+ dragging = obj;
+ dragging_candidate = null;
+ dragging.classList.add("dragged");
+ var elemRect = dragging.getBoundingClientRect();
+ dragging_offset_x = e.clientX - elemRect.left;
+ dragging_offset_y = -e.clientY + elemRect.top;
+ if (dragging_placeholder)
+ obj.parentNode.insertBefore(dragging_placeholder, obj);
+ dragging.style.left = (e.clientX - dragging_offset_x) + "px";
+ dragging.style.top = (e.clientY + dragging_offset_y) + "px";
+ if (!dont_set_width) {
+ dragging.style.width = elemRect.width + "px";
+ dragging.style.height = elemRect.height + "px";
+ }
+ dragging.style.position = "absolute";
+ document.body.appendChild(dragging);
+ }
+ function end_drag(_e) {
+ set_iframe_enabled(true);
+ // If there's a dragging placeholder remove it and put the dragged node back into its place
+ if (dragging_placeholder) {
+ dragging_placeholder.parentNode.insertBefore(dragging, dragging_placeholder);
+ dragging_placeholder.remove();
+ dragging_placeholder = null;
+ }
+ // If we were dragging a FileView, we need to reset some CSS
+ if (dragging_fileview) {
+ dragging.style.removeProperty("position");
+ dragging.style.removeProperty("width");
+ dragging.style.removeProperty("height");
+ dragging.style.removeProperty("left");
+ dragging.style.removeProperty("top");
+ dragging_fileview = null;
+ }
+ dragging.classList.remove("dragged");
+ dragging = null;
+ }
+ // This creates the parts of a window that are common between all window types
+ // This should only really be called by another function that will then fill up the window
+ function make_window_base(pwd, x, y, w, h) {
+ var wnd = new OurWindow(pwd);
+ windows.push(wnd);
+ wnd.visuals = mk(document.body, 'div', 'window');
+ wnd.visuals.style.width = w + "px";
+ wnd.visuals.style.height = h ? (h + "px") : "unset";
+ wnd.visuals.style.position = "absolute";
+ wnd.visuals.style.left = x + "px";
+ wnd.visuals.style.top = y + "px";
+ wnd.h2 = mk(wnd.visuals, 'h2');
+ wnd.visuals.onmousedown = function (_e) {
+ focus_window(wnd);
+ };
+ wnd.h2.onmousedown = function (e) {
+ if (!dragging) {
+ dragging_candidate = wnd.visuals;
+ dragging_candidate_x = e.clientX;
+ dragging_candidate_y = e.clientY;
+ }
+ };
+ return wnd;
+ }
+ // This is a widely abused helper function that creates a DOM element, attaches it as the
+ // last child of 'parent' and possibly gives it a class
+ function mk(parent, type, _class) {
+ var el = document.createElement(type);
+ parent.appendChild(el);
+ if (_class)
+ el.classList.add(_class);
+ return el;
+ }
+ // Crate a horizontal div
+ function mkhdiv(parent) {
+ var hdiv = mk(parent, 'div');
+ hdiv.style.display = "flex";
+ hdiv.style.alignItems = "center";
+ hdiv.style.padding = "0.3rem";
+ hdiv.style.gap = "0.3rem";
+ return hdiv;
+ }
+ // Create a checkbocx with a label.
+ // togglefn will be called when its value changes with an argument that's either true/false
+ function mkcheckbox(parent, label, togglefn) {
+ var hdiv = mkhdiv(parent);
+ var write_checkbox = mk(hdiv, 'input');
+ write_checkbox.type = 'checkbox';
+ var write_checkbox_label = mk(hdiv, 'label');
+ write_checkbox_label.innerText = label;
+ write_checkbox_label.onclick = function (_e) { write_checkbox.click(); };
+ write_checkbox_label.classList.add('noselect');
+ write_checkbox.onchange = function (_e) {
+ togglefn(write_checkbox.checked);
+ };
+ }
+ // This monstrocity creates the 'Share file' window
+ function make_share_window(folder, filename) {
+ var wnd = make_window_base(null, 400, 400, 400, 0);
+ wnd.h2.style.display = 'flex';
+ // The title of the window. WE set its 'flex' to 1 1 0 so it fills up the titlebar
+ // and pushes the X button to the very right
+ var heading = mk(wnd.h2, 'span', 'wndtitle');
+ heading.innerText = "Share " + filename;
+ // Close button
+ var x_button = mk(wnd.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = function () { return delete_window(wnd); };
+ wnd.foldercontents = mk(wnd.visuals, 'div', 'share_dialog_contents');
+ wnd.foldercontents.style.padding = "0.5rem";
+ // This is the data that will be sent when we hit "Generate link"
+ var data = {
+ write_permissions: false,
+ private: false,
+ has_password: false,
+ password: "",
+ userlist: []
+ };
+ // If private link is clicked, show the "Add user" button and the user list
+ var userlist, add_user;
+ mkcheckbox(wnd.foldercontents, "Private link", function (toggled) {
+ add_user.style.display = toggled ? "block" : "none";
+ userlist.style.display = toggled ? "block" : "none";
+ data.private = toggled;
+ });
+ userlist = mk(wnd.foldercontents, 'div');
+ userlist.style.display = "none";
+ add_user = mk(wnd.foldercontents, 'button');
+ add_user.innerText = "Add user";
+ add_user.style.display = "none";
+ // When we hit 'Add user', add an input field for a new user
+ add_user.onclick = function (_e) {
+ var i = mk(userlist, 'input');
+ i.value = 'John Doe';
+ var index = data.userlist.length;
+ data.userlist.push(i.value);
+ i.onchange = function (_e) {
+ data.userlist[index] = i.value;
+ };
+ };
+ // Click the add_user once to add a default user, since a URL that nobody can use makes no sense
+ add_user.click();
+ mkcheckbox(wnd.foldercontents, "Give write permissions", function (toggled) {
+ data.write_permissions = toggled;
+ });
+ // If 'Password protected' is checked, show the password field
+ var password_container;
+ mkcheckbox(wnd.foldercontents, "Password protected", function (toggled) {
+ data.has_password = toggled;
+ password_container.style.display = toggled ? "flex" : "none";
+ });
+ password_container = mkhdiv(wnd.foldercontents);
+ password_container.style.display = 'none';
+ var password_label = mk(password_container, 'label');
+ password_label.innerText = "Password";
+ var password_input = mk(password_container, 'input');
+ password_input.type = 'password';
+ password_input.autocomplete = 'off';
+ password_input.style.flex = "1 0 0";
+ password_input.onchange = function (_e) {
+ data.password = password_input.value;
+ };
+ var generate_url_button = mk(wnd.foldercontents, 'button');
+ generate_url_button.innerText = "Generate link";
+ generate_url_button.onclick = function () {
+ // The backend expects the users to be either an empty string, if the URL is public
+ // or a comma separated list of usernaems
+ var users = "";
+ if (data.private) {
+ users = data.userlist.join(',');
+ }
+ var form_data = new FormData();
+ form_data.append('folder', folder);
+ form_data.append('filename', filename);
+ form_data.append('users', users);
+ // 0 = No permissions, 1 = Read only, 2 = Write , 1|2 = 3 = RW
+ // Only 1 and 3 make sense in the context of a URL
+ form_data.append('permissions', (data.write_permissions ? 3 : 1).toString());
+ form_data.append('password', data.has_password ? data.password : "");
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/share.php', true);
+ xhr.onload = function () {
+ alert(xhr.response);
+ };
+ xhr.send(form_data);
+ delete_window(wnd);
+ };
+ return wnd;
+ }
+ function download_file(in_file, filename, wnd) {
+ if (!wnd) {
+ alert(802);
+ wnd = focused_window;
+ }
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ }
+ else {
+ var folder = get_path(wnd);
+ }
+ // Read the file contents and then do DISGUSTING javascript things to download the ifle
+ // We create a invisible <a> that we click and then delete
+ // That <a> has its download attribute set so we download the contents instead of opening it in a new tab
+ // and of course its href is a virtual object URL that has its content set to a blob
+ read_file_contents(false, function (x) {
+ var blob = new Blob([new Uint8Array(x, 0, x.length)]);
+ var url = URL.createObjectURL(blob);
+ var a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(function () {
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ });
+ }, folder, filename);
+ return;
+ }
+ // make_window creates an explorer window - the kind that can list directories/open files
+ function make_window(pwd, has_close) {
+ var wnd = make_window_base(pwd, 100, 100, 800, 600);
+ mk(wnd.h2, 'div', 'path');
+ if (has_close) {
+ var x_button = mk(wnd.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = function () { return delete_window(wnd); };
+ }
+ // wnd.foldercontents is where the FileViews will be stored
+ // it also has a subheader (h3) with 'Upload' and 'New FOlder' buttons
+ {
+ wnd.foldercontents = mk(wnd.visuals, 'div', 'foldercontents');
+ var h3 = mk(wnd.foldercontents, 'h3');
+ var upload_btn = mk(h3, 'button');
+ upload_btn.innerText = "Upload";
+ upload_btn.onclick = function () {
+ override_file = false;
+ the_file.click();
+ };
+ mk(h3, 'div', 'separator');
+ var new_folder_btn = mk(h3, 'button');
+ new_folder_btn.innerText = "New Folder";
+ new_folder_btn.onclick = function () { new_folder(wnd); };
+ mk(h3, 'div', 'separator');
+ wnd.filegrid = mk(wnd.foldercontents, 'div', 'files');
+ }
+ // wnd.filecontentsroot is where the filedata will be stored for open files
+ // it also has a subheader (h3) with Share and Download buttons
+ {
+ wnd.filecontentsroot = mk(wnd.visuals, 'div', 'filecontentsroot');
+ var h3 = mk(wnd.filecontentsroot, 'h3');
+ var download_btn = mk(h3, 'button');
+ download_btn.innerText = "Download";
+ download_btn.onclick = function () { download_file(true); };
+ mk(h3, 'div', 'separator');
+ var share_btn = mk(h3, 'button');
+ share_btn.innerText = "Share";
+ share_btn.onclick = function () { alert("TODO NOT IMPLEMENTETD"); }; //share(true, fileview.filename, wnd); }
+ mk(h3, 'div', 'separator');
+ wnd.save_btn_container = mk(h3, 'div');
+ wnd.save_btn_container.style.display = 'flex';
+ var save_btn = mk(wnd.save_btn_container, 'button');
+ save_btn.innerText = "Save";
+ save_btn.onclick = function () { return save_open_text_file(wnd); };
+ mk(wnd.save_btn_container, 'div', 'separator');
+ wnd.filecontents = mk(wnd.filecontentsroot, 'div', 'filecontents');
+ }
+ return wnd;
+ }
+ function save_open_text_file(wnd) {
+ var contents = wnd.txt_editor.innerText;
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/upload.php', true);
+ var data = new FormData();
+ data.append('parent_directory', get_path(wnd, wnd.pwd.length - 1));
+ data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
+ data.append('content', contents);
+ data.append('overwrite', '1');
+ xhr.send(data);
+ }
+ // Create the visuals for a FileView
+ function add_file_visuals(fileview, wnd) {
+ // Are we in a subdirectory of the trash folder?
+ var is_in_trash = wnd.pwd.length > 0 && wnd.pwd[0] == "trash";
+ // Is the current filewview the trash folder itself?
+ var is_trash = wnd.pwd.length == 0 && fileview.filename == "trash";
+ var is_share = wnd.pwd.length == 0 && fileview.filename == "share";
+ var visuals = mk(wnd.filegrid, 'div');
+ fileview.visuals = visuals;
+ var img = document.createElement('img');
+ var filename = document.createElement('div');
+ if (fileview.is_directory) {
+ if (get_path(wnd) == "/" && fileview.filename == "trash")
+ img.src = "/mimeicons/user-trash.png";
+ else if (get_path(wnd) == "/" && fileview.filename == "share")
+ img.src = "/mimeicons/user-share.png";
+ else
+ img.src = "/mimeicons/directory.png";
+ }
+ else {
+ img.src = "/mimeicons/" + fileview.mimetype.replace("/", "-") + ".png";
+ }
+ fileview.visuals.onclick = function () {
+ wnd.pwd.push(fileview.filename);
+ if (!fileview.is_directory) {
+ open_file = fileview;
+ }
+ openfile(fileview.is_directory, wnd);
+ };
+ visuals.oncontextmenu = function (e) {
+ if (!dragging) {
+ var context_list = [
+ // Open is always in the context list
+ ['Open', function () {
+ wnd.pwd.push(fileview.filename);
+ openfile(fileview.is_directory, wnd);
+ }],
+ ['Open in New Window', function () {
+ var new_pwd = wnd.pwd.slice();
+ new_pwd.push(fileview.filename);
+ var new_wnd = make_window(new_pwd, true);
+ open_file = fileview;
+ openfile(fileview.is_directory, new_wnd);
+ focus_window(new_wnd);
+ }],
+ ];
+ if (is_in_trash) {
+ // If we're in the trash, we can restore files or delete them forever
+ context_list.push(['Restore', function () { restore_from_trash(wnd, fileview.filename); }]);
+ context_list.push(['Delete forever', function () { delete_file(wnd, fileview.filename); }]);
+ }
+ else if (!is_trash && !is_share) {
+ // If we;'re not in trash we can rename/share/download/move files to trash
+ context_list.push(['Rename', function () { rename_file(fileview.filename, wnd); }]);
+ if (!fileview.is_directory) {
+ var _loop_2 = function (a) {
+ if (fileview.filename.endsWith(a.extension)) {
+ context_list.push([a.text, function () {
+ read_file_contents(true, function (x) {
+ var ue = encodeURIComponent(x);
+ var url = a.url.replace("$content_urlencoded", ue)
+ .replace("$filename", fileview.filename);
+ if (a.open_in_iframe) {
+ var wnd_1 = make_window_base([], 10, 10, 800, 600);
+ var title = mk(wnd_1.h2, 'span', 'wndtitle');
+ title.innerText = fileview.filename;
+ // Close button
+ var x_button = mk(wnd_1.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = function () { return delete_window(wnd_1); };
+ var contents = mk(wnd_1.visuals, 'div', 'filecontentsroot');
+ var iframe = mk(contents, 'iframe');
+ iframe.style.flex = '1 0 0';
+ iframe.src = url;
+ focus_window(wnd_1);
+ }
+ else {
+ window.location = url;
+ }
+ }, get_path(wnd), fileview.filename);
+ }]);
+ }
+ };
+ for (var _i = 0, actions_1 = actions; _i < actions_1.length; _i++) {
+ var a = actions_1[_i];
+ _loop_2(a);
+ }
+ if (fileview.write_permissions) {
+ context_list.push(['Replace', function () { replace_file(false, fileview.filename, wnd); }]);
+ }
+ context_list.push(['Share', function () { share(false, fileview.filename, wnd); }], ['Download', function () { download_file(false, fileview.filename); }]);
+ }
+ context_list.push(['Delete', function () { move_to_trash(wnd, fileview.filename); }]);
+ }
+ context(e, context_list);
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ };
+ visuals.ondragstart = function (e) {
+ if (is_trash || is_in_trash || is_share) {
+ e.preventDefault();
+ return;
+ }
+ begin_drag_fileview(e, fileview);
+ e.preventDefault();
+ };
+ visuals.onmouseup = function (e) {
+ if (dragging) {
+ if (fileview.is_directory) {
+ if (get_path(wnd) == "/" && fileview.filename == "trash") {
+ // If we've dragged something onto the trashcan, it's trash
+ move_to_trash(wnd, dragging_fileview.filename);
+ }
+ else if (get_path(wnd) == "/" && fileview.filename == "share") {
+ // move to 'share' is invalid
+ }
+ else {
+ // If we've dragged something onto a directory, move it into that directory
+ move_file(dragging_fileview.wnd, wnd, path_combine(get_path(wnd), fileview.filename), dragging_fileview.filename);
+ }
+ }
+ else {
+ // alert(`Dropped ${dst.filename} on ${src.filename}`);
+ }
+ end_drag(e);
+ }
+ e.preventDefault();
+ };
+ visuals.classList.add('file');
+ filename.classList.add('filename');
+ if (is_in_trash) {
+ var split = fileview.filename.split("/");
+ filename.innerText = split[split.length - 1];
+ }
+ else if (is_trash) {
+ filename.innerText = "Trash";
+ }
+ else if (is_share) {
+ var x = mk(filename, 'span');
+ x.style.fontSize = "0.8rem";
+ x.innerText = "Shared with me";
+ }
+ else {
+ filename.innerText = fileview.filename;
+ }
+ visuals.appendChild(img);
+ visuals.appendChild(filename);
+ }
+ // Reads the 'pwd' of the focused window
+ // If pwd is ['foo', 'bar', 'baz'], this returns '/foo/bar/baz'
+ function get_path(wnd, max_length) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ console.trace();
+ wnd = focused_window;
+ }
+ if (max_length == undefined) {
+ max_length = wnd.pwd.length;
+ }
+ var path = "/";
+ for (var i = 0; i < max_length; i++) {
+ path += wnd.pwd[i];
+ if (i != max_length - 1)
+ path += "/";
+ }
+ return path;
+ }
+ function path_combine(a, b) {
+ var last_char = a.slice(-1);
+ if (last_char == "/")
+ return a + b;
+ else
+ return a + "/" + b;
+ }
+ // When we click anywhere, remove the context menu
+ // The context menu itself has a onmousedown that prevents propagation so we can click its elements
+ document.body.onmousedown = function (_e) {
+ if (context_menu) {
+ context_menu.remove();
+ context_menu = null;
+ }
+ };
+ document.body.onmousemove = function (e) {
+ if (dragging) {
+ dragging.style.left = (e.clientX - dragging_offset_x) + "px";
+ dragging.style.top = (e.clientY + dragging_offset_y) + "px";
+ }
+ else if (dragging_candidate) {
+ var d = Math.abs(e.clientX - dragging_candidate_x) + Math.abs(e.clientY - dragging_candidate_y);
+ if (d > 15)
+ begin_drag(e, dragging_candidate, true);
+ }
+ };
+ document.body.onmouseup = function (_e) {
+ if (dragging_candidate)
+ dragging_candidate = null;
+ if (dragging)
+ end_drag(_e);
+ };
+ document.body.oncontextmenu = function (e) {
+ if (dragging) {
+ end_drag(e);
+ e.preventDefault();
+ }
+ if (context_menu) {
+ context_menu.remove();
+ context_menu = null;
+ }
+ };
+ the_file.onchange = function (e) { on_file_added(e); };
+ function set_iframe_enabled(en) {
+ for (var _i = 0, _a = document.getElementsByTagName('iframe'); _i < _a.length; _i++) {
+ var iframe = _a[_i];
+ iframe.hidden = !en;
+ }
+ }
+ main();
F diff --git a/css/sharefile_style.css b/css/sharefile_style.css
deleted file mode 100644
--- a/css/sharefile_style.css
+++ /dev/null
- body {
- display: flex;
- align-items: center;
- justify-content: center;
- }
-
- .content {
- flex: 0 0 0;
- display: flex;
- flex-direction: column;
- gap: 1rem;
- align-items: center;
- justify-content: center;
- }
-
- .h {
- display: flex;
- gap: 0.8rem;
- align-items: center;
- justify-content: center;
- }
-
- input[type=submit] {
- margin-top: 0.5rem;
- }
F diff --git a/css/style.css b/css/style.css
deleted file mode 100644
--- a/css/style.css
+++ /dev/null
-
- html, body {
- margin: 0;
- height: 100%;
- }
-
- body {
- background: #f0f0f0;
- color: black;
- font-family: Roboto, sans-serif;
- overflow: hidden;
- }
-
- .noselect {
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- }
-
- #page {
- display: flex;
- flex: 1 0 0;
- align-items: stretch;
-
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- }
-
- #page > * {
- flex: 1 0 0;
- }
-
- /* Disable the black outline that Chromium browsers put on focused buttons */
- input:focus,
- button:focus
- {
- outline:0;
- }
-
- #header {
- background: white;
- margin: 0;
- padding: 0;
- height: 3rem;
- font-size: 2em;
- display: flex;
- align-items: stretch;
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- }
-
- #topmenu {
- font-size: 1.3rem;
- list-style-type: none;
- display: flex;
- margin: 0;
- padding: 0;
- }
-
- #topmenu > li {
- cursor: pointer;
- padding: 10px;
- }
-
- #topmenu > li:hover {
- background: #eee;
- }
-
- .logo {
- font-family: monospace;
- margin: 0;
- align-self: center;
- padding-left: 2rem;
- }
-
- #hero {
- flex: 1.5 0 0;
- position: relative;
- font-size: 3.5rem;
- }
-
- .vcenter {
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
-
- #hero .bg {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- z-index: -5;
- }
-
- #hero > * > p {
- color: #676d75;
- margin: 0rem;
- padding: 0.5rem;
- white-space: nowrap;
- text-align: center;
- }
-
- #hero .big {
- font-size: 1.2em;
- }
-
- .blue {
- color: #231179;
- font-weight: bold;
- }
-
- form {
- background: #fbfbfb;
- margin: 4.5rem;
- padding-top: 0;
- box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);
- border-radius: 0.5rem;
- border: 1px solid #b9b9b9;
- }
-
- .vert {
- justify-content: center;
- display: flex;
- flex-direction: column;
- }
-
- .vert2 {
- display: flex;
- flex-direction: column;
- }
-
- .overlay {
- display: grid;
- }
-
- .overlay > * {
- grid-row: 1;
- grid-column: 1;
- }
-
- form > .content {
- margin: 2rem;
- margin-top: 1rem;
- margin-bottom: 1.7rem;
- padding: 0;
- display: flex;
- flex-direction: column;
- justify-content: center;
- }
-
- form > h2 {
- color: #4d4d4d;
- margin: 0;
- text-align: center;
- padding: 1rem;
- background: #f0f0f0;
- border-top-left-radius: 0.5rem;
- border-top-right-radius: 0.5rem;
- align-content: center;
- }
-
-
- .window h3 button,
- .window > h2 button
- {
- border: none;
- padding: 0.3rem 1rem;
- background: inherit;
- border-radius: 0;
- }
-
- .window h3 button:not(.pathentry):hover {
- background: white;
- }
-
- .window h3 .separator {
- flex: 0 0 1px;
- align-self: stretch;
- background: #bbb;
- }
-
- .window > h2 > *:first-child {
- border-top-left-radius: 0.5rem;
- }
- form p {
- margin: 1rem 0px 0.3rem 0px;
- }
-
- .hero_form_error {
- animation: fadein 0.2s;
- background-color: #ff4d4d;
- color: #ffffff;
- padding-left: 0.5rem;
- border-bottom-left-radius: 0.5rem;
- border-bottom-right-radius: 0.5rem;
- margin-top: -0.2rem;
-
- }
- @keyframes fadein {
- from { opacity: 0; }
- to { opacity: 1; }
- }
-
- .content > input {
- min-width: 300px;
- }
-
- button,
- input:not([type=file])
- {
- border: 1px solid #bbb;
- padding: 0.5rem 0.8rem;
- font-size: inherit;
- font-family: inherit;
- border-radius: 0.3rem;
- background: #fcfcfc;
- }
-
- input[type=button], button, input[type=submit] {
- cursor: pointer;
- }
-
- input[type=submit] {
- margin-top: 2rem;
- width: 100%;
- display: block;
- padding: 0.7rem;
- font-size: 1.1em;
- box-shadow: 0 0.2rem 0.6rem #eee;
- background: #231179;
- color: white;
- outline: none;
- }
-
-
- input:hover,
- button:hover
- {
- background: white;
- }
-
- input:focus {
- border-color: black;
- }
-
- input[type=submit]:hover {
- background: #5b4d9d;
- }
-
- .bgbottom {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- z-index: -100;
- }
-
-
- #arrows {
- position: relative;
- }
-
- #arrows > img {
- position: absolute;
- bottom: 0;
- z-index: -200;
- }
-
- #signupform {
- display: none;
- }
-
- .window {
- overflow: hidden;
- box-sizing: border-box;
- margin: 0rem;
- padding: 0;
- box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);
- border-radius: 0.5rem;
- border: 1px solid #b9b9b9;
-
- display: block;
- position: absolute;
- top: 0;
- left: 0;
- width: 800px;
- height: 600px;
-
- transition: opacity 0.3s;
- display: flex;
- flex-direction: column;
- }
-
- .window.focus {
- border-color: black;
- }
-
- .foldercontents, .filecontents, .filecontentsroot {
- background: rgba(250, 250, 250, .9);
- flex: 1 0 0;
- }
-
- .filecontents, .filecontentsroot {
- background: white;
- }
-
- .filecontents {
- font-family: monospace;
- }
-
- .filecontents.imgview {
- background-color: black;
- background-repeat: no-repeat;
- background-position: center;
- background-size: contain;
- }
-
- .filecontentsroot, .foldercontents {
- display: flex;
- flex-direction: column;
- }
-
- .filecontents {
- overflow-y: scroll;
- }
- [contenteditable] {
- outline: 0px solid transparent;
- }
-
- pre {
- font-size: 1.3rem;
- min-height: 100%;
- margin: 0.3rem;
- box-sizing: border-box;
- }
-
- .window h3,
- .window > h2 {
- color: #4d4d4d;
- background: #f0f0f0;
- margin: 0;
-
-
- display: flex;
- align-items: stretch;
- font-weight: normal;
- padding: 0rem;
- border-bottom: 1px solid #bbb;
- }
-
- .window > h2 {
- font-size: 1.3rem;
- border-top-left-radius: 0.5rem;
- border-top-right-radius: 0.5rem;
- cursor: grab;
-
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- }
-
- .window h3 {
- font-size: 1.2rem;
- }
-
- .window.dragged {
- /* opacity: 0.9; */
- }
-
- .files {
- padding: 0.3rem;
- display: grid;
-
- grid-gap: 0rem;
- grid-auto-rows: 8rem;
- grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
- }
-
- .file {
- box-sizing: border-box;
- padding: 0.5rem;
- cursor: pointer;
- color: #333;
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- border: 1px solid rgba(0,0,0,0);
- border-radius: 0.1rem;
- }
-
- .file.dragged {
- border: none;
- pointer-events: none;
- }
-
- .file:hover:not(.dragged) {
- background: rgba(255,255,255, 0.5);
- color: black;
- border-color: #ddd;
- }
-
- .file > img {
- flex: 1 1 0;
- min-width: 0;
- min-height: 0;
- }
-
- .file:hover > img {
- filter: brightness(120%);
- }
-
- .path {
- display: flex;
- align-items: stretch;
- flex: 1 0 0;
- }
-
- .path > .separator {
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- padding: 0 0.1rem;
- align-self: center;
- }
-
- .pathentry:last-child {
- cursor: inherit;
- }
-
- .pathentry:not(:last-child):hover {
- text-decoration: underline;
- }
-
- .context {
- background: white;
- position: absolute;
- z-index: 1000;
- list-style-type: none;
- margin: 0;
- padding: 0;
- top: 0;
- left: 0;
-
- border: 1px solid #ccc;
- box-shadow: 0 0.3rem 0.5rem rgba(0,0,0,0.2);
- }
-
- .context > li {
- padding: 0.4rem 1.5rem;
- margin: 0;
-
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- }
-
- .context > li:hover {
- background: #2e91db;
- color: white;
- }
-
- .backdrop {
- position: absolute;
- width: 100%;
- height: 100%;
-
- user-select: none;
- -webkit-user-select: none;
- -ms-user-select: none;
- background-image: url("/img/backdrop.jpg");
- background-size: cover;
- }
-
- .share_dialog_contents {
- display: flex;
- flex-direction: column;
- background: white;
- background: rgba(255,255,255, 0.9);
- }
-
- .window > h2 .close_button {
- border-left: 1px solid #ccc;
- }
-
- .close_button:hover {
- background: white;
- }
-
-
- .wndtitle {
- display: flex;
- align-items: center;
- flex: 1 1 0;
- padding-left: 0.8rem;
- }
-
- iframe {
- margin: 0;
- border: 0;
- }
-
- .filename {
- word-wrap: break-word;
- word-break: break-word;
- }
F diff --git a/documentation/web_project_documentataion.docx b/documentation/web_project_documentataion.docx
deleted file mode 100644
B Binary files a/documentation/web_project_documentataion.docx and /dev/null differ
F diff --git a/front/loggedin.ts b/front/loggedin.ts
new file mode 100644
--- /dev/null
+++ b/front/loggedin.ts
+ // This should be only set to false for debugging purposes
+ // If it's set to false, the upload requests will be synchronous
+ // and you will be able to see PHP's echo output in the browser
+ var FORM_ASYNC = true;
+
+ // A FileView is an entry inside the explorer window
+ class FileView {
+ filename: string;
+ wnd: OurWindow;
+ visuals: HTMLElement;
+ mimetype: string;
+ is_directory: boolean;
+ write_permissions: boolean;
+
+ constructor(filename, wnd, mimetype, is_directory, write_permissions) {
+ this.filename = filename;
+ this.wnd = wnd;
+ this.visuals = null; // The DOM object with the icon and the filenam text
+ this.mimetype = mimetype;
+ this.is_directory = is_directory;
+ this.write_permissions = write_permissions;
+ }
+
+ full_path() {
+ return path_combine(get_path(this.wnd), this.filename);
+ }
+ }
+
+ // An array of all fileviews currently open
+
+ class OurWindow {
+ pwd: string[];
+ visuals: HTMLElement;
+ h2: HTMLElement; // The titlebar of the window
+ fileview: any; // TODO what is this
+ files: any[];
+ txt_editor: HTMLElement; // For editable text files, this is the DOM element the user can edit
+
+ // TODO move these to a subclass
+ foldercontents: any;
+ filecontents: any;
+ filecontentsroot: any;
+ filegrid: any;
+ save_btn_container: any;
+
+ constructor(pwd) {
+ this.pwd = pwd; // pwd = [ "Folder1", "Folder2" ] means the current directory of that window is /Folder1/Folder2
+ this.visuals = null; // The DOM object
+ this.h2 = null; // The titlebar of the window
+ this.fileview = null;
+ this.files = [];
+ this.txt_editor = null; // For editable text files, this is the DOM element the user can edit
+ }
+ }
+
+ // An array with all the windows on the screen
+ var windows: OurWindow[] = [];
+
+ // The focused window
+ var focused_window: OurWindow = null;
+
+ // Those all belong to the hidden file upload form
+ const upload_form: HTMLFormElement = document.getElementById("upload_form") as any;
+ const filename_input: HTMLInputElement = document.getElementById("filename") as any;
+ const override_input: HTMLInputElement = document.getElementById("override_input") as any;
+ const upload_parent_directory: HTMLInputElement = document.getElementById("upload_parent_directory") as any;
+ const the_file: HTMLInputElement = document.getElementById("the_file") as any;
+
+ // If this is set to true, requests to uploads.php will be sent with the "override" flag
+ // which will override existing files with the same name
+ var override_file = false;
+ var override_file_filename = "";
+ var override_file_path = "";
+
+ var open_file = null;
+
+ // Some elements have custom right click context menus
+ // If there's a custom context menu active, this will be it
+ var context_menu = null;
+
+
+ // If we're currently dragging something (a window or a file), this is the DOM object we're dragging
+ // dragging_offset_x and dragging_offset_y are the difference between the objec's top left point and the cursor position
+ // this is then added to the cursor position when the mouse is moved
+ var dragging = null;
+ var dragging_offset_x = 0, dragging_offset_y = 0;
+
+ // If we have pressed down a window's title bar but haven't yet started dragging it, it's the drag candidate
+ // This is needed because we don't yet know whether we want to start dragging the window or click an element in the titlebar
+ // Once the mouse has moved sufficiently far away from dragging_candidate_x or y, we start dragging
+ var dragging_candidate = null;
+ var dragging_candidate_x, dragging_candidate_y;
+
+ // If we're dragging a fileview, this is set to the fileview class instance itself, because 'dragging' is just a DOM object
+ // The placeholder is a dummy DIV that we insert in the object's place on the grid to keep things nicely aligned
+ var dragging_fileview;
+ var dragging_placeholder = null;
+
+ // Windows have a z-index. When we click a window it is sent to the top this will be its new z-index
+ // We then increment the depth, and the next window we click will go on top of the current one
+ var depth = 20;
+
+
+
+ function main() {
+ // Create a window that looks at the root directory
+ var root_window = make_window([], false);
+
+ // Focus that window and load the directory
+ focus_window(root_window);
+ openfile(true, root_window);
+ }
+
+ function focus_window(wnd: OurWindow) {
+ // Unfocus the old window
+ if (focused_window)
+ focused_window.visuals.classList.remove('focus');
+ focused_window = wnd;
+ // And focus the new one!
+ if (wnd) {
+ wnd.visuals.classList.add('focus');
+ wnd.visuals.style.zIndex = (depth ++).toString();
+ }
+ }
+
+ // Delete the focused window
+ function delete_window(wnd: OurWindow) {
+ var index = windows.indexOf(wnd);
+
+ if (index >= 0)
+ windows.splice(index, 1);
+
+ wnd.visuals.parentNode.removeChild(wnd.visuals);
+ if (wnd == focused_window)
+ focused_window = null;
+ }
+
+ // Create a right click context menu
+ function context(e, entries) {
+ if (context_menu)
+ context_menu.remove();
+
+ context_menu = mk(document.body, 'ul', 'context');
+
+ context_menu.onmousedown = (e) => {
+ e.stopPropagation();
+ }
+
+ context_menu.onclick = (_e) => {
+ context_menu.remove();
+ context_menu = null;
+ }
+
+
+ context_menu.style.left = e.clientX + "px";
+ context_menu.style.top = e.clientY + "px";
+
+ for (const e of entries) {
+ const li = document.createElement('li');
+ li.innerText = e[0];
+ li.onclick = e[1];
+ context_menu.appendChild(li);
+ }
+ }
+
+ // This is called whenever the <input type="file">'s value changes
+ function on_file_added(_e) {
+ if (the_file.files.length >= 1) {
+
+ if (override_file) {
+ filename_input.value = override_file_filename;
+ override_input.value = "1";
+ upload_parent_directory.value = override_file_path;
+ console.log(filename_input.value, override_input.value, upload_parent_directory.value);
+ } else {
+ filename_input.value = the_file.files[0].name;
+ override_input.value = "0";
+ upload_parent_directory.value = get_path(focused_window);
+ }
+
+ if (!FORM_ASYNC) {
+ upload_form.submit();
+ return;
+ }
+
+ // Send the form asynchronously through the fetch api
+ fetch(upload_form.action, {
+ method: upload_form.method,
+ body: new FormData(upload_form)
+ }).then((resp) => {
+ if (resp.status == 200) {
+ // Reload the directory so the user can see the newly uploaded file
+ openfile(true, focused_window);
+ } else {
+ alert("Upload failed");
+ }
+ }, () => {
+ alert("Upload failed")
+ });
+ }
+ else {
+ alert("No files selected");
+ }
+ }
+
+ // It's honestly really sad that we need this
+ // We have an image viewer, but we load the uploaded via the XMLHttpRequest API, which gives us an array buffer
+ // We need to base64 encode the image data so we can feed it into the <img src="...">
+ // and the standart base64 encode API is shit
+ // https://stackoverflow.com/questions/7370943/retrieving-binary-file-content-using-javascript-base64-encode-it-and-reverse-de
+ function base64ArrayBuffer(arrayBuffer) {
+ var base64 = ''
+ var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
+
+ var bytes = new Uint8Array(arrayBuffer)
+ var byteLength = bytes.byteLength
+ var byteRemainder = byteLength % 3
+ var mainLength = byteLength - byteRemainder
+
+ var a, b, c, d
+ var chunk
+
+ // Main loop deals with bytes in chunks of 3
+ for (var i = 0; i < mainLength; i = i + 3) {
+ // Combine the three bytes into a single integer
+ chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
+
+ // Use bitmasks to extract 6-bit segments from the triplet
+ a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
+ b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
+ c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
+ d = chunk & 63 // 63 = 2^6 - 1
+
+ // Convert the raw binary segments to the appropriate ASCII encoding
+ base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
+ }
+
+ // Deal with the remaining bytes and padding
+ if (byteRemainder == 1) {
+ chunk = bytes[mainLength]
+
+ a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
+
+ // Set the 4 least significant bits to zero
+ b = (chunk & 3) << 4 // 3 = 2^2 - 1
+
+ base64 += encodings[a] + encodings[b] + '=='
+ } else if (byteRemainder == 2) {
+ chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
+
+ a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
+ b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
+
+ // Set the 2 least significant bits to zero
+ c = (chunk & 15) << 2 // 15 = 2^4 - 1
+
+ base64 += encodings[a] + encodings[b] + encodings[c] + '='
+ }
+
+ return base64
+ }
+
+
+ // This updates the path of the window's DOM (the "Root > Folder1 > Folder2 > foo.png")
+ function update_path_visuals(wnd) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ wnd = focused_window;
+ }
+
+ var the_path = wnd.visuals.getElementsByClassName('path')[0];
+
+ // Remove the old path
+ while (the_path.children.length > 0)
+ the_path.removeChild(the_path.lastChild);
+
+ for (let i = -1; i < wnd.pwd.length; i++) {
+ var d;
+ // For each element after the first create a separator
+ if (i >= 0) {
+ d = wnd.pwd[i];
+ var separator_div = mk(the_path, 'div', 'separator');
+ separator_div.innerText = "ยป";
+ }
+ else
+ d = "Root";
+
+ var entry = mk(the_path, 'button', 'pathentry');
+ entry.innerText = d;
+
+ // When we click the entry, go to its folder
+ entry.onclick = (_e) => {
+ if (length < wnd.pwd.length) {
+ wnd.pwd.length = i + 1;
+ openfile(true, wnd);
+ }
+ }
+
+ // We can drop files onto the path, which will omve them to teh folder
+ entry.onmouseup = (e) => {
+ if (dragging && dragging_fileview) {
+ var new_folder = get_path(wnd, i + 1);
+ move_file(dragging_fileview.wnd, wnd, new_folder, dragging_fileview.filename);
+ end_drag(e);
+
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ }
+ }
+ }
+
+
+ // This asks the server for the contents of the specified file
+ // The 'cb' callback is then called, which gives you the file as either text or binary
+ // depending on whether or not text is true/false
+ function read_file_contents(text, cb, folder, filename) {
+ var data = new FormData();
+ data.append('folder', folder);
+ data.append('filename', filename);
+
+ let xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/readfile.php', true);
+
+ if (text) {
+ xhr.onload = function () {
+ cb(xhr.responseText);
+ };
+ } else {
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function () {
+ cb(xhr.response);
+ };
+ }
+
+ xhr.send(data);
+ }
+
+
+ // This opens a file.
+ // If the file has image/* mimetype, it will be displayed as an image
+ // otherwise it will be displayed as plaintext
+ function openfile_nondir(wnd) {
+
+ while (wnd.filecontents.children.length > 0)
+ wnd.filecontents.removeChild(wnd.filecontents.lastChild);
+
+ // Send a request to readfile.php, which will give us the contents
+ var data = new FormData();
+ data.append('folder', get_path(wnd, wnd.pwd.length - 1));
+ data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
+
+ var xhr = new XMLHttpRequest();
+
+ update_path_visuals(wnd);
+
+ xhr.open('POST', '/php/readfile.php', true);
+
+ wnd.filecontents.innerText = "";
+ wnd.filecontentsroot.style.display = 'flex';
+ wnd.foldercontents.style.display = 'none';
+
+ let is_image = open_file.mimetype.split("/")[0] == "image";
+ wnd.save_btn_container.style.display = (open_file.write_permissions && !is_image) ? "flex" : "none";
+
+ if (is_image) {
+ xhr.responseType = 'arraybuffer';
+ xhr.onload = function () {
+ let b = `data:image/png;base64,${base64ArrayBuffer(xhr.response)}`;
+ wnd.filecontents.style.backgroundImage = `url('${b}')`;
+ wnd.filecontents.classList.add('imgview');
+ }
+ }
+ else {
+ wnd.filecontents.classList.remove('imgview');
+ wnd.filecontents.style.backgroundImage = "unset";
+
+ wnd.txt_editor = mk(wnd.filecontents, 'pre');
+
+ xhr.onload = function () {
+ wnd.txt_editor.innerText = xhr.responseText;
+ if (open_file.write_permissions)
+ wnd.txt_editor.contentEditable = "true";
+ };
+ }
+
+ xhr.send(data);
+ }
+
+ // This is a tiny wrapper around the share_window.
+ function share(in_file: boolean, filename: string, wnd: OurWindow) {
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ } else {
+ var folder = get_path(wnd);
+ }
+
+ var wnd = make_share_window(folder, filename);
+ focus_window(wnd);
+ }
+
+ // Replace an existing file with a new one
+ function replace_file(in_file, filename, wnd) {
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ } else {
+ var folder = get_path(wnd);
+ }
+
+ override_file = true;
+ override_file_path = folder;
+ override_file_filename = filename;
+ the_file.click();
+ }
+
+ // This loads the contents of the current directory
+ function openfile_dir(wnd) {
+ update_path_visuals(wnd);
+
+ var data = new FormData();
+ data.append('path', get_path(wnd));
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/readdir.php', true);
+ xhr.onload = function () {
+ for (const f of wnd.files)
+ f.visuals.remove();
+ wnd.files = [];
+
+ var json = JSON.parse(xhr.responseText);
+ if (!json)
+ return;
+
+ // Create the FileViews from the json response
+ for (const f of json) {
+ var view = new FileView(f.name,
+ wnd,
+ f.mimetype,
+ f.is_directory && f.is_directory != "0",
+ f.can_edit && f.can_edit != "0");
+ wnd.files.push(view);
+ }
+
+ // Sort the files nicely before adding their visuals
+ // Folders come first, then files, then the special trash directory
+ // Everything inside the categories is lexically sorted
+ wnd.files.sort((a, b) => {
+ if (wnd.pwd.length == 0 && a.filename == "share")
+ return -10;
+ if (wnd.pwd.length == 0 && b.filename == "share")
+ return 10;
+
+ if (wnd.pwd.length == 0 && a.filename == "trash")
+ return 10;
+ if (wnd.pwd.length == 0 && b.filename == "trash")
+ return -10;
+ if (a.is_directory && !b.is_directory)
+ return -1;
+ if (!a.is_directory && b.is_directory)
+ return 1;
+ return a.filename.localeCompare(b.filename);
+ });
+
+ for (const f of wnd.files) {
+ add_file_visuals(f, wnd);
+ }
+
+ };
+ xhr.send(data);
+
+ wnd.filecontentsroot.style.display = 'none';
+ wnd.foldercontents.style.display = 'flex';
+
+ wnd.foldercontents.onmouseup = () => {
+ if (dragging && dragging_fileview) {
+ move_file(dragging_fileview.wnd, wnd, get_path(wnd), dragging_fileview.filename);
+ }
+ }
+ }
+
+
+ function openfile(is_directory, wnd) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ console.trace();
+ wnd = focused_window;
+ }
+
+ if (is_directory) {
+ openfile_dir(wnd);
+ } else {
+ openfile_nondir(wnd);
+ }
+ }
+
+ function move_to_trash(wnd, filename) {
+ move_file(wnd, wnd, "/trash", filename, path_combine(get_path(wnd), filename));
+ }
+
+ function restore_from_trash(wnd, filename) {
+ var split = filename.split("/");
+ var new_filename = split.pop();
+ var new_directory = "/" + split.join("/");
+
+ move_file(wnd, wnd, new_directory, filename, new_filename);
+ }
+
+
+ // This deletes the file, *for real*
+ // move_to_trash is what is actually called when the user clicks 'Delete'
+ function delete_file(wnd, filename) {
+ var data = new FormData();
+ data.append('folder', get_path(wnd));
+ data.append('filename', filename);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/delete.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+
+ function rename_file(filename, wnd) {
+ var new_name = prompt(`Rename ${filename} to`, filename);
+ if (!new_name)
+ return;
+
+ var data = new FormData();
+ data.append('folder', get_path(wnd));
+ data.append('old_filename', filename);
+ data.append('new_filename', new_name);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/rename.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+
+ function move_file(srcwnd: OurWindow, dstwnd: OurWindow, new_folder: string, filename: string, new_filename?: string) {
+ if (!new_filename)
+ new_filename = filename;
+
+ var data = new FormData();
+ data.append('old_folder', get_path(srcwnd));
+ data.append('new_folder', new_folder);
+ data.append('filename', filename);
+ data.append('new_filename',new_filename);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/move.php', true);
+ xhr.onload = () => {
+ openfile(true, srcwnd);
+ openfile(true, dstwnd);
+ };
+ xhr.send(data);
+ }
+
+ function new_folder(wnd) {
+ var dirname = prompt(`Directory name`, "New Folder");
+ if (!dirname)
+ return;
+
+ var data = new FormData();
+ data.append('parent_directory', get_path(wnd));
+ data.append('dirname', dirname);
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/mkdir.php', true);
+ xhr.onload = function () {
+ openfile(true, wnd);
+ };
+ xhr.send(data);
+ }
+
+
+ // Dragging a fileview is a bit different from dragging a window
+ // This does some setup work before calling the common begin_drag
+ function begin_drag_fileview(e, fileview) {
+ if (dragging)
+ end_drag(e);
+
+ // The dragging_placeholder is inserted into its place by the begin_drag function
+ dragging_placeholder = document.createElement('div');
+ dragging_fileview = fileview;
+
+ dragging = fileview.visuals;
+ dragging.style.zIndex = 50000;
+
+ begin_drag(e, fileview.visuals);
+ }
+
+ // Start dragging the 'obj' DOM element
+ // e is a DOM event, this should only get called in response of a DOM event
+ function begin_drag(e, obj: HTMLElement, dont_set_width?: boolean) {
+ set_iframe_enabled(false);
+ dragging = obj;
+ dragging_candidate = null;
+ dragging.classList.add("dragged");
+
+ var elemRect = dragging.getBoundingClientRect();
+ dragging_offset_x = e.clientX - elemRect.left;
+ dragging_offset_y = -e.clientY + elemRect.top;
+
+ if (dragging_placeholder)
+ obj.parentNode.insertBefore(dragging_placeholder, obj);
+
+ dragging.style.left = (e.clientX - dragging_offset_x) + "px";
+ dragging.style.top = (e.clientY + dragging_offset_y) + "px";
+
+ if (!dont_set_width) {
+ dragging.style.width = elemRect.width + "px";
+ dragging.style.height = elemRect.height + "px";
+ }
+
+ dragging.style.position = "absolute";
+ document.body.appendChild(dragging);
+ }
+
+ function end_drag(_e) {
+ set_iframe_enabled(true);
+
+ // If there's a dragging placeholder remove it and put the dragged node back into its place
+ if (dragging_placeholder) {
+ dragging_placeholder.parentNode.insertBefore(dragging, dragging_placeholder);
+ dragging_placeholder.remove();
+ dragging_placeholder = null;
+ }
+
+ // If we were dragging a FileView, we need to reset some CSS
+ if (dragging_fileview) {
+ dragging.style.removeProperty("position");
+ dragging.style.removeProperty("width");
+ dragging.style.removeProperty("height");
+ dragging.style.removeProperty("left");
+ dragging.style.removeProperty("top");
+ dragging_fileview = null;
+ }
+
+ dragging.classList.remove("dragged");
+ dragging = null;
+ }
+
+ // This creates the parts of a window that are common between all window types
+ // This should only really be called by another function that will then fill up the window
+ function make_window_base(pwd, x, y, w, h) {
+ var wnd = new OurWindow(pwd);
+ windows.push(wnd);
+
+ wnd.visuals = mk(document.body, 'div', 'window');
+
+ wnd.visuals.style.width = w + "px";
+ wnd.visuals.style.height = h ? (h + "px") : "unset";
+ wnd.visuals.style.position = "absolute";
+ wnd.visuals.style.left = x + "px";
+ wnd.visuals.style.top = y + "px";
+
+ wnd.h2 = mk(wnd.visuals, 'h2');
+
+ wnd.visuals.onmousedown = (_e) => {
+ focus_window(wnd);
+ }
+
+ wnd.h2.onmousedown = (e) => {
+ if (!dragging) {
+ dragging_candidate = wnd.visuals;
+ dragging_candidate_x = e.clientX;
+ dragging_candidate_y = e.clientY;
+ }
+ };
+
+ return wnd;
+ }
+
+ // This is a widely abused helper function that creates a DOM element, attaches it as the
+ // last child of 'parent' and possibly gives it a class
+ function mk(parent: HTMLElement, type: keyof HTMLElementTagNameMap, _class?: string): HTMLElement {
+ var el = document.createElement(type);
+ parent.appendChild(el);
+ if (_class)
+ el.classList.add(_class);
+ return el;
+ }
+
+ // Crate a horizontal div
+ function mkhdiv(parent) {
+ var hdiv = mk(parent, 'div');
+ hdiv.style.display = "flex";
+ hdiv.style.alignItems = "center";
+ hdiv.style.padding = "0.3rem";
+ hdiv.style.gap = "0.3rem";
+ return hdiv;
+ }
+
+ // Create a checkbocx with a label.
+ // togglefn will be called when its value changes with an argument that's either true/false
+ function mkcheckbox(parent, label, togglefn) {
+ var hdiv = mkhdiv(parent);
+
+ var write_checkbox: HTMLInputElement = mk(hdiv, 'input') as any;
+ write_checkbox.type = 'checkbox';
+
+ var write_checkbox_label = mk(hdiv, 'label');
+ write_checkbox_label.innerText = label;
+ write_checkbox_label.onclick = (_e) => { write_checkbox.click(); }
+ write_checkbox_label.classList.add('noselect');
+
+ write_checkbox.onchange = (_e) => {
+ togglefn(write_checkbox.checked);
+ };
+ }
+
+ // This monstrocity creates the 'Share file' window
+ function make_share_window(folder, filename): OurWindow {
+ var wnd = make_window_base(null, 400, 400, 400, 0);
+
+ wnd.h2.style.display = 'flex';
+
+ // The title of the window. WE set its 'flex' to 1 1 0 so it fills up the titlebar
+ // and pushes the X button to the very right
+ var heading = mk(wnd.h2, 'span', 'wndtitle');
+ heading.innerText = "Share " + filename;
+
+ // Close button
+ var x_button = mk(wnd.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = () => delete_window(wnd);
+
+ wnd.foldercontents = mk(wnd.visuals, 'div', 'share_dialog_contents');
+ wnd.foldercontents.style.padding = "0.5rem";
+
+ // This is the data that will be sent when we hit "Generate link"
+ var data = {
+ write_permissions: false,
+ private: false,
+ has_password: false,
+ password: "",
+ userlist: [],
+ }
+
+ // If private link is clicked, show the "Add user" button and the user list
+ var userlist, add_user;
+ mkcheckbox(wnd.foldercontents, "Private link", (toggled) => {
+ add_user.style.display = toggled ? "block" : "none";
+ userlist.style.display = toggled ? "block" : "none";
+ data.private = toggled;
+ });
+
+ userlist = mk(wnd.foldercontents, 'div');
+ userlist.style.display = "none";
+ add_user = mk(wnd.foldercontents, 'button');
+ add_user.innerText = "Add user";
+ add_user.style.display = "none";
+
+ // When we hit 'Add user', add an input field for a new user
+ add_user.onclick = (_e) => {
+ var i = mk(userlist, 'input') as HTMLInputElement;
+ i.value = 'John Doe';
+
+ let index = data.userlist.length;
+ data.userlist.push(i.value);
+
+ i.onchange = (_e) => {
+ data.userlist[index] = i.value;
+ }
+ }
+
+ // Click the add_user once to add a default user, since a URL that nobody can use makes no sense
+ add_user.click();
+
+ mkcheckbox(wnd.foldercontents, "Give write permissions", (toggled) => {
+ data.write_permissions = toggled;
+ });
+
+ // If 'Password protected' is checked, show the password field
+ let password_container;
+ mkcheckbox(wnd.foldercontents, "Password protected", (toggled) => {
+ data.has_password = toggled;
+ password_container.style.display = toggled ? "flex" : "none";
+ });
+
+ password_container = mkhdiv(wnd.foldercontents);
+ password_container.style.display = 'none'
+ var password_label = mk(password_container, 'label');
+ password_label.innerText = "Password";
+ var password_input = mk(password_container, 'input') as HTMLInputElement;
+ password_input.type = 'password';
+ password_input.autocomplete = 'off'
+
+ password_input.style.flex = "1 0 0";
+ password_input.onchange = (_e) => {
+ data.password = password_input.value;
+ };
+
+ var generate_url_button = mk(wnd.foldercontents, 'button');
+ generate_url_button.innerText = "Generate link";
+
+ generate_url_button.onclick = () => {
+ // The backend expects the users to be either an empty string, if the URL is public
+ // or a comma separated list of usernaems
+ var users = "";
+ if (data.private) {
+ users = data.userlist.join(',');
+ }
+
+ var form_data = new FormData();
+ form_data.append('folder', folder);
+ form_data.append('filename', filename);
+ form_data.append('users', users);
+ // 0 = No permissions, 1 = Read only, 2 = Write , 1|2 = 3 = RW
+ // Only 1 and 3 make sense in the context of a URL
+ form_data.append('permissions', (data.write_permissions ? 3 : 1).toString());
+ form_data.append('password', data.has_password ? data.password : "");
+
+ var xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/share.php', true);
+ xhr.onload = function () {
+ alert(xhr.response);
+ }
+
+ xhr.send(form_data);
+ delete_window(wnd);
+ }
+
+ return wnd;
+ }
+
+ function download_file(in_file, filename?: any, wnd?: any) {
+ if (!wnd) {
+ alert (802);
+ wnd = focused_window;
+ }
+ if (in_file) {
+ var folder = get_path(wnd, wnd.pwd.length - 1);
+ filename = wnd.pwd[wnd.pwd.length - 1];
+ } else {
+ var folder = get_path(wnd);
+ }
+
+ // Read the file contents and then do DISGUSTING javascript things to download the ifle
+ // We create a invisible <a> that we click and then delete
+ // That <a> has its download attribute set so we download the contents instead of opening it in a new tab
+ // and of course its href is a virtual object URL that has its content set to a blob
+ read_file_contents(false, (x) => {
+ var blob = new Blob([new Uint8Array(x, 0, x.length)]);
+ var url = URL.createObjectURL(blob);
+ var a = document.createElement('a');
+ a.href = url;
+ a.download = filename;
+ document.body.appendChild(a);
+ a.click();
+ setTimeout(() => {
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ });
+ }, folder, filename);
+
+ return;
+ }
+
+
+ // make_window creates an explorer window - the kind that can list directories/open files
+ function make_window(pwd: string[], has_close: boolean): OurWindow {
+ var wnd = make_window_base(pwd, 100, 100, 800, 600);
+
+ mk(wnd.h2, 'div', 'path');
+
+ if (has_close) {
+ var x_button = mk(wnd.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = () => delete_window(wnd);
+ }
+
+ // wnd.foldercontents is where the FileViews will be stored
+ // it also has a subheader (h3) with 'Upload' and 'New FOlder' buttons
+ {
+ wnd.foldercontents = mk(wnd.visuals, 'div', 'foldercontents');
+ var h3 = mk(wnd.foldercontents, 'h3');
+
+ var upload_btn = mk(h3, 'button');
+ upload_btn.innerText = "Upload";
+
+ upload_btn.onclick = () => {
+ override_file = false;
+ the_file.click();
+ }
+
+ mk(h3, 'div', 'separator');
+
+ var new_folder_btn = mk(h3, 'button');
+ new_folder_btn.innerText = "New Folder";
+ new_folder_btn.onclick = () => { new_folder(wnd); }
+
+ mk(h3, 'div', 'separator');
+
+ wnd.filegrid = mk(wnd.foldercontents, 'div', 'files');
+ }
+
+ // wnd.filecontentsroot is where the filedata will be stored for open files
+ // it also has a subheader (h3) with Share and Download buttons
+ {
+ wnd.filecontentsroot = mk(wnd.visuals, 'div', 'filecontentsroot');
+ var h3 = mk(wnd.filecontentsroot, 'h3');
+
+ let download_btn = mk(h3, 'button');
+ download_btn.innerText = "Download";
+ download_btn.onclick = () => { download_file(true); }
+ mk(h3, 'div', 'separator');
+
+ let share_btn = mk(h3, 'button');
+ share_btn.innerText = "Share";
+ share_btn.onclick = () => { alert("TODO NOT IMPLEMENTETD"); } //share(true, fileview.filename, wnd); }
+ mk(h3, 'div', 'separator');
+
+ wnd.save_btn_container = mk(h3, 'div');
+ wnd.save_btn_container.style.display = 'flex';
+
+ let save_btn = mk(wnd.save_btn_container, 'button');
+ save_btn.innerText = "Save";
+ save_btn.onclick = () => save_open_text_file(wnd);
+ mk(wnd.save_btn_container, 'div', 'separator');
+
+ wnd.filecontents = mk(wnd.filecontentsroot, 'div', 'filecontents');
+ }
+
+ return wnd;
+ }
+
+
+ function save_open_text_file(wnd) {
+ const contents = wnd.txt_editor.innerText;
+ let xhr = new XMLHttpRequest();
+ xhr.open('POST', '/php/upload.php', true);
+
+ var data = new FormData();
+ data.append('parent_directory', get_path (wnd, wnd.pwd.length - 1));
+ data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
+ data.append('content', contents);
+ data.append('overwrite', '1');
+
+ xhr.send(data);
+ }
+
+
+ // Create the visuals for a FileView
+ function add_file_visuals(fileview, wnd) {
+ // Are we in a subdirectory of the trash folder?
+ var is_in_trash = wnd.pwd.length > 0 && wnd.pwd[0] == "trash";
+
+ // Is the current filewview the trash folder itself?
+ var is_trash = wnd.pwd.length == 0 && fileview.filename == "trash";
+ var is_share = wnd.pwd.length == 0 && fileview.filename == "share";
+
+ var visuals = mk(wnd.filegrid, 'div');
+ fileview.visuals = visuals;
+
+ var img = document.createElement('img');
+ var filename = document.createElement('div');
+
+ if (fileview.is_directory) {
+ if (get_path(wnd) == "/" && fileview.filename == "trash")
+ img.src="/mimeicons/user-trash.png";
+ else if (get_path(wnd) == "/" && fileview.filename == "share")
+ img.src = "/mimeicons/user-share.png";
+ else
+ img.src="/mimeicons/directory.png";
+ } else {
+ img.src=`/mimeicons/${fileview.mimetype.replace("/", "-")}.png`;
+ }
+
+ fileview.visuals.onclick = () => {
+ wnd.pwd.push(fileview.filename);
+ if (!fileview.is_directory) {
+ open_file = fileview;
+ }
+ openfile(fileview.is_directory, wnd);
+ }
+
+ visuals.oncontextmenu = (e) => {
+ if (!dragging) {
+
+ var context_list = [
+ // Open is always in the context list
+ ['Open', () => {
+ wnd.pwd.push(fileview.filename);
+ openfile(fileview.is_directory, wnd);
+ }],
+ ['Open in New Window', () => {
+ var new_pwd = wnd.pwd.slice();
+ new_pwd.push(fileview.filename);
+ var new_wnd = make_window(new_pwd, true);
+ open_file = fileview;
+ openfile(fileview.is_directory, new_wnd);
+ focus_window(new_wnd);
+ }],
+ ];
+
+ if (is_in_trash) {
+ // If we're in the trash, we can restore files or delete them forever
+ context_list.push(['Restore', () => { restore_from_trash(wnd, fileview.filename); }]);
+ context_list.push(['Delete forever', () => { delete_file(wnd, fileview.filename); }]);
+ } else if (!is_trash && !is_share) {
+ // If we;'re not in trash we can rename/share/download/move files to trash
+ context_list.push(
+ ['Rename', () => { rename_file(fileview.filename, wnd); }],
+ );
+ if (!fileview.is_directory) {
+ for (let a of actions) {
+ if (fileview.filename.endsWith(a.extension)) {
+ context_list.push(
+ [a.text, () => {
+ read_file_contents(true, (x) => {
+ const ue = encodeURIComponent(x);
+ let url = a.url.replace("$content_urlencoded", ue)
+ .replace("$filename", fileview.filename);
+
+ if (a.open_in_iframe) {
+ const wnd = make_window_base([], 10, 10, 800, 600);
+
+ var title = mk(wnd.h2, 'span', 'wndtitle');
+ title.innerText = fileview.filename;
+
+ // Close button
+ var x_button = mk(wnd.h2, 'button', 'close_button');
+ x_button.innerText = "X";
+ x_button.onclick = () => delete_window(wnd);
+
+ const contents = mk(wnd.visuals, 'div', 'filecontentsroot');
+ const iframe = mk(contents, 'iframe') as HTMLIFrameElement;
+ iframe.style.flex = '1 0 0';
+ iframe.src = url;
+
+ focus_window(wnd);
+ } else {
+ window.location = url;
+ }
+ }, get_path(wnd), fileview.filename);
+ }]
+ );
+ }
+ }
+
+ if (fileview.write_permissions) {
+ context_list.push(
+ ['Replace', () => { replace_file(false, fileview.filename, wnd); }],
+ );
+ }
+ context_list.push(
+ ['Share', () => { share(false, fileview.filename, wnd); }],
+ ['Download', () => { download_file(false, fileview.filename); }],
+ );
+ }
+ context_list.push(
+ ['Delete', () => { move_to_trash(wnd, fileview.filename); }]
+ );
+ }
+
+ context(e, context_list);
+ }
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ visuals.ondragstart = (e) => {
+ if (is_trash || is_in_trash || is_share) {
+ e.preventDefault();
+ return;
+ }
+ begin_drag_fileview(e, fileview);
+ e.preventDefault();
+ };
+
+ visuals.onmouseup = (e) => {
+ if (dragging) {
+ if (fileview.is_directory) {
+ if (get_path(wnd) == "/" && fileview.filename == "trash") {
+ // If we've dragged something onto the trashcan, it's trash
+ move_to_trash(wnd, dragging_fileview.filename);
+ }
+ else if (get_path(wnd) == "/" && fileview.filename == "share") {
+ // move to 'share' is invalid
+ } else {
+ // If we've dragged something onto a directory, move it into that directory
+ move_file(dragging_fileview.wnd, wnd, path_combine(get_path(wnd), fileview.filename), dragging_fileview.filename);
+ }
+ } else {
+ // alert(`Dropped ${dst.filename} on ${src.filename}`);
+ }
+ end_drag(e);
+ }
+ e.preventDefault();
+ };
+
+ visuals.classList.add('file');
+ filename.classList.add('filename');
+
+ if (is_in_trash) {
+ var split = fileview.filename.split("/");
+ filename.innerText = split[split.length - 1];
+ } else if (is_trash) {
+ filename.innerText = "Trash";
+ } else if (is_share) {
+ var x = mk(filename, 'span');
+ x.style.fontSize = "0.8rem";
+ x.innerText = "Shared with me";
+ } else{
+ filename.innerText = fileview.filename;
+ }
+
+ visuals.appendChild(img);
+ visuals.appendChild(filename);
+ }
+
+
+ // Reads the 'pwd' of the focused window
+ // If pwd is ['foo', 'bar', 'baz'], this returns '/foo/bar/baz'
+ function get_path(wnd: OurWindow, max_length?: number) {
+ if (!wnd) {
+ alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
+ console.trace();
+ wnd = focused_window;
+ }
+ if (max_length == undefined) {
+ max_length = wnd.pwd.length;
+ }
+
+ var path = "/";
+ for (let i = 0; i < max_length; i++) {
+ path += wnd.pwd[i];
+ if (i != max_length - 1)
+ path += "/";
+ }
+ return path;
+ }
+
+ function path_combine(a, b) {
+ const last_char = a.slice(-1);
+ if (last_char == "/")
+ return a + b;
+ else
+ return a + "/" + b;
+ }
+
+
+ // When we click anywhere, remove the context menu
+ // The context menu itself has a onmousedown that prevents propagation so we can click its elements
+ document.body.onmousedown = (_e) => {
+ if (context_menu) {
+ context_menu.remove();
+ context_menu = null;
+ }
+ }
+
+ document.body.onmousemove = (e) => {
+ if (dragging) {
+ dragging.style.left = (e.clientX - dragging_offset_x) + "px";
+ dragging.style.top = (e.clientY + dragging_offset_y) + "px";
+ }
+ else if (dragging_candidate) {
+ var d = Math.abs(e.clientX - dragging_candidate_x) + Math.abs(e.clientY - dragging_candidate_y);
+ if (d > 15)
+ begin_drag(e, dragging_candidate, true);
+ }
+ }
+
+ document.body.onmouseup = (_e) => {
+ if (dragging_candidate)
+ dragging_candidate = null;
+ if (dragging)
+ end_drag(_e);
+ }
+
+ document.body.oncontextmenu = (e) => {
+ if (dragging) {
+ end_drag(e);
+ e.preventDefault();
+ }
+ if (context_menu) {
+ context_menu.remove();
+ context_menu = null;
+ }
+ }
+
+ the_file.onchange = (e) => { on_file_added(e); };
+
+ function set_iframe_enabled(en) {
+ for (const iframe of document.getElementsByTagName('iframe'))
+ iframe.hidden = !en;
+ }
+
+ main();
F diff --git a/img/arrow.svg b/img/arrow.svg
new file mode 100644
--- /dev/null
+++ b/img/arrow.svg
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ <svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="128.46289"
+ height="620.49023"
+ viewBox="0 0 33.989141 164.17137"
+ version="1.1"
+ id="svg8"
+ sodipodi:docname="arrow.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1033"
+ inkscape:window-height="1050"
+ id="namedview58"
+ showgrid="false"
+ inkscape:zoom="1.3908357"
+ inkscape:cx="62.793461"
+ inkscape:cy="309.88562"
+ inkscape:window-x="16"
+ inkscape:window-y="25"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg8"
+ inkscape:document-rotation="0" />
+ <defs
+ id="defs2" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ transform="translate(-9.8831177,-26.865543)"
+ style="fill:#ffffff;fill-opacity:1">
+ <path
+ id="rect12"
+ style="fill:#ffffff;fill-rule:evenodd;stroke-width:0.860931;fill-opacity:1"
+ d="M 101.58398,101.53906 37.353516,254.44747 75.386719,234.3418 v 487.68554 l 26.197261,-20.0516 26.19922,20.05356 V 234.3418 l 38.03321,20.10567 z"
+ transform="scale(0.26458333)" />
+ </g>
+ </svg>
F diff --git a/img/bottom.svg b/img/bottom.svg
new file mode 100644
--- /dev/null
+++ b/img/bottom.svg
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ <svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1920"
+ height="1080"
+ viewBox="0 0 508 285.75"
+ version="1.1"
+ id="svg8"
+ sodipodi:docname="bottom.svg"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1080"
+ id="namedview879"
+ showgrid="false"
+ inkscape:zoom="0.69739583"
+ inkscape:cx="698.31217"
+ inkscape:cy="540"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="layer1" />
+ <defs
+ id="defs2" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1">
+ <path
+ id="path67"
+ style="fill:#231179;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
+ d="m 497.63807,220.97629 c -0.21383,0.005 -0.42079,0.0476 -0.61497,0.1447 -0.30964,0.15483 -0.44627,0.54229 -0.73432,0.73432 -0.87445,0.58298 -2.75508,0.63823 -3.71295,1.11569 -0.31176,0.15541 -0.43654,0.56558 -0.73896,0.73846 -1.20155,0.6869 -2.47454,1.23984 -3.71245,1.8588 -3.67231,1.83615 -5.9033,1.81488 -9.942,1.81488 -3.68084,0 -0.648,2.88761 -3.12749,5.36711 -2.18828,2.1883 -4.03066,1.78386 -6.69104,2.60243 -3.72497,1.1462 0.8976,0.48362 -3.2489,1.11518 -1.41195,0.21506 -3.33134,-0.5085 -4.6142,0.35398 -0.80788,0.54319 -1.43637,1.32595 -2.2443,1.86913 -1.32096,0.88812 -1.62378,-0.004 -2.72386,1.46245 -0.49406,0.65874 -0.78574,1.4507 -1.0976,2.21278 -0.023,0.0565 0.0278,0.6494 0,0.6718 -0.7134,0.57388 -2.97177,2.19666 -3.95481,2.48925 -1.4324,0.42634 -3.91221,0.20244 -5.4348,0.37207 -1.82431,0.20323 -3.10261,0.97046 -4.82349,1.48673 -1.50932,0.45279 -3.41048,0.36304 -4.78835,0.92139 -0.51356,0.20812 -0.9615,0.56787 -1.48725,0.74311 -0.0245,0.008 -2.70674,0.24511 -2.87269,0.37207 -1.17967,0.90248 -2.13,2.36269 -3.58685,2.85254 -2.67652,0.89994 -5.15641,-0.79198 -7.26725,-1.85415 -0.30681,-0.15438 -0.41148,-0.59684 -0.72864,-0.72864 -0.58393,-0.24268 -1.09873,0.24949 -1.58906,0.37207 -0.24043,0.0601 -0.52194,-0.11083 -0.74361,0 -0.31348,0.15674 -0.45149,0.5487 -0.74311,0.74311 -1.4795,0.98633 -2.2283,1.44756 -2.59519,1.64589 -0.28194,0.2849 -0.84371,0.80561 -1.86603,1.70016 -0.67249,0.5884 -1.59388,0.85958 -2.23036,1.48673 -0.25789,0.2541 -0.24715,0.81774 -0.64339,0.99374 -0.5648,0.25087 -1.23613,0 -1.85414,0 -4.73573,0 -7.96452,-1.07723 -11.62876,1.85415 -2.64559,2.1165 -3.55491,5.00358 -5.18416,7.78608 -1.01481,1.73315 -3.39051,2.5346 -4.6762,3.94085 -0.25992,0.28606 0.0373,0.88714 -0.26252,1.13274 -2.72698,2.23357 -3.12936,2.20431 -4.84571,5.20124 -0.63365,1.10639 -0.16473,1.62327 -0.74361,2.97398 -0.4617,1.07729 -1.68582,1.73516 -2.21279,2.95589 -0.15565,0.36056 0.1827,0.87171 0,1.23713 -0.15674,0.31348 -0.61343,0.41823 -0.74364,0.74363 -0.13806,0.34515 0.0729,0.75065 0,1.11517 -0.35898,1.79489 -2.9442,4.69887 -4.82811,5.07566 -0.76573,0.15314 -5.75429,0.27416 -6.59442,0 -0.7656,-0.24984 -1.38044,-1.27974 -2.16059,-1.08003 -1.67865,0.42973 -3.77251,-0.15703 -5.34128,0.37155 -3.36248,1.13293 -5.64478,1.12189 -9.18964,1.12189 -0.99578,0 -2.63472,0.25916 -3.67469,0 -2.09669,-0.52243 -2.29434,-1.87255 -3.70263,-2.2257 -1.77134,-0.44418 -5.93903,0.26012 -7.44347,0.75138 -0.23342,0.0762 -0.41355,0.33979 -0.65887,0.32969 -0.79372,-0.0326 -1.12755,-1.18561 -1.86243,-1.48725 -0.81142,-0.33305 -1.67338,-0.52834 -2.52179,-0.75085 -1.61312,-0.42308 -2.45994,0.27724 -3.62614,1.05471 -0.6471,0.4314 -1.85804,-0.24986 -2.61689,0 -1.10773,0.36475 -2.40581,1.0697 -3.6091,1.0697 -0.33743,0 -0.39614,-0.60902 -0.71623,-0.71572 -1.69796,-0.56599 -2.40102,-0.74299 -4.01111,-1.78076 -0.30218,-0.19476 -5.05704,-0.49093 -6.29626,-1.11053 -1.46111,-0.73053 -0.001,-0.73679 -1.47278,-1.10588 -0.78605,-0.19712 -0.7843,0.33089 -1.48725,-0.37207 -1.71204,-1.71203 -2.5177,-2.76083 -4.95422,-3.92844 -1.48222,-0.71028 -4.07511,0.3212 -5.65496,-0.50746 -0.83762,-0.43935 -0.81479,-1.50876 -1.83036,-1.84434 -1.3996,-0.46243 -3.33833,0.23042 -4.80439,0 -0.26265,-0.0413 -0.47881,-0.2312 -0.71829,-0.34674 -0.0864,-0.0864 -0.14126,-0.2276 -0.25942,-0.2589 -2.29989,-0.60974 -4.98044,0.20989 -7.30446,-0.36742 -1.94929,-0.48422 -4.37084,0.63303 -6.26991,0 -0.81349,-0.27116 -1.47921,-1.00355 -2.17763,-1.46916 -1.66113,-1.10743 -3.94404,-2.43903 -5.88129,-2.94039 -2.71332,-0.70218 -6.00825,0.88449 -8.62894,-0.3669 -1.49247,-0.71266 -2.58787,-1.85912 -3.96565,-2.89182 -0.40918,-0.3067 -2.44325,-0.56894 -2.72128,-0.66094 -0.37565,-0.12431 -0.62032,-0.52561 -0.99839,-0.64234 -2.68235,-0.82824 -5.37604,1.01928 -7.96489,0.37207 -0.50788,-0.12697 -0.6247,-0.91598 -1.11052,-1.11104 -0.65105,-0.26141 -3.19028,-0.27466 -4.01423,0 -0.95413,0.31804 -1.67417,1.15422 -2.61948,1.49758 -0.72075,0.26179 -1.51901,0.22052 -2.26911,0.37982 -0.37422,0.0795 -0.67233,0.5779 -1.04231,0.4806 -0.24094,-0.0634 -0.47245,-0.66545 -0.71365,-0.71366 -1.61484,-0.32297 -1.69751,-0.19289 -3.33572,-0.73897 -1.73767,-0.57924 -4.1251,0.26068 -5.9433,0 -1.97976,-0.28384 -4.28466,-0.77562 -6.30504,-0.37155 -0.2717,0.0543 -0.4687,0.33715 -0.74363,0.37155 -0.8737,0.10921 -10.32235,-0.28155 -10.49031,-0.37155 -3.92899,-2.10471 -4.2422,-2.21796 -8.78965,-2.21796 -0.20941,0 -4.62015,0.46026 -4.79764,0.37156 -0.3996,-0.19981 -0.6856,-0.62037 -1.11518,-0.74311 -3.03456,-0.86701 -0.082,1.05631 -2.6019,-0.74362 -1.01452,-0.72465 -2.76881,-3.02727 -3.71761,-3.34553 -1.31732,-0.44188 -2.54185,0.37155 -3.69383,0.37155 -2.15526,0 -4.13542,-0.0666 -6.2699,-0.37155 -1.66509,-0.23787 -3.43728,0.26789 -5.09788,0 -1.98284,-0.3199 -4.136,-0.53441 -6.20841,-0.72606 -1.0062,-0.0931 -2.10829,0.41124 -3.03134,0 -0.32363,-0.14419 0.66285,-0.2512 0.2682,-0.50023 -0.21136,-0.13337 -0.52011,0.0985 -0.74982,0 -0.78632,-0.33698 -1.47729,-0.89788 -2.29547,-1.14773 -1.20609,-0.36831 -3.56557,0 -4.82037,0 -2.02487,0 -6.61095,0.55532 -8.13698,0 -0.21342,-0.0777 -2.86775,-2.04728 -3.00291,-1.90996 -2.28069,2.31718 -3.90435,6.13176 -7.23419,7.244 -1.40999,0.47097 -3.44576,-0.14158 -4.82296,-0.37207 -1.68505,-0.28202 -7.35983,-0.37619 -8.86457,0 -0.49565,0.12391 -1.04864,0.10922 -1.48673,0.37207 -0.70454,0.42273 -1.72072,2.05402 -2.60243,2.23036 -1.7418,0.34837 -3.64959,-0.22411 -5.41621,0 -1.67931,0.21305 -3.53647,1.11131 -5.25859,0.76687 -0.16445,-0.0329 -0.18995,-0.32893 -0.35553,-0.35553 -1.14451,-0.18388 -4.021125,-0.38821 -5.185728,0 -0.423844,0.14128 -0.70036,0.57718 -1.115176,0.74311 -1.451475,0.58059 -3.654618,0.62796 -5.204333,0.37207 -1.126384,-0.18598 -1.04458,-0.90503 -1.831414,-1.10174 -0.360638,-0.0902 -0.770028,0.13806 -1.115177,0 -0.325405,-0.13016 -0.411136,-0.6328 -0.743624,-0.74363 -1.240928,-0.41363 -3.213346,-0.17582 -4.460708,-0.37155 -1.809176,-0.2839 -3.827521,-1.04683 -5.587773,-1.47381 -1.64842,-0.39985 -1.741114,-1.86662 -4.748032,-2.16731 -5.409454,-0.54095 -11.412834,-0.011 -16.695105,0.74362 -1.681041,0.24015 -3.773774,1.91921 -5.947955,2.23036 -1.476822,0.21135 -3.366834,-0.47174 -4.75165,0 -0.96639,0.32924 -2.259102,0.63967 -3.331577,0.37155 -0.627976,-0.157 -1.788642,-1.05167 -2.480988,-0.70332 -0.742301,0.37349 -1.904894,2.61144 -2.8298,2.47375 -0.312037,-0.0464 -0.669301,-1.04087 -0.721921,-1.09347 -0.630166,-0.63017 -1.83686,-0.65414 -2.602423,-1.11518 -0.515459,-0.31042 -0.871453,-0.90037 -1.44539,-1.08107 -0.818266,-0.25763 -6.200195,-0.17458 -6.881752,0 -1.394754,0.35725 -2.689666,0.83539 -4.088639,1.11518 -1.923868,0.38478 -4.641963,-0.89646 -6.216675,-0.37155 -1.645038,0.54834 -2.970826,0.7431 -4.8322629,0.7431 -0.963813,0 -1.670468,-0.9414 -2.530078,-0.71933 -2.5866389,0.66822 -6.29791725,2.58785 -8.5498656,4.08916 -0.2916132,0.1944 -0.4187418,0.61215 -0.7436238,0.74362 -0.6323156,0.25588 -1.3415011,0.24764 -2.0122802,0.37155 l -1.0867554,9.08058 v 10e-4 c -0.3089068,1.2756 -0.2408467,2.75466 -0.7493081,3.8902 -0.1676488,0.3744 0.1500007,1.00218 0,1.37718 -0.1659266,0.41482 -0.6023428,0.69133 -0.7436239,1.11517 -0.5064281,1.51928 -0.2110941,3.23623 -0.7431072,4.83227 -0.294686,0.88406 -1.2685035,1.39606 -1.4696778,2.19573 -0.5917971,2.35242 0.6814589,4.4124 1.1105266,6.55774 0.072903,0.36451 -0.1175531,0.76252 0,1.11517 0.015277,0.0458 0.035384,0.0895 0.056844,0.1323 l -1.2014771,10.04073 v 5.3e-4 c -0.2488039,1.1631 -0.3110667,2.52356 -0.6004796,3.39047 -0.1427866,0.4277 -0.716228,0.67591 -0.750342,1.12551 -0.08441,1.11255 0.275246,2.21454 0.412894,3.32177 0,1.24931 -0.428128,3.31906 0,4.46071 0.253695,0.67654 0.8468392,1.18796 1.1151778,1.85881 0.9613397,2.40334 0.076328,4.32181 1.8691365,6.71224 0.7696115,1.02613 1.0081654,0.75964 2.2303549,1.48725 0.528941,0.3149 0.8693407,1.04037 1.3761434,1.37562 0.3255679,0.2154 0.9790305,-0.54211 1.1105264,0.37208 0.9503502,0.71276 1.82411345,2.06523 3.00343416,2.40761 2.25410904,0.65442 9.40822744,-0.79762 10.92078084,0 2.515322,1.3264 5.499643,0.96451 8.339025,1.18287 2.726979,0.20971 5.771924,0.75726 8.489923,0.90691 6.384589,0.35153 12.872973,0 19.277374,0 h 9.828858 c 1.519434,0 3.359555,-0.27707 4.857584,0 7.18616,1.32913 -0.753759,0.84958 9.474358,0.84958 H 100.3047 c 4.60658,0 9.3189,-0.2445 13.92886,0 5.25967,0.27895 3.19361,0.37483 7.92665,0.8578 4.14056,0.42249 8.31525,0.50512 12.44317,1.03717 12.48686,1.60938 1.58295,0.0618 5.04569,1.20613 0.84924,0.28062 3.93607,0.32168 4.3279,0.39119 2.84707,0.5053 5.62899,1.35948 8.4894,1.78284 3.21201,0.47543 6.70281,-0.22252 9.94151,0 9.709,0.66704 19.51619,0.39169 29.26798,0.39169 h 74.88431 11.89541 c 2.40265,0 4.82359,0.29665 7.20783,0 1.8986,-0.23625 3.70149,-0.90586 5.62911,-1.12242 2.99197,-0.33618 6.64258,0 9.53379,0 h 17.19172 c 3.31044,0 6.96111,0.27435 10.27999,0 8.33223,-0.68879 16.44521,-2.98336 24.90132,-3.37756 5.22139,-0.24339 10.29996,0.20449 15.47656,0.37208 2.47229,0.08 4.9608,-0.25948 7.42074,0 24.66634,2.60194 -1.93971,1.60478 19.83394,2.38485 9.03808,0.3238 18.16258,0 27.20816,0 3.78613,0 7.99158,-0.29337 11.80547,0 2.58548,0.19889 4.85296,1.92849 7.47241,2.23761 1.1099,0.13096 2.24526,-0.14973 3.35278,0 0.78478,0.10609 1.4696,0.63595 2.25205,0.75808 3.76206,0.58724 10.32304,0 14.27872,0 1.92955,0 7.04114,0.57637 8.92193,0 1.19007,-0.3647 2.2569,-1.0545 3.41789,-1.50326 5.53447,-2.13929 10.75036,-3.68609 16.68116,-4.45659 4.14311,-0.53826 9.81726,-0.63002 14.15158,-1.69653 0.6863,-0.16888 1.22132,-0.71218 1.82417,-1.08106 1.8156,-1.11096 3.56746,-2.32365 5.37229,-3.45199 0.59264,-0.3705 4.51053,-2.1979 5.38313,-3.56153 0.86445,-1.35094 -1.28357,-9.90698 -1.49397,-11.57658 -0.27924,-2.21586 -0.57947,-5.29765 -0.74311,-7.42487 -0.12483,-1.6228 0.25445,-1.62243 -0.3674,-3.33209 -0.34877,-0.95874 -1.29609,-2.20644 -1.48675,-3.34553 -0.25831,-1.54326 0.15529,-3.26313 0,-4.81417 -0.15271,-1.52508 -0.83944,-2.95301 -1.11516,-4.46071 -0.5093,-2.78479 2.7786,-2.44943 3.00188,-7.10345 0.0764,-1.59162 0.26138,-7.10688 0,-9.19478 -0.10885,-0.86941 -0.42907,-1.72757 -0.37208,-2.60191 0.26971,-4.14004 2.79027,-7.29198 2.44274,-11.48767 -0.1468,-1.77283 -1.30636,-2.67562 -1.48672,-4.52169 -0.42971,-4.39852 -0.44133,-8.46347 -0.44133,-12.84418 v -12.8726 c 0,-1.37247 0.18214,-2.75724 0,-4.11758 -0.074,-0.55273 -0.57327,-0.96652 -0.75136,-1.495 -0.29189,-0.86633 0.094,-1.9343 -0.37981,-2.71611 -0.53412,-0.88125 -7.68369,-1.46805 -7.8259,-1.49655 -4.19285,-0.83992 -4.43378,-3.32318 -7.43159,-4.32893 -0.61746,-0.20717 -1.3222,-0.53158 -1.96369,-0.51677 z m -83.81814,31.47715 c 0.88741,-0.89676 -0.97401,0.52652 0,0 z" />
+ <path
+ id="rect30"
+ style="fill:#f9f9f9;stroke-width:0.264583"
+ d="m 507.90936,37.330101 h 55.26015 V 316.89684 H 507.90936 Z M -55.282452,286.078 H 538.42226 v 49.69312 H -55.282452 Z M -72.186882,71.966187 H -0.1733017 V 321.29134 H -72.186882 Z" />
+ </g>
+ </svg>
F diff --git a/img/icons/.txt.svg b/img/icons/.txt.svg
new file mode 100644
--- /dev/null
+++ b/img/icons/.txt.svg
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
+ <svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="210mm"
+ height="297mm"
+ viewBox="0 0 210 297"
+ version="1.1"
+ id="svg8"
+ inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
+ sodipodi:docname="txt.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.35"
+ inkscape:cx="400"
+ inkscape:cy="560"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ inkscape:document-rotation="0"
+ showgrid="false"
+ inkscape:window-width="1918"
+ inkscape:window-height="1059"
+ inkscape:window-x="768"
+ inkscape:window-y="19"
+ inkscape:window-maximized="0" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;white-space:pre;inline-size:15.4878;opacity:1;fill:#008080;fill-opacity:1;stroke:none;stroke-width:0.264583;"
+ x="54.487259"
+ y="164.02113"
+ id="text12"
+ transform="matrix(11.172769,0,0,12.603914,-603.10178,-1943.9698)"><tspan
+ x="54.487259"
+ y="164.02113"><tspan
+ style="fill:#008080;stroke-width:0.264583">txt</tspan></tspan></text>
+ </g>
+ </svg>
F diff --git a/index.php b/index.php
--- a/index.php
+++ b/index.php
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
- <title>shady file upload</title> <link rel="stylesheet" type="text/css" href="css/style.css">
+ <title>shady file upload</title> <link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
</div>
</div>
- <img src="svg/bottom.svg" class="bgbottom">
+ <img src="img/bottom.svg" class="bgbottom">
</div>
</body>
<html>
F diff --git a/loggedin.js b/loggedin.js
deleted file mode 100644
--- a/loggedin.js
+++ /dev/null
- // This should be only set to false for debugging purposes
- // If it's set to false, the upload requests will be synchronous
- // and you will be able to see PHP's echo output in the browser
- var FORM_ASYNC = true;
-
- // A FileView is an entry inside the explorer window
- class FileView {
- constructor(filename, wnd, mimetype, is_directory, write_permissions) {
- this.filename = filename;
- this.wnd = wnd;
- this.visuals = null; // The DOM object with the icon and the filenam text
- this.mimetype = mimetype;
- this.is_directory = is_directory;
- this.write_permissions = write_permissions;
- }
-
- full_path() {
- return path_combine(get_path(this.wnd), this.filename);
- }
- }
-
- // An array of all fileviews currently open
-
- class Window {
- constructor(pwd) {
- this.pwd = pwd; // pwd = [ "Folder1", "Folder2" ] means the current directory of that window is /Folder1/Folder2
- this.visuals = null; // The DOM object
- this.h2 = null; // The titlebar of the window
- this.fileview = null;
- this.files = [];
- this.txt_editor = null; // For editable text files, this is the DOM element the user can edit
- }
- }
-
- // An array with all the windows on the screen
- var windows = [];
-
- // The focused window
- var focus = null;
-
- // Those all belong to the hidden file upload form
- const upload_form = document.getElementById("upload_form");
- const the_file = document.getElementById("the_file");
- const filename_input = document.getElementById("filename");
- const override_input = document.getElementById("override_input");
- const current_directory = document.getElementById("current_directory");
- const upload_parent_directory = document.getElementById("upload_parent_directory");
-
- // If this is set to true, requests to uploads.php will be sent with the "override" flag
- // which will override existing files with the same name
- var override_file = false;
- var override_file_filename = "";
- var override_file_path = "";
-
- var open_file = null;
-
- // Some elements have custom right click context menus
- // If there's a custom context menu active, this will be it
- var context_menu = null;
-
-
- // If we're currently dragging something (a window or a file), this is the DOM object we're dragging
- // dragging_offset_x and dragging_offset_y are the difference between the objec's top left point and the cursor position
- // this is then added to the cursor position when the mouse is moved
- var dragging = null;
- var dragging_offset_x = 0, dragging_offset_y = 0;
-
- // If we have pressed down a window's title bar but haven't yet started dragging it, it's the drag candidate
- // This is needed because we don't yet know whether we want to start dragging the window or click an element in the titlebar
- // Once the mouse has moved sufficiently far away from dragging_candidate_x or y, we start dragging
- var dragging_candidate = null;
- var dragging_candidate_x, dragging_candidate_y;
-
- // If we're dragging a fileview, this is set to the fileview class instance itself, because 'dragging' is just a DOM object
- // The placeholder is a dummy DIV that we insert in the object's place on the grid to keep things nicely aligned
- var dragging_fileview;
- var dragging_placeholder = null;
-
- // Windows have a z-index. When we click a window it is sent to the top this will be its new z-index
- // We then increment the depth, and the next window we click will go on top of the current one
- var depth = 20;
-
-
-
- function main() {
- // Create a window that looks at the root directory
- var root_window = make_window([]);
-
- // Focus that window and load the directory
- focus_window(root_window);
- openfile(true, root_window);
- }
-
- function focus_window(wnd) {
- // Unfocus the old window
- if (focus)
- focus.visuals.classList.remove('focus');
- focus = wnd;
- // And focus the new one!
- if (wnd) {
- wnd.visuals.classList.add('focus');
- wnd.visuals.style.zIndex = depth ++;
- }
- }
-
- // Delete the focused window
- function delete_window(wnd) {
- var index = windows.indexOf(wnd);
-
- if (index >= 0)
- windows.splice(index, 1);
-
- wnd.visuals.parentNode.removeChild(wnd.visuals);
- if (wnd == focus)
- focus = null;
- }
-
- // Create a right click context menu
- function context(e, entries) {
- if (context_menu)
- context_menu.remove();
-
- context_menu = mk(document.body, 'ul', 'context');
-
- context_menu.onmousedown = (e) => {
- e.stopPropagation();
- }
-
- context_menu.onclick = (_e) => {
- context_menu.remove();
- context_menu = null;
- }
-
-
- context_menu.style.left = e.clientX + "px";
- context_menu.style.top = e.clientY + "px";
-
- for (const e of entries) {
- const li = document.createElement('li');
- li.innerText = e[0];
- li.onclick = e[1];
- context_menu.appendChild(li);
- }
- }
-
- // This is called whenever the <input type="file">'s value changes
- function on_file_added(_e) {
- if (the_file.files.length >= 1) {
-
- if (override_file) {
- filename_input.value = override_file_filename;
- override_input.value = "1";
- upload_parent_directory.value = override_file_path;
- console.log(filename_input.value, override_input.value, upload_parent_directory.value);
- } else {
- filename_input.value = the_file.files[0].name;
- override_input.value = "0";
- upload_parent_directory.value = get_path(focus);
- }
-
- if (!FORM_ASYNC) {
- upload_form.submit();
- return;
- }
-
- // Send the form asynchronously through the fetch api
- fetch(upload_form.action, {
- method: upload_form.method,
- body: new FormData(upload_form)
- }).then((resp) => {
- if (resp.status == 200) {
- // Reload the directory so the user can see the newly uploaded file
- openfile(true, focus);
- } else {
- alert("Upload failed");
- }
- }, () => {
- alert("Upload failed")
- });
- }
- else {
- alert("No files selected");
- }
- }
-
- // It's honestly really sad that we need this
- // We have an image viewer, but we load the uploaded via the XMLHttpRequest API, which gives us an array buffer
- // We need to base64 encode the image data so we can feed it into the <img src="...">
- // and the standart base64 encode API is shit
- // https://stackoverflow.com/questions/7370943/retrieving-binary-file-content-using-javascript-base64-encode-it-and-reverse-de
- function base64ArrayBuffer(arrayBuffer) {
- var base64 = ''
- var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
-
- var bytes = new Uint8Array(arrayBuffer)
- var byteLength = bytes.byteLength
- var byteRemainder = byteLength % 3
- var mainLength = byteLength - byteRemainder
-
- var a, b, c, d
- var chunk
-
- // Main loop deals with bytes in chunks of 3
- for (var i = 0; i < mainLength; i = i + 3) {
- // Combine the three bytes into a single integer
- chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]
-
- // Use bitmasks to extract 6-bit segments from the triplet
- a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
- b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
- c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
- d = chunk & 63 // 63 = 2^6 - 1
-
- // Convert the raw binary segments to the appropriate ASCII encoding
- base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
- }
-
- // Deal with the remaining bytes and padding
- if (byteRemainder == 1) {
- chunk = bytes[mainLength]
-
- a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
-
- // Set the 4 least significant bits to zero
- b = (chunk & 3) << 4 // 3 = 2^2 - 1
-
- base64 += encodings[a] + encodings[b] + '=='
- } else if (byteRemainder == 2) {
- chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]
-
- a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
- b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
-
- // Set the 2 least significant bits to zero
- c = (chunk & 15) << 2 // 15 = 2^4 - 1
-
- base64 += encodings[a] + encodings[b] + encodings[c] + '='
- }
-
- return base64
- }
-
-
- // This updates the path of the window's DOM (the "Root > Folder1 > Folder2 > foo.png")
- function update_path_visuals(wnd) {
- if (!wnd) {
- alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
- wnd = focus;
- }
-
- var the_path = wnd.visuals.getElementsByClassName('path')[0];
-
- // Remove the old path
- while (the_path.children.length > 0)
- the_path.removeChild(the_path.lastChild);
-
- for (let i = -1; i < wnd.pwd.length; i++) {
- var d;
- // For each element after the first create a separator
- if (i >= 0) {
- d = wnd.pwd[i];
- var separator_div = mk(the_path, 'div', 'separator');
- separator_div.innerText = "ยป";
- }
- else
- d = "Root";
-
- var entry = mk(the_path, 'button', 'pathentry');
- entry.innerText = d;
-
- // When we click the entry, go to its folder
- entry.onclick = (_e) => {
- if (length < wnd.pwd.length) {
- wnd.pwd.length = i + 1;
- openfile(true, wnd);
- }
- }
-
- // We can drop files onto the path, which will omve them to teh folder
- entry.onmouseup = (e) => {
- if (dragging && dragging_fileview) {
- var new_folder = get_path(wnd, i + 1);
- move_file(dragging_fileview.wnd, wnd, new_folder, dragging_fileview.filename);
- end_drag();
-
- e.preventDefault();
- e.stopPropagation();
- }
- }
- }
- }
-
-
- // This asks the server for the contents of the specified file
- // The 'cb' callback is then called, which gives you the file as either text or binary
- // depending on whether or not text is true/false
- function read_file_contents(text, cb, folder, filename) {
- var data = new FormData();
- data.append('folder', folder);
- data.append('filename', filename);
-
- let xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/readfile.php', true);
-
- if (text) {
- xhr.onload = function () {
- cb(xhr.responseText);
- };
- } else {
- xhr.responseType = 'arraybuffer';
- xhr.onload = function () {
- cb(xhr.response);
- };
- }
-
- xhr.send(data);
- }
-
-
- // This opens a file.
- // If the file has image/* mimetype, it will be displayed as an image
- // otherwise it will be displayed as plaintext
- function openfile_nondir(wnd) {
-
- while (wnd.filecontents.children.length > 0)
- wnd.filecontents.removeChild(wnd.filecontents.lastChild);
-
- // Send a request to readfile.php, which will give us the contents
- var data = new FormData();
- data.append('folder', get_path(wnd, wnd.pwd.length - 1));
- data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
-
- var xhr = new XMLHttpRequest();
-
- update_path_visuals(wnd);
-
- xhr.open('POST', '/php/readfile.php', true);
-
- wnd.filecontents.innerText = "";
- wnd.filecontentsroot.style.display = 'flex';
- wnd.foldercontents.style.display = 'none';
-
- let is_image = open_file.mimetype.split("/")[0] == "image";
- wnd.save_btn_container.style.display = (open_file.write_permissions && !is_image) ? "flex" : "none";
-
- if (is_image) {
- xhr.responseType = 'arraybuffer';
- xhr.onload = function () {
- let b = `data:image/png;base64,${base64ArrayBuffer(xhr.response)}`;
- wnd.filecontents.style.backgroundImage = `url('${b}')`;
- wnd.filecontents.classList.add('imgview');
- }
- }
- else {
- wnd.filecontents.classList.remove('imgview');
- wnd.filecontents.style.backgroundImage = "unset";
-
- wnd.txt_editor = mk(wnd.filecontents, 'pre');
-
- xhr.onload = function () {
- wnd.txt_editor.innerText = xhr.responseText;
- if (open_file.write_permissions)
- wnd.txt_editor.contentEditable = "true";
- };
- }
-
- xhr.send(data);
- }
-
- // This is a tiny wrapper around the share_window.
- function share(in_file, filename, wnd) {
- if (in_file) {
- var folder = get_path(wnd, wnd.pwd.length - 1);
- filename = wnd.pwd[wnd.pwd.length - 1];
- } else {
- var folder = get_path(wnd);
- }
-
- var wnd = make_share_window(folder, filename);
- focus_window(wnd);
- }
-
- // Replace an existing file with a new one
- function replace_file(in_file, filename, wnd) {
- if (in_file) {
- var folder = get_path(wnd, wnd.pwd.length - 1);
- filename = wnd.pwd[wnd.pwd.length - 1];
- } else {
- var folder = get_path(wnd);
- }
-
- override_file = true;
- override_file_path = folder;
- override_file_filename = filename;
- the_file.click();
- }
-
- // This loads the contents of the current directory
- function openfile_dir(wnd) {
- update_path_visuals(wnd);
-
- var data = new FormData();
- data.append('path', get_path(wnd));
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/readdir.php', true);
- xhr.onload = function () {
- for (const f of wnd.files)
- f.visuals.remove();
- wnd.files = [];
-
- var json = JSON.parse(xhr.responseText);
- if (!json)
- return;
-
- // Create the FileViews from the json response
- for (const f of json) {
- var view = new FileView(f.name,
- wnd,
- f.mimetype,
- f.is_directory && f.is_directory != "0",
- f.can_edit && f.can_edit != "0");
- wnd.files.push(view);
- }
-
- // Sort the files nicely before adding their visuals
- // Folders come first, then files, then the special trash directory
- // Everything inside the categories is lexically sorted
- wnd.files.sort((a, b) => {
- if (wnd.pwd.length == 0 && a.filename == "share")
- return -10;
- if (wnd.pwd.length == 0 && b.filename == "share")
- return 10;
-
- if (wnd.pwd.length == 0 && a.filename == "trash")
- return 10;
- if (wnd.pwd.length == 0 && b.filename == "trash")
- return -10;
- if (a.is_directory && !b.is_directory)
- return -1;
- if (!a.is_directory && b.is_directory)
- return 1;
- return a.filename.localeCompare(b.filename);
- });
-
- for (const f of wnd.files) {
- add_file_visuals(f, wnd);
- }
-
- };
- xhr.send(data);
-
- wnd.filecontentsroot.style.display = 'none';
- wnd.foldercontents.style.display = 'flex';
-
- wnd.foldercontents.onmouseup = () => {
- if (dragging && dragging_fileview) {
- move_file(dragging_fileview.wnd, wnd, get_path(wnd), dragging_fileview.filename);
- }
- }
- }
-
-
- function openfile(is_directory, wnd) {
- if (!wnd) {
- alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
- console.trace();
- wnd = focus;
- }
-
- if (is_directory) {
- openfile_dir(wnd);
- } else {
- openfile_nondir(wnd);
- }
- }
-
- function move_to_trash(wnd, filename) {
- move_file(wnd, wnd, "/trash", filename, path_combine(get_path(wnd), filename));
- }
-
- function restore_from_trash(wnd, filename) {
- var split = filename.split("/");
- var new_filename = split.pop();
- var new_directory = "/" + split.join("/");
-
- move_file(wnd, new_directory, filename, new_filename);
- }
-
-
- // This deletes the file, *for real*
- // move_to_trash is what is actually called when the user clicks 'Delete'
- function delete_file(wnd, filename) {
- var data = new FormData();
- data.append('folder', get_path(wnd));
- data.append('filename', filename);
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/delete.php', true);
- xhr.onload = function () {
- openfile(true, wnd);
- };
- xhr.send(data);
- }
-
- function rename_file(filename, wnd) {
- var new_name = prompt(`Rename ${filename} to`, filename);
- if (!new_name)
- return;
-
- var data = new FormData();
- data.append('folder', get_path(wnd));
- data.append('old_filename', filename);
- data.append('new_filename', new_name);
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/rename.php', true);
- xhr.onload = function () {
- openfile(true, wnd);
- };
- xhr.send(data);
- }
-
- function move_file(srcwnd, dstwnd, new_folder, filename, new_filename) {
- if (!new_filename)
- new_filename = filename;
-
- var data = new FormData();
- data.append('old_folder', get_path(srcwnd));
- data.append('new_folder', new_folder);
- data.append('filename', filename);
- data.append('new_filename',new_filename);
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/move.php', true);
- xhr.onload = () => {
- openfile(true, srcwnd);
- openfile(true, dstwnd);
- };
- xhr.send(data);
- }
-
- function new_folder(wnd) {
- var dirname = prompt(`Directory name`, "New Folder");
- if (!dirname)
- return;
-
- var data = new FormData();
- data.append('parent_directory', get_path(wnd));
- data.append('dirname', dirname);
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/mkdir.php', true);
- xhr.onload = function () {
- openfile(true, wnd);
- };
- xhr.send(data);
- }
-
-
- // Dragging a fileview is a bit different from dragging a window
- // This does some setup work before calling the common begin_drag
- function begin_drag_fileview(e, fileview) {
- if (dragging)
- end_drag();
-
- // The dragging_placeholder is inserted into its place by the begin_drag function
- dragging_placeholder = document.createElement('div');
- dragging_fileview = fileview;
-
- dragging = fileview.visuals;
- dragging.style.zIndex = 50000;
-
- begin_drag(e, fileview.visuals);
- }
-
- // Start dragging the 'obj' DOM element
- // e is a DOM event, this should only get called in response of a DOM event
- function begin_drag(e, obj, dont_set_width) {
- set_iframe_enabled(false);
- dragging = obj;
- dragging_candidate = null;
- dragging.classList.add("dragged");
-
- var elemRect = dragging.getBoundingClientRect();
- dragging_offset_x = e.clientX - elemRect.left;
- dragging_offset_y = -e.clientY + elemRect.top;
-
- if (dragging_placeholder)
- obj.parentNode.insertBefore(dragging_placeholder, obj);
-
- dragging.style.left = (e.clientX - dragging_offset_x) + "px";
- dragging.style.top = (e.clientY + dragging_offset_y) + "px";
-
- if (!dont_set_width) {
- dragging.style.width = elemRect.width + "px";
- dragging.style.height = elemRect.height + "px";
- }
-
- dragging.style.position = "absolute";
- document.body.appendChild(dragging);
- }
-
- function end_drag(_e) {
- set_iframe_enabled(true);
-
- // If there's a dragging placeholder remove it and put the dragged node back into its place
- if (dragging_placeholder) {
- dragging_placeholder.parentNode.insertBefore(dragging, dragging_placeholder);
- dragging_placeholder.remove();
- dragging_placeholder = null;
- }
-
- // If we were dragging a FileView, we need to reset some CSS
- if (dragging_fileview) {
- dragging.style.removeProperty("position");
- dragging.style.removeProperty("width");
- dragging.style.removeProperty("height");
- dragging.style.removeProperty("left");
- dragging.style.removeProperty("top");
- dragging_fileview = null;
- }
-
- dragging.classList.remove("dragged");
- dragging = null;
- }
-
- // This creates the parts of a window that are common between all window types
- // This should only really be called by another function that will then fill up the window
- function make_window_base(pwd, x, y, w, h) {
- var wnd = new Window(pwd);
- windows.push(wnd);
-
- wnd.visuals = mk(document.body, 'div', 'window');
-
- wnd.visuals.style.width = w + "px";
- wnd.visuals.style.height = h ? (h + "px") : "unset";
- wnd.visuals.style.position = "absolute";
- wnd.visuals.style.left = x + "px";
- wnd.visuals.style.top = y + "px";
-
- wnd.h2 = mk(wnd.visuals, 'h2');
-
- wnd.visuals.onmousedown = (_e) => {
- focus_window(wnd);
- }
-
- wnd.h2.onmousedown = (e) => {
- if (!dragging) {
- dragging_candidate = wnd.visuals;
- dragging_candidate_x = e.clientX;
- dragging_candidate_y = e.clientY;
- }
- };
-
- return wnd;
- }
-
- // This is a widely abused helper function that creates a DOM element, attaches it as the
- // last child of 'parent' and possibly gives it a class
- function mk(parent, type, _class) {
- var el = document.createElement(type);
- parent.appendChild(el);
- if (_class)
- el.classList.add(_class);
- return el;
- }
-
- // Crate a horizontal div
- function mkhdiv(parent) {
- var hdiv = mk(parent, 'div');
- hdiv.style.display = "flex";
- hdiv.style.alignItems = "center";
- hdiv.style.padding = "0.3rem";
- hdiv.style.gap = "0.3rem";
- return hdiv;
- }
-
- // Create a checkbocx with a label.
- // togglefn will be called when its value changes with an argument that's either true/false
- function mkcheckbox(parent, label, togglefn) {
- var hdiv = mkhdiv(parent);
-
- var write_checkbox = mk(hdiv, 'input');
- write_checkbox.type = 'checkbox';
-
- var write_checkbox_label = mk(hdiv, 'label');
- write_checkbox_label.innerText = label;
- write_checkbox_label.onclick = (_e) => { write_checkbox.click(); }
- write_checkbox_label.classList.add('noselect');
-
- write_checkbox.onchange = (_e) => {
- togglefn(write_checkbox.checked);
- };
- }
-
- // This monstrocity creates the 'Share file' window
- function make_share_window(folder, filename) {
- var wnd = make_window_base(null, 400, 400, 400, 0);
-
- wnd.h2.style.display = 'flex';
-
- // The title of the window. WE set its 'flex' to 1 1 0 so it fills up the titlebar
- // and pushes the X button to the very right
- var heading = mk(wnd.h2, 'span', 'wndtitle');
- heading.innerText = "Share " + filename;
-
- // Close button
- var x_button = mk(wnd.h2, 'button', 'close_button');
- x_button.innerText = "X";
- x_button.onclick = () => delete_window(wnd);
-
- wnd.foldercontents = mk(wnd.visuals, 'div', 'share_dialog_contents');
- wnd.foldercontents.style.padding = "0.5rem";
-
- // This is the data that will be sent when we hit "Generate link"
- var data = {
- write_permissions: false,
- private: false,
- has_password: false,
- password: "",
- userlist: [],
- }
-
- // If private link is clicked, show the "Add user" button and the user list
- var userlist, add_user;
- mkcheckbox(wnd.foldercontents, "Private link", (toggled) => {
- add_user.style.display = toggled ? "block" : "none";
- userlist.style.display = toggled ? "block" : "none";
- data.private = toggled;
- });
-
- userlist = mk(wnd.foldercontents, 'div');
- userlist.style.display = "none";
- add_user = mk(wnd.foldercontents, 'button');
- add_user.innerText = "Add user";
- add_user.style.display = "none";
-
- // When we hit 'Add user', add an input field for a new user
- add_user.onclick = (_e) => {
- var i = mk(userlist, 'input');
- i.value = 'John Doe';
-
- let index = data.userlist.length;
- data.userlist.push(i.value);
-
- i.onchange = (_e) => {
- data.userlist[index] = i.value;
- }
- }
-
- // Click the add_user once to add a default user, since a URL that nobody can use makes no sense
- add_user.click();
-
- mkcheckbox(wnd.foldercontents, "Give write permissions", (toggled) => {
- data.write_permissions = toggled;
- });
-
- // If 'Password protected' is checked, show the password field
- let password_container;
- mkcheckbox(wnd.foldercontents, "Password protected", (toggled) => {
- data.has_password = toggled;
- password_container.style.display = toggled ? "flex" : "none";
- });
-
- password_container = mkhdiv(wnd.foldercontents);
- password_container.style.display = 'none'
- var password_label = mk(password_container, 'label');
- password_label.innerText = "Password";
- var password_input = mk(password_container, 'input');
- password_input.type = 'password';
- password_input.autocomplete = 'off'
-
- password_input.style.flex = "1 0 0";
- password_input.onchange = (_e) => {
- data.password = password_input.value;
- };
-
- var generate_url_button = mk(wnd.foldercontents, 'button');
- generate_url_button.innerText = "Generate link";
-
- generate_url_button.onclick = () => {
- // The backend expects the users to be either an empty string, if the URL is public
- // or a comma separated list of usernaems
- var users = "";
- if (data.private) {
- users = data.userlist.join(',');
- }
-
- var form_data = new FormData();
- form_data.append('folder', folder);
- form_data.append('filename', filename);
- form_data.append('users', users);
- // 0 = No permissions, 1 = Read only, 2 = Write , 1|2 = 3 = RW
- // Only 1 and 3 make sense in the context of a URL
- form_data.append('permissions', data.write_permissions ? 3 : 1);
- form_data.append('password', data.has_password ? data.password : "");
-
- var xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/share.php', true);
- xhr.onload = function () {
- alert(xhr.response);
- }
-
- xhr.send(form_data);
- delete_window(wnd);
- }
-
- return wnd;
- }
-
- function download_file(in_file, filename, wnd) {
- if (!wnd) {
- alert (802);
- wnd = focus;
- }
- if (in_file) {
- var folder = get_path(wnd, wnd.pwd.length - 1);
- filename = wnd.pwd[wnd.pwd.length - 1];
- } else {
- var folder = get_path(wnd);
- }
-
- // Read the file contents and then do DISGUSTING javascript things to download the ifle
- // We create a invisible <a> that we click and then delete
- // That <a> has its download attribute set so we download the contents instead of opening it in a new tab
- // and of course its href is a virtual object URL that has its content set to a blob
- read_file_contents(false, (x) => {
- var blob = new Blob([new Uint8Array(x, 0, x.length)]);
- var url = URL.createObjectURL(blob);
- var a = document.createElement('a');
- a.href = url;
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- setTimeout(() => {
- document.body.removeChild(a);
- URL.revokeObjectURL(url);
- });
- }, folder, filename);
-
- return;
- }
-
-
- // make_window creates an explorer window - the kind that can list directories/open files
- function make_window(pwd, has_close) {
- var wnd = make_window_base(pwd, 100, 100, 800, 600);
-
- path = mk(wnd.h2, 'div', 'path');
-
- if (has_close) {
- var x_button = mk(wnd.h2, 'button', 'close_button');
- x_button.innerText = "X";
- x_button.onclick = () => delete_window(wnd);
- }
-
- // wnd.foldercontents is where the FileViews will be stored
- // it also has a subheader (h3) with 'Upload' and 'New FOlder' buttons
- {
- wnd.foldercontents = mk(wnd.visuals, 'div', 'foldercontents');
- var h3 = mk(wnd.foldercontents, 'h3');
-
- var upload_btn = mk(h3, 'button');
- upload_btn.innerText = "Upload";
-
- upload_btn.onclick = () => {
- override_file = false;
- the_file.click();
- }
-
- mk(h3, 'div', 'separator');
-
- var new_folder_btn = mk(h3, 'button');
- new_folder_btn.innerText = "New Folder";
- new_folder_btn.onclick = () => { new_folder(wnd); }
-
- mk(h3, 'div', 'separator');
-
- wnd.filegrid = mk(wnd.foldercontents, 'div', 'files');
- }
-
- // wnd.filecontentsroot is where the filedata will be stored for open files
- // it also has a subheader (h3) with Share and Download buttons
- {
- wnd.filecontentsroot = mk(wnd.visuals, 'div', 'filecontentsroot');
- var h3 = mk(wnd.filecontentsroot, 'h3');
-
- let download_btn = mk(h3, 'button');
- download_btn.innerText = "Download";
- download_btn.onclick = () => { download_file(true); }
- mk(h3, 'div', 'separator');
-
- let share_btn = mk(h3, 'button');
- share_btn.innerText = "Share";
- share_btn.onclick = () => { share(true, wnd); }
- mk(h3, 'div', 'separator');
-
- wnd.save_btn_container = mk(h3, 'div');
- wnd.save_btn_container.style.display = 'flex';
-
- let save_btn = mk(wnd.save_btn_container, 'button');
- save_btn.innerText = "Save";
- save_btn.onclick = () => save_open_text_file(wnd);
- mk(wnd.save_btn_container, 'div', 'separator');
-
- wnd.filecontents = mk(wnd.filecontentsroot, 'div', 'filecontents');
- }
-
- return wnd;
- }
-
-
- function save_open_text_file(wnd) {
- const contents = wnd.txt_editor.innerText;
- let xhr = new XMLHttpRequest();
- xhr.open('POST', '/php/upload.php', true);
-
- var data = new FormData();
- data.append('parent_directory', get_path (wnd.pwd.length - 1));
- data.append('filename', wnd.pwd[wnd.pwd.length - 1]);
- data.append('content', contents);
- data.append('overwrite', '1');
-
- xhr.send(data);
- }
-
-
- // Create the visuals for a FileView
- function add_file_visuals(fileview, wnd) {
- // Are we in a subdirectory of the trash folder?
- var is_in_trash = wnd.pwd.length > 0 && wnd.pwd[0] == "trash";
-
- // Is the current filewview the trash folder itself?
- var is_trash = wnd.pwd.length == 0 && fileview.filename == "trash";
- var is_share = wnd.pwd.length == 0 && fileview.filename == "share";
-
- var visuals = mk(wnd.filegrid, 'div');
- fileview.visuals = visuals;
-
- var img = document.createElement('img');
- var filename = document.createElement('div');
-
- if (fileview.is_directory) {
- if (get_path(wnd) == "/" && fileview.filename == "trash")
- img.src="/mimeicons/user-trash.png";
- else if (get_path(wnd) == "/" && fileview.filename == "share")
- img.src = "/mimeicons/user-share.png";
- else
- img.src="/mimeicons/directory.png";
- } else {
- img.src=`/mimeicons/${fileview.mimetype.replace("/", "-")}.png`;
- }
-
- fileview.visuals.onclick = () => {
- wnd.pwd.push(fileview.filename);
- if (!fileview.is_directory) {
- open_file = fileview;
- }
- openfile(fileview.is_directory, wnd);
- }
-
- visuals.oncontextmenu = (e) => {
- if (!dragging) {
-
- var context_list = [
- // Open is always in the context list
- ['Open', () => {
- wnd.pwd.push(fileview.filename);
- openfile(fileview.is_directory, wnd);
- }],
- ['Open in New Window', () => {
- var new_pwd = wnd.pwd.slice();
- new_pwd.push(fileview.filename);
- var new_wnd = make_window(new_pwd, true);
- open_file = fileview;
- openfile(fileview.is_directory, new_wnd);
- focus_window(new_wnd);
- }],
- ];
-
- if (is_in_trash) {
- // If we're in the trash, we can restore files or delete them forever
- context_list.push(['Restore', () => { restore_from_trash(wnd, fileview.filename); }]);
- context_list.push(['Delete forever', () => { delete_file(wnd, fileview.filename); }]);
- } else if (!is_trash && !is_share) {
- // If we;'re not in trash we can rename/share/download/move files to trash
- context_list.push(
- ['Rename', () => { rename_file(fileview.filename, wnd); }],
- );
- if (!fileview.is_directory) {
- for (let a of actions) {
- if (fileview.filename.endsWith(a.extension)) {
- context_list.push(
- [a.text, () => {
- read_file_contents(true, (x) => {
- const ue = encodeURIComponent(x);
- let url = a.url.replace("$content_urlencoded", ue)
- .replace("$filename", fileview.filename);
-
- if (a.open_in_iframe) {
- const wnd = make_window_base([], 10, 10, 800, 600);
-
- var title = mk(wnd.h2, 'span', 'wndtitle');
- title.innerText = fileview.filename;
-
- // Close button
- var x_button = mk(wnd.h2, 'button', 'close_button');
- x_button.innerText = "X";
- x_button.onclick = () => delete_window(wnd);
-
- const contents = mk(wnd.visuals, 'div', 'filecontentsroot');
- const iframe = mk(contents, 'iframe');
- iframe.style.flex = '1 0 0';
- iframe.src = url;
-
- focus_window(wnd);
- } else {
- window.location = url;
- }
- }, get_path(wnd), fileview.filename);
- }]
- );
- }
- }
-
- if (fileview.write_permissions) {
- context_list.push(
- ['Replace', () => { replace_file(false, fileview.filename, wnd); }],
- );
- }
- context_list.push(
- ['Share', () => { share(false, fileview.filename, wnd); }],
- ['Download', () => { download_file(false, fileview.filename); }],
- );
- }
- context_list.push(
- ['Delete', () => { move_to_trash(wnd, fileview.filename); }]
- );
- }
-
- context(e, context_list);
- }
- e.preventDefault();
- e.stopPropagation();
- }
-
- visuals.ondragstart = (e) => {
- if (is_trash || is_in_trash || is_share) {
- e.preventDefault();
- return;
- }
- begin_drag_fileview(e, fileview);
- e.preventDefault();
- };
-
- visuals.onmouseup = (e) => {
- if (dragging) {
- if (fileview.is_directory) {
- if (get_path(wnd) == "/" && fileview.filename == "trash") {
- // If we've dragged something onto the trashcan, it's trash
- move_to_trash(wnd, dragging_fileview.filename);
- }
- else if (get_path(wnd) == "/" && fileview.filename == "share") {
- // move to 'share' is invalid
- } else {
- // If we've dragged something onto a directory, move it into that directory
- move_file(dragging_fileview.wnd, wnd, path_combine(get_path(wnd), fileview.filename), dragging_fileview.filename);
- }
- } else {
- // alert(`Dropped ${dst.filename} on ${src.filename}`);
- }
- end_drag();
- }
- e.preventDefault();
- };
-
- visuals.classList.add('file');
- filename.classList.add('filename');
-
- if (is_in_trash) {
- var split = fileview.filename.split("/");
- filename.innerText = split[split.length - 1];
- } else if (is_trash) {
- filename.innerText = "Trash";
- } else if (is_share) {
- var x = mk(filename, 'span');
- x.style.fontSize = "0.8rem";
- x.innerText = "Shared with me";
- } else{
- filename.innerText = fileview.filename;
- }
-
- visuals.appendChild(img);
- visuals.appendChild(filename);
- }
-
-
- // Reads the 'pwd' of the focused window
- // If pwd is ['foo', 'bar', 'baz'], this returns '/foo/bar/baz'
- function get_path(wnd, max_length) {
- if (!wnd) {
- alert("YOU ARE NOT SUPPOSED TO SEE THIS. PLEASE COPY THE STACKTRACE FROM THE CONSOLE AND SEND IT TO THE NEAREST DEVELOPER");
- console.trace();
- wnd = focus;
- }
- if (max_length == undefined) {
- max_length = wnd.pwd.length;
- }
-
- var path = "/";
- for (let i = 0; i < max_length; i++) {
- path += wnd.pwd[i];
- if (i != max_length - 1)
- path += "/";
- }
- return path;
- }
-
- function path_combine(a, b) {
- const last_char = a.slice(-1);
- if (last_char == "/")
- return a + b;
- else
- return a + "/" + b;
- }
-
-
- // When we click anywhere, remove the context menu
- // The context menu itself has a onmousedown that prevents propagation so we can click its elements
- document.body.onmousedown = (_e) => {
- if (context_menu) {
- context_menu.remove();
- context_menu = null;
- }
- }
-
- document.body.onmousemove = (e) => {
- if (dragging) {
- dragging.style.left = (e.clientX - dragging_offset_x) + "px";
- dragging.style.top = (e.clientY + dragging_offset_y) + "px";
- }
- else if (dragging_candidate) {
- var d = Math.abs(e.clientX - dragging_candidate_x) + Math.abs(e.clientY - dragging_candidate_y);
- if (d > 15)
- begin_drag(e, dragging_candidate, true);
- }
- }
-
- document.body.onmouseup = (_e) => {
- if (dragging_candidate)
- dragging_candidate = null;
- if (dragging)
- end_drag();
- }
-
- document.body.oncontextmenu = (e) => {
- if (dragging) {
- end_drag();
- e.preventDefault();
- }
- if (context_menu) {
- context_menu.remove();
- context_menu = null;
- }
- }
-
- the_file.onchange = (e) => { on_file_added(e); };
-
- function set_iframe_enabled(en) {
- for (const iframe of document.getElementsByTagName('iframe'))
- iframe.hidden = !en;
- }
-
- main();
F diff --git a/loggedin.php b/loggedin.php
--- a/loggedin.php
+++ b/loggedin.php
</form>
<script src="actions.js"></script>
- <script src="loggedin.js"></script>
+ <script src="build/bundle.js"></script>
F diff --git a/loginregister.php b/loginregister.php
--- a/loginregister.php
+++ b/loginregister.php
<div id="hero" class="overlay">
<div id="arrows">
- <img src="svg/arrow.svg" id="protoarrow" style="display: none">
+ <img src="img/arrow.svg" id="protoarrow" style="display: none">
</div>
<div class="vcenter">
F diff --git a/package-lock.json b/package-lock.json
new file mode 100644
--- /dev/null
+++ b/package-lock.json
+ {
+ "name": "htdocs",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "typescript": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz",
+ "integrity": "sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw==",
+ "dev": true
+ }
+ }
+ }
F diff --git a/package.json b/package.json
new file mode 100644
--- /dev/null
+++ b/package.json
+ {
+ "name": "htdocs",
+ "version": "1.0.0",
+ "description": "FILEUP",
+ "main": "actions.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/alexvitkov/india.git"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "ISC",
+ "bugs": {
+ "url": "https://github.com/alexvitkov/india/issues"
+ },
+ "homepage": "https://github.com/alexvitkov/india#readme",
+ "devDependencies": {
+ "typescript": "^4.2.3"
+ }
+ }
F diff --git a/php/file_type_recogniser.php b/php/file_type_recogniser.php
--- a/php/file_type_recogniser.php
+++ b/php/file_type_recogniser.php
function get_icon($path_to_file)
{
- $file_ext="svg/icons/".file_extension($path_to_file).".svg";
+ $file_ext="img/icons/".file_extension($path_to_file).".svg";
if(!file_exists($file_ext))
{
return "svg/icons/.dat.svg";
F diff --git a/share_frontend.php b/share_frontend.php
--- a/share_frontend.php
+++ b/share_frontend.php
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&display=swap" rel="stylesheet">
<link rel="shortcut icon" href="data:image/x-icon;," type="image/x-icon">
- <title>shady file upload</title> <link rel="stylesheet" type="text/css" href="../css/style.css">
- <title>shady file upload</title> <link rel="stylesheet" type="text/css" href="../css/sharefile_style.css">
+ <title>shady file upload</title>
+ <link rel="stylesheet" type="text/css" href="../css/style.css">
</head>
</head>
<body>
F diff --git a/style.css b/style.css
new file mode 100644
--- /dev/null
+++ b/style.css
+
+ html, body {
+ margin: 0;
+ height: 100%;
+ }
+
+ body {
+ background: #f0f0f0;
+ color: black;
+ font-family: Roboto, sans-serif;
+ overflow: hidden;
+ }
+
+ .noselect {
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ #page {
+ display: flex;
+ flex: 1 0 0;
+ align-items: stretch;
+
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ #page > * {
+ flex: 1 0 0;
+ }
+
+ /* Disable the black outline that Chromium browsers put on focused buttons */
+ input:focus,
+ button:focus
+ {
+ outline:0;
+ }
+
+ #header {
+ background: white;
+ margin: 0;
+ padding: 0;
+ height: 3rem;
+ font-size: 2em;
+ display: flex;
+ align-items: stretch;
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ #topmenu {
+ font-size: 1.3rem;
+ list-style-type: none;
+ display: flex;
+ margin: 0;
+ padding: 0;
+ }
+
+ #topmenu > li {
+ cursor: pointer;
+ padding: 10px;
+ }
+
+ #topmenu > li:hover {
+ background: #eee;
+ }
+
+ .logo {
+ font-family: monospace;
+ margin: 0;
+ align-self: center;
+ padding-left: 2rem;
+ }
+
+ #hero {
+ flex: 1.5 0 0;
+ position: relative;
+ font-size: 3.5rem;
+ }
+
+ .vcenter {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ #hero .bg {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ z-index: -5;
+ }
+
+ #hero > * > p {
+ color: #676d75;
+ margin: 0rem;
+ padding: 0.5rem;
+ white-space: nowrap;
+ text-align: center;
+ }
+
+ #hero .big {
+ font-size: 1.2em;
+ }
+
+ .blue {
+ color: #231179;
+ font-weight: bold;
+ }
+
+ form {
+ background: #fbfbfb;
+ margin: 4.5rem;
+ padding-top: 0;
+ box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);
+ border-radius: 0.5rem;
+ border: 1px solid #b9b9b9;
+ }
+
+ .vert {
+ justify-content: center;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .vert2 {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .overlay {
+ display: grid;
+ }
+
+ .overlay > * {
+ grid-row: 1;
+ grid-column: 1;
+ }
+
+ form > .content {
+ margin: 2rem;
+ margin-top: 1rem;
+ margin-bottom: 1.7rem;
+ padding: 0;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ }
+
+ form > h2 {
+ color: #4d4d4d;
+ margin: 0;
+ text-align: center;
+ padding: 1rem;
+ background: #f0f0f0;
+ border-top-left-radius: 0.5rem;
+ border-top-right-radius: 0.5rem;
+ align-content: center;
+ }
+
+
+ .window h3 button,
+ .window > h2 button
+ {
+ border: none;
+ padding: 0.3rem 1rem;
+ background: inherit;
+ border-radius: 0;
+ }
+
+ .window h3 button:not(.pathentry):hover {
+ background: white;
+ }
+
+ .window h3 .separator {
+ flex: 0 0 1px;
+ align-self: stretch;
+ background: #bbb;
+ }
+
+ .window > h2 > *:first-child {
+ border-top-left-radius: 0.5rem;
+ }
+ form p {
+ margin: 1rem 0px 0.3rem 0px;
+ }
+
+ .hero_form_error {
+ animation: fadein 0.2s;
+ background-color: #ff4d4d;
+ color: #ffffff;
+ padding-left: 0.5rem;
+ border-bottom-left-radius: 0.5rem;
+ border-bottom-right-radius: 0.5rem;
+ margin-top: -0.2rem;
+
+ }
+ @keyframes fadein {
+ from { opacity: 0; }
+ to { opacity: 1; }
+ }
+
+ .content > input {
+ min-width: 300px;
+ }
+
+ button,
+ input:not([type=file])
+ {
+ border: 1px solid #bbb;
+ padding: 0.5rem 0.8rem;
+ font-size: inherit;
+ font-family: inherit;
+ border-radius: 0.3rem;
+ background: #fcfcfc;
+ }
+
+ input[type=button], button, input[type=submit] {
+ cursor: pointer;
+ }
+
+ input[type=submit] {
+ margin-top: 2rem;
+ width: 100%;
+ display: block;
+ padding: 0.7rem;
+ font-size: 1.1em;
+ box-shadow: 0 0.2rem 0.6rem #eee;
+ background: #231179;
+ color: white;
+ outline: none;
+ }
+
+
+ input:hover,
+ button:hover
+ {
+ background: white;
+ }
+
+ input:focus {
+ border-color: black;
+ }
+
+ input[type=submit]:hover {
+ background: #5b4d9d;
+ }
+
+ .bgbottom {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ z-index: -100;
+ }
+
+
+ #arrows {
+ position: relative;
+ }
+
+ #arrows > img {
+ position: absolute;
+ bottom: 0;
+ z-index: -200;
+ }
+
+ #signupform {
+ display: none;
+ }
+
+ .window {
+ overflow: hidden;
+ box-sizing: border-box;
+ margin: 0rem;
+ padding: 0;
+ box-shadow: 0 0.8rem 1.3rem rgba(0,0,0,0.2);
+ border-radius: 0.5rem;
+ border: 1px solid #b9b9b9;
+
+ display: block;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 800px;
+ height: 600px;
+
+ transition: opacity 0.3s;
+ display: flex;
+ flex-direction: column;
+ }
+
+ .window.focus {
+ border-color: black;
+ }
+
+ .foldercontents, .filecontents, .filecontentsroot {
+ background: rgba(250, 250, 250, .9);
+ flex: 1 0 0;
+ }
+
+ .filecontents, .filecontentsroot {
+ background: white;
+ }
+
+ .filecontents {
+ font-family: monospace;
+ }
+
+ .filecontents.imgview {
+ background-color: black;
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: contain;
+ }
+
+ .filecontentsroot, .foldercontents {
+ display: flex;
+ flex-direction: column;
+ }
+
+ .filecontents {
+ overflow-y: scroll;
+ }
+ [contenteditable] {
+ outline: 0px solid transparent;
+ }
+
+ pre {
+ font-size: 1.3rem;
+ min-height: 100%;
+ margin: 0.3rem;
+ box-sizing: border-box;
+ }
+
+ .window h3,
+ .window > h2 {
+ color: #4d4d4d;
+ background: #f0f0f0;
+ margin: 0;
+
+
+ display: flex;
+ align-items: stretch;
+ font-weight: normal;
+ padding: 0rem;
+ border-bottom: 1px solid #bbb;
+ }
+
+ .window > h2 {
+ font-size: 1.3rem;
+ border-top-left-radius: 0.5rem;
+ border-top-right-radius: 0.5rem;
+ cursor: grab;
+
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ .window h3 {
+ font-size: 1.2rem;
+ }
+
+ .window.dragged {
+ /* opacity: 0.9; */
+ }
+
+ .files {
+ padding: 0.3rem;
+ display: grid;
+
+ grid-gap: 0rem;
+ grid-auto-rows: 8rem;
+ grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
+ }
+
+ .file {
+ box-sizing: border-box;
+ padding: 0.5rem;
+ cursor: pointer;
+ color: #333;
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ border: 1px solid rgba(0,0,0,0);
+ border-radius: 0.1rem;
+ }
+
+ .file.dragged {
+ border: none;
+ pointer-events: none;
+ }
+
+ .file:hover:not(.dragged) {
+ background: rgba(255,255,255, 0.5);
+ color: black;
+ border-color: #ddd;
+ }
+
+ .file > img {
+ flex: 1 1 0;
+ min-width: 0;
+ min-height: 0;
+ }
+
+ .file:hover > img {
+ filter: brightness(120%);
+ }
+
+ .path {
+ display: flex;
+ align-items: stretch;
+ flex: 1 0 0;
+ }
+
+ .path > .separator {
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ padding: 0 0.1rem;
+ align-self: center;
+ }
+
+ .pathentry:last-child {
+ cursor: inherit;
+ }
+
+ .pathentry:not(:last-child):hover {
+ text-decoration: underline;
+ }
+
+ .context {
+ background: white;
+ position: absolute;
+ z-index: 1000;
+ list-style-type: none;
+ margin: 0;
+ padding: 0;
+ top: 0;
+ left: 0;
+
+ border: 1px solid #ccc;
+ box-shadow: 0 0.3rem 0.5rem rgba(0,0,0,0.2);
+ }
+
+ .context > li {
+ padding: 0.4rem 1.5rem;
+ margin: 0;
+
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ }
+
+ .context > li:hover {
+ background: #2e91db;
+ color: white;
+ }
+
+ .backdrop {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+
+ user-select: none;
+ -webkit-user-select: none;
+ -ms-user-select: none;
+ background-image: url("/img/backdrop.jpg");
+ background-size: cover;
+ }
+
+ .share_dialog_contents {
+ display: flex;
+ flex-direction: column;
+ background: white;
+ background: rgba(255,255,255, 0.9);
+ }
+
+ .window > h2 .close_button {
+ border-left: 1px solid #ccc;
+ }
+
+ .close_button:hover {
+ background: white;
+ }
+
+
+ .wndtitle {
+ display: flex;
+ align-items: center;
+ flex: 1 1 0;
+ padding-left: 0.8rem;
+ }
+
+ iframe {
+ margin: 0;
+ border: 0;
+ }
+
+ .filename {
+ word-wrap: break-word;
+ word-break: break-word;
+ }
F diff --git a/svg/arrow.svg b/svg/arrow.svg
deleted file mode 100644
--- a/svg/arrow.svg
+++ /dev/null
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="128.46289"
- height="620.49023"
- viewBox="0 0 33.989141 164.17137"
- version="1.1"
- id="svg8"
- sodipodi:docname="arrow.svg"
- inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1033"
- inkscape:window-height="1050"
- id="namedview58"
- showgrid="false"
- inkscape:zoom="1.3908357"
- inkscape:cx="62.793461"
- inkscape:cy="309.88562"
- inkscape:window-x="16"
- inkscape:window-y="25"
- inkscape:window-maximized="0"
- inkscape:current-layer="svg8"
- inkscape:document-rotation="0" />
- <defs
- id="defs2" />
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1"
- transform="translate(-9.8831177,-26.865543)"
- style="fill:#ffffff;fill-opacity:1">
- <path
- id="rect12"
- style="fill:#ffffff;fill-rule:evenodd;stroke-width:0.860931;fill-opacity:1"
- d="M 101.58398,101.53906 37.353516,254.44747 75.386719,234.3418 v 487.68554 l 26.197261,-20.0516 26.19922,20.05356 V 234.3418 l 38.03321,20.10567 z"
- transform="scale(0.26458333)" />
- </g>
- </svg>
F diff --git a/svg/bottom.svg b/svg/bottom.svg
deleted file mode 100644
--- a/svg/bottom.svg
+++ /dev/null
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="1920"
- height="1080"
- viewBox="0 0 508 285.75"
- version="1.1"
- id="svg8"
- sodipodi:docname="bottom.svg"
- inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
- <sodipodi:namedview
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1"
- objecttolerance="10"
- gridtolerance="10"
- guidetolerance="10"
- inkscape:pageopacity="0"
- inkscape:pageshadow="2"
- inkscape:window-width="1920"
- inkscape:window-height="1080"
- id="namedview879"
- showgrid="false"
- inkscape:zoom="0.69739583"
- inkscape:cx="698.31217"
- inkscape:cy="540"
- inkscape:window-x="0"
- inkscape:window-y="0"
- inkscape:window-maximized="0"
- inkscape:current-layer="layer1" />
- <defs
- id="defs2" />
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- id="layer1">
- <path
- id="path67"
- style="fill:#231179;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
- d="m 497.63807,220.97629 c -0.21383,0.005 -0.42079,0.0476 -0.61497,0.1447 -0.30964,0.15483 -0.44627,0.54229 -0.73432,0.73432 -0.87445,0.58298 -2.75508,0.63823 -3.71295,1.11569 -0.31176,0.15541 -0.43654,0.56558 -0.73896,0.73846 -1.20155,0.6869 -2.47454,1.23984 -3.71245,1.8588 -3.67231,1.83615 -5.9033,1.81488 -9.942,1.81488 -3.68084,0 -0.648,2.88761 -3.12749,5.36711 -2.18828,2.1883 -4.03066,1.78386 -6.69104,2.60243 -3.72497,1.1462 0.8976,0.48362 -3.2489,1.11518 -1.41195,0.21506 -3.33134,-0.5085 -4.6142,0.35398 -0.80788,0.54319 -1.43637,1.32595 -2.2443,1.86913 -1.32096,0.88812 -1.62378,-0.004 -2.72386,1.46245 -0.49406,0.65874 -0.78574,1.4507 -1.0976,2.21278 -0.023,0.0565 0.0278,0.6494 0,0.6718 -0.7134,0.57388 -2.97177,2.19666 -3.95481,2.48925 -1.4324,0.42634 -3.91221,0.20244 -5.4348,0.37207 -1.82431,0.20323 -3.10261,0.97046 -4.82349,1.48673 -1.50932,0.45279 -3.41048,0.36304 -4.78835,0.92139 -0.51356,0.20812 -0.9615,0.56787 -1.48725,0.74311 -0.0245,0.008 -2.70674,0.24511 -2.87269,0.37207 -1.17967,0.90248 -2.13,2.36269 -3.58685,2.85254 -2.67652,0.89994 -5.15641,-0.79198 -7.26725,-1.85415 -0.30681,-0.15438 -0.41148,-0.59684 -0.72864,-0.72864 -0.58393,-0.24268 -1.09873,0.24949 -1.58906,0.37207 -0.24043,0.0601 -0.52194,-0.11083 -0.74361,0 -0.31348,0.15674 -0.45149,0.5487 -0.74311,0.74311 -1.4795,0.98633 -2.2283,1.44756 -2.59519,1.64589 -0.28194,0.2849 -0.84371,0.80561 -1.86603,1.70016 -0.67249,0.5884 -1.59388,0.85958 -2.23036,1.48673 -0.25789,0.2541 -0.24715,0.81774 -0.64339,0.99374 -0.5648,0.25087 -1.23613,0 -1.85414,0 -4.73573,0 -7.96452,-1.07723 -11.62876,1.85415 -2.64559,2.1165 -3.55491,5.00358 -5.18416,7.78608 -1.01481,1.73315 -3.39051,2.5346 -4.6762,3.94085 -0.25992,0.28606 0.0373,0.88714 -0.26252,1.13274 -2.72698,2.23357 -3.12936,2.20431 -4.84571,5.20124 -0.63365,1.10639 -0.16473,1.62327 -0.74361,2.97398 -0.4617,1.07729 -1.68582,1.73516 -2.21279,2.95589 -0.15565,0.36056 0.1827,0.87171 0,1.23713 -0.15674,0.31348 -0.61343,0.41823 -0.74364,0.74363 -0.13806,0.34515 0.0729,0.75065 0,1.11517 -0.35898,1.79489 -2.9442,4.69887 -4.82811,5.07566 -0.76573,0.15314 -5.75429,0.27416 -6.59442,0 -0.7656,-0.24984 -1.38044,-1.27974 -2.16059,-1.08003 -1.67865,0.42973 -3.77251,-0.15703 -5.34128,0.37155 -3.36248,1.13293 -5.64478,1.12189 -9.18964,1.12189 -0.99578,0 -2.63472,0.25916 -3.67469,0 -2.09669,-0.52243 -2.29434,-1.87255 -3.70263,-2.2257 -1.77134,-0.44418 -5.93903,0.26012 -7.44347,0.75138 -0.23342,0.0762 -0.41355,0.33979 -0.65887,0.32969 -0.79372,-0.0326 -1.12755,-1.18561 -1.86243,-1.48725 -0.81142,-0.33305 -1.67338,-0.52834 -2.52179,-0.75085 -1.61312,-0.42308 -2.45994,0.27724 -3.62614,1.05471 -0.6471,0.4314 -1.85804,-0.24986 -2.61689,0 -1.10773,0.36475 -2.40581,1.0697 -3.6091,1.0697 -0.33743,0 -0.39614,-0.60902 -0.71623,-0.71572 -1.69796,-0.56599 -2.40102,-0.74299 -4.01111,-1.78076 -0.30218,-0.19476 -5.05704,-0.49093 -6.29626,-1.11053 -1.46111,-0.73053 -0.001,-0.73679 -1.47278,-1.10588 -0.78605,-0.19712 -0.7843,0.33089 -1.48725,-0.37207 -1.71204,-1.71203 -2.5177,-2.76083 -4.95422,-3.92844 -1.48222,-0.71028 -4.07511,0.3212 -5.65496,-0.50746 -0.83762,-0.43935 -0.81479,-1.50876 -1.83036,-1.84434 -1.3996,-0.46243 -3.33833,0.23042 -4.80439,0 -0.26265,-0.0413 -0.47881,-0.2312 -0.71829,-0.34674 -0.0864,-0.0864 -0.14126,-0.2276 -0.25942,-0.2589 -2.29989,-0.60974 -4.98044,0.20989 -7.30446,-0.36742 -1.94929,-0.48422 -4.37084,0.63303 -6.26991,0 -0.81349,-0.27116 -1.47921,-1.00355 -2.17763,-1.46916 -1.66113,-1.10743 -3.94404,-2.43903 -5.88129,-2.94039 -2.71332,-0.70218 -6.00825,0.88449 -8.62894,-0.3669 -1.49247,-0.71266 -2.58787,-1.85912 -3.96565,-2.89182 -0.40918,-0.3067 -2.44325,-0.56894 -2.72128,-0.66094 -0.37565,-0.12431 -0.62032,-0.52561 -0.99839,-0.64234 -2.68235,-0.82824 -5.37604,1.01928 -7.96489,0.37207 -0.50788,-0.12697 -0.6247,-0.91598 -1.11052,-1.11104 -0.65105,-0.26141 -3.19028,-0.27466 -4.01423,0 -0.95413,0.31804 -1.67417,1.15422 -2.61948,1.49758 -0.72075,0.26179 -1.51901,0.22052 -2.26911,0.37982 -0.37422,0.0795 -0.67233,0.5779 -1.04231,0.4806 -0.24094,-0.0634 -0.47245,-0.66545 -0.71365,-0.71366 -1.61484,-0.32297 -1.69751,-0.19289 -3.33572,-0.73897 -1.73767,-0.57924 -4.1251,0.26068 -5.9433,0 -1.97976,-0.28384 -4.28466,-0.77562 -6.30504,-0.37155 -0.2717,0.0543 -0.4687,0.33715 -0.74363,0.37155 -0.8737,0.10921 -10.32235,-0.28155 -10.49031,-0.37155 -3.92899,-2.10471 -4.2422,-2.21796 -8.78965,-2.21796 -0.20941,0 -4.62015,0.46026 -4.79764,0.37156 -0.3996,-0.19981 -0.6856,-0.62037 -1.11518,-0.74311 -3.03456,-0.86701 -0.082,1.05631 -2.6019,-0.74362 -1.01452,-0.72465 -2.76881,-3.02727 -3.71761,-3.34553 -1.31732,-0.44188 -2.54185,0.37155 -3.69383,0.37155 -2.15526,0 -4.13542,-0.0666 -6.2699,-0.37155 -1.66509,-0.23787 -3.43728,0.26789 -5.09788,0 -1.98284,-0.3199 -4.136,-0.53441 -6.20841,-0.72606 -1.0062,-0.0931 -2.10829,0.41124 -3.03134,0 -0.32363,-0.14419 0.66285,-0.2512 0.2682,-0.50023 -0.21136,-0.13337 -0.52011,0.0985 -0.74982,0 -0.78632,-0.33698 -1.47729,-0.89788 -2.29547,-1.14773 -1.20609,-0.36831 -3.56557,0 -4.82037,0 -2.02487,0 -6.61095,0.55532 -8.13698,0 -0.21342,-0.0777 -2.86775,-2.04728 -3.00291,-1.90996 -2.28069,2.31718 -3.90435,6.13176 -7.23419,7.244 -1.40999,0.47097 -3.44576,-0.14158 -4.82296,-0.37207 -1.68505,-0.28202 -7.35983,-0.37619 -8.86457,0 -0.49565,0.12391 -1.04864,0.10922 -1.48673,0.37207 -0.70454,0.42273 -1.72072,2.05402 -2.60243,2.23036 -1.7418,0.34837 -3.64959,-0.22411 -5.41621,0 -1.67931,0.21305 -3.53647,1.11131 -5.25859,0.76687 -0.16445,-0.0329 -0.18995,-0.32893 -0.35553,-0.35553 -1.14451,-0.18388 -4.021125,-0.38821 -5.185728,0 -0.423844,0.14128 -0.70036,0.57718 -1.115176,0.74311 -1.451475,0.58059 -3.654618,0.62796 -5.204333,0.37207 -1.126384,-0.18598 -1.04458,-0.90503 -1.831414,-1.10174 -0.360638,-0.0902 -0.770028,0.13806 -1.115177,0 -0.325405,-0.13016 -0.411136,-0.6328 -0.743624,-0.74363 -1.240928,-0.41363 -3.213346,-0.17582 -4.460708,-0.37155 -1.809176,-0.2839 -3.827521,-1.04683 -5.587773,-1.47381 -1.64842,-0.39985 -1.741114,-1.86662 -4.748032,-2.16731 -5.409454,-0.54095 -11.412834,-0.011 -16.695105,0.74362 -1.681041,0.24015 -3.773774,1.91921 -5.947955,2.23036 -1.476822,0.21135 -3.366834,-0.47174 -4.75165,0 -0.96639,0.32924 -2.259102,0.63967 -3.331577,0.37155 -0.627976,-0.157 -1.788642,-1.05167 -2.480988,-0.70332 -0.742301,0.37349 -1.904894,2.61144 -2.8298,2.47375 -0.312037,-0.0464 -0.669301,-1.04087 -0.721921,-1.09347 -0.630166,-0.63017 -1.83686,-0.65414 -2.602423,-1.11518 -0.515459,-0.31042 -0.871453,-0.90037 -1.44539,-1.08107 -0.818266,-0.25763 -6.200195,-0.17458 -6.881752,0 -1.394754,0.35725 -2.689666,0.83539 -4.088639,1.11518 -1.923868,0.38478 -4.641963,-0.89646 -6.216675,-0.37155 -1.645038,0.54834 -2.970826,0.7431 -4.8322629,0.7431 -0.963813,0 -1.670468,-0.9414 -2.530078,-0.71933 -2.5866389,0.66822 -6.29791725,2.58785 -8.5498656,4.08916 -0.2916132,0.1944 -0.4187418,0.61215 -0.7436238,0.74362 -0.6323156,0.25588 -1.3415011,0.24764 -2.0122802,0.37155 l -1.0867554,9.08058 v 10e-4 c -0.3089068,1.2756 -0.2408467,2.75466 -0.7493081,3.8902 -0.1676488,0.3744 0.1500007,1.00218 0,1.37718 -0.1659266,0.41482 -0.6023428,0.69133 -0.7436239,1.11517 -0.5064281,1.51928 -0.2110941,3.23623 -0.7431072,4.83227 -0.294686,0.88406 -1.2685035,1.39606 -1.4696778,2.19573 -0.5917971,2.35242 0.6814589,4.4124 1.1105266,6.55774 0.072903,0.36451 -0.1175531,0.76252 0,1.11517 0.015277,0.0458 0.035384,0.0895 0.056844,0.1323 l -1.2014771,10.04073 v 5.3e-4 c -0.2488039,1.1631 -0.3110667,2.52356 -0.6004796,3.39047 -0.1427866,0.4277 -0.716228,0.67591 -0.750342,1.12551 -0.08441,1.11255 0.275246,2.21454 0.412894,3.32177 0,1.24931 -0.428128,3.31906 0,4.46071 0.253695,0.67654 0.8468392,1.18796 1.1151778,1.85881 0.9613397,2.40334 0.076328,4.32181 1.8691365,6.71224 0.7696115,1.02613 1.0081654,0.75964 2.2303549,1.48725 0.528941,0.3149 0.8693407,1.04037 1.3761434,1.37562 0.3255679,0.2154 0.9790305,-0.54211 1.1105264,0.37208 0.9503502,0.71276 1.82411345,2.06523 3.00343416,2.40761 2.25410904,0.65442 9.40822744,-0.79762 10.92078084,0 2.515322,1.3264 5.499643,0.96451 8.339025,1.18287 2.726979,0.20971 5.771924,0.75726 8.489923,0.90691 6.384589,0.35153 12.872973,0 19.277374,0 h 9.828858 c 1.519434,0 3.359555,-0.27707 4.857584,0 7.18616,1.32913 -0.753759,0.84958 9.474358,0.84958 H 100.3047 c 4.60658,0 9.3189,-0.2445 13.92886,0 5.25967,0.27895 3.19361,0.37483 7.92665,0.8578 4.14056,0.42249 8.31525,0.50512 12.44317,1.03717 12.48686,1.60938 1.58295,0.0618 5.04569,1.20613 0.84924,0.28062 3.93607,0.32168 4.3279,0.39119 2.84707,0.5053 5.62899,1.35948 8.4894,1.78284 3.21201,0.47543 6.70281,-0.22252 9.94151,0 9.709,0.66704 19.51619,0.39169 29.26798,0.39169 h 74.88431 11.89541 c 2.40265,0 4.82359,0.29665 7.20783,0 1.8986,-0.23625 3.70149,-0.90586 5.62911,-1.12242 2.99197,-0.33618 6.64258,0 9.53379,0 h 17.19172 c 3.31044,0 6.96111,0.27435 10.27999,0 8.33223,-0.68879 16.44521,-2.98336 24.90132,-3.37756 5.22139,-0.24339 10.29996,0.20449 15.47656,0.37208 2.47229,0.08 4.9608,-0.25948 7.42074,0 24.66634,2.60194 -1.93971,1.60478 19.83394,2.38485 9.03808,0.3238 18.16258,0 27.20816,0 3.78613,0 7.99158,-0.29337 11.80547,0 2.58548,0.19889 4.85296,1.92849 7.47241,2.23761 1.1099,0.13096 2.24526,-0.14973 3.35278,0 0.78478,0.10609 1.4696,0.63595 2.25205,0.75808 3.76206,0.58724 10.32304,0 14.27872,0 1.92955,0 7.04114,0.57637 8.92193,0 1.19007,-0.3647 2.2569,-1.0545 3.41789,-1.50326 5.53447,-2.13929 10.75036,-3.68609 16.68116,-4.45659 4.14311,-0.53826 9.81726,-0.63002 14.15158,-1.69653 0.6863,-0.16888 1.22132,-0.71218 1.82417,-1.08106 1.8156,-1.11096 3.56746,-2.32365 5.37229,-3.45199 0.59264,-0.3705 4.51053,-2.1979 5.38313,-3.56153 0.86445,-1.35094 -1.28357,-9.90698 -1.49397,-11.57658 -0.27924,-2.21586 -0.57947,-5.29765 -0.74311,-7.42487 -0.12483,-1.6228 0.25445,-1.62243 -0.3674,-3.33209 -0.34877,-0.95874 -1.29609,-2.20644 -1.48675,-3.34553 -0.25831,-1.54326 0.15529,-3.26313 0,-4.81417 -0.15271,-1.52508 -0.83944,-2.95301 -1.11516,-4.46071 -0.5093,-2.78479 2.7786,-2.44943 3.00188,-7.10345 0.0764,-1.59162 0.26138,-7.10688 0,-9.19478 -0.10885,-0.86941 -0.42907,-1.72757 -0.37208,-2.60191 0.26971,-4.14004 2.79027,-7.29198 2.44274,-11.48767 -0.1468,-1.77283 -1.30636,-2.67562 -1.48672,-4.52169 -0.42971,-4.39852 -0.44133,-8.46347 -0.44133,-12.84418 v -12.8726 c 0,-1.37247 0.18214,-2.75724 0,-4.11758 -0.074,-0.55273 -0.57327,-0.96652 -0.75136,-1.495 -0.29189,-0.86633 0.094,-1.9343 -0.37981,-2.71611 -0.53412,-0.88125 -7.68369,-1.46805 -7.8259,-1.49655 -4.19285,-0.83992 -4.43378,-3.32318 -7.43159,-4.32893 -0.61746,-0.20717 -1.3222,-0.53158 -1.96369,-0.51677 z m -83.81814,31.47715 c 0.88741,-0.89676 -0.97401,0.52652 0,0 z" />
- <path
- id="rect30"
- style="fill:#f9f9f9;stroke-width:0.264583"
- d="m 507.90936,37.330101 h 55.26015 V 316.89684 H 507.90936 Z M -55.282452,286.078 H 538.42226 v 49.69312 H -55.282452 Z M -72.186882,71.966187 H -0.1733017 V 321.29134 H -72.186882 Z" />
- </g>
- </svg>
F diff --git a/svg/icons/.txt.svg b/svg/icons/.txt.svg
deleted file mode 100644
--- a/svg/icons/.txt.svg
+++ /dev/null
- <?xml version="1.0" encoding="UTF-8" standalone="no"?>
- <svg
- xmlns:dc="http://purl.org/dc/elements/1.1/"
- xmlns:cc="http://creativecommons.org/ns#"
- xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:svg="http://www.w3.org/2000/svg"
- xmlns="http://www.w3.org/2000/svg"
- xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
- xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="210mm"
- height="297mm"
- viewBox="0 0 210 297"
- version="1.1"
- id="svg8"
- inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"
- sodipodi:docname="txt.svg">
- <defs
- id="defs2" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.35"
- inkscape:cx="400"
- inkscape:cy="560"
- inkscape:document-units="mm"
- inkscape:current-layer="layer1"
- inkscape:document-rotation="0"
- showgrid="false"
- inkscape:window-width="1918"
- inkscape:window-height="1059"
- inkscape:window-x="768"
- inkscape:window-y="19"
- inkscape:window-maximized="0" />
- <metadata
- id="metadata5">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- <dc:title></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <text
- xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:10.5833px;line-height:1.25;font-family:sans-serif;white-space:pre;inline-size:15.4878;opacity:1;fill:#008080;fill-opacity:1;stroke:none;stroke-width:0.264583;"
- x="54.487259"
- y="164.02113"
- id="text12"
- transform="matrix(11.172769,0,0,12.603914,-603.10178,-1943.9698)"><tspan
- x="54.487259"
- y="164.02113"><tspan
- style="fill:#008080;stroke-width:0.264583">txt</tspan></tspan></text>
- </g>
- </svg>
F diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
--- /dev/null
+++ b/tsconfig.json
+ {
+ "compilerOptions": {
+ "rootDir": "front",
+ "outFile": "build/bundle.js"
+ }
+ }
<
\ No newline at end of file