(function($) { jQuery.fn.html5_upload = function(options) { var available_events = ['onStart', 'onStartOne', 'onProgress', 'onFinishOne', 'onFinish', 'onError']; var options = jQuery.extend({ onStart: function(event, total) { return true; }, onStartOne: function(event, name, number, total) { return true; }, onProgress: function(event, progress, name, number, total) { }, onFinishOne: function(event, response, name, number, total) { }, onFinish: function(event, total) { }, onError: function(event, name, error) { }, onBrowserIncompatible: function() { alert("Sorry, but your browser is incompatible with uploading files using HTML5 (at least, with current preferences.\n Please install the latest version of Firefox, Safari or Chrome"); }, autostart: true, autoclear: true, stopOnFirstError: false, sendBoundary: false, fieldName: 'user_file[]',//ignore if sendBoundary is false method: 'post', STATUSES: { 'STARTED': 'Запуск', 'PROGRESS': 'Загрузка', 'LOADED': 'Обработка', 'FINISHED': 'Завершено' }, headers: { "Cache-Control":"no-cache", "X-Requested-With":"XMLHttpRequest", "X-File-Name": function(file){return file.fileName}, "X-File-Size": function(file){return file.fileSize}, "Content-Type": function(file){ if (!options.sendBoundary) return 'multipart/form-data'; return false; } }, setName: function(text) {}, setStatus: function(text) {}, setProgress: function(value) {}, genName: function(file, number, total) { return file + "(" + (number+1) + " из " + total + ")"; }, genStatus: function(progress, finished) { if (finished) { return options.STATUSES['FINISHED']; } if (progress == 0) { return options.STATUSES['STARTED']; } else if (progress == 1) { return options.STATUSES['LOADED']; } else { return options.STATUSES['PROGRESS']; } }, genProgress: function(loaded, total) { return loaded / total; } }, options); function upload() { var files = this.files; var total = files.length; var $this = $(this); if (!$this.triggerHandler('html5_upload.onStart', [total])) { return false; } this.disabled = true; var uploaded = 0; var xhr = this.html5_upload['xhr']; this.html5_upload['continue_after_abort'] = true; function upload_file(number) { if (number == total) { $this.trigger('html5_upload.onFinish', [total]); options.setStatus(options.genStatus(1, true)); $this.attr("disabled", false); if (options.autoclear) { $this.val(""); } return; } var file = files[number]; if (!$this.triggerHandler('html5_upload.onStartOne', [file.fileName, number, total])) { return upload_file(number+1); } options.setStatus(options.genStatus(0)); options.setName(options.genName(file.fileName, number, total)); options.setProgress(options.genProgress(0, file.fileSize)); xhr.upload['onprogress'] = function(rpe) { $this.trigger('html5_upload.onProgress', [rpe.loaded / rpe.total, file.fileName, number, total]); options.setStatus(options.genStatus(rpe.loaded / rpe.total)); options.setProgress(options.genProgress(rpe.loaded, rpe.total)); }; xhr.onload = function(load) { $this.trigger('html5_upload.onFinishOne', [xhr.responseText, file.fileName, number, total]); options.setStatus(options.genStatus(1, true)); options.setProgress(options.genProgress(file.fileSize, file.fileSize)); upload_file(number+1); }; xhr.onabort = function() { if ($this[0].html5_upload['continue_after_abort']) { upload_file(number+1); } else { $this.attr("disabled", false); if (options.autoclear) { $this.val(""); } } }; xhr.onerror = function(e) { $this.trigger('html5_upload.onError', [file.fileName, e]); if (!options.stopOnFirstError) { upload_file(number+1); } }; xhr.open(options.method, typeof(options.url) == "function" ? options.url(number) : options.url, true); $.each(options.headers,function(key,val){ val = typeof(val) == "function" ? val(file) : val; // resolve value if (val === false) return true; // if resolved value is boolean false, do not send this header xhr.setRequestHeader(key, val); }); if (!options.sendBoundary) { xhr.send(file); } else { if (window.FormData) {//Many thanks to scottt.tw var f = new FormData(); f.append(typeof(options.fieldName) == "function" ? options.fieldName() : options.fieldName, file); xhr.send(f); } else if (file.getAsBinary) {//Thanks to jm.schelcher var boundary = '------multipartformboundary' + (new Date).getTime(); var dashdash = '--'; var crlf = '\r\n'; /* Build RFC2388 string. */ var builder = ''; builder += dashdash; builder += boundary; builder += crlf; builder += 'Content-Disposition: form-data; name="'+(typeof(options.fieldName) == "function" ? options.fieldName() : options.fieldName)+'"'; //thanks to oyejo...@gmail.com for this fix fileName = unescape(encodeURIComponent(file.fileName)); //encode_utf8 builder += '; filename="' + fileName + '"'; builder += crlf; builder += 'Content-Type: application/octet-stream'; builder += crlf; builder += crlf; /* Append binary data. */ builder += file.getAsBinary(); builder += crlf; /* Write boundary. */ builder += dashdash; builder += boundary; builder += dashdash; builder += crlf; xhr.setRequestHeader('content-type', 'multipart/form-data; boundary=' + boundary); xhr.sendAsBinary(builder); } else { options.onBrowserIncompatible(); } } } upload_file(0); return true; } return this.each(function() { this.html5_upload = { xhr: new XMLHttpRequest(), continue_after_abort: true }; if (options.autostart) { $(this).bind('change', upload); } for (event in available_events) { if (options[available_events[event]]) { $(this).bind("html5_upload."+available_events[event], options[available_events[event]]); } } $(this) .bind('html5_upload.start', upload) .bind('html5_upload.cancelOne', function() { this.html5_upload['xhr'].abort(); }) .bind('html5_upload.cancelAll', function() { this.html5_upload['continue_after_abort'] = false; this.html5_upload['xhr'].abort(); }) .bind('html5_upload.destroy', function() { this.html5_upload['continue_after_abort'] = false; this.xhr.abort(); delete this.html5_upload; $(this).unbind('html5_upload.*').unbind('change', upload); }); }); }; })(jQuery);