fine_uploader 2.1.1 → 3.1.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.
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
+ };