2015-04-07 15:17:47 +08:00
|
|
|
// Backbone.Model File Upload v1.0.0
|
2014-03-18 19:16:27 +08:00
|
|
|
// by Joe Vu - joe.vu@homeslicesolutions.com
|
|
|
|
// For all details and documentation:
|
|
|
|
// https://github.com/homeslicesolutions/backbone-model-file-upload
|
2014-08-07 08:46:18 +08:00
|
|
|
// Contributors:
|
|
|
|
// lutherism - Alex Jansen - alex.openrobot.net
|
|
|
|
// bildja - Dima Bildin - github.com/bildja
|
2014-09-11 18:21:24 +08:00
|
|
|
// Minjung - Alejandro - github.com/Minjung
|
2015-04-07 15:17:47 +08:00
|
|
|
// XemsDoom - Luca Moser - https://github.com/XemsDoom
|
|
|
|
// DanilloCorvalan - Danillo Corvalan - https://github.com/DanilloCorvalan
|
2014-03-18 19:16:27 +08:00
|
|
|
|
2014-12-17 19:56:31 +08:00
|
|
|
(function(root, factory) {
|
|
|
|
|
|
|
|
// AMD
|
2014-09-11 18:21:24 +08:00
|
|
|
if (typeof define === 'function' && define.amd) {
|
2015-10-12 21:29:46 +08:00
|
|
|
define(['underscore', 'backbone'], function(_, Backbone){
|
|
|
|
factory(root, Backbone, _);
|
2014-12-17 19:56:31 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// NodeJS/CommonJS
|
|
|
|
} else if (typeof exports !== 'undefined') {
|
2015-10-12 21:29:46 +08:00
|
|
|
var _ = require('underscore'), Backbone = require('backbone');
|
|
|
|
factory(root, Backbone, _);
|
2014-12-17 19:56:31 +08:00
|
|
|
|
|
|
|
// Browser global
|
2014-09-11 18:21:24 +08:00
|
|
|
} else {
|
2015-10-12 21:29:46 +08:00
|
|
|
factory(root, root.Backbone, root._);
|
2014-09-11 18:21:24 +08:00
|
|
|
}
|
2014-03-18 19:16:27 +08:00
|
|
|
|
2015-10-12 21:29:46 +08:00
|
|
|
}(this, function(root, Backbone, _) {
|
2014-12-17 19:56:31 +08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
// Clone the original Backbone.Model.prototype as superClass
|
|
|
|
var _superClass = _.clone( Backbone.Model.prototype );
|
2014-03-18 19:16:27 +08:00
|
|
|
|
|
|
|
// Extending out
|
2014-12-17 19:56:31 +08:00
|
|
|
var BackboneModelFileUpload = Backbone.Model.extend({
|
2014-03-18 19:16:27 +08:00
|
|
|
|
2014-03-18 20:06:20 +08:00
|
|
|
// ! Default file attribute - can be overwritten
|
2014-03-18 19:16:27 +08:00
|
|
|
fileAttribute: 'file',
|
|
|
|
|
|
|
|
// @ Save - overwritten
|
|
|
|
save: function(key, val, options) {
|
|
|
|
|
|
|
|
// Variables
|
2016-01-20 03:36:07 +08:00
|
|
|
var attrs, attributes = this.attributes,
|
|
|
|
that = this;
|
2014-03-18 19:16:27 +08:00
|
|
|
|
2014-12-17 19:56:31 +08:00
|
|
|
// Signature parsing - taken directly from original Backbone.Model.save
|
2014-08-31 03:08:56 +08:00
|
|
|
// and it states: 'Handle both "key", value and {key: value} -style arguments.'
|
|
|
|
if (key == null || typeof key === 'object') {
|
|
|
|
attrs = key;
|
|
|
|
options = val;
|
2014-03-18 19:16:27 +08:00
|
|
|
} else {
|
|
|
|
(attrs = {})[key] = val;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Validate & wait options - taken directly from original Backbone.Model.save
|
|
|
|
options = _.extend({validate: true}, options);
|
|
|
|
if (attrs && !options.wait) {
|
|
|
|
if (!this.set(attrs, options)) return false;
|
|
|
|
} else {
|
|
|
|
if (!this._validate(attrs, options)) return false;
|
|
|
|
}
|
2014-08-31 03:08:56 +08:00
|
|
|
|
|
|
|
// Merge data temporarily for formdata
|
|
|
|
var mergedAttrs = _.extend({}, attributes, attrs);
|
|
|
|
|
2014-03-18 19:16:27 +08:00
|
|
|
if (attrs && options.wait) {
|
2014-08-31 03:08:56 +08:00
|
|
|
this.attributes = mergedAttrs;
|
2014-03-18 19:16:27 +08:00
|
|
|
}
|
|
|
|
|
2014-03-19 04:21:16 +08:00
|
|
|
// Check for "formData" flag and check for if file exist.
|
2014-12-17 19:56:31 +08:00
|
|
|
if ( options.formData === true
|
|
|
|
|| options.formData !== false
|
|
|
|
&& mergedAttrs[ this.fileAttribute ]
|
|
|
|
&& mergedAttrs[ this.fileAttribute ] instanceof File
|
|
|
|
|| mergedAttrs[ this.fileAttribute ] instanceof FileList
|
|
|
|
|| mergedAttrs[ this.fileAttribute ] instanceof Blob ) {
|
|
|
|
|
2014-03-19 00:17:02 +08:00
|
|
|
// Flatten Attributes reapplying File Object
|
2014-08-31 03:08:56 +08:00
|
|
|
var formAttrs = _.clone( mergedAttrs ),
|
2014-12-17 19:56:31 +08:00
|
|
|
fileAttr = mergedAttrs[ this.fileAttribute ];
|
2014-03-19 00:17:02 +08:00
|
|
|
formAttrs = this._flatten( formAttrs );
|
|
|
|
formAttrs[ this.fileAttribute ] = fileAttr;
|
|
|
|
|
2014-03-18 19:16:27 +08:00
|
|
|
// Converting Attributes to Form Data
|
|
|
|
var formData = new FormData();
|
2014-03-19 00:17:02 +08:00
|
|
|
_.each( formAttrs, function( value, key ){
|
2016-01-20 03:36:07 +08:00
|
|
|
if (value instanceof FileList || (key === that.fileAttribute && value instanceof Array)) {
|
2016-01-16 02:40:25 +08:00
|
|
|
_.each(value, function(file) {
|
|
|
|
formData.append( key, file );
|
|
|
|
});
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
formData.append( key, value );
|
|
|
|
}
|
2014-03-18 19:16:27 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
// Set options for AJAX call
|
|
|
|
options.data = formData;
|
|
|
|
options.processData = false;
|
|
|
|
options.contentType = false;
|
|
|
|
|
2015-10-12 21:29:46 +08:00
|
|
|
// Handle "progress" events
|
2016-01-16 02:40:25 +08:00
|
|
|
if (!options.xhr) {
|
|
|
|
options.xhr = function(){
|
|
|
|
var xhr = Backbone.$.ajaxSettings.xhr();
|
2016-01-20 03:36:07 +08:00
|
|
|
xhr.upload.addEventListener('progress', _.bind(that._progressHandler, that), false);
|
2016-01-16 02:40:25 +08:00
|
|
|
return xhr
|
2015-10-12 21:29:46 +08:00
|
|
|
}
|
2014-12-17 19:56:31 +08:00
|
|
|
}
|
2014-03-18 19:16:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Resume back to original state
|
|
|
|
if (attrs && options.wait) this.attributes = attributes;
|
|
|
|
|
|
|
|
// Continue to call the existing "save" method
|
2014-12-17 19:56:31 +08:00
|
|
|
return _superClass.save.call(this, attrs, options);
|
|
|
|
|
2014-03-18 19:16:27 +08:00
|
|
|
},
|
|
|
|
|
2014-03-19 00:17:02 +08:00
|
|
|
// _ FlattenObject gist by "penguinboy". Thank You!
|
|
|
|
// https://gist.github.com/penguinboy/762197
|
2015-04-07 15:17:47 +08:00
|
|
|
// NOTE for those who use "<1.0.0". The notation changed to nested brackets
|
|
|
|
_flatten: function flatten( obj ) {
|
2014-03-19 00:17:02 +08:00
|
|
|
var output = {};
|
|
|
|
for (var i in obj) {
|
|
|
|
if (!obj.hasOwnProperty(i)) continue;
|
|
|
|
if (typeof obj[i] == 'object') {
|
2015-04-07 15:17:47 +08:00
|
|
|
var flatObject = flatten(obj[i]);
|
2014-03-19 00:17:02 +08:00
|
|
|
for (var x in flatObject) {
|
|
|
|
if (!flatObject.hasOwnProperty(x)) continue;
|
2015-04-07 15:17:47 +08:00
|
|
|
output[i + '[' + x + ']'] = flatObject[x];
|
2014-03-19 00:17:02 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
output[i] = obj[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
|
|
|
|
},
|
2014-12-17 19:56:31 +08:00
|
|
|
|
2015-04-07 15:17:47 +08:00
|
|
|
// An "Unflatten" tool which is something normally should be on the backend
|
|
|
|
// But this is a guide to how you would unflatten the object
|
|
|
|
_unflatten: function unflatten(obj, output) {
|
|
|
|
var re = /^([^\[\]]+)\[(.+)\]$/g;
|
|
|
|
output = output || {};
|
|
|
|
for (var key in obj) {
|
|
|
|
var value = obj[key];
|
|
|
|
if (!key.toString().match(re)) {
|
|
|
|
var tempOut = {};
|
|
|
|
tempOut[key] = value;
|
|
|
|
_.extend(output, tempOut);
|
|
|
|
} else {
|
|
|
|
var keys = _.compact(key.split(re)), tempOut = {};
|
|
|
|
tempOut[keys[1]] = value;
|
|
|
|
output[keys[0]] = unflatten( tempOut, output[keys[0]] )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return output;
|
|
|
|
},
|
|
|
|
|
2014-03-18 19:16:27 +08:00
|
|
|
// _ Get the Progress of the uploading file
|
|
|
|
_progressHandler: function( event ) {
|
|
|
|
if (event.lengthComputable) {
|
|
|
|
var percentComplete = event.loaded / event.total;
|
|
|
|
this.trigger( 'progress', percentComplete );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-12-17 19:56:31 +08:00
|
|
|
// Export out to override Backbone Model
|
|
|
|
Backbone.Model = BackboneModelFileUpload;
|
|
|
|
|
2014-07-26 23:40:32 +08:00
|
|
|
}));
|