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 +4 -4
- data/lib/resumablejs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/resumable.js +688 -591
- metadata +11 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 193ac0f83f8157b10c100d48601be68b89c89769
|
|
4
|
+
data.tar.gz: b20ed9c5eb8af57b297b6aa904106e9f058a2cb3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 25c53ae556106fecf94eccb1b5df1cf0c556df1c8e5c535b06446c51a69471be2b9623fc28b149ffcb49e4531669325c6e0b3dd4a9249f12058f9a61b581e811
|
|
7
|
+
data.tar.gz: c0b25f735c94f26c49396d560223a711b2324ee87750fd9fac2bc259c9e488da65fb31456f63744b19d2b38fd76333aad0fca334b0ee21913a7e5913f0fc6811
|
|
@@ -5,609 +5,696 @@
|
|
|
5
5
|
* Steffen Tiedemann Christensen, steffen@23company.com
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
-
|
|
137
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
var
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
var
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
199
|
-
|
|
219
|
+
o.minFileSizeErrorCallback(file, errorCount++);
|
|
220
|
+
return false;
|
|
200
221
|
}
|
|
201
222
|
if (typeof(o.maxFileSize)!=='undefined' && file.size>o.maxFileSize) {
|
|
202
|
-
|
|
203
|
-
|
|
223
|
+
o.maxFileSizeErrorCallback(file, errorCount++);
|
|
224
|
+
return false;
|
|
204
225
|
}
|
|
205
226
|
|
|
206
227
|
// directories have size == 0
|
|
207
|
-
if (
|
|
228
|
+
if (!$.getFromUniqueIdentifier($h.generateUniqueIdentifier(file))) {(function(){
|
|
208
229
|
var f = new ResumableFile($, file);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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
|
-
|
|
251
|
-
case 'retry':
|
|
252
|
-
$.resumableObj.fire('fileRetry', $);
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
283
|
+
};
|
|
256
284
|
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
$.
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
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
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
$.xhr.
|
|
380
|
-
|
|
381
|
-
|
|
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
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
465
|
+
$.preprocessFinished = function(){
|
|
466
|
+
$.preprocessState = 2;
|
|
467
|
+
$.send();
|
|
468
|
+
};
|
|
388
469
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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
|
-
|
|
405
|
-
|
|
485
|
+
// Set up request and listen for event
|
|
486
|
+
$.xhr = new XMLHttpRequest();
|
|
406
487
|
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
var
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
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
|
-
$.
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
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
|
-
//
|
|
507
|
-
|
|
508
|
-
|
|
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
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
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
|
-
|
|
534
|
-
|
|
535
|
-
|
|
624
|
+
// QUEUE
|
|
625
|
+
$.uploadNextChunk = function(){
|
|
626
|
+
var found = false;
|
|
536
627
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
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[
|
|
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
|
-
|
|
554
|
-
|
|
644
|
+
if(found) return(true);
|
|
645
|
+
}
|
|
555
646
|
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
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
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
670
|
+
if(!outstanding) {
|
|
671
|
+
// All chunks have been uploaded, complete
|
|
672
|
+
$.fire('complete');
|
|
673
|
+
}
|
|
674
|
+
return(false);
|
|
675
|
+
};
|
|
587
676
|
|
|
588
677
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
678
|
+
// PUBLIC METHODS FOR RESUMABLE.JS
|
|
679
|
+
$.assignBrowse = function(domNodes, isDirectory){
|
|
680
|
+
if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
|
|
592
681
|
|
|
593
|
-
|
|
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
|
-
|
|
685
|
+
input = domNode;
|
|
600
686
|
} else {
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
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.
|
|
610
|
-
|
|
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
|
-
|
|
626
|
-
|
|
712
|
+
appendFilesFromFileList(e.target.files,e);
|
|
713
|
+
e.target.value = '';
|
|
627
714
|
}, false);
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
715
|
+
});
|
|
716
|
+
};
|
|
717
|
+
$.assignDrop = function(domNodes){
|
|
718
|
+
if(typeof(domNodes.length)=='undefined') domNodes = [domNodes];
|
|
632
719
|
|
|
633
|
-
|
|
720
|
+
$h.each(domNodes, function(domNode) {
|
|
634
721
|
domNode.addEventListener('dragover', onDragOver, false);
|
|
635
722
|
domNode.addEventListener('drop', onDrop, false);
|
|
636
723
|
});
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
724
|
+
};
|
|
725
|
+
$.unAssignDrop = function(domNodes) {
|
|
726
|
+
if (typeof(domNodes.length) == 'undefined') domNodes = [domNodes];
|
|
640
727
|
|
|
641
|
-
|
|
728
|
+
$h.each(domNodes, function(domNode) {
|
|
642
729
|
domNode.removeEventListener('dragover', onDragOver);
|
|
643
730
|
domNode.removeEventListener('drop', onDrop);
|
|
644
731
|
});
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
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
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
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
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
|
|
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
|
|
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
|
+
})();
|