diff --git a/README.md b/README.md index 4207f35..25bb8a2 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.11 -* jQuery File Upload Plugin 5.19.3 -* jQuery UI Widget 1.9.1+amd +* jQuery File Upload User Interface Plugin 9.6.0 +* jQuery File Upload Plugin 5.42.0 +* jQuery UI Widget 1.11.1 ## Installing Gem @@ -23,14 +23,22 @@ 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/tmpl //= 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-process + //= require jquery-fileupload/jquery.fileupload-image + //= require jquery-fileupload/jquery.fileupload-audio + //= require jquery-fileupload/jquery.fileupload-video + //= require jquery-fileupload/jquery.fileupload-validate //= require jquery-fileupload/jquery.fileupload-ui //= require jquery-fileupload/locale + //= require jquery-fileupload/jquery.fileupload-angular + //= require jquery-fileupload/jquery.fileupload-jquery-ui + //= require jquery-fileupload/cors/jquery.postmessage-transport + //= require jquery-fileupload/cors/jquery.xdr-transport 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) @@ -42,12 +50,26 @@ The basic setup only includes the following files: //= require jquery-fileupload/jquery.iframe-transport //= require jquery-fileupload/jquery.fileupload +You can also require the following to get the js from the Basic-Plus, AngularJS and jQuery UI Examples: + + //= require jquery-fileupload/basic-plus + + //= require jquery-fileupload/angularjs + + //= require jquery-fileupload/jquery-ui + ## Using the stylesheet Require the stylesheet file to app/assets/stylesheets/application.css + *= require jquery.fileupload *= require jquery.fileupload-ui +There are also noscript styles for Browsers with Javascript disabled, to use them create a noscript.css and add it to your precompile-list and layout inside a noscript tag: + + *= require jquery.fileupload-noscript + *= require jquery.fileupload-ui-noscript + ## 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. diff --git a/vendor/assets/images/loading.gif b/app/assets/images/loading.gif old mode 100755 new mode 100644 similarity index 100% rename from vendor/assets/images/loading.gif rename to app/assets/images/loading.gif diff --git a/vendor/assets/images/progressbar.gif b/app/assets/images/progressbar.gif old mode 100755 new mode 100644 similarity index 100% rename from vendor/assets/images/progressbar.gif rename to app/assets/images/progressbar.gif diff --git a/app/assets/javascripts/jquery-fileupload/angularjs.js b/app/assets/javascripts/jquery-fileupload/angularjs.js new file mode 100644 index 0000000..c87d11c --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/angularjs.js @@ -0,0 +1,12 @@ +//= require jquery-fileupload/vendor/jquery.ui.widget +//= require jquery-fileupload/vendor/load-image.all.min +//= require jquery-fileupload/vendor/canvas-to-blob +//= require jquery-fileupload/jquery.iframe-transport +//= require jquery-fileupload/jquery.fileupload +//= require jquery-fileupload/jquery.fileupload-process +//= require jquery-fileupload/jquery.fileupload-image +//= require jquery-fileupload/jquery.fileupload-audio +//= require jquery-fileupload/jquery.fileupload-video +//= require jquery-fileupload/jquery.fileupload-validate +//= require jquery-fileupload/jquery.fileupload-angular +//= require jquery-fileupload/locale diff --git a/app/assets/javascripts/jquery-fileupload/basic-plus.js b/app/assets/javascripts/jquery-fileupload/basic-plus.js new file mode 100644 index 0000000..dacdb2e --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/basic-plus.js @@ -0,0 +1,11 @@ +//= require jquery-fileupload/vendor/jquery.ui.widget +//= require jquery-fileupload/vendor/load-image.all.min +//= require jquery-fileupload/vendor/canvas-to-blob +//= require jquery-fileupload/jquery.iframe-transport +//= require jquery-fileupload/jquery.fileupload +//= require jquery-fileupload/jquery.fileupload-process +//= require jquery-fileupload/jquery.fileupload-image +//= require jquery-fileupload/jquery.fileupload-audio +//= require jquery-fileupload/jquery.fileupload-video +//= require jquery-fileupload/jquery.fileupload-validate +//= require jquery-fileupload/locale diff --git a/app/assets/javascripts/jquery-fileupload/basic.js b/app/assets/javascripts/jquery-fileupload/basic.js new file mode 100644 index 0000000..6d07907 --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/basic.js @@ -0,0 +1,3 @@ +//= require jquery-fileupload/vendor/jquery.ui.widget +//= require jquery-fileupload/jquery.iframe-transport +//= require jquery-fileupload/jquery.fileupload diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js b/app/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js old mode 100755 new mode 100644 similarity index 95% rename from vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js rename to app/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js index 931b635..2b4851e --- a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js +++ b/app/assets/javascripts/jquery-fileupload/cors/jquery.postmessage-transport.js @@ -1,5 +1,5 @@ /* - * jQuery postMessage Transport Plugin 1.1 + * jQuery postMessage Transport Plugin 1.1.1 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -9,8 +9,7 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint unparam: true, nomen: true */ -/*global define, window, document */ +/* global define, window, document */ (function (factory) { 'use strict'; @@ -64,8 +63,9 @@ xhrUpload = options.xhr().upload; return { send: function (_, completeCallback) { + counter += 1; var message = { - id: 'postmessage-transport-' + (counter += 1) + id: 'postmessage-transport-' + counter }, eventName = 'message.' + message.id; iframe = $( diff --git a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js b/app/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js old mode 100755 new mode 100644 similarity index 97% rename from vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js rename to app/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js index d769f45..0044cc2 --- a/vendor/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js +++ b/app/assets/javascripts/jquery-fileupload/cors/jquery.xdr-transport.js @@ -12,8 +12,7 @@ * https://github.com/jaubourg/ajaxHooks/ */ -/*jslint unparam: true */ -/*global define, window, XDomainRequest */ +/* global define, window, XDomainRequest */ (function (factory) { 'use strict'; diff --git a/app/assets/javascripts/jquery-fileupload/index.js b/app/assets/javascripts/jquery-fileupload/index.js new file mode 100644 index 0000000..58d29a3 --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/index.js @@ -0,0 +1,13 @@ +//= require jquery-fileupload/vendor/jquery.ui.widget +//= require jquery-fileupload/vendor/tmpl +//= require jquery-fileupload/vendor/load-image.all.min +//= require jquery-fileupload/vendor/canvas-to-blob +//= require jquery-fileupload/jquery.iframe-transport +//= require jquery-fileupload/jquery.fileupload +//= require jquery-fileupload/jquery.fileupload-process +//= require jquery-fileupload/jquery.fileupload-image +//= require jquery-fileupload/jquery.fileupload-audio +//= require jquery-fileupload/jquery.fileupload-video +//= require jquery-fileupload/jquery.fileupload-validate +//= require jquery-fileupload/jquery.fileupload-ui +//= require jquery-fileupload/locale diff --git a/app/assets/javascripts/jquery-fileupload/jquery-ui.js b/app/assets/javascripts/jquery-fileupload/jquery-ui.js new file mode 100644 index 0000000..256325d --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery-ui.js @@ -0,0 +1,13 @@ +//= require jquery-fileupload/vendor/tmpl +//= require jquery-fileupload/vendor/load-image.all.min +//= require jquery-fileupload/vendor/canvas-to-blob +//= require jquery-fileupload/jquery.iframe-transport +//= require jquery-fileupload/jquery.fileupload +//= require jquery-fileupload/jquery.fileupload-process +//= require jquery-fileupload/jquery.fileupload-image +//= require jquery-fileupload/jquery.fileupload-audio +//= require jquery-fileupload/jquery.fileupload-video +//= require jquery-fileupload/jquery.fileupload-validate +//= require jquery-fileupload/jquery.fileupload-ui +//= require jquery-fileupload/jquery.fileupload-jquery-ui +//= require jquery-fileupload/locale diff --git a/app/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js new file mode 100644 index 0000000..e4ef392 --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-angular.js @@ -0,0 +1,429 @@ +/* + * jQuery File Upload AngularJS Plugin 2.2.0 + * 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 + */ + +/* jshint nomen:false */ +/* global define, angular */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + 'angular', + './jquery.fileupload-image', + './jquery.fileupload-audio', + './jquery.fileupload-video', + './jquery.fileupload-validate' + ], factory); + } else { + factory(); + } +}(function () { + 'use strict'; + + angular.module('blueimp.fileupload', []) + + // The fileUpload service provides configuration options + // for the fileUpload directive and default handlers for + // File Upload events: + .provider('fileUpload', function () { + var scopeEvalAsync = function (expression) { + var scope = angular.element(this) + .fileupload('option', 'scope'); + // Schedule a new $digest cycle if not already inside of one + // and evaluate the given expression: + scope.$evalAsync(expression); + }, + addFileMethods = function (scope, data) { + var files = data.files, + file = files[0]; + angular.forEach(files, function (file, index) { + file._index = index; + file.$state = function () { + return data.state(); + }; + file.$processing = function () { + return data.processing(); + }; + file.$progress = function () { + return data.progress(); + }; + file.$response = function () { + return data.response(); + }; + }); + file.$submit = function () { + if (!file.error) { + return data.submit(); + } + }; + file.$cancel = function () { + return data.abort(); + }; + }, + $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) { + if (e.isDefaultPrevented()) { + return false; + } + var scope = data.scope, + filesCopy = []; + angular.forEach(data.files, function (file) { + filesCopy.push(file); + }); + scope.$apply(function () { + addFileMethods(scope, data); + var method = scope.option('prependFiles') ? + 'unshift' : 'push'; + Array.prototype[method].apply(scope.queue, data.files); + }); + data.process(function () { + return scope.process(data); + }).always(function () { + scope.$apply(function () { + addFileMethods(scope, data); + scope.replace(filesCopy, data.files); + }); + }).then(function () { + if ((scope.option('autoUpload') || + data.autoUpload) && + data.autoUpload !== false) { + data.submit(); + } + }); + }, + progress: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } + data.scope.$apply(); + }, + done: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } + var that = this; + data.scope.$apply(function () { + data.handleResponse.call(that, e, data); + }); + }, + fail: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } + var that = this, + scope = data.scope; + if (data.errorThrown === 'abort') { + scope.clear(data.files); + return; + } + scope.$apply(function () { + data.handleResponse.call(that, e, data); + }); + }, + stop: scopeEvalAsync, + processstart: scopeEvalAsync, + processstop: scopeEvalAsync, + getNumberOfFiles: function () { + var scope = this.scope; + return scope.queue.length - scope.processing(); + }, + dataType: 'json', + autoUpload: false + }; + this.$get = [ + function () { + return { + defaults: $config + }; + } + ]; + }) + + // Format byte numbers to readable presentations: + .provider('formatFileSizeFilter', function () { + var $config = { + // 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.defaults = $config; + this.$get = function () { + return function (bytes) { + if (!angular.isNumber(bytes)) { + return ''; + } + var unit = true, + i = 0, + prefix, + suffix; + while (unit) { + unit = $config.units[i]; + prefix = unit.prefix || ''; + suffix = unit.suffix || ''; + if (i === $config.units.length - 1 || bytes >= unit.size) { + return prefix + (bytes / unit.size).toFixed(2) + suffix; + } + i += 1; + } + }; + }; + }) + + // The FileUploadController initializes the fileupload widget and + // provides scope methods to control the File Upload functionality: + .controller('FileUploadController', [ + '$scope', '$element', '$attrs', '$window', 'fileUpload', + function ($scope, $element, $attrs, $window, fileUpload) { + var uploadMethods = { + progress: function () { + return $element.fileupload('progress'); + }, + active: function () { + return $element.fileupload('active'); + }, + option: function (option, data) { + if (arguments.length === 1) { + return $element.fileupload('option', option); + } + $element.fileupload('option', option, data); + }, + add: function (data) { + return $element.fileupload('add', data); + }, + send: function (data) { + return $element.fileupload('send', data); + }, + process: function (data) { + return $element.fileupload('process', data); + }, + processing: function (data) { + return $element.fileupload('processing', data); + } + }; + $scope.disabled = !$window.jQuery.support.fileInput; + $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) { + i -= 1; + if (queue[i] === 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.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'); + }; + // Add upload methods to the scope: + angular.extend($scope, uploadMethods); + // 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: $scope}, + fileUpload.defaults + )).on('fileuploadadd', function (e, data) { + data.scope = $scope; + }).on('fileuploadfail', function (e, data) { + if (data.errorThrown === 'abort') { + return; + } + if (data.dataType && + data.dataType.indexOf('json') === data.dataType.length - 4) { + try { + data.result = angular.fromJson(data.jqXHR.responseText); + } catch (ignore) {} + } + }).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) { + if ($scope.$emit(e.type, data).defaultPrevented) { + e.preventDefault(); + } + }).on('remove', function () { + // Remove upload methods from the scope, + // when the widget is removed: + var method; + for (method in uploadMethods) { + if (uploadMethods.hasOwnProperty(method)) { + delete $scope[method]; + } + } + }); + // Observe option changes: + $scope.$watch( + $attrs.fileUpload, + function (newOptions) { + if (newOptions) { + $element.fileupload('option', newOptions); + } + } + ); + } + ]) + + // Provide File Upload progress feedback: + .controller('FileUploadProgressController', [ + '$scope', '$attrs', '$parse', + function ($scope, $attrs, $parse) { + var fn = $parse($attrs.fileUploadProgress), + update = function () { + var progress = fn($scope); + if (!progress || !progress.total) { + return; + } + $scope.num = Math.floor( + progress.loaded / progress.total * 100 + ); + }; + update(); + $scope.$watch( + $attrs.fileUploadProgress + '.loaded', + function (newValue, oldValue) { + if (newValue !== oldValue) { + update(); + } + } + ); + } + ]) + + // Display File Upload previews: + .controller('FileUploadPreviewController', [ + '$scope', '$element', '$attrs', + function ($scope, $element, $attrs) { + $scope.$watch( + $attrs.fileUploadPreview + '.preview', + function (preview) { + $element.empty(); + if (preview) { + $element.append(preview); + } + } + ); + } + ]) + + .directive('fileUpload', function () { + return { + controller: 'FileUploadController', + scope: true + }; + }) + + .directive('fileUploadProgress', function () { + return { + controller: 'FileUploadProgressController', + scope: true + }; + }) + + .directive('fileUploadPreview', function () { + return { + controller: 'FileUploadPreviewController' + }; + }) + + // Enhance the HTML5 download attribute to + // allow drag&drop of files to the desktop: + .directive('download', function () { + return function (scope, elm) { + elm.on('dragstart', function (e) { + try { + e.originalEvent.dataTransfer.setData( + 'DownloadURL', + [ + 'application/octet-stream', + elm.prop('download'), + elm.prop('href') + ].join(':') + ); + } catch (ignore) {} + }); + }; + }); + +})); diff --git a/app/assets/javascripts/jquery-fileupload/jquery.fileupload-audio.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-audio.js new file mode 100644 index 0000000..575800e --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-audio.js @@ -0,0 +1,106 @@ +/* + * jQuery File Upload Audio Preview Plugin 1.0.3 + * 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 + */ + +/* jshint nomen:false */ +/* global define, window, document */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + 'load-image', + './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: 'loadAudio', + // Use the action as prefix for the "@" options: + prefix: true, + fileTypes: '@', + maxFileSize: '@', + disabled: '@disableAudioPreview' + }, + { + action: 'setAudio', + name: '@audioPreviewName', + disabled: '@disableAudioPreview' + } + ); + + // The File Upload Audio Preview plugin extends the fileupload widget + // with audio preview functionality: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + // The regular expression for the types of audio files to load, + // matched against the file type: + loadAudioFileTypes: /^audio\/.*$/ + }, + + _audioElement: document.createElement('audio'), + + processActions: { + + // Loads the audio file given via data.files and data.index + // as audio element if the browser supports playing it. + // Accepts the options fileTypes (regular expression) + // and maxFileSize (integer) to limit the files to load: + loadAudio: function (data, options) { + if (options.disabled) { + return data; + } + var file = data.files[data.index], + url, + audio; + if (this._audioElement.canPlayType && + this._audioElement.canPlayType(file.type) && + ($.type(options.maxFileSize) !== 'number' || + file.size <= options.maxFileSize) && + (!options.fileTypes || + options.fileTypes.test(file.type))) { + url = loadImage.createObjectURL(file); + if (url) { + audio = this._audioElement.cloneNode(false); + audio.src = url; + audio.controls = true; + data.audio = audio; + return data; + } + } + return data; + }, + + // Sets the audio element as a property of the file object: + setAudio: function (data, options) { + if (data.audio && !options.disabled) { + data.files[data.index][options.name || 'preview'] = data.audio; + } + return data; + } + + } + + }); + +})); diff --git a/app/assets/javascripts/jquery-fileupload/jquery.fileupload-image.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-image.js new file mode 100644 index 0000000..5bb7026 --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-image.js @@ -0,0 +1,315 @@ +/* + * jQuery File Upload Image Preview & Resize Plugin 1.7.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 + */ + +/* jshint nomen:false */ +/* global define, window, Blob */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + 'load-image', + 'load-image-meta', + 'load-image-exif', + 'load-image-ios', + '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: 'loadImageMetaData', + disableImageHead: '@', + disableExif: '@', + disableExifThumbnail: '@', + disableExifSub: '@', + disableExifGps: '@', + disabled: '@disableImageMetaDataLoad' + }, + { + action: 'loadImage', + // Use the action as prefix for the "@" options: + prefix: true, + fileTypes: '@', + maxFileSize: '@', + noRevoke: '@', + disabled: '@disableImageLoad' + }, + { + action: 'resizeImage', + // Use "image" as prefix for the "@" options: + prefix: 'image', + maxWidth: '@', + maxHeight: '@', + minWidth: '@', + minHeight: '@', + crop: '@', + orientation: '@', + forceResize: '@', + disabled: '@disableImageResize' + }, + { + action: 'saveImage', + quality: '@imageQuality', + type: '@imageType', + disabled: '@disableImageResize' + }, + { + action: 'saveImageMetaData', + disabled: '@disableImageMetaDataSave' + }, + { + action: 'resizeImage', + // Use "preview" as prefix for the "@" options: + prefix: 'preview', + maxWidth: '@', + maxHeight: '@', + minWidth: '@', + minHeight: '@', + crop: '@', + orientation: '@', + thumbnail: '@', + canvas: '@', + disabled: '@disableImagePreview' + }, + { + action: 'setImage', + name: '@imagePreviewName', + disabled: '@disableImagePreview' + }, + { + action: 'deleteImageReferences', + disabled: '@disableImageReferencesDeletion' + } + ); + + // 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|svg\+xml)$/, + // The maximum file size of images to load: + loadImageMaxFileSize: 10000000, // 10MB + // The maximum width of resized images: + imageMaxWidth: 1920, + // The maximum height of resized images: + imageMaxHeight: 1080, + // Defines the image orientation (1-8) or takes the orientation + // value from Exif data if set to true: + imageOrientation: false, + // 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, + // Defines the preview orientation (1-8) or takes the orientation + // value from Exif data if set to true: + previewOrientation: true, + // Create the preview using the Exif data thumbnail: + previewThumbnail: true, + // Define if preview images should be cropped or only scaled: + previewCrop: false, + // Define if preview images should be resized as canvas elements: + previewCanvas: true + }, + + processActions: { + + // Loads the image given via data.files and data.index + // as img element, if the browser supports the File API. + // 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) { + data.img = img; + } + dfd.resolveWith(that, [data]); + }, + options + )) { + return 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. + // Also stores the resized image as preview property. + // Accepts the options maxWidth, maxHeight, minWidth, + // minHeight, canvas and crop: + resizeImage: function (data, options) { + if (options.disabled || !(data.canvas || data.img)) { + return data; + } + options = $.extend({canvas: true}, options); + var that = this, + dfd = $.Deferred(), + img = (options.canvas && data.canvas) || data.img, + resolve = function (newImg) { + if (newImg && (newImg.width !== img.width || + newImg.height !== img.height || + options.forceResize)) { + data[newImg.getContext ? 'canvas' : 'img'] = newImg; + } + data.preview = newImg; + dfd.resolveWith(that, [data]); + }, + thumbnail; + if (data.exif) { + if (options.orientation === true) { + options.orientation = data.exif.get('Orientation'); + } + if (options.thumbnail) { + thumbnail = data.exif.get('Thumbnail'); + if (thumbnail) { + loadImage(thumbnail, resolve, options); + return dfd.promise(); + } + } + // Prevent orienting the same image twice: + if (data.orientation) { + delete options.orientation; + } else { + data.orientation = options.orientation; + } + } + if (img) { + resolve(loadImage.scale(img, options)); + return dfd.promise(); + } + 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], + dfd = $.Deferred(); + if (data.canvas.toBlob) { + data.canvas.toBlob( + 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) + ); + } + } + // Don't restore invalid meta data: + if (file.type !== blob.type) { + delete data.imageHead; + } + // Store the created blob at the position + // of the original file in the files list: + data.files[data.index] = blob; + dfd.resolveWith(that, [data]); + }, + options.type || file.type, + options.quality + ); + } else { + return data; + } + return dfd.promise(); + }, + + loadImageMetaData: function (data, options) { + if (options.disabled) { + return data; + } + var that = this, + dfd = $.Deferred(); + loadImage.parseMetaData(data.files[data.index], function (result) { + $.extend(data, result); + dfd.resolveWith(that, [data]); + }, options); + return dfd.promise(); + }, + + saveImageMetaData: function (data, options) { + if (!(data.imageHead && data.canvas && + data.canvas.toBlob && !options.disabled)) { + return data; + } + var file = data.files[data.index], + blob = new Blob([ + data.imageHead, + // Resized images always have a head size of 20 bytes, + // including the JPEG marker and a minimal JFIF header: + this._blobSlice.call(file, 20) + ], {type: file.type}); + blob.name = file.name; + data.files[data.index] = blob; + return data; + }, + + // Sets the resized version of the image as a property of the + // file object, must be called after "saveImage": + setImage: function (data, options) { + if (data.preview && !options.disabled) { + data.files[data.index][options.name || 'preview'] = data.preview; + } + return data; + }, + + deleteImageReferences: function (data, options) { + if (!options.disabled) { + delete data.img; + delete data.canvas; + delete data.preview; + delete data.imageHead; + } + return data; + } + + } + + }); + +})); diff --git a/app/assets/javascripts/jquery-fileupload/jquery.fileupload-jquery-ui.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-jquery-ui.js new file mode 100644 index 0000000..af0a00b --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-jquery-ui.js @@ -0,0 +1,152 @@ +/* + * jQuery File Upload jQuery UI Plugin 8.7.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 + */ + +/* jshint nomen:false */ +/* global define, window */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define(['jquery', './jquery.fileupload-ui'], factory); + } else { + // Browser globals: + factory(window.jQuery); + } +}(function ($) { + 'use strict'; + + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + processdone: function (e, data) { + data.context.find('.start').button('enable'); + }, + progress: function (e, data) { + if (data.context) { + data.context.find('.progress').progressbar( + 'option', + 'value', + parseInt(data.loaded / data.total * 100, 10) + ); + } + }, + progressall: function (e, data) { + var $this = $(this); + $this.find('.fileupload-progress') + .find('.progress').progressbar( + 'option', + 'value', + parseInt(data.loaded / data.total * 100, 10) + ).end() + .find('.progress-extended').each(function () { + $(this).html( + ($this.data('blueimp-fileupload') || + $this.data('fileupload')) + ._renderExtendedProgress(data) + ); + }); + } + }, + + _renderUpload: function (func, files) { + var node = this._super(func, files), + showIconText = $(window).width() > 480; + node.find('.progress').empty().progressbar(); + node.find('.start').button({ + icons: {primary: 'ui-icon-circle-arrow-e'}, + text: showIconText + }); + node.find('.cancel').button({ + icons: {primary: 'ui-icon-cancel'}, + text: showIconText + }); + if (node.hasClass('fade')) { + node.hide(); + } + return node; + }, + + _renderDownload: function (func, files) { + var node = this._super(func, files), + showIconText = $(window).width() > 480; + node.find('.delete').button({ + icons: {primary: 'ui-icon-trash'}, + text: showIconText + }); + if (node.hasClass('fade')) { + node.hide(); + } + return node; + }, + + _startHandler: function (e) { + $(e.currentTarget).button('disable'); + this._super(e); + }, + + _transition: function (node) { + var deferred = $.Deferred(); + if (node.hasClass('fade')) { + node.fadeToggle( + this.options.transitionDuration, + this.options.transitionEasing, + function () { + deferred.resolveWith(node); + } + ); + } else { + deferred.resolveWith(node); + } + return deferred; + }, + + _create: function () { + this._super(); + this.element + .find('.fileupload-buttonbar') + .find('.fileinput-button').each(function () { + var input = $(this).find('input:file').detach(); + $(this) + .button({icons: {primary: 'ui-icon-plusthick'}}) + .append(input); + }) + .end().find('.start') + .button({icons: {primary: 'ui-icon-circle-arrow-e'}}) + .end().find('.cancel') + .button({icons: {primary: 'ui-icon-cancel'}}) + .end().find('.delete') + .button({icons: {primary: 'ui-icon-trash'}}) + .end().find('.progress').progressbar(); + }, + + _destroy: function () { + this.element + .find('.fileupload-buttonbar') + .find('.fileinput-button').each(function () { + var input = $(this).find('input:file').detach(); + $(this) + .button('destroy') + .append(input); + }) + .end().find('.start') + .button('destroy') + .end().find('.cancel') + .button('destroy') + .end().find('.delete') + .button('destroy') + .end().find('.progress').progressbar('destroy'); + this._super(); + } + + }); + +})); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js old mode 100755 new mode 100644 similarity index 82% rename from vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js rename to app/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js index 2f9eeed..8a6b929 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-process.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Processing Plugin 1.1 + * jQuery File Upload Processing Plugin 1.3.0 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2012, Sebastian Tschan @@ -9,8 +9,8 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint nomen: true, unparam: true */ -/*global define, window */ +/* jshint nomen:false */ +/* global define, window */ (function (factory) { 'use strict'; @@ -64,13 +64,17 @@ */ }, - _processFile: function (data) { + _processFile: function (data, originalData) { 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) { + if (originalData.errorThrown) { + return $.Deferred() + .rejectWith(that, [originalData]).promise(); + } return that.processActions[settings.action].call( that, data, @@ -98,14 +102,20 @@ _transformProcessQueue: function (options) { var processQueue = []; $.each(options.processQueue, function () { - var settings = {}; + var settings = {}, + action = this.action, + prefix = this.prefix === true ? action : this.prefix; $.each(this, function (key, value) { if ($.type(value) === 'string' && value.charAt(0) === '@') { - settings[key] = options[value.slice(1)]; + settings[key] = options[ + value.slice(1) || (prefix ? prefix + + key.charAt(0).toUpperCase() + key.slice(1) : key) + ]; } else { settings[key] = value; } + }); processQueue.push(settings); }); @@ -127,10 +137,14 @@ if (this._processing === 0) { this._trigger('processstart'); } - $.each(data.files, function (index, file) { + $.each(data.files, function (index) { var opts = index ? $.extend({}, options) : options, func = function () { - return that._processFile(opts); + if (data.errorThrown) { + return $.Deferred() + .rejectWith(that, [data]).promise(); + } + return that._processFile(opts, data); }; opts.index = index; that._processing += 1; diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js old mode 100755 new mode 100644 similarity index 82% rename from vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js rename to app/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js index 5d22346..710e869 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-ui.js +++ b/app/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 9.6.0 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -9,8 +9,8 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, URL, webkitURL, FileReader */ +/* jshint nomen:false */ +/* global define, window */ (function (factory) { 'use strict'; @@ -19,7 +19,9 @@ define([ 'jquery', 'tmpl', - './jquery.fileupload-resize', + './jquery.fileupload-image', + './jquery.fileupload-audio', + './jquery.fileupload-video', './jquery.fileupload-validate' ], factory); } else { @@ -29,7 +31,7 @@ window.tmpl ); } -}(function ($, tmpl, loadImage) { +}(function ($, tmpl) { 'use strict'; $.blueimp.fileupload.prototype._specialOptions.push( @@ -61,10 +63,16 @@ // option of the $.ajax upload requests: dataType: 'json', + // Error and info messages: + messages: { + unknownError: 'Unknown error' + }, + // Function returning the current number of files, // used by the maxNumberOfFiles validation: getNumberOfFiles: function () { - return this.filesContainer.children().length; + return this.filesContainer.children() + .not('.processing').length; }, // Callback to retrieve the list of files from the server response: @@ -79,33 +87,53 @@ // widget (via file input selection, drag & drop or add API call). // See the basic file upload widget for more information: add: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var $this = $(this), that = $this.data('blueimp-fileupload') || $this.data('fileupload'), - options = that.options, - files = data.files; + options = that.options; + data.context = that._renderUpload(data.files) + .data('data', data) + .addClass('processing'); + options.filesContainer[ + options.prependFiles ? 'prepend' : 'append' + ](data.context); + that._forceReflow(data.context); + that._transition(data.context); data.process(function () { return $this.fileupload('process', data); }).always(function () { - data.context = that._renderUpload(files).data('data', data); + data.context.each(function (index) { + $(this).find('.size').text( + that._formatFileSize(data.files[index].size) + ); + }).removeClass('processing'); that._renderPreviews(data); - options.filesContainer[ - options.prependFiles ? 'prepend' : 'append' - ](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.submit(); + }).done(function () { + data.context.find('.start').prop('disabled', false); + if ((that._trigger('added', e, data) !== false) && + (options.autoUpload || data.autoUpload) && + data.autoUpload !== false) { + data.submit(); + } + }).fail(function () { + if (data.files.error) { + data.context.each(function (index) { + var error = data.files[index].error; + if (error) { + $(this).find('.error').text(error); } - } - ); + }); + } }); }, // Callback for the start of each file upload request: send: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); if (data.context && data.dataType && @@ -118,7 +146,7 @@ !$.support.transition && 'progress-animated' ) .attr('aria-valuenow', 100) - .find('.bar').css( + .children().first().css( 'width', '100%' ); @@ -127,6 +155,9 @@ }, // Callback for successful uploads: done: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'), getFilesFromResponse = data.getFilesFromResponse || @@ -137,8 +168,8 @@ if (data.context) { data.context.each(function (index) { var file = files[index] || - {error: 'Empty file upload result'}, - deferred = that._addFinishedDeferreds(); + {error: 'Empty file upload result'}; + deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { var node = $(this); @@ -157,8 +188,9 @@ ); }); } else { - template = that._renderDownload(files) - .appendTo(that.options.filesContainer); + template = that._renderDownload(files)[ + that.options.prependFiles ? 'prependTo' : 'appendTo' + ](that.options.filesContainer); that._forceReflow(template); deferred = that._addFinishedDeferreds(); that._transition(template).done( @@ -173,6 +205,9 @@ }, // Callback for failed (abort or error) uploads: fail: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'), template, @@ -182,7 +217,7 @@ if (data.errorThrown !== 'abort') { var file = data.files[index]; file.error = file.error || data.errorThrown || - true; + data.i18n('unknownError'); deferred = that._addFinishedDeferreds(); that._transition($(this)).done( function () { @@ -213,8 +248,9 @@ } }); } else if (data.errorThrown !== 'abort') { - data.context = that._renderUpload(data.files) - .appendTo(that.options.filesContainer) + data.context = that._renderUpload(data.files)[ + that.options.prependFiles ? 'prependTo' : 'appendTo' + ](that.options.filesContainer) .data('data', data); that._forceReflow(data.context); deferred = that._addFinishedDeferreds(); @@ -234,18 +270,26 @@ }, // Callback for upload progress events: progress: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } + var progress = Math.floor(data.loaded / data.total * 100); 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.each(function () { + $(this).find('.progress') + .attr('aria-valuenow', progress) + .children().first().css( + 'width', + progress + '%' + ); + }); } }, // Callback for global upload progress events: progressall: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var $this = $(this), progress = Math.floor(data.loaded / data.total * 100), globalProgressNode = $this.find('.fileupload-progress'), @@ -260,13 +304,16 @@ globalProgressNode .find('.progress') .attr('aria-valuenow', progress) - .find('.bar').css( + .children().first().css( 'width', progress + '%' ); }, // Callback for uploads start, equivalent to the global ajaxStart event: start: function (e) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'); that._resetFinishedDeferreds(); @@ -278,6 +325,9 @@ }, // Callback for uploads stop, equivalent to the global ajaxStop event: stop: function (e) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || $(this).data('fileupload'), deferred = that._addFinishedDeferreds(); @@ -289,31 +339,46 @@ function () { $(this).find('.progress') .attr('aria-valuenow', '0') - .find('.bar').css('width', '0%'); + .children().first().css('width', '0%'); $(this).find('.progress-extended').html(' '); deferred.resolve(); } ); }, - processstart: function () { + processstart: function (e) { + if (e.isDefaultPrevented()) { + return false; + } $(this).addClass('fileupload-processing'); }, - processstop: function () { + processstop: function (e) { + if (e.isDefaultPrevented()) { + return false; + } $(this).removeClass('fileupload-processing'); }, // Callback for file deletion: destroy: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } var that = $(this).data('blueimp-fileupload') || - $(this).data('fileupload'); - if (data.url) { - $.ajax(data).done(function () { + $(this).data('fileupload'), + removeNode = function () { that._transition(data.context).done( function () { $(this).remove(); that._trigger('destroyed', e, data); } ); + }; + if (data.url) { + data.dataType = data.dataType || that.options.dataType; + $.ajax(data).done(removeNode).fail(function () { + that._trigger('destroyfailed', e, data); }); + } else { + removeNode(); } } }, @@ -446,20 +511,23 @@ var button = $(e.currentTarget), template = button.closest('.template-upload'), data = template.data('data'); - if (data && data.submit && !data.jqXHR && data.submit()) { - button.prop('disabled', true); + button.prop('disabled', true); + if (data && data.submit) { + data.submit(); } }, _cancelHandler: function (e) { e.preventDefault(); - var template = $(e.currentTarget).closest('.template-upload'), + var template = $(e.currentTarget) + .closest('.template-upload,.template-download'), data = template.data('data') || {}; - if (!data.jqXHR) { + data.context = data.context || template; + if (data.abort) { + data.abort(); + } else { data.errorThrown = 'abort'; this._trigger('fail', e, data); - } else { - data.jqXHR.abort(); } }, @@ -606,6 +674,9 @@ _create: function () { this._super(); this._resetFinishedDeferreds(); + if (!$.support.fileInput) { + this._disableFileInputButton(); + } }, enable: function () { diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js old mode 100755 new mode 100644 similarity index 81% rename from vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js rename to app/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js index 2599da8..f93a18f --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-validate.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Validation Plugin 1.0.2 + * jQuery File Upload Validation Plugin 1.1.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2013, Sebastian Tschan @@ -9,8 +9,7 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window */ +/* global define, window */ (function (factory) { 'use strict'; @@ -37,10 +36,10 @@ // even if the previous action was rejected: always: true, // Options taken from the global options map: - acceptFileTypes: '@acceptFileTypes', - maxFileSize: '@maxFileSize', - minFileSize: '@minFileSize', - maxNumberOfFiles: '@maxNumberOfFiles', + acceptFileTypes: '@', + maxFileSize: '@', + minFileSize: '@', + maxNumberOfFiles: '@', disabled: '@disableValidation' } ); @@ -84,18 +83,22 @@ 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) { + fileSize; + if (options.minFileSize || options.maxFileSize) { + fileSize = file.size; + } + if ($.type(options.maxNumberOfFiles) === 'number' && + (settings.getNumberOfFiles() || 0) + 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) { + } else if (fileSize > options.maxFileSize) { file.error = settings.i18n('maxFileSize'); - } else if ($.type(file.size) === 'number' && - file.size < options.minFileSize) { + } else if ($.type(fileSize) === 'number' && + fileSize < options.minFileSize) { file.error = settings.i18n('minFileSize'); } else { delete file.error; diff --git a/app/assets/javascripts/jquery-fileupload/jquery.fileupload-video.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-video.js new file mode 100644 index 0000000..3764b27 --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload-video.js @@ -0,0 +1,106 @@ +/* + * jQuery File Upload Video Preview Plugin 1.0.3 + * 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 + */ + +/* jshint nomen:false */ +/* global define, window, document */ + +(function (factory) { + 'use strict'; + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + 'jquery', + 'load-image', + './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: 'loadVideo', + // Use the action as prefix for the "@" options: + prefix: true, + fileTypes: '@', + maxFileSize: '@', + disabled: '@disableVideoPreview' + }, + { + action: 'setVideo', + name: '@videoPreviewName', + disabled: '@disableVideoPreview' + } + ); + + // The File Upload Video Preview plugin extends the fileupload widget + // with video preview functionality: + $.widget('blueimp.fileupload', $.blueimp.fileupload, { + + options: { + // The regular expression for the types of video files to load, + // matched against the file type: + loadVideoFileTypes: /^video\/.*$/ + }, + + _videoElement: document.createElement('video'), + + processActions: { + + // Loads the video file given via data.files and data.index + // as video element if the browser supports playing it. + // Accepts the options fileTypes (regular expression) + // and maxFileSize (integer) to limit the files to load: + loadVideo: function (data, options) { + if (options.disabled) { + return data; + } + var file = data.files[data.index], + url, + video; + if (this._videoElement.canPlayType && + this._videoElement.canPlayType(file.type) && + ($.type(options.maxFileSize) !== 'number' || + file.size <= options.maxFileSize) && + (!options.fileTypes || + options.fileTypes.test(file.type))) { + url = loadImage.createObjectURL(file); + if (url) { + video = this._videoElement.cloneNode(false); + video.src = url; + video.controls = true; + data.video = video; + return data; + } + } + return data; + }, + + // Sets the video element as a property of the file object: + setVideo: function (data, options) { + if (data.video && !options.disabled) { + data.files[data.index][options.name || 'preview'] = data.video; + } + return data; + } + + } + + }); + +})); diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js b/app/assets/javascripts/jquery-fileupload/jquery.fileupload.js old mode 100755 new mode 100644 similarity index 81% rename from vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js rename to app/assets/javascripts/jquery-fileupload/jquery.fileupload.js index 03678f3..a4cfdc0 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.fileupload.js +++ b/app/assets/javascripts/jquery-fileupload/jquery.fileupload.js @@ -1,5 +1,5 @@ /* - * jQuery File Upload Plugin 5.31.1 + * jQuery File Upload Plugin 5.42.0 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2010, Sebastian Tschan @@ -9,8 +9,8 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint nomen: true, unparam: true, regexp: true */ -/*global define, window, document, File, Blob, FormData, location */ +/* jshint nomen:false */ +/* global define, window, document, location, Blob, FormData */ (function (factory) { 'use strict'; @@ -27,12 +27,49 @@ }(function ($) { 'use strict'; + // Detect file input support, based on + // http://viljamis.com/blog/2012/file-upload-support-on-mobile/ + $.support.fileInput = !(new RegExp( + // Handle devices which give false positives for the feature detection: + '(Android (1\\.[0156]|2\\.[01]))' + + '|(Windows Phone (OS 7|8\\.0))|(XBLWP)|(ZuneWP)|(WPDesktop)' + + '|(w(eb)?OSBrowser)|(webOS)' + + '|(Kindle/(1\\.0|2\\.[05]|3\\.0))' + ).test(window.navigator.userAgent) || + // Feature detection for all other devices: + $('').prop('disabled')); + // The FileReader API is not actually used, but works as feature detection, - // as e.g. Safari supports XHR file uploads via the FormData API, - // but not non-multipart XHR file uploads: - $.support.xhrFileUpload = !!(window.XMLHttpRequestUpload && window.FileReader); + // as some Safari versions (5?) support XHR file uploads via the FormData API, + // but not non-multipart XHR file uploads. + // window.XMLHttpRequestUpload is not available on IE10, so we check for + // window.ProgressEvent instead to detect XHR2 file upload capability: + $.support.xhrFileUpload = !!(window.ProgressEvent && window.FileReader); $.support.xhrFormDataFileUpload = !!window.FormData; + // Detect support for Blob slicing (required for chunked uploads): + $.support.blobSlice = window.Blob && (Blob.prototype.slice || + Blob.prototype.webkitSlice || Blob.prototype.mozSlice); + + // Helper function to create drag handlers for dragover/dragenter/dragleave: + function getDragHandler(type) { + var isDragOver = type === 'dragover'; + return function (e) { + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; + var dataTransfer = e.dataTransfer; + if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 && + this._trigger( + type, + $.Event(type, {delegatedEvent: e}) + ) !== false) { + e.preventDefault(); + if (isDragOver) { + dataTransfer.dropEffect = 'copy'; + } + } + }; + } + // 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 @@ -47,9 +84,9 @@ // The drop target element(s), by the default the complete document. // Set to null to disable drag & drop support: dropZone: $(document), - // The paste target element(s), by the default the complete document. - // Set to null to disable paste support: - pasteZone: $(document), + // The paste target element(s), by the default undefined. + // Set to a DOM node or jQuery object to enable file pasting: + pasteZone: undefined, // 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. @@ -72,6 +109,14 @@ // To limit the number of files uploaded with one XHR request, // set the following option to an integer greater than 0: limitMultiFileUploads: undefined, + // The following option limits the number of files uploaded with one + // XHR request to keep the request size under or equal to the defined + // limit in bytes: + limitMultiFileUploadSize: undefined, + // Multipart file uploads add a number of bytes to each uploaded file, + // therefore the following option adds an overhead for each file used + // in the limitMultiFileUploadSize configuration: + limitMultiFileUploadSizeOverhead: 512, // Set the following option to true to issue all file upload requests // in a sequential order: sequentialUploads: false, @@ -144,17 +189,23 @@ // The add callback is invoked as soon as files are added to the fileupload // widget (via file input selection, drag & drop, paste or add API call). // If the singleFileUploads option is enabled, this callback will be - // called once for each file in the selection for XHR file uplaods, else + // called once for each file in the selection for XHR file uploads, else // once for each file selection. + // // The upload starts when the submit method is invoked on the data parameter. // The data object contains a files property holding the added files - // and allows to override plugin options as well as define ajax settings. + // and allows you to override plugin options as well as define ajax settings. + // // Listeners for this callback can also be bound the following way: // .bind('fileuploadadd', func); + // // data.submit() returns a Promise object and allows to attach additional // handlers using jQuery's Deferred callbacks: // data.submit().done(func).fail(func).always(func); add: function (e, data) { + if (e.isDefaultPrevented()) { + return false; + } if (data.autoUpload || (data.autoUpload !== false && $(this).fileupload('option', 'autoUpload'))) { data.process().done(function () { @@ -233,6 +284,11 @@ 'forceIframeTransport' ], + _blobSlice: $.support.blobSlice && function () { + var slice = this.slice || this.webkitSlice || this.mozSlice; + return slice.apply(this, arguments); + }, + _BitrateTimer: function () { this.timestamp = ((Date.now) ? Date.now() : (new Date()).getTime()); this.loaded = 0; @@ -256,7 +312,7 @@ _getFormData: function (options) { var formData; - if (typeof options.formData === 'function') { + if ($.type(options.formData) === 'function') { return options.formData(options.form); } if ($.isArray(options.formData)) { @@ -336,10 +392,18 @@ // Trigger a custom progress event with a total data property set // to the file size(s) of the current upload and a loaded data // property calculated accordingly: - this._trigger('progress', e, data); + this._trigger( + 'progress', + $.Event('progress', {delegatedEvent: e}), + data + ); // Trigger a global progress event for all current file uploads, // including ajax calls queued for sequential file uploads: - this._trigger('progressall', e, this._progress); + this._trigger( + 'progressall', + $.Event('progressall', {delegatedEvent: e}), + this._progress + ); } }, @@ -374,15 +438,18 @@ file = options.files[0], // Ignore non-multipart setting if not supported: multipart = options.multipart || !$.support.xhrFileUpload, - paramName = options.paramName[0]; - options.headers = options.headers || {}; + paramName = $.type(options.paramName) === 'array' ? + options.paramName[0] : options.paramName; + options.headers = $.extend({}, options.headers); if (options.contentRange) { options.headers['Content-Range'] = options.contentRange; } - if (!multipart) { + if (!multipart || options.blob || !this._isInstanceOf('File', file)) { options.headers['Content-Disposition'] = 'attachment; filename="' + encodeURI(file.name) + '"'; - options.contentType = file.type; + } + if (!multipart) { + options.contentType = file.type || 'application/octet-stream'; options.data = options.blob || file; } else if ($.support.xhrFormDataFileUpload) { if (options.postMessage) { @@ -399,7 +466,8 @@ } else { $.each(options.files, function (index, file) { formData.push({ - name: options.paramName[index] || paramName, + name: ($.type(options.paramName) === 'array' && + options.paramName[index]) || paramName, value: file }); }); @@ -414,8 +482,6 @@ }); } if (options.blob) { - options.headers['Content-Disposition'] = 'attachment; filename="' + - encodeURI(file.name) + '"'; formData.append(paramName, options.blob, file.name); } else { $.each(options.files, function (index, file) { @@ -424,9 +490,10 @@ if (that._isInstanceOf('File', file) || that._isInstanceOf('Blob', file)) { formData.append( - options.paramName[index] || paramName, + ($.type(options.paramName) === 'array' && + options.paramName[index]) || paramName, file, - file.name + file.uploadName || file.name ); } }); @@ -439,13 +506,13 @@ }, _initIframeSettings: function (options) { + var targetHost = $('').prop('href', options.url).prop('host'); // Setting the dataType to iframe enables the iframe transport: options.dataType = 'iframe ' + (options.dataType || ''); // The iframe transport accepts a serialized array as form data: options.formData = this._getFormData(options); // Add redirect url to form data on cross-domain uploads: - if (options.redirect && $('').prop('href', options.url) - .prop('host') !== location.host) { + if (options.redirect && targetHost && targetHost !== location.host) { options.formData.push({ name: options.redirectParamName || 'redirect', value: options.redirect @@ -510,8 +577,10 @@ options.url = options.form.prop('action') || location.href; } // The HTTP request method must be "POST" or "PUT": - options.type = (options.type || options.form.prop('method') || '') - .toUpperCase(); + options.type = (options.type || + ($.type(options.form.prop('method')) === 'string' && + options.form.prop('method')) || '' + ).toUpperCase(); if (options.type !== 'POST' && options.type !== 'PUT' && options.type !== 'PATCH') { options.type = 'POST'; @@ -570,22 +639,32 @@ // Adds convenience methods to the data callback argument: _addConvenienceMethods: function (e, data) { var that = this, - getPromise = function (data) { - return $.Deferred().resolveWith(that, [data]).promise(); + getPromise = function (args) { + return $.Deferred().resolveWith(that, args).promise(); }; data.process = function (resolveFunc, rejectFunc) { if (resolveFunc || rejectFunc) { data._processQueue = this._processQueue = - (this._processQueue || getPromise(this)) - .pipe(resolveFunc, rejectFunc); + (this._processQueue || getPromise([this])).pipe( + function () { + if (data.errorThrown) { + return $.Deferred() + .rejectWith(that, [data]).promise(); + } + return getPromise(arguments); + } + ).pipe(resolveFunc, rejectFunc); } - return this._processQueue || getPromise(this); + return this._processQueue || getPromise([this]); }; data.submit = function () { if (this.state() !== 'pending') { data.jqXHR = this.jqXHR = - (that._trigger('submit', e, this) !== false) && - that._onSend(e, this); + (that._trigger( + 'submit', + $.Event('submit', {delegatedEvent: e}), + this + ) !== false) && that._onSend(e, this); } return this.jqXHR || that._getXHRPromise(); }; @@ -593,7 +672,9 @@ if (this.jqXHR) { return this.jqXHR.abort(); } - return that._getXHRPromise(); + this.errorThrown = 'abort'; + that._trigger('fail', null, this); + return that._getXHRPromise(false); }; data.state = function () { if (this.jqXHR) { @@ -603,6 +684,10 @@ return that._getDeferredState(this._processQueue); } }; + data.processing = function () { + return !this.jqXHR && this._processQueue && that + ._getDeferredState(this._processQueue) === 'pending'; + }; data.progress = function () { return this._progress; }; @@ -627,12 +712,13 @@ // should be uploaded in chunks, but does not invoke any // upload requests: _chunkedUpload: function (options, testOnly) { + options.uploadedBytes = options.uploadedBytes || 0; var that = this, file = options.files[0], fs = file.size, - ub = options.uploadedBytes = options.uploadedBytes || 0, + ub = options.uploadedBytes, mcs = options.maxChunkSize || fs, - slice = file.slice || file.webkitSlice || file.mozSlice, + slice = this._blobSlice, dfd = $.Deferred(), promise = dfd.promise(), jqXHR, @@ -804,7 +890,11 @@ // Set timer for bitrate progress calculation: options._bitrateTimer = new that._BitrateTimer(); jqXHR = jqXHR || ( - ((aborted || that._trigger('send', e, options) === false) && + ((aborted || that._trigger( + 'send', + $.Event('send', {delegatedEvent: e}), + options + ) === false) && that._getXHRPromise(false, options.context, aborted)) || that._chunkedUpload(options) || $.ajax(options) ).done(function (result, textStatus, jqXHR) { @@ -850,7 +940,8 @@ this._slots.push(slot); pipe = slot.pipe(send); } else { - pipe = (this._sequence = this._sequence.pipe(send, send)); + this._sequence = this._sequence.pipe(send, send); + pipe = this._sequence; } // Return the piped Promise object, enhanced with an abort method, // which is delegated to the jqXHR object of the current upload, @@ -874,46 +965,80 @@ var that = this, result = true, options = $.extend({}, this.options, data), + files = data.files, + filesLength = files.length, limit = options.limitMultiFileUploads, + limitSize = options.limitMultiFileUploadSize, + overhead = options.limitMultiFileUploadSizeOverhead, + batchSize = 0, paramName = this._getParamName(options), paramNameSet, paramNameSlice, fileSet, - i; - if (!(options.singleFileUploads || limit) || + i, + j = 0; + if (limitSize && (!filesLength || files[0].size === undefined)) { + limitSize = undefined; + } + if (!(options.singleFileUploads || limit || limitSize) || !this._isXHRUpload(options)) { - fileSet = [data.files]; + fileSet = [files]; paramNameSet = [paramName]; - } else if (!options.singleFileUploads && limit) { + } else if (!(options.singleFileUploads || limitSize) && limit) { fileSet = []; paramNameSet = []; - for (i = 0; i < data.files.length; i += limit) { - fileSet.push(data.files.slice(i, i + limit)); + for (i = 0; i < filesLength; i += limit) { + fileSet.push(files.slice(i, i + limit)); paramNameSlice = paramName.slice(i, i + limit); if (!paramNameSlice.length) { paramNameSlice = paramName; } paramNameSet.push(paramNameSlice); } + } else if (!options.singleFileUploads && limitSize) { + fileSet = []; + paramNameSet = []; + for (i = 0; i < filesLength; i = i + 1) { + batchSize += files[i].size + overhead; + if (i + 1 === filesLength || + ((batchSize + files[i + 1].size + overhead) > limitSize) || + (limit && i + 1 - j >= limit)) { + fileSet.push(files.slice(j, i + 1)); + paramNameSlice = paramName.slice(j, i + 1); + if (!paramNameSlice.length) { + paramNameSlice = paramName; + } + paramNameSet.push(paramNameSlice); + j = i + 1; + batchSize = 0; + } + } } else { paramNameSet = paramName; } - data.originalFiles = data.files; - $.each(fileSet || data.files, function (index, element) { + data.originalFiles = files; + $.each(fileSet || files, function (index, element) { var newData = $.extend({}, data); newData.files = fileSet ? element : [element]; newData.paramName = paramNameSet[index]; that._initResponseObject(newData); that._initProgressObject(newData); that._addConvenienceMethods(e, newData); - result = that._trigger('add', e, newData); + result = that._trigger( + 'add', + $.Event('add', {delegatedEvent: e}), + newData + ); return result; }); return result; }, - _replaceFileInput: function (input) { - var inputClone = input.clone(true); + _replaceFileInput: function (data) { + var input = data.fileInput, + inputClone = input.clone(true); + // Add a reference for the new cloned file input to the data argument: + data.fileInputClone = inputClone; $('
').append(inputClone)[0].reset(); // Detaching allows to insert the fileInput on another form // without loosing the file input value: @@ -949,7 +1074,25 @@ // to be returned together in one set: dfd.resolve([e]); }, - dirReader; + successHandler = function (entries) { + that._handleFileTreeEntries( + entries, + path + entry.name + '/' + ).done(function (files) { + dfd.resolve(files); + }).fail(errorHandler); + }, + readEntries = function () { + dirReader.readEntries(function (results) { + if (!results.length) { + successHandler(entries); + } else { + entries = entries.concat(results); + readEntries(); + } + }, errorHandler); + }, + dirReader, entries = []; path = path || ''; if (entry.isFile) { if (entry._file) { @@ -964,14 +1107,7 @@ } } 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); + readEntries(); } else { // Return an empy list for file system items // other than files or directories: @@ -1073,9 +1209,13 @@ this._getFileInputFiles(data.fileInput).always(function (files) { data.files = files; if (that.options.replaceFileInput) { - that._replaceFileInput(data.fileInput); + that._replaceFileInput(data); } - if (that._trigger('change', e, data) !== false) { + if (that._trigger( + 'change', + $.Event('change', {delegatedEvent: e}), + data + ) !== false) { that._onAdd(e, data); } }); @@ -1092,60 +1232,65 @@ data.files.push(file); } }); - if (this._trigger('paste', e, data) === false || - this._onAdd(e, data) === false) { - return false; + if (this._trigger( + 'paste', + $.Event('paste', {delegatedEvent: e}), + data + ) !== false) { + this._onAdd(e, data); } } }, _onDrop: function (e) { + e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer; var that = this, - dataTransfer = e.dataTransfer = e.originalEvent && - e.originalEvent.dataTransfer, + dataTransfer = e.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) { + if (that._trigger( + 'drop', + $.Event('drop', {delegatedEvent: e}), + data + ) !== false) { that._onAdd(e, data); } }); } }, - _onDragOver: function (e) { - 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(); - } - } - }, + _onDragOver: getDragHandler('dragover'), + + _onDragEnter: getDragHandler('dragenter'), + + _onDragLeave: getDragHandler('dragleave'), _initEventHandlers: function () { if (this._isXHRUpload(this.options)) { this._on(this.options.dropZone, { dragover: this._onDragOver, - drop: this._onDrop + drop: this._onDrop, + // event.preventDefault() on dragenter is required for IE10+: + dragenter: this._onDragEnter, + // dragleave is not required, but added for completeness: + dragleave: this._onDragLeave }); this._on(this.options.pasteZone, { paste: this._onPaste }); } - this._on(this.options.fileInput, { - change: this._onChange - }); + if ($.support.fileInput) { + this._on(this.options.fileInput, { + change: this._onChange + }); + } }, _destroyEventHandlers: function () { - this._off(this.options.dropZone, 'dragover drop'); + this._off(this.options.dropZone, 'dragenter dragleave dragover drop'); this._off(this.options.pasteZone, 'paste'); this._off(this.options.fileInput, 'change'); }, @@ -1192,15 +1337,21 @@ _initDataAttributes: function () { var that = this, - options = this.options; + options = this.options, + clone = $(this.element[0].cloneNode(false)); // Initialize options set via HTML5 data-attributes: $.each( - $(this.element[0].cloneNode(false)).data(), + clone.data(), function (key, value) { - if (that._isRegExpOption(key, value)) { - value = that._getRegExp(value); + var dataAttributeName = 'data-' + + // Convert camelCase to hyphen-ated key: + key.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); + if (clone.attr(dataAttributeName)) { + if (that._isRegExpOption(key, value)) { + value = that._getRegExp(value); + } + options[key] = value; } - options[key] = value; } ); }, @@ -1275,8 +1426,13 @@ if (aborted) { return; } + if (!files.length) { + dfd.reject(); + return; + } data.files = files; - jqXHR = that._onSend(null, data).then( + jqXHR = that._onSend(null, data); + jqXHR.then( function (result, textStatus, jqXHR) { dfd.resolve(result, textStatus, jqXHR); }, diff --git a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js b/app/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js old mode 100755 new mode 100644 similarity index 80% rename from vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js rename to app/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js index e04e7a0..8d64b59 --- a/vendor/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js +++ b/app/assets/javascripts/jquery-fileupload/jquery.iframe-transport.js @@ -1,5 +1,5 @@ /* - * jQuery Iframe Transport Plugin 1.6.2 + * jQuery Iframe Transport Plugin 1.8.2 * https://github.com/blueimp/jQuery-File-Upload * * Copyright 2011, Sebastian Tschan @@ -9,8 +9,7 @@ * http://www.opensource.org/licenses/MIT */ -/*jslint unparam: true, nomen: true */ -/*global define, window, document */ +/* global define, window, document */ (function (factory) { 'use strict'; @@ -27,7 +26,7 @@ // Helper variable to create unique names for the transport iframes: var counter = 0; - // The iframe transport accepts three additional options: + // The iframe transport accepts four additional options: // options.fileInput: a jQuery collection of file input fields // options.paramName: the parameter name for the file form data, // overrides the name property of the file input field(s), @@ -35,9 +34,16 @@ // options.formData: an array of objects with name and value properties, // equivalent to the return data of .serializeArray(), e.g.: // [{name: 'a', value: 1}, {name: 'b', value: 2}] + // options.initialIframeSrc: the URL of the initial iframe src, + // by default set to "javascript:false;" $.ajaxTransport('iframe', function (options) { if (options.async) { - var form, + // javascript:false as initial iframe src + // prevents warning popups on HTTPS in IE6: + /*jshint scripturl: true */ + var initialIframeSrc = options.initialIframeSrc || 'javascript:false;', + /*jshint scripturl: false */ + form, iframe, addParamChar; return { @@ -56,15 +62,13 @@ 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 // elements that have already been added to the DOM, // so we set the name along with the iframe HTML markup: counter += 1; iframe = $( - '' + '' ).bind('load', function () { var fileInputClones, paramNames = $.isArray(options.paramName) ? @@ -95,7 +99,7 @@ ); // Fix for IE endless progress bar activity bug // (happens on form submits to iframe targets): - $('') + $('') .appendTo(form); window.setTimeout(function () { // Removing the form in a setTimeout call @@ -138,6 +142,8 @@ .prop('enctype', 'multipart/form-data') // enctype must be set as encoding for IE: .prop('encoding', 'multipart/form-data'); + // Remove the HTML5 form attribute from the input(s): + options.fileInput.removeAttr('form'); } form.submit(); // Insert the file input fields at their original location @@ -145,7 +151,10 @@ if (fileInputClones && fileInputClones.length) { options.fileInput.each(function (index, input) { var clone = $(fileInputClones[index]); - $(input).prop('name', clone.prop('name')); + // Restore the original name and form properties: + $(input) + .prop('name', clone.prop('name')) + .attr('form', clone.attr('form')); clone.replaceWith(input); }); } @@ -159,7 +168,7 @@ // concat is used to avoid the "Script URL" JSLint error: iframe .unbind('load') - .prop('src', 'javascript'.concat(':false;')); + .prop('src', initialIframeSrc); } if (form) { form.remove(); @@ -170,7 +179,15 @@ }); // The iframe transport returns the iframe content document as response. - // The following adds converters from iframe to text, json, html, and script: + // The following adds converters from iframe to text, json, html, xml + // and script. + // Please note that the Content-Type for JSON responses has to be text/plain + // or text/html, if the browser doesn't include application/json in the + // Accept header, else IE will show a download dialog. + // The Content-Type for XML responses on the other hand has to be always + // application/xml or text/xml, so IE properly parses the XML response. + // See also + // https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation $.ajaxSetup({ converters: { 'iframe text': function (iframe) { @@ -182,6 +199,12 @@ 'iframe html': function (iframe) { return iframe && $(iframe[0].body).html(); }, + 'iframe xml': function (iframe) { + var xmlDoc = iframe && iframe[0]; + return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc : + $.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) || + $(xmlDoc.body).html()); + }, 'iframe script': function (iframe) { return iframe && $.globalEval($(iframe[0].body).text()); } diff --git a/vendor/assets/javascripts/jquery-fileupload/locale.js b/app/assets/javascripts/jquery-fileupload/locale.js similarity index 100% rename from vendor/assets/javascripts/jquery-fileupload/locale.js rename to app/assets/javascripts/jquery-fileupload/locale.js diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js b/app/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js similarity index 100% rename from vendor/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js rename to app/assets/javascripts/jquery-fileupload/vendor/canvas-to-blob.js diff --git a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js b/app/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js similarity index 87% rename from vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js rename to app/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js index fd2948f..7899e6b 100644 --- a/vendor/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js +++ b/app/assets/javascripts/jquery-fileupload/vendor/jquery.ui.widget.js @@ -1,36 +1,52 @@ -/* - * jQuery UI Widget 1.10.1+amd - * https://github.com/blueimp/jQuery-File-Upload +/*! jQuery UI - v1.11.1 - 2014-09-17 +* http://jqueryui.com +* Includes: widget.js +* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */ + +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ "jquery" ], factory ); + } else { + + // Browser globals + factory( jQuery ); + } +}(function( $ ) { +/*! + * jQuery UI Widget 1.11.1 + * http://jqueryui.com * - * Copyright 2013 jQuery Foundation and other contributors + * Copyright 2014 jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/jQuery.widget/ */ -(function (factory) { - if (typeof define === "function" && define.amd) { - // Register as an anonymous AMD module: - define(["jquery"], factory); - } else { - // Browser globals: - factory(jQuery); - } -}(function( $, undefined ) { - -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 ); -}; + +var widget_uuid = 0, + widget_slice = Array.prototype.slice; + +$.cleanData = (function( orig ) { + return function( elems ) { + var events, elem, i; + for ( i = 0; (elem = elems[i]) != null; i++ ) { + try { + + // Only trigger remove when necessary to save time + events = $._data( elem, "events" ); + if ( events && events.remove ) { + $( elem ).triggerHandler( "remove" ); + } + + // http://bugs.jquery.com/ticket/8235 + } catch( e ) {} + } + orig( elems ); + }; +})( $.cleanData ); $.widget = function( name, base, prototype ) { var fullName, existingConstructor, constructor, basePrototype, @@ -115,7 +131,7 @@ $.widget = function( name, base, prototype ) { // 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: existingConstructor ? basePrototype.widgetEventPrefix : name + widgetEventPrefix: existingConstructor ? (basePrototype.widgetEventPrefix || name) : name }, proxiedPrototype, { constructor: constructor, namespace: namespace, @@ -143,10 +159,12 @@ $.widget = function( name, base, prototype ) { } $.widget.bridge( name, constructor ); + + return constructor; }; $.widget.extend = function( target ) { - var input = slice.call( arguments, 1 ), + var input = widget_slice.call( arguments, 1 ), inputIndex = 0, inputLength = input.length, key, @@ -175,7 +193,7 @@ $.widget.bridge = function( name, object ) { var fullName = object.prototype.widgetFullName || name; $.fn[ name ] = function( options ) { var isMethodCall = typeof options === "string", - args = slice.call( arguments, 1 ), + args = widget_slice.call( arguments, 1 ), returnValue = this; // allow multiple hashes to be passed on init @@ -187,6 +205,10 @@ $.widget.bridge = function( name, object ) { this.each(function() { var methodValue, instance = $.data( this, fullName ); + if ( options === "instance" ) { + returnValue = instance; + return false; + } if ( !instance ) { return $.error( "cannot call methods on " + name + " prior to initialization; " + "attempted to call method '" + options + "'" ); @@ -206,7 +228,10 @@ $.widget.bridge = function( name, object ) { this.each(function() { var instance = $.data( this, fullName ); if ( instance ) { - instance.option( options || {} )._init(); + instance.option( options || {} ); + if ( instance._init ) { + instance._init(); + } } else { $.data( this, fullName, new object( options, this ) ); } @@ -233,7 +258,7 @@ $.Widget.prototype = { _createWidget: function( options, element ) { element = $( element || this.defaultElement || this )[ 0 ]; this.element = $( element ); - this.uuid = uuid++; + this.uuid = widget_uuid++; this.eventNamespace = "." + this.widgetName + this.uuid; this.options = $.widget.extend( {}, this.options, @@ -276,9 +301,6 @@ $.Widget.prototype = { // all event bindings should go through this._on() this.element .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 @@ -324,12 +346,12 @@ $.Widget.prototype = { curOption = curOption[ parts[ i ] ]; } key = parts.pop(); - if ( value === undefined ) { + if ( arguments.length === 1 ) { return curOption[ key ] === undefined ? null : curOption[ key ]; } curOption[ key ] = value; } else { - if ( value === undefined ) { + if ( arguments.length === 1 ) { return this.options[ key ] === undefined ? null : this.options[ key ]; } options[ key ] = value; @@ -354,20 +376,23 @@ $.Widget.prototype = { if ( key === "disabled" ) { this.widget() - .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value ) - .attr( "aria-disabled", value ); - this.hoverable.removeClass( "ui-state-hover" ); - this.focusable.removeClass( "ui-state-focus" ); + .toggleClass( this.widgetFullName + "-disabled", !!value ); + + // If the widget is becoming disabled, then nothing is interactive + if ( value ) { + this.hoverable.removeClass( "ui-state-hover" ); + this.focusable.removeClass( "ui-state-focus" ); + } } return this; }, enable: function() { - return this._setOption( "disabled", false ); + return this._setOptions({ disabled: false }); }, disable: function() { - return this._setOption( "disabled", true ); + return this._setOptions({ disabled: true }); }, _on: function( suppressDisabledCheck, element, handlers ) { @@ -387,7 +412,6 @@ $.Widget.prototype = { element = this.element; delegateElement = this.widget(); } else { - // accept selectors, DOM elements element = delegateElement = $( element ); this.bindings = this.bindings.add( element ); } @@ -412,7 +436,7 @@ $.Widget.prototype = { handler.guid || handlerProxy.guid || $.guid++; } - var match = event.match( /^(\w+)\s*(.*)$/ ), + var match = event.match( /^([\w:-]*)\s*(.*)$/ ), eventName = match[1] + instance.eventNamespace, selector = match[2]; if ( selector ) { @@ -527,4 +551,8 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) { }; }); +var widget = $.widget; + + + })); diff --git a/app/assets/javascripts/jquery-fileupload/vendor/load-image.all.min.js b/app/assets/javascripts/jquery-fileupload/vendor/load-image.all.min.js new file mode 100755 index 0000000..befe38a --- /dev/null +++ b/app/assets/javascripts/jquery-fileupload/vendor/load-image.all.min.js @@ -0,0 +1 @@ +!function(a){"use strict";var b=function(a,c,d){var e,f,g=document.createElement("img");if(g.onerror=c,g.onload=function(){!f||d&&d.noRevoke||b.revokeObjectURL(f),c&&c(b.scale(g,d))},b.isInstanceOf("Blob",a)||b.isInstanceOf("File",a))e=f=b.createObjectURL(a),g._type=a.type;else{if("string"!=typeof a)return!1;e=a,d&&d.crossOrigin&&(g.crossOrigin=d.crossOrigin)}return e?(g.src=e,g):b.readFile(a,function(a){var b=a.target;b&&b.result?g.src=b.result:c&&c(a)})},c=window.createObjectURL&&window||window.URL&&URL.revokeObjectURL&&URL||window.webkitURL&&webkitURL;b.isInstanceOf=function(a,b){return Object.prototype.toString.call(b)==="[object "+a+"]"},b.transformCoordinates=function(){},b.getTransformedOptions=function(a,b){var c,d,e,f,g=b.aspectRatio;if(!g)return b;c={};for(d in b)b.hasOwnProperty(d)&&(c[d]=b[d]);return c.crop=!0,e=a.naturalWidth||a.width,f=a.naturalHeight||a.height,e/f>g?(c.maxWidth=f*g,c.maxHeight=f):(c.maxWidth=e,c.maxHeight=e/g),c},b.renderImageToCanvas=function(a,b,c,d,e,f,g,h,i,j){return a.getContext("2d").drawImage(b,c,d,e,f,g,h,i,j),a},b.hasCanvasOption=function(a){return a.canvas||a.crop||a.aspectRatio},b.scale=function(a,c){c=c||{};var d,e,f,g,h,i,j,k,l,m=document.createElement("canvas"),n=a.getContext||b.hasCanvasOption(c)&&m.getContext,o=a.naturalWidth||a.width,p=a.naturalHeight||a.height,q=o,r=p,s=function(){var a=Math.max((f||q)/q,(g||r)/r);a>1&&(q*=a,r*=a)},t=function(){var a=Math.min((d||q)/q,(e||r)/r);1>a&&(q*=a,r*=a)};return n&&(c=b.getTransformedOptions(a,c),j=c.left||0,k=c.top||0,c.sourceWidth?(h=c.sourceWidth,void 0!==c.right&&void 0===c.left&&(j=o-h-c.right)):h=o-j-(c.right||0),c.sourceHeight?(i=c.sourceHeight,void 0!==c.bottom&&void 0===c.top&&(k=p-i-c.bottom)):i=p-k-(c.bottom||0),q=h,r=i),d=c.maxWidth,e=c.maxHeight,f=c.minWidth,g=c.minHeight,n&&d&&e&&c.crop?(q=d,r=e,l=h/i-d/e,0>l?(i=e*h/d,void 0===c.top&&void 0===c.bottom&&(k=(p-i)/2)):l>0&&(h=d*i/e,void 0===c.left&&void 0===c.right&&(j=(o-h)/2))):((c.contain||c.cover)&&(f=d=d||f,g=e=e||g),c.cover?(t(),s()):(s(),t())),n?(m.width=q,m.height=r,b.transformCoordinates(m,c),b.renderImageToCanvas(m,a,j,k,h,i,0,0,q,r)):(a.width=q,a.height=r,a)},b.createObjectURL=function(a){return c?c.createObjectURL(a):!1},b.revokeObjectURL=function(a){return c?c.revokeObjectURL(a):!1},b.readFile=function(a,b,c){if(window.FileReader){var d=new FileReader;if(d.onload=d.onerror=b,c=c||"readAsDataURL",d[c])return d[c](a),d}return!1},"function"==typeof define&&define.amd?define(function(){return b}):a.loadImage=b}(this),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";if(window.navigator&&window.navigator.platform&&/iP(hone|od|ad)/.test(window.navigator.platform)){var b=a.renderImageToCanvas;a.detectSubsampling=function(a){var b,c;return a.width*a.height>1048576?(b=document.createElement("canvas"),b.width=b.height=1,c=b.getContext("2d"),c.drawImage(a,-a.width+1,0),0===c.getImageData(0,0,1,1).data[3]):!1},a.detectVerticalSquash=function(a,b){var c,d,e,f,g,h=a.naturalHeight||a.height,i=document.createElement("canvas"),j=i.getContext("2d");for(b&&(h/=2),i.width=1,i.height=h,j.drawImage(a,0,0),c=j.getImageData(0,0,1,h).data,d=0,e=h,f=h;f>d;)g=c[4*(f-1)+3],0===g?e=f:d=f,f=e+d>>1;return f/h||1},a.renderImageToCanvas=function(c,d,e,f,g,h,i,j,k,l){if("image/jpeg"===d._type){var m,n,o,p,q=c.getContext("2d"),r=document.createElement("canvas"),s=1024,t=r.getContext("2d");if(r.width=s,r.height=s,q.save(),m=a.detectSubsampling(d),m&&(e/=2,f/=2,g/=2,h/=2),n=a.detectVerticalSquash(d,m),m||1!==n){for(f*=n,k=Math.ceil(s*k/g),l=Math.ceil(s*l/h/n),j=0,p=0;h>p;){for(i=0,o=0;g>o;)t.clearRect(0,0,s,s),t.drawImage(d,e,f,g,h,-o,-p,g,h),q.drawImage(r,0,0,s,s,i,j,k,l),o+=s,i+=k;p+=s,j+=l}return q.restore(),c}}return b(c,d,e,f,g,h,i,j,k,l)}}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=a.hasCanvasOption,c=a.transformCoordinates,d=a.getTransformedOptions;a.hasCanvasOption=function(c){return b.call(a,c)||c.orientation},a.transformCoordinates=function(b,d){c.call(a,b,d);var e=b.getContext("2d"),f=b.width,g=b.height,h=d.orientation;if(h&&!(h>8))switch(h>4&&(b.width=g,b.height=f),h){case 2:e.translate(f,0),e.scale(-1,1);break;case 3:e.translate(f,g),e.rotate(Math.PI);break;case 4:e.translate(0,g),e.scale(1,-1);break;case 5:e.rotate(.5*Math.PI),e.scale(1,-1);break;case 6:e.rotate(.5*Math.PI),e.translate(0,-g);break;case 7:e.rotate(.5*Math.PI),e.translate(f,-g),e.scale(-1,1);break;case 8:e.rotate(-.5*Math.PI),e.translate(-f,0)}},a.getTransformedOptions=function(b,c){var e,f,g=d.call(a,b,c),h=g.orientation;if(!h||h>8||1===h)return g;e={};for(f in g)g.hasOwnProperty(f)&&(e[f]=g[f]);switch(g.orientation){case 2:e.left=g.right,e.right=g.left;break;case 3:e.left=g.right,e.top=g.bottom,e.right=g.left,e.bottom=g.top;break;case 4:e.top=g.bottom,e.bottom=g.top;break;case 5:e.left=g.top,e.top=g.left,e.right=g.bottom,e.bottom=g.right;break;case 6:e.left=g.top,e.top=g.right,e.right=g.bottom,e.bottom=g.left;break;case 7:e.left=g.bottom,e.top=g.right,e.right=g.top,e.bottom=g.left;break;case 8:e.left=g.bottom,e.top=g.left,e.right=g.top,e.bottom=g.right}return g.orientation>4&&(e.maxWidth=g.maxHeight,e.maxHeight=g.maxWidth,e.minWidth=g.minHeight,e.minHeight=g.minWidth,e.sourceWidth=g.sourceHeight,e.sourceHeight=g.sourceWidth),e}}),function(a){"use strict";"function"==typeof define&&define.amd?define(["load-image"],a):a(window.loadImage)}(function(a){"use strict";var b=window.Blob&&(Blob.prototype.slice||Blob.prototype.webkitSlice||Blob.prototype.mozSlice);a.blobSlice=b&&function(){var a=this.slice||this.webkitSlice||this.mozSlice;return a.apply(this,arguments)},a.metaDataParsers={jpeg:{65505:[]}},a.parseMetaData=function(b,c,d){d=d||{};var e=this,f=d.maxMetaDataSize||262144,g={},h=!(window.DataView&&b&&b.size>=12&&"image/jpeg"===b.type&&a.blobSlice);(h||!a.readFile(a.blobSlice.call(b,0,f),function(b){if(b.target.error)return console.log(b.target.error),void c(g);var f,h,i,j,k=b.target.result,l=new DataView(k),m=2,n=l.byteLength-4,o=m;if(65496===l.getUint16(0)){for(;n>m&&(f=l.getUint16(m),f>=65504&&65519>=f||65534===f);){if(h=l.getUint16(m+2)+2,m+h>l.byteLength){console.log("Invalid meta data: Invalid segment size.");break}if(i=a.metaDataParsers.jpeg[f])for(j=0;j