diff --git a/composer.json b/composer.json
index a72256410e23f23a285fbcb7cd0254ee50032178..8cd023b4dd0c5c2ff3fe32c9a654fb316c619218 100644
--- a/composer.json
+++ b/composer.json
@@ -1,3 +1,4 @@
+
 {
     "name": "irstea/file-upload-bundle",
     "description": "Bundle de gestion de fichiers intégrée à Symfony et Twitter-Bootstrap.",
@@ -149,6 +150,7 @@
     ],
     "test:install-assets": [
       "@test:clear",
+      "@test:install-theme-assets",
       "cd tests/Fixtures && npm install && npm run dev #"
     ],
         "test:install-theme-assets": [
diff --git a/src/Resources/assets/js/file_upload.js b/src/Resources/assets/js/file_upload.js
deleted file mode 100644
index 42c95ab1353c74ae4caa0618a8300b11be5f0b24..0000000000000000000000000000000000000000
--- a/src/Resources/assets/js/file_upload.js
+++ /dev/null
@@ -1,223 +0,0 @@
-const Translator = require('translator');
-
-/*
- * Copyright (C) 2015 IRSTEA
- * All rights reserved.
- */
-(function ($, Translator) {
-
-  var formatFileSize = function (size) {
-      var unit;
-      if (size > 1000000000) {
-        size = (size / 1000000000).toFixed(2);
-        unit = 'Gi';
-      } else if (size > 1000000) {
-        size = (size / 1000000).toFixed(2);
-        unit = 'Mi';
-      } else if (size > 1000) {
-        size = (size / 1000).toFixed(2);
-        unit = 'Ki';
-      } else {
-        unit = '';
-      }
-
-      return Translator.trans('file_size(%size%,%unit%)', {size: size, unit: unit}, 'file_upload');
-    },
-    formatBitrate = function (rate) {
-      return formatFileSize(rate) + '/s';
-    };
-
-  $.blueimp.fileupload.prototype._formatFileSize = formatFileSize;
-  $.blueimp.fileupload.prototype._formatBitrate = formatBitrate;
-
-  /** Plugin irsteaFileUpload.
-   */
-  $.fn.irsteaFileUpload = function (options) {
-
-    var $this = $(this),
-      $button = $this.find('.fileinput-button'),
-      input = $this.find(':file')[0],
-      $entries = $this.find('.fileinput-entries'),
-      nextIndex = $entries.find('.fileinput-entry').length,
-      createUrl = options.createUrl,
-      uploadPrototype = options.uploadPrototype,
-      downloadPrototype = options.downloadPrototype,
-      csrfQuery = '?token=' + options.csrfToken;
-
-    delete options.createUrl;
-    delete options.uploadPrototype;
-    delete options.downloadPrototype;
-    delete options.csrfToken;
-
-    if (options.acceptFileTypes) {
-      options.acceptFileTypes = new RegExp('^' + options.acceptFileTypes + '$');
-    }
-
-    var checkValidity = function () {
-        var counts = {
-            upload: $entries.find('.template-upload').length,
-            download: $entries.find('.template-download').length,
-            error: $entries.find('.alert-danger').length,
-          },
-          error = '';
-
-        if (counts.upload > 0) {
-          error = Translator.trans('file_upload.runningUpload', null, 'validators');
-
-        } else if (counts.error > 0) {
-          error = Translator.trans('file_upload.uploadError', null, 'validators');
-
-        } else if (options.required && counts.download < 1) {
-          error = Translator.trans('file_upload.required', null, 'validators');
-        }
-
-        input.setCustomValidity(error);
-
-        // console.debug(input, counts, input.validationMessage, input.validity);
-      },
-      showError = function (entry, message) {
-        var $this = $(entry);
-        if (message) {
-          $this.find('.error').text(Translator.trans(message));
-        }
-        $this.addClass('alert alert-danger');
-        $this.find('.error').show();
-        $this.find('.icon')
-          .removeClass('fa-circle-o-notch fa-spin fa-file-o')
-          .addClass('fa-exclamation-triangle');
-        $this.find('.description').remove();
-        $this.find('.id').remove();
-      },
-      updateDisplay = function (event) {
-        var hasEntry = false;
-        $entries.find('.fileinput-entry').each(function () {
-          var data = $(this).data('data');
-          if (data && data.files.error) {
-            showError(this, data.files[0].error);
-          }
-          hasEntry = true;
-        });
-        $button.toggle(options.multiple || !hasEntry);
-        checkValidity();
-      };
-
-    // Activation
-    $button.fileupload($.extend(
-      options,
-      {
-        type: 'PUT',
-        autoUpload: true,
-        formData: {},
-        multipart: false,
-        uploadTemplateId: null,
-        downloadTemplateId: null,
-        filesContainer: $this.find('.fileinput-entries'),
-        dropZone: $this,
-        i18n: function(key, values, domain) {
-          var res = Translator.trans(key, values, domain || 'file_upload');
-          if (res !== key) {
-            return res;
-          }
-          res = Translator.trans('file_upload.'+ key, values, 'validators');
-          if (res !== 'file_upload.'+ key) {
-            return res;
-          }
-          return Translator.trans(key, values, 'messages');
-        },
-        uploadTemplate: function (data) {
-          var rows = $();
-          $.each(data.files, function (index, file) {
-            var row = $(uploadPrototype);
-            rows = rows.add(row);
-            row.find('.name').text(file.name);
-            if (file.error) {
-              showError(row, file.error);
-              return;
-            }
-            row.find('.size').text(formatFileSize(file.size));
-          });
-          return rows;
-        },
-        downloadTemplate: function (data) {
-          var rows = $();
-          $.each(data.files, function (index, file) {
-            var row = $(downloadPrototype.replace(/__index__/g, nextIndex++));
-            rows = rows.add(row);
-            if (file.error) {
-              row.find('.name').text(file.name);
-              showError(row, file.error);
-              return;
-            }
-            row.find('.repr').html(file.repr);
-            row.find('.delete')
-              .attr('data-type', file.delete_type)
-              .attr('data-url', file.delete_url + csrfQuery);
-            row.find('input.id')
-              .val(file.id);
-          });
-          return rows;
-        },
-        submit: function (e, data) {
-          var $this = $(this),
-            file = data.files[0];
-          $.post(
-            createUrl,
-            {file: {name: file.name, size: file.size, type: file.type, lastModified: file.lastModified}},
-            function (response) {
-              file.icon = response.icon;
-              data.url = response.put_url + csrfQuery;
-              data.delete_url = response.delete_url;
-              data.delete_type = response.delete_type;
-              data.jqXHR = $this.fileupload('send', data);
-            }
-          )
-            .fail(function (jqXHR, textStatus, errorThrown) {
-              file.error = textStatus === "error" ? errorThrown : ('Error #' + jqXHR.status);
-              data.files.error = true;
-              showError(data.context, file.error);
-            })
-            .always(updateDisplay);
-          return false;
-        },
-        progress: function (e, data) {
-          if (!data.context || e.isDefaultPrevented()) {
-            return;
-          }
-          var percent = data.loaded / data.total * 100,
-            percentText = percent.toFixed(1);
-          data.context.each(function () {
-            var $this = $(data.context);
-            $this.find('.progress').show();
-            $this.find('.progress-bar').css('width', percent + '%').attr('aria-valuenow', percentText);
-            $this.find('.progress-text').show().html(percentText + '% (' + formatBitrate(data.bitrate) + ')');
-          });
-        },
-        getFilesFromResponse: function (data) {
-          var files = [];
-          $.each(data.files, function (index, file) {
-            files.push($.extend(file, data.result.files[index]));
-          });
-          return files;
-        },
-      }
-    )).bind({
-      fileuploadfailed: function (event, data) {
-        if (data.delete_url) {
-          $.ajax(data.delete_url + csrfQuery, {type: data.delete_type});
-          checkValidity();
-        }
-      },
-      fileuploadadded: updateDisplay,
-      fileuploadfinished: updateDisplay,
-      fileuploaddestroyed: updateDisplay,
-      fileuploadprocessalways: updateDisplay
-    });
-
-    if (options.disabled || options.readonly) {
-      $button.fileupload('disable');
-    }
-
-    updateDisplay();
-  };
-
-})(jQuery, Translator);
diff --git a/src/Resources/assets/js/jquery.file_upload.js b/src/Resources/assets/js/jquery.file_upload.js
deleted file mode 100644
index 146de7fba0059e0a78600c2ffa836d2ccbae6d1e..0000000000000000000000000000000000000000
--- a/src/Resources/assets/js/jquery.file_upload.js
+++ /dev/null
@@ -1,1535 +0,0 @@
-/*
- * jQuery File Upload Plugin
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
-
-/* jshint nomen:false */
-/* global define, require, window, document, location, Blob, FormData */
-
-;(function (factory) {
-  'use strict';
-  if (typeof define === 'function' && define.amd) {
-    // Register as an anonymous AMD module:
-    define([
-      'jquery',
-      'jquery-ui/ui/widget'
-    ], factory);
-  } else if (typeof exports === 'object') {
-    // Node/CommonJS:
-    factory(
-      require('jquery'),
-      require('./vendor/jquery.ui.widget')
-    );
-  } else {
-    // Browser globals:
-    factory(window.jQuery);
-  }
-}(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:
-    $('<input type="file"/>').prop('disabled'));
-
-  // The FileReader API is not actually used, but works as feature detection,
-  // 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
-  // exposes the "add" and "send" methods, to add or directly send files using
-  // the fileupload API.
-  // By default, files added via file input selection, paste, drag & drop or
-  // "add" method are uploaded immediately, but it is possible to override
-  // the "add" callback option to queue file uploads.
-  $.widget('blueimp.fileupload', {
-
-    options: {
-      // 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 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.
-      // 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
-      // queues and allows change events to be fired for the same file
-      // selection, but can be disabled by setting the following option to false:
-      replaceFileInput: true,
-      // The parameter name for the file form data (the request argument name).
-      // If undefined or empty, the name property of the file input field is
-      // used, or "files[]" if the file input name property is also empty,
-      // can be a string or an array of strings:
-      paramName: undefined,
-      // By default, each file of a selection is uploaded using an individual
-      // request for XHR type uploads. Set to false to upload file
-      // selections in one request each:
-      singleFileUploads: true,
-      // 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,
-      // To limit the number of concurrent uploads,
-      // set the following option to an integer greater than 0:
-      limitConcurrentUploads: undefined,
-      // Set the following option to true to force iframe transport uploads:
-      forceIframeTransport: false,
-      // Set the following option to the location of a redirect url on the
-      // origin server, for cross-domain iframe transport uploads:
-      redirect: undefined,
-      // The parameter name for the redirect url, sent as part of the form
-      // data and set to 'redirect' if this option is empty:
-      redirectParamName: undefined,
-      // Set the following option to the location of a postMessage window,
-      // to enable postMessage transport uploads:
-      postMessage: undefined,
-      // By default, XHR file uploads are sent as multipart/form-data.
-      // The iframe transport is always using multipart/form-data.
-      // Set to false to enable non-multipart XHR uploads:
-      multipart: true,
-      // To upload large files in smaller chunks, set the following option
-      // to a preferred maximum chunk size. If set to 0, null or undefined,
-      // or the browser does not support the required Blob API, files will
-      // be uploaded as a whole.
-      maxChunkSize: undefined,
-      // When a non-multipart upload or a chunked multipart upload has been
-      // aborted, this option can be used to resume the upload by setting
-      // it to the size of the already uploaded bytes. This option is most
-      // useful when modifying the options object inside of the "add" or
-      // "send" callbacks, as the options are cloned for each file upload.
-      uploadedBytes: undefined,
-      // By default, failed (abort or error) file uploads are removed from the
-      // global progress calculation. Set the following option to false to
-      // prevent recalculating the global progress data:
-      recalculateProgress: true,
-      // Interval in milliseconds to calculate and trigger progress events:
-      progressInterval: 100,
-      // Interval in milliseconds to calculate progress bitrate:
-      bitrateInterval: 500,
-      // By default, uploads are started automatically when adding files:
-      autoUpload: true,
-      // By default, duplicate file names are expected to be handled on
-      // the server-side. If this is not possible (e.g. when uploading
-      // files directly to Amazon S3), the following option can be set to
-      // an empty object or an object mapping existing filenames, e.g.:
-      // { "image.jpg": true, "image (1).jpg": true }
-      // If it is set, all files will be uploaded with unique filenames,
-      // adding increasing number suffixes if necessary, e.g.:
-      // "image (2).jpg"
-      uniqueFilenames: undefined,
-
-      // 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
-      // object (for XHR file uploads), or a simple object.
-      // The form of the first fileInput is given as parameter to the function:
-      formData: function (form) {
-        return form.serializeArray();
-      },
-
-      // 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 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 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 () {
-            data.submit();
-          });
-        }
-      },
-
-      // 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 before the start of each chunk upload request (before form data initialization):
-      // chunkbeforesend: function (e, data) {}, // .bind('fileuploadchunkbeforesend', 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,
-      contentType: false,
-      cache: false,
-      timeout: 0
-    },
-
-    // A list of options that require reinitializing event listeners and/or
-    // special initialization code:
-    _specialOptions: [
-      'fileInput',
-      'dropZone',
-      'pasteZone',
-      'multipart',
-      '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;
-      this.bitrate = 0;
-      this.getBitrate = function (now, loaded, interval) {
-        var timeDiff = now - this.timestamp;
-        if (!this.bitrate || !interval || timeDiff > interval) {
-          this.bitrate = (loaded - this.loaded) * (1000 / timeDiff) * 8;
-          this.loaded = loaded;
-          this.timestamp = now;
-        }
-        return this.bitrate;
-      };
-    },
-
-    _isXHRUpload: function (options) {
-      return !options.forceIframeTransport &&
-        ((!options.multipart && $.support.xhrFileUpload) ||
-          $.support.xhrFormDataFileUpload);
-    },
-
-    _getFormData: function (options) {
-      var formData;
-      if ($.type(options.formData) === 'function') {
-        return options.formData(options.form);
-      }
-      if ($.isArray(options.formData)) {
-        return options.formData;
-      }
-      if ($.type(options.formData) === 'object') {
-        formData = [];
-        $.each(options.formData, function (name, value) {
-          formData.push({name: name, value: value});
-        });
-        return formData;
-      }
-      return [];
-    },
-
-    _getTotal: function (files) {
-      var total = 0;
-      $.each(files, function (index, file) {
-        total += file.size || 1;
-      });
-      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 = ((Date.now) ? Date.now() : (new Date()).getTime()),
-          loaded;
-        if (data._time && data.progressInterval &&
-          (now - data._time < data.progressInterval) &&
-          e.loaded !== e.total) {
-          return;
-        }
-        data._time = now;
-        loaded = Math.floor(
-          e.loaded / e.total * (data.chunkSize || data._progress.total)
-        ) + (data.uploadedBytes || 0);
-        // 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
-        );
-        // 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',
-          $.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',
-          $.Event('progressall', {delegatedEvent: e}),
-          this._progress
-        );
-      }
-    },
-
-    _initProgressListener: function (options) {
-      var that = this,
-        xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
-      // Accesss to the native XHR object is required to add event listeners
-      // for the upload progress event:
-      if (xhr.upload) {
-        $(xhr.upload).bind('progress', function (e) {
-          var oe = e.originalEvent;
-          // Make sure the progress event properties get copied over:
-          e.lengthComputable = oe.lengthComputable;
-          e.loaded = oe.loaded;
-          e.total = oe.total;
-          that._onProgress(e, options);
-        });
-        options.xhr = function () {
-          return xhr;
-        };
-      }
-    },
-
-    _deinitProgressListener: function (options) {
-      var xhr = options.xhr ? options.xhr() : $.ajaxSettings.xhr();
-      if (xhr.upload) {
-        $(xhr.upload).unbind('progress');
-      }
-    },
-
-    _isInstanceOf: function (type, obj) {
-      // Cross-frame instanceof check
-      return Object.prototype.toString.call(obj) === '[object ' + type + ']';
-    },
-
-    _getUniqueFilename: function (name, map) {
-      name = String(name);
-      if (map[name]) {
-        name = name.replace(
-          /(?: \(([\d]+)\))?(\.[^.]+)?$/,
-          function (_, p1, p2) {
-            var index = p1 ? Number(p1) + 1 : 1;
-            var ext = p2 || '';
-            return ' (' + index + ')' + ext;
-          }
-        );
-        return this._getUniqueFilename(name, map);
-      }
-      map[name] = true;
-      return name;
-    },
-
-    _initXHRData: function (options) {
-      var that = this,
-        formData,
-        file = options.files[0],
-        // Ignore non-multipart setting if not supported:
-        multipart = options.multipart || !$.support.xhrFileUpload,
-        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 || options.blob || !this._isInstanceOf('File', file)) {
-        options.headers['Content-Disposition'] = 'attachment; filename="' +
-          encodeURI(file.uploadName || file.name) + '"';
-      }
-      if (!multipart) {
-        options.contentType = file.type || 'application/octet-stream';
-        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
-          // the formData array and let the postMessage window
-          // create the FormData object out of this array:
-          formData = this._getFormData(options);
-          if (options.blob) {
-            formData.push({
-              name: paramName,
-              value: options.blob
-            });
-          } else {
-            $.each(options.files, function (index, file) {
-              formData.push({
-                name: ($.type(options.paramName) === 'array' &&
-                  options.paramName[index]) || paramName,
-                value: file
-              });
-            });
-          }
-        } else {
-          if (that._isInstanceOf('FormData', options.formData)) {
-            formData = options.formData;
-          } else {
-            formData = new FormData();
-            $.each(this._getFormData(options), function (index, field) {
-              formData.append(field.name, field.value);
-            });
-          }
-          if (options.blob) {
-            formData.append(
-              paramName,
-              options.blob,
-              file.uploadName || file.name
-            );
-          } else {
-            $.each(options.files, function (index, file) {
-              // This check allows the tests to run with
-              // dummy objects:
-              if (that._isInstanceOf('File', file) ||
-                that._isInstanceOf('Blob', file)) {
-                var fileName = file.uploadName || file.name;
-                if (options.uniqueFilenames) {
-                  fileName = that._getUniqueFilename(
-                    fileName,
-                    options.uniqueFilenames
-                  );
-                }
-                formData.append(
-                  ($.type(options.paramName) === 'array' &&
-                    options.paramName[index]) || paramName,
-                  file,
-                  fileName
-                );
-              }
-            });
-          }
-        }
-        options.data = formData;
-      }
-      // Blob reference is not needed anymore, free memory:
-      options.blob = null;
-    },
-
-    _initIframeSettings: function (options) {
-      var targetHost = $('<a></a>').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 && targetHost && targetHost !== location.host) {
-        options.formData.push({
-          name: options.redirectParamName || 'redirect',
-          value: options.redirect
-        });
-      }
-    },
-
-    _initDataSettings: function (options) {
-      if (this._isXHRUpload(options)) {
-        if (!this._chunkedUpload(options, true)) {
-          if (!options.data) {
-            this._initXHRData(options);
-          }
-          this._initProgressListener(options);
-        }
-        if (options.postMessage) {
-          // Setting the dataType to postmessage enables the
-          // postMessage transport:
-          options.dataType = 'postmessage ' + (options.dataType || '');
-        }
-      } else {
-        this._initIframeSettings(options);
-      }
-    },
-
-    _getParamName: function (options) {
-      var fileInput = $(options.fileInput),
-        paramName = options.paramName;
-      if (!paramName) {
-        paramName = [];
-        fileInput.each(function () {
-          var input = $(this),
-            name = input.prop('name') || 'files[]',
-            i = (input.prop('files') || [1]).length;
-          while (i) {
-            paramName.push(name);
-            i -= 1;
-          }
-        });
-        if (!paramName.length) {
-          paramName = [fileInput.prop('name') || 'files[]'];
-        }
-      } else if (!$.isArray(paramName)) {
-        paramName = [paramName];
-      }
-      return paramName;
-    },
-
-    _initFormSettings: function (options) {
-      // Retrieve missing options from the input field and the
-      // 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) {
-        options.url = options.form.prop('action') || location.href;
-      }
-      // The HTTP request method must be "POST" or "PUT":
-      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';
-      }
-      if (!options.formAcceptCharset) {
-        options.formAcceptCharset = options.form.attr('accept-charset');
-      }
-    },
-
-    _getAJAXSettings: function (data) {
-      var options = $.extend({}, this.options, data);
-      this._initFormSettings(options);
-      this._initDataSettings(options);
-      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) {
-      promise.success = promise.done;
-      promise.error = promise.fail;
-      promise.complete = promise.always;
-      return promise;
-    },
-
-    // Creates and returns a Promise object enhanced with
-    // the jqXHR methods abort, success, error and complete:
-    _getXHRPromise: function (resolveOrReject, context, args) {
-      var dfd = $.Deferred(),
-        promise = dfd.promise();
-      context = context || this.options.context || promise;
-      if (resolveOrReject === true) {
-        dfd.resolveWith(context, args);
-      } else if (resolveOrReject === false) {
-        dfd.rejectWith(context, args);
-      }
-      promise.abort = dfd.promise;
-      return this._enhancePromise(promise);
-    },
-
-    // Adds convenience methods to the data callback argument:
-    _addConvenienceMethods: function (e, data) {
-      var that = this,
-        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])).then(
-              function () {
-                if (data.errorThrown) {
-                  return $.Deferred()
-                    .rejectWith(that, [data]).promise();
-                }
-                return getPromise(arguments);
-              }
-            ).then(resolveFunc, rejectFunc);
-        }
-        return this._processQueue || getPromise([this]);
-      };
-      data.submit = function () {
-        if (this.state() !== 'pending') {
-          data.jqXHR = this.jqXHR =
-            (that._trigger(
-              'submit',
-              $.Event('submit', {delegatedEvent: e}),
-              this
-            ) !== false) && that._onSend(e, this);
-        }
-        return this.jqXHR || that._getXHRPromise();
-      };
-      data.abort = function () {
-        if (this.jqXHR) {
-          return this.jqXHR.abort();
-        }
-        this.errorThrown = 'abort';
-        that._trigger('fail', null, this);
-        return that._getXHRPromise(false);
-      };
-      data.state = function () {
-        if (this.jqXHR) {
-          return that._getDeferredState(this.jqXHR);
-        }
-        if (this._processQueue) {
-          return that._getDeferredState(this._processQueue);
-        }
-      };
-      data.processing = function () {
-        return !this.jqXHR && this._processQueue && that
-          ._getDeferredState(this._processQueue) === 'pending';
-      };
-      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) {
-      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
-    // 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,
-        mcs = options.maxChunkSize || fs,
-        slice = this._blobSlice,
-        dfd = $.Deferred(),
-        promise = dfd.promise(),
-        jqXHR,
-        upload;
-      if (!(this._isXHRUpload(options) && slice && (ub || ($.type(mcs) === 'function' ? mcs(options) : mcs) < fs)) ||
-        options.data) {
-        return false;
-      }
-      if (testOnly) {
-        return true;
-      }
-      if (ub >= fs) {
-        file.error = options.i18n('uploadedBytes');
-        return this._getXHRPromise(
-          false,
-          options.context,
-          [null, 'error', file.error]
-        );
-      }
-      // The chunk upload method:
-      upload = function () {
-        // Clone the options object for each chunk upload:
-        var o = $.extend({}, options),
-          currentLoaded = o._progress.loaded;
-        o.blob = slice.call(
-          file,
-          ub,
-          ub + ($.type(mcs) === 'function' ? mcs(o) : mcs),
-          file.type
-        );
-        // 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;
-        // Trigger chunkbeforesend to allow form data to be updated for this chunk
-        that._trigger('chunkbeforesend', null, o);
-        // Process the upload data (the blob and potential form data):
-        that._initXHRData(o);
-        // Add progress listeners for this chunk upload:
-        that._initProgressListener(o);
-        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 no final progress event
-            // with loaded equaling total has been triggered
-            // for this chunk:
-            if (currentLoaded + o.chunkSize - o._progress.loaded) {
-              that._onProgress($.Event('progress', {
-                lengthComputable: true,
-                loaded: ub - o.uploadedBytes,
-                total: ub - o.uploadedBytes
-              }), 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:
-              upload();
-            } else {
-              dfd.resolveWith(
-                o.context,
-                [result, textStatus, jqXHR]
-              );
-            }
-          })
-          .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]
-            );
-          })
-          .always(function () {
-            that._deinitProgressListener(o);
-          });
-      };
-      this._enhancePromise(promise);
-      promise.abort = function () {
-        return jqXHR.abort();
-      };
-      upload();
-      return promise;
-    },
-
-    _beforeSend: function (e, data) {
-      if (this._active === 0) {
-        // the start callback is triggered when an upload starts
-        // and no other uploads are currently running,
-        // equivalent to the global ajaxStart event:
-        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._progress.loaded += data.loaded;
-      this._progress.total += data.total;
-    },
-
-    _onDone: function (result, textStatus, jqXHR, options) {
-      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);
-      }
-      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) {
-      var response = options._response;
-      if (options.recalculateProgress) {
-        // Remove the failed (error or abort) file upload from
-        // the global progress calculation:
-        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._trigger('always', null, options);
-    },
-
-    _onSend: function (e, data) {
-      if (!data.submit) {
-        this._addConvenienceMethods(e, data);
-      }
-      var that = this,
-        jqXHR,
-        aborted,
-        slot,
-        pipe,
-        options = that._getAJAXSettings(data),
-        send = function () {
-          that._sending += 1;
-          // Set timer for bitrate progress calculation:
-          options._bitrateTimer = new that._BitrateTimer();
-          jqXHR = jqXHR || (
-            ((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) {
-            that._onDone(result, textStatus, jqXHR, options);
-          }).fail(function (jqXHR, textStatus, errorThrown) {
-            that._onFail(jqXHR, textStatus, errorThrown, options);
-          }).always(function (jqXHRorResult, textStatus, jqXHRorError) {
-            that._deinitProgressListener(options);
-            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();
-              while (nextSlot) {
-                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;
-        };
-      this._beforeSend(e, options);
-      if (this.options.sequentialUploads ||
-        (this.options.limitConcurrentUploads &&
-          this.options.limitConcurrentUploads <= this._sending)) {
-        if (this.options.limitConcurrentUploads > 1) {
-          slot = $.Deferred();
-          this._slots.push(slot);
-          pipe = slot.then(send);
-        } else {
-          this._sequence = this._sequence.then(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,
-        // and jqXHR callbacks mapped to the equivalent Promise methods:
-        pipe.abort = function () {
-          aborted = [undefined, 'abort', 'abort'];
-          if (!jqXHR) {
-            if (slot) {
-              slot.rejectWith(options.context, aborted);
-            }
-            return send();
-          }
-          return jqXHR.abort();
-        };
-        return this._enhancePromise(pipe);
-      }
-      return send();
-    },
-
-    _onAdd: function (e, data) {
-      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,
-        j = 0;
-      if (!filesLength) {
-        return false;
-      }
-      if (limitSize && files[0].size === undefined) {
-        limitSize = undefined;
-      }
-      if (!(options.singleFileUploads || limit || limitSize) ||
-        !this._isXHRUpload(options)) {
-        fileSet = [files];
-        paramNameSet = [paramName];
-      } else if (!(options.singleFileUploads || limitSize) && limit) {
-        fileSet = [];
-        paramNameSet = [];
-        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 = 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',
-          $.Event('add', {delegatedEvent: e}),
-          newData
-        );
-        return result;
-      });
-      return result;
-    },
-
-    _replaceFileInput: function (data) {
-      var input = data.fileInput,
-        inputClone = input.clone(true),
-        restoreFocus = input.is(document.activeElement);
-      // Add a reference for the new cloned file input to the data argument:
-      data.fileInputClone = inputClone;
-      $('<form></form>').append(inputClone)[0].reset();
-      // Detaching allows to insert the fileInput on another form
-      // without loosing the file input value:
-      input.after(inputClone).detach();
-      // If the fileInput had focus before it was detached,
-      // restore focus to the inputClone.
-      if (restoreFocus) {
-        inputClone.focus();
-      }
-      // Avoid memory leaks with the detached file input:
-      $.cleanData(input.unbind('remove'));
-      // Replace the original file input element in the fileInput
-      // 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]) {
-          return inputClone[0];
-        }
-        return el;
-      });
-      // If the widget has been initialized on the file input itself,
-      // override this.element with the file input clone:
-      if (input[0] === this.element[0]) {
-        this.element = inputClone;
-      }
-    },
-
-    _handleFileTreeEntry: function (entry, path) {
-      var that = this,
-        dfd = $.Deferred(),
-        entries = [],
-        dirReader,
-        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]);
-        },
-        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);
-        };
-      path = path || '';
-      if (entry.isFile) {
-        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();
-        readEntries();
-      } else {
-        // Return an empty list for file system items
-        // other than files or directories:
-        dfd.resolve([]);
-      }
-      return dfd.promise();
-    },
-
-    _handleFileTreeEntries: function (entries, path) {
-      var that = this;
-      return $.when.apply(
-        $,
-        $.map(entries, function (entry) {
-          return that._handleFileTreeEntry(entry, path);
-        })
-      ).then(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) {
-            var entry;
-            if (item.webkitGetAsEntry) {
-              entry = item.webkitGetAsEntry();
-              if (entry) {
-                // Workaround for Chrome bug #149735:
-                entry._file = item.getAsFile();
-              }
-              return entry;
-            }
-            return item.getAsEntry();
-          })
-        );
-      }
-      return $.Deferred().resolve(
-        $.makeArray(dataTransfer.files)
-      ).promise();
-    },
-
-    _getSingleFileInputFiles: function (fileInput) {
-      fileInput = $(fileInput);
-      var entries = fileInput.prop('webkitEntries') ||
-        fileInput.prop('entries'),
-        files,
-        value;
-      if (entries && entries.length) {
-        return this._handleFileTreeEntries(entries);
-      }
-      files = $.makeArray(fileInput.prop('files'));
-      if (!files.length) {
-        value = fileInput.prop('value');
-        if (!value) {
-          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)
-      ).then(function () {
-        return Array.prototype.concat.apply(
-          [],
-          arguments
-        );
-      });
-    },
-
-    _onChange: function (e) {
-      var that = this,
-        data = {
-          fileInput: $(e.target),
-          form: $(e.target.form)
-        };
-      this._getFileInputFiles(data.fileInput).always(function (files) {
-        data.files = files;
-        if (that.options.replaceFileInput) {
-          that._replaceFileInput(data);
-        }
-        if (that._trigger(
-          'change',
-          $.Event('change', {delegatedEvent: e}),
-          data
-        ) !== false) {
-          that._onAdd(e, data);
-        }
-      });
-    },
-
-    _onPaste: function (e) {
-      var items = e.originalEvent && e.originalEvent.clipboardData &&
-        e.originalEvent.clipboardData.items,
-        data = {files: []};
-      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',
-          $.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,
-        data = {};
-      if (dataTransfer && dataTransfer.files && dataTransfer.files.length) {
-        e.preventDefault();
-        this._getDroppedFiles(dataTransfer).always(function (files) {
-          data.files = files;
-          if (that._trigger(
-            'drop',
-            $.Event('drop', {delegatedEvent: e}),
-            data
-          ) !== false) {
-            that._onAdd(e, data);
-          }
-        });
-      }
-    },
-
-    _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,
-          // 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
-        });
-      }
-      if ($.support.fileInput) {
-        this._on(this.options.fileInput, {
-          change: this._onChange
-        });
-      }
-    },
-
-    _destroyEventHandlers: function () {
-      this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
-      this._off(this.options.pasteZone, 'paste');
-      this._off(this.options.fileInput, 'change');
-    },
-
-    _destroy: function () {
-      this._destroyEventHandlers();
-    },
-
-    _setOption: function (key, value) {
-      var reinit = $.inArray(key, this._specialOptions) !== -1;
-      if (reinit) {
-        this._destroyEventHandlers();
-      }
-      this._super(key, value);
-      if (reinit) {
-        this._initSpecialOptions();
-        this._initEventHandlers();
-      }
-    },
-
-    _initSpecialOptions: function () {
-      var options = this.options;
-      if (options.fileInput === undefined) {
-        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);
-      }
-      if (!(options.dropZone instanceof $)) {
-        options.dropZone = $(options.dropZone);
-      }
-      if (!(options.pasteZone instanceof $)) {
-        options.pasteZone = $(options.pasteZone);
-      }
-    },
-
-    _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,
-        data = this.element.data();
-      // Initialize options set via HTML5 data-attributes:
-      $.each(
-        this.element[0].attributes,
-        function (index, attr) {
-          var key = attr.name.toLowerCase(),
-            value;
-          if (/^data-/.test(key)) {
-            // Convert hyphen-ated key to camelCase:
-            key = key.slice(5).replace(/-[a-z]/g, function (str) {
-              return str.charAt(1).toUpperCase();
-            });
-            value = data[key];
-            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);
-      this._sending = this._active = 0;
-      this._initProgressObject(this);
-      this._initEventHandlers();
-    },
-
-    // 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
-    // using the fileupload API. The data parameter accepts an object which
-    // 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) {
-        this._getFileInputFiles(data.fileInput).always(function (files) {
-          data.files = files;
-          that._onAdd(null, data);
-        });
-      } else {
-        data.files = $.makeArray(data.files);
-        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 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) {
-          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;
-              }
-              if (!files.length) {
-                dfd.reject();
-                return;
-              }
-              data.files = files;
-              jqXHR = that._onSend(null, data);
-              jqXHR.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);
-        }
-      }
-      return this._getXHRPromise(false, data && data.context);
-    }
-
-  });
-
-}));
diff --git a/src/Resources/assets/js/jquery.fileupload-process.js b/src/Resources/assets/js/jquery.fileupload-process.js
deleted file mode 100644
index 7d25dbe18839ee55fa4671e10750e4c0670eb5e8..0000000000000000000000000000000000000000
--- a/src/Resources/assets/js/jquery.fileupload-process.js
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * jQuery File Upload Processing Plugin
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2012, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
-
-/* jshint nomen:false */
-/* global define, require, window */
-
-;(function (factory) {
-  'use strict';
-  if (typeof define === 'function' && define.amd) {
-    // Register as an anonymous AMD module:
-    define([
-      'jquery',
-      './jquery.fileupload'
-    ], factory);
-  } else if (typeof exports === 'object') {
-    // Node/CommonJS:
-    factory(
-      require('jquery'),
-      require('./jquery.fileupload')
-    );
-  } 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, 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,
-            settings
-          );
-        };
-        chain = chain.then(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 = {},
-          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) || (prefix ? prefix +
-              key.charAt(0).toUpperCase() + key.slice(1) : key)
-              ];
-          } 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) {
-          var opts = index ? $.extend({}, options) : options,
-            func = function () {
-              if (data.errorThrown) {
-                return $.Deferred()
-                  .rejectWith(that, [data]).promise();
-              }
-              return that._processFile(opts, data);
-            };
-          opts.index = index;
-          that._processing += 1;
-          that._processingQueue = that._processingQueue.then(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/src/Resources/assets/js/jquery.fileupload-ui.js b/src/Resources/assets/js/jquery.fileupload-ui.js
deleted file mode 100644
index a9577cdf0cecaf2f4f5c18cd8face705a62024b7..0000000000000000000000000000000000000000
--- a/src/Resources/assets/js/jquery.fileupload-ui.js
+++ /dev/null
@@ -1,714 +0,0 @@
-/*
- * jQuery File Upload User Interface Plugin
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2010, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
-
-/* jshint nomen:false */
-/* global define, require, window */
-
-;(function (factory) {
-  'use strict';
-  if (typeof define === 'function' && define.amd) {
-    // Register as an anonymous AMD module:
-    define([
-      'jquery',
-      'blueimp-tmpl',
-      './jquery.fileupload-image',
-      './jquery.fileupload-audio',
-      './jquery.fileupload-video',
-      './jquery.fileupload-validate'
-    ], factory);
-  } else if (typeof exports === 'object') {
-    // Node/CommonJS:
-    factory(
-      require('jquery'),
-      require('blueimp-tmpl'),
-      require('./jquery.fileupload-image'),
-      require('./jquery.fileupload-audio'),
-      require('./jquery.fileupload-video'),
-      require('./jquery.fileupload-validate')
-    );
-  } else {
-    // Browser globals:
-    factory(
-      window.jQuery,
-      window.tmpl
-    );
-  }
-}(function ($, tmpl) {
-  '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, {
-
-    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 ID of the upload template:
-      uploadTemplateId: 'template-upload',
-      // The ID of the download template:
-      downloadTemplateId: 'template-download',
-      // The container for the list of files. If undefined, it is set to
-      // an element with class "files" inside of the widget element:
-      filesContainer: undefined,
-      // By default, files are appended to the files container.
-      // Set the following option to true, to prepend files instead:
-      prependFiles: false,
-      // The expected data type of the upload response, sets the dataType
-      // 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()
-          .not('.processing').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) {
-        if (e.isDefaultPrevented()) {
-          return false;
-        }
-        var $this = $(this),
-          that = $this.data('blueimp-fileupload') ||
-            $this.data('fileupload'),
-          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.each(function (index) {
-            $(this).find('.size').text(
-              that._formatFileSize(data.files[index].size)
-            );
-          }).removeClass('processing');
-          that._renderPreviews(data);
-        }).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 &&
-          data.dataType.substr(0, 6) === 'iframe') {
-          // Iframe Transport does not support progress events.
-          // In lack of an indeterminate progress bar, we set
-          // the progress to 100%, showing the full animated bar:
-          data.context
-            .find('.progress').addClass(
-            !$.support.transition && 'progress-animated'
-          )
-            .attr('aria-valuenow', 100)
-            .children().first().css(
-            'width',
-            '100%'
-          );
-        }
-        return that._trigger('sent', e, data);
-      },
-      // 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 ||
-            that.options.getFilesFromResponse,
-          files = getFilesFromResponse(data),
-          template,
-          deferred;
-        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();
-                  }
-                );
-              }
-            );
-          });
-        } else {
-          template = that._renderDownload(files)[
-            that.options.prependFiles ? 'prependTo' : '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) {
-        if (e.isDefaultPrevented()) {
-          return false;
-        }
-        var that = $(this).data('blueimp-fileupload') ||
-          $(this).data('fileupload'),
-          template,
-          deferred;
-        if (data.context) {
-          data.context.each(function (index) {
-            if (data.errorThrown !== 'abort') {
-              var file = data.files[index];
-              file.error = file.error || data.errorThrown ||
-                data.i18n('unknownError');
-              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('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') {
-          data.context = that._renderUpload(data.files)[
-            that.options.prependFiles ? 'prependTo' : '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 (e.isDefaultPrevented()) {
-          return false;
-        }
-        var progress = Math.floor(data.loaded / data.total * 100);
-        if (data.context) {
-          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'),
-          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)
-          .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();
-        that._transition($(this).find('.fileupload-progress')).done(
-          function () {
-            that._trigger('started', e);
-          }
-        );
-      },
-      // 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();
-        $.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')
-              .children().first().css('width', '0%');
-            $(this).find('.progress-extended').html('&nbsp;');
-            deferred.resolve();
-          }
-        );
-      },
-      processstart: function (e) {
-        if (e.isDefaultPrevented()) {
-          return false;
-        }
-        $(this).addClass('fileupload-processing');
-      },
-      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'),
-          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();
-        }
-      }
-    },
-
-    _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 () {
-      var link = $(this),
-        url = link.prop('href'),
-        name = link.prop('download'),
-        type = 'application/octet-stream';
-      link.bind('dragstart', function (e) {
-        try {
-          e.originalEvent.dataTransfer.setData(
-            'DownloadURL',
-            [type, name, url].join(':')
-          );
-        } catch (ignore) {}
-      });
-    },
-
-    _formatFileSize: function (bytes) {
-      if (typeof bytes !== 'number') {
-        return '';
-      }
-      if (bytes >= 1000000000) {
-        return (bytes / 1000000000).toFixed(2) + ' GB';
-      }
-      if (bytes >= 1000000) {
-        return (bytes / 1000000).toFixed(2) + ' MB';
-      }
-      return (bytes / 1000).toFixed(2) + ' KB';
-    },
-
-    _formatBitrate: function (bits) {
-      if (typeof bits !== 'number') {
-        return '';
-      }
-      if (bits >= 1000000000) {
-        return (bits / 1000000000).toFixed(2) + ' Gbit/s';
-      }
-      if (bits >= 1000000) {
-        return (bits / 1000000).toFixed(2) + ' Mbit/s';
-      }
-      if (bits >= 1000) {
-        return (bits / 1000).toFixed(2) + ' kbit/s';
-      }
-      return bits.toFixed(2) + ' bit/s';
-    },
-
-    _formatTime: function (seconds) {
-      var date = new Date(seconds * 1000),
-        days = Math.floor(seconds / 86400);
-      days = days ? days + 'd ' : '';
-      return days +
-        ('0' + date.getUTCHours()).slice(-2) + ':' +
-        ('0' + date.getUTCMinutes()).slice(-2) + ':' +
-        ('0' + date.getUTCSeconds()).slice(-2);
-    },
-
-    _formatPercentage: function (floatValue) {
-      return (floatValue * 100).toFixed(2) + ' %';
-    },
-
-    _renderExtendedProgress: function (data) {
-      return this._formatBitrate(data.bitrate) + ' | ' +
-        this._formatTime(
-          (data.total - data.loaded) * 8 / data.bitrate
-        ) + ' | ' +
-        this._formatPercentage(
-          data.loaded / data.total
-        ) + ' | ' +
-        this._formatFileSize(data.loaded) + ' / ' +
-        this._formatFileSize(data.total);
-    },
-
-    _renderTemplate: function (func, files) {
-      if (!func) {
-        return $();
-      }
-      var result = func({
-        files: files,
-        formatFileSize: this._formatFileSize,
-        options: this.options
-      });
-      if (result instanceof $) {
-        return result;
-      }
-      return $(this.options.templatesContainer).html(result).children();
-    },
-
-    _renderPreviews: function (data) {
-      data.context.find('.preview').each(function (index, elm) {
-        $(elm).append(data.files[index].preview);
-      });
-    },
-
-    _renderUpload: function (files) {
-      return this._renderTemplate(
-        this.options.uploadTemplate,
-        files
-      );
-    },
-
-    _renderDownload: function (files) {
-      return this._renderTemplate(
-        this.options.downloadTemplate,
-        files
-      ).find('a[download]').each(this._enableDragToDesktop).end();
-    },
-
-    _startHandler: function (e) {
-      e.preventDefault();
-      var button = $(e.currentTarget),
-        template = button.closest('.template-upload'),
-        data = template.data('data');
-      button.prop('disabled', true);
-      if (data && data.submit) {
-        data.submit();
-      }
-    },
-
-    _cancelHandler: function (e) {
-      e.preventDefault();
-      var template = $(e.currentTarget)
-          .closest('.template-upload,.template-download'),
-        data = template.data('data') || {};
-      data.context = data.context || template;
-      if (data.abort) {
-        data.abort();
-      } else {
-        data.errorThrown = 'abort';
-        this._trigger('fail', e, data);
-      }
-    },
-
-    _deleteHandler: function (e) {
-      e.preventDefault();
-      var button = $(e.currentTarget);
-      this._trigger('destroy', e, $.extend({
-        context: button.closest('.template-download'),
-        type: 'DELETE'
-      }, button.data()));
-    },
-
-    _forceReflow: function (node) {
-      return $.support.transition && node.length &&
-        node[0].offsetWidth;
-    },
-
-    _transition: function (node) {
-      var dfd = $.Deferred();
-      if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
-        node.bind(
-          $.support.transition.end,
-          function (e) {
-            // Make sure we don't respond to other transitions events
-            // in the container element, e.g. from button elements:
-            if (e.target === node[0]) {
-              node.unbind($.support.transition.end);
-              dfd.resolveWith(node);
-            }
-          }
-        ).toggleClass('in');
-      } else {
-        node.toggleClass('in');
-        dfd.resolveWith(node);
-      }
-      return dfd;
-    },
-
-    _initButtonBarEventHandlers: function () {
-      var fileUploadButtonBar = this.element.find('.fileupload-buttonbar'),
-        filesList = this.options.filesContainer;
-      this._on(fileUploadButtonBar.find('.start'), {
-        click: function (e) {
-          e.preventDefault();
-          filesList.find('.start').click();
-        }
-      });
-      this._on(fileUploadButtonBar.find('.cancel'), {
-        click: function (e) {
-          e.preventDefault();
-          filesList.find('.cancel').click();
-        }
-      });
-      this._on(fileUploadButtonBar.find('.delete'), {
-        click: function (e) {
-          e.preventDefault();
-          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('.toggle').prop(
-            'checked',
-            $(e.currentTarget).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.'
-      );
-    },
-
-    _initEventHandlers: function () {
-      this._super();
-      this._on(this.options.filesContainer, {
-        'click .start': this._startHandler,
-        'click .cancel': this._cancelHandler,
-        'click .delete': this._deleteHandler
-      });
-      this._initButtonBarEventHandlers();
-    },
-
-    _destroyEventHandlers: function () {
-      this._destroyButtonBarEventHandlers();
-      this._off(this.options.filesContainer, 'click');
-      this._super();
-    },
-
-    _enableFileInputButton: function () {
-      this.element.find('.fileinput-button input')
-        .prop('disabled', false)
-        .parent().removeClass('disabled');
-    },
-
-    _disableFileInputButton: function () {
-      this.element.find('.fileinput-button input')
-        .prop('disabled', true)
-        .parent().addClass('disabled');
-    },
-
-    _initTemplates: function () {
-      var options = this.options;
-      options.templatesContainer = this.document[0].createElement(
-        options.filesContainer.prop('nodeName')
-      );
-      if (tmpl) {
-        if (options.uploadTemplateId) {
-          options.uploadTemplate = tmpl(options.uploadTemplateId);
-        }
-        if (options.downloadTemplateId) {
-          options.downloadTemplate = tmpl(options.downloadTemplateId);
-        }
-      }
-    },
-
-    _initFilesContainer: function () {
-      var options = this.options;
-      if (options.filesContainer === undefined) {
-        options.filesContainer = this.element.find('.files');
-      } else if (!(options.filesContainer instanceof $)) {
-        options.filesContainer = $(options.filesContainer);
-      }
-    },
-
-    _initSpecialOptions: function () {
-      this._super();
-      this._initFilesContainer();
-      this._initTemplates();
-    },
-
-    _create: function () {
-      this._super();
-      this._resetFinishedDeferreds();
-      if (!$.support.fileInput) {
-        this._disableFileInputButton();
-      }
-    },
-
-    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();
-      }
-    },
-
-    disable: function () {
-      if (!this.options.disabled) {
-        this.element.find('input, button').prop('disabled', true);
-        this._disableFileInputButton();
-      }
-      this._super();
-    }
-
-  });
-
-}));
diff --git a/src/Resources/assets/js/jquery.fileupload-validate.js b/src/Resources/assets/js/jquery.fileupload-validate.js
deleted file mode 100644
index 7487b76d963c6261077c77d52815788f40833317..0000000000000000000000000000000000000000
--- a/src/Resources/assets/js/jquery.fileupload-validate.js
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * jQuery File Upload Validation Plugin
- * https://github.com/blueimp/jQuery-File-Upload
- *
- * Copyright 2013, Sebastian Tschan
- * https://blueimp.net
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
-
-/* global define, require, window */
-
-;(function (factory) {
-  'use strict';
-  if (typeof define === 'function' && define.amd) {
-    // Register as an anonymous AMD module:
-    define([
-      'jquery',
-      './jquery.fileupload-process'
-    ], factory);
-  } else if (typeof exports === 'object') {
-    // Node/CommonJS:
-    factory(
-      require('jquery'),
-      require('./jquery.fileupload-process')
-    );
-  } 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: '@',
-      maxFileSize: '@',
-      minFileSize: '@',
-      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],
-          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 (fileSize > options.maxFileSize) {
-          file.error = settings.i18n('maxFileSize');
-        } else if ($.type(fileSize) === 'number' &&
-          fileSize < 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/src/Resources/assets/js/main.js b/src/Resources/assets/js/main.js
index e2c2f6ba18f521cb51004eccba36361ea801d54c..4a78b014b24d80e3be16cad3c2567e2824fed445 100644
--- a/src/Resources/assets/js/main.js
+++ b/src/Resources/assets/js/main.js
@@ -1,21 +1,15 @@
 import * as jQuery from 'jquery';
 global.$ = window.$ = jQuery;
-import 'bootstrap-sass';
-import './widget/file_upload.js';
-import '../sass/file_upload.scss';
 
-import 'blueimp-file-upload/js/vendor/jquery.ui.widget';
-import 'blueimp-file-upload/js/jquery.iframe-transport';
-
-import 'blueimp-load-image';
+//css to convert to less
+import 'blueimp-file-upload/css/jquery.fileupload.css';
+import 'blueimp-file-upload/css/jquery.fileupload-ui.css';
+import '../sass/file_upload.scss';
 
-import 'blueimp-file-upload/js/jquery.fileupload';
 import 'blueimp-file-upload/js/jquery.fileupload';
 import 'blueimp-file-upload/js/jquery.fileupload-process';
 import 'blueimp-file-upload/js/jquery.fileupload-validate';
+import 'blueimp-file-upload/js/jquery.fileupload-ui';
+import './widget/file_upload.js';
 
 
-//css to convert to less
-import 'blueimp-file-upload/css/jquery.fileupload.css';
-import 'blueimp-file-upload/css/jquery.fileupload-ui.css';
-
diff --git a/tests/Fixtures/templates/base.html.twig b/tests/Fixtures/templates/base.html.twig
index 9dcfde3702f4fc5f729144d7a5c9aecad2409e1b..0a851d28dbf3b5ffb62fc09f8ee68afcdf646e38 100644
--- a/tests/Fixtures/templates/base.html.twig
+++ b/tests/Fixtures/templates/base.html.twig
@@ -2,7 +2,6 @@
 
 {% block titrePage %}
     {{ parent() }}
-
     Show case: {{ type | capitalize }}
 {% endblock %}
 
@@ -12,8 +11,7 @@
 {% endblock %}
 
 {%- block javascripts %}
-{{ parent() }}
-
+    {{ parent() }}
     {{ encore_entry_script_tags('main') }}
 {% endblock %}
 
diff --git a/tests/Fixtures/templates/index.html.twig b/tests/Fixtures/templates/index.html.twig
index b377ea802fc8527b3dc4e70c63df505b5d84739f..d5d79f5d75d15dd8ca59e4b696a818b20f33558d 100644
--- a/tests/Fixtures/templates/index.html.twig
+++ b/tests/Fixtures/templates/index.html.twig
@@ -1,5 +1,4 @@
 {% extends '/base.html.twig' %}
-
 {% block content -%}
     {{ form_start(form) }}
     <div class="tab-content">
diff --git a/tests/Fixtures/webpack.config.js b/tests/Fixtures/webpack.config.js
index a8fe4fb096587c1c0809e42e89c94236770a3fd8..3a80616769629f8a0e7832f0037922345000f85f 100644
--- a/tests/Fixtures/webpack.config.js
+++ b/tests/Fixtures/webpack.config.js
@@ -1,4 +1,5 @@
  var Encore = require('@symfony/webpack-encore');
+ //var config = Encore.getWebpackConfig();
 
 Encore
   // the project directory where compiled assets will be stored
@@ -36,10 +37,19 @@ Encore
   .addEntry('main', [
     './assets/main.js',
     './assets/main.scss',
-    './assets/fileupload_demo.js',
   ])
 
-
   .splitEntryChunks();
 
-module.exports = Encore.getWebpackConfig();
+//module.exports = Encore.getWebpackConfig();
+ const config = Encore.getWebpackConfig();
+ config.resolve.alias = {
+  'load-image': 'blueimp-load-image/js/load-image.js',
+ 'load-image-meta': 'blueimp-load-image/js/load-image-meta.js',
+ 'load-image-exif': 'blueimp-load-image/js/load-image-exif.js',
+   'load-image-orientation': 'blueimp-load-image/js/load-image-orientation.js',
+   'load-image-scale': 'blueimp-load-image/js/load-image-scale.js',
+   'canvas-to-blob': 'blueimp-canvas-to-blob/js/canvas-to-blob.js',
+ 'jquery-ui/widget': 'blueimp-file-upload/js/vendor/jquery.ui.widget.js',
+};
+module.exports = config;