resumablejs-rails 0.1.1 → 1.0

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 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
+ })();