From fe98da5ae73fc363b6d94b417b893ead8df84a0a Mon Sep 17 00:00:00 2001 From: Patrick Schmitz Date: Sat, 19 May 2012 17:17:26 +0200 Subject: [PATCH 01/33] fixed loading path for images with asset pipeline in stylesheet --- vendor/assets/stylesheets/jquery.fileupload-ui.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.css b/vendor/assets/stylesheets/jquery.fileupload-ui.css index e36a93d..603c19f 100644 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.css +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.css @@ -37,7 +37,7 @@ width: 200px; } .progress-animated .bar { - background: url(../img/progressbar.gif) !important; + background: image-url('images/progressbar.gif') !important; filter: none; } .fileupload-loading { @@ -45,7 +45,7 @@ left: 50%; width: 128px; height: 128px; - background: url(../img/loading.gif) center no-repeat; + background: image-url('images/loading.gif') center no-repeat; display: none; } .fileupload-processing .fileupload-loading { From 3a284da3301a5a061f4fe17378b50874c5f2ff67 Mon Sep 17 00:00:00 2001 From: Patrick Schmitz Date: Mon, 21 May 2012 19:11:18 +0200 Subject: [PATCH 02/33] fixed image url path --- ...{jquery.fileupload-ui.css => jquery.fileupload-ui.css.erb} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename vendor/assets/stylesheets/{jquery.fileupload-ui.css => jquery.fileupload-ui.css.erb} (90%) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.css b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb similarity index 90% rename from vendor/assets/stylesheets/jquery.fileupload-ui.css rename to vendor/assets/stylesheets/jquery.fileupload-ui.css.erb index 603c19f..b4daf70 100644 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.css +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb @@ -37,7 +37,7 @@ width: 200px; } .progress-animated .bar { - background: image-url('images/progressbar.gif') !important; + background: url(<%= asset_path 'progressbar.gif' %>) !important; filter: none; } .fileupload-loading { @@ -45,7 +45,7 @@ left: 50%; width: 128px; height: 128px; - background: image-url('images/loading.gif') center no-repeat; + background: url(<%= asset_path 'images/loading.gif' %>) center no-repeat; display: none; } .fileupload-processing .fileupload-loading { From 56007aac057db6e33c83fc9cc5159138984e776c Mon Sep 17 00:00:00 2001 From: Jason Thomas Date: Thu, 28 Jun 2012 22:31:38 -0600 Subject: [PATCH 03/33] Fix asset path --- vendor/assets/stylesheets/jquery.fileupload-ui.css.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb index b4daf70..7b2d101 100644 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb @@ -45,7 +45,7 @@ left: 50%; width: 128px; height: 128px; - background: url(<%= asset_path 'images/loading.gif' %>) center no-repeat; + background: url(<%= asset_path 'loading.gif' %>) center no-repeat; display: none; } .fileupload-processing .fileupload-loading { From 97708d3038cd24b7d6cb696aa18e460348b77254 Mon Sep 17 00:00:00 2001 From: Dave Kroondyk Date: Thu, 26 Jul 2012 22:04:01 -0400 Subject: [PATCH 04/33] update javascripts to latest --- .../jquery-fileupload/jquery.fileupload-ui.js | 72 ++++++++---- .../jquery-fileupload/jquery.fileupload.js | 49 +++++--- .../vendor/canvas-to-blob.js | 107 ++++++++---------- .../vendor/jquery.ui.widget.js | 4 +- .../jquery-fileupload/vendor/load-image.js | 57 ++++++---- 5 files changed, 168 insertions(+), 121 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js index 57ef7b6..1cad20e 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 6.8.2 + * jQuery File Upload User Interface Plugin 6.9.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -128,9 +128,10 @@ .find('.progress').addClass( !$.support.transition && 'progress-animated' ) + .attr('aria-valuenow', 100) .find('.bar').css( 'width', - parseInt(100, 10) + '%' + '100%' ); } return that._trigger('sent', e, data); @@ -150,7 +151,6 @@ function () { var node = $(this); template = that._renderDownload([file]) - .css('height', node.height()) .replaceAll(node); that._forceReflow(template); that._transition(template).done( @@ -227,26 +227,34 @@ // Callback for upload progress events: progress: function (e, data) { if (data.context) { - data.context.find('.bar').css( - 'width', - parseInt(data.loaded / data.total * 100, 10) + '%' - ); + var progress = parseInt(data.loaded / data.total * 100, 10); + data.context.find('.progress') + .attr('aria-valuenow', progress) + .find('.bar').css( + 'width', + progress + '%' + ); } }, // Callback for global upload progress events: progressall: function (e, data) { - var $this = $(this); - $this.find('.fileupload-progress') + var $this = $(this), + progress = parseInt(data.loaded / data.total * 100, 10), + globalProgressNode = $this.find('.fileupload-progress'), + extendedProgressNode = globalProgressNode + .find('.progress-extended'); + if (extendedProgressNode.length) { + extendedProgressNode.html( + $this.data('fileupload')._renderExtendedProgress(data) + ); + } + globalProgressNode + .find('.progress') + .attr('aria-valuenow', progress) .find('.bar').css( 'width', - parseInt(data.loaded / data.total * 100, 10) + '%' - ).end() - .find('.progress-extended').each(function () { - $(this).html( - $this.data('fileupload') - ._renderExtendedProgress(data) - ); - }); + progress + '%' + ); }, // Callback for uploads start, equivalent to the global ajaxStart event: start: function (e) { @@ -262,7 +270,9 @@ var that = $(this).data('fileupload'); that._transition($(this).find('.fileupload-progress')).done( function () { - $(this).find('.bar').css('width', '0%'); + $(this).find('.progress') + .attr('aria-valuenow', '0') + .find('.bar').css('width', '0%'); $(this).find('.progress-extended').html(' '); that._trigger('stopped', e); } @@ -519,8 +529,8 @@ }, _forceReflow: function (node) { - this._reflow = $.support.transition && - node.length && node[0].offsetWidth; + return $.support.transition && node.length && + node[0].offsetWidth; }, _transition: function (node) { @@ -653,10 +663,32 @@ } }, + _stringToRegExp: function (str) { + var parts = str.split('/'), + modifiers = parts.pop(); + parts.shift(); + return new RegExp(parts.join('/'), modifiers); + }, + + _initRegExpOptions: function () { + var options = this.options; + if ($.type(options.acceptFileTypes) === 'string') { + options.acceptFileTypes = this._stringToRegExp( + options.acceptFileTypes + ); + } + if ($.type(options.previewSourceFileTypes) === 'string') { + options.previewSourceFileTypes = this._stringToRegExp( + options.previewSourceFileTypes + ); + } + }, + _initSpecialOptions: function () { parentWidget.prototype._initSpecialOptions.call(this); this._initFilesContainer(); this._initTemplates(); + this._initRegExpOptions(); }, _create: function () { diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js index 40661c1..4bbd287 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.11.2 + * jQuery File Upload Plugin 5.13 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -210,10 +210,10 @@ if (typeof options.formData === 'function') { return options.formData(options.form); } - if ($.isArray(options.formData)) { + if ($.isArray(options.formData)) { return options.formData; } - if (options.formData) { + if (options.formData) { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -530,6 +530,10 @@ ub + i * mcs, ub + (i + 1) * mcs ); + // Expose the chunk index: + o.chunkIndex = i; + // Expose the number of chunks: + o.chunksNumber = n; // Store the current chunk size, as the blob itself // will be dereferenced after data processing: o.chunkSize = o.blob.size; @@ -776,19 +780,30 @@ } }, + _getFileInputFiles: function (fileInput) { + fileInput = $(fileInput); + var files = $.each($.makeArray(fileInput.prop('files')), this._normalizeFile), + value; + if (!files.length) { + value = fileInput.prop('value'); + if (!value) { + return []; + } + // If the files property is not available, the browser does not + // support the File API and we add a pseudo File object with + // the input value as name with path information removed: + files = [{name: value.replace(/^.*\\/, '')}]; + } + return files; + }, + _onChange: function (e) { var that = e.data.fileupload, data = { - files: $.each($.makeArray(e.target.files), that._normalizeFile), fileInput: $(e.target), form: $(e.target.form) }; - if (!data.files.length) { - // If the files property is not available, the browser does not - // support the File API and we add a pseudo File object with - // the input value as name with path information removed: - data.files = [{name: e.target.value.replace(/^.*\\/, '')}]; - } + data.files = that._getFileInputFiles(data.fileInput); if (that.options.replaceFileInput) { that._replaceFileInput(data.fileInput); } @@ -838,7 +853,7 @@ return false; } if (dataTransfer) { - dataTransfer.dropEffect = dataTransfer.effectAllowed = 'copy'; + dataTransfer.dropEffect = 'copy'; } e.preventDefault(); }, @@ -925,7 +940,11 @@ if (!data || this.options.disabled) { return; } - data.files = $.each($.makeArray(data.files), this._normalizeFile); + if (data.fileInput && !data.files) { + data.files = this._getFileInputFiles(data.fileInput); + } else { + data.files = $.each($.makeArray(data.files), this._normalizeFile); + } this._onAdd(null, data); }, @@ -936,7 +955,11 @@ // The method returns a Promise object for the file upload call. send: function (data) { if (data && !this.options.disabled) { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + if (data.fileInput && !data.files) { + data.files = this._getFileInputFiles(data.fileInput); + } else { + data.files = $.each($.makeArray(data.files), this._normalizeFile); + } if (data.files.length) { return this._onSend(null, data); } diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js index 7078fc9..f4959cf 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js @@ -1,5 +1,5 @@ /* - * JavaScript Canvas to Blob 1.0.1 + * JavaScript Canvas to Blob 2.0.1 * https://github.com/blueimp/JavaScript-Canvas-to-Blob * * Copyright 2012, Sebastian Tschan @@ -15,71 +15,56 @@ /*jslint nomen: true, regexp: true */ /*global window, atob, ArrayBuffer, Uint8Array, define */ -(function ($) { +(function (window) { 'use strict'; - - var BlobBuilder = window.MozBlobBuilder || - window.WebKitBlobBuilder || window.BlobBuilder, - blobTypes = /^image\/(jpeg|png)$/, - - // Converts a canvas element to a Blob or File object: - canvasToBlob = function (canvas, callback, options) { - options = options || {}; - if (canvas.toBlob) { - canvas.toBlob(callback, options.type); - return true; - } else if (canvas.mozGetAsFile) { - var name = options.name; - callback(canvas.mozGetAsFile( - (blobTypes.test(options.type) && name) || - ((name && name.replace(/\..+$/, '')) || 'blob') + '.png', - options.type - )); - return true; - } else if (canvas.toDataURL && BlobBuilder && window.atob && - window.ArrayBuffer && window.Uint8Array) { - callback(canvasToBlob.dataURItoBlob( - canvas.toDataURL(options.type) - )); - return true; - } - return false; - }; - - // Converts a dataURI to a Blob: - canvasToBlob.dataURItoBlob = function (dataURI) { - var byteString, - arrayBuffer, - intArray, - i, - bb, - mimeString; - if (dataURI.split(',')[0].indexOf('base64') >= 0) { - // Convert base64 to raw binary data held in a string: - byteString = atob(dataURI.split(',')[1]); - } else { - // Convert base64/URLEncoded data component to raw binary data: - byteString = decodeURIComponent(dataURI.split(',')[1]); + var CanvasPrototype = window.HTMLCanvasElement && + window.HTMLCanvasElement.prototype, + BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || + window.MozBlobBuilder || window.MSBlobBuilder, + dataURLtoBlob = BlobBuilder && window.atob && window.ArrayBuffer && + window.Uint8Array && function (dataURI) { + var byteString, + arrayBuffer, + intArray, + i, + bb, + mimeString; + if (dataURI.split(',')[0].indexOf('base64') >= 0) { + // Convert base64 to raw binary data held in a string: + byteString = atob(dataURI.split(',')[1]); + } else { + // Convert base64/URLEncoded data component to raw binary data: + byteString = decodeURIComponent(dataURI.split(',')[1]); + } + // Write the bytes of the string to an ArrayBuffer: + arrayBuffer = new ArrayBuffer(byteString.length); + intArray = new Uint8Array(arrayBuffer); + for (i = 0; i < byteString.length; i += 1) { + intArray[i] = byteString.charCodeAt(i); + } + // Write the ArrayBuffer to a blob: + bb = new BlobBuilder(); + bb.append(arrayBuffer); + // Separate out the mime component: + mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; + return bb.getBlob(mimeString); + }; + if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { + if (CanvasPrototype.mozGetAsFile) { + CanvasPrototype.toBlob = function (callback, type) { + callback(this.mozGetAsFile('blob', type)); + }; + } else if (CanvasPrototype.toDataURL && dataURLtoBlob) { + CanvasPrototype.toBlob = function (callback, type) { + callback(dataURLtoBlob(this.toDataURL(type))); + }; } - // Write the bytes of the string to an ArrayBuffer: - arrayBuffer = new ArrayBuffer(byteString.length); - intArray = new Uint8Array(arrayBuffer); - for (i = 0; i < byteString.length; i += 1) { - intArray[i] = byteString.charCodeAt(i); - } - // Write the ArrayBuffer to a blob: - bb = new BlobBuilder(); - bb.append(arrayBuffer); - // Separate out the mime component: - mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - return bb.getBlob(mimeString); - }; - + } if (typeof define !== 'undefined' && define.amd) { define(function () { - return canvasToBlob; + return dataURLtoBlob; }); } else { - $.canvasToBlob = canvasToBlob; + window.dataURLtoBlob = dataURLtoBlob; } }(this)); diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js index 9da8673..b980122 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,8 +1,8 @@ /* - * jQuery UI Widget 1.8.18+amd + * jQuery UI Widget 1.8.22+amd * https://github.com/blueimp/jQuery-File-Upload * - * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) * Dual licensed under the MIT or GPL Version 2 licenses. * http://jquery.org/license * diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js index e0caac3..4697f68 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js @@ -1,5 +1,5 @@ /* - * JavaScript Load Image 1.1.4 + * JavaScript Load Image 1.2.1 * https://github.com/blueimp/JavaScript-Load-Image * * Copyright 2011, Sebastian Tschan @@ -24,7 +24,7 @@ oUrl; img.onerror = callback; img.onload = function () { - if (oUrl) { + if (oUrl && !(options && options.noRevoke)) { loadImage.revokeObjectURL(oUrl); } callback(loadImage.scale(img, options)); @@ -40,46 +40,53 @@ if (url) { img.src = url; return img; - } else { - return loadImage.readFile(file, function (url) { - img.src = url; - }); } + return loadImage.readFile(file, function (url) { + img.src = url; + }); }, + // The check for URL.revokeObjectURL fixes an issue with Opera 12, + // which provides URL.createObjectURL but doesn't properly implement it: urlAPI = (window.createObjectURL && window) || - (window.URL && URL) || (window.webkitURL && webkitURL); + (window.URL && URL.revokeObjectURL && URL) || + (window.webkitURL && webkitURL); - // Scales the given image (img HTML element) + // Scales the given image (img or canvas HTML element) // using the given options. - // Returns a canvas object if the canvas option is true - // and the browser supports canvas, else the scaled image: + // Returns a canvas object if the browser supports canvas + // and the canvas option is true or a canvas object is passed + // as image, else the scaled image: loadImage.scale = function (img, options) { options = options || {}; var canvas = document.createElement('canvas'), + width = img.width, + height = img.height, scale = Math.max( - (options.minWidth || img.width) / img.width, - (options.minHeight || img.height) / img.height + (options.minWidth || width) / width, + (options.minHeight || height) / height ); if (scale > 1) { - img.width = parseInt(img.width * scale, 10); - img.height = parseInt(img.height * scale, 10); + width = parseInt(width * scale, 10); + height = parseInt(height * scale, 10); } scale = Math.min( - (options.maxWidth || img.width) / img.width, - (options.maxHeight || img.height) / img.height + (options.maxWidth || width) / width, + (options.maxHeight || height) / height ); if (scale < 1) { - img.width = parseInt(img.width * scale, 10); - img.height = parseInt(img.height * scale, 10); + width = parseInt(width * scale, 10); + height = parseInt(height * scale, 10); } - if (!options.canvas || !canvas.getContext) { - return img; + if (img.getContext || (options.canvas && canvas.getContext)) { + canvas.width = width; + canvas.height = height; + canvas.getContext('2d') + .drawImage(img, 0, 0, width, height); + return canvas; } - canvas.width = img.width; - canvas.height = img.height; - canvas.getContext('2d') - .drawImage(img, 0, 0, img.width, img.height); - return canvas; + img.width = width; + img.height = height; + return img; }; loadImage.createObjectURL = function (file) { From d9619f644bbb53b367cc7a86f498aa6cff1e25f9 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Tue, 31 Jul 2012 16:09:51 +0800 Subject: [PATCH 05/33] update jquery plugin version in readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8ccc9a3..c581114 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [jQuery-File-Plugin](https://github.com/blueimp/jQuery-File-Upload) is a file upload plugin written by [Sebastian Tschan](https://github.com/blueimp). jQuery File Upload features multiple file selection, drag&drop support, progress bars and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. -jquery-fileupload-rails is a library that integrates jQuery File Upload for Rails 3.1 Asset Pipeline. +jquery-fileupload-rails is a library that integrates jQuery File Upload for Rails 3.1 Asset Pipeline (Rails 3.2 supported). -jquery-fileupload-rails is currently using jQuery-File-Plugin version 6.7.0. +jquery-fileupload-rails is currently using jQuery-File-Plugin version 6.9.2. ## Installing Gem From 23da13dabe9a97415459fdca3131160067a36cb3 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Tue, 31 Jul 2012 16:10:34 +0800 Subject: [PATCH 06/33] bump version --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index f057966..6a9e204 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.0" + VERSION = "0.3.1" end end end From 8ba72303f2b54b65bfc44c2779a0921c84392280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lipski?= Date: Sun, 19 Aug 2012 09:54:47 +0200 Subject: [PATCH 07/33] Update JavaScript assets. --- .../jquery-fileupload/jquery.fileupload-ui.js | 23 ++- .../jquery-fileupload/jquery.fileupload.js | 182 ++++++++++++++---- .../jquery.iframe-transport.js | 3 +- .../vendor/canvas-to-blob.js | 22 ++- .../vendor/jquery.ui.widget.js | 2 +- 5 files changed, 178 insertions(+), 54 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js index 1cad20e..4c36f00 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 6.9.3 + * jQuery File Upload User Interface Plugin 6.9.4 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -89,7 +89,7 @@ files = data.files; $(this).fileupload('process', data).done(function () { that._adjustMaxNumberOfFiles(-files.length); - data.isAdjusted = true; + data.maxNumberOfFilesAdjusted = true; data.files.valid = data.isValidated = that._validate(files); data.context = that._renderUpload(files).data('data', data); options.filesContainer[ @@ -112,8 +112,9 @@ send: function (e, data) { var that = $(this).data('fileupload'); if (!data.isValidated) { - if (!data.isAdjusted) { + if (!data.maxNumberOfFilesAdjusted) { that._adjustMaxNumberOfFiles(-data.files.length); + data.maxNumberOfFilesAdjusted = true; } if (!that._validate(data.files)) { return false; @@ -163,6 +164,17 @@ ); }); } else { + if ($.isArray(data.result)) { + $.each(data.result, function (index, file) { + if (data.maxNumberOfFilesAdjusted && file.error) { + that._adjustMaxNumberOfFiles(1); + } else if (!data.maxNumberOfFilesAdjusted && + !file.error) { + that._adjustMaxNumberOfFiles(-1); + } + }); + data.maxNumberOfFilesAdjusted = true; + } template = that._renderDownload(data.result) .appendTo(that.options.filesContainer); that._forceReflow(template); @@ -178,7 +190,9 @@ fail: function (e, data) { var that = $(this).data('fileupload'), template; - that._adjustMaxNumberOfFiles(data.files.length); + if (data.maxNumberOfFilesAdjusted) { + that._adjustMaxNumberOfFiles(data.files.length); + } if (data.context) { data.context.each(function (index) { if (data.errorThrown !== 'abort') { @@ -209,7 +223,6 @@ } }); } else if (data.errorThrown !== 'abort') { - that._adjustMaxNumberOfFiles(-data.files.length); data.context = that._renderUpload(data.files) .appendTo(that.options.filesContainer) .data('data', data); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js index 4bbd287..0ec51b6 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.13 + * jQuery File Upload Plugin 5.16.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -447,6 +447,9 @@ if (options.type !== 'POST' && options.type !== 'PUT') { options.type = 'POST'; } + if (!options.formAcceptCharset) { + options.formAcceptCharset = options.form.attr('accept-charset'); + } }, _getAJAXSettings: function (data) { @@ -663,9 +666,15 @@ options.limitConcurrentUploads > that._sending) { // Start the next queued upload, // that has not been aborted: - var nextSlot = that._slots.shift(); + var nextSlot = that._slots.shift(), + isPending; while (nextSlot) { - if (!nextSlot.isRejected()) { + // jQuery 1.6 doesn't provide .state(), + // while jQuery 1.8+ removed .isRejected(): + isPending = nextSlot.state ? + nextSlot.state() === 'pending' : + !nextSlot.isRejected(); + if (isPending) { nextSlot.resolve(); break; } @@ -693,7 +702,7 @@ var args = [undefined, 'abort', 'abort']; if (!jqXHR) { if (slot) { - slot.rejectWith(args); + slot.rejectWith(pipe, args); } return send(false, args); } @@ -748,14 +757,6 @@ return result; }, - // File Normalization for Gecko 1.9.1 (Firefox 3.5) support: - _normalizeFile: function (index, file) { - if (file.name === undefined && file.size === undefined) { - file.name = file.fileName; - file.size = file.fileSize; - } - }, - _replaceFileInput: function (input) { var inputClone = input.clone(true); $('
').append(inputClone)[0].reset(); @@ -780,21 +781,90 @@ } }, + _handleFileTreeEntry: function (entry, path) { + var that = this, + dfd = $.Deferred(), + errorHandler = function () { + dfd.reject(); + }, + dirReader; + path = path || ''; + if (entry.isFile) { + entry.file(function (file) { + file.relativePath = path; + dfd.resolve(file); + }, errorHandler); + } else if (entry.isDirectory) { + dirReader = entry.createReader(); + dirReader.readEntries(function (entries) { + that._handleFileTreeEntries( + entries, + path + entry.name + '/' + ).done(function (files) { + dfd.resolve(files); + }).fail(errorHandler); + }, errorHandler); + } else { + errorHandler(); + } + return dfd.promise(); + }, + + _handleFileTreeEntries: function (entries, path) { + var that = this; + return $.when.apply( + $, + $.map(entries, function (entry) { + return that._handleFileTreeEntry(entry, path); + }) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + + _getDroppedFiles: function (dataTransfer) { + dataTransfer = dataTransfer || {}; + var items = dataTransfer.items; + if (items && items.length && (items[0].webkitGetAsEntry || + items[0].getAsEntry)) { + return this._handleFileTreeEntries( + $.map(items, function (item) { + if (item.webkitGetAsEntry) { + return item.webkitGetAsEntry(); + } + return item.getAsEntry(); + }) + ); + } + return $.Deferred().resolve( + $.makeArray(dataTransfer.files) + ).promise(); + }, + _getFileInputFiles: function (fileInput) { fileInput = $(fileInput); - var files = $.each($.makeArray(fileInput.prop('files')), this._normalizeFile), + var entries = fileInput.prop('webkitEntries') || + fileInput.prop('entries'), + files, value; + if (entries) { + return this._handleFileTreeEntries(entries); + } + files = $.makeArray(fileInput.prop('files')); if (!files.length) { value = fileInput.prop('value'); if (!value) { - return []; + return $.Deferred().reject([]).promise(); } // If the files property is not available, the browser does not // support the File API and we add a pseudo File object with // the input value as name with path information removed: files = [{name: value.replace(/^.*\\/, '')}]; } - return files; + return $.Deferred().resolve(files).promise(); }, _onChange: function (e) { @@ -803,14 +873,15 @@ fileInput: $(e.target), form: $(e.target.form) }; - data.files = that._getFileInputFiles(data.fileInput); - if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); - } - if (that._trigger('change', e, data) === false || - that._onAdd(e, data) === false) { - return false; - } + that._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + if (that.options.replaceFileInput) { + that._replaceFileInput(data.fileInput); + } + if (that._trigger('change', e, data) !== false) { + that._onAdd(e, data); + } + }); }, _onPaste: function (e) { @@ -831,19 +902,16 @@ }, _onDrop: function (e) { + e.preventDefault(); var that = e.data.fileupload, dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, - data = { - files: $.each( - $.makeArray(dataTransfer && dataTransfer.files), - that._normalizeFile - ) - }; - if (that._trigger('drop', e, data) === false || - that._onAdd(e, data) === false) { - return false; - } - e.preventDefault(); + data = {}; + that._getDroppedFiles(dataTransfer).always(function (files) { + data.files = files; + if (that._trigger('drop', e, data) !== false) { + that._onAdd(e, data); + } + }); }, _onDragOver: function (e) { @@ -937,29 +1005,61 @@ // must have a files property and can contain additional options: // .fileupload('add', {files: filesList}); add: function (data) { + var that = this; if (!data || this.options.disabled) { return; } if (data.fileInput && !data.files) { - data.files = this._getFileInputFiles(data.fileInput); + this._getFileInputFiles(data.fileInput).always(function (files) { + data.files = files; + that._onAdd(null, data); + }); } else { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + data.files = $.makeArray(data.files); + this._onAdd(null, data); } - this._onAdd(null, data); }, // This method is exposed to the widget API and allows sending files // using the fileupload API. The data parameter accepts an object which - // must have a files property and can contain additional options: + // must have a files or fileInput property and can contain additional options: // .fileupload('send', {files: filesList}); // The method returns a Promise object for the file upload call. send: function (data) { if (data && !this.options.disabled) { if (data.fileInput && !data.files) { - data.files = this._getFileInputFiles(data.fileInput); - } else { - data.files = $.each($.makeArray(data.files), this._normalizeFile); + var that = this, + dfd = $.Deferred(), + promise = dfd.promise(), + jqXHR, + aborted; + promise.abort = function () { + aborted = true; + if (jqXHR) { + return jqXHR.abort(); + } + dfd.reject(null, 'abort', 'abort'); + return promise; + }; + this._getFileInputFiles(data.fileInput).always( + function (files) { + if (aborted) { + return; + } + data.files = files; + jqXHR = that._onSend(null, data).then( + function (result, textStatus, jqXHR) { + dfd.resolve(result, textStatus, jqXHR); + }, + function (jqXHR, textStatus, errorThrown) { + dfd.reject(jqXHR, textStatus, errorThrown); + } + ); + } + ); + return this._enhancePromise(promise); } + data.files = $.makeArray(data.files); if (data.files.length) { return this._onSend(null, data); } diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js index 04a5662..4749f46 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.4 + * jQuery Iframe Transport Plugin 1.5 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -42,6 +42,7 @@ return { send: function (_, completeCallback) { form = $('
'); + form.attr('accept-charset', options.formAcceptCharset); // javascript:false as initial iframe src // prevents warning popups on HTTPS in IE6. // IE versions below IE8 cannot set the name property of diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js index f4959cf..0d58491 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js @@ -1,5 +1,5 @@ /* - * JavaScript Canvas to Blob 2.0.1 + * JavaScript Canvas to Blob 2.0.2 * https://github.com/blueimp/JavaScript-Canvas-to-Blob * * Copyright 2012, Sebastian Tschan @@ -13,16 +13,23 @@ */ /*jslint nomen: true, regexp: true */ -/*global window, atob, ArrayBuffer, Uint8Array, define */ +/*global window, atob, Blob, ArrayBuffer, Uint8Array, define */ (function (window) { 'use strict'; var CanvasPrototype = window.HTMLCanvasElement && window.HTMLCanvasElement.prototype, + hasBlobConstructor = function () { + try { + return !!new Blob(); + } catch (e) { + return false; + } + }(), BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder, - dataURLtoBlob = BlobBuilder && window.atob && window.ArrayBuffer && - window.Uint8Array && function (dataURI) { + dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob && + window.ArrayBuffer && window.Uint8Array && function (dataURI) { var byteString, arrayBuffer, intArray, @@ -42,11 +49,14 @@ for (i = 0; i < byteString.length; i += 1) { intArray[i] = byteString.charCodeAt(i); } + // Separate out the mime component: + mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; // Write the ArrayBuffer to a blob: + if (hasBlobConstructor) { + return new Blob([arrayBuffer], {type: mimeString}); + } bb = new BlobBuilder(); bb.append(arrayBuffer); - // Separate out the mime component: - mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; return bb.getBlob(mimeString); }; if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js index b980122..39287bd 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,5 +1,5 @@ /* - * jQuery UI Widget 1.8.22+amd + * jQuery UI Widget 1.8.23+amd * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) From c908fb499712d798d95aff579ed8fd32f98036f1 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Mon, 20 Aug 2012 14:43:40 +0800 Subject: [PATCH 08/33] Update readme --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c581114..eb762bb 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,11 @@ jquery-fileupload-rails is a library that integrates jQuery File Upload for Rails 3.1 Asset Pipeline (Rails 3.2 supported). -jquery-fileupload-rails is currently using jQuery-File-Plugin version 6.9.2. +## Plugin versions + +* jQuery File Upload User Interface Plugin 6.9.4 +* jQuery File Upload Plugin 5.16.1 +* jQuery UI Widget 1.8.23+amd ## Installing Gem @@ -44,6 +48,10 @@ Require the stylesheet file to app/assets/stylesheets/application.css *= require jquery.fileupload-ui +## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example) +This app uses paperclip and twitter-bootstrap-rails + + ## Thanks Thanks to [Sebastian Tschan](https://github.com/blueimp) for writing an awesome file upload plugin. From 042d7c2738b42a041ed1b328d717284a02ef2b64 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Mon, 20 Aug 2012 14:44:12 +0800 Subject: [PATCH 09/33] bump version --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index 6a9e204..20dbf30 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.1" + VERSION = "0.3.2" end end end From 8620129666bcfd42c54d7ecdbf2fbea8b7e583a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lipski?= Date: Fri, 31 Aug 2012 10:22:34 +0200 Subject: [PATCH 10/33] Update JavaScript assets. --- README.md | 2 +- .../jquery-fileupload/jquery.fileupload.js | 8 ++--- .../vendor/canvas-to-blob.js | 29 +++++++++++++------ .../jquery-fileupload/vendor/load-image.js | 2 +- 4 files changed, 26 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index eb762bb..6b11d9b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ jquery-fileupload-rails is a library that integrates jQuery File Upload for Rail ## Plugin versions * jQuery File Upload User Interface Plugin 6.9.4 -* jQuery File Upload Plugin 5.16.1 +* jQuery File Upload Plugin 5.16.3 * jQuery UI Widget 1.8.23+amd ## Installing Gem diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js index 0ec51b6..ba67a3a 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.16.1 + * jQuery File Upload Plugin 5.16.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -850,7 +850,7 @@ fileInput.prop('entries'), files, value; - if (entries) { + if (entries && entries.length) { return this._handleFileTreeEntries(entries); } files = $.makeArray(fileInput.prop('files')); @@ -963,8 +963,8 @@ _initSpecialOptions: function () { var options = this.options; if (options.fileInput === undefined) { - options.fileInput = this.element.is('input:file') ? - this.element : this.element.find('input:file'); + options.fileInput = this.element.is('input[type="file"]') ? + this.element : this.element.find('input[type="file"]'); } else if (!(options.fileInput instanceof $)) { options.fileInput = $(options.fileInput); } diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js index 0d58491..72edfdb 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js @@ -1,5 +1,5 @@ /* - * JavaScript Canvas to Blob 2.0.2 + * JavaScript Canvas to Blob 2.0.3 * https://github.com/blueimp/JavaScript-Canvas-to-Blob * * Copyright 2012, Sebastian Tschan @@ -19,13 +19,21 @@ 'use strict'; var CanvasPrototype = window.HTMLCanvasElement && window.HTMLCanvasElement.prototype, - hasBlobConstructor = function () { + hasBlobConstructor = window.Blob && (function () { + try { + return Boolean(new Blob()); + } catch (e) { + return false; + } + }()), + hasArrayBufferViewSupport = hasBlobConstructor && window.Uint8Array && + (function () { try { - return !!new Blob(); + return new Blob([new Uint8Array(100)]).size === 100; } catch (e) { return false; } - }(), + }()), BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder, dataURLtoBlob = (hasBlobConstructor || BlobBuilder) && window.atob && @@ -34,8 +42,8 @@ arrayBuffer, intArray, i, - bb, - mimeString; + mimeString, + bb; if (dataURI.split(',')[0].indexOf('base64') >= 0) { // Convert base64 to raw binary data held in a string: byteString = atob(dataURI.split(',')[1]); @@ -51,9 +59,12 @@ } // Separate out the mime component: mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]; - // Write the ArrayBuffer to a blob: + // Write the ArrayBuffer (or ArrayBufferView) to a blob: if (hasBlobConstructor) { - return new Blob([arrayBuffer], {type: mimeString}); + return new Blob( + [hasArrayBufferViewSupport ? intArray : arrayBuffer], + {type: mimeString} + ); } bb = new BlobBuilder(); bb.append(arrayBuffer); @@ -70,7 +81,7 @@ }; } } - if (typeof define !== 'undefined' && define.amd) { + if (typeof define === 'function' && define.amd) { define(function () { return dataURLtoBlob; }); diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js index 4697f68..96a149e 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js @@ -111,7 +111,7 @@ return false; }; - if (typeof define !== 'undefined' && define.amd) { + if (typeof define === 'function' && define.amd) { define(function () { return loadImage; }); From 968016b88aed21bdebd21f08ad29caaa73f0cab6 Mon Sep 17 00:00:00 2001 From: Salim Semaoune Date: Thu, 13 Sep 2012 16:01:34 +0300 Subject: [PATCH 11/33] Update vendor/assets/stylesheets/jquery.fileupload-ui.css.erb --- vendor/assets/stylesheets/jquery.fileupload-ui.css.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb index 7b2d101..5ebe411 100644 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb @@ -53,13 +53,13 @@ } /* Fix for IE 6: */ -*html .fileinput-button { +* html .fileinput-button { line-height: 22px; margin: 1px -3px 0 0; } /* Fix for IE 7: */ -*+html .fileinput-button { +* + html .fileinput-button { margin: 1px 0 0 0; } From ed4de21cef0fe565ed8007cd676821c28ede1870 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sat, 22 Sep 2012 18:11:57 +0800 Subject: [PATCH 12/33] bump version * Fixed invalid CSS that raise an error with SASS 3.2 --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index 20dbf30..2f93bdc 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.2" + VERSION = "0.3.3" end end end From 9cc9fcd3c091b6a8601d1d50ee2baee71ff8c63b Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Wed, 26 Sep 2012 14:25:51 +0800 Subject: [PATCH 13/33] bump version --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index 2f93bdc..dd69c2d 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.3" + VERSION = "0.3.4" end end end From f7c4bb6a3a0fb34b642df81334399a5e66248b88 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 22:54:23 +0800 Subject: [PATCH 14/33] put railscast episode link in readme file --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b11d9b..e23ee9e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# jQuery File Upload for Rails 3.1 Asset Pipeline +# jQuery File Upload for Rails [jQuery-File-Plugin](https://github.com/blueimp/jQuery-File-Upload) is a file upload plugin written by [Sebastian Tschan](https://github.com/blueimp). jQuery File Upload features multiple file selection, drag&drop support, progress bars and preview images for jQuery. Supports cross-domain, chunked and resumable file uploads and client-side image resizing. @@ -51,6 +51,9 @@ Require the stylesheet file to app/assets/stylesheets/application.css ## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example) This app uses paperclip and twitter-bootstrap-rails +You can also checkout Ryan Bate's RailsCast [jQuery File Upload episode](http://railscasts.com/episodes/381-jquery-file-upload). You will +need a pro account to watch it though. + ## Thanks Thanks to [Sebastian Tschan](https://github.com/blueimp) for writing an awesome file upload plugin. From d86b8f5827ba0973e46baa753769c0dd7a15fb05 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 22:58:36 +0800 Subject: [PATCH 15/33] update jquery.fileupload to 5.19.2 --- .../jquery-fileupload/jquery.fileupload-fp.js | 35 +- .../jquery-fileupload/jquery.fileupload-ui.js | 149 ++++---- .../jquery-fileupload/jquery.fileupload.js | 322 ++++++++++-------- 3 files changed, 269 insertions(+), 237 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js index 634fb5e..2102b8b 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload File Processing Plugin 1.0 + * jQuery File Upload File Processing Plugin 1.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2012, Sebastian Tschan @@ -32,9 +32,9 @@ }(function ($, loadImage) { 'use strict'; - // The File Upload IP version extends the basic fileupload widget + // The File Upload FP version extends the fileupload widget // with file processing functionality: - $.widget('blueimpFP.fileupload', $.blueimp.fileupload, { + $.widget('blueimp.fileupload', $.blueimp.fileupload, { options: { // The list of file processing actions: @@ -70,7 +70,7 @@ processActions: { // Loads the image given via data.files and data.index - // as canvas element. + // as img element if the browser supports canvas. // Accepts the options fileTypes (regular expression) // and maxFileSize (integer) to limit the files to load: load: function (data, options) { @@ -85,28 +85,29 @@ options.fileTypes.test(file.type))) { loadImage( file, - function (canvas) { - data.canvas = canvas; + function (img) { + data.img = img; dfd.resolveWith(that, [data]); - }, - {canvas: true} + } ); } else { dfd.rejectWith(that, [data]); } return dfd.promise(); }, - // Resizes the image given as data.canvas and updates - // data.canvas with the resized image. + // Resizes the image given as data.img and updates + // data.canvas with the resized image as canvas element. // Accepts the options maxWidth, maxHeight, minWidth and // minHeight to scale the given image: resize: function (data, options) { - if (data.canvas) { - var canvas = loadImage.scale(data.canvas, options); - if (canvas.width !== data.canvas.width || - canvas.height !== data.canvas.height) { + var img = data.img, + canvas; + options = $.extend({canvas: true}, options); + if (img) { + canvas = loadImage.scale(img, options); + if (canvas.width !== img.width || + canvas.height !== img.height) { data.canvas = canvas; - data.processed = true; } } return data; @@ -115,7 +116,7 @@ // inplace at data.index of data.files: save: function (data, options) { // Do nothing if no processing has happened: - if (!data.canvas || !data.processed) { + if (!data.canvas) { return data; } var that = this, @@ -208,7 +209,7 @@ }, _create: function () { - $.blueimp.fileupload.prototype._create.call(this); + this._super(); this._processing = 0; this._processingQueue = $.Deferred().resolveWith(this) .promise(); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js index 4c36f00..ad7f2f8 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 6.9.4 + * jQuery File Upload User Interface Plugin 6.11 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, URL, webkitURL, FileReader */ +/*global define, window, URL, webkitURL, FileReader */ (function (factory) { 'use strict'; @@ -33,10 +33,9 @@ }(function ($, tmpl, loadImage) { 'use strict'; - // The UI version extends the FP (file processing) version or the basic - // file upload widget and adds complete user interface interaction: - var parentWidget = ($.blueimpFP || $.blueimp).fileupload; - $.widget('blueimpUI.fileupload', parentWidget, { + // The UI version extends the file upload widget + // and adds complete user interface interaction: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { options: { // By default, files added to the widget are uploaded as soon @@ -144,7 +143,8 @@ if (data.context) { data.context.each(function (index) { var file = ($.isArray(data.result) && - data.result[index]) || {error: 'emptyResult'}; + data.result[index]) || + {error: 'Empty file upload result'}; if (file.error) { that._adjustMaxNumberOfFiles(1); } @@ -398,22 +398,22 @@ // maxNumberOfFiles before validation, so we check if // maxNumberOfFiles is below 0 (instead of below 1): if (this.options.maxNumberOfFiles < 0) { - return 'maxNumberOfFiles'; + return 'Maximum number of files exceeded'; } // Files are accepted if either the file type or the file name // matches against the acceptFileTypes regular expression, as // only browsers with support for the File API report the type: if (!(this.options.acceptFileTypes.test(file.type) || this.options.acceptFileTypes.test(file.name))) { - return 'acceptFileTypes'; + return 'Filetype not allowed'; } if (this.options.maxFileSize && file.size > this.options.maxFileSize) { - return 'maxFileSize'; + return 'File is too big'; } if (typeof file.size === 'number' && file.size < this.options.minFileSize) { - return 'minFileSize'; + return 'File is too small'; } return null; }, @@ -457,7 +457,7 @@ that._transition(node).done(function () { dfd.resolveWith(node); }); - if (!$.contains(document.body, node[0])) { + if (!$.contains(that.document[0].body, node[0])) { // If the element is not part of the DOM, // transition events are not triggered, // so we have to resolve manually: @@ -510,7 +510,7 @@ _startHandler: function (e) { e.preventDefault(); - var button = $(this), + var button = $(e.currentTarget), template = button.closest('.template-upload'), data = template.data('data'); if (data && data.submit && !data.jqXHR && data.submit()) { @@ -520,11 +520,11 @@ _cancelHandler: function (e) { e.preventDefault(); - var template = $(this).closest('.template-upload'), + var template = $(e.currentTarget).closest('.template-upload'), data = template.data('data') || {}; if (!data.jqXHR) { data.errorThrown = 'abort'; - e.data.fileupload._trigger('fail', e, data); + this._trigger('fail', e, data); } else { data.jqXHR.abort(); } @@ -532,13 +532,12 @@ _deleteHandler: function (e) { e.preventDefault(); - var button = $(this); - e.data.fileupload._trigger('destroy', e, { + var button = $(e.currentTarget); + this._trigger('destroy', e, $.extend({ context: button.closest('.template-download'), - url: button.attr('data-url'), - type: button.attr('data-type') || 'DELETE', - dataType: e.data.fileupload.options.dataType - }); + type: 'DELETE', + dataType: this.options.dataType + }, button.data())); }, _forceReflow: function (node) { @@ -569,75 +568,63 @@ _initButtonBarEventHandlers: function () { var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'), - filesList = this.options.filesContainer, - ns = this.options.namespace; - fileUploadButtonBar.find('.start') - .bind('click.' + ns, function (e) { + filesList = this.options.filesContainer; + this._on(fileUploadButtonBar.find('.start'), { + click: function (e) { e.preventDefault(); filesList.find('.start button').click(); - }); - fileUploadButtonBar.find('.cancel') - .bind('click.' + ns, function (e) { + } + }); + this._on(fileUploadButtonBar.find('.cancel'), { + click: function (e) { e.preventDefault(); filesList.find('.cancel button').click(); - }); - fileUploadButtonBar.find('.delete') - .bind('click.' + ns, function (e) { + } + }); + this._on(fileUploadButtonBar.find('.delete'), { + click: function (e) { e.preventDefault(); filesList.find('.delete input:checked') .siblings('button').click(); fileUploadButtonBar.find('.toggle') .prop('checked', false); - }); - fileUploadButtonBar.find('.toggle') - .bind('change.' + ns, function (e) { + } + }); + this._on(fileUploadButtonBar.find('.toggle'), { + change: function (e) { filesList.find('.delete input').prop( 'checked', - $(this).is(':checked') + $(e.currentTarget).is(':checked') ); - }); + } + }); }, _destroyButtonBarEventHandlers: function () { - this.element.find('.fileupload-buttonbar button') - .unbind('click.' + this.options.namespace); - this.element.find('.fileupload-buttonbar .toggle') - .unbind('change.' + this.options.namespace); + this._off( + this.element.find('.fileupload-buttonbar button'), + 'click' + ); + this._off( + this.element.find('.fileupload-buttonbar .toggle'), + 'change.' + ); }, _initEventHandlers: function () { - parentWidget.prototype._initEventHandlers.call(this); - var eventData = {fileupload: this}; - this.options.filesContainer - .delegate( - '.start button', - 'click.' + this.options.namespace, - eventData, - this._startHandler - ) - .delegate( - '.cancel button', - 'click.' + this.options.namespace, - eventData, - this._cancelHandler - ) - .delegate( - '.delete button', - 'click.' + this.options.namespace, - eventData, - this._deleteHandler - ); + this._super(); + this._on(this.options.filesContainer, { + 'click .start button': this._startHandler, + 'click .cancel button': this._cancelHandler, + 'click .delete button': this._deleteHandler + }); this._initButtonBarEventHandlers(); }, _destroyEventHandlers: function () { - var options = this.options; this._destroyButtonBarEventHandlers(); - options.filesContainer - .undelegate('.start button', 'click.' + options.namespace) - .undelegate('.cancel button', 'click.' + options.namespace) - .undelegate('.delete button', 'click.' + options.namespace); - parentWidget.prototype._destroyEventHandlers.call(this); + this._off(this.options.filesContainer, 'click'); + this._super(); }, _enableFileInputButton: function () { @@ -654,7 +641,7 @@ _initTemplates: function () { var options = this.options; - options.templatesContainer = document.createElement( + options.templatesContainer = this.document[0].createElement( options.filesContainer.prop('nodeName') ); if (tmpl) { @@ -698,20 +685,20 @@ }, _initSpecialOptions: function () { - parentWidget.prototype._initSpecialOptions.call(this); + this._super(); this._initFilesContainer(); this._initTemplates(); this._initRegExpOptions(); }, _create: function () { - parentWidget.prototype._create.call(this); + this._super(); this._refreshOptionsList.push( 'filesContainer', 'uploadTemplateId', 'downloadTemplateId' ); - if (!$.blueimpFP) { + if (!this._processingQueue) { this._processingQueue = $.Deferred().resolveWith(this).promise(); this.process = function () { return this._processingQueue; @@ -720,15 +707,23 @@ }, enable: function () { - parentWidget.prototype.enable.call(this); - this.element.find('input, button').prop('disabled', false); - this._enableFileInputButton(); + var wasDisabled = false; + if (this.options.disabled) { + wasDisabled = true; + } + this._super(); + if (wasDisabled) { + this.element.find('input, button').prop('disabled', false); + this._enableFileInputButton(); + } }, disable: function () { - this.element.find('input, button').prop('disabled', true); - this._disableFileInputButton(); - parentWidget.prototype.disable.call(this); + if (!this.options.disabled) { + this.element.find('input, button').prop('disabled', true); + this._disableFileInputButton(); + } + this._super(); } }); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js index ba67a3a..5504ebe 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.16.3 + * jQuery File Upload Plugin 5.19.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -44,17 +44,16 @@ $.widget('blueimp.fileupload', { options: { - // The namespace used for event handler binding on the dropZone and - // fileInput collections. - // If not set, the name of the widget ("fileupload") is used. - namespace: undefined, - // The drop target collection, by the default the complete document. - // Set to null or an empty collection to disable drag & drop support: + // The drop target element(s), by the default the complete document. + // Set to null to disable drag & drop support: dropZone: $(document), - // The file input field collection, that is listened for change events. + // The paste target element(s), by the default the complete document. + // Set to null to disable paste support: + pasteZone: $(document), + // The file input field(s), that are listened to for change events. // If undefined, it is set to the file input fields inside // of the widget element on plugin initialization. - // Set to null or an empty collection to disable the change listener. + // Set to null to disable the change listener. fileInput: undefined, // By default, the file input field is replaced with a clone after // each input field change event. This is required for iframe transport @@ -159,13 +158,13 @@ // start: function (e) {}, // .bind('fileuploadstart', func); // Callback for uploads stop, equivalent to the global ajaxStop event: // stop: function (e) {}, // .bind('fileuploadstop', func); - // Callback for change events of the fileInput collection: + // Callback for change events of the fileInput(s): // change: function (e, data) {}, // .bind('fileuploadchange', func); - // Callback for paste events to the dropZone collection: + // Callback for paste events to the pasteZone(s): // paste: function (e, data) {}, // .bind('fileuploadpaste', func); - // Callback for drop events of the dropZone collection: + // Callback for drop events of the dropZone(s): // drop: function (e, data) {}, // .bind('fileuploaddrop', func); - // Callback for dragover events of the dropZone collection: + // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); // The plugin options are used as settings object for the ajax calls. @@ -177,9 +176,9 @@ // A list of options that require a refresh after assigning a new value: _refreshOptionsList: [ - 'namespace', - 'dropZone', 'fileInput', + 'dropZone', + 'pasteZone', 'multipart', 'forceIframeTransport' ], @@ -301,29 +300,16 @@ // Ignore non-multipart setting if not supported: multipart = options.multipart || !$.support.xhrFileUpload, paramName = options.paramName[0]; - if (!multipart || options.blob) { - // For non-multipart uploads and chunked uploads, - // file meta data is not part of the request body, - // so we transmit this data as part of the HTTP headers. - // For cross domain requests, these headers must be allowed - // via Access-Control-Allow-Headers or removed using - // the beforeSend callback: - options.headers = $.extend(options.headers, { - 'X-File-Name': file.name, - 'X-File-Type': file.type, - 'X-File-Size': file.size - }); - if (!options.blob) { - // Non-chunked non-multipart upload: - options.contentType = file.type; - options.data = file; - } else if (!multipart) { - // Chunked non-multipart upload: - options.contentType = 'application/octet-stream'; - options.data = options.blob; - } + options.headers = options.headers || {}; + if (options.contentRange) { + options.headers['Content-Range'] = options.contentRange; } - if (multipart && $.support.xhrFormDataFileUpload) { + if (!multipart) { + options.headers['Content-Disposition'] = 'attachment; filename="' + + encodeURI(file.name) + '"'; + options.contentType = file.type; + options.data = options.blob || file; + } else if ($.support.xhrFormDataFileUpload) { if (options.postMessage) { // window.postMessage does not allow sending FormData // objects, so we just add the File/Blob objects to @@ -353,6 +339,9 @@ }); } if (options.blob) { + options.headers['Content-Disposition'] = 'attachment; filename="' + + encodeURI(file.name) + '"'; + options.headers['Content-Description'] = encodeURI(file.type); formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { @@ -436,6 +425,11 @@ // associated form, if available: if (!options.form || !options.form.length) { options.form = $(options.fileInput.prop('form')); + // If the given file input doesn't have an associated form, + // use the default widget file input's form: + if (!options.form.length) { + options.form = $(this.options.fileInput.prop('form')); + } } options.paramName = this._getParamName(options); if (!options.url) { @@ -483,6 +477,16 @@ return this._enhancePromise(promise); }, + // Parses the Range header from the server response + // and returns the uploaded bytes: + _getUploadedBytes: function (jqXHR) { + var range = jqXHR.getResponseHeader('Range'), + parts = range && range.split('-'), + upperBytesPos = parts && parts.length > 1 && + parseInt(parts[1], 10); + return upperBytesPos && upperBytesPos + 1; + }, + // Uploads a file in multiple, sequential requests // by splitting the file up in multiple blob chunks. // If the second parameter is true, only tests if the file @@ -494,13 +498,11 @@ fs = file.size, ub = options.uploadedBytes = options.uploadedBytes || 0, mcs = options.maxChunkSize || fs, - // Use the Blob methods with the slice implementation - // according to the W3C Blob API specification: - slice = file.webkitSlice || file.mozSlice || file.slice, - upload, - n, + slice = file.slice || file.webkitSlice || file.mozSlice, + dfd = $.Deferred(), + promise = dfd.promise(), jqXHR, - pipe; + upload; if (!(this._isXHRUpload(options) && slice && (ub || mcs < fs)) || options.data) { return false; @@ -509,66 +511,70 @@ return true; } if (ub >= fs) { - file.error = 'uploadedBytes'; + file.error = 'Uploaded bytes exceed file size'; return this._getXHRPromise( false, options.context, [null, 'error', file.error] ); } - // n is the number of blobs to upload, - // calculated via filesize, uploaded bytes and max chunk size: - n = Math.ceil((fs - ub) / mcs); - // The chunk upload method accepting the chunk number as parameter: + // The chunk upload method: upload = function (i) { - if (!i) { - return that._getXHRPromise(true, options.context); - } - // Upload the blobs in sequential order: - return upload(i -= 1).pipe(function () { - // Clone the options object for each chunk upload: - var o = $.extend({}, options); - o.blob = slice.call( - file, - ub + i * mcs, - ub + (i + 1) * mcs - ); - // Expose the chunk index: - o.chunkIndex = i; - // Expose the number of chunks: - o.chunksNumber = n; - // Store the current chunk size, as the blob itself - // will be dereferenced after data processing: - o.chunkSize = o.blob.size; - // Process the upload data (the blob and potential form data): - that._initXHRData(o); - // Add progress listeners for this chunk upload: - that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) - .done(function () { - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { - that._onProgress($.Event('progress', { - lengthComputable: true, - loaded: o.chunkSize, - total: o.chunkSize - }), o); - } - options.uploadedBytes = o.uploadedBytes += - o.chunkSize; - }); - return jqXHR; - }); + // Clone the options object for each chunk upload: + var o = $.extend({}, options); + o.blob = slice.call( + file, + ub, + ub + mcs + ); + // Store the current chunk size, as the blob itself + // will be dereferenced after data processing: + o.chunkSize = o.blob.size; + // Expose the chunk bytes position range: + o.contentRange = 'bytes ' + ub + '-' + + (ub + o.chunkSize - 1) + '/' + fs; + // Process the upload data (the blob and potential form data): + that._initXHRData(o); + // Add progress listeners for this chunk upload: + that._initProgressListener(o); + jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) + .done(function (result, textStatus, jqXHR) { + ub = that._getUploadedBytes(jqXHR) || + (ub + o.chunkSize); + // Create a progress event if upload is done and + // no progress event has been invoked for this chunk: + if (!o.loaded) { + that._onProgress($.Event('progress', { + lengthComputable: true, + loaded: ub - o.uploadedBytes, + total: ub - o.uploadedBytes + }), o); + } + options.uploadedBytes = o.uploadedBytes = ub; + if (ub < fs) { + // File upload not yet complete, + // continue with the next chunk: + upload(); + } else { + dfd.resolveWith( + o.context, + [result, textStatus, jqXHR] + ); + } + }) + .fail(function (jqXHR, textStatus, errorThrown) { + dfd.rejectWith( + o.context, + [jqXHR, textStatus, errorThrown] + ); + }); }; - // Return the piped Promise object, enhanced with an abort method, - // which is delegated to the jqXHR object of the current upload, - // and jqXHR callbacks mapped to the equivalent Promise methods: - pipe = upload(n); - pipe.abort = function () { + this._enhancePromise(promise); + promise.abort = function () { return jqXHR.abort(); }; - return this._enhancePromise(pipe); + upload(); + return promise; }, _beforeSend: function (e, data) { @@ -766,7 +772,7 @@ // Avoid memory leaks with the detached file input: $.cleanData(input.unbind('remove')); // Replace the original file input element in the fileInput - // collection with the clone, which has been copied including + // elements set with the clone, which has been copied including // event handlers: this.options.fileInput = this.options.fileInput.map(function (i, el) { if (el === input[0]) { @@ -784,16 +790,29 @@ _handleFileTreeEntry: function (entry, path) { var that = this, dfd = $.Deferred(), - errorHandler = function () { - dfd.reject(); + errorHandler = function (e) { + if (e && !e.entry) { + e.entry = entry; + } + // Since $.when returns immediately if one + // Deferred is rejected, we use resolve instead. + // This allows valid files and invalid items + // to be returned together in one set: + dfd.resolve([e]); }, dirReader; path = path || ''; if (entry.isFile) { - entry.file(function (file) { - file.relativePath = path; - dfd.resolve(file); - }, errorHandler); + if (entry._file) { + // Workaround for Chrome bug #149735 + entry._file.relativePath = path; + dfd.resolve(entry._file); + } else { + entry.file(function (file) { + file.relativePath = path; + dfd.resolve(file); + }, errorHandler); + } } else if (entry.isDirectory) { dirReader = entry.createReader(); dirReader.readEntries(function (entries) { @@ -805,7 +824,9 @@ }).fail(errorHandler); }, errorHandler); } else { - errorHandler(); + // Return an empy list for file system items + // other than files or directories: + dfd.resolve([]); } return dfd.promise(); }, @@ -832,8 +853,14 @@ items[0].getAsEntry)) { return this._handleFileTreeEntries( $.map(items, function (item) { + var entry; if (item.webkitGetAsEntry) { - return item.webkitGetAsEntry(); + entry = item.webkitGetAsEntry(); + if (entry) { + // Workaround for Chrome bug #149735: + entry._file = item.getAsFile(); + } + return entry; } return item.getAsEntry(); }) @@ -844,7 +871,7 @@ ).promise(); }, - _getFileInputFiles: function (fileInput) { + _getSingleFileInputFiles: function (fileInput) { fileInput = $(fileInput); var entries = fileInput.prop('webkitEntries') || fileInput.prop('entries'), @@ -857,23 +884,44 @@ if (!files.length) { value = fileInput.prop('value'); if (!value) { - return $.Deferred().reject([]).promise(); + return $.Deferred().resolve([]).promise(); } // If the files property is not available, the browser does not // support the File API and we add a pseudo File object with // the input value as name with path information removed: files = [{name: value.replace(/^.*\\/, '')}]; + } else if (files[0].name === undefined && files[0].fileName) { + // File normalization for Safari 4 and Firefox 3: + $.each(files, function (index, file) { + file.name = file.fileName; + file.size = file.fileSize; + }); } return $.Deferred().resolve(files).promise(); }, + _getFileInputFiles: function (fileInput) { + if (!(fileInput instanceof $) || fileInput.length === 1) { + return this._getSingleFileInputFiles(fileInput); + } + return $.when.apply( + $, + $.map(fileInput, this._getSingleFileInputFiles) + ).pipe(function () { + return Array.prototype.concat.apply( + [], + arguments + ); + }); + }, + _onChange: function (e) { - var that = e.data.fileupload, + var that = this, data = { fileInput: $(e.target), form: $(e.target.form) }; - that._getFileInputFiles(data.fileInput).always(function (files) { + this._getFileInputFiles(data.fileInput).always(function (files) { data.files = files; if (that.options.replaceFileInput) { that._replaceFileInput(data.fileInput); @@ -885,8 +933,7 @@ }, _onPaste: function (e) { - var that = e.data.fileupload, - cbd = e.originalEvent.clipboardData, + var cbd = e.originalEvent.clipboardData, items = (cbd && cbd.items) || [], data = {files: []}; $.each(items, function (index, item) { @@ -895,18 +942,18 @@ data.files.push(file); } }); - if (that._trigger('paste', e, data) === false || - that._onAdd(e, data) === false) { + if (this._trigger('paste', e, data) === false || + this._onAdd(e, data) === false) { return false; } }, _onDrop: function (e) { e.preventDefault(); - var that = e.data.fileupload, + var that = this, dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, data = {}; - that._getDroppedFiles(dataTransfer).always(function (files) { + this._getDroppedFiles(dataTransfer).always(function (files) { data.files = files; if (that._trigger('drop', e, data) !== false) { that._onAdd(e, data); @@ -915,9 +962,8 @@ }, _onDragOver: function (e) { - var that = e.data.fileupload, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (that._trigger('dragover', e) === false) { + var dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; + if (this._trigger('dragover', e) === false) { return false; } if (dataTransfer) { @@ -927,25 +973,24 @@ }, _initEventHandlers: function () { - var ns = this.options.namespace; if (this._isXHRUpload(this.options)) { - this.options.dropZone - .bind('dragover.' + ns, {fileupload: this}, this._onDragOver) - .bind('drop.' + ns, {fileupload: this}, this._onDrop) - .bind('paste.' + ns, {fileupload: this}, this._onPaste); + this._on(this.options.dropZone, { + dragover: this._onDragOver, + drop: this._onDrop + }); + this._on(this.options.pasteZone, { + paste: this._onPaste + }); } - this.options.fileInput - .bind('change.' + ns, {fileupload: this}, this._onChange); + this._on(this.options.fileInput, { + change: this._onChange + }); }, _destroyEventHandlers: function () { - var ns = this.options.namespace; - this.options.dropZone - .unbind('dragover.' + ns, this._onDragOver) - .unbind('drop.' + ns, this._onDrop) - .unbind('paste.' + ns, this._onPaste); - this.options.fileInput - .unbind('change.' + ns, this._onChange); + this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.pasteZone, 'paste'); + this._off(this.options.fileInput, 'change'); }, _setOption: function (key, value) { @@ -953,7 +998,7 @@ if (refresh) { this._destroyEventHandlers(); } - $.Widget.prototype._setOption.call(this, key, value); + this._super(key, value); if (refresh) { this._initSpecialOptions(); this._initEventHandlers(); @@ -971,13 +1016,15 @@ if (!(options.dropZone instanceof $)) { options.dropZone = $(options.dropZone); } + if (!(options.pasteZone instanceof $)) { + options.pasteZone = $(options.pasteZone); + } }, _create: function () { var options = this.options; // Initialize options set via HTML5 data-attributes: $.extend(options, $(this.element[0].cloneNode(false)).data()); - options.namespace = options.namespace || this.widgetName; this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); @@ -985,19 +1032,8 @@ this._initEventHandlers(); }, - destroy: function () { - this._destroyEventHandlers(); - $.Widget.prototype.destroy.call(this); - }, - - enable: function () { - $.Widget.prototype.enable.call(this); - this._initEventHandlers(); - }, - - disable: function () { + _destroy: function () { this._destroyEventHandlers(); - $.Widget.prototype.disable.call(this); }, // This method is exposed to the widget API and allows adding files From 7e551da4e18f7a0a43f4ec447f3a5d9081822d96 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 23:00:30 +0800 Subject: [PATCH 16/33] silly verb --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e23ee9e..407e9b6 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Require the stylesheet file to app/assets/stylesheets/application.css ## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example) This app uses paperclip and twitter-bootstrap-rails -You can also checkout Ryan Bate's RailsCast [jQuery File Upload episode](http://railscasts.com/episodes/381-jquery-file-upload). You will +You can also check out Ryan Bate's RailsCast [jQuery File Upload episode](http://railscasts.com/episodes/381-jquery-file-upload). You will need a pro account to watch it though. From f31293100441c7f155e4a93d9dfa4b5b1fd5ae54 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 23:02:29 +0800 Subject: [PATCH 17/33] update readme file versions --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 407e9b6..232f3e0 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ jquery-fileupload-rails is a library that integrates jQuery File Upload for Rail ## Plugin versions -* jQuery File Upload User Interface Plugin 6.9.4 -* jQuery File Upload Plugin 5.16.3 -* jQuery UI Widget 1.8.23+amd +* jQuery File Upload User Interface Plugin 6.11 +* jQuery File Upload Plugin 5.19.2 +* jQuery UI Widget 1.9.1+amd ## Installing Gem From 2de5502b04cf02d9e476bcf8275f7698e1ecb98a Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 23:02:50 +0800 Subject: [PATCH 18/33] update jquery ui widget --- .../vendor/jquery.ui.widget.js | 480 +++++++++++++----- 1 file changed, 363 insertions(+), 117 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js index 39287bd..886aff6 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,12 +1,12 @@ /* - * jQuery UI Widget 1.8.23+amd + * jQuery UI Widget 1.9.1+amd * https://github.com/blueimp/jQuery-File-Upload * - * Copyright 2012, AUTHORS.txt (http://jqueryui.com/about) - * Dual licensed under the MIT or GPL Version 2 licenses. + * Copyright 2012 jQuery Foundation and other contributors + * Released under the MIT license. * http://jquery.org/license * - * http://docs.jquery.com/UI/Widget + * http://api.jqueryui.com/jQuery.widget/ */ (function (factory) { @@ -19,40 +19,23 @@ } }(function( $, undefined ) { -// jQuery 1.4+ -if ( $.cleanData ) { - var _cleanData = $.cleanData; - $.cleanData = function( elems ) { - for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { - try { - $( elem ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - } - _cleanData( elems ); - }; -} else { - var _remove = $.fn.remove; - $.fn.remove = function( selector, keepData ) { - return this.each(function() { - if ( !keepData ) { - if ( !selector || $.filter( selector, [ this ] ).length ) { - $( "*", this ).add( [ this ] ).each(function() { - try { - $( this ).triggerHandler( "remove" ); - // http://bugs.jquery.com/ticket/8235 - } catch( e ) {} - }); - } - } - return _remove.call( $(this), selector, keepData ); - }); - }; -} +var uuid = 0, + slice = Array.prototype.slice, + _cleanData = $.cleanData; +$.cleanData = function( elems ) { + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + try { + $( elem ).triggerHandler( "remove" ); + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + _cleanData( elems ); +}; $.widget = function( name, base, prototype ) { - var namespace = name.split( "." )[ 0 ], - fullName; + var fullName, existingConstructor, constructor, basePrototype, + namespace = name.split( "." )[ 0 ]; + name = name.split( "." )[ 1 ]; fullName = namespace + "-" + name; @@ -62,81 +45,167 @@ $.widget = function( name, base, prototype ) { } // create selector for plugin - $.expr[ ":" ][ fullName ] = function( elem ) { - return !!$.data( elem, name ); + $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) { + return !!$.data( elem, fullName ); }; $[ namespace ] = $[ namespace ] || {}; - $[ namespace ][ name ] = function( options, element ) { + existingConstructor = $[ namespace ][ name ]; + constructor = $[ namespace ][ name ] = function( options, element ) { + // allow instantiation without "new" keyword + if ( !this._createWidget ) { + return new constructor( options, element ); + } + // allow instantiation without initializing for simple inheritance + // must use "new" keyword (the code above always passes args) if ( arguments.length ) { this._createWidget( options, element ); } }; - - var basePrototype = new base(); + // extend with the existing constructor to carry over any static properties + $.extend( constructor, existingConstructor, { + version: prototype.version, + // copy the object used to create the prototype in case we need to + // redefine the widget later + _proto: $.extend( {}, prototype ), + // track widgets that inherit from this widget in case this widget is + // redefined after a widget inherits from it + _childConstructors: [] + }); + + basePrototype = new base(); // we need to make the options hash a property directly on the new instance // otherwise we'll modify the options hash on the prototype that we're // inheriting from -// $.each( basePrototype, function( key, val ) { -// if ( $.isPlainObject(val) ) { -// basePrototype[ key ] = $.extend( {}, val ); -// } -// }); - basePrototype.options = $.extend( true, {}, basePrototype.options ); - $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { + basePrototype.options = $.widget.extend( {}, basePrototype.options ); + $.each( prototype, function( prop, value ) { + if ( $.isFunction( value ) ) { + prototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); + } + }); + constructor.prototype = $.widget.extend( basePrototype, { + // TODO: remove support for widgetEventPrefix + // always use the name + a colon as the prefix, e.g., draggable:start + // don't prefix for widgets that aren't DOM-based + widgetEventPrefix: basePrototype.widgetEventPrefix || name + }, prototype, { + constructor: constructor, namespace: namespace, widgetName: name, - widgetEventPrefix: $[ namespace ][ name ].prototype.widgetEventPrefix || name, - widgetBaseClass: fullName - }, prototype ); + // TODO remove widgetBaseClass, see #8155 + widgetBaseClass: fullName, + widgetFullName: fullName + }); + + // If this widget is being redefined then we need to find all widgets that + // are inheriting from it and redefine all of them so that they inherit from + // the new version of this widget. We're essentially trying to replace one + // level in the prototype chain. + if ( existingConstructor ) { + $.each( existingConstructor._childConstructors, function( i, child ) { + var childPrototype = child.prototype; + + // redefine the child widget using the same prototype that was + // originally used, but inherit from the new version of the base + $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto ); + }); + // remove the list of existing child constructors from the old constructor + // so the old child constructors can be garbage collected + delete existingConstructor._childConstructors; + } else { + base._childConstructors.push( constructor ); + } - $.widget.bridge( name, $[ namespace ][ name ] ); + $.widget.bridge( name, constructor ); +}; + +$.widget.extend = function( target ) { + var input = slice.call( arguments, 1 ), + inputIndex = 0, + inputLength = input.length, + key, + value; + for ( ; inputIndex < inputLength; inputIndex++ ) { + for ( key in input[ inputIndex ] ) { + value = input[ inputIndex ][ key ]; + if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) { + // Clone objects + if ( $.isPlainObject( value ) ) { + target[ key ] = $.isPlainObject( target[ key ] ) ? + $.widget.extend( {}, target[ key ], value ) : + // Don't extend strings, arrays, etc. with objects + $.widget.extend( {}, value ); + // Copy everything else by reference + } else { + target[ key ] = value; + } + } + } + } + return target; }; $.widget.bridge = function( name, object ) { + var fullName = object.prototype.widgetFullName; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", - args = Array.prototype.slice.call( arguments, 1 ), + args = slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init options = !isMethodCall && args.length ? - $.extend.apply( null, [ true, options ].concat(args) ) : + $.widget.extend.apply( null, [ options ].concat(args) ) : options; - // prevent calls to internal methods - if ( isMethodCall && options.charAt( 0 ) === "_" ) { - return returnValue; - } - if ( isMethodCall ) { this.each(function() { - var instance = $.data( this, name ), - methodValue = instance && $.isFunction( instance[options] ) ? - instance[ options ].apply( instance, args ) : - instance; - // TODO: add this back in 1.9 and use $.error() (see #5972) -// if ( !instance ) { -// throw "cannot call methods on " + name + " prior to initialization; " + -// "attempted to call method '" + options + "'"; -// } -// if ( !$.isFunction( instance[options] ) ) { -// throw "no such method '" + options + "' for " + name + " widget instance"; -// } -// var methodValue = instance[ options ].apply( instance, args ); + var methodValue, + instance = $.data( this, fullName ); + if ( !instance ) { + return $.error( "cannot call methods on " + name + " prior to initialization; " + + "attempted to call method '" + options + "'" ); + } + if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) { + return $.error( "no such method '" + options + "' for " + name + " widget instance" ); + } + methodValue = instance[ options ].apply( instance, args ); if ( methodValue !== instance && methodValue !== undefined ) { - returnValue = methodValue; + returnValue = methodValue && methodValue.jquery ? + returnValue.pushStack( methodValue.get() ) : + methodValue; return false; } }); } else { this.each(function() { - var instance = $.data( this, name ); + var instance = $.data( this, fullName ); if ( instance ) { instance.option( options || {} )._init(); } else { - $.data( this, name, new object( options, this ) ); + new object( options, this ); } }); } @@ -145,74 +214,126 @@ $.widget.bridge = function( name, object ) { }; }; -$.Widget = function( options, element ) { - // allow instantiation without initializing for simple inheritance - if ( arguments.length ) { - this._createWidget( options, element ); - } -}; +$.Widget = function( /* options, element */ ) {}; +$.Widget._childConstructors = []; $.Widget.prototype = { widgetName: "widget", widgetEventPrefix: "", + defaultElement: "
", options: { - disabled: false + disabled: false, + + // callbacks + create: null }, _createWidget: function( options, element ) { - // $.widget.bridge stores the plugin instance, but we do it anyway - // so that it's stored even before the _create function runs - $.data( element, this.widgetName, this ); + element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); - this.options = $.extend( true, {}, + this.uuid = uuid++; + this.eventNamespace = "." + this.widgetName + this.uuid; + this.options = $.widget.extend( {}, this.options, this._getCreateOptions(), options ); - var self = this; - this.element.bind( "remove." + this.widgetName, function() { - self.destroy(); - }); + this.bindings = $(); + this.hoverable = $(); + this.focusable = $(); + + if ( element !== this ) { + // 1.9 BC for #7810 + // TODO remove dual storage + $.data( element, this.widgetName, this ); + $.data( element, this.widgetFullName, this ); + this._on( this.element, { + remove: function( event ) { + if ( event.target === element ) { + this.destroy(); + } + } + }); + this.document = $( element.style ? + // element within the document + element.ownerDocument : + // element is window or document + element.document || element ); + this.window = $( this.document[0].defaultView || this.document[0].parentWindow ); + } this._create(); - this._trigger( "create" ); + this._trigger( "create", null, this._getCreateEventData() ); this._init(); }, - _getCreateOptions: function() { - return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; - }, - _create: function() {}, - _init: function() {}, + _getCreateOptions: $.noop, + _getCreateEventData: $.noop, + _create: $.noop, + _init: $.noop, destroy: function() { + this._destroy(); + // we can probably remove the unbind calls in 2.0 + // all event bindings should go through this._on() this.element - .unbind( "." + this.widgetName ) - .removeData( this.widgetName ); + .unbind( this.eventNamespace ) + // 1.9 BC for #7810 + // TODO remove dual storage + .removeData( this.widgetName ) + .removeData( this.widgetFullName ) + // support: jquery <1.6.3 + // http://bugs.jquery.com/ticket/9413 + .removeData( $.camelCase( this.widgetFullName ) ); this.widget() - .unbind( "." + this.widgetName ) + .unbind( this.eventNamespace ) .removeAttr( "aria-disabled" ) .removeClass( - this.widgetBaseClass + "-disabled " + + this.widgetFullName + "-disabled " + "ui-state-disabled" ); + + // clean up events and states + this.bindings.unbind( this.eventNamespace ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); }, + _destroy: $.noop, widget: function() { return this.element; }, option: function( key, value ) { - var options = key; + var options = key, + parts, + curOption, + i; if ( arguments.length === 0 ) { // don't return a reference to the internal hash - return $.extend( {}, this.options ); + return $.widget.extend( {}, this.options ); } - if (typeof key === "string" ) { - if ( value === undefined ) { - return this.options[ key ]; - } + if ( typeof key === "string" ) { + // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } } options = {}; - options[ key ] = value; + parts = key.split( "." ); + key = parts.shift(); + if ( parts.length ) { + curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] ); + for ( i = 0; i < parts.length - 1; i++ ) { + curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {}; + curOption = curOption[ parts[ i ] ]; + } + key = parts.pop(); + if ( value === undefined ) { + return curOption[ key ] === undefined ? null : curOption[ key ]; + } + curOption[ key ] = value; + } else { + if ( value === undefined ) { + return this.options[ key ] === undefined ? null : this.options[ key ]; + } + options[ key ] = value; + } } this._setOptions( options ); @@ -220,10 +341,11 @@ $.Widget.prototype = { return this; }, _setOptions: function( options ) { - var self = this; - $.each( options, function( key, value ) { - self._setOption( key, value ); - }); + var key; + + for ( key in options ) { + this._setOption( key, options[ key ] ); + } return this; }, @@ -232,10 +354,10 @@ $.Widget.prototype = { if ( key === "disabled" ) { this.widget() - [ value ? "addClass" : "removeClass"]( - this.widgetBaseClass + "-disabled" + " " + - "ui-state-disabled" ) + .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) .attr( "aria-disabled", value ); + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); } return this; @@ -248,6 +370,88 @@ $.Widget.prototype = { return this._setOption( "disabled", true ); }, + _on: function( element, handlers ) { + var delegateElement, + instance = this; + // no element argument, shuffle and use this.element + if ( !handlers ) { + handlers = element; + element = this.element; + delegateElement = this.widget(); + } else { + // accept selectors, DOM elements + element = delegateElement = $( element ); + this.bindings = this.bindings.add( element ); + } + + $.each( handlers, function( event, handler ) { + function handlerProxy() { + // allow widgets to customize the disabled handling + // - disabled as an array instead of boolean + // - disabled class as method for disabling individual parts + if ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) { + return; + } + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + + // copy the guid so direct unbinding works + if ( typeof handler !== "string" ) { + handlerProxy.guid = handler.guid = + handler.guid || handlerProxy.guid || $.guid++; + } + + var match = event.match( /^(\w+)\s*(.*)$/ ), + eventName = match[1] + instance.eventNamespace, + selector = match[2]; + if ( selector ) { + delegateElement.delegate( selector, eventName, handlerProxy ); + } else { + element.bind( eventName, handlerProxy ); + } + }); + }, + + _off: function( element, eventName ) { + eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace; + element.unbind( eventName ).undelegate( eventName ); + }, + + _delay: function( handler, delay ) { + function handlerProxy() { + return ( typeof handler === "string" ? instance[ handler ] : handler ) + .apply( instance, arguments ); + } + var instance = this; + return setTimeout( handlerProxy, delay || 0 ); + }, + + _hoverable: function( element ) { + this.hoverable = this.hoverable.add( element ); + this._on( element, { + mouseenter: function( event ) { + $( event.currentTarget ).addClass( "ui-state-hover" ); + }, + mouseleave: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-hover" ); + } + }); + }, + + _focusable: function( element ) { + this.focusable = this.focusable.add( element ); + this._on( element, { + focusin: function( event ) { + $( event.currentTarget ).addClass( "ui-state-focus" ); + }, + focusout: function( event ) { + $( event.currentTarget ).removeClass( "ui-state-focus" ); + } + }); + }, + _trigger: function( type, event, data ) { var prop, orig, callback = this.options[ type ]; @@ -272,11 +476,53 @@ $.Widget.prototype = { } this.element.trigger( event, data ); - - return !( $.isFunction(callback) && - callback.call( this.element[0], event, data ) === false || + return !( $.isFunction( callback ) && + callback.apply( this.element[0], [ event ].concat( data ) ) === false || event.isDefaultPrevented() ); } }; +$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { + $.Widget.prototype[ "_" + method ] = function( element, options, callback ) { + if ( typeof options === "string" ) { + options = { effect: options }; + } + var hasOptions, + effectName = !options ? + method : + options === true || typeof options === "number" ? + defaultEffect : + options.effect || defaultEffect; + options = options || {}; + if ( typeof options === "number" ) { + options = { duration: options }; + } + hasOptions = !$.isEmptyObject( options ); + options.complete = callback; + if ( options.delay ) { + element.delay( options.delay ); + } + if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) { + element[ method ]( options ); + } else if ( effectName !== method && element[ effectName ] ) { + element[ effectName ]( options.duration, options.easing, callback ); + } else { + element.queue(function( next ) { + $( this )[ method ](); + if ( callback ) { + callback.call( element[ 0 ] ); + } + next(); + }); + } + }; +}); + +// DEPRECATED +if ( $.uiBackCompat !== false ) { + $.Widget.prototype._getCreateOptions = function() { + return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; + }; +} + })); From 9eb4e600877cf91d50b6ab966b429a7fcbc3795c Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Sun, 28 Oct 2012 23:03:25 +0800 Subject: [PATCH 19/33] bump version --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index dd69c2d..517de9f 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.4" + VERSION = "0.3.5" end end end From 978ad528091b8c7ef412af6da68b6ec598bbd286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lipski?= Date: Tue, 13 Nov 2012 02:35:41 +0100 Subject: [PATCH 20/33] Update JavaScript assets. --- .../jquery-fileupload/jquery.fileupload-fp.js | 5 ++++- .../jquery-fileupload/jquery.fileupload.js | 18 +++++++++--------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js index 2102b8b..fdf18fb 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload File Processing Plugin 1.2 + * jQuery File Upload File Processing Plugin 1.2.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2012, Sebastian Tschan @@ -86,6 +86,9 @@ loadImage( file, function (img) { + if (!img.src) { + return dfd.rejectWith(that, [data]); + } data.img = img; dfd.resolveWith(that, [data]); } diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js index 5504ebe..6356012 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.19.2 + * jQuery File Upload Plugin 5.19.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -644,18 +644,18 @@ _onSend: function (e, data) { var that = this, jqXHR, + aborted, slot, pipe, options = that._getAJAXSettings(data), - send = function (resolve, args) { + send = function () { that._sending += 1; // Set timer for bitrate progress calculation: options._bitrateTimer = new that._BitrateTimer(); jqXHR = jqXHR || ( - (resolve !== false && - that._trigger('send', e, options) !== false && - (that._chunkedUpload(options) || $.ajax(options))) || - that._getXHRPromise(false, options.context, args) + ((aborted || that._trigger('send', e, options) === false) && + that._getXHRPromise(false, options.context, aborted)) || + that._chunkedUpload(options) || $.ajax(options) ).done(function (result, textStatus, jqXHR) { that._onDone(result, textStatus, jqXHR, options); }).fail(function (jqXHR, textStatus, errorThrown) { @@ -705,12 +705,12 @@ // which is delegated to the jqXHR object of the current upload, // and jqXHR callbacks mapped to the equivalent Promise methods: pipe.abort = function () { - var args = [undefined, 'abort', 'abort']; + aborted = [undefined, 'abort', 'abort']; if (!jqXHR) { if (slot) { - slot.rejectWith(pipe, args); + slot.rejectWith(options.context, aborted); } - return send(false, args); + return send(); } return jqXHR.abort(); }; From db28426656223c13873d2bcd006581c53c93cb1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lipski?= Date: Tue, 13 Nov 2012 02:36:17 +0100 Subject: [PATCH 21/33] Update readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 232f3e0..936b58b 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ jquery-fileupload-rails is a library that integrates jQuery File Upload for Rail ## Plugin versions * jQuery File Upload User Interface Plugin 6.11 -* jQuery File Upload Plugin 5.19.2 +* jQuery File Upload Plugin 5.19.3 * jQuery UI Widget 1.9.1+amd ## Installing Gem From aa6b1f994542c177ba8cf2ddddbb8718645da2ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Lipski?= Date: Tue, 13 Nov 2012 02:36:34 +0100 Subject: [PATCH 22/33] Bump version. --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index 517de9f..e037147 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.5" + VERSION = "0.3.6" end end end From b5bc1fefaf81acc6db58748620771c07376255bf Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Wed, 5 Dec 2012 16:23:02 -0600 Subject: [PATCH 23/33] Add X-Requested-With param and IFrame middleware --- README.md | 8 +++ lib/jquery/fileupload/rails/middleware.rb | 59 +++++++++++++++++++ lib/jquery/fileupload/rails/upload.rb | 1 + .../jquery.iframe-transport.js | 7 +++ 4 files changed, 75 insertions(+) create mode 100644 lib/jquery/fileupload/rails/middleware.rb diff --git a/README.md b/README.md index 936b58b..ec2a088 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ Require the stylesheet file to app/assets/stylesheets/application.css *= require jquery.fileupload-ui +## Using the middleware + +The `jquery.iframe-transport` fallback transport has some special caveats regarding the response data type, http status, and character encodings. `jquery-fileupload-rails` includes a middleware that handles these inconsistencies seamlessly. If you decide to use it, create an initializer that adds the middleware to your application's middleware stack. + + ````ruby + Rails.application.config.middleware.use JQuery::FileUpload::Rails::Middleware + ```` + ## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example) This app uses paperclip and twitter-bootstrap-rails diff --git a/lib/jquery/fileupload/rails/middleware.rb b/lib/jquery/fileupload/rails/middleware.rb new file mode 100644 index 0000000..334ea67 --- /dev/null +++ b/lib/jquery/fileupload/rails/middleware.rb @@ -0,0 +1,59 @@ +module JQuery + module FileUpload + module Rails + class Middleware + def initialize(app) + @app = app + end + + def call(env) + dup._call(env) + end + + def _call(env) + @status, @headers, @response = @app.call(env) + @request = Rack::Request.new(env) + + if iframe_transport? + @headers['Content-Type'] = 'text/html' + [@status, @headers, self] + else + [@status, @headers, @response] + end + end + + def each(&block) + block.call(html_document_left) if iframe_transport? + @response.each(&block) + block.call(html_document_right) if iframe_transport? + end + + def iframe_transport? + @request.params['X-Requested-With'] == 'IFrame' + end + + def html_document_left + "" + end + + def metadata + meta = {} + meta['data-status'] = @response.status if @response.respond_to? :status + meta['data-statusText'] = @response.status_message if @response.respond_to? :status_message + meta['data-type'] = @headers['Content-Type'] if @headers.has_key?('Content-Type') + meta.map {|key,value| "#{key}='#{value}'" }.join(' ') + end + + private + + def method_missing(method, *args) + @response.send(method.intern, *args) + end + end + end + end +end diff --git a/lib/jquery/fileupload/rails/upload.rb b/lib/jquery/fileupload/rails/upload.rb index bd8adcb..f02d383 100644 --- a/lib/jquery/fileupload/rails/upload.rb +++ b/lib/jquery/fileupload/rails/upload.rb @@ -1,2 +1,3 @@ require "jquery/fileupload/rails/engine" require "jquery/fileupload/rails/version" +require "jquery/fileupload/rails/middleware" diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js index 4749f46..3b10b5c 100644 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js @@ -96,6 +96,13 @@ .val(field.value) .appendTo(form); }); + // Add a hidden `X-Requested-With` field with the value `IFrame` to the + // form, to help server-side code to determine that the upload happened + // through this transport. + $('') + .prop('name', 'X-Requested-With') + .val('IFrame') + .appendTo(form); } if (options.fileInput && options.fileInput.length && options.type === 'POST') { From 3a5519c75b62cd98f936695c327facba5da41ee6 Mon Sep 17 00:00:00 2001 From: Duncan Beevers Date: Wed, 5 Dec 2012 16:37:18 -0600 Subject: [PATCH 24/33] Fix code sample formatting in README --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index ec2a088..76500b8 100644 --- a/README.md +++ b/README.md @@ -52,9 +52,7 @@ Require the stylesheet file to app/assets/stylesheets/application.css The `jquery.iframe-transport` fallback transport has some special caveats regarding the response data type, http status, and character encodings. `jquery-fileupload-rails` includes a middleware that handles these inconsistencies seamlessly. If you decide to use it, create an initializer that adds the middleware to your application's middleware stack. - ````ruby Rails.application.config.middleware.use JQuery::FileUpload::Rails::Middleware - ```` ## [Example app](https://github.com/tors/jquery-fileupload-rails-paperclip-example) This app uses paperclip and twitter-bootstrap-rails From 4425d7cf95ab4aae61fbebeedc825876d6f8b196 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Thu, 6 Dec 2012 20:48:40 +0800 Subject: [PATCH 25/33] bump version to include @duncanbeevers middleware --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index e037147..c802048 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.3.6" + VERSION = "0.4.0" end end end From 9645a2d372454a15f12a702ecc2cf12d10ff255f Mon Sep 17 00:00:00 2001 From: Igor Davydov Date: Mon, 10 Dec 2012 16:17:33 +0400 Subject: [PATCH 26/33] using sass for stylesheet --- ...jquery.fileupload-ui.css.erb => jquery.fileupload-ui.scss} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename vendor/assets/stylesheets/{jquery.fileupload-ui.css.erb => jquery.fileupload-ui.scss} (91%) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb b/vendor/assets/stylesheets/jquery.fileupload-ui.scss similarity index 91% rename from vendor/assets/stylesheets/jquery.fileupload-ui.css.erb rename to vendor/assets/stylesheets/jquery.fileupload-ui.scss index 5ebe411..58a7315 100644 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.scss @@ -37,7 +37,7 @@ width: 200px; } .progress-animated .bar { - background: url(<%= asset_path 'progressbar.gif' %>) !important; + background: image-url('progressbar.gif') !important; filter: none; } .fileupload-loading { @@ -45,7 +45,7 @@ left: 50%; width: 128px; height: 128px; - background: url(<%= asset_path 'loading.gif' %>) center no-repeat; + background: image-url('loading.gif') center no-repeat; display: none; } .fileupload-processing .fileupload-loading { From e3d1258a269c0199679013a23cc6f28e61448ac5 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Fri, 25 Jan 2013 09:04:50 +0100 Subject: [PATCH 27/33] Updating to latest jquery fileupload version --- .../cors/jquery.postmessage-transport.js | 0 .../cors/jquery.xdr-transport.js | 12 +- .../jquery-fileupload/jquery.fileupload-fp.js | 0 .../jquery-fileupload/jquery.fileupload-ui.js | 116 ++++++++++++++---- .../jquery-fileupload/jquery.fileupload.js | 114 ++++++++++++----- .../jquery.iframe-transport.js | 34 ++--- .../vendor/jquery.ui.widget.js | 96 ++++++++------- 7 files changed, 253 insertions(+), 119 deletions(-) mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js old mode 100644 new mode 100755 diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js old mode 100644 new mode 100755 index c42c548..d769f45 --- a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js +++ b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js @@ -1,5 +1,5 @@ /* - * jQuery XDomainRequest Transport Plugin 1.1.2 + * jQuery XDomainRequest Transport Plugin 1.1.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -36,6 +36,7 @@ var xdr; return { send: function (headers, completeCallback) { + var addParamChar = /\?/.test(s.url) ? '&' : '?'; function callback(status, statusText, responses, responseHeaders) { xdr.onload = xdr.onerror = xdr.ontimeout = $.noop; xdr = null; @@ -44,12 +45,13 @@ xdr = new XDomainRequest(); // XDomainRequest only supports GET and POST: if (s.type === 'DELETE') { - s.url = s.url + (/\?/.test(s.url) ? '&' : '?') + - '_method=DELETE'; + s.url = s.url + addParamChar + '_method=DELETE'; s.type = 'POST'; } else if (s.type === 'PUT') { - s.url = s.url + (/\?/.test(s.url) ? '&' : '?') + - '_method=PUT'; + s.url = s.url + addParamChar + '_method=PUT'; + s.type = 'POST'; + } else if (s.type === 'PATCH') { + s.url = s.url + addParamChar + '_method=PATCH'; s.type = 'POST'; } xdr.open(s.type, s.url); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js old mode 100644 new mode 100755 diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js old mode 100644 new mode 100755 index ad7f2f8..a23efdb --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 6.11 + * jQuery File Upload User Interface Plugin 7.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -83,7 +83,8 @@ // widget (via file input selection, drag & drop or add API call). // See the basic file upload widget for more information: add: function (e, data) { - var that = $(this).data('fileupload'), + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'), options = that.options, files = data.files; $(this).fileupload('process', data).done(function () { @@ -94,7 +95,7 @@ options.filesContainer[ options.prependFiles ? 'prepend' : 'append' ](data.context); - that._renderPreviews(files, data.context); + that._renderPreviews(data); that._forceReflow(data.context); that._transition(data.context).done( function () { @@ -109,7 +110,8 @@ }, // Callback for the start of each file upload request: send: function (e, data) { - var that = $(this).data('fileupload'); + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'); if (!data.isValidated) { if (!data.maxNumberOfFilesAdjusted) { that._adjustMaxNumberOfFiles(-data.files.length); @@ -138,13 +140,16 @@ }, // Callback for successful uploads: done: function (e, data) { - var that = $(this).data('fileupload'), - template; + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'), + files = that._getFilesFromResponse(data), + template, + deferred; if (data.context) { data.context.each(function (index) { - var file = ($.isArray(data.result) && - data.result[index]) || - {error: 'Empty file upload result'}; + var file = files[index] || + {error: 'Empty file upload result'}, + deferred = that._addFinishedDeferreds(); if (file.error) { that._adjustMaxNumberOfFiles(1); } @@ -158,14 +163,16 @@ function () { data.context = $(this); that._trigger('completed', e, data); + that._trigger('finished', e, data); + deferred.resolve(); } ); } ); }); } else { - if ($.isArray(data.result)) { - $.each(data.result, function (index, file) { + if (files.length) { + $.each(files, function (index, file) { if (data.maxNumberOfFilesAdjusted && file.error) { that._adjustMaxNumberOfFiles(1); } else if (!data.maxNumberOfFilesAdjusted && @@ -175,21 +182,26 @@ }); data.maxNumberOfFilesAdjusted = true; } - template = that._renderDownload(data.result) + template = that._renderDownload(files) .appendTo(that.options.filesContainer); that._forceReflow(template); + deferred = that._addFinishedDeferreds(); that._transition(template).done( function () { data.context = $(this); that._trigger('completed', e, data); + that._trigger('finished', e, data); + deferred.resolve(); } ); } }, // Callback for failed (abort or error) uploads: fail: function (e, data) { - var that = $(this).data('fileupload'), - template; + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'), + template, + deferred; if (data.maxNumberOfFilesAdjusted) { that._adjustMaxNumberOfFiles(data.files.length); } @@ -199,6 +211,7 @@ var file = data.files[index]; file.error = file.error || data.errorThrown || true; + deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { var node = $(this); @@ -209,15 +222,20 @@ function () { data.context = $(this); that._trigger('failed', e, data); + that._trigger('finished', e, data); + deferred.resolve(); } ); } ); } else { + deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { $(this).remove(); that._trigger('failed', e, data); + that._trigger('finished', e, data); + deferred.resolve(); } ); } @@ -227,14 +245,19 @@ .appendTo(that.options.filesContainer) .data('data', data); that._forceReflow(data.context); + deferred = that._addFinishedDeferreds(); that._transition(data.context).done( function () { data.context = $(this); that._trigger('failed', e, data); + that._trigger('finished', e, data); + deferred.resolve(); } ); } else { that._trigger('failed', e, data); + that._trigger('finished', e, data); + that._addFinishedDeferreds().resolve(); } }, // Callback for upload progress events: @@ -258,7 +281,8 @@ .find('.progress-extended'); if (extendedProgressNode.length) { extendedProgressNode.html( - $this.data('fileupload')._renderExtendedProgress(data) + ($this.data('blueimp-fileupload') || $this.data('fileupload')) + ._renderExtendedProgress(data) ); } globalProgressNode @@ -271,7 +295,9 @@ }, // Callback for uploads start, equivalent to the global ajaxStart event: start: function (e) { - var that = $(this).data('fileupload'); + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'); + that._resetFinishedDeferreds(); that._transition($(this).find('.fileupload-progress')).done( function () { that._trigger('started', e); @@ -280,20 +306,27 @@ }, // Callback for uploads stop, equivalent to the global ajaxStop event: stop: function (e) { - var that = $(this).data('fileupload'); + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'), + deferred = that._addFinishedDeferreds(); + $.when.apply($, that._getFinishedDeferreds()) + .done(function () { + that._trigger('stopped', e); + }); that._transition($(this).find('.fileupload-progress')).done( function () { $(this).find('.progress') .attr('aria-valuenow', '0') .find('.bar').css('width', '0%'); $(this).find('.progress-extended').html(' '); - that._trigger('stopped', e); + deferred.resolve(); } ); }, // Callback for file deletion: destroy: function (e, data) { - var that = $(this).data('fileupload'); + var that = $(this).data('blueimp-fileupload') || + $(this).data('fileupload'); if (data.url) { $.ajax(data); that._adjustMaxNumberOfFiles(1); @@ -307,6 +340,29 @@ } }, + _resetFinishedDeferreds: function () { + this._finishedUploads = []; + }, + + _addFinishedDeferreds: function (deferred) { + if (!deferred) { + deferred = $.Deferred(); + } + this._finishedUploads.push(deferred); + return deferred; + }, + + _getFinishedDeferreds: function () { + return this._finishedUploads; + }, + + _getFilesFromResponse: function (data) { + if (data.result && $.isArray(data.result.files)) { + return data.result.files; + } + return []; + }, + // Link handler, that allows to download files // by drag & drop of the links to the desktop: _enableDragToDesktop: function () { @@ -361,7 +417,7 @@ if (bits >= 1000) { return (bits / 1000).toFixed(2) + ' kbit/s'; } - return bits + ' bit/s'; + return bits.toFixed(2) + ' bit/s'; }, _formatTime: function (seconds) { @@ -472,18 +528,22 @@ )) || dfd.resolveWith(node)) && dfd; }, - _renderPreviews: function (files, nodes) { + _renderPreviews: function (data) { var that = this, options = this.options; - nodes.find('.preview span').each(function (index, element) { - var file = files[index]; + data.context.find('.preview span').each(function (index, element) { + var file = data.files[index]; if (options.previewSourceFileTypes.test(file.type) && ($.type(options.previewSourceMaxFileSize) !== 'number' || file.size < options.previewSourceMaxFileSize)) { that._processingQueue = that._processingQueue.pipe(function () { - var dfd = $.Deferred(); + var dfd = $.Deferred(), + ev = $.Event('previewdone', { + target: element + }); that._renderPreview(file, $(element)).done( function () { + that._trigger(ev.type, ev, data); dfd.resolveWith(that); } ); @@ -691,6 +751,13 @@ this._initRegExpOptions(); }, + _setOption: function (key, value) { + this._super(key, value); + if (key === 'maxNumberOfFiles') { + this._adjustMaxNumberOfFiles(0); + } + }, + _create: function () { this._super(); this._refreshOptionsList.push( @@ -704,6 +771,7 @@ return this._processingQueue; }; } + this._resetFinishedDeferreds(); }, enable: function () { diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js old mode 100644 new mode 100755 index 6356012..04caf69 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.19.3 + * jQuery File Upload Plugin 5.21 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, Blob, FormData, location */ +/*global define, window, document, File, Blob, FormData, location */ (function (factory) { 'use strict'; @@ -33,6 +33,21 @@ $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; + // The form.elements propHook is added to filter serialized elements + // to not include file inputs in jQuery 1.9.0. + // This hooks directly into jQuery.fn.serializeArray. + // For more info, see http://bugs.jquery.com/ticket/13306 + $.propHooks.elements = { + get: function (form) { + if ($.nodeName(form, 'form')) { + return $.grep(form.elements, function (elem) { + return !$.nodeName(elem, 'input') || elem.type !== 'file'; + }); + } + return null; + } + }; + // The fileupload widget listens for change events on file input fields defined // via fileInput setting and paste or drop events of the given dropZone. // In addition to the default jQuery Widget methods, the fileupload widget @@ -140,33 +155,58 @@ }, // Other callbacks: + // Callback for the submit event of each file upload: // submit: function (e, data) {}, // .bind('fileuploadsubmit', func); + // Callback for the start of each file upload request: // send: function (e, data) {}, // .bind('fileuploadsend', func); + // Callback for successful uploads: // done: function (e, data) {}, // .bind('fileuploaddone', func); + // Callback for failed (abort or error) uploads: // fail: function (e, data) {}, // .bind('fileuploadfail', func); + // Callback for completed (success, abort or error) requests: // always: function (e, data) {}, // .bind('fileuploadalways', func); + // Callback for upload progress events: // progress: function (e, data) {}, // .bind('fileuploadprogress', func); + // Callback for global upload progress events: // progressall: function (e, data) {}, // .bind('fileuploadprogressall', func); + // Callback for uploads start, equivalent to the global ajaxStart event: // start: function (e) {}, // .bind('fileuploadstart', func); + // Callback for uploads stop, equivalent to the global ajaxStop event: // stop: function (e) {}, // .bind('fileuploadstop', func); + // Callback for change events of the fileInput(s): // change: function (e, data) {}, // .bind('fileuploadchange', func); + // Callback for paste events to the pasteZone(s): // paste: function (e, data) {}, // .bind('fileuploadpaste', func); + // Callback for drop events of the dropZone(s): // drop: function (e, data) {}, // .bind('fileuploaddrop', func); + // Callback for dragover events of the dropZone(s): // dragover: function (e) {}, // .bind('fileuploaddragover', func); + // Callback for the start of each chunk upload request: + // chunksend: function (e, data) {}, // .bind('fileuploadchunksend', func); + + // Callback for successful chunk uploads: + // chunkdone: function (e, data) {}, // .bind('fileuploadchunkdone', func); + + // Callback for failed (abort or error) chunk uploads: + // chunkfail: function (e, data) {}, // .bind('fileuploadchunkfail', func); + + // Callback for completed (success, abort or error) chunk upload requests: + // chunkalways: function (e, data) {}, // .bind('fileuploadchunkalways', func); + // The plugin options are used as settings object for the ajax calls. // The following are jQuery ajax settings required for the file uploads: processData: false, @@ -209,10 +249,10 @@ if (typeof options.formData === 'function') { return options.formData(options.form); } - if ($.isArray(options.formData)) { + if ($.isArray(options.formData)) { return options.formData; } - if (options.formData) { + if (options.formData) { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -341,14 +381,15 @@ if (options.blob) { options.headers['Content-Disposition'] = 'attachment; filename="' + encodeURI(file.name) + '"'; - options.headers['Content-Description'] = encodeURI(file.type); formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { - // File objects are also Blob instances. + // Files are also Blob instances, but some browsers + // (Firefox 3.6) support the File API but not Blobs. // This check allows the tests to run with // dummy objects: - if (file instanceof Blob) { + if ((window.Blob && file instanceof Blob) || + (window.File && file instanceof File)) { formData.append( options.paramName[index] || paramName, file, @@ -438,7 +479,8 @@ // The HTTP request method must be "POST" or "PUT": options.type = (options.type || options.form.prop('method') || '') .toUpperCase(); - if (options.type !== 'POST' && options.type !== 'PUT') { + if (options.type !== 'POST' && options.type !== 'PUT' && + options.type !== 'PATCH') { options.type = 'POST'; } if (!options.formAcceptCharset) { @@ -525,7 +567,8 @@ o.blob = slice.call( file, ub, - ub + mcs + ub + mcs, + file.type ); // Store the current chunk size, as the blob itself // will be dereferenced after data processing: @@ -537,13 +580,15 @@ that._initXHRData(o); // Add progress listeners for this chunk upload: that._initProgressListener(o); - jqXHR = ($.ajax(o) || that._getXHRPromise(false, o.context)) + jqXHR = ((that._trigger('chunksend', null, o) !== false && $.ajax(o)) || + that._getXHRPromise(false, o.context)) .done(function (result, textStatus, jqXHR) { ub = that._getUploadedBytes(jqXHR) || (ub + o.chunkSize); - // Create a progress event if upload is done and - // no progress event has been invoked for this chunk: - if (!o.loaded) { + // Create a progress event if upload is done and no progress + // event has been invoked for this chunk, or there has been + // no progress event with loaded equaling total: + if (!o.loaded || o.loaded < o.total) { that._onProgress($.Event('progress', { lengthComputable: true, loaded: ub - o.uploadedBytes, @@ -551,6 +596,11 @@ }), o); } options.uploadedBytes = o.uploadedBytes = ub; + o.result = result; + o.textStatus = textStatus; + o.jqXHR = jqXHR; + that._trigger('chunkdone', null, o); + that._trigger('chunkalways', null, o); if (ub < fs) { // File upload not yet complete, // continue with the next chunk: @@ -563,6 +613,11 @@ } }) .fail(function (jqXHR, textStatus, errorThrown) { + o.jqXHR = jqXHR; + o.textStatus = textStatus; + o.errorThrown = errorThrown; + that._trigger('chunkfail', null, o); + that._trigger('chunkalways', null, o); dfd.rejectWith( o.context, [jqXHR, textStatus, errorThrown] @@ -593,12 +648,16 @@ }, _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options)) { - // Create a progress event for each iframe load: + if (!this._isXHRUpload(options) || !options.loaded || + options.loaded < options.total) { + var total = this._getTotal(options.files) || 1; + // Create a progress event for each iframe load, + // or if there has been no progress event with + // loaded equaling total for XHR uploads: this._onProgress($.Event('progress', { lengthComputable: true, - loaded: 1, - total: 1 + loaded: total, + total: total }), options); } options.result = result; @@ -621,15 +680,9 @@ }, _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { + // jqXHRorResult, textStatus and jqXHRorError are added to the + // options object via done and fail callbacks this._active -= 1; - options.textStatus = textStatus; - if (jqXHRorError && jqXHRorError.always) { - options.jqXHR = jqXHRorError; - options.result = jqXHRorResult; - } else { - options.jqXHR = jqXHRorResult; - options.errorThrown = jqXHRorError; - } this._trigger('always', null, options); if (this._active === 0) { // The stop callback is triggered when all uploads have @@ -758,7 +811,8 @@ that._onSend(e, this); return this.jqXHR; }; - return (result = that._trigger('add', e, newData)); + result = that._trigger('add', e, newData); + return result; }); return result; }, @@ -949,10 +1003,12 @@ }, _onDrop: function (e) { - e.preventDefault(); var that = this, dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, data = {}; + if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { + e.preventDefault(); + } this._getDroppedFiles(dataTransfer).always(function (files) { data.files = files; if (that._trigger('drop', e, data) !== false) { @@ -966,10 +1022,10 @@ if (this._trigger('dragover', e) === false) { return false; } - if (dataTransfer) { + if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1) { dataTransfer.dropEffect = 'copy'; + e.preventDefault(); } - e.preventDefault(); }, _initEventHandlers: function () { diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js old mode 100644 new mode 100755 index 3b10b5c..ed25895 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.5 + * jQuery Iframe Transport Plugin 1.6.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -36,13 +36,26 @@ // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] $.ajaxTransport('iframe', function (options) { - if (options.async && (options.type === 'POST' || options.type === 'GET')) { + if (options.async) { var form, - iframe; + iframe, + addParamChar; return { send: function (_, completeCallback) { form = $('
'); form.attr('accept-charset', options.formAcceptCharset); + addParamChar = /\?/.test(options.url) ? '&' : '?'; + // XDomainRequest only supports GET and POST: + if (options.type === 'DELETE') { + options.url = options.url + addParamChar + '_method=DELETE'; + options.type = 'POST'; + } else if (options.type === 'PUT') { + options.url = options.url + addParamChar + '_method=PUT'; + options.type = 'POST'; + } else if (options.type === 'PATCH') { + options.url = options.url + addParamChar + '_method=PATCH'; + options.type = 'POST'; + } // javascript:false as initial iframe src // prevents warning popups on HTTPS in IE6. // IE versions below IE8 cannot set the name property of @@ -96,13 +109,6 @@ .val(field.value) .appendTo(form); }); - // Add a hidden `X-Requested-With` field with the value `IFrame` to the - // form, to help server-side code to determine that the upload happened - // through this transport. - $('') - .prop('name', 'X-Requested-With') - .val('IFrame') - .appendTo(form); } if (options.fileInput && options.fileInput.length && options.type === 'POST') { @@ -162,16 +168,16 @@ $.ajaxSetup({ converters: { 'iframe text': function (iframe) { - return $(iframe[0].body).text(); + return iframe && $(iframe[0].body).text(); }, 'iframe json': function (iframe) { - return $.parseJSON($(iframe[0].body).text()); + return iframe && $.parseJSON($(iframe[0].body).text()); }, 'iframe html': function (iframe) { - return $(iframe[0].body).html(); + return iframe && $(iframe[0].body).html(); }, 'iframe script': function (iframe) { - return $.globalEval($(iframe[0].body).text()); + return iframe && $.globalEval($(iframe[0].body).text()); } } }); diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js old mode 100644 new mode 100755 index 886aff6..f5d71a1 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,8 +1,8 @@ /* - * jQuery UI Widget 1.9.1+amd + * jQuery UI Widget 1.10.0+amd * https://github.com/blueimp/jQuery-File-Upload * - * Copyright 2012 jQuery Foundation and other contributors + * Copyright 2013 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * @@ -34,6 +34,9 @@ $.cleanData = function( elems ) { $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, + // proxiedPrototype allows the provided prototype to remain unmodified + // so that it can be used as a mixin for multiple widgets (#8876) + proxiedPrototype = {}, namespace = name.split( "." )[ 0 ]; name = name.split( "." )[ 1 ]; @@ -80,43 +83,43 @@ $.widget = function( name, base, prototype ) { // inheriting from basePrototype.options = $.widget.extend( {}, basePrototype.options ); $.each( prototype, function( prop, value ) { - if ( $.isFunction( value ) ) { - prototype[ prop ] = (function() { - var _super = function() { - return base.prototype[ prop ].apply( this, arguments ); - }, - _superApply = function( args ) { - return base.prototype[ prop ].apply( this, args ); - }; - return function() { - var __super = this._super, - __superApply = this._superApply, - returnValue; - - this._super = _super; - this._superApply = _superApply; - - returnValue = value.apply( this, arguments ); - - this._super = __super; - this._superApply = __superApply; - - return returnValue; - }; - })(); + if ( !$.isFunction( value ) ) { + proxiedPrototype[ prop ] = value; + return; } + proxiedPrototype[ prop ] = (function() { + var _super = function() { + return base.prototype[ prop ].apply( this, arguments ); + }, + _superApply = function( args ) { + return base.prototype[ prop ].apply( this, args ); + }; + return function() { + var __super = this._super, + __superApply = this._superApply, + returnValue; + + this._super = _super; + this._superApply = _superApply; + + returnValue = value.apply( this, arguments ); + + this._super = __super; + this._superApply = __superApply; + + return returnValue; + }; + })(); }); constructor.prototype = $.widget.extend( basePrototype, { // TODO: remove support for widgetEventPrefix // always use the name + a colon as the prefix, e.g., draggable:start // don't prefix for widgets that aren't DOM-based - widgetEventPrefix: basePrototype.widgetEventPrefix || name - }, prototype, { + widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name + }, proxiedPrototype, { constructor: constructor, namespace: namespace, widgetName: name, - // TODO remove widgetBaseClass, see #8155 - widgetBaseClass: fullName, widgetFullName: fullName }); @@ -169,7 +172,7 @@ $.widget.extend = function( target ) { }; $.widget.bridge = function( name, object ) { - var fullName = object.prototype.widgetFullName; + var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", args = slice.call( arguments, 1 ), @@ -205,7 +208,7 @@ $.widget.bridge = function( name, object ) { if ( instance ) { instance.option( options || {} )._init(); } else { - new object( options, this ); + $.data( this, fullName, new object( options, this ) ); } }); } @@ -242,11 +245,8 @@ $.Widget.prototype = { this.focusable = $(); if ( element !== this ) { - // 1.9 BC for #7810 - // TODO remove dual storage - $.data( element, this.widgetName, this ); $.data( element, this.widgetFullName, this ); - this._on( this.element, { + this._on( true, this.element, { remove: function( event ) { if ( event.target === element ) { this.destroy(); @@ -370,9 +370,17 @@ $.Widget.prototype = { return this._setOption( "disabled", true ); }, - _on: function( element, handlers ) { + _on: function( suppressDisabledCheck, element, handlers ) { var delegateElement, instance = this; + + // no suppressDisabledCheck flag, shuffle arguments + if ( typeof suppressDisabledCheck !== "boolean" ) { + handlers = element; + element = suppressDisabledCheck; + suppressDisabledCheck = false; + } + // no element argument, shuffle and use this.element if ( !handlers ) { handlers = element; @@ -389,8 +397,9 @@ $.Widget.prototype = { // allow widgets to customize the disabled handling // - disabled as an array instead of boolean // - disabled class as method for disabling individual parts - if ( instance.options.disabled === true || - $( this ).hasClass( "ui-state-disabled" ) ) { + if ( !suppressDisabledCheck && + ( instance.options.disabled === true || + $( this ).hasClass( "ui-state-disabled" ) ) ) { return; } return ( typeof handler === "string" ? instance[ handler ] : handler ) @@ -502,7 +511,7 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { if ( options.delay ) { element.delay( options.delay ); } - if ( hasOptions && $.effects && ( $.effects.effect[ effectName ] || $.uiBackCompat !== false && $.effects[ effectName ] ) ) { + if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) { element[ method ]( options ); } else if ( effectName !== method && element[ effectName ] ) { element[ effectName ]( options.duration, options.easing, callback ); @@ -518,11 +527,4 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { }; }); -// DEPRECATED -if ( $.uiBackCompat !== false ) { - $.Widget.prototype._getCreateOptions = function() { - return $.metadata && $.metadata.get( this.element[0] )[ this.widgetName ]; - }; -} - })); From d99117f29e74778d16361b0d75e46875dcc87101 Mon Sep 17 00:00:00 2001 From: Tors Dalid Date: Mon, 18 Feb 2013 13:17:01 +0800 Subject: [PATCH 28/33] bump version --- lib/jquery/fileupload/rails/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jquery/fileupload/rails/version.rb b/lib/jquery/fileupload/rails/version.rb index c802048..10fbdc9 100644 --- a/lib/jquery/fileupload/rails/version.rb +++ b/lib/jquery/fileupload/rails/version.rb @@ -1,7 +1,7 @@ module JQuery module FileUpload module Rails - VERSION = "0.4.0" + VERSION = "0.4.1" end end end From 9d5aa4476f429dca0a371b4d124588990b45bc6b Mon Sep 17 00:00:00 2001 From: John Rees Date: Thu, 28 Feb 2013 17:09:39 +0100 Subject: [PATCH 29/33] Added spaces to sprockets //=require --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 76500b8..4207f35 100644 --- a/README.md +++ b/README.md @@ -22,25 +22,25 @@ Require jquery-fileupload in your app/assets/application.js file. The snippet above will add the following js files to the mainfest file. - //=require jquery-fileupload/vendor/jquery.ui.widget - //=require jquery-fileupload/vendor/load-image - //=require jquery-fileupload/vendor/canvas-to-blob - //=require jquery-fileupload/vendor/tmpl - //=require jquery-fileupload/jquery.iframe-transport - //=require jquery-fileupload/jquery.fileupload - //=require jquery-fileupload/jquery.fileupload-fp - //=require jquery-fileupload/jquery.fileupload-ui - //=require jquery-fileupload/locale + //= require jquery-fileupload/vendor/jquery.ui.widget + //= require jquery-fileupload/vendor/load-image + //= require jquery-fileupload/vendor/canvas-to-blob + //= require jquery-fileupload/vendor/tmpl + //= require jquery-fileupload/jquery.iframe-transport + //= require jquery-fileupload/jquery.fileupload + //= require jquery-fileupload/jquery.fileupload-fp + //= require jquery-fileupload/jquery.fileupload-ui + //= require jquery-fileupload/locale If you only need the basic files, just add the code below to your application.js file. [Basic setup guide](https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin) - //=require jquery-fileupload/basic + //= require jquery-fileupload/basic The basic setup only includes the following files: - //=require jquery-fileupload/vendor/jquery.ui.widget - //=require jquery-fileupload/jquery.iframe-transport - //=require jquery-fileupload/jquery.fileupload + //= require jquery-fileupload/vendor/jquery.ui.widget + //= require jquery-fileupload/jquery.iframe-transport + //= require jquery-fileupload/jquery.fileupload ## Using the stylesheet From 0ceda3515bc2cbebbb799081e3443ce3d3c7f929 Mon Sep 17 00:00:00 2001 From: Christopher Manning Date: Sun, 24 Mar 2013 16:45:04 -0500 Subject: [PATCH 30/33] Update javascript assets from master --- .../cors/jquery.postmessage-transport.js | 0 .../cors/jquery.xdr-transport.js | 0 .../jquery-fileupload/jquery.fileupload-fp.js | 12 +- .../jquery-fileupload/jquery.fileupload-ui.js | 36 ++- .../jquery-fileupload/jquery.fileupload.js | 249 +++++++++++------- .../jquery.iframe-transport.js | 0 .../vendor/canvas-to-blob.js | 14 +- .../vendor/jquery.ui.widget.js | 2 +- .../jquery-fileupload/vendor/load-image.js | 135 +++++++++- 9 files changed, 320 insertions(+), 128 deletions(-) mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js mode change 100755 => 100644 vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js old mode 100755 new mode 100644 diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js old mode 100755 new mode 100644 diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js old mode 100755 new mode 100644 index fdf18fb..c782f1e --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-fp.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload File Processing Plugin 1.2.1 + * jQuery File Upload File Processing Plugin 1.2.3 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2012, Sebastian Tschan @@ -62,9 +62,13 @@ // fileupload widget (via file input selection, drag & drop or add // API call). See the basic file upload widget for more information: add: function (e, data) { - $(this).fileupload('process', data).done(function () { - data.submit(); - }); + if (data.autoUpload || (data.autoUpload !== false && + ($(this).data('blueimp-fileupload') || + $(this).data('fileupload')).options.autoUpload)) { + $(this).fileupload('process', data).done(function () { + data.submit(); + }); + } } }, diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js old mode 100755 new mode 100644 index a23efdb..0386fb2 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 7.3 + * jQuery File Upload User Interface Plugin 7.4.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -263,7 +263,7 @@ // Callback for upload progress events: progress: function (e, data) { if (data.context) { - var progress = parseInt(data.loaded / data.total * 100, 10); + var progress = Math.floor(data.loaded / data.total * 100); data.context.find('.progress') .attr('aria-valuenow', progress) .find('.bar').css( @@ -275,7 +275,7 @@ // Callback for global upload progress events: progressall: function (e, data) { var $this = $(this), - progress = parseInt(data.loaded / data.total * 100, 10), + progress = Math.floor(data.loaded / data.total * 100), globalProgressNode = $this.find('.fileupload-progress'), extendedProgressNode = globalProgressNode .find('.progress-extended'); @@ -422,7 +422,7 @@ _formatTime: function (seconds) { var date = new Date(seconds * 1000), - days = parseInt(seconds / 86400, 10); + days = Math.floor(seconds / 86400); days = days ? days + 'd ' : ''; return days + ('0' + date.getUTCHours()).slice(-2) + ':' + @@ -519,6 +519,12 @@ // so we have to resolve manually: dfd.resolveWith(node); } + node.on('remove', function () { + // If the element is removed before the + // transition finishes, transition events are + // not triggered, resolve manually: + dfd.resolveWith(node); + }); }, { maxWidth: options.previewMaxWidth, @@ -607,7 +613,7 @@ _transition: function (node) { var dfd = $.Deferred(); - if ($.support.transition && node.hasClass('fade')) { + if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { node.bind( $.support.transition.end, function (e) { @@ -632,27 +638,28 @@ this._on(fileUploadButtonBar.find('.start'), { click: function (e) { e.preventDefault(); - filesList.find('.start button').click(); + filesList.find('.start').click(); } }); this._on(fileUploadButtonBar.find('.cancel'), { click: function (e) { e.preventDefault(); - filesList.find('.cancel button').click(); + filesList.find('.cancel').click(); } }); this._on(fileUploadButtonBar.find('.delete'), { click: function (e) { e.preventDefault(); - filesList.find('.delete input:checked') - .siblings('button').click(); + filesList.find('.toggle:checked') + .closest('.template-download') + .find('.delete').click(); fileUploadButtonBar.find('.toggle') .prop('checked', false); } }); this._on(fileUploadButtonBar.find('.toggle'), { change: function (e) { - filesList.find('.delete input').prop( + filesList.find('.toggle').prop( 'checked', $(e.currentTarget).is(':checked') ); @@ -662,7 +669,8 @@ _destroyButtonBarEventHandlers: function () { this._off( - this.element.find('.fileupload-buttonbar button'), + this.element.find('.fileupload-buttonbar') + .find('.start, .cancel, .delete'), 'click' ); this._off( @@ -674,9 +682,9 @@ _initEventHandlers: function () { this._super(); this._on(this.options.filesContainer, { - 'click .start button': this._startHandler, - 'click .cancel button': this._cancelHandler, - 'click .delete button': this._deleteHandler + 'click .start': this._startHandler, + 'click .cancel': this._cancelHandler, + 'click .delete': this._deleteHandler }); this._initButtonBarEventHandlers(); }, diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js old mode 100755 new mode 100644 index 04caf69..dbee297 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.21 + * jQuery File Upload Plugin 5.28.4 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -33,21 +33,6 @@ $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; - // The form.elements propHook is added to filter serialized elements - // to not include file inputs in jQuery 1.9.0. - // This hooks directly into jQuery.fn.serializeArray. - // For more info, see http://bugs.jquery.com/ticket/13306 - $.propHooks.elements = { - get: function (form) { - if ($.nodeName(form, 'form')) { - return $.grep(form.elements, function (elem) { - return !$.nodeName(elem, 'input') || elem.type !== 'file'; - }); - } - return null; - } - }; - // The fileupload widget listens for change events on file input fields defined // via fileInput setting and paste or drop events of the given dropZone. // In addition to the default jQuery Widget methods, the fileupload widget @@ -127,6 +112,8 @@ progressInterval: 100, // Interval in milliseconds to calculate progress bitrate: bitrateInterval: 500, + // By default, uploads are started automatically when adding files: + autoUpload: true, // Additional form data to be sent along with the file uploads can be set // using this option, which accepts an array of objects with name and @@ -151,7 +138,11 @@ // handlers using jQuery's Deferred callbacks: // data.submit().done(func).fail(func).always(func); add: function (e, data) { - data.submit(); + if (data.autoUpload || (data.autoUpload !== false && + ($(this).data('blueimp-fileupload') || + $(this).data('fileupload')).options.autoUpload)) { + data.submit(); + } }, // Other callbacks: @@ -224,7 +215,7 @@ ], _BitrateTimer: function () { - this.timestamp = +(new Date()); + this.timestamp = (new Date()).getTime(); this.loaded = 0; this.bitrate = 0; this.getBitrate = function (now, loaded, interval) { @@ -252,7 +243,7 @@ if ($.isArray(options.formData)) { return options.formData; } - if (options.formData) { + if ($.type(options.formData) === 'object') { formData = []; $.each(options.formData, function (name, value) { formData.push({name: name, value: value}); @@ -270,10 +261,35 @@ return total; }, + _initProgressObject: function (obj) { + var progress = { + loaded: 0, + total: 0, + bitrate: 0 + }; + if (obj._progress) { + $.extend(obj._progress, progress); + } else { + obj._progress = progress; + } + }, + + _initResponseObject: function (obj) { + var prop; + if (obj._response) { + for (prop in obj._response) { + if (obj._response.hasOwnProperty(prop)) { + delete obj._response[prop]; + } + } + } else { + obj._response = {}; + } + }, + _onProgress: function (e, data) { if (e.lengthComputable) { - var now = +(new Date()), - total, + var now = (new Date()).getTime(), loaded; if (data._time && data.progressInterval && (now - data._time < data.progressInterval) && @@ -281,16 +297,19 @@ return; } data._time = now; - total = data.total || this._getTotal(data.files); - loaded = parseInt( - e.loaded / e.total * (data.chunkSize || total), - 10 + loaded = Math.floor( + e.loaded / e.total * (data.chunkSize || data._progress.total) ) + (data.uploadedBytes || 0); - this._loaded += loaded - (data.loaded || data.uploadedBytes || 0); - data.lengthComputable = true; - data.loaded = loaded; - data.total = total; - data.bitrate = data._bitrateTimer.getBitrate( + // Add the difference from the previously loaded state + // to the global loaded counter: + this._progress.loaded += (loaded - data._progress.loaded); + this._progress.bitrate = this._bitrateTimer.getBitrate( + now, + this._progress.loaded, + data.bitrateInterval + ); + data._progress.loaded = data.loaded = loaded; + data._progress.bitrate = data.bitrate = data._bitrateTimer.getBitrate( now, loaded, data.bitrateInterval @@ -301,16 +320,7 @@ this._trigger('progress', e, data); // Trigger a global progress event for all current file uploads, // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, { - lengthComputable: true, - loaded: this._loaded, - total: this._total, - bitrate: this._bitrateTimer.getBitrate( - now, - this._loaded, - data.bitrateInterval - ) - }); + this._trigger('progressall', e, this._progress); } }, @@ -495,6 +505,21 @@ return options; }, + // jQuery 1.6 doesn't provide .state(), + // while jQuery 1.8+ removed .isRejected() and .isResolved(): + _getDeferredState: function (deferred) { + if (deferred.state) { + return deferred.state(); + } + if (deferred.isResolved()) { + return 'resolved'; + } + if (deferred.isRejected()) { + return 'rejected'; + } + return 'pending'; + }, + // Maps jqXHR callbacks to the equivalent // methods of the given Promise object: _enhancePromise: function (promise) { @@ -519,6 +544,36 @@ return this._enhancePromise(promise); }, + // Adds convenience methods to the callback arguments: + _addConvenienceMethods: function (e, data) { + var that = this; + data.submit = function () { + if (this.state() !== 'pending') { + data.jqXHR = this.jqXHR = + (that._trigger('submit', e, this) !== false) && + that._onSend(e, this); + } + return this.jqXHR || that._getXHRPromise(); + }; + data.abort = function () { + if (this.jqXHR) { + return this.jqXHR.abort(); + } + return that._getXHRPromise(); + }; + data.state = function () { + if (this.jqXHR) { + return that._getDeferredState(this.jqXHR); + } + }; + data.progress = function () { + return this._progress; + }; + data.response = function () { + return this._response; + }; + }, + // Parses the Range header from the server response // and returns the uploaded bytes: _getUploadedBytes: function (jqXHR) { @@ -561,9 +616,10 @@ ); } // The chunk upload method: - upload = function (i) { + upload = function () { // Clone the options object for each chunk upload: - var o = $.extend({}, options); + var o = $.extend({}, options), + currentLoaded = o._progress.loaded; o.blob = slice.call( file, ub, @@ -585,10 +641,10 @@ .done(function (result, textStatus, jqXHR) { ub = that._getUploadedBytes(jqXHR) || (ub + o.chunkSize); - // Create a progress event if upload is done and no progress - // event has been invoked for this chunk, or there has been - // no progress event with loaded equaling total: - if (!o.loaded || o.loaded < o.total) { + // Create a progress event if no final progress event + // with loaded equaling total has been triggered + // for this chunk: + if (o._progress.loaded === currentLoaded) { that._onProgress($.Event('progress', { lengthComputable: true, loaded: ub - o.uploadedBytes, @@ -640,61 +696,66 @@ this._trigger('start'); // Set timer for global bitrate progress calculation: this._bitrateTimer = new this._BitrateTimer(); + // Reset the global progress values: + this._progress.loaded = this._progress.total = 0; + this._progress.bitrate = 0; } + // Make sure the container objects for the .response() and + // .progress() methods on the data object are available + // and reset to their initial state: + this._initResponseObject(data); + this._initProgressObject(data); + data._progress.loaded = data.loaded = data.uploadedBytes || 0; + data._progress.total = data.total = this._getTotal(data.files) || 1; + data._progress.bitrate = data.bitrate = 0; this._active += 1; // Initialize the global progress values: - this._loaded += data.uploadedBytes || 0; - this._total += this._getTotal(data.files); + this._progress.loaded += data.loaded; + this._progress.total += data.total; }, _onDone: function (result, textStatus, jqXHR, options) { - if (!this._isXHRUpload(options) || !options.loaded || - options.loaded < options.total) { - var total = this._getTotal(options.files) || 1; - // Create a progress event for each iframe load, - // or if there has been no progress event with - // loaded equaling total for XHR uploads: + var total = options._progress.total, + response = options._response; + if (options._progress.loaded < total) { + // Create a progress event if no final progress event + // with loaded equaling total has been triggered: this._onProgress($.Event('progress', { lengthComputable: true, loaded: total, total: total }), options); } - options.result = result; - options.textStatus = textStatus; - options.jqXHR = jqXHR; + response.result = options.result = result; + response.textStatus = options.textStatus = textStatus; + response.jqXHR = options.jqXHR = jqXHR; this._trigger('done', null, options); }, _onFail: function (jqXHR, textStatus, errorThrown, options) { - options.jqXHR = jqXHR; - options.textStatus = textStatus; - options.errorThrown = errorThrown; - this._trigger('fail', null, options); + var response = options._response; if (options.recalculateProgress) { // Remove the failed (error or abort) file upload from // the global progress calculation: - this._loaded -= options.loaded || options.uploadedBytes || 0; - this._total -= options.total || this._getTotal(options.files); + this._progress.loaded -= options._progress.loaded; + this._progress.total -= options._progress.total; } + response.jqXHR = options.jqXHR = jqXHR; + response.textStatus = options.textStatus = textStatus; + response.errorThrown = options.errorThrown = errorThrown; + this._trigger('fail', null, options); }, _onAlways: function (jqXHRorResult, textStatus, jqXHRorError, options) { // jqXHRorResult, textStatus and jqXHRorError are added to the // options object via done and fail callbacks - this._active -= 1; this._trigger('always', null, options); - if (this._active === 0) { - // The stop callback is triggered when all uploads have - // been completed, equivalent to the global ajaxStop event: - this._trigger('stop'); - // Reset the global progress values: - this._loaded = this._total = 0; - this._bitrateTimer = null; - } }, _onSend: function (e, data) { + if (!data.submit) { + this._addConvenienceMethods(e, data); + } var that = this, jqXHR, aborted, @@ -714,32 +775,32 @@ }).fail(function (jqXHR, textStatus, errorThrown) { that._onFail(jqXHR, textStatus, errorThrown, options); }).always(function (jqXHRorResult, textStatus, jqXHRorError) { - that._sending -= 1; that._onAlways( jqXHRorResult, textStatus, jqXHRorError, options ); + that._sending -= 1; + that._active -= 1; if (options.limitConcurrentUploads && options.limitConcurrentUploads > that._sending) { // Start the next queued upload, // that has not been aborted: - var nextSlot = that._slots.shift(), - isPending; + var nextSlot = that._slots.shift(); while (nextSlot) { - // jQuery 1.6 doesn't provide .state(), - // while jQuery 1.8+ removed .isRejected(): - isPending = nextSlot.state ? - nextSlot.state() === 'pending' : - !nextSlot.isRejected(); - if (isPending) { + if (that._getDeferredState(nextSlot) === 'pending') { nextSlot.resolve(); break; } nextSlot = that._slots.shift(); } } + if (that._active === 0) { + // The stop callback is triggered when all uploads have + // been completed, equivalent to the global ajaxStop event: + that._trigger('stop'); + } }); return jqXHR; }; @@ -805,12 +866,9 @@ var newData = $.extend({}, data); newData.files = fileSet ? element : [element]; newData.paramName = paramNameSet[index]; - newData.submit = function () { - newData.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); - return this.jqXHR; - }; + that._initResponseObject(newData); + that._initProgressObject(newData); + that._addConvenienceMethods(e, newData); result = that._trigger('add', e, newData); return result; }); @@ -1084,12 +1142,23 @@ this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); - this._sending = this._active = this._loaded = this._total = 0; + this._sending = this._active = 0; + this._initProgressObject(this); this._initEventHandlers(); }, - _destroy: function () { - this._destroyEventHandlers(); + // This method is exposed to the widget API and allows to query + // the number of active uploads: + active: function () { + return this._active; + }, + + // This method is exposed to the widget API and allows to query + // the widget upload progress. + // It returns an object with loaded, total and bitrate properties + // for the running uploads: + progress: function () { + return this._progress; }, // This method is exposed to the widget API and allows adding files diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js old mode 100755 new mode 100644 diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js index 72edfdb..fcef339 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js @@ -1,5 +1,5 @@ /* - * JavaScript Canvas to Blob 2.0.3 + * JavaScript Canvas to Blob 2.0.5 * https://github.com/blueimp/JavaScript-Canvas-to-Blob * * Copyright 2012, Sebastian Tschan @@ -72,12 +72,16 @@ }; if (window.HTMLCanvasElement && !CanvasPrototype.toBlob) { if (CanvasPrototype.mozGetAsFile) { - CanvasPrototype.toBlob = function (callback, type) { - callback(this.mozGetAsFile('blob', type)); + CanvasPrototype.toBlob = function (callback, type, quality) { + if (quality && CanvasPrototype.toDataURL && dataURLtoBlob) { + callback(dataURLtoBlob(this.toDataURL(type, quality))); + } else { + callback(this.mozGetAsFile('blob', type)); + } }; } else if (CanvasPrototype.toDataURL && dataURLtoBlob) { - CanvasPrototype.toBlob = function (callback, type) { - callback(dataURLtoBlob(this.toDataURL(type))); + CanvasPrototype.toBlob = function (callback, type, quality) { + callback(dataURLtoBlob(this.toDataURL(type, quality))); }; } } diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js old mode 100755 new mode 100644 index f5d71a1..fd2948f --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,5 +1,5 @@ /* - * jQuery UI Widget 1.10.0+amd + * jQuery UI Widget 1.10.1+amd * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2013 jQuery Foundation and other contributors diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js index 96a149e..0155d38 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js +++ b/vendor/assets/javascripts/jquery-fileupload/vendor/load-image.js @@ -1,15 +1,18 @@ /* - * JavaScript Load Image 1.2.1 + * JavaScript Load Image 1.3.1 * https://github.com/blueimp/JavaScript-Load-Image * * Copyright 2011, Sebastian Tschan * https://blueimp.net * + * iOS image scaling fixes based on + * https://github.com/stomita/ios-imagefile-megapixel + * * Licensed under the MIT license: * http://www.opensource.org/licenses/MIT */ -/*jslint nomen: true */ +/*jslint nomen: true, bitwise: true */ /*global window, document, URL, webkitURL, Blob, File, FileReader, define */ (function ($) { @@ -34,6 +37,8 @@ // (Firefox 3.6) support the File API but not Blobs: (window.File && file instanceof File)) { url = oUrl = loadImage.createObjectURL(file); + // Store the file type for resize processing: + img._type = file.type; } else { url = file; } @@ -41,8 +46,13 @@ img.src = url; return img; } - return loadImage.readFile(file, function (url) { - img.src = url; + return loadImage.readFile(file, function (e) { + var target = e.target; + if (target && target.result) { + img.src = target.result; + } else { + callback(e); + } }); }, // The check for URL.revokeObjectURL fixes an issue with Opera 12, @@ -51,6 +61,99 @@ (window.URL && URL.revokeObjectURL && URL) || (window.webkitURL && webkitURL); + // Detects subsampling in JPEG images: + loadImage.detectSubsampling = function (img) { + var iw = img.width, + ih = img.height, + canvas, + ctx; + if (iw * ih > 1024 * 1024) { // only consider mexapixel images + canvas = document.createElement('canvas'); + canvas.width = canvas.height = 1; + ctx = canvas.getContext('2d'); + ctx.drawImage(img, -iw + 1, 0); + // subsampled image becomes half smaller in rendering size. + // check alpha channel value to confirm image is covering edge pixel or not. + // if alpha value is 0 image is not covering, hence subsampled. + return ctx.getImageData(0, 0, 1, 1).data[3] === 0; + } + return false; + }; + + // Detects vertical squash in JPEG images: + loadImage.detectVerticalSquash = function (img, ih) { + var canvas = document.createElement('canvas'), + ctx = canvas.getContext('2d'), + data, + sy, + ey, + py, + alpha; + canvas.width = 1; + canvas.height = ih; + ctx.drawImage(img, 0, 0); + data = ctx.getImageData(0, 0, 1, ih).data; + // search image edge pixel position in case it is squashed vertically: + sy = 0; + ey = ih; + py = ih; + while (py > sy) { + alpha = data[(py - 1) * 4 + 3]; + if (alpha === 0) { + ey = py; + } else { + sy = py; + } + py = (ey + sy) >> 1; + } + return (py / ih) || 1; + }; + + // Renders image to canvas while working around iOS image scaling bugs: + // https://github.com/blueimp/JavaScript-Load-Image/issues/13 + loadImage.renderImageToCanvas = function (img, canvas, width, height) { + var iw = img.width, + ih = img.height, + ctx = canvas.getContext('2d'), + vertSquashRatio, + d = 1024, // size of tiling canvas + tmpCanvas = document.createElement('canvas'), + tmpCtx, + dw, + dh, + dx, + dy, + sx, + sy; + ctx.save(); + if (loadImage.detectSubsampling(img)) { + iw /= 2; + ih /= 2; + } + vertSquashRatio = loadImage.detectVerticalSquash(img, ih); + tmpCanvas.width = tmpCanvas.height = d; + tmpCtx = tmpCanvas.getContext('2d'); + dw = Math.ceil(d * width / iw); + dh = Math.ceil(d * height / ih / vertSquashRatio); + dy = 0; + sy = 0; + while (sy < ih) { + dx = 0; + sx = 0; + while (sx < iw) { + tmpCtx.clearRect(0, 0, d, d); + tmpCtx.drawImage(img, -sx, -sy); + ctx.drawImage(tmpCanvas, 0, 0, d, d, dx, dy, dw, dh); + sx += d; + dx += dw; + } + sy += d; + dy += dh; + } + ctx.restore(); + tmpCanvas = tmpCtx = null; + }; + // Scales the given image (img or canvas HTML element) // using the given options. // Returns a canvas object if the browser supports canvas @@ -66,22 +169,27 @@ (options.minHeight || height) / height ); if (scale > 1) { - width = parseInt(width * scale, 10); - height = parseInt(height * scale, 10); + width = Math.ceil(width * scale); + height = Math.ceil(height * scale); } scale = Math.min( (options.maxWidth || width) / width, (options.maxHeight || height) / height ); if (scale < 1) { - width = parseInt(width * scale, 10); - height = parseInt(height * scale, 10); + width = Math.ceil(width * scale); + height = Math.ceil(height * scale); } if (img.getContext || (options.canvas && canvas.getContext)) { canvas.width = width; canvas.height = height; - canvas.getContext('2d') - .drawImage(img, 0, 0, width, height); + if (img._type === 'image/jpeg') { + loadImage + .renderImageToCanvas(img, canvas, width, height); + } else { + canvas.getContext('2d') + .drawImage(img, 0, 0, width, height); + } return canvas; } img.width = width; @@ -98,13 +206,12 @@ }; // Loads a given File object via FileReader interface, - // invokes the callback with a data url: + // invokes the callback with the event object (load or error). + // The result can be read via event.target.result: loadImage.readFile = function (file, callback) { if (window.FileReader && FileReader.prototype.readAsDataURL) { var fileReader = new FileReader(); - fileReader.onload = function (e) { - callback(e.target.result); - }; + fileReader.onload = fileReader.onerror = callback; fileReader.readAsDataURL(file); return fileReader; } From 3dce496cda19b33a7a63cf41c3cc9ec884fdcf5c Mon Sep 17 00:00:00 2001 From: Rafael Gaspar Date: Wed, 22 May 2013 14:45:01 -0300 Subject: [PATCH 31/33] Update Assets --- vendor/assets/images/loading.gif | Bin vendor/assets/images/progressbar.gif | Bin .../cors/jquery.postmessage-transport.js | 0 .../cors/jquery.xdr-transport.js | 0 .../jquery.fileupload-angular.js | 348 ++++++++++++++++++ .../jquery.fileupload-process.js | 158 ++++++++ .../jquery.fileupload-resize.js | 212 +++++++++++ .../jquery-fileupload/jquery.fileupload-ui.js | 280 +++----------- .../jquery.fileupload-validate.js | 116 ++++++ .../jquery-fileupload/jquery.fileupload.js | 166 ++++++--- .../jquery.iframe-transport.js | 12 +- .../stylesheets/jquery.fileupload-ui.scss | 53 +-- 12 files changed, 1027 insertions(+), 318 deletions(-) mode change 100644 => 100755 vendor/assets/images/loading.gif mode change 100644 => 100755 vendor/assets/images/progressbar.gif mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js create mode 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js create mode 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js create mode 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-resize.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js create mode 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js mode change 100644 => 100755 vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js mode change 100644 => 100755 vendor/assets/stylesheets/jquery.fileupload-ui.scss diff --git a/vendor/assets/images/loading.gif b/vendor/assets/images/loading.gif old mode 100644 new mode 100755 diff --git a/vendor/assets/images/progressbar.gif b/vendor/assets/images/progressbar.gif old mode 100644 new mode 100755 diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js old mode 100644 new mode 100755 diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js b/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js old mode 100644 new mode 100755 diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js new file mode 100755 index 0000000..e7ba784 --- /dev/null +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js @@ -0,0 +1,348 @@ +/* + * jQuery File Upload AngularJS Plugin 1.0.1 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, unparam: true */ +/*global angular */ + +(function () { + 'use strict'; + + angular.module('blueimp.fileupload', []) + + .provider('fileUpload', function () { + var scopeApply = function () { + var scope = angular.element(this) + .fileupload('option', 'scope')(); + if (!scope.$$phase) { + scope.$apply(); + } + }, + $config; + $config = this.defaults = { + handleResponse: function (e, data) { + var files = data.result && data.result.files; + if (files) { + data.scope().replace(data.files, files); + } else if (data.errorThrown || + data.textStatus === 'error') { + data.files[0].error = data.errorThrown || + data.textStatus; + } + }, + add: function (e, data) { + var scope = data.scope(); + data.process(function () { + return scope.process(data); + }).always( + function () { + var file = data.files[0], + submit = function () { + return data.submit(); + }; + file.$cancel = function () { + scope.clear(data.files); + return data.abort(); + }; + file.$state = function () { + return data.state(); + }; + file.$progress = function () { + return data.progress(); + }; + file.$response = function () { + return data.response(); + }; + if (file.$state() === 'rejected') { + file._$submit = submit; + } else { + file.$submit = submit; + } + scope.$apply(function () { + var method = scope.option('prependFiles') ? + 'unshift' : 'push'; + Array.prototype[method].apply( + scope.queue, + data.files + ); + if (file.$submit && + (scope.option('autoUpload') || + data.autoUpload) && + data.autoUpload !== false) { + file.$submit(); + } + }); + } + ); + }, + progress: function (e, data) { + data.scope().$apply(); + }, + done: function (e, data) { + var that = this; + data.scope().$apply(function () { + data.handleResponse.call(that, e, data); + }); + }, + fail: function (e, data) { + var that = this; + if (data.errorThrown === 'abort') { + return; + } + if (data.dataType.indexOf('json') === data.dataType.length - 4) { + try { + data.result = angular.fromJson(data.jqXHR.responseText); + } catch (err) {} + } + data.scope().$apply(function () { + data.handleResponse.call(that, e, data); + }); + }, + stop: scopeApply, + processstart: scopeApply, + processstop: scopeApply, + getNumberOfFiles: function () { + return this.scope().queue.length; + }, + dataType: 'json', + prependFiles: true, + autoUpload: false + }; + this.$get = [ + function () { + return { + defaults: $config + }; + } + ]; + }) + + .provider('formatFileSizeFilter', function () { + var $config = this.defaults = { + // Byte units following the IEC format + // http://en.wikipedia.org/wiki/Kilobyte + units: [ + {size: 1000000000, suffix: ' GB'}, + {size: 1000000, suffix: ' MB'}, + {size: 1000, suffix: ' KB'} + ] + }; + this.$get = function () { + return function (bytes) { + if (!angular.isNumber(bytes)) { + return ''; + } + var unit = true, + i = -1; + while (unit) { + unit = $config.units[i += 1]; + if (i === $config.units.length - 1 || bytes >= unit.size) { + return (bytes / unit.size).toFixed(2) + unit.suffix; + } + } + }; + }; + }) + + .controller('FileUploadController', [ + '$scope', '$element', '$attrs', 'fileUpload', + function ($scope, $element, $attrs, fileUpload) { + $scope.disabled = angular.element('') + .prop('disabled'); + $scope.queue = $scope.queue || []; + $scope.clear = function (files) { + var queue = this.queue, + i = queue.length, + file = files, + length = 1; + if (angular.isArray(files)) { + file = files[0]; + length = files.length; + } + while (i) { + if (queue[i -= 1] === file) { + return queue.splice(i, length); + } + } + }; + $scope.replace = function (oldFiles, newFiles) { + var queue = this.queue, + file = oldFiles[0], + i, + j; + for (i = 0; i < queue.length; i += 1) { + if (queue[i] === file) { + for (j = 0; j < newFiles.length; j += 1) { + queue[i + j] = newFiles[j]; + } + return; + } + } + }; + $scope.progress = function () { + return $element.fileupload('progress'); + }; + $scope.active = function () { + return $element.fileupload('active'); + }; + $scope.option = function (option, data) { + return $element.fileupload('option', option, data); + }; + $scope.add = function (data) { + return $element.fileupload('add', data); + }; + $scope.send = function (data) { + return $element.fileupload('send', data); + }; + $scope.process = function (data) { + return $element.fileupload('process', data); + }; + $scope.processing = function (data) { + return $element.fileupload('processing', data); + }; + $scope.applyOnQueue = function (method) { + var list = this.queue.slice(0), + i, + file; + for (i = 0; i < list.length; i += 1) { + file = list[i]; + if (file[method]) { + file[method](); + } + } + }; + $scope.submit = function () { + this.applyOnQueue('$submit'); + }; + $scope.cancel = function () { + this.applyOnQueue('$cancel'); + }; + // The fileupload widget will initialize with + // the options provided via "data-"-parameters, + // as well as those given via options object: + $element.fileupload(angular.extend( + {scope: function () { + return $scope; + }}, + fileUpload.defaults + )).on('fileuploadadd', function (e, data) { + data.scope = $scope.option('scope'); + }).on([ + 'fileuploadadd', + 'fileuploadsubmit', + 'fileuploadsend', + 'fileuploaddone', + 'fileuploadfail', + 'fileuploadalways', + 'fileuploadprogress', + 'fileuploadprogressall', + 'fileuploadstart', + 'fileuploadstop', + 'fileuploadchange', + 'fileuploadpaste', + 'fileuploaddrop', + 'fileuploaddragover', + 'fileuploadchunksend', + 'fileuploadchunkdone', + 'fileuploadchunkfail', + 'fileuploadchunkalways', + 'fileuploadprocessstart', + 'fileuploadprocess', + 'fileuploadprocessdone', + 'fileuploadprocessfail', + 'fileuploadprocessalways', + 'fileuploadprocessstop' + ].join(' '), function (e, data) { + $scope.$emit(e.type, data); + }); + // Observe option changes: + $scope.$watch( + $attrs.fileupload, + function (newOptions, oldOptions) { + if (newOptions) { + $element.fileupload('option', newOptions); + } + } + ); + } + ]) + + .controller('FileUploadProgressController', [ + '$scope', '$attrs', '$parse', + function ($scope, $attrs, $parse) { + var fn = $parse($attrs.progress), + update = function () { + var progress = fn($scope); + if (!progress || !progress.total) { + return; + } + $scope.num = Math.floor( + progress.loaded / progress.total * 100 + ); + }; + update(); + $scope.$watch( + $attrs.progress + '.loaded', + function (newValue, oldValue) { + if (newValue !== oldValue) { + update(); + } + } + ); + } + ]) + + .controller('FileUploadPreviewController', [ + '$scope', '$element', '$attrs', '$parse', + function ($scope, $element, $attrs, $parse) { + var fn = $parse($attrs.preview), + file = fn($scope); + if (file.preview) { + $element.append(file.preview); + } + } + ]) + + .directive('fileupload', function () { + return { + controller: 'FileUploadController' + }; + }) + + .directive('progress', function () { + return { + controller: 'FileUploadProgressController' + }; + }) + + .directive('preview', function () { + return { + controller: 'FileUploadPreviewController' + }; + }) + + .directive('download', function () { + return function (scope, elm, attrs) { + elm.on('dragstart', function (e) { + try { + e.originalEvent.dataTransfer.setData( + 'DownloadURL', + [ + 'application/octet-stream', + elm.prop('download'), + elm.prop('href') + ].join(':') + ); + } catch (err) {} + }); + }; + }); + +}()); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js new file mode 100755 index 0000000..2f9eeed --- /dev/null +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js @@ -0,0 +1,158 @@ +/* + * jQuery File Upload Processing Plugin 1.1 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2012, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, unparam: true */ +/*global define, window */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + './jquery.fileupload' + ], factory); + } else { + // Browser globals: + factory( + window.jQuery + ); + } +}(function ($) { + 'use strict'; + + var originalAdd = $.blueimp.fileupload.prototype.options.add; + + // The File Upload Processing plugin extends the fileupload widget + // with file processing functionality: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + // The list of processing actions: + processQueue: [ + /* + { + action: 'log', + type: 'debug' + } + */ + ], + add: function (e, data) { + var $this = $(this); + data.process(function () { + return $this.fileupload('process', data); + }); + originalAdd.call(this, e, data); + } + }, + + processActions: { + /* + log: function (data, options) { + console[options.type]( + 'Processing "' + data.files[data.index].name + '"' + ); + } + */ + }, + + _processFile: function (data) { + var that = this, + dfd = $.Deferred().resolveWith(that, [data]), + chain = dfd.promise(); + this._trigger('process', null, data); + $.each(data.processQueue, function (i, settings) { + var func = function (data) { + return that.processActions[settings.action].call( + that, + data, + settings + ); + }; + chain = chain.pipe(func, settings.always && func); + }); + chain + .done(function () { + that._trigger('processdone', null, data); + that._trigger('processalways', null, data); + }) + .fail(function () { + that._trigger('processfail', null, data); + that._trigger('processalways', null, data); + }); + return chain; + }, + + // Replaces the settings of each processQueue item that + // are strings starting with an "@", using the remaining + // substring as key for the option map, + // e.g. "@autoUpload" is replaced with options.autoUpload: + _transformProcessQueue: function (options) { + var processQueue = []; + $.each(options.processQueue, function () { + var settings = {}; + $.each(this, function (key, value) { + if ($.type(value) === 'string' && + value.charAt(0) === '@') { + settings[key] = options[value.slice(1)]; + } else { + settings[key] = value; + } + }); + processQueue.push(settings); + }); + options.processQueue = processQueue; + }, + + // Returns the number of files currently in the processsing queue: + processing: function () { + return this._processing; + }, + + // Processes the files given as files property of the data parameter, + // returns a Promise object that allows to bind callbacks: + process: function (data) { + var that = this, + options = $.extend({}, this.options, data); + if (options.processQueue && options.processQueue.length) { + this._transformProcessQueue(options); + if (this._processing === 0) { + this._trigger('processstart'); + } + $.each(data.files, function (index, file) { + var opts = index ? $.extend({}, options) : options, + func = function () { + return that._processFile(opts); + }; + opts.index = index; + that._processing += 1; + that._processingQueue = that._processingQueue.pipe(func, func) + .always(function () { + that._processing -= 1; + if (that._processing === 0) { + that._trigger('processstop'); + } + }); + }); + } + return this._processingQueue; + }, + + _create: function () { + this._super(); + this._processing = 0; + this._processingQueue = $.Deferred().resolveWith(this) + .promise(); + } + + }); + +})); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-resize.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-resize.js new file mode 100755 index 0000000..ae5c5be --- /dev/null +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-resize.js @@ -0,0 +1,212 @@ +/* + * jQuery File Upload Image Resize Plugin 1.1.2 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, unparam: true, regexp: true */ +/*global define, window */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + 'load-image', + 'canvas-to-blob', + './jquery.fileupload-process' + ], factory); + } else { + // Browser globals: + factory( + window.jQuery, + window.loadImage + ); + } +}(function ($, loadImage) { + 'use strict'; + + // Prepend to the default processQueue: + $.blueimp.fileupload.prototype.options.processQueue.unshift( + { + action: 'loadImage', + fileTypes: '@loadImageFileTypes', + maxFileSize: '@loadImageMaxFileSize', + noRevoke: '@loadImageNoRevoke', + disabled: '@disableImageLoad' + }, + { + action: 'resizeImage', + maxWidth: '@imageMaxWidth', + maxHeight: '@imageMaxHeight', + minWidth: '@imageMinWidth', + minHeight: '@imageMinHeight', + crop: '@imageCrop', + disabled: '@disableImageResize' + }, + { + action: 'saveImage', + disabled: '@disableImageResize' + }, + { + action: 'resizeImage', + maxWidth: '@previewMaxWidth', + maxHeight: '@previewMaxHeight', + minWidth: '@previewMinWidth', + minHeight: '@previewMinHeight', + crop: '@previewCrop', + canvas: '@previewAsCanvas', + disabled: '@disableImagePreview' + }, + { + action: 'setImage', + // The name of the property the resized image + // is saved as on the associated file object: + name: 'preview', + disabled: '@disableImagePreview' + } + ); + + // The File Upload Resize plugin extends the fileupload widget + // with image resize functionality: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + // The regular expression for the types of images to load: + // matched against the file type: + loadImageFileTypes: /^image\/(gif|jpeg|png)$/, + // The maximum file size of images to load: + loadImageMaxFileSize: 5000000, // 5MB + // The maximum width of resized images: + imageMaxWidth: 1920, + // The maximum height of resized images: + imageMaxHeight: 1080, + // Define if resized images should be cropped or only scaled: + imageCrop: false, + // Disable the resize image functionality by default: + disableImageResize: true, + // The maximum width of the preview images: + previewMaxWidth: 80, + // The maximum height of the preview images: + previewMaxHeight: 80, + // Define if preview images should be cropped or only scaled: + previewCrop: false, + // Define if preview images should be resized as canvas elements: + previewAsCanvas: true + }, + + processActions: { + + // Loads the image given via data.files and data.index + // as img element if the browser supports canvas. + // Accepts the options fileTypes (regular expression) + // and maxFileSize (integer) to limit the files to load: + loadImage: function (data, options) { + if (options.disabled) { + return data; + } + var that = this, + file = data.files[data.index], + dfd = $.Deferred(); + if (($.type(options.maxFileSize) === 'number' && + file.size > options.maxFileSize) || + (options.fileTypes && + !options.fileTypes.test(file.type)) || + !loadImage( + file, + function (img) { + if (!img.src) { + return dfd.rejectWith(that, [data]); + } + data.img = img; + dfd.resolveWith(that, [data]); + }, + options + )) { + dfd.rejectWith(that, [data]); + } + return dfd.promise(); + }, + + // Resizes the image given as data.canvas or data.img + // and updates data.canvas or data.img with the resized image. + // Accepts the options maxWidth, maxHeight, minWidth, + // minHeight, canvas and crop: + resizeImage: function (data, options) { + options = $.extend({canvas: true}, options); + var img = (options.canvas && data.canvas) || data.img, + canvas; + if (img && !options.disabled) { + canvas = loadImage.scale(img, options); + if (canvas && (canvas.width !== img.width || + canvas.height !== img.height)) { + data[canvas.getContext ? 'canvas' : 'img'] = canvas; + } + } + return data; + }, + + // Saves the processed image given as data.canvas + // inplace at data.index of data.files: + saveImage: function (data, options) { + if (!data.canvas || options.disabled) { + return data; + } + var that = this, + file = data.files[data.index], + name = file.name, + dfd = $.Deferred(), + callback = function (blob) { + if (!blob.name) { + if (file.type === blob.type) { + blob.name = file.name; + } else if (file.name) { + blob.name = file.name.replace( + /\..+$/, + '.' + blob.type.substr(6) + ); + } + } + // Store the created blob at the position + // of the original file in the files list: + data.files[data.index] = blob; + dfd.resolveWith(that, [data]); + }; + // Use canvas.mozGetAsFile directly, to retain the filename, as + // Gecko doesn't support the filename option for FormData.append: + if (data.canvas.mozGetAsFile) { + callback(data.canvas.mozGetAsFile( + (/^image\/(jpeg|png)$/.test(file.type) && name) || + ((name && name.replace(/\..+$/, '')) || + 'blob') + '.png', + file.type + )); + } else if (data.canvas.toBlob) { + data.canvas.toBlob(callback, file.type); + } else { + return data; + } + return dfd.promise(); + }, + + // Sets the resized version of the image as a property of the + // file object, must be called after "saveImage": + setImage: function (data, options) { + var img = data.canvas || data.img; + if (img && !options.disabled) { + data.files[data.index][options.name] = img; + } + return data; + } + + } + + }); + +})); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js old mode 100644 new mode 100755 index 0386fb2..5d22346 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 7.4.1 + * jQuery File Upload User Interface Plugin 8.2.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -19,20 +19,25 @@ define([ 'jquery', 'tmpl', - 'load-image', - './jquery.fileupload-fp' + './jquery.fileupload-resize', + './jquery.fileupload-validate' ], factory); } else { // Browser globals: factory( window.jQuery, - window.tmpl, - window.loadImage + window.tmpl ); } }(function ($, tmpl, loadImage) { 'use strict'; + $.blueimp.fileupload.prototype._specialOptions.push( + 'filesContainer', + 'uploadTemplateId', + 'downloadTemplateId' + ); + // The UI version extends the file upload widget // and adds complete user interface interaction: $.widget('blueimp.fileupload', $.blueimp.fileupload, { @@ -42,29 +47,6 @@ // as the user clicks on the start buttons. To enable automatic // uploads, set the following option to true: autoUpload: false, - // The following option limits the number of files that are - // allowed to be uploaded using this widget: - maxNumberOfFiles: undefined, - // The maximum allowed file size: - maxFileSize: undefined, - // The minimum allowed file size: - minFileSize: undefined, - // The regular expression for allowed file types, matches - // against either file type or file name: - acceptFileTypes: /.+$/i, - // The regular expression to define for which files a preview - // image is shown, matched against the file type: - previewSourceFileTypes: /^image\/(gif|jpeg|png)$/, - // The maximum file size of images that are to be displayed as preview: - previewSourceMaxFileSize: 5000000, // 5MB - // The maximum width of the preview images: - previewMaxWidth: 80, - // The maximum height of the preview images: - previewMaxHeight: 80, - // By default, preview images are displayed as canvas elements - // if supported by the browser. Set the following option to false - // to always display preview images as img elements: - previewAsCanvas: true, // The ID of the upload template: uploadTemplateId: 'template-upload', // The ID of the download template: @@ -79,29 +61,43 @@ // option of the $.ajax upload requests: dataType: 'json', + // Function returning the current number of files, + // used by the maxNumberOfFiles validation: + getNumberOfFiles: function () { + return this.filesContainer.children().length; + }, + + // Callback to retrieve the list of files from the server response: + getFilesFromResponse: function (data) { + if (data.result && $.isArray(data.result.files)) { + return data.result.files; + } + return []; + }, + // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop or add API call). // See the basic file upload widget for more information: add: function (e, data) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), + var $this = $(this), + that = $this.data('blueimp-fileupload') || + $this.data('fileupload'), options = that.options, files = data.files; - $(this).fileupload('process', data).done(function () { - that._adjustMaxNumberOfFiles(-files.length); - data.maxNumberOfFilesAdjusted = true; - data.files.valid = data.isValidated = that._validate(files); + data.process(function () { + return $this.fileupload('process', data); + }).always(function () { data.context = that._renderUpload(files).data('data', data); + that._renderPreviews(data); options.filesContainer[ options.prependFiles ? 'prepend' : 'append' ](data.context); - that._renderPreviews(data); that._forceReflow(data.context); that._transition(data.context).done( function () { if ((that._trigger('added', e, data) !== false) && (options.autoUpload || data.autoUpload) && - data.autoUpload !== false && data.isValidated) { + data.autoUpload !== false && !data.files.error) { data.submit(); } } @@ -112,15 +108,6 @@ send: function (e, data) { var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); - if (!data.isValidated) { - if (!data.maxNumberOfFilesAdjusted) { - that._adjustMaxNumberOfFiles(-data.files.length); - data.maxNumberOfFilesAdjusted = true; - } - if (!that._validate(data.files)) { - return false; - } - } if (data.context && data.dataType && data.dataType.substr(0, 6) === 'iframe') { // Iframe Transport does not support progress events. @@ -142,7 +129,9 @@ done: function (e, data) { var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'), - files = that._getFilesFromResponse(data), + getFilesFromResponse = data.getFilesFromResponse || + that.options.getFilesFromResponse, + files = getFilesFromResponse(data), template, deferred; if (data.context) { @@ -150,9 +139,6 @@ var file = files[index] || {error: 'Empty file upload result'}, deferred = that._addFinishedDeferreds(); - if (file.error) { - that._adjustMaxNumberOfFiles(1); - } that._transition($(this)).done( function () { var node = $(this); @@ -171,17 +157,6 @@ ); }); } else { - if (files.length) { - $.each(files, function (index, file) { - if (data.maxNumberOfFilesAdjusted && file.error) { - that._adjustMaxNumberOfFiles(1); - } else if (!data.maxNumberOfFilesAdjusted && - !file.error) { - that._adjustMaxNumberOfFiles(-1); - } - }); - data.maxNumberOfFilesAdjusted = true; - } template = that._renderDownload(files) .appendTo(that.options.filesContainer); that._forceReflow(template); @@ -202,9 +177,6 @@ $(this).data('fileupload'), template, deferred; - if (data.maxNumberOfFilesAdjusted) { - that._adjustMaxNumberOfFiles(data.files.length); - } if (data.context) { data.context.each(function (index) { if (data.errorThrown !== 'abort') { @@ -323,20 +295,26 @@ } ); }, + processstart: function () { + $(this).addClass('fileupload-processing'); + }, + processstop: function () { + $(this).removeClass('fileupload-processing'); + }, // Callback for file deletion: destroy: function (e, data) { var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); if (data.url) { - $.ajax(data); - that._adjustMaxNumberOfFiles(1); + $.ajax(data).done(function () { + that._transition(data.context).done( + function () { + $(this).remove(); + that._trigger('destroyed', e, data); + } + ); + }); } - that._transition(data.context).done( - function () { - $(this).remove(); - that._trigger('destroyed', e, data); - } - ); } }, @@ -356,13 +334,6 @@ return this._finishedUploads; }, - _getFilesFromResponse: function (data) { - if (data.result && $.isArray(data.result.files)) { - return data.result.files; - } - return []; - }, - // Link handler, that allows to download files // by drag & drop of the links to the desktop: _enableDragToDesktop: function () { @@ -376,21 +347,10 @@ 'DownloadURL', [type, name, url].join(':') ); - } catch (err) {} + } catch (ignore) {} }); }, - _adjustMaxNumberOfFiles: function (operand) { - if (typeof this.options.maxNumberOfFiles === 'number') { - this.options.maxNumberOfFiles += operand; - if (this.options.maxNumberOfFiles < 1) { - this._disableFileInputButton(); - } else { - this._enableFileInputButton(); - } - } - }, - _formatFileSize: function (bytes) { if (typeof bytes !== 'number') { return ''; @@ -446,46 +406,6 @@ this._formatFileSize(data.total); }, - _hasError: function (file) { - if (file.error) { - return file.error; - } - // The number of added files is subtracted from - // maxNumberOfFiles before validation, so we check if - // maxNumberOfFiles is below 0 (instead of below 1): - if (this.options.maxNumberOfFiles < 0) { - return 'Maximum number of files exceeded'; - } - // Files are accepted if either the file type or the file name - // matches against the acceptFileTypes regular expression, as - // only browsers with support for the File API report the type: - if (!(this.options.acceptFileTypes.test(file.type) || - this.options.acceptFileTypes.test(file.name))) { - return 'Filetype not allowed'; - } - if (this.options.maxFileSize && - file.size > this.options.maxFileSize) { - return 'File is too big'; - } - if (typeof file.size === 'number' && - file.size < this.options.minFileSize) { - return 'File is too small'; - } - return null; - }, - - _validate: function (files) { - var that = this, - valid = !!files.length; - $.each(files, function (index, file) { - file.error = that._hasError(file); - if (file.error) { - valid = false; - } - }); - return valid; - }, - _renderTemplate: function (func, files) { if (!func) { return $(); @@ -501,63 +421,10 @@ return $(this.options.templatesContainer).html(result).children(); }, - _renderPreview: function (file, node) { - var that = this, - options = this.options, - dfd = $.Deferred(); - return ((loadImage && loadImage( - file, - function (img) { - node.append(img); - that._forceReflow(node); - that._transition(node).done(function () { - dfd.resolveWith(node); - }); - if (!$.contains(that.document[0].body, node[0])) { - // If the element is not part of the DOM, - // transition events are not triggered, - // so we have to resolve manually: - dfd.resolveWith(node); - } - node.on('remove', function () { - // If the element is removed before the - // transition finishes, transition events are - // not triggered, resolve manually: - dfd.resolveWith(node); - }); - }, - { - maxWidth: options.previewMaxWidth, - maxHeight: options.previewMaxHeight, - canvas: options.previewAsCanvas - } - )) || dfd.resolveWith(node)) && dfd; - }, - _renderPreviews: function (data) { - var that = this, - options = this.options; - data.context.find('.preview span').each(function (index, element) { - var file = data.files[index]; - if (options.previewSourceFileTypes.test(file.type) && - ($.type(options.previewSourceMaxFileSize) !== 'number' || - file.size < options.previewSourceMaxFileSize)) { - that._processingQueue = that._processingQueue.pipe(function () { - var dfd = $.Deferred(), - ev = $.Event('previewdone', { - target: element - }); - that._renderPreview(file, $(element)).done( - function () { - that._trigger(ev.type, ev, data); - dfd.resolveWith(that); - } - ); - return dfd.promise(); - }); - } + data.context.find('.preview').each(function (index, elm) { + $(elm).append(data.files[index].preview); }); - return this._processingQueue; }, _renderUpload: function (files) { @@ -601,8 +468,7 @@ var button = $(e.currentTarget); this._trigger('destroy', e, $.extend({ context: button.closest('.template-download'), - type: 'DELETE', - dataType: this.options.dataType + type: 'DELETE' }, button.data())); }, @@ -731,54 +597,14 @@ } }, - _stringToRegExp: function (str) { - var parts = str.split('/'), - modifiers = parts.pop(); - parts.shift(); - return new RegExp(parts.join('/'), modifiers); - }, - - _initRegExpOptions: function () { - var options = this.options; - if ($.type(options.acceptFileTypes) === 'string') { - options.acceptFileTypes = this._stringToRegExp( - options.acceptFileTypes - ); - } - if ($.type(options.previewSourceFileTypes) === 'string') { - options.previewSourceFileTypes = this._stringToRegExp( - options.previewSourceFileTypes - ); - } - }, - _initSpecialOptions: function () { this._super(); this._initFilesContainer(); this._initTemplates(); - this._initRegExpOptions(); - }, - - _setOption: function (key, value) { - this._super(key, value); - if (key === 'maxNumberOfFiles') { - this._adjustMaxNumberOfFiles(0); - } }, _create: function () { this._super(); - this._refreshOptionsList.push( - 'filesContainer', - 'uploadTemplateId', - 'downloadTemplateId' - ); - if (!this._processingQueue) { - this._processingQueue = $.Deferred().resolveWith(this).promise(); - this.process = function () { - return this._processingQueue; - }; - } this._resetFinishedDeferreds(); }, diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js new file mode 100755 index 0000000..2599da8 --- /dev/null +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js @@ -0,0 +1,116 @@ +/* + * jQuery File Upload Validation Plugin 1.0.2 + * https://github.com/blueimp/jQuery-File-Upload + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/*jslint nomen: true, unparam: true, regexp: true */ +/*global define, window */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + './jquery.fileupload-process' + ], factory); + } else { + // Browser globals: + factory( + window.jQuery + ); + } +}(function ($) { + 'use strict'; + + // Append to the default processQueue: + $.blueimp.fileupload.prototype.options.processQueue.push( + { + action: 'validate', + // Always trigger this action, + // even if the previous action was rejected: + always: true, + // Options taken from the global options map: + acceptFileTypes: '@acceptFileTypes', + maxFileSize: '@maxFileSize', + minFileSize: '@minFileSize', + maxNumberOfFiles: '@maxNumberOfFiles', + disabled: '@disableValidation' + } + ); + + // The File Upload Validation plugin extends the fileupload widget + // with file validation functionality: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + /* + // The regular expression for allowed file types, matches + // against either file type or file name: + acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i, + // The maximum allowed file size in bytes: + maxFileSize: 10000000, // 10 MB + // The minimum allowed file size in bytes: + minFileSize: undefined, // No minimal file size + // The limit of files to be uploaded: + maxNumberOfFiles: 10, + */ + + // Function returning the current number of files, + // has to be overriden for maxNumberOfFiles validation: + getNumberOfFiles: $.noop, + + // Error and info messages: + messages: { + maxNumberOfFiles: 'Maximum number of files exceeded', + acceptFileTypes: 'File type not allowed', + maxFileSize: 'File is too large', + minFileSize: 'File is too small' + } + }, + + processActions: { + + validate: function (data, options) { + if (options.disabled) { + return data; + } + var dfd = $.Deferred(), + settings = this.options, + file = data.files[data.index], + numberOfFiles = settings.getNumberOfFiles(); + if (numberOfFiles && $.type(options.maxNumberOfFiles) === 'number' && + numberOfFiles + data.files.length > options.maxNumberOfFiles) { + file.error = settings.i18n('maxNumberOfFiles'); + } else if (options.acceptFileTypes && + !(options.acceptFileTypes.test(file.type) || + options.acceptFileTypes.test(file.name))) { + file.error = settings.i18n('acceptFileTypes'); + } else if (options.maxFileSize && file.size > options.maxFileSize) { + file.error = settings.i18n('maxFileSize'); + } else if ($.type(file.size) === 'number' && + file.size < options.minFileSize) { + file.error = settings.i18n('minFileSize'); + } else { + delete file.error; + } + if (file.error || data.files.error) { + data.files.error = true; + dfd.rejectWith(this, [data]); + } else { + dfd.resolveWith(this, [data]); + } + return dfd.promise(); + } + + } + + }); + +})); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js old mode 100644 new mode 100755 index dbee297..03678f3 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.28.4 + * jQuery File Upload Plugin 5.31.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -115,6 +115,23 @@ // By default, uploads are started automatically when adding files: autoUpload: true, + // Error and info messages: + messages: { + uploadedBytes: 'Uploaded bytes exceed file size' + }, + + // Translation function, gets the message key to be translated + // and an object with context specific data as arguments: + i18n: function (message, context) { + message = this.messages[message] || message.toString(); + if (context) { + $.each(context, function (key, value) { + message = message.replace('{' + key + '}', value); + }); + } + return message; + }, + // Additional form data to be sent along with the file uploads can be set // using this option, which accepts an array of objects with name and // value properties, a function returning such an array, a FormData @@ -139,9 +156,10 @@ // data.submit().done(func).fail(func).always(func); add: function (e, data) { if (data.autoUpload || (data.autoUpload !== false && - ($(this).data('blueimp-fileupload') || - $(this).data('fileupload')).options.autoUpload)) { - data.submit(); + $(this).fileupload('option', 'autoUpload'))) { + data.process().done(function () { + data.submit(); + }); } }, @@ -205,8 +223,9 @@ cache: false }, - // A list of options that require a refresh after assigning a new value: - _refreshOptionsList: [ + // A list of options that require reinitializing event listeners and/or + // special initialization code: + _specialOptions: [ 'fileInput', 'dropZone', 'pasteZone', @@ -215,7 +234,7 @@ ], _BitrateTimer: function () { - this.timestamp = (new Date()).getTime(); + this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); this.loaded = 0; this.bitrate = 0; this.getBitrate = function (now, loaded, interval) { @@ -289,7 +308,7 @@ _onProgress: function (e, data) { if (e.lengthComputable) { - var now = (new Date()).getTime(), + var now = ((Date.now) ? Date.now() : (new Date()).getTime()), loaded; if (data._time && data.progressInterval && (now - data._time < data.progressInterval) && @@ -344,8 +363,14 @@ } }, + _isInstanceOf: function (type, obj) { + // Cross-frame instanceof check + return Object.prototype.toString.call(obj) === '[object ' + type + ']'; + }, + _initXHRData: function (options) { - var formData, + var that = this, + formData, file = options.files[0], // Ignore non-multipart setting if not supported: multipart = options.multipart || !$.support.xhrFileUpload, @@ -380,7 +405,7 @@ }); } } else { - if (options.formData instanceof FormData) { + if (that._isInstanceOf('FormData', options.formData)) { formData = options.formData; } else { formData = new FormData(); @@ -394,12 +419,10 @@ formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { - // Files are also Blob instances, but some browsers - // (Firefox 3.6) support the File API but not Blobs. // This check allows the tests to run with // dummy objects: - if ((window.Blob && file instanceof Blob) || - (window.File && file instanceof File)) { + if (that._isInstanceOf('File', file) || + that._isInstanceOf('Blob', file)) { formData.append( options.paramName[index] || paramName, file, @@ -444,7 +467,7 @@ options.dataType = 'postmessage ' + (options.dataType || ''); } } else { - this._initIframeSettings(options, 'iframe'); + this._initIframeSettings(options); } }, @@ -544,9 +567,20 @@ return this._enhancePromise(promise); }, - // Adds convenience methods to the callback arguments: + // Adds convenience methods to the data callback argument: _addConvenienceMethods: function (e, data) { - var that = this; + var that = this, + getPromise = function (data) { + return $.Deferred().resolveWith(that, [data]).promise(); + }; + data.process = function (resolveFunc, rejectFunc) { + if (resolveFunc || rejectFunc) { + data._processQueue = this._processQueue = + (this._processQueue || getPromise(this)) + .pipe(resolveFunc, rejectFunc); + } + return this._processQueue || getPromise(this); + }; data.submit = function () { if (this.state() !== 'pending') { data.jqXHR = this.jqXHR = @@ -565,6 +599,9 @@ if (this.jqXHR) { return that._getDeferredState(this.jqXHR); } + if (this._processQueue) { + return that._getDeferredState(this._processQueue); + } }; data.progress = function () { return this._progress; @@ -608,7 +645,7 @@ return true; } if (ub >= fs) { - file.error = 'Uploaded bytes exceed file size'; + file.error = options.i18n('uploadedBytes'); return this._getXHRPromise( false, options.context, @@ -644,7 +681,7 @@ // Create a progress event if no final progress event // with loaded equaling total has been triggered // for this chunk: - if (o._progress.loaded === currentLoaded) { + if (currentLoaded + o.chunkSize - o._progress.loaded) { that._onProgress($.Event('progress', { lengthComputable: true, loaded: ub - o.uploadedBytes, @@ -1045,44 +1082,50 @@ }, _onPaste: function (e) { - var cbd = e.originalEvent.clipboardData, - items = (cbd && cbd.items) || [], + var items = e.originalEvent && e.originalEvent.clipboardData && + e.originalEvent.clipboardData.items, data = {files: []}; - $.each(items, function (index, item) { - var file = item.getAsFile && item.getAsFile(); - if (file) { - data.files.push(file); + if (items && items.length) { + $.each(items, function (index, item) { + var file = item.getAsFile && item.getAsFile(); + if (file) { + data.files.push(file); + } + }); + if (this._trigger('paste', e, data) === false || + this._onAdd(e, data) === false) { + return false; } - }); - if (this._trigger('paste', e, data) === false || - this._onAdd(e, data) === false) { - return false; } }, _onDrop: function (e) { var that = this, - dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer, + dataTransfer = e.dataTransfer = e.originalEvent && + e.originalEvent.dataTransfer, data = {}; if (dataTransfer && dataTransfer.files && dataTransfer.files.length) { e.preventDefault(); + this._getDroppedFiles(dataTransfer).always(function (files) { + data.files = files; + if (that._trigger('drop', e, data) !== false) { + that._onAdd(e, data); + } + }); } - this._getDroppedFiles(dataTransfer).always(function (files) { - data.files = files; - if (that._trigger('drop', e, data) !== false) { - that._onAdd(e, data); - } - }); }, _onDragOver: function (e) { - var dataTransfer = e.dataTransfer = e.originalEvent.dataTransfer; - if (this._trigger('dragover', e) === false) { - return false; - } - if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1) { - dataTransfer.dropEffect = 'copy'; - e.preventDefault(); + var dataTransfer = e.dataTransfer = e.originalEvent && + e.originalEvent.dataTransfer; + if (dataTransfer) { + if (this._trigger('dragover', e) === false) { + return false; + } + if ($.inArray('Files', dataTransfer.types) !== -1) { + dataTransfer.dropEffect = 'copy'; + e.preventDefault(); + } } }, @@ -1108,12 +1151,12 @@ }, _setOption: function (key, value) { - var refresh = $.inArray(key, this._refreshOptionsList) !== -1; - if (refresh) { + var reinit = $.inArray(key, this._specialOptions) !== -1; + if (reinit) { this._destroyEventHandlers(); } this._super(key, value); - if (refresh) { + if (reinit) { this._initSpecialOptions(); this._initEventHandlers(); } @@ -1135,10 +1178,35 @@ } }, - _create: function () { - var options = this.options; + _getRegExp: function (str) { + var parts = str.split('/'), + modifiers = parts.pop(); + parts.shift(); + return new RegExp(parts.join('/'), modifiers); + }, + + _isRegExpOption: function (key, value) { + return key !== 'url' && $.type(value) === 'string' && + /^\/.*\/[igm]{0,3}$/.test(value); + }, + + _initDataAttributes: function () { + var that = this, + options = this.options; // Initialize options set via HTML5 data-attributes: - $.extend(options, $(this.element[0].cloneNode(false)).data()); + $.each( + $(this.element[0].cloneNode(false)).data(), + function (key, value) { + if (that._isRegExpOption(key, value)) { + value = that._getRegExp(value); + } + options[key] = value; + } + ); + }, + + _create: function () { + this._initDataAttributes(); this._initSpecialOptions(); this._slots = []; this._sequence = this._getXHRPromise(true); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js old mode 100644 new mode 100755 index ed25895..e04e7a0 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.6.1 + * jQuery Iframe Transport Plugin 1.6.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -61,9 +61,10 @@ // IE versions below IE8 cannot set the name property of // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: + counter += 1; iframe = $( '' + counter + '">' ).bind('load', function () { var fileInputClones, paramNames = $.isArray(options.paramName) ? @@ -96,7 +97,12 @@ // (happens on form submits to iframe targets): $('') .appendTo(form); - form.remove(); + window.setTimeout(function () { + // Removing the form in a setTimeout call + // allows Chrome's developer tools to display + // the response result + form.remove(); + }, 0); }); form .prop('target', iframe.prop('name')) diff --git a/vendor/assets/stylesheets/jquery.fileupload-ui.scss b/vendor/assets/stylesheets/jquery.fileupload-ui.scss old mode 100644 new mode 100755 index 58a7315..72bfcb6 --- a/vendor/assets/stylesheets/jquery.fileupload-ui.scss +++ b/vendor/assets/stylesheets/jquery.fileupload-ui.scss @@ -1,6 +1,6 @@ -@charset 'UTF-8'; +@charset "UTF-8"; /* - * jQuery File Upload UI Plugin CSS 6.3 + * jQuery File Upload UI Plugin CSS 8.0 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -13,19 +13,16 @@ .fileinput-button { position: relative; overflow: hidden; - float: left; - margin-right: 4px; } .fileinput-button input { position: absolute; top: 0; right: 0; margin: 0; - border: solid transparent; - border-width: 0 0 100px 200px; opacity: 0; filter: alpha(opacity=0); - -moz-transform: translate(-300px, 0) scale(4); + transform: translate(-300px, 0) scale(4); + font-size: 23px; direction: ltr; cursor: pointer; } @@ -33,52 +30,30 @@ .fileupload-buttonbar .toggle { margin-bottom: 5px; } -.files .progress { - width: 200px; -} .progress-animated .bar { - background: image-url('progressbar.gif') !important; + background: url(../img/progressbar.gif) !important; filter: none; } .fileupload-loading { - position: absolute; - left: 50%; - width: 128px; - height: 128px; - background: image-url('loading.gif') center no-repeat; + float: right; + width: 32px; + height: 32px; + background: url(../img/loading.gif) center no-repeat; + background-size: contain; display: none; } .fileupload-processing .fileupload-loading { display: block; } -/* Fix for IE 6: */ -* html .fileinput-button { - line-height: 22px; - margin: 1px -3px 0 0; -} - -/* Fix for IE 7: */ -* + html .fileinput-button { - margin: 1px 0 0 0; -} - -@media (max-width: 480px) { +@media (max-width: 767px) { + .fileupload-buttonbar .toggle, + .files .toggle, .files .btn span { display: none; } - .files .preview * { - width: 40px; - } - .files .name * { + .files .name { width: 80px; - display: inline-block; word-wrap: break-word; } - .files .progress { - width: 20px; - } - .files .delete { - width: 60px; - } } From 851cda559c5a7c0e52c4ca8244ba1038e057623a Mon Sep 17 00:00:00 2001 From: Chris Whitman Date: Wed, 23 May 2012 06:41:04 -0700 Subject: [PATCH 32/33] Remove tmpl.js (JS template engine) from manifest --- vendor/assets/javascripts/jquery-fileupload/index.js | 1 - 1 file changed, 1 deletion(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/index.js b/vendor/assets/javascripts/jquery-fileupload/index.js index 0cda385..3087040 100644 --- a/vendor/assets/javascripts/jquery-fileupload/index.js +++ b/vendor/assets/javascripts/jquery-fileupload/index.js @@ -1,7 +1,6 @@ //=require jquery-fileupload/vendor/jquery.ui.widget //=require jquery-fileupload/vendor/load-image //=require jquery-fileupload/vendor/canvas-to-blob -//=require jquery-fileupload/vendor/tmpl //=require jquery-fileupload/jquery.iframe-transport //=require jquery-fileupload/jquery.fileupload //=require jquery-fileupload/jquery.fileupload-fp From 5d7e0cb470862ae8e6cb854d010baf4ff6c8c182 Mon Sep 17 00:00:00 2001 From: Waseem Ahmad Date: Wed, 17 Apr 2013 03:25:07 +0530 Subject: [PATCH 33/33] Make sure no error is raised if fileupload element is not present in DOM --- .../jquery-fileupload/jquery.fileupload-ui.js | 518 ++++++++++-------- 1 file changed, 289 insertions(+), 229 deletions(-) diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js index 5d22346..6d19391 100755 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload User Interface Plugin 8.2.1 + * jQuery File Upload User Interface Plugin 6.8.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -10,7 +10,7 @@ */ /*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, URL, webkitURL, FileReader */ +/*global define, window, document, URL, webkitURL, FileReader */ (function (factory) { 'use strict'; @@ -19,34 +19,53 @@ define([ 'jquery', 'tmpl', - './jquery.fileupload-resize', - './jquery.fileupload-validate' + 'load-image', + './jquery.fileupload-fp' ], factory); } else { // Browser globals: factory( window.jQuery, - window.tmpl + window.tmpl, + window.loadImage ); } }(function ($, tmpl, loadImage) { 'use strict'; - $.blueimp.fileupload.prototype._specialOptions.push( - 'filesContainer', - 'uploadTemplateId', - 'downloadTemplateId' - ); - - // The UI version extends the file upload widget - // and adds complete user interface interaction: - $.widget('blueimp.fileupload', $.blueimp.fileupload, { + // The UI version extends the FP (file processing) version or the basic + // file upload widget and adds complete user interface interaction: + var parentWidget = ($.blueimpFP || $.blueimp).fileupload; + $.widget('blueimpUI.fileupload', parentWidget, { options: { // By default, files added to the widget are uploaded as soon // as the user clicks on the start buttons. To enable automatic // uploads, set the following option to true: autoUpload: false, + // The following option limits the number of files that are + // allowed to be uploaded using this widget: + maxNumberOfFiles: undefined, + // The maximum allowed file size: + maxFileSize: undefined, + // The minimum allowed file size: + minFileSize: undefined, + // The regular expression for allowed file types, matches + // against either file type or file name: + acceptFileTypes: /.+$/i, + // The regular expression to define for which files a preview + // image is shown, matched against the file type: + previewSourceFileTypes: /^image\/(gif|jpeg|png)$/, + // The maximum file size of images that are to be displayed as preview: + previewSourceMaxFileSize: 5000000, // 5MB + // The maximum width of the preview images: + previewMaxWidth: 80, + // The maximum height of the preview images: + previewMaxHeight: 80, + // By default, preview images are displayed as canvas elements + // if supported by the browser. Set the following option to false + // to always display preview images as img elements: + previewAsCanvas: true, // The ID of the upload template: uploadTemplateId: 'template-upload', // The ID of the download template: @@ -61,43 +80,28 @@ // option of the $.ajax upload requests: dataType: 'json', - // Function returning the current number of files, - // used by the maxNumberOfFiles validation: - getNumberOfFiles: function () { - return this.filesContainer.children().length; - }, - - // Callback to retrieve the list of files from the server response: - getFilesFromResponse: function (data) { - if (data.result && $.isArray(data.result.files)) { - return data.result.files; - } - return []; - }, - // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop or add API call). // See the basic file upload widget for more information: add: function (e, data) { - var $this = $(this), - that = $this.data('blueimp-fileupload') || - $this.data('fileupload'), + var that = $(this).data('fileupload'), options = that.options, files = data.files; - data.process(function () { - return $this.fileupload('process', data); - }).always(function () { + $(this).fileupload('process', data).done(function () { + that._adjustMaxNumberOfFiles(-files.length); + data.isAdjusted = true; + data.files.valid = data.isValidated = that._validate(files); data.context = that._renderUpload(files).data('data', data); - that._renderPreviews(data); options.filesContainer[ options.prependFiles ? 'prepend' : 'append' ](data.context); + that._renderPreviews(files, data.context); that._forceReflow(data.context); that._transition(data.context).done( function () { if ((that._trigger('added', e, data) !== false) && (options.autoUpload || data.autoUpload) && - data.autoUpload !== false && !data.files.error) { + data.autoUpload !== false && data.isValidated) { data.submit(); } } @@ -106,8 +110,15 @@ }, // Callback for the start of each file upload request: send: function (e, data) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); + var that = $(this).data('fileupload'); + if (!data.isValidated) { + if (!data.isAdjusted) { + that._adjustMaxNumberOfFiles(-data.files.length); + } + if (!that._validate(data.files)) { + return false; + } + } if (data.context && data.dataType && data.dataType.substr(0, 6) === 'iframe') { // Iframe Transport does not support progress events. @@ -117,73 +128,65 @@ .find('.progress').addClass( !$.support.transition && 'progress-animated' ) - .attr('aria-valuenow', 100) .find('.bar').css( 'width', - '100%' + parseInt(100, 10) + '%' ); } return that._trigger('sent', e, data); }, // Callback for successful uploads: done: function (e, data) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - getFilesFromResponse = data.getFilesFromResponse || - that.options.getFilesFromResponse, - files = getFilesFromResponse(data), - template, - deferred; + var that = $(this).data('fileupload'), + template; if (data.context) { data.context.each(function (index) { - var file = files[index] || - {error: 'Empty file upload result'}, - deferred = that._addFinishedDeferreds(); - that._transition($(this)).done( - function () { - var node = $(this); - template = that._renderDownload([file]) - .replaceAll(node); - that._forceReflow(template); - that._transition(template).done( - function () { - data.context = $(this); - that._trigger('completed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); - } - ); - } - ); + var file = ($.isArray(data.result) && + data.result[index]) || {error: 'emptyResult'}; + if (file.error) { + that._adjustMaxNumberOfFiles(1); + } + if (that) { + that._transition($(this)).done( + function () { + var node = $(this); + template = that._renderDownload([file]) + .css('height', node.height()) + .replaceAll(node); + that._forceReflow(template); + that._transition(template).done( + function () { + data.context = $(this); + that._trigger('completed', e, data); + } + ); + } + ); + } }); } else { - template = that._renderDownload(files) + template = that._renderDownload(data.result) .appendTo(that.options.filesContainer); that._forceReflow(template); - deferred = that._addFinishedDeferreds(); that._transition(template).done( function () { data.context = $(this); that._trigger('completed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); } ); } }, // Callback for failed (abort or error) uploads: fail: function (e, data) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - template, - deferred; + var that = $(this).data('fileupload'), + template; + that._adjustMaxNumberOfFiles(data.files.length); if (data.context) { data.context.each(function (index) { if (data.errorThrown !== 'abort') { var file = data.files[index]; file.error = file.error || data.errorThrown || true; - deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { var node = $(this); @@ -194,82 +197,62 @@ function () { data.context = $(this); that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); } ); } ); } else { - deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { $(this).remove(); that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); } ); } }); } else if (data.errorThrown !== 'abort') { + that._adjustMaxNumberOfFiles(-data.files.length); data.context = that._renderUpload(data.files) .appendTo(that.options.filesContainer) .data('data', data); that._forceReflow(data.context); - deferred = that._addFinishedDeferreds(); that._transition(data.context).done( function () { data.context = $(this); that._trigger('failed', e, data); - that._trigger('finished', e, data); - deferred.resolve(); } ); } else { that._trigger('failed', e, data); - that._trigger('finished', e, data); - that._addFinishedDeferreds().resolve(); } }, // Callback for upload progress events: progress: function (e, data) { if (data.context) { - var progress = Math.floor(data.loaded / data.total * 100); - data.context.find('.progress') - .attr('aria-valuenow', progress) - .find('.bar').css( - 'width', - progress + '%' - ); + data.context.find('.bar').css( + 'width', + parseInt(data.loaded / data.total * 100, 10) + '%' + ); } }, // Callback for global upload progress events: progressall: function (e, data) { - var $this = $(this), - progress = Math.floor(data.loaded / data.total * 100), - globalProgressNode = $this.find('.fileupload-progress'), - extendedProgressNode = globalProgressNode - .find('.progress-extended'); - if (extendedProgressNode.length) { - extendedProgressNode.html( - ($this.data('blueimp-fileupload') || $this.data('fileupload')) - ._renderExtendedProgress(data) - ); - } - globalProgressNode - .find('.progress') - .attr('aria-valuenow', progress) + var $this = $(this); + $this.find('.fileupload-progress') .find('.bar').css( 'width', - progress + '%' - ); + parseInt(data.loaded / data.total * 100, 10) + '%' + ).end() + .find('.progress-extended').each(function () { + $(this).html( + $this.data('fileupload') + ._renderExtendedProgress(data) + ); + }); }, // Callback for uploads start, equivalent to the global ajaxStart event: start: function (e) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); - that._resetFinishedDeferreds(); + var that = $(this).data('fileupload'); that._transition($(this).find('.fileupload-progress')).done( function () { that._trigger('started', e); @@ -278,62 +261,31 @@ }, // Callback for uploads stop, equivalent to the global ajaxStop event: stop: function (e) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'), - deferred = that._addFinishedDeferreds(); - $.when.apply($, that._getFinishedDeferreds()) - .done(function () { - that._trigger('stopped', e); - }); + var that = $(this).data('fileupload'); that._transition($(this).find('.fileupload-progress')).done( function () { - $(this).find('.progress') - .attr('aria-valuenow', '0') - .find('.bar').css('width', '0%'); + $(this).find('.bar').css('width', '0%'); $(this).find('.progress-extended').html(' '); - deferred.resolve(); + that._trigger('stopped', e); } ); }, - processstart: function () { - $(this).addClass('fileupload-processing'); - }, - processstop: function () { - $(this).removeClass('fileupload-processing'); - }, // Callback for file deletion: destroy: function (e, data) { - var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); + var that = $(this).data('fileupload'); if (data.url) { - $.ajax(data).done(function () { - that._transition(data.context).done( - function () { - $(this).remove(); - that._trigger('destroyed', e, data); - } - ); - }); + $.ajax(data); + that._adjustMaxNumberOfFiles(1); } + that._transition(data.context).done( + function () { + $(this).remove(); + that._trigger('destroyed', e, data); + } + ); } }, - _resetFinishedDeferreds: function () { - this._finishedUploads = []; - }, - - _addFinishedDeferreds: function (deferred) { - if (!deferred) { - deferred = $.Deferred(); - } - this._finishedUploads.push(deferred); - return deferred; - }, - - _getFinishedDeferreds: function () { - return this._finishedUploads; - }, - // Link handler, that allows to download files // by drag & drop of the links to the desktop: _enableDragToDesktop: function () { @@ -347,10 +299,21 @@ 'DownloadURL', [type, name, url].join(':') ); - } catch (ignore) {} + } catch (err) {} }); }, + _adjustMaxNumberOfFiles: function (operand) { + if (typeof this.options.maxNumberOfFiles === 'number') { + this.options.maxNumberOfFiles += operand; + if (this.options.maxNumberOfFiles < 1) { + this._disableFileInputButton(); + } else { + this._enableFileInputButton(); + } + } + }, + _formatFileSize: function (bytes) { if (typeof bytes !== 'number') { return ''; @@ -377,12 +340,12 @@ if (bits >= 1000) { return (bits / 1000).toFixed(2) + ' kbit/s'; } - return bits.toFixed(2) + ' bit/s'; + return bits + ' bit/s'; }, _formatTime: function (seconds) { var date = new Date(seconds * 1000), - days = Math.floor(seconds / 86400); + days = parseInt(seconds / 86400, 10); days = days ? days + 'd ' : ''; return days + ('0' + date.getUTCHours()).slice(-2) + ':' + @@ -406,6 +369,46 @@ this._formatFileSize(data.total); }, + _hasError: function (file) { + if (file.error) { + return file.error; + } + // The number of added files is subtracted from + // maxNumberOfFiles before validation, so we check if + // maxNumberOfFiles is below 0 (instead of below 1): + if (this.options.maxNumberOfFiles < 0) { + return 'maxNumberOfFiles'; + } + // Files are accepted if either the file type or the file name + // matches against the acceptFileTypes regular expression, as + // only browsers with support for the File API report the type: + if (!(this.options.acceptFileTypes.test(file.type) || + this.options.acceptFileTypes.test(file.name))) { + return 'acceptFileTypes'; + } + if (this.options.maxFileSize && + file.size > this.options.maxFileSize) { + return 'maxFileSize'; + } + if (typeof file.size === 'number' && + file.size < this.options.minFileSize) { + return 'minFileSize'; + } + return null; + }, + + _validate: function (files) { + var that = this, + valid = !!files.length; + $.each(files, function (index, file) { + file.error = that._hasError(file); + if (file.error) { + valid = false; + } + }); + return valid; + }, + _renderTemplate: function (func, files) { if (!func) { return $(); @@ -421,10 +424,53 @@ return $(this.options.templatesContainer).html(result).children(); }, - _renderPreviews: function (data) { - data.context.find('.preview').each(function (index, elm) { - $(elm).append(data.files[index].preview); + _renderPreview: function (file, node) { + var that = this, + options = this.options, + dfd = $.Deferred(); + return ((loadImage && loadImage( + file, + function (img) { + node.append(img); + that._forceReflow(node); + that._transition(node).done(function () { + dfd.resolveWith(node); + }); + if (!$.contains(document.body, node[0])) { + // If the element is not part of the DOM, + // transition events are not triggered, + // so we have to resolve manually: + dfd.resolveWith(node); + } + }, + { + maxWidth: options.previewMaxWidth, + maxHeight: options.previewMaxHeight, + canvas: options.previewAsCanvas + } + )) || dfd.resolveWith(node)) && dfd; + }, + + _renderPreviews: function (files, nodes) { + var that = this, + options = this.options; + nodes.find('.preview span').each(function (index, element) { + var file = files[index]; + if (options.previewSourceFileTypes.test(file.type) && + ($.type(options.previewSourceMaxFileSize) !== 'number' || + file.size < options.previewSourceMaxFileSize)) { + that._processingQueue = that._processingQueue.pipe(function () { + var dfd = $.Deferred(); + that._renderPreview(file, $(element)).done( + function () { + dfd.resolveWith(that); + } + ); + return dfd.promise(); + }); + } }); + return this._processingQueue; }, _renderUpload: function (files) { @@ -443,7 +489,7 @@ _startHandler: function (e) { e.preventDefault(); - var button = $(e.currentTarget), + var button = $(this), template = button.closest('.template-upload'), data = template.data('data'); if (data && data.submit && !data.jqXHR && data.submit()) { @@ -453,11 +499,11 @@ _cancelHandler: function (e) { e.preventDefault(); - var template = $(e.currentTarget).closest('.template-upload'), + var template = $(this).closest('.template-upload'), data = template.data('data') || {}; if (!data.jqXHR) { data.errorThrown = 'abort'; - this._trigger('fail', e, data); + e.data.fileupload._trigger('fail', e, data); } else { data.jqXHR.abort(); } @@ -465,21 +511,23 @@ _deleteHandler: function (e) { e.preventDefault(); - var button = $(e.currentTarget); - this._trigger('destroy', e, $.extend({ + var button = $(this); + e.data.fileupload._trigger('destroy', e, { context: button.closest('.template-download'), - type: 'DELETE' - }, button.data())); + url: button.attr('data-url'), + type: button.attr('data-type') || 'DELETE', + dataType: e.data.fileupload.options.dataType + }); }, _forceReflow: function (node) { - return $.support.transition && node.length && - node[0].offsetWidth; + this._reflow = $.support.transition && + node.length && node[0].offsetWidth; }, _transition: function (node) { var dfd = $.Deferred(); - if ($.support.transition && node.hasClass('fade') && node.is(':visible')) { + if ($.support.transition && node.hasClass('fade')) { node.bind( $.support.transition.end, function (e) { @@ -500,65 +548,75 @@ _initButtonBarEventHandlers: function () { var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'), - filesList = this.options.filesContainer; - this._on(fileUploadButtonBar.find('.start'), { - click: function (e) { + filesList = this.options.filesContainer, + ns = this.options.namespace; + fileUploadButtonBar.find('.start') + .bind('click.' + ns, function (e) { e.preventDefault(); - filesList.find('.start').click(); - } - }); - this._on(fileUploadButtonBar.find('.cancel'), { - click: function (e) { + filesList.find('.start button').click(); + }); + fileUploadButtonBar.find('.cancel') + .bind('click.' + ns, function (e) { e.preventDefault(); - filesList.find('.cancel').click(); - } - }); - this._on(fileUploadButtonBar.find('.delete'), { - click: function (e) { + filesList.find('.cancel button').click(); + }); + fileUploadButtonBar.find('.delete') + .bind('click.' + ns, function (e) { e.preventDefault(); - filesList.find('.toggle:checked') - .closest('.template-download') - .find('.delete').click(); + filesList.find('.delete input:checked') + .siblings('button').click(); fileUploadButtonBar.find('.toggle') .prop('checked', false); - } - }); - this._on(fileUploadButtonBar.find('.toggle'), { - change: function (e) { - filesList.find('.toggle').prop( + }); + fileUploadButtonBar.find('.toggle') + .bind('change.' + ns, function (e) { + filesList.find('.delete input').prop( 'checked', - $(e.currentTarget).is(':checked') + $(this).is(':checked') ); - } - }); + }); }, _destroyButtonBarEventHandlers: function () { - this._off( - this.element.find('.fileupload-buttonbar') - .find('.start, .cancel, .delete'), - 'click' - ); - this._off( - this.element.find('.fileupload-buttonbar .toggle'), - 'change.' - ); + this.element.find('.fileupload-buttonbar button') + .unbind('click.' + this.options.namespace); + this.element.find('.fileupload-buttonbar .toggle') + .unbind('change.' + this.options.namespace); }, _initEventHandlers: function () { - this._super(); - this._on(this.options.filesContainer, { - 'click .start': this._startHandler, - 'click .cancel': this._cancelHandler, - 'click .delete': this._deleteHandler - }); + parentWidget.prototype._initEventHandlers.call(this); + var eventData = {fileupload: this}; + this.options.filesContainer + .delegate( + '.start button', + 'click.' + this.options.namespace, + eventData, + this._startHandler + ) + .delegate( + '.cancel button', + 'click.' + this.options.namespace, + eventData, + this._cancelHandler + ) + .delegate( + '.delete button', + 'click.' + this.options.namespace, + eventData, + this._deleteHandler + ); this._initButtonBarEventHandlers(); }, _destroyEventHandlers: function () { + var options = this.options; this._destroyButtonBarEventHandlers(); - this._off(this.options.filesContainer, 'click'); - this._super(); + options.filesContainer + .undelegate('.start button', 'click.' + options.namespace) + .undelegate('.cancel button', 'click.' + options.namespace) + .undelegate('.delete button', 'click.' + options.namespace); + parentWidget.prototype._destroyEventHandlers.call(this); }, _enableFileInputButton: function () { @@ -575,7 +633,7 @@ _initTemplates: function () { var options = this.options; - options.templatesContainer = this.document[0].createElement( + options.templatesContainer = document.createElement( options.filesContainer.prop('nodeName') ); if (tmpl) { @@ -598,34 +656,36 @@ }, _initSpecialOptions: function () { - this._super(); + parentWidget.prototype._initSpecialOptions.call(this); this._initFilesContainer(); this._initTemplates(); }, _create: function () { - this._super(); - this._resetFinishedDeferreds(); + parentWidget.prototype._create.call(this); + this._refreshOptionsList.push( + 'filesContainer', + 'uploadTemplateId', + 'downloadTemplateId' + ); + if (!$.blueimpFP) { + this._processingQueue = $.Deferred().resolveWith(this).promise(); + this.process = function () { + return this._processingQueue; + }; + } }, enable: function () { - var wasDisabled = false; - if (this.options.disabled) { - wasDisabled = true; - } - this._super(); - if (wasDisabled) { - this.element.find('input, button').prop('disabled', false); - this._enableFileInputButton(); - } + parentWidget.prototype.enable.call(this); + this.element.find('input, button').prop('disabled', false); + this._enableFileInputButton(); }, disable: function () { - if (!this.options.disabled) { - this.element.find('input, button').prop('disabled', true); - this._disableFileInputButton(); - } - this._super(); + this.element.find('input, button').prop('disabled', true); + this._disableFileInputButton(); + parentWidget.prototype.disable.call(this); } });