static/js/common/upload-image.js (97 lines of code) (raw):
import $ from 'jquery';
import { _pd } from '../lib/prevent-default';
import { capabilities } from '../zamboni/capabilities';
import { CustomFormData } from '../zamboni/form-data';
/* To use this, upload_field must have a parent form that contains a
csrf token. Additionally, the field must have the attribute
data-upload-url. It will upload the files (note: multiple files
are supported; they are uploaded separately and each event is triggered
separately), and clear the upload field.
The data-upload-url must return a JSON object containing an `upload_hash`
and an `errors` array. If the error array is empty ([]), the upload is
assumed to be a success.
Example:
No Error: {"upload_hash": "123ABC", "errors": []}
Error: {"upload_hash": "", "errors": ["Uh oh!"]}
In the events, the `file` var is a JSON object with the following:
- name
- size
- type: image/jpeg, etc
- instance: A unique ID for distinguishing between multiple uploads.
- dataURL: a data url for the image (`false` if it doesn't exist)
Events:
- upload_start(e, file): The upload is started
- upload_success(e, file, upload_hash): The upload was successful
- upload_errors(e, file, array_of_errors): The upload failed
- upload_finished(e, file): Called after a success OR failure
- [todo] upload_progress(e, file, percent): Percentage progress of the file
upload.
- upload_start_all(e): All uploads are starting
- upload_finished_all(e): All uploads have either succeeded or failed
[Note: the upload_*_all events are only triggered if there is at least one
file in the upload box when the "onchange" event is fired.]
*/
// Get an object URL across browsers.
$.fn.objectUrl = function (offset) {
let files = $(this)[0].files,
url = false;
if (capabilities.fileAPI && files.length) {
offset = offset || 0;
let f = files[offset];
if (typeof window.URL !== 'undefined') {
url = window.URL.createObjectURL(f);
} else if (typeof window.webkitURL == 'function') {
url = window.webkitURL.createObjectURL(f);
} else if (typeof f.getAsDataURL == 'function') {
url = f.getAsDataURL();
}
}
return url;
};
let instance_id = 0;
$.fn.imageUploader = function () {
let $upload_field = this,
outstanding_uploads = 0,
files = $upload_field[0].files,
url = $upload_field.attr('data-upload-url'),
csrf = $upload_field.closest('form').find('input[name^=csrf]').val();
// No files? No API support? No shirt? No service.
if (!capabilities.fileAPI || files.length === 0) {
return false;
}
$upload_field.trigger('upload_start_all');
// Loop through the files.
$.each(files, function (v, f) {
let data = '',
file = {
instance: instance_id,
name: f.name || f.fileName,
size: f.size,
type: f.type,
aborted: false,
dataURL: false,
},
finished = function () {
outstanding_uploads--;
if (outstanding_uploads <= 0) {
$upload_field.trigger('upload_finished_all');
}
$upload_field.trigger('upload_finished', [file]);
},
formData = new CustomFormData();
instance_id++;
outstanding_uploads++;
if (
$upload_field.attr('data-allowed-types').split('|').indexOf(file.type) < 0
) {
let errors = [gettext('Images must be either PNG or JPG.')];
if (typeof $upload_field.attr('multiple') !== 'undefined') {
// If we have a `multiple` attribute, assume not an icon.
if ($upload_field.attr('data-allowed-types').indexOf('video') > -1) {
errors.push([gettext('Videos must be in WebM.')]);
}
}
$upload_field.trigger('upload_start', [file]);
$upload_field.trigger('upload_errors', [file, errors]);
finished();
return;
}
file.dataURL = $upload_field.objectUrl(v);
// And we're off!
$upload_field.trigger('upload_start', [file]);
// Set things up
formData.open('POST', url, true);
formData.append('csrfmiddlewaretoken', csrf);
formData.append('upload_image', f);
// Monitor progress and report back.
formData.xhr.onreadystatechange = function () {
if (
formData.xhr.readyState == 4 &&
formData.xhr.responseText &&
(formData.xhr.status == 200 || formData.xhr.status == 304)
) {
let json = {};
try {
json = JSON.parse(formData.xhr.responseText);
} catch (err) {
let error = gettext('There was a problem contacting the server.');
$upload_field.trigger('upload_errors', [file, error]);
finished();
return false;
}
if (json.errors.length) {
$upload_field.trigger('upload_errors', [file, json.errors]);
} else {
$upload_field.trigger('upload_success', [file, json.upload_hash]);
}
finished();
}
};
// Actually do the sending.
formData.send();
});
// Clear out images, since we uploaded them.
$upload_field.val('');
};