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.includes(".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; } } }