|
95 | 95 | }(window)); |
96 | 96 |
|
97 | 97 | /*jslint evil: true */ |
98 | | -/*global window, Image, Blob, XMLHttpRequest, URL, webkitURL, ActiveXObject */ |
| 98 | +/*global window, Image, URL, webkitURL, ActiveXObject */ |
99 | 99 |
|
100 | | -(function (window, jQuery, undef){ |
| 100 | +(function (window, undef){ |
101 | 101 | 'use strict'; |
102 | 102 |
|
103 | 103 | var |
|
110 | 110 | // https://github.com/blueimp/JavaScript-Load-Image/blob/master/load-image.js#L48 |
111 | 111 | apiURL = (window.createObjectURL && window) || (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL), |
112 | 112 |
|
| 113 | +Blob = window.Blob, |
113 | 114 | File = window.File, |
114 | 115 | FileReader = window.FileReader, |
115 | 116 | FormData = window.FormData, |
116 | 117 |
|
117 | | -html5 = !!(File && (FileReader && window.Uint8Array || FormData)) |
| 118 | + |
| 119 | +XMLHttpRequest = window.XMLHttpRequest, |
| 120 | +jQuery = window.jQuery, |
| 121 | + |
| 122 | +html5 = !!(File && (FileReader && (window.Uint8Array || FormData || XMLHttpRequest.prototype.sendAsBinary))) |
118 | 123 | && !(/safari\//.test(userAgent) && /windows/i.test(userAgent)), // BugFix: https://github.com/mailru/FileAPI/issues/25 |
119 | 124 |
|
120 | 125 | cors = html5 && ('withCredentials' in (new XMLHttpRequest)), |
|
269 | 274 | version: '2.0.0b', |
270 | 275 |
|
271 | 276 | cors: false, |
| 277 | +html5: true, |
272 | 278 | debug: false, |
273 | 279 | pingUrl: false, |
274 | 280 | multiFlash: false, |
|
317 | 323 | getXHR: function (){ |
318 | 324 | var xhr; |
319 | 325 |
|
320 | | -if( window.XMLHttpRequest ){ |
| 326 | +if( XMLHttpRequest ){ |
321 | 327 | xhr = new XMLHttpRequest; |
322 | 328 | } |
323 | 329 | else if( window.ActiveXObject ){ |
324 | | -try { xhr = new ActiveXObject('MSXML2.XMLHttp.3.0'); } catch (e) { } |
| 330 | +try { |
| 331 | +xhr = new ActiveXObject('MSXML2.XMLHttp.3.0'); |
| 332 | +} catch (e) { |
| 333 | +xhr = new ActiveXObject('Microsoft.XMLHTTP'); |
| 334 | +} |
325 | 335 | } |
326 | 336 |
|
327 | 337 | return xhr; |
|
999 | 1009 | , _fileOptions = _simpleClone(options) |
1000 | 1010 | ; |
1001 | 1011 |
|
| 1012 | +if( _file && _file.name === api.expando ){ |
| 1013 | +_file = null; |
| 1014 | +api.log('[warn] FileAPI.upload() — called without files'); |
| 1015 | +} |
| 1016 | + |
1002 | 1017 | if( ( proxyXHR.statusText != 'abort' || proxyXHR.current ) && data ){ |
1003 | 1018 | // Mark active job |
1004 | 1019 | _complete = false; |
|
1007 | 1022 | proxyXHR.currentFile = _file; |
1008 | 1023 |
|
1009 | 1024 | // Prepare file options |
1010 | | -options.prepare(_file, _fileOptions); |
| 1025 | +_file && options.prepare(_file, _fileOptions); |
1011 | 1026 |
|
1012 | 1027 | _this._getFormData(_fileOptions, data, function (form){ |
1013 | 1028 | if( !_loaded ){ |
|
1017 | 1032 |
|
1018 | 1033 | var xhr = new api.XHR(api.extend({}, _fileOptions, { |
1019 | 1034 |
|
1020 | | -upload: function (){ |
| 1035 | +upload: _file ? function (){ |
1021 | 1036 | // emit "fileupload" event |
1022 | 1037 | options.fileupload(_file, xhr, _fileOptions); |
1023 | | -}, |
| 1038 | +} : noop, |
1024 | 1039 |
|
1025 | | -progress: function (evt){ |
| 1040 | +progress: _file ? function (evt){ |
1026 | 1041 | if( !_fileLoaded ){ |
1027 | 1042 | // emit "fileprogress" event |
1028 | 1043 | options.fileprogress({ |
|
1038 | 1053 | , loaded: proxyXHR.loaded = (_loaded + data.size * (evt.loaded/evt.total))|0 |
1039 | 1054 | }, _file, xhr, _fileOptions); |
1040 | 1055 | } |
1041 | | -}, |
| 1056 | +} : noop, |
1042 | 1057 |
|
1043 | 1058 | complete: function (err){ |
| 1059 | +// fixed throttle event |
| 1060 | +_fileLoaded = true; |
| 1061 | + |
1044 | 1062 | _each(_xhrPropsExport, function (name){ |
1045 | 1063 | proxyXHR[name] = xhr[name]; |
1046 | 1064 | }); |
1047 | 1065 |
|
1048 | | -data.loaded= data.total; |
| 1066 | +if( _file ){ |
| 1067 | +data.loaded= data.total; |
1049 | 1068 |
|
1050 | | -// emulate 100% "progress" |
1051 | | -this.progress(data); |
| 1069 | +// emulate 100% "progress" |
| 1070 | +this.progress(data); |
1052 | 1071 |
|
1053 | | -// bytes loaded |
1054 | | -_loaded += data.size; // data.size != data.total, it's desirable fix this |
1055 | | -proxyXHR.loaded = _loaded; |
| 1072 | +// bytes loaded |
| 1073 | +_loaded += data.size; // data.size != data.total, it's desirable fix this |
| 1074 | +proxyXHR.loaded = _loaded; |
1056 | 1075 |
|
1057 | | -// emit "filecomplete" event |
1058 | | -options.filecomplete(err, xhr, _file, _fileOptions); |
1059 | | - |
1060 | | -// fixed throttle event |
1061 | | -_fileLoaded = true; |
| 1076 | +// emit "filecomplete" event |
| 1077 | +options.filecomplete(err, xhr, _file, _fileOptions); |
| 1078 | +} |
1062 | 1079 |
|
1063 | 1080 | // upload next file |
1064 | 1081 | _nextFile.call(_this); |
|
1086 | 1103 |
|
1087 | 1104 |
|
1088 | 1105 | // Append more files to the existing request |
1089 | | -proxyXHR.append = function (files) { |
| 1106 | +// first - add them to the queue head/tail |
| 1107 | +proxyXHR.append = function (files, first) { |
1090 | 1108 | files = api._getFilesDataArray([].concat(files)); |
1091 | 1109 |
|
1092 | 1110 | _each(files, function (data) { |
1093 | 1111 | _total += data.size; |
1094 | 1112 | proxyXHR.files.push(data.file); |
1095 | | -dataArray.push(data); |
| 1113 | +if (first) { |
| 1114 | +dataArray.unshift(data); |
| 1115 | +} else { |
| 1116 | +dataArray.push(data); |
| 1117 | +} |
1096 | 1118 | }); |
1097 | 1119 |
|
1098 | 1120 | if( _complete ){ |
|
1150 | 1172 | } |
1151 | 1173 | }); |
1152 | 1174 |
|
| 1175 | +if( !files.length ){ |
| 1176 | +// Create fake `file` object |
| 1177 | +files.push({ file: { name: api.expando } }); |
| 1178 | +} |
| 1179 | + |
1153 | 1180 | returnfiles; |
1154 | 1181 | }, |
1155 | 1182 |
|
|
1218 | 1245 | queue.next(); |
1219 | 1246 | }); |
1220 | 1247 | } |
1221 | | -else { |
| 1248 | +else if( filename !== api.expando ){ |
1222 | 1249 | Form.append(name, file, filename); |
1223 | 1250 | } |
1224 | 1251 | })(file); |
|
1630 | 1657 | // @configuration |
1631 | 1658 | if( !api.flashUrl ){ api.flashUrl = api.staticPath + 'FileAPI.flash.swf'; } |
1632 | 1659 | if( !api.flashImageUrl ){ api.flashImageUrl = api.staticPath + 'FileAPI.flash.image.swf'; } |
1633 | | -})(window, window.jQuery); |
| 1660 | +})(window, void 0); |
1634 | 1661 |
|
1635 | 1662 | /*global window, FileAPI, document */ |
1636 | 1663 |
|
|
2032 | 2059 | append: function (name, blob, file, type){ |
2033 | 2060 | this.items.push({ |
2034 | 2061 | name: name |
2035 | | -, blob: blob && blob.blob || blob |
2036 | | -, file: file || blob.name |
2037 | | -, type:type || blob.type |
| 2062 | +, blob: blob && blob.blob || (blob == void 0 ? '' : blob) |
| 2063 | +, file: blob && (file || blob.name) |
| 2064 | +, type:blob && (type || blob.type) |
2038 | 2065 | }); |
2039 | 2066 | }, |
2040 | 2067 |
|
|
2054 | 2081 | api.log('FileAPI.Form.toHtmlData'); |
2055 | 2082 | this.toHtmlData(fn); |
2056 | 2083 | } |
2057 | | -else if( this.multipart ){ |
| 2084 | +else if( this.multipart || !FormData ){ |
2058 | 2085 | api.log('FileAPI.Form.toMultipartData'); |
2059 | 2086 | this.toMultipartData(fn); |
2060 | 2087 | } |
|
2134 | 2161 | } |
2135 | 2162 |
|
2136 | 2163 | data.start = -1; |
2137 | | -data.end = -1; |
| 2164 | +data.end = data.file.FileAPIReadPosition || -1; |
2138 | 2165 | data.retry = 0; |
2139 | 2166 | }); |
2140 | 2167 | }, |
|
2164 | 2191 |
|
2165 | 2192 | toMultipartData: function (fn){ |
2166 | 2193 | this._to([], fn, function (file, data, queue, boundary){ |
| 2194 | +queue.inc(); |
2167 | 2195 | _converFile(file, function (file, blob){ |
2168 | 2196 | data.push( |
2169 | 2197 | '--_' + boundary + ('\r\nContent-Disposition: form-data; name="'+ file.name +'"'+ (file.file ? '; filename="'+ encodeURIComponent(file.file) +'"' : '') |
|
2172 | 2200 | + '\r\n'+ (file.file ? blob : encodeURIComponent(blob)) |
2173 | 2201 | + '\r\n') |
2174 | 2202 | ); |
| 2203 | +queue.next(); |
2175 | 2204 | }); |
2176 | 2205 | }, api.expando); |
2177 | 2206 | } |
|
2182 | 2211 | var blob = file.blob, filename = file.file; |
2183 | 2212 |
|
2184 | 2213 | if( filename ){ |
| 2214 | +if( !blob.toDataURL ){ |
| 2215 | +// The Blob is not an image. |
| 2216 | +api.readAsBinaryString(blob, function (evt){ |
| 2217 | +if( evt.type == 'load' ){ |
| 2218 | +fn(file, evt.result); |
| 2219 | +} |
| 2220 | +}); |
| 2221 | +return; |
| 2222 | +} |
| 2223 | + |
2185 | 2224 | var |
2186 | 2225 | mime = { 'image/jpeg': '.jpe?g', 'image/png': '.png' } |
2187 | 2226 | , type = mime[file.type] ? file.type : 'image/png' |
|
2190 | 2229 | ; |
2191 | 2230 |
|
2192 | 2231 | if( !filename.match(new RegExp(ext+'$', 'i')) ){ |
| 2232 | +// Does not change the current extension, but add a new one. |
2193 | 2233 | filename += ext.replace('?', ''); |
2194 | 2234 | } |
2195 | 2235 |
|
|
2202 | 2242 | }, type, quality); |
2203 | 2243 | } |
2204 | 2244 | else { |
2205 | | -blob = api.toBinaryString(blob.toDataURL(type, quality)); |
| 2245 | +fn(file, api.toBinaryString(blob.toDataURL(type, quality))); |
2206 | 2246 | } |
2207 | 2247 | } |
2208 | | - |
2209 | | -returnblob; |
| 2248 | +else { |
| 2249 | +fn(file, blob); |
| 2250 | +} |
2210 | 2251 | } |
2211 | 2252 |
|
2212 | 2253 |
|
|
2294 | 2335 | }, |
2295 | 2336 |
|
2296 | 2337 | _send: function (options, data){ |
| 2338 | + |
2297 | 2339 | var _this = this, xhr, uid = _this.uid, url = options.url; |
2298 | 2340 |
|
2299 | 2341 | api.log('XHR._send:', data); |
|
2352 | 2394 | } |
2353 | 2395 | else { |
2354 | 2396 | // html5 |
| 2397 | +if (this.xhr && this.xhr.aborted) { |
| 2398 | +api.log("Error: already aborted"); |
| 2399 | +return; |
| 2400 | +} |
2355 | 2401 | xhr = _this.xhr = api.getXHR(); |
2356 | 2402 |
|
2357 | 2403 | if (data.params) { |
|
2387 | 2433 | } |
2388 | 2434 |
|
2389 | 2435 | xhr.onreadystatechange = function (){ |
| 2436 | +var lkb = parseInt(xhr.getResponseHeader('X-Last-Known-Byte'), 10); |
| 2437 | + |
2390 | 2438 | _this.status = xhr.status; |
2391 | 2439 | _this.statusText = xhr.statusText; |
2392 | 2440 | _this.readyState = xhr.readyState; |
|
2411 | 2459 | options.pause(data.file, options); |
2412 | 2460 |
|
2413 | 2461 | // smart restart if server reports about the last known byte |
2414 | | -var lkb = xhr.getResponseHeader('X-Last-Known-Byte'); |
2415 | 2462 | api.log("X-Last-Known-Byte: " + lkb); |
2416 | | - |
2417 | 2463 | if (lkb) { |
2418 | 2464 | data.end = parseInt(lkb, 10); |
2419 | 2465 | } else { |
|
2436 | 2482 | _this.end(xhr.status); |
2437 | 2483 | } else { |
2438 | 2484 | // next chunk |
| 2485 | + |
| 2486 | +// shift position if server reports about the last known byte |
| 2487 | +api.log("X-Last-Known-Byte: " + lkb); |
| 2488 | +if (lkb) { |
| 2489 | +data.end = lkb; |
| 2490 | +} |
| 2491 | +data.file.FileAPIReadPosition = data.end; |
| 2492 | + |
2439 | 2493 | setTimeout(function () { |
2440 | 2494 | _this._send(options, data); |
2441 | 2495 | }, 0); |
|
2446 | 2500 | }; |
2447 | 2501 |
|
2448 | 2502 | data.start = data.end + 1; |
2449 | | -data.end = Math.min(data.start + options.chunkSize, data.size ) - 1; |
| 2503 | +data.end = Math.max(Math.min(data.start + options.chunkSize, data.size ) - 1, data.start); |
2450 | 2504 |
|
2451 | 2505 | var slice; |
2452 | 2506 | (slice = 'slice') in data.file || (slice = 'mozSlice') in data.file || (slice = 'webkitSlice') in data.file; |
|
0 commit comments