voltron-upload 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,264 @@
1
+ //= require dropzone
2
+ //= require voltron
3
+
4
+ Dropzone.autoDiscover = false;
5
+
6
+ Voltron.addModule('Upload', function(){
7
+
8
+ var _dz = null,
9
+ _form = null;
10
+
11
+ var Upload = function(element){
12
+
13
+ return {
14
+ initialize: function(){
15
+ _form = this.getInput().closest('form');
16
+ this.createMarkup().createDropzone();
17
+ },
18
+
19
+ getInput: function(){
20
+ return $(element);
21
+ },
22
+
23
+ getForm: function(){
24
+ return _form;
25
+ },
26
+
27
+ isMultiple: function(){
28
+ return this.getInput().prop('multiple');
29
+ },
30
+
31
+ getModel: function(){
32
+ return this.getInput().attr('name').match(/^[A-Z_0-9]+/i)[0];
33
+ },
34
+
35
+ getMethod: function(){
36
+ return this.getInput().attr('name').match(/^[A-Z_0-9]+\[([A-Z_0-9]+)\]/i)[1];
37
+ },
38
+
39
+ getParamName: function(name){
40
+ return this.getModel() + '[' + name + '_' + this.getMethod() + ']' + (this.isMultiple() ? '[]' : '');
41
+ },
42
+
43
+ getDropzone: function(){
44
+ return _dz;
45
+ },
46
+
47
+ getRemovals: function(){
48
+ return this.getInput().data('upload-remove') || [];
49
+ },
50
+
51
+ getFiles: function(){
52
+ return this.getInput().data('upload-files') || [];
53
+ },
54
+
55
+ getOptions: function(){
56
+ return this.getInput().data('upload-options');
57
+ },
58
+
59
+ addHiddenInputs: function(){
60
+ var removes = this.getRemovals();
61
+
62
+ for(var i=0; i<removes.length; i++){
63
+ this.addRemoval(removes[i]);
64
+ }
65
+ },
66
+
67
+ addExistingFiles: function(){
68
+ var dz = this.getDropzone(),
69
+ files = this.getFiles(),
70
+ caches = this.getCacheIds();
71
+
72
+ // If set to preserve file uploads, iterate through each uploaded file associated with
73
+ // the model and add to the file upload box upon initialization
74
+ // If not set to preserve, the data-files attribute will always be an empty array
75
+ for(var i=0; i<files.length; i++){
76
+ Voltron('Upload/getFileObject', files[i], files[i].id, function(fileObject, id){
77
+ dz.files.push(fileObject);
78
+ dz.options.addedfile.call(dz, fileObject);
79
+ $(fileObject.previewElement).attr('data-id', id);
80
+ $(fileObject.previewElement).addClass('dz-success');
81
+ dz._enqueueThumbnail(fileObject);
82
+ dz.options.complete.call(dz, fileObject);
83
+ dz._updateMaxFilesReachedClass();
84
+ });
85
+ }
86
+
87
+ for(var i=0; i<caches.length; i++){
88
+ this.addCache(caches[i]);
89
+ }
90
+ },
91
+
92
+ addRemoval: function(id){
93
+ var cache = this.getCache();
94
+ var index = cache.indexOf(id);
95
+
96
+ // If the id of the file we want to remove is in the cache input array, remove it
97
+ if(index > -1){
98
+ cache.splice(index, 1);
99
+ if(this.isMultiple()){
100
+ this.getCacheInput().val(JSON.stringify(cache));
101
+ }else{
102
+ this.getCacheInput().val('');
103
+ }
104
+ }
105
+
106
+ // Add the removal input field with the given id
107
+ this.getForm().prepend($('<input />', { type: 'hidden', class: 'input-' + id.split('/')[0], name: this.getParamName('remove'), value: id }));
108
+ },
109
+
110
+ addCache: function(id){
111
+ if(!/^(-)?[\d]+\-[\d]+(\-[\d]{4})?\-[\d]{4}/.test(id)) return;
112
+
113
+ if(this.isMultiple()){
114
+ var cache = this.getCache();
115
+ cache.push(id);
116
+ this.getCacheInput().val(JSON.stringify(cache.uniq()));
117
+ }else{
118
+ this.getCacheInput().val(id);
119
+ }
120
+ },
121
+
122
+ getCache: function(){
123
+ try
124
+ {
125
+ if(this.isMultiple()){
126
+ return $.parseJSON(this.getCacheInput().val()) || [];
127
+ }else{
128
+ return [this.getCacheInput().val()];
129
+ }
130
+ }catch(e){
131
+ return [];
132
+ }
133
+ },
134
+
135
+ getCacheIds: function(){
136
+ return this.getInput().data('upload-cache');
137
+ },
138
+
139
+ getCacheInput: function(){
140
+ var paramName = [this.getModel(), this.getMethod(), 'cache'].join('_');
141
+
142
+ if(!this.getForm().find('#' + paramName).length){
143
+ this.getForm().prepend($('<input />', { type: 'hidden', name: this.getModel() + '[' + this.getMethod() + '_cache]', id: paramName, value: (this.isMultiple() ? '[]' : '') }));
144
+ }
145
+ return this.getForm().find('#' + paramName);
146
+ },
147
+
148
+ createMarkup: function(){
149
+ if(!this.getInput().closest('.fallback').length) this.getInput().wrap($('<div />', { class: 'fallback' }));
150
+ if(!this.getInput().closest('.dropzone').length) this.getInput().closest('.fallback').wrap($('<div />', { class: 'dropzone' }));
151
+ return this;
152
+ },
153
+
154
+ createDropzone: function(){
155
+ _dz = new Dropzone(this.getInput().closest('.dropzone').addClass(this.getInput().get(0).className).get(0), this.getOptions());
156
+
157
+ // If the dropzone became a fallback upload input, dz will be a DOM element, not an instance of Dropzone
158
+ if(_dz instanceof Dropzone){
159
+ // Add the dropzone object to the file input, so it's easily accessible
160
+ this.getInput().data('dropzone', _dz);
161
+ this.getInput().data('upload', this);
162
+
163
+ // Assign the hidden file input a unique id
164
+ // Not required outside the test environment, but may
165
+ // be useful for other reasons in case one needs to get at the hidden inputs
166
+ $(_dz.hiddenFileInput).attr('id', this.getInput().attr('id') + '_input');
167
+
168
+ this.addHiddenInputs();
169
+ this.addExistingFiles();
170
+
171
+ _dz.on('sending', $.proxy(this.onBeforeSend, this));
172
+ _dz.on('success', $.proxy(this.onSuccess, this));
173
+ _dz.on('removedfile', $.proxy(this.onRemove, this));
174
+ _dz.on('error', $.proxy(this.onError, this));
175
+ Voltron.dispatch('upload:initialized', { upload: this, dropzone: _dz, element: this.getInput().get(0) });
176
+ }
177
+ },
178
+
179
+ // Add the authenticity token to the request
180
+ onBeforeSend: function(file, xhr, data){
181
+ data.append('authenticity_token', Voltron.getAuthToken());
182
+
183
+ Voltron.dispatch('upload:sending', { upload: this, form: this.getForm().get(0), file: file, xhr: xhr, data: data, element: this.getInput().get(0) });
184
+
185
+ // If single file upload dropzone, remove anything that may have been previously uploaded,
186
+ // change any commit inputs to remove inputs, so the file will be deleted when submitted
187
+ if(!this.isMultiple()){
188
+ $(file.previewElement).closest('.dropzone').find('.dz-preview').each($.proxy(function(index, el){
189
+ var id = $(el).data('id');
190
+
191
+ if(id) this.addRemoval(id);
192
+ $(el).remove();
193
+ }, this));
194
+ }
195
+ },
196
+
197
+ // Once a file is uploaded, add a hidden input that flags the file to be committed once the form is submitted
198
+ onSuccess: function(file, data){
199
+ for(var i=0; i<data.uploads.length; i++){
200
+ $(file.previewElement).attr('data-id', data.uploads[i].id);
201
+ this.addCache(data.uploads[i].id);
202
+ }
203
+
204
+ // Dispatch upload complete event. This can be picked up by other modules to perform additional actions
205
+ // i.e. - The Voltron Crop module will observe this to set the crop image once uploaded
206
+ Voltron.dispatch('upload:complete', { upload: this, file: file, data: data, element: this.getInput().get(0) });
207
+ },
208
+
209
+ // When a file is removed, eliminate any hidden inputs that may have flagged the file for "committing"
210
+ // and add the hidden input that flags the file in question for removal
211
+ onRemove: function(file){
212
+ var f, id = $(file.previewElement).data('id');
213
+
214
+ // This line accounts for files that were uploaded, but removed before the form was submitted
215
+ // Once the form is submitted the file exists in the data-files attribute
216
+ this.addRemoval(id);
217
+
218
+ // Dispatch the upload removed event, can be observed in any other Voltron module
219
+ // by defining an onUploadRemoved event
220
+ Voltron.dispatch('upload:removed', { upload: this, file: file, element: this.getInput().get(0) });
221
+ },
222
+
223
+ onError: function(file, data){
224
+ $(file.previewElement).find('.dz-error-message').text(typeof data == 'string' ? data : data.error.join('<br />'));
225
+ Voltron.dispatch('upload:error', { upload: this, file: file, data: [data].flatten(), element: this.getInput().get(0) })
226
+ }
227
+ };
228
+ };
229
+
230
+ return {
231
+ initialize: function(){
232
+ $.each($('input[type="file"]:visible'), this.addUpload);
233
+ },
234
+
235
+ addUpload: function(){
236
+ var upload = new Upload(this);
237
+ upload.initialize();
238
+ },
239
+
240
+ getFileBlob: function(url, cb){
241
+ var xhr = new XMLHttpRequest();
242
+ xhr.open('GET', url);
243
+ xhr.responseType = 'blob';
244
+ xhr.addEventListener('load', function(){
245
+ cb(xhr.response);
246
+ });
247
+ xhr.send();
248
+ },
249
+
250
+ blobToFile: function(blob, name){
251
+ blob.lastModifiedDate = new Date();
252
+ blob.name = name;
253
+ blob.status = 'added';
254
+ blob.accepted = true;
255
+ return blob;
256
+ },
257
+
258
+ getFileObject: function(file, name, cb){
259
+ this.getFileBlob(file.url, function(blob){
260
+ cb(Voltron('Upload/blobToFile', blob, file.name), name);
261
+ });
262
+ }
263
+ };
264
+ }, true);