1 // Handle any tasks which require uploading files to the server (such as attachments)
2 // h/t https://www.smashingmagazine.com/2018/01/drag-drop-file-uploader-vanilla-js/ which inspired the design of this module
4 // Copyright (c) 2016-2023 by the citadel.org team
6 // This program is open source software. Use, duplication, or
7 // disclosure are subject to the GNU General Public License v3.
9 var uploads_in_progress = 0;
10 var uploads = [] ; // everything the user has uploaded
13 // Turn the specified div into a place where we can upload. (Note: permanently changes the drag-and-drop behavior of that div.)
14 function activate_uploads(parent_div) {
15 document.getElementById(parent_div).innerHTML += `
16 <div class="ctdl-upload" id="ctdl-upload">
17 <div id="ctdl_attachments_title" class="ctdl-compose-attachments-title">
18 <div><h1><i class="fa fa-paperclip" style="color:grey"></i>` + _("Attachments:") + ` <span id="num_attachments">` + uploads.length + `</span></h1></div>
19 <div><h1><i class="fas fa-window-close" style="color:red" onClick="show_or_hide_upload_window()"></i></h1></div>
22 <ul id="ctdl-upload_list">
25 <div id="drop-area" class="ctdl-upload-drop-area">
26 <form class="ctdl-upload-form">
27 <p>${_("Drop files here to upload")}</p>
28 <input type="file" id="fileElem" multiple accept="*/*" onChange="handle_upload_files(this.files)">
34 // activate drag and drop
35 let dropArea = document.getElementById(parent_div);
36 ;['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
37 dropArea.addEventListener(eventName, upload_prevent_defaults, false)
39 ;["dragenter", "dragover"].forEach(eventName => {
40 dropArea.addEventListener(eventName, upload_highlight, false)
42 ;['dragleave', 'drop'].forEach(eventName => {
43 dropArea.addEventListener(eventName, upload_unhighlight, false)
45 dropArea.addEventListener('drop', upload_handle_drop, false);
49 // prevent drag and drop events from propagating up through the DOM
50 function upload_prevent_defaults(e) {
56 function upload_handle_drop(e) {
57 let dt = e.dataTransfer;
59 handle_upload_files(files);
63 function handle_upload_files(files) {
64 ([...files]).forEach(upload_file)
68 function upload_file(file) {
70 var xhr = new XMLHttpRequest();
71 var formData = new FormData();
72 xhr.open('POST', url, true);
74 xhr.addEventListener('readystatechange', function(e) {
75 if (xhr.readyState == 4 && xhr.status == 200) {
76 // remove the "uploading in progress" message
77 let li = document.getElementById("ctdl_uploading_" + uploads_in_progress.toString());
78 li.parentNode.removeChild(li);
79 uploads_in_progress -= 1;
81 // The response body will be a JSON array of completed uploads.
82 var j_response = JSON.parse(xhr.response);
84 // Add these uploads to the displayed list
85 j_response.forEach((item) => {
86 let new_upl = document.createElement("li");
87 // item["ref"] is what we need
88 new_upl.innerHTML = `<i class="fa-solid fa-circle-xmark" style="color:red" onClick="alert('` + item["ref"] + `')"></i>`
90 + item["uploadfilename"] + " (" + item["contenttype"] + ", " + item["contentlength"] + " " + _("bytes") + ")";
91 document.getElementById("ctdl-upload_list").appendChild(new_upl);
93 // append it to the global list of uploads
96 document.getElementById("num_attachments").innerHTML = uploads.length;
98 else if (xhr.readyState == 4 && xhr.status != 200) {
99 // remove the "uploading in progress" message (there was an error, so just let it disappear)
100 let li = document.getElementById("ctdl_uploading_" + uploads_in_progress.toString());
101 li.parentNode.removeChild(li);
102 uploads_in_progress -= 1;
106 formData.append('file', file);
108 uploads_in_progress += 1;
110 // Make an "uploading in progress" message appear in the uploads list!
111 progress = document.createElement("li");
112 progress.setAttribute("id", "ctdl_uploading_" + uploads_in_progress.toString());
113 progress.innerHTML = `<img src="/ctdl/s/images/dotcrawl.gif" /> ` + _("Processing dropped files...");
114 document.getElementById("ctdl-upload_list").appendChild(progress);
118 // called when the user drags a file into the upload area
119 function upload_highlight(e) {
120 let dropArea = document.getElementById("ctdl-upload");
121 dropArea.classList.add('highlight')
123 document.getElementById("ctdl-upload").style.display = "block"; /* also make it appear */
127 // called when the user is no longer dragging a file into the upload area
128 function upload_unhighlight(e) {
129 let dropArea = document.getElementById("ctdl-upload");
130 dropArea.classList.remove('highlight')
134 // Show or hide the attachments window in the composer
135 function show_or_hide_upload_window() {
137 if (document.getElementById("ctdl-upload").style.display == "block") {
138 document.getElementById("ctdl-upload").style.display = "none"; /* turn it off */
141 document.getElementById("ctdl-upload").style.display = "block"; /* turn it on */
146 // Flush all uploaded files and close the window
147 function flush_uploads() {
148 document.getElementById('ctdl-upload').style.display='none';
150 // FIXME tell the server to delete the files
151 uploads.forEach(u => {