Aaron van Geffen
12ea378b02
To create inline thumbnails locally, the previous version of the photo uploader used base64 representations of the local files to be uploaded. While this is effective, it is a little wasteful. When uploading several photos at once, this could even lead to the page becoming less responsive. This commit changes the process such that the photos are actually resized on a canvas, only using the local original as a buffer. Hence, only the resized photo is retained in memory.
208 lines
5.6 KiB
JavaScript
208 lines
5.6 KiB
JavaScript
function UploadQueue(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();
|
|
}
|
|
|
|
UploadQueue.prototype.addEvents = function() {
|
|
var that = this;
|
|
that.queue.addEventListener('change', function() {
|
|
that.showSpinner(that.queue, "Generating previews (not uploading yet!)");
|
|
that.clearPreviews();
|
|
for (var i = 0; i < that.queue.files.length; i++) {
|
|
var callback = (i !== that.queue.files.length - 1) ? null : function() {
|
|
that.hideSpinner();
|
|
that.submit.disabled = false;
|
|
};
|
|
that.addPreviewBoxForQueueSlot(i);
|
|
that.addPreviewForFile(that.queue.files[i], i, callback);
|
|
};
|
|
});
|
|
that.submit.addEventListener('click', function(e) {
|
|
e.preventDefault();
|
|
that.process();
|
|
});
|
|
this.submit.disabled = true;
|
|
};
|
|
|
|
UploadQueue.prototype.clearPreviews = function() {
|
|
this.preview_area.innerHTML = '';
|
|
this.submit.disabled = true;
|
|
this.current_upload_index = -1;
|
|
}
|
|
|
|
UploadQueue.prototype.addPreviewBoxForQueueSlot = function(index) {
|
|
var preview_box = document.createElement('div');
|
|
preview_box.id = 'upload_preview_' + index;
|
|
this.preview_area.appendChild(preview_box);
|
|
};
|
|
|
|
UploadQueue.prototype.addPreviewForFile = function(file, index, callback) {
|
|
if (!file) {
|
|
return false;
|
|
}
|
|
|
|
var preview = document.createElement('canvas');
|
|
preview.title = file.name;
|
|
|
|
var preview_box = document.getElementById('upload_preview_' + index);
|
|
preview_box.appendChild(preview);
|
|
|
|
var reader = new FileReader();
|
|
var that = this;
|
|
reader.addEventListener('load', function() {
|
|
var 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.
|
|
var 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.
|
|
var 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);
|
|
};
|
|
|
|
UploadQueue.prototype.process = function() {
|
|
this.showSpinner(this.submit, "Preparing to upload files...");
|
|
if (this.queue.files.length > 0) {
|
|
this.submit.disabled = true;
|
|
this.nextFile();
|
|
}
|
|
};
|
|
|
|
UploadQueue.prototype.nextFile = function() {
|
|
var files = this.queue.files;
|
|
var 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, function() {
|
|
this.nextFile();
|
|
});
|
|
}
|
|
};
|
|
|
|
UploadQueue.prototype.sendFile = function(file, index, callback) {
|
|
// Prepare the request.
|
|
var that = this;
|
|
var request = new XMLHttpRequest();
|
|
request.addEventListener('error', function(event) {
|
|
that.updateProgress(index, -1);
|
|
});
|
|
request.addEventListener('progress', function(event) {
|
|
that.updateProgress(index, event.loaded / event.total);
|
|
});
|
|
request.addEventListener('load', function(event) {
|
|
that.updateProgress(index, 1);
|
|
if (request.responseText !== null && request.status === 200) {
|
|
var obj = JSON.parse(request.responseText);
|
|
if (obj.error) {
|
|
alert(obj.error);
|
|
return;
|
|
}
|
|
else if (callback) {
|
|
callback.call(that, obj);
|
|
}
|
|
}
|
|
});
|
|
|
|
var data = new FormData();
|
|
data.append('uploads', file, file.name);
|
|
|
|
request.open('POST', this.upload_url, true);
|
|
request.send(data);
|
|
};
|
|
|
|
UploadQueue.prototype.addProgressBar = function(index) {
|
|
if (index in this.upload_progress) {
|
|
return;
|
|
}
|
|
|
|
var progress_container = document.createElement('div');
|
|
progress_container.className = 'progress';
|
|
|
|
var progress = document.createElement('div');
|
|
progress_container.appendChild(progress);
|
|
|
|
var preview_box = document.getElementById('upload_preview_' + index);
|
|
preview_box.appendChild(progress_container);
|
|
|
|
this.upload_progress[index] = progress;
|
|
};
|
|
|
|
UploadQueue.prototype.updateProgress = function(index, progress) {
|
|
if (!(index in this.upload_progress)) {
|
|
this.addProgressBar(index);
|
|
}
|
|
|
|
var 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";
|
|
}
|
|
}
|
|
};
|
|
|
|
UploadQueue.prototype.showSpinner = function(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);
|
|
}
|
|
};
|
|
|
|
UploadQueue.prototype.setSpinnerLabel = function(label) {
|
|
if (this.spinner_label) {
|
|
this.spinner_label.innerHTML = label;
|
|
}
|
|
}
|
|
|
|
UploadQueue.prototype.hideSpinner = function() {
|
|
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;
|
|
}
|
|
};
|