forked from homeslicesolutions-zz/backbone-model-file-upload
-
Notifications
You must be signed in to change notification settings - Fork 0
/
backbone-model-file-upload.js
176 lines (148 loc) · 5.58 KB
/
backbone-model-file-upload.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Backbone.Model File Upload v1.0.0
// by Joe Vu - [email protected]
// For all details and documentation:
// https://github.com/homeslicesolutions/backbone-model-file-upload
// Contributors:
// lutherism - Alex Jansen - alex.openrobot.net
// bildja - Dima Bildin - github.com/bildja
// Minjung - Alejandro - github.com/Minjung
// XemsDoom - Luca Moser - https://github.com/XemsDoom
// DanilloCorvalan - Danillo Corvalan - https://github.com/DanilloCorvalan
(function(root, factory) {
// AMD
if (typeof define === 'function' && define.amd) {
define(['underscore', 'backbone'], function(_, Backbone){
factory(root, Backbone, _);
});
// NodeJS/CommonJS
} else if (typeof exports !== 'undefined') {
var _ = require('underscore'), Backbone = require('backbone');
factory(root, Backbone, _);
// Browser global
} else {
factory(root, root.Backbone, root._);
}
}(this, function(root, Backbone, _) {
'use strict';
// Clone the original Backbone.Model.prototype as superClass
var _superClass = _.clone( Backbone.Model.prototype );
// Extending out
var BackboneModelFileUpload = Backbone.Model.extend({
// ! Default file attribute - can be overwritten
fileAttribute: 'file',
// @ Save - overwritten
save: function(key, val, options) {
// Variables
var attrs, attributes = this.attributes,
that = this;
// Signature parsing - taken directly from original Backbone.Model.save
// and it states: 'Handle both "key", value and {key: value} -style arguments.'
if (key == null || typeof key === 'object') {
attrs = key;
options = val;
} 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;
}
// Merge data temporarily for formdata
var mergedAttrs = _.extend({}, attributes, attrs);
if (attrs && options.wait) {
this.attributes = mergedAttrs;
}
// Check for "formData" flag and check for if file exist.
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 ) {
// Flatten Attributes reapplying File Object
var formAttrs = _.clone( mergedAttrs ),
fileAttr = mergedAttrs[ this.fileAttribute ];
formAttrs = this._flatten( formAttrs );
formAttrs[ this.fileAttribute ] = fileAttr;
// Converting Attributes to Form Data
var formData = new FormData();
_.each( formAttrs, function( value, key ){
if (value instanceof FileList || (key === that.fileAttribute && value instanceof Array)) {
_.each(value, function(file) {
formData.append( key, file );
});
}
else {
formData.append( key, value );
}
});
// Set options for AJAX call
options.data = formData;
options.processData = false;
options.contentType = false;
// Handle "progress" events
if (!options.xhr) {
options.xhr = function(){
var xhr = Backbone.$.ajaxSettings.xhr();
xhr.upload.addEventListener('progress', _.bind(that._progressHandler, that), false);
return xhr
}
}
}
// Resume back to original state
if (attrs && options.wait) this.attributes = attributes;
// Continue to call the existing "save" method
return _superClass.save.call(this, attrs, options);
},
// _ FlattenObject gist by "penguinboy". Thank You!
// https://gist.github.com/penguinboy/762197
// NOTE for those who use "<1.0.0". The notation changed to nested brackets
_flatten: function flatten( obj ) {
var output = {};
for (var i in obj) {
if (!obj.hasOwnProperty(i)) continue;
if (typeof obj[i] == 'object') {
var flatObject = flatten(obj[i]);
for (var x in flatObject) {
if (!flatObject.hasOwnProperty(x)) continue;
output[i + '[' + x + ']'] = flatObject[x];
}
} else {
output[i] = obj[i];
}
}
return output;
},
// 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;
},
// _ Get the Progress of the uploading file
_progressHandler: function( event ) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
this.trigger( 'progress', percentComplete );
}
}
});
// Export out to override Backbone Model
Backbone.Model = BackboneModelFileUpload;
}));