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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/Rakefile +6 -0
- data/app/assets/javascripts/dropzone.js +1769 -0
- data/app/assets/javascripts/voltron-upload.js +264 -0
- data/app/assets/stylesheets/dropzone.scss +595 -0
- data/app/assets/stylesheets/voltron-upload.scss +446 -0
- data/app/views/voltron/upload/preview/_horizontal_tile.html.erb +18 -0
- data/app/views/voltron/upload/preview/_progress.html.erb +8 -0
- data/app/views/voltron/upload/preview/_vertical_tile.html.erb +18 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/generators/voltron/upload/install/assets_generator.rb +25 -0
- data/lib/generators/voltron/upload/install/views_generator.rb +21 -0
- data/lib/generators/voltron/upload/install_generator.rb +57 -0
- data/lib/voltron/config/upload.rb +18 -0
- data/lib/voltron/upload/action_dispatch/routes.rb +17 -0
- data/lib/voltron/upload/action_view/field.rb +152 -0
- data/lib/voltron/upload/active_record/base.rb +87 -0
- data/lib/voltron/upload/carrierwave/uploader/base.rb +66 -0
- data/lib/voltron/upload/engine.rb +20 -0
- data/lib/voltron/upload/error.rb +20 -0
- data/lib/voltron/upload/version.rb +5 -0
- data/lib/voltron/upload.rb +51 -0
- data/lib/voltron/uploader.rb +58 -0
- data/voltron-upload.gemspec +34 -0
- metadata +243 -0
@@ -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);
|