fine_uploader 2.1.1 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # FineUploader
1
+ # Fine Uploader
2
2
 
3
- TODO: Write a gem description
3
+ Fine Uploader is a project attempts to achieve a user-friendly file-uploading experience over the web. It's built as a Javascript plugin for developers looking to incorporate file-uploading into their website.
4
4
 
5
5
  ## Installation
6
6
 
@@ -18,7 +18,13 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- TODO: Write usage instructions here
21
+ Add `application.js`
22
+
23
+ //= require fine_uploader
24
+
25
+ and `application.css`
26
+
27
+ /*= require fine_uploader
22
28
 
23
29
  ## Contributing
24
30
 
data/Vendorfile CHANGED
@@ -1,9 +1,21 @@
1
- from 'git://github.com/valums/file-uploader.git', branch: '2.1.1' do
2
- file 'vendor/assets/images/loading.gif', 'client/loading.gif'
3
- file 'vendor/assets/javascripts/fine_uploader.js', 'client/fileuploader.js'
4
- file 'vendor/assets/stylesheets/fine_uploader.css.scss', 'client/fileuploader.css' do |path|
1
+ from 'git://github.com/valums/file-uploader.git', ref: 'be738538a57633e355529ec13c63a7b2493b7ec8' do
2
+ file 'vendor/assets/images/fine_uploader/loading.gif', 'client/loading.gif'
3
+ file 'vendor/assets/images/fine_uploader/processing.gif', 'client/processing.gif'
4
+ file 'vendor/assets/javascripts/fine_uploader/header.js', 'client/js/header.js'
5
+ file 'vendor/assets/javascripts/fine_uploader/util.js', 'client/js/util.js'
6
+ file 'vendor/assets/javascripts/fine_uploader/button.js', 'client/js/button.js'
7
+ file 'vendor/assets/javascripts/fine_uploader/handler.base.js', 'client/js/handler.base.js'
8
+ file 'vendor/assets/javascripts/fine_uploader/handler.form.js', 'client/js/handler.form.js'
9
+ file 'vendor/assets/javascripts/fine_uploader/handler.xhr.js', 'client/js/handler.xhr.js'
10
+ file 'vendor/assets/javascripts/fine_uploader/uploader.basic.js', 'client/js/uploader.basic.js'
11
+ file 'vendor/assets/javascripts/fine_uploader/dnd.js', 'client/js/dnd.js'
12
+ file 'vendor/assets/javascripts/fine_uploader/uploader.js', 'client/js/uploader.js'
13
+ file 'vendor/assets/javascripts/fine_uploader/jquery-plugin.js', 'client/js/jquery-plugin.js'
14
+ file 'vendor/assets/stylesheets/fine_uploader.css.scss', 'client/fineuploader.css' do |path|
5
15
  rewrite(path) do |content|
6
- content.gsub('url', 'image-url')
16
+ content.gsub!('url', 'image-url')
17
+ content.gsub!('loading.gif', 'fine_uploader/loading.gif')
18
+ content.gsub!('processing.gif', 'fine_uploader/processing.gif')
7
19
  end
8
20
  end
9
21
  end
@@ -1,3 +1,3 @@
1
1
  module FineUploader
2
- VERSION = "2.1.1"
2
+ VERSION = "3.1.1"
3
3
  end
@@ -0,0 +1,102 @@
1
+ qq.UploadButton = function(o){
2
+ this._options = {
3
+ element: null,
4
+ // if set to true adds multiple attribute to file input
5
+ multiple: false,
6
+ acceptFiles: null,
7
+ // name attribute of file input
8
+ name: 'file',
9
+ onChange: function(input){},
10
+ hoverClass: 'qq-upload-button-hover',
11
+ focusClass: 'qq-upload-button-focus'
12
+ };
13
+
14
+ qq.extend(this._options, o);
15
+ this._disposeSupport = new qq.DisposeSupport();
16
+
17
+ this._element = this._options.element;
18
+
19
+ // make button suitable container for input
20
+ qq(this._element).css({
21
+ position: 'relative',
22
+ overflow: 'hidden',
23
+ // Make sure browse button is in the right side
24
+ // in Internet Explorer
25
+ direction: 'ltr'
26
+ });
27
+
28
+ this._input = this._createInput();
29
+ };
30
+
31
+ qq.UploadButton.prototype = {
32
+ /* returns file input element */
33
+ getInput: function(){
34
+ return this._input;
35
+ },
36
+ /* cleans/recreates the file input */
37
+ reset: function(){
38
+ if (this._input.parentNode){
39
+ qq(this._input).remove();
40
+ }
41
+
42
+ qq(this._element).removeClass(this._options.focusClass);
43
+ this._input = this._createInput();
44
+ },
45
+ _createInput: function(){
46
+ var input = document.createElement("input");
47
+
48
+ if (this._options.multiple){
49
+ input.setAttribute("multiple", "multiple");
50
+ }
51
+
52
+ if (this._options.acceptFiles) input.setAttribute("accept", this._options.acceptFiles);
53
+
54
+ input.setAttribute("type", "file");
55
+ input.setAttribute("name", this._options.name);
56
+
57
+ qq(input).css({
58
+ position: 'absolute',
59
+ // in Opera only 'browse' button
60
+ // is clickable and it is located at
61
+ // the right side of the input
62
+ right: 0,
63
+ top: 0,
64
+ fontFamily: 'Arial',
65
+ // 4 persons reported this, the max values that worked for them were 243, 236, 236, 118
66
+ fontSize: '118px',
67
+ margin: 0,
68
+ padding: 0,
69
+ cursor: 'pointer',
70
+ opacity: 0
71
+ });
72
+
73
+ this._element.appendChild(input);
74
+
75
+ var self = this;
76
+ this._disposeSupport.attach(input, 'change', function(){
77
+ self._options.onChange(input);
78
+ });
79
+
80
+ this._disposeSupport.attach(input, 'mouseover', function(){
81
+ qq(self._element).addClass(self._options.hoverClass);
82
+ });
83
+ this._disposeSupport.attach(input, 'mouseout', function(){
84
+ qq(self._element).removeClass(self._options.hoverClass);
85
+ });
86
+ this._disposeSupport.attach(input, 'focus', function(){
87
+ qq(self._element).addClass(self._options.focusClass);
88
+ });
89
+ this._disposeSupport.attach(input, 'blur', function(){
90
+ qq(self._element).removeClass(self._options.focusClass);
91
+ });
92
+
93
+ // IE and Opera, unfortunately have 2 tab stops on file input
94
+ // which is unacceptable in our case, disable keyboard access
95
+ if (window.attachEvent){
96
+ // it is IE or Opera
97
+ input.setAttribute('tabIndex', "-1");
98
+ }
99
+
100
+ return input;
101
+ }
102
+ };
@@ -0,0 +1,355 @@
1
+ /*globals qq, document*/
2
+ qq.DragAndDrop = function(o) {
3
+ "use strict";
4
+
5
+ var options, dz, dirPending,
6
+ droppedFiles = [],
7
+ droppedEntriesCount = 0,
8
+ droppedEntriesParsedCount = 0,
9
+ disposeSupport = new qq.DisposeSupport();
10
+
11
+ options = {
12
+ dropArea: null,
13
+ extraDropzones: [],
14
+ hideDropzones: true,
15
+ multiple: true,
16
+ classes: {
17
+ dropActive: null
18
+ },
19
+ callbacks: {
20
+ dropProcessing: function(isProcessing, files) {},
21
+ error: function(code, filename) {},
22
+ log: function(message, level) {}
23
+ }
24
+ };
25
+
26
+ qq.extend(options, o);
27
+
28
+ function maybeUploadDroppedFiles() {
29
+ if (droppedEntriesCount === droppedEntriesParsedCount && !dirPending) {
30
+ options.callbacks.log('Grabbed ' + droppedFiles.length + " files after tree traversal.");
31
+ dz.dropDisabled(false);
32
+ options.callbacks.dropProcessing(false, droppedFiles);
33
+ }
34
+ }
35
+ function addDroppedFile(file) {
36
+ droppedFiles.push(file);
37
+ droppedEntriesParsedCount+=1;
38
+ maybeUploadDroppedFiles();
39
+ }
40
+
41
+ function traverseFileTree(entry) {
42
+ var dirReader, i;
43
+
44
+ droppedEntriesCount+=1;
45
+
46
+ if (entry.isFile) {
47
+ entry.file(function(file) {
48
+ addDroppedFile(file);
49
+ });
50
+ }
51
+ else if (entry.isDirectory) {
52
+ dirPending = true;
53
+ dirReader = entry.createReader();
54
+ dirReader.readEntries(function(entries) {
55
+ droppedEntriesParsedCount+=1;
56
+ for (i = 0; i < entries.length; i+=1) {
57
+ traverseFileTree(entries[i]);
58
+ }
59
+
60
+ dirPending = false;
61
+
62
+ if (!entries.length) {
63
+ maybeUploadDroppedFiles();
64
+ }
65
+ });
66
+ }
67
+ }
68
+
69
+ function handleDataTransfer(dataTransfer) {
70
+ var i, items, entry;
71
+
72
+ options.callbacks.dropProcessing(true);
73
+ dz.dropDisabled(true);
74
+
75
+ if (dataTransfer.files.length > 1 && !options.multiple) {
76
+ options.callbacks.error('tooManyFilesError', "");
77
+ }
78
+ else {
79
+ droppedFiles = [];
80
+ droppedEntriesCount = 0;
81
+ droppedEntriesParsedCount = 0;
82
+
83
+ if (qq.isFolderDropSupported(dataTransfer)) {
84
+ items = dataTransfer.items;
85
+
86
+ for (i = 0; i < items.length; i+=1) {
87
+ entry = items[i].webkitGetAsEntry();
88
+ if (entry) {
89
+ //due to a bug in Chrome's File System API impl - #149735
90
+ if (entry.isFile) {
91
+ droppedFiles.push(items[i].getAsFile());
92
+ if (i === items.length-1) {
93
+ maybeUploadDroppedFiles();
94
+ }
95
+ }
96
+
97
+ else {
98
+ traverseFileTree(entry);
99
+ }
100
+ }
101
+ }
102
+ }
103
+ else {
104
+ options.callbacks.dropProcessing(false, dataTransfer.files);
105
+ dz.dropDisabled(false);
106
+ }
107
+ }
108
+ }
109
+
110
+ function setupDropzone(dropArea){
111
+ dz = new qq.UploadDropZone({
112
+ element: dropArea,
113
+ onEnter: function(e){
114
+ qq(dropArea).addClass(options.classes.dropActive);
115
+ e.stopPropagation();
116
+ },
117
+ onLeaveNotDescendants: function(e){
118
+ qq(dropArea).removeClass(options.classes.dropActive);
119
+ },
120
+ onDrop: function(e){
121
+ if (options.hideDropzones) {
122
+ qq(dropArea).hide();
123
+ }
124
+ qq(dropArea).removeClass(options.classes.dropActive);
125
+
126
+ handleDataTransfer(e.dataTransfer);
127
+ }
128
+ });
129
+
130
+ disposeSupport.addDisposer(function() {
131
+ dz.dispose();
132
+ });
133
+
134
+ if (options.hideDropzones) {
135
+ qq(dropArea).hide();
136
+ }
137
+ }
138
+
139
+ function isFileDrag(dragEvent) {
140
+ var fileDrag;
141
+
142
+ qq.each(dragEvent.dataTransfer.types, function(key, val) {
143
+ if (val === 'Files') {
144
+ fileDrag = true;
145
+ return false;
146
+ }
147
+ });
148
+
149
+ return fileDrag;
150
+ }
151
+
152
+ function setupDragDrop(){
153
+ if (options.dropArea) {
154
+ options.extraDropzones.push(options.dropArea);
155
+ }
156
+
157
+ var i, dropzones = options.extraDropzones;
158
+
159
+ for (i=0; i < dropzones.length; i+=1){
160
+ setupDropzone(dropzones[i]);
161
+ }
162
+
163
+ // IE <= 9 does not support the File API used for drag+drop uploads
164
+ if (options.dropArea && (!qq.ie() || qq.ie10())) {
165
+ disposeSupport.attach(document, 'dragenter', function(e) {
166
+ if (!dz.dropDisabled() && isFileDrag(e)) {
167
+ if (qq(options.dropArea).hasClass(options.classes.dropDisabled)) {
168
+ return;
169
+ }
170
+
171
+ options.dropArea.style.display = 'block';
172
+ for (i=0; i < dropzones.length; i+=1) {
173
+ dropzones[i].style.display = 'block';
174
+ }
175
+ }
176
+ });
177
+ }
178
+ disposeSupport.attach(document, 'dragleave', function(e){
179
+ if (options.hideDropzones && qq.FineUploader.prototype._leaving_document_out(e)) {
180
+ for (i=0; i < dropzones.length; i+=1) {
181
+ qq(dropzones[i]).hide();
182
+ }
183
+ }
184
+ });
185
+ disposeSupport.attach(document, 'drop', function(e){
186
+ if (options.hideDropzones) {
187
+ for (i=0; i < dropzones.length; i+=1) {
188
+ qq(dropzones[i]).hide();
189
+ }
190
+ }
191
+ e.preventDefault();
192
+ });
193
+ }
194
+
195
+ return {
196
+ setup: function() {
197
+ setupDragDrop();
198
+ },
199
+
200
+ setupExtraDropzone: function(element) {
201
+ options.extraDropzones.push(element);
202
+ setupDropzone(element);
203
+ },
204
+
205
+ removeExtraDropzone: function(element) {
206
+ var i, dzs = options.extraDropzones;
207
+ for(i in dzs) {
208
+ if (dzs[i] === element) {
209
+ return dzs.splice(i, 1);
210
+ }
211
+ }
212
+ },
213
+
214
+ dispose: function() {
215
+ disposeSupport.dispose();
216
+ dz.dispose();
217
+ }
218
+ };
219
+ };
220
+
221
+
222
+ qq.UploadDropZone = function(o){
223
+ "use strict";
224
+
225
+ var options, element, preventDrop, dropOutsideDisabled, disposeSupport = new qq.DisposeSupport();
226
+
227
+ options = {
228
+ element: null,
229
+ onEnter: function(e){},
230
+ onLeave: function(e){},
231
+ // is not fired when leaving element by hovering descendants
232
+ onLeaveNotDescendants: function(e){},
233
+ onDrop: function(e){}
234
+ };
235
+
236
+ qq.extend(options, o);
237
+ element = options.element;
238
+
239
+ function dragover_should_be_canceled(){
240
+ return qq.safari() || (qq.firefox() && qq.windows());
241
+ }
242
+
243
+ function disableDropOutside(e){
244
+ // run only once for all instances
245
+ if (!dropOutsideDisabled ){
246
+
247
+ // for these cases we need to catch onDrop to reset dropArea
248
+ if (dragover_should_be_canceled){
249
+ disposeSupport.attach(document, 'dragover', function(e){
250
+ e.preventDefault();
251
+ });
252
+ } else {
253
+ disposeSupport.attach(document, 'dragover', function(e){
254
+ if (e.dataTransfer){
255
+ e.dataTransfer.dropEffect = 'none';
256
+ e.preventDefault();
257
+ }
258
+ });
259
+ }
260
+
261
+ dropOutsideDisabled = true;
262
+ }
263
+ }
264
+
265
+ function isValidFileDrag(e){
266
+ // e.dataTransfer currently causing IE errors
267
+ // IE9 does NOT support file API, so drag-and-drop is not possible
268
+ if (qq.ie() && !qq.ie10()) {
269
+ return false;
270
+ }
271
+
272
+ var effectTest, dt = e.dataTransfer,
273
+ // do not check dt.types.contains in webkit, because it crashes safari 4
274
+ isSafari = qq.safari();
275
+
276
+ // dt.effectAllowed is none in Safari 5
277
+ // dt.types.contains check is for firefox
278
+ effectTest = qq.ie10() ? true : dt.effectAllowed !== 'none';
279
+ return dt && effectTest && (dt.files || (!isSafari && dt.types.contains && dt.types.contains('Files')));
280
+ }
281
+
282
+ function isOrSetDropDisabled(isDisabled) {
283
+ if (isDisabled !== undefined) {
284
+ preventDrop = isDisabled;
285
+ }
286
+ return preventDrop;
287
+ }
288
+
289
+ function attachEvents(){
290
+ disposeSupport.attach(element, 'dragover', function(e){
291
+ if (!isValidFileDrag(e)) {
292
+ return;
293
+ }
294
+
295
+ var effect = qq.ie() ? null : e.dataTransfer.effectAllowed;
296
+ if (effect === 'move' || effect === 'linkMove'){
297
+ e.dataTransfer.dropEffect = 'move'; // for FF (only move allowed)
298
+ } else {
299
+ e.dataTransfer.dropEffect = 'copy'; // for Chrome
300
+ }
301
+
302
+ e.stopPropagation();
303
+ e.preventDefault();
304
+ });
305
+
306
+ disposeSupport.attach(element, 'dragenter', function(e){
307
+ if (!isOrSetDropDisabled()) {
308
+ if (!isValidFileDrag(e)) {
309
+ return;
310
+ }
311
+ options.onEnter(e);
312
+ }
313
+ });
314
+
315
+ disposeSupport.attach(element, 'dragleave', function(e){
316
+ if (!isValidFileDrag(e)) {
317
+ return;
318
+ }
319
+
320
+ options.onLeave(e);
321
+
322
+ var relatedTarget = document.elementFromPoint(e.clientX, e.clientY);
323
+ // do not fire when moving a mouse over a descendant
324
+ if (qq(this).contains(relatedTarget)) {
325
+ return;
326
+ }
327
+
328
+ options.onLeaveNotDescendants(e);
329
+ });
330
+
331
+ disposeSupport.attach(element, 'drop', function(e){
332
+ if (!isOrSetDropDisabled()) {
333
+ if (!isValidFileDrag(e)) {
334
+ return;
335
+ }
336
+
337
+ e.preventDefault();
338
+ options.onDrop(e);
339
+ }
340
+ });
341
+ }
342
+
343
+ disableDropOutside();
344
+ attachEvents();
345
+
346
+ return {
347
+ dropDisabled: function(isDisabled) {
348
+ return isOrSetDropDisabled(isDisabled);
349
+ },
350
+
351
+ dispose: function() {
352
+ disposeSupport.dispose();
353
+ }
354
+ };
355
+ };
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Class for uploading files, uploading itself is handled by child classes
3
+ */
4
+ qq.UploadHandlerAbstract = function(o){
5
+ // Default options, can be overridden by the user
6
+ this._options = {
7
+ debug: false,
8
+ endpoint: '/upload.php',
9
+ paramsInBody: false,
10
+ // maximum number of concurrent uploads
11
+ maxConnections: 999,
12
+ log: function(str, level) {},
13
+ onProgress: function(id, fileName, loaded, total){},
14
+ onComplete: function(id, fileName, response, xhr){},
15
+ onCancel: function(id, fileName){},
16
+ onUpload: function(id, fileName, xhr){},
17
+ onAutoRetry: function(id, fileName, response, xhr){}
18
+
19
+ };
20
+ qq.extend(this._options, o);
21
+
22
+ this._queue = [];
23
+
24
+ this.log = this._options.log;
25
+ };
26
+ qq.UploadHandlerAbstract.prototype = {
27
+ /**
28
+ * Adds file or file input to the queue
29
+ * @returns id
30
+ **/
31
+ add: function(file){},
32
+ /**
33
+ * Sends the file identified by id
34
+ */
35
+ upload: function(id){
36
+ var len = this._queue.push(id);
37
+
38
+ // if too many active uploads, wait...
39
+ if (len <= this._options.maxConnections){
40
+ this._upload(id);
41
+ }
42
+ },
43
+ retry: function(id) {
44
+ var i = qq.indexOf(this._queue, id);
45
+ if (i >= 0) {
46
+ this._upload(id);
47
+ }
48
+ else {
49
+ this.upload(id);
50
+ }
51
+ },
52
+ /**
53
+ * Cancels file upload by id
54
+ */
55
+ cancel: function(id){
56
+ this.log('Cancelling ' + id);
57
+ this._options.paramsStore.remove(id);
58
+ this._cancel(id);
59
+ this._dequeue(id);
60
+ },
61
+ /**
62
+ * Cancells all uploads
63
+ */
64
+ cancelAll: function(){
65
+ for (var i=0; i<this._queue.length; i++){
66
+ this._cancel(this._queue[i]);
67
+ }
68
+ this._queue = [];
69
+ },
70
+ /**
71
+ * Returns name of the file identified by id
72
+ */
73
+ getName: function(id){},
74
+ /**
75
+ * Returns size of the file identified by id
76
+ */
77
+ getSize: function(id){},
78
+ /**
79
+ * Returns id of files being uploaded or
80
+ * waiting for their turn
81
+ */
82
+ getQueue: function(){
83
+ return this._queue;
84
+ },
85
+ reset: function() {
86
+ this.log('Resetting upload handler');
87
+ this._queue = [];
88
+ },
89
+ /**
90
+ * Actual upload method
91
+ */
92
+ _upload: function(id){},
93
+ /**
94
+ * Actual cancel method
95
+ */
96
+ _cancel: function(id){},
97
+ /**
98
+ * Removes element from queue, starts upload of next
99
+ */
100
+ _dequeue: function(id){
101
+ var i = qq.indexOf(this._queue, id);
102
+ this._queue.splice(i, 1);
103
+
104
+ var max = this._options.maxConnections;
105
+
106
+ if (this._queue.length >= max && i < max){
107
+ var nextId = this._queue[max-1];
108
+ this._upload(nextId);
109
+ }
110
+ },
111
+ /**
112
+ * Determine if the file exists.
113
+ */
114
+ isValid: function(id) {}
115
+ };