pics/public/js/upload_queue.js

212 lines
5.5 KiB
JavaScript

class UploadQueue {
constructor(options) {
this.queue = options.queue_element;
this.preview_area = options.preview_area;
this.upload_progress = [];
this.upload_url = options.upload_url;
this.submit = options.submit_button;
this.addEvents();
}
addEvents() {
this.queue.addEventListener('change', event => {
this.showSpinner(this.queue, "Generating previews (not uploading yet!)");
this.clearPreviews();
for (let i = 0; i < this.queue.files.length; i++) {
const callback = (i !== this.queue.files.length - 1) ? null : () => {
this.hideSpinner();
this.submit.disabled = false;
};
if (this.queue.files[0].name.toUpperCase().endsWith(".HEIC")) {
alert('Sorry, the HEIC image format is not supported.\nPlease convert your photos to JPEG before uploading.');
this.hideSpinner();
this.submit.disabled = false;
break;
}
this.addPreviewBoxForQueueSlot(i);
this.addPreviewForFile(this.queue.files[i], i, callback);
};
});
this.submit.addEventListener('click', event => {
event.preventDefault();
this.process();
});
this.submit.disabled = true;
}
clearPreviews() {
this.preview_area.innerHTML = '';
this.submit.disabled = true;
this.current_upload_index = -1;
}
addPreviewBoxForQueueSlot(index) {
const preview_box = document.createElement('div');
preview_box.id = 'upload_preview_' + index;
this.preview_area.appendChild(preview_box);
}
addPreviewForFile(file, index, callback) {
if (!file) {
return false;
}
const preview = document.createElement('canvas');
preview.title = file.name;
const preview_box = document.getElementById('upload_preview_' + index);
preview_box.appendChild(preview);
const reader = new FileReader();
reader.addEventListener('load', event => {
const original = document.createElement('img');
original.src = reader.result;
original.addEventListener('load', function() {
// Preparation: make canvas size proportional to the original image.
preview.height = 150;
preview.width = preview.height * (original.width / original.height);
// First pass: resize to 50% on temp canvas.
const temp = document.createElement('canvas'),
tempCtx = temp.getContext('2d');
temp.width = original.width * 0.5;
temp.height = original.height * 0.5;
tempCtx.drawImage(original, 0, 0, temp.width, temp.height);
// Second pass: resize again on temp canvas.
tempCtx.drawImage(temp, 0, 0, temp.width * 0.5, temp.height * 0.5);
// Final pass: resize to desired size on preview canvas.
const context = preview.getContext('2d');
context.drawImage(temp, 0, 0, temp.width * 0.5, temp.height * 0.5,
0, 0, preview.width, preview.height);
if (callback) {
callback();
}
});
}, false);
reader.readAsDataURL(file);
}
process() {
this.showSpinner(this.submit, "Preparing to upload files...");
if (this.queue.files.length > 0) {
this.submit.disabled = true;
this.nextFile();
}
}
nextFile() {
const files = this.queue.files;
const i = ++this.current_upload_index;
if (i === files.length) {
this.hideSpinner();
} else {
this.setSpinnerLabel("Uploading file " + (i + 1) + " out of " + files.length);
this.sendFile(files[i], i, this.nextFile);
}
}
sendFile(file, index, callback) {
const request = new XMLHttpRequest();
request.addEventListener('error', event => {
this.updateProgress(index, -1);
});
request.addEventListener('progress', event => {
this.updateProgress(index, event.loaded / event.total);
});
request.addEventListener('load', event => {
this.updateProgress(index, 1);
if (request.responseText !== null && request.status === 200) {
const obj = JSON.parse(request.responseText);
if (obj.error) {
alert(obj.error);
return;
}
else if (callback) {
callback.call(this, obj);
}
}
});
const data = new FormData();
data.append('uploads', file, file.name);
request.open('POST', this.upload_url, true);
request.send(data);
}
addProgressBar(index) {
if (index in this.upload_progress) {
return;
}
const progress_container = document.createElement('div');
progress_container.className = 'progress';
const progress = document.createElement('div');
progress_container.appendChild(progress);
const preview_box = document.getElementById('upload_preview_' + index);
preview_box.appendChild(progress_container);
this.upload_progress[index] = progress;
}
updateProgress(index, progress) {
if (!(index in this.upload_progress)) {
this.addProgressBar(index);
}
const bar = this.upload_progress[index];
if (progress >= 0) {
bar.style.width = Math.ceil(progress * 100) + '%';
} else {
bar.style.width = "";
if (progress === -1) {
bar.className = "error";
}
}
}
showSpinner(sibling, label) {
if (this.spinner) {
return;
}
this.spinner = document.createElement('div');
this.spinner.className = 'spinner';
sibling.parentNode.appendChild(this.spinner);
if (label) {
this.spinner_label = document.createElement('span');
this.spinner_label.className = 'spinner_label';
this.spinner_label.innerHTML = label;
sibling.parentNode.appendChild(this.spinner_label);
}
}
setSpinnerLabel(label) {
if (this.spinner_label) {
this.spinner_label.innerHTML = label;
}
}
hideSpinner() {
if (this.spinner) {
this.spinner.parentNode.removeChild(this.spinner);
this.spinner = null;
}
if (this.spinner_label) {
this.spinner_label.parentNode.removeChild(this.spinner_label);
this.spinner_label = null;
}
}
}