1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
/*
sources:
MDN:
https://developer.mozilla.org/en-US/docs/Web/API/File_API/Using_files_from_web_applications#example_uploading_a_user-selected_file
https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API/File_drag_and_drop
web.dev:
https://web.dev/patterns/clipboard/paste-files/
note that this likely means the code is mixed-licensed under:
MDN: "Attributions and copyright licensing" by Mozilla Contributors, licensed under CC-BY-SA 2.5.
web.dev: code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies
*/
addEventListener("DOMContentLoaded", function () {
function uploadFiles(files) {
for (const file of files) {
const li = document.createElement("li");
if (file.type.startsWith("image/")) {
const img = document.createElement("img");
img.src = URL.createObjectURL(file);
img.alt = file.name;
li.appendChild(img);
}
li.appendChild(document.createTextNode("Uploading " + file.name + "..."));
preview.appendChild(li);
new FileUpload(li, file)
}
}
/* DRAG-N-DROP */
const preview = document.getElementById("preview");
function dropHandler(ev) {
ev.preventDefault();
const files = [...ev.dataTransfer.items]
.map((item) => item.getAsFile())
.filter((file) => file);
uploadFiles(files);
}
window.addEventListener("drop", dropHandler);
window.addEventListener("dragover", (e) => {
const fileItems = [...e.dataTransfer.items].filter(
(item) => item.kind === "file",
);
if (fileItems.length > 0) {
e.preventDefault();
if (fileItems.some((item) => item.type.startsWith("image/"))) {
e.dataTransfer.dropEffect = "copy";
} else {
e.dataTransfer.dropEffect = "none";
}
}
});
const fileInput = document.getElementById("file-input");
fileInput.addEventListener("change", (e) => {
uploadFiles(e.target.files);
});
window.addEventListener("drop", (e) => {
if ([...e.dataTransfer.items].some((item) => item.kind === "file")) {
e.preventDefault();
}
});
/* PASTE */
document.addEventListener('paste', async (e) => {
// Prevent the default behavior, so you can code your own logic.
e.preventDefault();
if (!e.clipboardData.files.length) {
return;
}
// Iterate over all pasted files.
uploadFiles(e.clipboardData.files)
});
});
function FileUpload(preview, file) {
this.ctrl = createThrobber(preview);
const xhr = new XMLHttpRequest();
this.xhr = xhr;
this.xhr.upload.addEventListener("progress", (e) => {
if (e.lengthComputable) {
const percentage = Math.round((e.loaded * 100) / e.total);
this.ctrl.update(percentage);
}
});
xhr.upload.addEventListener("load", (e) => {
this.ctrl.update(100);
});
xhr.addEventListener("load", (e) => {
preview.removeChild(this.ctrl);
const a = document.createElement('a');
const text = document.createTextNode(xhr.responseText);
a.href = xhr.responseText;
a.appendChild(text);
preview.appendChild(a);
});
xhr.open("POST", ascend(preview, "form").action + "/" + file.name);
xhr.overrideMimeType("application/octet-stream; charset=x-user-defined-binary");
xhr.send(file);
}
function createThrobber(element) {
const throbberWidth = 64;
const throbberHeight = 6;
const throbber = document.createElement("canvas");
throbber.classList.add("upload-progress");
throbber.setAttribute("width", throbberWidth);
throbber.setAttribute("height", throbberHeight);
element.appendChild(throbber);
throbber.ctx = throbber.getContext("2d");
throbber.ctx.fillStyle = "hsl(from orange h s l)";
throbber.update = (percent) => {
throbber.ctx.fillRect(
0,
0,
(throbberWidth * percent) / 100,
throbberHeight,
);
throbber.ctx.fillStyle = "hsl(from orange calc(h + " + (90*percent/100) + ") s l)";
if (percent === 100) {
throbber.ctx.fillStyle = "hsl(from green h s l)";
}
};
throbber.update(0);
return throbber;
}
function ascend(from_element, to_tag_name) {
to_tag_name = to_tag_name.toLowerCase();
e = from_element;
while (e && e.parentNode) {
e = e.parentNode;
if (e.tagName && e.tagName.toLowerCase() == to_tag_name) {
return e;
}
}
}
|