2016-02-12 23:52:43 +08:00
|
|
|
(function (exports) {
|
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
var styleOptionRe = /(\d+)x(\d+)/i;
|
|
|
|
|
|
|
|
function parseStyleOption(option) {
|
|
|
|
var m = option.match(styleOptionRe);
|
|
|
|
|
|
|
|
return {
|
|
|
|
width: m && m[1] || 150,
|
|
|
|
height: m && m[2] || 150
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
// Edits (size, quality, parameters) image file for S3 server uploading
|
2016-02-12 23:52:43 +08:00
|
|
|
function generateThumbnail(origFile, type, max_width, max_height, cb) {
|
|
|
|
var img = new Image;
|
|
|
|
var canvas = document.createElement("canvas");
|
|
|
|
var ctx = canvas.getContext("2d");
|
|
|
|
// todo allow for different x/y ratio
|
|
|
|
canvas.width = max_width;
|
|
|
|
canvas.height = max_height;
|
|
|
|
|
|
|
|
img.onload = function () {
|
|
|
|
var size;
|
|
|
|
var offsetX = 0;
|
|
|
|
var offsetY = 0;
|
2016-07-18 19:16:41 +08:00
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
if (this.width > this.height) {
|
|
|
|
size = this.height;
|
|
|
|
offsetX = (this.width - this.height) / 2;
|
|
|
|
} else {
|
|
|
|
size = this.width;
|
|
|
|
offsetY = (this.height - this.width) / 2;
|
|
|
|
}
|
2016-08-09 20:39:24 +08:00
|
|
|
if (type === "image/jpeg") {
|
2016-02-12 23:52:43 +08:00
|
|
|
type = "image/jpg";
|
|
|
|
}
|
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
ctx.drawImage(this, offsetX, offsetY, size, size, 0, 0,
|
|
|
|
canvas.width, canvas.height);
|
2016-02-12 23:52:43 +08:00
|
|
|
canvas.toBlob(function (blob) {
|
|
|
|
cb(blob);
|
2016-08-05 23:00:29 +08:00
|
|
|
}, type, 0.8);
|
2016-02-12 23:52:43 +08:00
|
|
|
};
|
2016-08-05 23:00:29 +08:00
|
|
|
img.src = URL.createObjectURL(origFile);
|
2016-02-12 23:52:43 +08:00
|
|
|
}
|
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
// This server checks if files are OK (correct file type, presence,
|
2016-08-09 20:39:24 +08:00
|
|
|
// size and spoofing) and only then generates posts for S3 server
|
|
|
|
// file uploading (each post for different size of the same file)
|
2016-08-05 23:00:29 +08:00
|
|
|
function fetchUploadSignature(file, signUrl, cb) {
|
2016-02-12 23:52:43 +08:00
|
|
|
var csrfParam = $("meta[name=csrf-param]").attr("content");
|
|
|
|
var csrfToken = $("meta[name=csrf-token]").attr("content");
|
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
var formData = new FormData();
|
|
|
|
formData.append("file", file);
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
url : signUrl,
|
|
|
|
type : 'POST',
|
|
|
|
data : formData,
|
|
|
|
processData: false,
|
|
|
|
contentType: false,
|
|
|
|
complete : function(xhr) {
|
|
|
|
if (xhr.readyState === 4) { // complete
|
|
|
|
var data = JSON.parse(xhr.responseText);
|
|
|
|
cb(data);
|
|
|
|
} else if (xhr.readyState == 0) { // connection error
|
|
|
|
cb();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2016-02-12 23:52:43 +08:00
|
|
|
}
|
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
// Upload file to S3 server
|
2016-08-09 20:39:24 +08:00
|
|
|
function uploadFile(postData, cb) {
|
2016-02-12 23:52:43 +08:00
|
|
|
var xhr = new XMLHttpRequest;
|
|
|
|
var fd = new FormData();
|
2016-08-05 23:00:29 +08:00
|
|
|
var fields = postData.fields;
|
|
|
|
var url = postData.url;
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
for (var k in fields) {
|
|
|
|
fd.append(k, fields[k]);
|
|
|
|
}
|
2016-08-05 23:00:29 +08:00
|
|
|
fd.append("file", postData.file, postData.fileName);
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2016-08-05 23:00:29 +08:00
|
|
|
xhr.onreadystatechange = function() {
|
|
|
|
if (xhr.readyState === 4) { // complete
|
|
|
|
cb();
|
|
|
|
} else if (xhr.readyState == 0) { // connection error
|
2016-08-09 20:39:24 +08:00
|
|
|
cb(I18n.t("general.file.upload_failure"));
|
2016-08-05 23:00:29 +08:00
|
|
|
}
|
|
|
|
}
|
2016-02-12 23:52:43 +08:00
|
|
|
xhr.open("POST", url);
|
|
|
|
xhr.send(fd);
|
|
|
|
}
|
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
// For each file proccesses its posts and uploads them
|
|
|
|
// If one post fails, the user is allowed to leave page,
|
|
|
|
// but other files are still being uplaoded because of
|
|
|
|
// asynchronous behaviour, so errors for other files may
|
|
|
|
// can still show afterwards
|
|
|
|
// TODO On S3 server uplaod error the other files that
|
|
|
|
// were already asynchronously uploaded remain, but
|
|
|
|
// should be deleted - this should generally not happen,
|
|
|
|
// because all files should fail to upload in such cases
|
|
|
|
// (connection error)
|
|
|
|
function uploadFiles(ev, fileInputs, datas, cb) {
|
2016-08-05 23:00:29 +08:00
|
|
|
var noErrors = true;
|
2016-08-09 20:39:24 +08:00
|
|
|
$.each(datas, function(fileIndex, data) {
|
|
|
|
var fileInput = fileInputs.get(fileIndex);
|
2016-08-05 23:00:29 +08:00
|
|
|
var file = fileInput.files[0];
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
function processPost(error) {
|
|
|
|
// File upload error handling
|
|
|
|
if (error) {
|
|
|
|
renderFormError(ev, fileInput, error);
|
2016-08-05 23:00:29 +08:00
|
|
|
noErrors = false;
|
2016-08-09 20:39:24 +08:00
|
|
|
animateSpinner(null, false);
|
|
|
|
return;
|
2016-08-05 23:00:29 +08:00
|
|
|
}
|
2016-07-18 19:16:41 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
var postData = posts[postIndex];
|
|
|
|
if (!postData) {
|
|
|
|
if (fileIndex === datas.length-1 && noErrors) {
|
|
|
|
// After successful file processing and uploading
|
|
|
|
$.each(datas, function(fileIndex, data) {
|
|
|
|
// Use file input to pass file info on submit
|
|
|
|
var fileInput = fileInputs.get(fileIndex);
|
|
|
|
cb(fileInput, data.asset_id);
|
|
|
|
});
|
2016-08-05 23:00:29 +08:00
|
|
|
animateSpinner(null, false);
|
2016-08-09 20:39:24 +08:00
|
|
|
$(ev.target.form).submit();
|
2016-07-18 19:16:41 +08:00
|
|
|
}
|
2016-08-09 20:39:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
postData.fileName = file.name;
|
|
|
|
postIndex++;
|
|
|
|
|
|
|
|
if (postData.style_option) {
|
|
|
|
// Picture file
|
|
|
|
var styleSize = parseStyleOption(postData.style_option);
|
|
|
|
generateThumbnail(file, postData.mime_type, styleSize.width,
|
|
|
|
styleSize.height, function (blob) {
|
|
|
|
|
|
|
|
postData.file = blob;
|
|
|
|
uploadFile(postData, processPost);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
// Other file
|
|
|
|
postData.file = file;
|
|
|
|
uploadFile(postData, processPost);
|
|
|
|
}
|
|
|
|
}
|
2016-07-18 19:16:41 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
var posts = data.posts;
|
|
|
|
var postIndex = 0;
|
|
|
|
processPost();
|
|
|
|
});
|
|
|
|
}
|
2016-07-18 19:16:41 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
// Validates files on server and uploads them to S3 server
|
|
|
|
//
|
|
|
|
// First we validate files on server and generate post requests (fetchUploadSignature),
|
|
|
|
// if OK the post requests are used to uplaod files asyncronously to S3 (uploadFiles),
|
|
|
|
// and if successful the form is submitted, otherwise no file is saved
|
|
|
|
exports.directUpload = function (ev, fileInputs, signUrl, cb) {
|
|
|
|
var noErrors = true;
|
|
|
|
var inputsPosts = []
|
|
|
|
|
|
|
|
function processFile(fileIndex) {
|
|
|
|
var fileInput = fileInputs.get(fileIndex);
|
|
|
|
if (!fileInput || !fileInput.files[0]) {
|
|
|
|
// After file processing
|
|
|
|
if(fileIndex !== 0) {
|
|
|
|
if(noErrors) {
|
|
|
|
uploadFiles(ev, fileInputs, inputsPosts, cb);
|
|
|
|
} else {
|
|
|
|
animateSpinner(null, false);
|
2016-07-18 19:16:41 +08:00
|
|
|
}
|
|
|
|
}
|
2016-08-09 20:39:24 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fileIndex === 0) {
|
|
|
|
// Before file processing and uploading
|
|
|
|
animateSpinner(null, true, undefined, I18n.t("general.file.uploading"));
|
|
|
|
ev.preventDefault();
|
|
|
|
ev.stopPropagation();
|
|
|
|
}
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
var file = fileInput.files[0];
|
|
|
|
fetchUploadSignature(file, signUrl, function (data) {
|
2016-08-05 23:00:29 +08:00
|
|
|
// File signature error handling
|
|
|
|
if (_.isUndefined(data)) {
|
2016-08-09 20:39:24 +08:00
|
|
|
renderFormError(ev, fileInput, I18n.t("general.file.upload_failure"));
|
|
|
|
noErrors = false;
|
2016-08-05 23:00:29 +08:00
|
|
|
}
|
2016-08-09 20:39:24 +08:00
|
|
|
else if (data.status === "error") {
|
|
|
|
renderFormError(ev, fileInput, jsonToValuesArray(data.errors));
|
|
|
|
noErrors = false;
|
|
|
|
} else {
|
|
|
|
inputsPosts.push(data);
|
2016-02-12 23:52:43 +08:00
|
|
|
}
|
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
processFile(fileIndex+1);
|
2016-07-18 19:16:41 +08:00
|
|
|
});
|
|
|
|
}
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2016-08-09 20:39:24 +08:00
|
|
|
processFile(0);
|
2016-07-21 19:11:15 +08:00
|
|
|
};
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
}(this));
|