resumablejs-rails 0.1.1 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45861949a93bd466764fd5d5327c762db1252fe8
4
- data.tar.gz: 99224df36588d4d913c07a071c204cde7a87969c
3
+ metadata.gz: 193ac0f83f8157b10c100d48601be68b89c89769
4
+ data.tar.gz: b20ed9c5eb8af57b297b6aa904106e9f058a2cb3
5
5
  SHA512:
6
- metadata.gz: 6ed22f902dd6c25c5453f9e921910f298f61ad9b08eadd369c0dff18fd616bea1da8782189c78799f660faf08529aada5ba5ad9ccfb10836501a2c793ed1aab5
7
- data.tar.gz: 1a190b818a9a0d8df8a0f1c9f20d0a4e956d5b3b72f7ae5899938e0b91818abdf9bacba5fb0f60af7dc841646e28a14c270fd4bf7d984c636138a639e42b561e
6
+ metadata.gz: 25c53ae556106fecf94eccb1b5df1cf0c556df1c8e5c535b06446c51a69471be2b9623fc28b149ffcb49e4531669325c6e0b3dd4a9249f12058f9a61b581e811
7
+ data.tar.gz: c0b25f735c94f26c49396d560223a711b2324ee87750fd9fac2bc259c9e488da65fb31456f63744b19d2b38fd76333aad0fca334b0ee21913a7e5913f0fc6811
@@ -1,5 +1,5 @@
1
1
  module Resumablejs
2
2
  module Rails
3
- VERSION = "0.1.1"
3
+ VERSION = "1.0"
4
4
  end
5
5
  end
@@ -5,609 +5,696 @@
5
5
  * Steffen Tiedemann Christensen, steffen@23company.com
6
6
  */
7
7
 
8
- var Resumable = function(opts){
9
- if ( !(this instanceof Resumable ) ) {
10
- return new Resumable( opts );
11
- }
12
- // SUPPORTED BY BROWSER?
13
- // Check if these features are support by the browser:
14
- // - File object type
15
- // - Blob object type
16
- // - FileList object type
17
- // - slicing files
18
- this.support = (
19
- (typeof(File)!=='undefined')
20
- &&
21
- (typeof(Blob)!=='undefined')
22
- &&
23
- (typeof(FileList)!=='undefined')
24
- &&
25
- (!!Blob.prototype.webkitSlice||!!Blob.prototype.mozSlice||Blob.prototype.slice||false)
26
- );
27
- if(!this.support) return(false);
28
-
29
-
30
- // PROPERTIES
31
- var $ = this;
32
- $.files = [];
33
- $.defaults = {
34
- chunkSize:1*1024*1024,
35
- forceChunkSize:false,
36
- simultaneousUploads:3,
37
- fileParameterName:'file',
38
- throttleProgressCallbacks:0.5,
39
- query:{},
40
- headers:{},
41
- preprocess:null,
42
- method:'multipart',
43
- prioritizeFirstAndLastChunk:false,
44
- target:'/',
45
- testChunks:true,
46
- generateUniqueIdentifier:null,
47
- maxChunkRetries:undefined,
48
- chunkRetryInterval:undefined,
49
- permanentErrors:[415, 500, 501],
50
- maxFiles:undefined,
51
- maxFilesErrorCallback:function (files, errorCount) {
52
- var maxFiles = $.getOpt('maxFiles');
53
- alert('Please upload ' + maxFiles + ' file' + (maxFiles === 1 ? '' : 's') + ' at a time.');
54
- },
55
- minFileSize:undefined,
56
- minFileSizeErrorCallback:function(file, errorCount) {
57
- alert(file.fileName +' is too small, please upload files larger than ' + $h.formatSize($.getOpt('minFileSize')) + '.');
58
- },
59
- maxFileSize:undefined,
60
- maxFileSizeErrorCallback:function(file, errorCount) {
61
- alert(file.fileName +' is too large, please upload files less than ' + $h.formatSize($.getOpt('maxFileSize')) + '.');
62
- },
63
- fileType: [],
64
- fileTypeErrorCallback: function(file, errorCount) {
65
- alert(file.fileName +' has type not allowed, please upload files of type ' + $.getOpt('fileType') + '.');
66
- }
67
- };
68
- $.opts = opts||{};
69
- $.getOpt = function(o) {
70
- var $this = this;
71
- // Get multiple option if passed an array
72
- if(o instanceof Array) {
73
- var options = {};
74
- $h.each(o, function(option){
75
- options[option] = $this.getOpt(option);
76
- });
77
- return options;
78
- }
79
- // Otherwise, just return a simple option
80
- if ($this instanceof ResumableChunk) {
81
- if (typeof $this.opts[o] !== 'undefined') { return $this.opts[o]; }
82
- else { $this = $this.fileObj; }
83
- }
84
- if ($this instanceof ResumableFile) {
85
- if (typeof $this.opts[o] !== 'undefined') { return $this.opts[o]; }
86
- else { $this = $this.resumableObj; }
87
- }
88
- if ($this instanceof Resumable) {
89
- if (typeof $this.opts[o] !== 'undefined') { return $this.opts[o]; }
90
- else { return $this.defaults[o]; }
91
- }
92
- };
8
+ (function(){
9
+ "use strict";
93
10
 
94
- // EVENTS
95
- // catchAll(event, ...)
96
- // fileSuccess(file), fileProgress(file), fileAdded(file, event), fileRetry(file), fileError(file, message),
97
- // complete(), progress(), error(message, file), pause()
98
- $.events = [];
99
- $.on = function(event,callback){
100
- $.events.push(event.toLowerCase(), callback);
101
- };
102
- $.fire = function(){
103
- // `arguments` is an object, not array, in FF, so:
104
- var args = [];
105
- for (var i=0; i<arguments.length; i++) args.push(arguments[i]);
106
- // Find event listeners, and support pseudo-event `catchAll`
107
- var event = args[0].toLowerCase();
108
- for (var i=0; i<=$.events.length; i+=2) {
109
- if($.events[i]==event) $.events[i+1].apply($,args.slice(1));
110
- if($.events[i]=='catchall') $.events[i+1].apply(null,args);
11
+ var Resumable = function(opts){
12
+ if ( !(this instanceof Resumable) ) {
13
+ return new Resumable(opts);
111
14
  }
112
- if(event=='fileerror') $.fire('error', args[2], args[1]);
113
- if(event=='fileprogress') $.fire('progress');
114
- };
115
-
116
-
117
- // INTERNAL HELPER METHODS (handy, but ultimately not part of uploading)
118
- $h = {
119
- stopEvent: function(e){
120
- e.stopPropagation();
121
- e.preventDefault();
122
- },
123
- each: function(o,callback){
124
- if(typeof(o.length)!=='undefined') {
125
- for (var i=0; i<o.length; i++) {
126
- // Array or FileList
127
- if(callback(o[i])===false) return;
128
- }
129
- } else {
130
- for (i in o) {
131
- // Object
132
- if(callback(i,o[i])===false) return;
133
- }
15
+ this.version = 1.0;
16
+ // SUPPORTED BY BROWSER?
17
+ // Check if these features are support by the browser:
18
+ // - File object type
19
+ // - Blob object type
20
+ // - FileList object type
21
+ // - slicing files
22
+ this.support = (
23
+ (typeof(File)!=='undefined')
24
+ &&
25
+ (typeof(Blob)!=='undefined')
26
+ &&
27
+ (typeof(FileList)!=='undefined')
28
+ &&
29
+ (!!Blob.prototype.webkitSlice||!!Blob.prototype.mozSlice||!!Blob.prototype.slice||false)
30
+ );
31
+ if(!this.support) return(false);
32
+
33
+
34
+ // PROPERTIES
35
+ var $ = this;
36
+ $.files = [];
37
+ $.defaults = {
38
+ chunkSize:1*1024*1024,
39
+ forceChunkSize:false,
40
+ simultaneousUploads:3,
41
+ fileParameterName:'file',
42
+ throttleProgressCallbacks:0.5,
43
+ query:{},
44
+ headers:{},
45
+ preprocess:null,
46
+ method:'multipart',
47
+ prioritizeFirstAndLastChunk:false,
48
+ target:'/',
49
+ testChunks:true,
50
+ generateUniqueIdentifier:null,
51
+ maxChunkRetries:undefined,
52
+ chunkRetryInterval:undefined,
53
+ permanentErrors:[404, 415, 500, 501],
54
+ maxFiles:undefined,
55
+ withCredentials:false,
56
+ xhrTimeout:0,
57
+ maxFilesErrorCallback:function (files, errorCount) {
58
+ var maxFiles = $.getOpt('maxFiles');
59
+ alert('Please upload ' + maxFiles + ' file' + (maxFiles === 1 ? '' : 's') + ' at a time.');
60
+ },
61
+ minFileSize:1,
62
+ minFileSizeErrorCallback:function(file, errorCount) {
63
+ alert(file.fileName||file.name +' is too small, please upload files larger than ' + $h.formatSize($.getOpt('minFileSize')) + '.');
64
+ },
65
+ maxFileSize:undefined,
66
+ maxFileSizeErrorCallback:function(file, errorCount) {
67
+ alert(file.fileName||file.name +' is too large, please upload files less than ' + $h.formatSize($.getOpt('maxFileSize')) + '.');
68
+ },
69
+ fileType: [],
70
+ fileTypeErrorCallback: function(file, errorCount) {
71
+ alert(file.fileName||file.name +' has type not allowed, please upload files of type ' + $.getOpt('fileType') + '.');
72
+ }
73
+ };
74
+ $.opts = opts||{};
75
+ $.getOpt = function(o) {
76
+ var $opt = this;
77
+ // Get multiple option if passed an array
78
+ if(o instanceof Array) {
79
+ var options = {};
80
+ $h.each(o, function(option){
81
+ options[option] = $opt.getOpt(option);
82
+ });
83
+ return options;
84
+ }
85
+ // Otherwise, just return a simple option
86
+ if ($opt instanceof ResumableChunk) {
87
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
88
+ else { $opt = $opt.fileObj; }
134
89
  }
135
- },
136
- generateUniqueIdentifier:function(file){
137
- var custom = $.getOpt('generateUniqueIdentifier');
138
- if(typeof custom === 'function') {
139
- return custom(file);
90
+ if ($opt instanceof ResumableFile) {
91
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
92
+ else { $opt = $opt.resumableObj; }
140
93
  }
141
- var relativePath = file.webkitRelativePath||file.fileName||file.name; // Some confusion in different versions of Firefox
142
- var size = file.size;
143
- return(size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, ''));
144
- },
145
- contains:function(array,test) {
146
- var result = false;
147
-
148
- $h.each(array, function(value) {
94
+ if ($opt instanceof Resumable) {
95
+ if (typeof $opt.opts[o] !== 'undefined') { return $opt.opts[o]; }
96
+ else { return $opt.defaults[o]; }
97
+ }
98
+ };
99
+
100
+ // EVENTS
101
+ // catchAll(event, ...)
102
+ // fileSuccess(file), fileProgress(file), fileAdded(file, event), fileRetry(file), fileError(file, message),
103
+ // complete(), progress(), error(message, file), pause()
104
+ $.events = [];
105
+ $.on = function(event,callback){
106
+ $.events.push(event.toLowerCase(), callback);
107
+ };
108
+ $.fire = function(){
109
+ // `arguments` is an object, not array, in FF, so:
110
+ var args = [];
111
+ for (var i=0; i<arguments.length; i++) args.push(arguments[i]);
112
+ // Find event listeners, and support pseudo-event `catchAll`
113
+ var event = args[0].toLowerCase();
114
+ for (var i=0; i<=$.events.length; i+=2) {
115
+ if($.events[i]==event) $.events[i+1].apply($,args.slice(1));
116
+ if($.events[i]=='catchall') $.events[i+1].apply(null,args);
117
+ }
118
+ if(event=='fileerror') $.fire('error', args[2], args[1]);
119
+ if(event=='fileprogress') $.fire('progress');
120
+ };
121
+
122
+
123
+ // INTERNAL HELPER METHODS (handy, but ultimately not part of uploading)
124
+ var $h = {
125
+ stopEvent: function(e){
126
+ e.stopPropagation();
127
+ e.preventDefault();
128
+ },
129
+ each: function(o,callback){
130
+ if(typeof(o.length)!=='undefined') {
131
+ for (var i=0; i<o.length; i++) {
132
+ // Array or FileList
133
+ if(callback(o[i])===false) return;
134
+ }
135
+ } else {
136
+ for (i in o) {
137
+ // Object
138
+ if(callback(i,o[i])===false) return;
139
+ }
140
+ }
141
+ },
142
+ generateUniqueIdentifier:function(file){
143
+ var custom = $.getOpt('generateUniqueIdentifier');
144
+ if(typeof custom === 'function') {
145
+ return custom(file);
146
+ }
147
+ var relativePath = file.webkitRelativePath||file.fileName||file.name; // Some confusion in different versions of Firefox
148
+ var size = file.size;
149
+ return(size + '-' + relativePath.replace(/[^0-9a-zA-Z_-]/img, ''));
150
+ },
151
+ contains:function(array,test) {
152
+ var result = false;
153
+
154
+ $h.each(array, function(value) {
149
155
  if (value == test) {
150
156
  result = true;
151
157
  return false;
152
158
  }
153
159
  return true;
154
- });
160
+ });
155
161
 
156
- return result;
157
- },
158
- formatSize:function(size){
159
- if(size<1024) {
160
- return size + ' bytes';
161
- } else if(size<1024*1024) {
162
- return (size/1024.0).toFixed(0) + ' KB';
163
- } else if(size<1024*1024*1024) {
164
- return (size/1024.0/1024.0).toFixed(1) + ' MB';
165
- } else {
166
- return (size/1024.0/1024.0/1024.0).toFixed(1) + ' GB';
162
+ return result;
163
+ },
164
+ formatSize:function(size){
165
+ if(size<1024) {
166
+ return size + ' bytes';
167
+ } else if(size<1024*1024) {
168
+ return (size/1024.0).toFixed(0) + ' KB';
169
+ } else if(size<1024*1024*1024) {
170
+ return (size/1024.0/1024.0).toFixed(1) + ' MB';
171
+ } else {
172
+ return (size/1024.0/1024.0/1024.0).toFixed(1) + ' GB';
173
+ }
174
+ },
175
+ getTarget:function(params){
176
+ var target = $.getOpt('target');
177
+ if(target.indexOf('?') < 0) {
178
+ target += '?';
179
+ } else {
180
+ target += '&';
181
+ }
182
+ return target + params.join('&');
167
183
  }
168
- }
169
- };
170
-
171
- var onDrop = function(event){
172
- $h.stopEvent(event);
173
- appendFilesFromFileList(event.dataTransfer.files, event);
174
- };
175
- var onDragOver = function(e) {
176
- e.preventDefault();
177
- };
184
+ };
178
185
 
179
- // INTERNAL METHODS (both handy and responsible for the heavy load)
180
- var appendFilesFromFileList = function(fileList, event){
181
- // check for uploading too many files
182
- var errorCount = 0;
183
- var o = $.getOpt(['maxFiles', 'minFileSize', 'maxFileSize', 'maxFilesErrorCallback', 'minFileSizeErrorCallback', 'maxFileSizeErrorCallback', 'fileType', 'fileTypeErrorCallback']);
184
- if (typeof(o.maxFiles)!=='undefined' && o.maxFiles<(fileList.length+$.files.length)) {
185
- o.maxFilesErrorCallback(fileList, errorCount++);
186
- return false;
187
- }
188
- var files = [];
189
- $h.each(fileList, function(file){
190
- file.name = file.fileName = file.fileName||file.name; // consistency across browsers for the error message
191
-
192
- if (o.fileType.length > 0 && !$h.contains(o.fileType, file.type.split('/')[1])) {
193
- o.fileTypeErrorCallback(file, errorCount++);
194
- return false;
186
+ var onDrop = function(event){
187
+ $h.stopEvent(event);
188
+ appendFilesFromFileList(event.dataTransfer.files, event);
189
+ };
190
+ var onDragOver = function(e) {
191
+ e.preventDefault();
192
+ };
193
+
194
+ // INTERNAL METHODS (both handy and responsible for the heavy load)
195
+ var appendFilesFromFileList = function(fileList, event){
196
+ // check for uploading too many files
197
+ var errorCount = 0;
198
+ var o = $.getOpt(['maxFiles', 'minFileSize', 'maxFileSize', 'maxFilesErrorCallback', 'minFileSizeErrorCallback', 'maxFileSizeErrorCallback', 'fileType', 'fileTypeErrorCallback']);
199
+ if (typeof(o.maxFiles)!=='undefined' && o.maxFiles<(fileList.length+$.files.length)) {
200
+ // if single-file upload, file is already added, and trying to add 1 new file, simply replace the already-added file
201
+ if (o.maxFiles===1 && $.files.length===1 && fileList.length===1) {
202
+ $.removeFile($.files[0]);
203
+ } else {
204
+ o.maxFilesErrorCallback(fileList, errorCount++);
205
+ return false;
206
+ }
207
+ }
208
+ var files = [];
209
+ $h.each(fileList, function(file){
210
+ var fileName = file.name.split('.');
211
+ var fileType = fileName[fileName.length-1].toLowerCase();
212
+
213
+ if (o.fileType.length > 0 && !$h.contains(o.fileType, fileType)) {
214
+ o.fileTypeErrorCallback(file, errorCount++);
215
+ return false;
195
216
  }
196
217
 
197
218
  if (typeof(o.minFileSize)!=='undefined' && file.size<o.minFileSize) {
198
- o.minFileSizeErrorCallback(file, errorCount++);
199
- return false;
219
+ o.minFileSizeErrorCallback(file, errorCount++);
220
+ return false;
200
221
  }
201
222
  if (typeof(o.maxFileSize)!=='undefined' && file.size>o.maxFileSize) {
202
- o.maxFileSizeErrorCallback(file, errorCount++);
203
- return false;
223
+ o.maxFileSizeErrorCallback(file, errorCount++);
224
+ return false;
204
225
  }
205
226
 
206
227
  // directories have size == 0
207
- if (file.size > 0 && !$.getFromUniqueIdentifier($h.generateUniqueIdentifier(file))) {
228
+ if (!$.getFromUniqueIdentifier($h.generateUniqueIdentifier(file))) {(function(){
208
229
  var f = new ResumableFile($, file);
209
- $.files.push(f);
210
- files.push(f);
211
- $.fire('fileAdded', f, event);
212
- }
230
+ window.setTimeout(function(){
231
+ $.files.push(f);
232
+ files.push(f);
233
+ f.container = (typeof event != 'undefined' ? event.srcElement : null);
234
+ $.fire('fileAdded', f, event)
235
+ },0);
236
+ })()};
213
237
  });
214
- $.fire('filesAdded', files);
215
- };
216
-
217
- // INTERNAL OBJECT TYPES
218
- function ResumableFile(resumableObj, file){
219
- var $ = this;
220
- $.opts = {};
221
- $.getOpt = resumableObj.getOpt;
222
- $._prevProgress = 0;
223
- $.resumableObj = resumableObj;
224
- $.file = file;
225
- $.fileName = file.fileName||file.name; // Some confusion in different versions of Firefox
226
- $.size = file.size;
227
- $.relativePath = file.webkitRelativePath || $.fileName;
228
- $.uniqueIdentifier = $h.generateUniqueIdentifier(file);
229
- var _error = false;
230
-
231
- // Callback when something happens within the chunk
232
- var chunkEvent = function(event, message){
233
- // event can be 'progress', 'success', 'error' or 'retry'
234
- switch(event){
235
- case 'progress':
236
- $.resumableObj.fire('fileProgress', $);
237
- break;
238
- case 'error':
239
- $.abort();
240
- _error = true;
241
- $.chunks = [];
242
- $.resumableObj.fire('fileError', $, message);
243
- break;
244
- case 'success':
245
- if(_error) return;
246
- $.resumableObj.fire('fileProgress', $); // it's at least progress
247
- if($.progress()==1) {
248
- $.resumableObj.fire('fileSuccess', $, message);
238
+ window.setTimeout(function(){
239
+ $.fire('filesAdded', files)
240
+ },0);
241
+ };
242
+
243
+ // INTERNAL OBJECT TYPES
244
+ function ResumableFile(resumableObj, file){
245
+ var $ = this;
246
+ $.opts = {};
247
+ $.getOpt = resumableObj.getOpt;
248
+ $._prevProgress = 0;
249
+ $.resumableObj = resumableObj;
250
+ $.file = file;
251
+ $.fileName = file.fileName||file.name; // Some confusion in different versions of Firefox
252
+ $.size = file.size;
253
+ $.relativePath = file.webkitRelativePath || $.fileName;
254
+ $.uniqueIdentifier = $h.generateUniqueIdentifier(file);
255
+ $._pause = false;
256
+ $.container = '';
257
+ var _error = false;
258
+
259
+ // Callback when something happens within the chunk
260
+ var chunkEvent = function(event, message){
261
+ // event can be 'progress', 'success', 'error' or 'retry'
262
+ switch(event){
263
+ case 'progress':
264
+ $.resumableObj.fire('fileProgress', $);
265
+ break;
266
+ case 'error':
267
+ $.abort();
268
+ _error = true;
269
+ $.chunks = [];
270
+ $.resumableObj.fire('fileError', $, message);
271
+ break;
272
+ case 'success':
273
+ if(_error) return;
274
+ $.resumableObj.fire('fileProgress', $); // it's at least progress
275
+ if($.isComplete()) {
276
+ $.resumableObj.fire('fileSuccess', $, message);
277
+ }
278
+ break;
279
+ case 'retry':
280
+ $.resumableObj.fire('fileRetry', $);
281
+ break;
249
282
  }
250
- break;
251
- case 'retry':
252
- $.resumableObj.fire('fileRetry', $);
253
- break;
254
- }
255
- }
283
+ };
256
284
 
257
- // Main code to set up a file object with chunks,
258
- // packaged to be able to handle retries if needed.
259
- $.chunks = [];
260
- $.abort = function(){
261
- // Stop current uploads
262
- $h.each($.chunks, function(c){
263
- if(c.status()=='uploading') c.abort();
264
- });
265
- $.resumableObj.fire('fileProgress', $);
266
- }
267
- $.cancel = function(){
268
- // Reset this file to be void
269
- var _chunks = $.chunks;
285
+ // Main code to set up a file object with chunks,
286
+ // packaged to be able to handle retries if needed.
270
287
  $.chunks = [];
271
- // Stop current uploads
272
- $h.each(_chunks, function(c){
288
+ $.abort = function(){
289
+ // Stop current uploads
290
+ var abortCount = 0;
291
+ $h.each($.chunks, function(c){
292
+ if(c.status()=='uploading') {
293
+ c.abort();
294
+ abortCount++;
295
+ }
296
+ });
297
+ if(abortCount>0) $.resumableObj.fire('fileProgress', $);
298
+ };
299
+ $.cancel = function(){
300
+ // Reset this file to be void
301
+ var _chunks = $.chunks;
302
+ $.chunks = [];
303
+ // Stop current uploads
304
+ $h.each(_chunks, function(c){
273
305
  if(c.status()=='uploading') {
274
306
  c.abort();
275
307
  $.resumableObj.uploadNextChunk();
276
308
  }
277
309
  });
278
- $.resumableObj.removeFile($);
279
- $.resumableObj.fire('fileProgress', $);
280
- },
281
- $.retry = function(){
282
- $.bootstrap();
283
- $.resumableObj.upload();
284
- }
285
- $.bootstrap = function(){
286
- $.abort();
310
+ $.resumableObj.removeFile($);
311
+ $.resumableObj.fire('fileProgress', $);
312
+ };
313
+ $.retry = function(){
314
+ $.bootstrap();
315
+ var firedRetry = false;
316
+ $.resumableObj.on('chunkingComplete', function(){
317
+ if(!firedRetry) $.resumableObj.upload();
318
+ firedRetry = true;
319
+ });
320
+ };
321
+ $.bootstrap = function(){
322
+ $.abort();
287
323
  _error = false;
288
- // Rebuild stack of chunks from file
289
- $.chunks = [];
290
- $._prevProgress = 0;
291
- var round = $.getOpt('forceChunkSize') ? Math.ceil : Math.floor;
292
- for (var offset=0; offset<Math.max(round($.file.size/$.getOpt('chunkSize')),1); offset++) {
293
- $.chunks.push(new ResumableChunk($.resumableObj, $, offset, chunkEvent));
294
- }
295
- }
296
- $.progress = function(){
297
- if(_error) return(1);
298
- // Sum up progress across everything
299
- var ret = 0;
300
- var error = false;
301
- $h.each($.chunks, function(c){
324
+ // Rebuild stack of chunks from file
325
+ $.chunks = [];
326
+ $._prevProgress = 0;
327
+ var round = $.getOpt('forceChunkSize') ? Math.ceil : Math.floor;
328
+ var maxOffset = Math.max(round($.file.size/$.getOpt('chunkSize')),1);
329
+ for (var offset=0; offset<maxOffset; offset++) {(function(offset){
330
+ window.setTimeout(function(){
331
+ $.chunks.push(new ResumableChunk($.resumableObj, $, offset, chunkEvent));
332
+ $.resumableObj.fire('chunkingProgress',$,offset/maxOffset);
333
+ },0);
334
+ })(offset)}
335
+ window.setTimeout(function(){
336
+ $.resumableObj.fire('chunkingComplete',$);
337
+ },0);
338
+ };
339
+ $.progress = function(){
340
+ if(_error) return(1);
341
+ // Sum up progress across everything
342
+ var ret = 0;
343
+ var error = false;
344
+ $h.each($.chunks, function(c){
302
345
  if(c.status()=='error') error = true;
303
346
  ret += c.progress(true); // get chunk progress relative to entire file
304
347
  });
305
- ret = (error ? 1 : (ret>0.999 ? 1 : ret))
306
- ret = Math.max($._prevProgress, ret); // We don't want to lose percentages when an upload is paused
307
- $._prevProgress = ret;
308
- return(ret);
309
- }
348
+ ret = (error ? 1 : (ret>0.999 ? 1 : ret));
349
+ ret = Math.max($._prevProgress, ret); // We don't want to lose percentages when an upload is paused
350
+ $._prevProgress = ret;
351
+ return(ret);
352
+ };
353
+ $.isUploading = function(){
354
+ var uploading = false;
355
+ $h.each($.chunks, function(chunk){
356
+ if(chunk.status()=='uploading') {
357
+ uploading = true;
358
+ return(false);
359
+ }
360
+ });
361
+ return(uploading);
362
+ };
363
+ $.isComplete = function(){
364
+ var outstanding = false;
365
+ $h.each($.chunks, function(chunk){
366
+ var status = chunk.status();
367
+ if(status=='pending' || status=='uploading' || chunk.preprocessState === 1) {
368
+ outstanding = true;
369
+ return(false);
370
+ }
371
+ });
372
+ return(!outstanding);
373
+ };
374
+ $.pause = function(pause){
375
+ if(typeof(pause)==='undefined'){
376
+ $._pause = ($._pause ? false : true);
377
+ }else{
378
+ $._pause = pause;
379
+ }
380
+ };
381
+ $.isPaused = function() {
382
+ return $._pause;
383
+ };
310
384
 
311
- // Bootstrap and return
312
- $.bootstrap();
313
- return(this);
314
- }
315
385
 
316
- function ResumableChunk(resumableObj, fileObj, offset, callback){
317
- var $ = this;
318
- $.opts = {};
319
- $.getOpt = resumableObj.getOpt;
320
- $.resumableObj = resumableObj;
321
- $.fileObj = fileObj;
322
- $.fileObjSize = fileObj.size;
323
- $.offset = offset;
324
- $.callback = callback;
325
- $.lastProgressCallback = (new Date);
326
- $.tested = false;
327
- $.retries = 0;
328
- $.preprocessState = 0; // 0 = unprocessed, 1 = processing, 2 = finished
329
-
330
- // Computed properties
331
- var chunkSize = $.getOpt('chunkSize');
332
- $.loaded = 0;
333
- $.startByte = $.offset*chunkSize;
334
- $.endByte = Math.min($.fileObjSize, ($.offset+1)*chunkSize);
335
- if ($.fileObjSize-$.endByte < chunkSize && !$.getOpt('forceChunkSize')) {
336
- // The last chunk will be bigger than the chunk size, but less than 2*chunkSize
337
- $.endByte = $.fileObjSize;
386
+ // Bootstrap and return
387
+ $.resumableObj.fire('chunkingStart', $);
388
+ $.bootstrap();
389
+ return(this);
338
390
  }
339
- $.xhr = null;
340
-
341
- // test() makes a GET request without any data to see if the chunk has already been uploaded in a previous session
342
- $.test = function(){
343
- // Set up request and listen for event
344
- $.xhr = new XMLHttpRequest();
345
-
346
- var testHandler = function(e){
347
- $.tested = true;
348
- var status = $.status();
349
- if(status=='success') {
350
- $.callback(status, $.message());
351
- $.resumableObj.uploadNextChunk();
352
- } else {
353
- $.send();
354
- }
391
+
392
+ function ResumableChunk(resumableObj, fileObj, offset, callback){
393
+ var $ = this;
394
+ $.opts = {};
395
+ $.getOpt = resumableObj.getOpt;
396
+ $.resumableObj = resumableObj;
397
+ $.fileObj = fileObj;
398
+ $.fileObjSize = fileObj.size;
399
+ $.fileObjType = fileObj.file.type;
400
+ $.offset = offset;
401
+ $.callback = callback;
402
+ $.lastProgressCallback = (new Date);
403
+ $.tested = false;
404
+ $.retries = 0;
405
+ $.pendingRetry = false;
406
+ $.preprocessState = 0; // 0 = unprocessed, 1 = processing, 2 = finished
407
+
408
+ // Computed properties
409
+ var chunkSize = $.getOpt('chunkSize');
410
+ $.loaded = 0;
411
+ $.startByte = $.offset*chunkSize;
412
+ $.endByte = Math.min($.fileObjSize, ($.offset+1)*chunkSize);
413
+ if ($.fileObjSize-$.endByte < chunkSize && !$.getOpt('forceChunkSize')) {
414
+ // The last chunk will be bigger than the chunk size, but less than 2*chunkSize
415
+ $.endByte = $.fileObjSize;
355
416
  }
356
- $.xhr.addEventListener("load", testHandler, false);
357
- $.xhr.addEventListener("error", testHandler, false);
358
-
359
- // Add data from the query options
360
- var url = ""
361
- var params = [];
362
- var customQuery = $.getOpt('query');
363
- if(typeof customQuery == "function") customQuery = customQuery($.fileObj, $);
364
- $h.each(customQuery, function(k,v){
417
+ $.xhr = null;
418
+
419
+ // test() makes a GET request without any data to see if the chunk has already been uploaded in a previous session
420
+ $.test = function(){
421
+ // Set up request and listen for event
422
+ $.xhr = new XMLHttpRequest();
423
+
424
+ var testHandler = function(e){
425
+ $.tested = true;
426
+ var status = $.status();
427
+ if(status=='success') {
428
+ $.callback(status, $.message());
429
+ $.resumableObj.uploadNextChunk();
430
+ } else {
431
+ $.send();
432
+ }
433
+ };
434
+ $.xhr.addEventListener('load', testHandler, false);
435
+ $.xhr.addEventListener('error', testHandler, false);
436
+
437
+ // Add data from the query options
438
+ var params = [];
439
+ var customQuery = $.getOpt('query');
440
+ if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $);
441
+ $h.each(customQuery, function(k,v){
365
442
  params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
366
443
  });
367
- // Add extra data to identify chunk
368
- params.push(['resumableChunkNumber', encodeURIComponent($.offset+1)].join('='));
369
- params.push(['resumableChunkSize', encodeURIComponent($.getOpt('chunkSize'))].join('='));
370
- params.push(['resumableCurrentChunkSize', encodeURIComponent($.endByte - $.startByte)].join('='));
371
- params.push(['resumableTotalSize', encodeURIComponent($.fileObjSize)].join('='));
372
- params.push(['resumableIdentifier', encodeURIComponent($.fileObj.uniqueIdentifier)].join('='));
373
- params.push(['resumableFilename', encodeURIComponent($.fileObj.fileName)].join('='));
374
- params.push(['resumableRelativePath', encodeURIComponent($.fileObj.relativePath)].join('='));
375
- // Append the relevant chunk and send it
376
- $.xhr.open("GET", $.getOpt('target') + '?' + params.join('&'));
377
- // Add data from header options
378
- $h.each($.getOpt('headers'), function(k,v) {
379
- $.xhr.setRequestHeader(k, v);
380
- });
381
- $.xhr.send(null);
382
- }
444
+ // Add extra data to identify chunk
445
+ params.push(['resumableChunkNumber', encodeURIComponent($.offset+1)].join('='));
446
+ params.push(['resumableChunkSize', encodeURIComponent($.getOpt('chunkSize'))].join('='));
447
+ params.push(['resumableCurrentChunkSize', encodeURIComponent($.endByte - $.startByte)].join('='));
448
+ params.push(['resumableTotalSize', encodeURIComponent($.fileObjSize)].join('='));
449
+ params.push(['resumableType', encodeURIComponent($.fileObjType)].join('='));
450
+ params.push(['resumableIdentifier', encodeURIComponent($.fileObj.uniqueIdentifier)].join('='));
451
+ params.push(['resumableFilename', encodeURIComponent($.fileObj.fileName)].join('='));
452
+ params.push(['resumableRelativePath', encodeURIComponent($.fileObj.relativePath)].join('='));
453
+ params.push(['resumableTotalChunks', encodeURIComponent($.fileObj.chunks.length)].join('='));
454
+ // Append the relevant chunk and send it
455
+ $.xhr.open('GET', $h.getTarget(params));
456
+ $.xhr.timeout = $.getOpt('xhrTimeout');
457
+ $.xhr.withCredentials = $.getOpt('withCredentials');
458
+ // Add data from header options
459
+ $h.each($.getOpt('headers'), function(k,v) {
460
+ $.xhr.setRequestHeader(k, v);
461
+ });
462
+ $.xhr.send(null);
463
+ };
383
464
 
384
- $.preprocessFinished = function(){
385
- $.preprocessState = 2;
386
- $.send();
387
- }
465
+ $.preprocessFinished = function(){
466
+ $.preprocessState = 2;
467
+ $.send();
468
+ };
388
469
 
389
- // send() uploads the actual data in a POST call
390
- $.send = function(){
391
- var preprocess = $.getOpt('preprocess');
392
- if(typeof preprocess === 'function') {
393
- switch($.preprocessState) {
470
+ // send() uploads the actual data in a POST call
471
+ $.send = function(){
472
+ var preprocess = $.getOpt('preprocess');
473
+ if(typeof preprocess === 'function') {
474
+ switch($.preprocessState) {
394
475
  case 0: preprocess($); $.preprocessState = 1; return;
395
476
  case 1: return;
396
477
  case 2: break;
478
+ }
479
+ }
480
+ if($.getOpt('testChunks') && !$.tested) {
481
+ $.test();
482
+ return;
397
483
  }
398
- }
399
- if($.getOpt('testChunks') && !$.tested) {
400
- $.test();
401
- return;
402
- }
403
484
 
404
- // Set up request and listen for event
405
- $.xhr = new XMLHttpRequest();
485
+ // Set up request and listen for event
486
+ $.xhr = new XMLHttpRequest();
406
487
 
407
- // Progress
408
- $.xhr.upload.addEventListener("progress", function(e){
488
+ // Progress
489
+ $.xhr.upload.addEventListener('progress', function(e){
409
490
  if( (new Date) - $.lastProgressCallback > $.getOpt('throttleProgressCallbacks') * 1000 ) {
410
491
  $.callback('progress');
411
492
  $.lastProgressCallback = (new Date);
412
493
  }
413
494
  $.loaded=e.loaded||0;
414
495
  }, false);
415
- $.loaded = 0;
416
- $.callback('progress');
417
-
418
- // Done (either done, failed or retry)
419
- var doneHandler = function(e){
420
- var status = $.status();
421
- if(status=='success'||status=='error') {
422
- $.callback(status, $.message());
423
- $.resumableObj.uploadNextChunk();
424
- } else {
425
- $.callback('retry', $.message());
426
- $.abort();
427
- $.retries++;
428
- var retryInterval = $.getOpt('chunkRetryInterval');
429
- if(retryInterval !== undefined) {
430
- setTimeout($.send, retryInterval);
496
+ $.loaded = 0;
497
+ $.pendingRetry = false;
498
+ $.callback('progress');
499
+
500
+ // Done (either done, failed or retry)
501
+ var doneHandler = function(e){
502
+ var status = $.status();
503
+ if(status=='success'||status=='error') {
504
+ $.callback(status, $.message());
505
+ $.resumableObj.uploadNextChunk();
431
506
  } else {
432
- $.send();
507
+ $.callback('retry', $.message());
508
+ $.abort();
509
+ $.retries++;
510
+ var retryInterval = $.getOpt('chunkRetryInterval');
511
+ if(retryInterval !== undefined) {
512
+ $.pendingRetry = true;
513
+ setTimeout($.send, retryInterval);
514
+ } else {
515
+ $.send();
516
+ }
433
517
  }
434
- }
435
- };
436
- $.xhr.addEventListener("load", doneHandler, false);
437
- $.xhr.addEventListener("error", doneHandler, false);
438
-
439
- // Set up the basic query data from Resumable
440
- var query = {
441
- resumableChunkNumber: $.offset+1,
442
- resumableChunkSize: $.getOpt('chunkSize'),
443
- resumableCurrentChunkSize: $.endByte - $.startByte,
444
- resumableTotalSize: $.fileObjSize,
445
- resumableIdentifier: $.fileObj.uniqueIdentifier,
446
- resumableFilename: $.fileObj.fileName,
447
- resumableRelativePath: $.fileObj.relativePath
448
- }
449
- // Mix in custom data
450
- var customQuery = $.getOpt('query');
451
- if(typeof customQuery == "function") customQuery = customQuery($.fileObj, $);
452
- $h.each(customQuery, function(k,v){
453
- query[k] = v;
454
- });
455
-
456
- // Add data from header options
457
- $h.each($.getOpt('headers'), function(k,v) {
458
- $.xhr.setRequestHeader(k, v);
459
- });
460
-
461
- var func = ($.fileObj.file.slice ? 'slice' : ($.fileObj.file.mozSlice ? 'mozSlice' : ($.fileObj.file.webkitSlice ? 'webkitSlice' : 'slice'))),
462
- bytes = $.fileObj.file[func]($.startByte,$.endByte),
463
- data = null,
464
- target = $.getOpt('target');
465
-
466
- if ($.getOpt('method') === 'octet') {
467
- // Add data from the query options
468
- data = bytes;
469
- var params = [];
470
- $h.each(query, function(k,v){
471
- params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
472
- });
473
- target += '?' + params.join('&');
474
- } else {
475
- // Add data from the query options
476
- data = new FormData();
477
- $h.each(query, function(k,v){
478
- data.append(k,v);
518
+ };
519
+ $.xhr.addEventListener('load', doneHandler, false);
520
+ $.xhr.addEventListener('error', doneHandler, false);
521
+
522
+ // Set up the basic query data from Resumable
523
+ var query = {
524
+ resumableChunkNumber: $.offset+1,
525
+ resumableChunkSize: $.getOpt('chunkSize'),
526
+ resumableCurrentChunkSize: $.endByte - $.startByte,
527
+ resumableTotalSize: $.fileObjSize,
528
+ resumableType: $.fileObjType,
529
+ resumableIdentifier: $.fileObj.uniqueIdentifier,
530
+ resumableFilename: $.fileObj.fileName,
531
+ resumableRelativePath: $.fileObj.relativePath,
532
+ resumableTotalChunks: $.fileObj.chunks.length
533
+ };
534
+ // Mix in custom data
535
+ var customQuery = $.getOpt('query');
536
+ if(typeof customQuery == 'function') customQuery = customQuery($.fileObj, $);
537
+ $h.each(customQuery, function(k,v){
538
+ query[k] = v;
479
539
  });
480
- data.append($.getOpt('fileParameterName'), bytes);
481
- }
482
540
 
483
- $.xhr.open('POST', target);
484
- $.xhr.send(data);
485
- }
486
- $.abort = function(){
487
- // Abort and reset
488
- if($.xhr) $.xhr.abort();
489
- $.xhr = null;
490
- }
491
- $.status = function(){
492
- // Returns: 'pending', 'uploading', 'success', 'error'
493
- if(!$.xhr) {
494
- return('pending');
495
- } else if($.xhr.readyState<4) {
496
- // Status is really 'OPENED', 'HEADERS_RECEIVED' or 'LOADING' - meaning that stuff is happening
497
- return('uploading');
498
- } else {
499
- if($.xhr.status==200) {
500
- // HTTP 200, perfect
501
- return('success');
502
- } else if($h.contains($.getOpt('permanentErrors'), $.xhr.status) || $.retries >= $.getOpt('maxChunkRetries')) {
503
- // HTTP 415/500/501, permanent error
504
- return('error');
541
+ var func = ($.fileObj.file.slice ? 'slice' : ($.fileObj.file.mozSlice ? 'mozSlice' : ($.fileObj.file.webkitSlice ? 'webkitSlice' : 'slice'))),
542
+ bytes = $.fileObj.file[func]($.startByte,$.endByte),
543
+ data = null,
544
+ target = $.getOpt('target');
545
+
546
+ if ($.getOpt('method') === 'octet') {
547
+ // Add data from the query options
548
+ data = bytes;
549
+ var params = [];
550
+ $h.each(query, function(k,v){
551
+ params.push([encodeURIComponent(k), encodeURIComponent(v)].join('='));
552
+ });
553
+ target = $h.getTarget(params);
505
554
  } else {
506
- // this should never happen, but we'll reset and queue a retry
507
- // a likely case for this would be 503 service unavailable
508
- $.abort();
555
+ // Add data from the query options
556
+ data = new FormData();
557
+ $h.each(query, function(k,v){
558
+ data.append(k,v);
559
+ });
560
+ data.append($.getOpt('fileParameterName'), bytes);
561
+ }
562
+
563
+ $.xhr.open('POST', target);
564
+ $.xhr.timeout = $.getOpt('xhrTimeout');
565
+ $.xhr.withCredentials = $.getOpt('withCredentials');
566
+ // Add data from header options
567
+ $h.each($.getOpt('headers'), function(k,v) {
568
+ $.xhr.setRequestHeader(k, v);
569
+ });
570
+ $.xhr.send(data);
571
+ };
572
+ $.abort = function(){
573
+ // Abort and reset
574
+ if($.xhr) $.xhr.abort();
575
+ $.xhr = null;
576
+ };
577
+ $.status = function(){
578
+ // Returns: 'pending', 'uploading', 'success', 'error'
579
+ if($.pendingRetry) {
580
+ // if pending retry then that's effectively the same as actively uploading,
581
+ // there might just be a slight delay before the retry starts
582
+ return('uploading');
583
+ } else if(!$.xhr) {
509
584
  return('pending');
585
+ } else if($.xhr.readyState<4) {
586
+ // Status is really 'OPENED', 'HEADERS_RECEIVED' or 'LOADING' - meaning that stuff is happening
587
+ return('uploading');
588
+ } else {
589
+ if($.xhr.status==200) {
590
+ // HTTP 200, perfect
591
+ return('success');
592
+ } else if($h.contains($.getOpt('permanentErrors'), $.xhr.status) || $.retries >= $.getOpt('maxChunkRetries')) {
593
+ // HTTP 415/500/501, permanent error
594
+ return('error');
595
+ } else {
596
+ // this should never happen, but we'll reset and queue a retry
597
+ // a likely case for this would be 503 service unavailable
598
+ $.abort();
599
+ return('pending');
600
+ }
510
601
  }
511
- }
512
- }
513
- $.message = function(){
514
- return($.xhr ? $.xhr.responseText : '');
515
- }
516
- $.progress = function(relative){
517
- if(typeof(relative)==='undefined') relative = false;
518
- var factor = (relative ? ($.endByte-$.startByte)/$.fileObjSize : 1);
519
- var s = $.status();
520
- switch(s){
521
- case 'success':
522
- case 'error':
523
- return(1*factor);
524
- case 'pending':
525
- return(0*factor);
526
- default:
527
- return($.loaded/($.endByte-$.startByte)*factor);
528
- }
602
+ };
603
+ $.message = function(){
604
+ return($.xhr ? $.xhr.responseText : '');
605
+ };
606
+ $.progress = function(relative){
607
+ if(typeof(relative)==='undefined') relative = false;
608
+ var factor = (relative ? ($.endByte-$.startByte)/$.fileObjSize : 1);
609
+ if($.pendingRetry) return(0);
610
+ var s = $.status();
611
+ switch(s){
612
+ case 'success':
613
+ case 'error':
614
+ return(1*factor);
615
+ case 'pending':
616
+ return(0*factor);
617
+ default:
618
+ return($.loaded/($.endByte-$.startByte)*factor);
619
+ }
620
+ };
621
+ return(this);
529
622
  }
530
- return(this);
531
- }
532
623
 
533
- // QUEUE
534
- $.uploadNextChunk = function(){
535
- var found = false;
624
+ // QUEUE
625
+ $.uploadNextChunk = function(){
626
+ var found = false;
536
627
 
537
- // In some cases (such as videos) it's really handy to upload the first
538
- // and last chunk of a file quickly; this let's the server check the file's
539
- // metadata and determine if there's even a point in continuing.
540
- if ($.getOpt('prioritizeFirstAndLastChunk')) {
541
- $h.each($.files, function(file){
628
+ // In some cases (such as videos) it's really handy to upload the first
629
+ // and last chunk of a file quickly; this let's the server check the file's
630
+ // metadata and determine if there's even a point in continuing.
631
+ if ($.getOpt('prioritizeFirstAndLastChunk')) {
632
+ $h.each($.files, function(file){
542
633
  if(file.chunks.length && file.chunks[0].status()=='pending' && file.chunks[0].preprocessState === 0) {
543
634
  file.chunks[0].send();
544
635
  found = true;
545
636
  return(false);
546
637
  }
547
- if(file.chunks.length>1 && file.chunks[file.chunks.length-1].status()=='pending' && file.chunks[0].preprocessState === 0) {
638
+ if(file.chunks.length>1 && file.chunks[file.chunks.length-1].status()=='pending' && file.chunks[file.chunks.length-1].preprocessState === 0) {
548
639
  file.chunks[file.chunks.length-1].send();
549
640
  found = true;
550
641
  return(false);
551
642
  }
552
643
  });
553
- if(found) return(true);
554
- }
644
+ if(found) return(true);
645
+ }
555
646
 
556
- // Now, simply look for the next, best thing to upload
557
- $h.each($.files, function(file){
558
- $h.each(file.chunks, function(chunk){
559
- if(chunk.status()=='pending' && chunk.preprocessState === 0) {
560
- chunk.send();
561
- found = true;
562
- return(false);
563
- }
647
+ // Now, simply look for the next, best thing to upload
648
+ $h.each($.files, function(file){
649
+ if(file.isPaused()===false){
650
+ $h.each(file.chunks, function(chunk){
651
+ if(chunk.status()=='pending' && chunk.preprocessState === 0) {
652
+ chunk.send();
653
+ found = true;
654
+ return(false);
655
+ }
564
656
  });
657
+ }
565
658
  if(found) return(false);
566
659
  });
567
- if(found) return(true);
568
-
569
- // The are no more outstanding chunks to upload, check is everything is done
570
- $h.each($.files, function(file){
571
- outstanding = false;
572
- $h.each(file.chunks, function(chunk){
573
- var status = chunk.status();
574
- if(status=='pending' || status=='uploading' || chunk.preprocessState === 1) {
575
- outstanding = true;
576
- return(false);
577
- }
578
- });
579
- if(outstanding) return(false);
660
+ if(found) return(true);
661
+
662
+ // The are no more outstanding chunks to upload, check is everything is done
663
+ var outstanding = false;
664
+ $h.each($.files, function(file){
665
+ if(!file.isComplete()) {
666
+ outstanding = true;
667
+ return(false);
668
+ }
580
669
  });
581
- if(!outstanding) {
582
- // All chunks have been uploaded, complete
583
- $.fire('complete');
584
- }
585
- return(false);
586
- };
670
+ if(!outstanding) {
671
+ // All chunks have been uploaded, complete
672
+ $.fire('complete');
673
+ }
674
+ return(false);
675
+ };
587
676
 
588
677
 
589
- // PUBLIC METHODS FOR RESUMABLE.JS
590
- $.assignBrowse = function(domNodes, isDirectory){
591
- if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
678
+ // PUBLIC METHODS FOR RESUMABLE.JS
679
+ $.assignBrowse = function(domNodes, isDirectory){
680
+ if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
592
681
 
593
- // We will create an <input> and overlay it on the domNode
594
- // (crappy, but since HTML5 doesn't have a cross-browser.browse() method we haven't a choice.
595
- // FF4+ allows click() for this though: https://developer.mozilla.org/en/using_files_from_web_applications)
596
- $h.each(domNodes, function(domNode) {
682
+ $h.each(domNodes, function(domNode) {
597
683
  var input;
598
684
  if(domNode.tagName==='INPUT' && domNode.type==='file'){
599
- input = domNode;
685
+ input = domNode;
600
686
  } else {
601
- input = document.createElement('input');
602
- input.setAttribute('type', 'file');
603
- // Place <input /> with the dom node an position the input to fill the entire space
604
- domNode.style.display = 'inline-block';
605
- domNode.style.position = 'relative';
606
- input.style.position = 'absolute';
607
- input.style.top = input.style.left = input.style.bottom = input.style.right = 0;
687
+ input = document.createElement('input');
688
+ input.setAttribute('type', 'file');
689
+ input.style.display = 'none';
690
+ domNode.addEventListener('click', function(){
608
691
  input.style.opacity = 0;
609
- input.style.cursor = 'pointer';
610
- domNode.appendChild(input);
692
+ input.style.display='block';
693
+ input.focus();
694
+ input.click();
695
+ input.style.display='none';
696
+ }, false);
697
+ domNode.appendChild(input);
611
698
  }
612
699
  var maxFiles = $.getOpt('maxFiles');
613
700
  if (typeof(maxFiles)==='undefined'||maxFiles!=1){
@@ -622,99 +709,109 @@ var Resumable = function(opts){
622
709
  }
623
710
  // When new files are added, simply append them to the overall list
624
711
  input.addEventListener('change', function(e){
625
- appendFilesFromFileList(e.target.files);
626
- e.target.value = '';
712
+ appendFilesFromFileList(e.target.files,e);
713
+ e.target.value = '';
627
714
  }, false);
628
- });
629
- };
630
- $.assignDrop = function(domNodes){
631
- if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
715
+ });
716
+ };
717
+ $.assignDrop = function(domNodes){
718
+ if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
632
719
 
633
- $h.each(domNodes, function(domNode) {
720
+ $h.each(domNodes, function(domNode) {
634
721
  domNode.addEventListener('dragover', onDragOver, false);
635
722
  domNode.addEventListener('drop', onDrop, false);
636
723
  });
637
- };
638
- $.unAssignDrop = function(domNodes) {
639
- if (typeof(domNodes.length) == 'undefined') domNodes = [domNodes];
724
+ };
725
+ $.unAssignDrop = function(domNodes) {
726
+ if (typeof(domNodes.length) == 'undefined') domNodes = [domNodes];
640
727
 
641
- $h.each(domNodes, function(domNode) {
728
+ $h.each(domNodes, function(domNode) {
642
729
  domNode.removeEventListener('dragover', onDragOver);
643
730
  domNode.removeEventListener('drop', onDrop);
644
731
  });
645
- };
646
- $.isUploading = function(){
647
- var uploading = false;
648
- $h.each($.files, function(file){
649
- $h.each(file.chunks, function(chunk){
650
- if(chunk.status()=='uploading') {
651
- uploading = true;
652
- return(false);
653
- }
654
- });
655
- if(uploading) return(false);
732
+ };
733
+ $.isUploading = function(){
734
+ var uploading = false;
735
+ $h.each($.files, function(file){
736
+ if (file.isUploading()) {
737
+ uploading = true;
738
+ return(false);
739
+ }
656
740
  });
657
- return(uploading);
658
- }
659
- $.upload = function(){
660
- // Make sure we don't start too many uploads at once
661
- if($.isUploading()) return;
662
- // Kick off the queue
663
- $.fire('uploadStart');
664
- for (var num=1; num<=$.getOpt('simultaneousUploads'); num++) {
665
- $.uploadNextChunk();
666
- }
667
- };
668
- $.pause = function(){
669
- // Resume all chunks currently being uploaded
670
- $h.each($.files, function(file){
741
+ return(uploading);
742
+ };
743
+ $.upload = function(){
744
+ // Make sure we don't start too many uploads at once
745
+ if($.isUploading()) return;
746
+ // Kick off the queue
747
+ $.fire('uploadStart');
748
+ for (var num=1; num<=$.getOpt('simultaneousUploads'); num++) {
749
+ $.uploadNextChunk();
750
+ }
751
+ };
752
+ $.pause = function(){
753
+ // Resume all chunks currently being uploaded
754
+ $h.each($.files, function(file){
671
755
  file.abort();
672
756
  });
673
- $.fire('pause');
674
- };
675
- $.cancel = function(){
676
- $h.each($.files, function(file){
677
- file.cancel();
678
- });
679
- $.fire('cancel');
680
- };
681
- $.progress = function(){
682
- var totalDone = 0;
683
- var totalSize = 0;
684
- // Resume all chunks currently being uploaded
685
- $h.each($.files, function(file){
757
+ $.fire('pause');
758
+ };
759
+ $.cancel = function(){
760
+ for(var i = $.files.length - 1; i >= 0; i--) {
761
+ $.files[i].cancel();
762
+ }
763
+ $.fire('cancel');
764
+ };
765
+ $.progress = function(){
766
+ var totalDone = 0;
767
+ var totalSize = 0;
768
+ // Resume all chunks currently being uploaded
769
+ $h.each($.files, function(file){
686
770
  totalDone += file.progress()*file.size;
687
771
  totalSize += file.size;
688
772
  });
689
- return(totalSize>0 ? totalDone/totalSize : 0);
690
- };
691
- $.addFile = function(file){
692
- appendFilesFromFileList([file]);
693
- };
694
- $.removeFile = function(file){
695
- var files = [];
696
- $h.each($.files, function(f,i){
697
- if(f!==file) files.push(f);
698
- });
699
- $.files = files;
700
- };
701
- $.getFromUniqueIdentifier = function(uniqueIdentifier){
702
- var ret = false;
703
- $h.each($.files, function(f){
773
+ return(totalSize>0 ? totalDone/totalSize : 0);
774
+ };
775
+ $.addFile = function(file, event){
776
+ appendFilesFromFileList([file], event);
777
+ };
778
+ $.removeFile = function(file){
779
+ for(var i = $.files.length - 1; i >= 0; i--) {
780
+ if($.files[i] === file) {
781
+ $.files.splice(i, 1);
782
+ }
783
+ }
784
+ };
785
+ $.getFromUniqueIdentifier = function(uniqueIdentifier){
786
+ var ret = false;
787
+ $h.each($.files, function(f){
704
788
  if(f.uniqueIdentifier==uniqueIdentifier) ret = f;
705
789
  });
706
- return(ret);
707
- };
708
- $.getSize = function(){
709
- var totalSize = 0;
710
- $h.each($.files, function(file){
790
+ return(ret);
791
+ };
792
+ $.getSize = function(){
793
+ var totalSize = 0;
794
+ $h.each($.files, function(file){
711
795
  totalSize += file.size;
712
796
  });
713
- return(totalSize);
797
+ return(totalSize);
798
+ };
799
+
800
+ return(this);
714
801
  };
715
802
 
716
- return(this);
717
- }
718
803
 
719
- // Node.js-style export for Node and Component
720
- module.exports = Resumable;
804
+ // Node.js-style export for Node and Component
805
+ if (typeof module != 'undefined') {
806
+ module.exports = Resumable;
807
+ } else if (typeof define === "function" && define.amd) {
808
+ // AMD/requirejs: Define the module
809
+ define(function(){
810
+ return Resumable;
811
+ });
812
+ } else {
813
+ // Browser: Expose to window
814
+ window.Resumable = Resumable;
815
+ }
816
+
817
+ })();