diff --git a/public/javascripts/jquery.html5_upload.js b/public/javascripts/jquery.html5_upload.js new file mode 100644 index 000000000..f9392921f --- /dev/null +++ b/public/javascripts/jquery.html5_upload.js @@ -0,0 +1,210 @@ +(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 + + STATUSES: { + 'STARTED': 'Запуск', + 'PROGRESS': 'Загрузка', + 'LOADED': 'Обработка', + 'FINISHED': 'Завершено' + }, + + 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("post", typeof(options.url) == "function" ? options.url() : options.url, true); + xhr.setRequestHeader("Cache-Control", "no-cache"); + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + xhr.setRequestHeader("X-File-Name", file.fileName); + xhr.setRequestHeader("X-File-Size", file.fileSize); + + if (!options.sendBoundary) { + xhr.setRequestHeader("Content-Type", "multipart/form-data"); + xhr.send(file); + } + + 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)+'"'; + builder += '; filename="' + file.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 += crlf; + + 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); +