From 8d8050e59a2b54922b6f2688a35178cc2ece150d Mon Sep 17 00:00:00 2001
From: Andreas Wolf <dev@a-w.io>
Date: Sun, 7 Jan 2018 17:38:48 +0100
Subject: [PATCH] [TASK] Rewrite DragUploader in TypeScript

Change-Id: If1aeff95d06e98e9c5a3725445bc616552c1eb14
Resolves: #82585
Releases: master
Reviewed-on: https://review.typo3.org/55295
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Tested-by: Mathias Schreiber <mathias.schreiber@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
---
 Build/types/TYPO3/index.d.ts                  |  44 +
 .../Private/TypeScript/DragUploader.ts        | 749 ++++++++++++++++++
 .../Public/JavaScript/DragUploader.js         | 671 +---------------
 3 files changed, 794 insertions(+), 670 deletions(-)
 create mode 100644 typo3/sysext/backend/Resources/Private/TypeScript/DragUploader.ts

diff --git a/Build/types/TYPO3/index.d.ts b/Build/types/TYPO3/index.d.ts
index f0512070a855..d114aad49161 100644
--- a/Build/types/TYPO3/index.d.ts
+++ b/Build/types/TYPO3/index.d.ts
@@ -31,6 +31,21 @@ declare namespace TYPO3 {
         public show(title: string, content: any, severity: number, buttons: any[], additionalCssClasses?: string[]): JQuery; // tslint:disable-line:max-line-length
         public dismiss(): void;
       }
+      export class Notification {
+        public readonly Notification: {
+          NOTICE: -2,
+          INFO: -1,
+          OK: 0,
+          WARNING: 1,
+          ERROR: 2
+        };
+        public notice(title: string, message: string, duration: Number): string;
+        public info(title: string, message: string, duration: Number): string;
+        public success(title: string, message: string, duration: Number): string;
+        public warning(title: string, message: string, duration: Number): string;
+        public error(title: string, message: string, duration: Number): string;
+        public showMessage(title: string, message: string, severity: Number, duration?: Number): string;
+      }
       export class Severity {
         public readonly notice: number;
         public readonly info: number;
@@ -59,6 +74,10 @@ declare module 'TYPO3/CMS/Backend/Modal' {
   export = new TYPO3.CMS.Backend.Modal();
 }
 
+declare module 'TYPO3/CMS/Backend/Notification' {
+  export = new TYPO3.CMS.Backend.Notification();
+}
+
 declare module 'TYPO3/CMS/Backend/Severity' {
   export = new TYPO3.CMS.Backend.Severity();
 }
@@ -67,6 +86,9 @@ declare module 'TYPO3/CMS/Backend/Severity' {
 interface Window {
   TYPO3: any;
   $: any;
+  inline: {
+    delayedImportElement: (objectId: number, table: string, uid: number, type: string) => void
+  };
 }
 
 /**
@@ -78,10 +100,32 @@ declare module 'TYPO3/CMS/Core/Contrib/imagesloaded.pkgd.min' {
 }
 
 declare module 'cm/lib/codemirror';
+declare module 'moment';
+declare module 'TYPO3/CMS/Backend/jsfunc.inline';
+
+/**
+ * Options for the plugin.
+ * TODO fix this
+ */
+interface DragUploaderOptions {
+  /**
+   * CSS selector for the element where generated messages are inserted. (required)
+   */
+  outputSelector: string;
+  /**
+   * Color of the message text. (optional)
+   */
+  outputColor?: string;
+}
+
+interface JQueryTypedEvent<T extends Event> extends JQueryEventObject {
+  originalEvent: T;
+}
 
 /**
  * Required to make jQuery plugins "available" in TypeScript
  */
 interface JQuery {
   clearable(): JQuery;
+  dragUploader(options?: DragUploaderOptions): JQuery;
 }
diff --git a/typo3/sysext/backend/Resources/Private/TypeScript/DragUploader.ts b/typo3/sysext/backend/Resources/Private/TypeScript/DragUploader.ts
new file mode 100644
index 000000000000..b1e7a326477f
--- /dev/null
+++ b/typo3/sysext/backend/Resources/Private/TypeScript/DragUploader.ts
@@ -0,0 +1,749 @@
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+/**
+ * Module: TYPO3/CMS/Backend/DragUploader
+ */
+
+import * as $ from 'jquery';
+import moment = require('moment');
+import NProgress = require('nprogress');
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+import Severity = require('TYPO3/CMS/Backend/Severity');
+
+/**
+ * Possible actions for conflicts w/ existing files
+ */
+enum Action {
+  OVERRIDE = 'replace',
+  RENAME = 'rename',
+  SKIP = 'cancel',
+  USE_EXISTING = 'useExisting'
+}
+
+/**
+ * Properties of a file as returned from the AJAX action; essential, this is a serialized instance of
+ * \TYPO3\CMS\Core\Resource\File plus some extra properties (see FileController::flattenResultDataValue())
+ */
+interface UploadedFile {
+  name: string;
+  id: number;
+  uid: number;
+  icon: string;
+  extension: string;
+  permissions: {read: boolean; write: boolean};
+  size: number;
+  // formatted as ddmmyy
+  date: string;
+
+  mtime: Date;
+  thumbUrl: string;
+  type: string;
+}
+
+class DragUploaderPlugin {
+  public irreObjectUid: number;
+  public $fileList: JQuery;
+  public fileListColumnCount: number;
+  public filesExtensionsAllowed: string;
+  public fileDenyPattern: RegExp | null;
+  public maxFileSize: number;
+  public $trigger: JQuery;
+  public target: string;
+
+  /**
+   * Array of files which are asked for being overridden
+   */
+  private askForOverride: Array<{original: UploadedFile, uploaded: File, action: Action}> = [];
+
+  private percentagePerFile: number = 1;
+
+  private $body: JQuery;
+  private $element: JQuery;
+  private $dropzone: JQuery;
+  private $dropzoneMask: JQuery;
+  private fileInput: HTMLInputElement;
+  private browserCapabilities: { fileReader: boolean; DnD: boolean; Progress: boolean };
+  private dropZoneInsertBefore: boolean;
+  private queueLength: number;
+
+  constructor(element: HTMLElement) {
+    this.$body = $('body');
+    this.$element = $(element);
+    const hasTrigger = this.$element.data('dropzoneTrigger') !== undefined;
+    this.$trigger = $(this.$element.data('dropzoneTrigger'));
+    this.$dropzone = $('<div />').addClass('dropzone').hide();
+    this.irreObjectUid = this.$element.data('fileIrreObject');
+
+    const dropZoneEscapedTarget = this.$element.data('dropzoneTarget');
+    if (this.irreObjectUid && this.$element.nextAll(dropZoneEscapedTarget).length !== 0) {
+      this.dropZoneInsertBefore = true;
+      this.$dropzone.insertBefore(dropZoneEscapedTarget);
+    } else {
+      this.dropZoneInsertBefore = false;
+      this.$dropzone.insertAfter(dropZoneEscapedTarget);
+    }
+    this.$dropzoneMask = $('<div />').addClass('dropzone-mask').appendTo(this.$dropzone);
+    this.fileInput = <HTMLInputElement>document.createElement('input');
+    this.fileInput.setAttribute('type', 'file');
+    this.fileInput.setAttribute('multiple', 'multiple');
+    this.fileInput.setAttribute('name', 'files[]');
+    this.fileInput.classList.add('upload-file-picker');
+    this.$body.append(this.fileInput);
+
+    this.$fileList = $(this.$element.data('progress-container'));
+    this.fileListColumnCount = $('thead tr:first th', this.$fileList).length;
+    this.filesExtensionsAllowed = this.$element.data('file-allowed');
+    this.fileDenyPattern = this.$element.data('file-deny-pattern') ? new RegExp(this.$element.data('file-deny-pattern'), 'i') : null;
+    this.maxFileSize = parseInt(this.$element.data('max-file-size'), 10);
+    this.target = this.$element.data('target-folder');
+
+    this.browserCapabilities = {
+      fileReader: typeof FileReader !== 'undefined',
+      DnD: 'draggable' in document.createElement('span'),
+      Progress: 'upload' in new XMLHttpRequest
+    };
+
+
+    if (!this.browserCapabilities.DnD) {
+      console.warn('Browser has no Drag and drop capabilities; cannot initialize DragUploader');
+      return;
+    }
+
+    this.$body.on('dragover', this.dragFileIntoDocument);
+    this.$body.on('dragend', this.dragAborted);
+    this.$body.on('drop', this.ignoreDrop);
+
+    this.$dropzone.on('dragenter', this.fileInDropzone);
+    this.$dropzoneMask.on('dragenter', this.fileInDropzone);
+    this.$dropzoneMask.on('dragleave', this.fileOutOfDropzone);
+    this.$dropzoneMask.on('drop', (ev: JQueryEventObject) => this.handleDrop(<JQueryTypedEvent<DragEvent>>ev));
+
+    this.$dropzone.prepend(
+      '<div class="dropzone-hint">' +
+      '<div class="dropzone-hint-media">' +
+      '<div class="dropzone-hint-icon"></div>' +
+      '</div>' +
+      '<div class="dropzone-hint-body">' +
+      '<h3 class="dropzone-hint-title">' +
+      TYPO3.lang['file_upload.dropzonehint.title'] +
+      '</h3>' +
+      '<p class="dropzone-hint-message">' +
+      TYPO3.lang['file_upload.dropzonehint.message'] +
+      '</p>' +
+      '</div>' +
+      '</div>'
+    ).click(() => {
+      this.fileInput.click();
+    });
+    $('<span />').addClass('dropzone-close').click(this.hideDropzone).appendTo(this.$dropzone);
+
+    // no filelist then create own progress table
+    if (this.$fileList.length === 0) {
+      this.$fileList = $('<table />')
+        .attr('id', 'typo3-filelist')
+        .addClass('table table-striped table-hover upload-queue')
+        .html('<tbody></tbody>').hide();
+
+      if (this.dropZoneInsertBefore) {
+        this.$fileList.insertAfter(this.$dropzone);
+      } else {
+        this.$fileList.insertBefore(this.$dropzone);
+      }
+      this.fileListColumnCount = 7;
+    }
+
+    this.fileInput.addEventListener('change', () => {
+      this.processFiles(Array.apply(null, this.fileInput.files));
+    });
+
+    this.bindUploadButton(hasTrigger === true ? this.$trigger : this.$element);
+  }
+
+  public showDropzone(): void {
+    this.$dropzone.show();
+  }
+
+  /**
+   *
+   * @param {Event} event
+   */
+  public hideDropzone(event: Event): void {
+    event.stopPropagation();
+    event.preventDefault();
+    this.$dropzone.hide();
+  }
+
+  /**
+   * @param {Event} event
+   * @returns {boolean}
+   */
+  public dragFileIntoDocument = (event: Event): boolean => {
+    event.stopPropagation();
+    event.preventDefault();
+    $(event.currentTarget).addClass('drop-in-progress');
+    this.showDropzone();
+    return false;
+  }
+
+  /**
+   *
+   * @param {Event} event
+   * @returns {Boolean}
+   */
+  public dragAborted = (event: Event): boolean => {
+    event.stopPropagation();
+    event.preventDefault();
+    $(event.currentTarget).removeClass('drop-in-progress');
+    return false;
+  }
+
+  public ignoreDrop = (event: Event): boolean => {
+    // stops the browser from redirecting.
+    event.stopPropagation();
+    event.preventDefault();
+    this.dragAborted(event);
+    return false;
+  }
+
+  public handleDrop = (event: JQueryTypedEvent<DragEvent>): void => {
+    this.ignoreDrop(event);
+    this.processFiles(event.originalEvent.dataTransfer.files);
+    this.$dropzone.removeClass('drop-status-ok');
+  }
+
+  /**
+   * @param {FileList} files
+   */
+  public processFiles(files: FileList): void {
+    this.queueLength = files.length;
+
+    if (!this.$fileList.is(':visible')) {
+      this.$fileList.show();
+    }
+
+    NProgress.start();
+    this.percentagePerFile = 1 / files.length;
+
+    // Check for each file if is already exist before adding it to the queue
+    const ajaxCalls: JQueryXHR[] = [];
+    $.each(files, (i: string, file) => {
+      ajaxCalls[parseInt(i, 10)] = $.ajax({
+        url: TYPO3.settings.ajaxUrls.file_exists,
+        data: {
+          fileName: file.name,
+          fileTarget: this.target
+        },
+        cache: false,
+        success: (response: any) => {
+          const fileExists = typeof response.uid !== 'undefined';
+          if (fileExists) {
+            this.askForOverride.push({
+              original: response,
+              uploaded: file,
+              action: this.irreObjectUid ? Action.USE_EXISTING : Action.SKIP
+            });
+            NProgress.inc(this.percentagePerFile);
+          } else {
+            // Unused var _ is necessary as "no-unused-expression" is active
+            const _ = new FileQueueItem(this, file, Action.SKIP);
+          }
+        }
+      });
+    });
+
+    $.when.apply($, ajaxCalls).done(() => {
+      this.drawOverrideModal();
+      NProgress.done();
+    });
+
+    this.fileInput.value = '';
+  }
+
+  public fileInDropzone = (): void => {
+    this.$dropzone.addClass('drop-status-ok');
+  }
+
+  public fileOutOfDropzone = (): void => {
+    this.$dropzone.removeClass('drop-status-ok');
+  }
+
+  /**
+   * Bind file picker to default upload button
+   *
+   * @param {Object} button
+   */
+  public bindUploadButton(button: JQuery): void {
+    button.click((event: Event) => {
+      event.preventDefault();
+      this.fileInput.click();
+      this.showDropzone();
+    });
+  }
+
+  /**
+   * Decrements the queue and renders a flash message if queue is empty
+   */
+  public decrementQueueLength(): void {
+    if (this.queueLength > 0) {
+      this.queueLength--;
+      if (this.queueLength === 0) {
+        $.ajax({
+          url: TYPO3.settings.ajaxUrls.flashmessages_render,
+          cache: false,
+          success: (data) => {
+            $.each(data, (index: number, flashMessage: {title: string, message: string, severity: number}) => {
+              Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
+            });
+          }
+        });
+      }
+    }
+  }
+
+  /**
+   * Renders the modal for existing files
+   */
+  public drawOverrideModal(): void {
+    const amountOfItems = Object.keys(this.askForOverride).length;
+    if (amountOfItems === 0) {
+      return;
+    }
+    const $modalContent = $('<div/>').append(
+      $('<p/>').text(TYPO3.lang['file_upload.existingfiles.description']),
+      $('<table/>', {class: 'table'}).append(
+        $('<thead/>').append(
+          $('<tr />').append(
+            $('<th/>'),
+            $('<th/>').text(TYPO3.lang['file_upload.header.originalFile']),
+            $('<th/>').text(TYPO3.lang['file_upload.header.uploadedFile']),
+            $('<th/>').text(TYPO3.lang['file_upload.header.action'])
+          )
+        )
+      )
+    );
+
+    for (let i = 0; i < amountOfItems; ++i) {
+      const $record = $('<tr />').append(
+        $('<td />').append(
+          (this.askForOverride[i].original.thumbUrl !== ''
+              ? $('<img />', {src: this.askForOverride[i].original.thumbUrl, height: 40})
+              : $(this.askForOverride[i].original.icon)
+          )
+        ),
+        $('<td />').html(
+          this.askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(this.askForOverride[i].uploaded.size)) + ')' +
+          '<br>' + moment(this.askForOverride[i].uploaded.lastModifiedDate, 'x').format('YYYY-MM-DD HH:mm')
+        ),
+        $('<td />').html(
+          this.askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(this.askForOverride[i].original.size)) + ')' +
+          '<br>' + moment(this.askForOverride[i].original.mtime, 'X').format('YYYY-MM-DD HH:mm')
+        ),
+        $('<td />').append(
+          $('<select />', {class: 'form-control t3js-actions', 'data-override': i}).append(
+            (this.irreObjectUid ? $('<option/>').val(Action.USE_EXISTING).text(TYPO3.lang['file_upload.actions.use_existing']) : ''),
+            $('<option />').val(Action.SKIP).text(TYPO3.lang['file_upload.actions.skip']),
+            $('<option />').val(Action.RENAME).text(TYPO3.lang['file_upload.actions.rename']),
+            $('<option />').val(Action.OVERRIDE).text(TYPO3.lang['file_upload.actions.override'])
+          )
+        )
+      );
+      $modalContent.find('table').append('<tbody />').append($record);
+    }
+
+    const $modal = Modal.confirm(
+      TYPO3.lang['file_upload.existingfiles.title'], $modalContent, Severity.warning,
+      [
+        {
+          text: $(this).data('button-close-text') || TYPO3.lang['file_upload.button.cancel'] || 'Cancel',
+          active: true,
+          btnClass: 'btn-default',
+          name: 'cancel'
+        },
+        {
+          text: $(this).data('button-ok-text') || TYPO3.lang['file_upload.button.continue'] || 'Continue with selected actions',
+          btnClass: 'btn-warning',
+          name: 'continue'
+        }
+      ],
+      ['modal-inner-scroll']
+    );
+    $modal.find('.modal-dialog').addClass('modal-lg');
+
+    $modal.find('.modal-footer').prepend(
+      $('<span/>').addClass('form-inline').append(
+        $('<label/>').text(TYPO3.lang['file_upload.actions.all.label']),
+        $('<select/>', {class: 'form-control t3js-actions-all'}).append(
+          $('<option/>').val('').text(TYPO3.lang['file_upload.actions.all.empty']),
+          (this.irreObjectUid ? $('<option/>').val(Action.USE_EXISTING).text(TYPO3.lang['file_upload.actions.all.use_existing']) : ''),
+          $('<option/>').val(Action.SKIP).text(TYPO3.lang['file_upload.actions.all.skip']),
+          $('<option/>').val(Action.RENAME).text(TYPO3.lang['file_upload.actions.all.rename']),
+          $('<option/>').val(Action.OVERRIDE).text(TYPO3.lang['file_upload.actions.all.override'])
+        )
+      )
+    );
+
+    const uploader = this;
+    $modal.on('change', '.t3js-actions-all', function(this: HTMLInputElement): void {
+      const $this = $(this),
+        value = $this.val();
+
+      if (value !== '') {
+        // mass action was selected, apply action to every file
+        $modal.find('.t3js-actions').each((i, select) => {
+          const $select = $(select),
+            index = parseInt($select.data('override'), 10);
+          $select.val(value).prop('disabled', 'disabled');
+          uploader.askForOverride[index].action = <Action>$select.val();
+        });
+      } else {
+        $modal.find('.t3js-actions').removeProp('disabled');
+      }
+    }).on('change', '.t3js-actions', function(this: HTMLInputElement): void {
+      const $this = $(this),
+        index = parseInt($this.data('override'), 10);
+      uploader.askForOverride[index].action = <Action>$this.val();
+    }).on('button.clicked', function(this: HTMLInputElement, e: Event): void {
+      if ((<HTMLInputElement>(e.target)).name === 'cancel') {
+        uploader.askForOverride = [];
+        Modal.dismiss();
+      } else if ((<HTMLInputElement>(e.target)).name === 'continue') {
+        $.each(uploader.askForOverride, (key, fileInfo) => {
+          if (fileInfo.action === Action.USE_EXISTING) {
+            DragUploader.addFileToIrre(
+              uploader.irreObjectUid,
+              fileInfo.original
+            );
+          } else if (fileInfo.action !== Action.SKIP) {
+            // Unused var _ is necessary as "no-unused-expression" is active
+            const _ = new FileQueueItem(uploader, fileInfo.uploaded, fileInfo.action);
+          }
+        });
+        uploader.askForOverride = [];
+        Modal.dismiss();
+      }
+    }).on('hidden.bs.modal', () => {
+      this.askForOverride = [];
+    });
+  }
+}
+
+class FileQueueItem {
+  private $row: JQuery;
+  private $iconCol: JQuery;
+  private $fileName: JQuery;
+  private $progress: JQuery;
+  private $progressContainer: JQuery;
+  private $progressBar: JQuery;
+  private $progressPercentage: JQuery;
+  private $progressMessage: JQuery;
+  private dragUploader: DragUploaderPlugin;
+  private file: File;
+  private override: Action;
+  private upload: XMLHttpRequest;
+
+  constructor(dragUploader: DragUploaderPlugin, file: File, override: Action) {
+    this.dragUploader = dragUploader;
+    this.file = file;
+    this.override = override;
+
+    this.$row = $('<tr />').addClass('upload-queue-item uploading');
+    this.$iconCol = $('<td />').addClass('col-icon').appendTo(this.$row);
+    this.$fileName = $('<td />').text(file.name).appendTo(this.$row);
+    this.$progress = $('<td />').attr('colspan', this.dragUploader.fileListColumnCount - 2).appendTo(this.$row);
+    this.$progressContainer = $('<div />').addClass('upload-queue-progress').appendTo(this.$progress);
+    this.$progressBar = $('<div />').addClass('upload-queue-progress-bar').appendTo(this.$progressContainer);
+    this.$progressPercentage = $('<span />').addClass('upload-queue-progress-percentage').appendTo(this.$progressContainer);
+    this.$progressMessage = $('<span />').addClass('upload-queue-progress-message').appendTo(this.$progressContainer);
+
+
+    // position queue item in filelist
+    if ($('tbody tr.upload-queue-item', this.dragUploader.$fileList).length === 0) {
+      this.$row.prependTo($('tbody', this.dragUploader.$fileList));
+      this.$row.addClass('last');
+    } else {
+      this.$row.insertBefore($('tbody tr.upload-queue-item:first', this.dragUploader.$fileList));
+    }
+
+    // set dummy file icon
+    this.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>');
+
+    // check file size
+    if (this.dragUploader.maxFileSize > 0 && this.file.size > this.dragUploader.maxFileSize) {
+      this.updateMessage(TYPO3.lang['file_upload.maxFileSizeExceeded']
+        .replace(/\{0\}/g, this.file.name)
+        .replace(/\{1\}/g, DragUploader.fileSizeAsString(this.dragUploader.maxFileSize)));
+      this.$row.addClass('error');
+
+      // check filename/extension against deny pattern
+    } else if (this.dragUploader.fileDenyPattern && this.file.name.match(this.dragUploader.fileDenyPattern)) {
+      this.updateMessage(TYPO3.lang['file_upload.fileNotAllowed'].replace(/\{0\}/g, this.file.name));
+      this.$row.addClass('error');
+
+    } else if (!this.checkAllowedExtensions()) {
+      this.updateMessage(TYPO3.lang['file_upload.fileExtensionExpected']
+        .replace(/\{0\}/g, this.dragUploader.filesExtensionsAllowed)
+      );
+      this.$row.addClass('error');
+    } else {
+      this.updateMessage('- ' + DragUploader.fileSizeAsString(this.file.size));
+
+      const formData = new FormData();
+      formData.append('data[upload][1][target]', this.dragUploader.target);
+      formData.append('data[upload][1][data]', '1');
+      formData.append('overwriteExistingFiles', this.override);
+      formData.append('redirect', '');
+      formData.append('upload_1', this.file);
+
+      const s = $.extend(true, {}, $.ajaxSettings, {
+        url: TYPO3.settings.ajaxUrls.file_process,
+        contentType: false,
+        processData: false,
+        data: formData,
+        cache: false,
+        type: 'POST',
+        success: (data: {upload?: UploadedFile[]}) => this.uploadSuccess(data),
+        error: (response: XMLHttpRequest) => this.uploadError(response)
+      });
+
+      s.xhr = () => {
+        const xhr = $.ajaxSettings.xhr();
+        xhr.upload.addEventListener('progress', (e: ProgressEvent) => this.updateProgress(e));
+        return xhr;
+      };
+
+      // start upload
+      this.upload = $.ajax(s);
+    }
+  }
+
+  /**
+   * @param {string} message
+   */
+  public updateMessage(message: string): void {
+    this.$progressMessage.text(message);
+  }
+
+  /**
+   * Remove the progress bar
+   */
+  public removeProgress(): void {
+    if (this.$progress) {
+      this.$progress.remove();
+    }
+  }
+
+  public uploadStart(): void {
+    this.$progressPercentage.text('(0%)');
+    this.$progressBar.width('1%');
+    this.dragUploader.$trigger.trigger('uploadStart', [this]);
+  }
+
+  /**
+   * @param {XMLHttpRequest} response
+   */
+  public uploadError(response: XMLHttpRequest): void {
+    this.updateMessage(TYPO3.lang['file_upload.uploadFailed'].replace(/\{0\}/g, this.file.name));
+    const error = $(response.responseText);
+    if (error.is('t3err')) {
+      this.$progressPercentage.text(error.text());
+    } else {
+      this.$progressPercentage.text('(' + response.statusText + ')');
+    }
+    this.$row.addClass('error');
+    this.dragUploader.decrementQueueLength();
+    this.dragUploader.$trigger.trigger('uploadError', [this, response]);
+  }
+
+  /**
+   * @param {ProgressEvent} event
+   */
+  public updateProgress(event: ProgressEvent): void {
+    const percentage = Math.round((event.loaded / event.total) * 100) + '%';
+    this.$progressBar.outerWidth(percentage);
+    this.$progressPercentage.text(percentage);
+    this.dragUploader.$trigger.trigger('updateProgress', [this, percentage, event]);
+  }
+
+  /**
+   * @param {{upload?: UploadedFile[]}} data
+   */
+  public uploadSuccess(data: {upload?: UploadedFile[]}): void {
+    if (data.upload) {
+      this.dragUploader.decrementQueueLength();
+      this.$row.removeClass('uploading');
+      this.$fileName.text(data.upload[0].name);
+      this.$progressPercentage.text('');
+      this.$progressMessage.text('100%');
+      this.$progressBar.outerWidth('100%');
+
+      // replace file icon
+      if (data.upload[0].icon) {
+        this.$iconCol
+          .html(
+            '<a href="#" class="t3js-contextmenutrigger" data-uid="'
+            + data.upload[0].id + '" data-table="sys_file">'
+            + data.upload[0].icon + '&nbsp;</span></a>'
+          );
+      }
+
+      if (this.dragUploader.irreObjectUid) {
+        DragUploader.addFileToIrre(
+          this.dragUploader.irreObjectUid,
+          data.upload[0]
+        );
+        setTimeout(
+          () => {
+            this.$row.remove();
+            if ($('tr', this.dragUploader.$fileList).length === 0) {
+              this.dragUploader.$fileList.hide();
+              this.dragUploader.$trigger.trigger('uploadSuccess', [this, data]);
+            }
+          },
+          3000);
+      } else {
+        setTimeout(
+          () => {
+            this.showFileInfo(data.upload[0]);
+            this.dragUploader.$trigger.trigger('uploadSuccess', [this, data]);
+          },
+          3000);
+      }
+    }
+  }
+
+  /**
+   * @param {UploadedFile} fileInfo
+   */
+  public showFileInfo(fileInfo: UploadedFile): void {
+    this.removeProgress();
+    // add spacing cells when clibboard and/or extended view is enabled
+    for (let i = 7; i < this.dragUploader.fileListColumnCount; i++) {
+      $('<td />').text('').appendTo(this.$row);
+    }
+    $('<td />').text(fileInfo.extension.toUpperCase()).appendTo(this.$row);
+    $('<td />').text(fileInfo.date).appendTo(this.$row);
+    $('<td />').text(DragUploader.fileSizeAsString(fileInfo.size)).appendTo(this.$row);
+    let permissions = '';
+    if (fileInfo.permissions.read) {
+      permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.read'] + '</strong>';
+    }
+    if (fileInfo.permissions.write) {
+      permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.write'] + '</strong>';
+    }
+    $('<td />').html(permissions).appendTo(this.$row);
+    $('<td />').text('-').appendTo(this.$row);
+  }
+
+  public checkAllowedExtensions(): boolean {
+    if (!this.dragUploader.filesExtensionsAllowed) {
+      return true;
+    }
+    const extension = this.file.name.split('.').pop();
+    const allowed = this.dragUploader.filesExtensionsAllowed.split(',');
+
+    return $.inArray(extension.toLowerCase(), allowed) !== -1;
+  }
+}
+
+class DragUploader {
+  private static options: DragUploaderOptions;
+  fileListColumnCount: number;
+  filesExtensionsAllowed: string;
+  fileDenyPattern: string;
+
+  public static fileSizeAsString(size: number): string {
+    const sizeKB: number = size / 1024;
+    let str = '';
+
+    if (sizeKB > 1024) {
+      str = (sizeKB / 1024).toFixed(1) + ' MB';
+    } else {
+      str = sizeKB.toFixed(1) + ' KB';
+    }
+    return str;
+  }
+
+  /**
+   * @param {number} irre_object
+   * @param {UploadedFile} file
+   */
+  public static addFileToIrre(irre_object: number, file: UploadedFile): void {
+    window.inline.delayedImportElement(
+      irre_object,
+      'sys_file',
+      file.uid,
+      'file'
+    );
+  }
+
+  public static init(): void {
+    const me = this;
+    const opts = me.options;
+
+    // register the jQuery plugin "DragUploaderPlugin"
+    $.fn.extend({
+      dragUploader: function (options?: DragUploaderOptions | string): JQuery {
+        return this.each((index: number, elem: HTMLElement): void => {
+          const $this = $(elem);
+          let data = $this.data('DragUploaderPlugin');
+          if (!data) {
+            $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(elem)));
+          }
+          if (typeof options === 'string') {
+            data[options]();
+          }
+        });
+      }
+    });
+
+    $(() => {
+      $('.t3js-drag-uploader').dragUploader(opts);
+    });
+  }
+
+}
+
+/**
+ * Function to apply the example plugin to the selected elements of a jQuery result.
+ */
+interface DragUploaderFunction {
+  /**
+   * Apply the example plugin to the elements selected in the jQuery result.
+   *
+   * @param options Options to use for this application of the example plugin.
+   * @returns jQuery result.
+   */
+  (options: DragUploaderOptions): JQuery;
+}
+
+export const initialize = function(): void {
+  DragUploader.init();
+
+  // load required modules to hook in the post initialize function
+  if (
+    'undefined' !== typeof TYPO3.settings
+    && 'undefined' !== typeof TYPO3.settings.RequireJS
+    && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules
+    && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']
+  ) {
+    $.each(
+      TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], (pos, moduleName) => {
+        require([moduleName]);
+      }
+    );
+  }
+};
+
+initialize();
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js b/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
index bf7c2cf550e2..ac35b7727b52 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/DragUploader.js
@@ -10,673 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-
-/**
- * Module: TYPO3/CMS/Backend/DragUploader
- *
- */
-define(['jquery',
-  'moment',
-  'nprogress',
-  'TYPO3/CMS/Backend/Modal',
-  'TYPO3/CMS/Backend/Notification',
-  'TYPO3/CMS/Backend/Severity',
-  'TYPO3/CMS/Lang/Lang'
-], function($, moment, NProgress, Modal, Notification, Severity) {
-
-  /**
-   * Array of files which are asked for being overridden
-   *
-   * @type {array}
-   */
-  var askForOverride = [],
-    percentagePerFile = 1;
-
-  /**
-   * File actions
-   */
-  var actions = {
-    OVERRIDE: 'replace',
-    RENAME: 'rename',
-    SKIP: 'cancel',
-    USE_EXISTING: 'useExisting'
-  };
-
-  /*
-   * part 1: a generic jQuery plugin "$.dragUploader"
-   */
-
-  // register the constructor
-  /**
-   *
-   * @param {HTMLElement} element
-   * @constructor
-   * @exports TYPO3/CMS/Backend/DragUploader
-   */
-  var DragUploaderPlugin = function(element) {
-    var me = this;
-    me.$body = $('body');
-    me.$element = $(element);
-    me.$trigger = $(me.$element.data('dropzoneTrigger'));
-    me.$dropzone = $('<div />').addClass('dropzone').hide();
-    me.irreObjectUid = me.$element.data('fileIrreObject');
-    var dropZoneEscapedTarget = me.$element.data('dropzoneTarget');
-    if (me.irreObjectUid && me.$element.nextAll(dropZoneEscapedTarget).length !== 0) {
-      me.dropZoneInsertBefore = true;
-      me.$dropzone.insertBefore(dropZoneEscapedTarget);
-    } else {
-      me.dropZoneInsertBefore = false;
-      me.$dropzone.insertAfter(dropZoneEscapedTarget);
-    }
-    me.$dropzoneMask = $('<div />').addClass('dropzone-mask').appendTo(me.$dropzone);
-    me.$fileInput = $('<input type="file" multiple name="files[]" />').addClass('upload-file-picker').appendTo(me.$body);
-    me.$fileList = $(me.$element.data('progress-container'));
-    me.fileListColumnCount = $('thead tr:first th', me.$fileList).length;
-    me.filesExtensionsAllowed = me.$element.data('file-allowed');
-    me.fileDenyPattern = me.$element.data('file-deny-pattern') ? new RegExp(me.$element.data('file-deny-pattern'), 'i') : false;
-    me.maxFileSize = parseInt(me.$element.data('max-file-size'));
-    me.target = me.$element.data('target-folder');
-
-    me.browserCapabilities = {
-      fileReader: typeof FileReader !== 'undefined',
-      DnD: 'draggable' in document.createElement('span'),
-      FormData: !!window.FormData,
-      Progress: "upload" in new XMLHttpRequest
-    };
-
-    /**
-     *
-     */
-    me.showDropzone = function() {
-      me.$dropzone.show();
-    };
-
-    /**
-     *
-     * @param {Event} event
-     */
-    me.hideDropzone = function(event) {
-      event.stopPropagation();
-      event.preventDefault();
-      me.$dropzone.hide();
-    };
-
-    /**
-     *
-     * @param {Event} event
-     * @returns {Boolean}
-     */
-    me.dragFileIntoDocument = function(event) {
-      event.stopPropagation();
-      event.preventDefault();
-      me.$body.addClass('drop-in-progress');
-      me.showDropzone();
-      return false;
-    };
-
-    /**
-     *
-     * @param {Event} event
-     * @returns {Boolean}
-     */
-    me.dragAborted = function(event) {
-      event.stopPropagation();
-      event.preventDefault();
-      me.$body.removeClass('drop-in-progress');
-      return false;
-    };
-
-    /**
-     *
-     * @param {Event} event
-     * @returns {Boolean}
-     */
-    me.ignoreDrop = function(event) {
-      // stops the browser from redirecting.
-      event.stopPropagation();
-      event.preventDefault();
-      me.dragAborted(event);
-      return false;
-    };
-
-    /**
-     *
-     * @param {Event} event
-     */
-    me.handleDrop = function(event) {
-      me.ignoreDrop(event);
-      me.processFiles(event.originalEvent.dataTransfer.files);
-      me.$dropzone.removeClass('drop-status-ok');
-    };
-
-    /**
-     *
-     * @param {Array} files
-     */
-    me.processFiles = function(files) {
-      me.queueLength = files.length;
-
-      if (!me.$fileList.is(':visible')) {
-        me.$fileList.show();
-      }
-
-      NProgress.start();
-      percentagePerFile = 1 / files.length;
-
-      // Check for each file if is already exist before adding it to the queue
-      var ajaxCalls = [];
-      $.each(files, function(i, file) {
-
-        ajaxCalls[i] = $.ajax({
-          url: TYPO3.settings.ajaxUrls['file_exists'],
-          data: {
-            fileName: file.name,
-            fileTarget: me.target
-          },
-          cache: false,
-          success: function(response) {
-            var fileExists = typeof response.uid !== 'undefined';
-            if (fileExists) {
-              askForOverride.push({
-                original: response,
-                uploaded: file,
-                action: me.irreObjectUid ? actions.USE_EXISTING : actions.SKIP
-              });
-              NProgress.inc(percentagePerFile);
-            } else {
-              new FileQueueItem(me, file, 'cancel');
-            }
-          }
-        });
-      });
-
-      $.when.apply($, ajaxCalls).done(function() {
-        me.drawOverrideModal();
-        NProgress.done();
-      });
-
-      delete ajaxCalls;
-      me.$fileInput.val('');
-    };
-
-    /**
-     *
-     * @param {Event} event
-     */
-    me.fileInDropzone = function(event) {
-      me.$dropzone.addClass('drop-status-ok');
-    };
-
-    /**
-     *
-     * @param {Event} event
-     */
-    me.fileOutOfDropzone = function(event) {
-      me.$dropzone.removeClass('drop-status-ok');
-    };
-
-    /**
-     * bind file picker to default upload button
-     *
-     * @param {Object} button
-     */
-    me.bindUploadButton = function(button) {
-      button.click(function(event) {
-        event.preventDefault();
-        me.$fileInput.click();
-        me.showDropzone();
-      });
-    };
-
-    if (me.browserCapabilities.DnD) {
-      me.$body.on('dragover', me.dragFileIntoDocument);
-      me.$body.on('dragend', me.dragAborted);
-      me.$body.on('drop', me.ignoreDrop);
-
-      me.$dropzone.on('dragenter', me.fileInDropzone);
-      me.$dropzoneMask.on('dragenter', me.fileInDropzone);
-      me.$dropzoneMask.on('dragleave', me.fileOutOfDropzone);
-      me.$dropzoneMask.on('drop', me.handleDrop);
-
-      me.$dropzone.prepend(
-        '<div class="dropzone-hint">' +
-        '<div class="dropzone-hint-media">' +
-        '<div class="dropzone-hint-icon"></div>' +
-        '</div>' +
-        '<div class="dropzone-hint-body">' +
-        '<h3 class="dropzone-hint-title">' +
-        TYPO3.lang['file_upload.dropzonehint.title'] +
-        '</h3>' +
-        '<p class="dropzone-hint-message">' +
-        TYPO3.lang['file_upload.dropzonehint.message'] +
-        '</p>' +
-        '</div>' +
-        '</div>').click(function() {
-        me.$fileInput.click()
-      });
-      $('<span />').addClass('dropzone-close').click(me.hideDropzone).appendTo(me.$dropzone);
-
-      // no filelist then create own progress table
-      if (me.$fileList.length === 0) {
-        me.$fileList = $('<table />')
-          .attr('id', 'typo3-filelist')
-          .addClass('table table-striped table-hover upload-queue')
-          .html('<tbody></tbody>').hide();
-        if (me.dropZoneInsertBefore) {
-          me.$fileList.insertAfter(me.$dropzone);
-        } else {
-          me.$fileList.insertBefore(me.$dropzone);
-        }
-        me.fileListColumnCount = 7;
-      }
-
-      me.$fileInput.on('change', function() {
-        me.processFiles(this.files);
-      });
-
-      me.bindUploadButton(me.$trigger.length ? me.$trigger : me.$element);
-    }
-
-    /**
-     *
-     */
-    me.decrementQueueLength = function() {
-      if (me.queueLength > 0) {
-        me.queueLength--;
-        if (me.queueLength === 0) {
-          $.ajax({
-            url: TYPO3.settings.ajaxUrls['flashmessages_render'],
-            cache: false,
-            success: function(data) {
-              $.each(data, function(index, flashMessage) {
-                Notification.showMessage(flashMessage.title, flashMessage.message, flashMessage.severity);
-              });
-            }
-          });
-        }
-      }
-    };
-
-    /**
-     *
-     */
-    me.drawOverrideModal = function() {
-      var amountOfItems = Object.keys(askForOverride).length;
-      if (amountOfItems === 0) {
-        return;
-      }
-      var $modalContent = $('<div/>').append(
-        $('<p/>').text(TYPO3.lang['file_upload.existingfiles.description']),
-        $('<table/>', {class: 'table'}).append(
-          $('<thead/>').append(
-            $('<tr />').append(
-              $('<th/>'),
-              $('<th/>').text(TYPO3.lang['file_upload.header.originalFile']),
-              $('<th/>').text(TYPO3.lang['file_upload.header.uploadedFile']),
-              $('<th/>').text(TYPO3.lang['file_upload.header.action'])
-            )
-          )
-        )
-      );
-
-      for (var i = 0; i < amountOfItems; ++i) {
-        var $record = $('<tr />').append(
-          $('<td />').append(
-            (askForOverride[i].original.thumbUrl !== ''
-                ? $('<img />', {src: askForOverride[i].original.thumbUrl, height: 40})
-                : $(askForOverride[i].original.icon)
-            )
-          ),
-          $('<td />').html(
-            askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(askForOverride[i].uploaded.size)) + ')' +
-            '<br>' + moment(askForOverride[i].uploaded.lastModified, 'x').format('YYYY-MM-DD HH:mm')
-          ),
-          $('<td />').html(
-            askForOverride[i].uploaded.name + ' (' + (DragUploader.fileSizeAsString(askForOverride[i].original.size)) + ')' +
-            '<br>' + moment(askForOverride[i].original.mtime, 'X').format('YYYY-MM-DD HH:mm')
-          ),
-          $('<td />').append(
-            $('<select />', {class: 'form-control t3js-actions', 'data-override': i}).append(
-              (me.irreObjectUid ? $('<option/>').val(actions.USE_EXISTING).text(TYPO3.lang['file_upload.actions.use_existing']) : ''),
-              $('<option />').val(actions.SKIP).text(TYPO3.lang['file_upload.actions.skip']),
-              $('<option />').val(actions.RENAME).text(TYPO3.lang['file_upload.actions.rename']),
-              $('<option />').val(actions.OVERRIDE).text(TYPO3.lang['file_upload.actions.override'])
-            )
-          )
-        );
-        $modalContent.find('table').append('<tbody />').append($record);
-      }
-
-      var $modal = Modal.confirm(TYPO3.lang['file_upload.existingfiles.title'], $modalContent, Severity.warning, [
-        {
-          text: $(this).data('button-close-text') || TYPO3.lang['file_upload.button.cancel'] || 'Cancel',
-          active: true,
-          btnClass: 'btn-default',
-          name: 'cancel'
-        },
-        {
-          text: $(this).data('button-ok-text') || TYPO3.lang['file_upload.button.continue'] || 'Continue with selected actions',
-          btnClass: 'btn-warning',
-          name: 'continue'
-        }
-      ], ['modal-inner-scroll']);
-      $modal.find('.modal-dialog').addClass('modal-lg');
-
-      $modal.find('.modal-footer').prepend(
-        $('<span/>').addClass('form-inline').append(
-          $('<label/>').text(TYPO3.lang['file_upload.actions.all.label']),
-          $('<select/>', {class: 'form-control t3js-actions-all'}).append(
-            $('<option/>').val('').text(TYPO3.lang['file_upload.actions.all.empty']),
-            (me.irreObjectUid ? $('<option/>').val(actions.USE_EXISTING).text(TYPO3.lang['file_upload.actions.all.use_existing']) : ''),
-            $('<option/>').val(actions.SKIP).text(TYPO3.lang['file_upload.actions.all.skip']),
-            $('<option/>').val(actions.RENAME).text(TYPO3.lang['file_upload.actions.all.rename']),
-            $('<option/>').val(actions.OVERRIDE).text(TYPO3.lang['file_upload.actions.all.override'])
-          )
-        )
-      );
-
-      $modal.on('change', '.t3js-actions-all', function() {
-        var $me = $(this),
-          value = $me.val();
-
-        if (value !== '') {
-          // mass action was selected, apply action to every file
-          $modal.find('.t3js-actions').each(function(i, select) {
-            var $select = $(select),
-              index = parseInt($select.data('override'));
-            $select.val(value).prop('disabled', 'disabled');
-            askForOverride[index].action = $select.val();
-          });
-        } else {
-          $modal.find('.t3js-actions').removeProp('disabled');
-        }
-      }).on('change', '.t3js-actions', function() {
-        var $me = $(this),
-          index = parseInt($me.data('override'));
-        askForOverride[index].action = $me.val();
-      }).on('button.clicked', function(e) {
-        if (e.target.name === 'cancel') {
-          askForOverride = [];
-          Modal.dismiss();
-        } else if (e.target.name === 'continue') {
-          $.each(askForOverride, function(key, fileInfo) {
-            if (fileInfo.action === actions.USE_EXISTING) {
-              DragUploader.addFileToIrre(
-                me.irreObjectUid,
-                fileInfo.original
-              );
-            } else if (fileInfo.action !== actions.SKIP) {
-              new FileQueueItem(me, fileInfo.uploaded, fileInfo.action);
-            }
-          });
-          askForOverride = [];
-          Modal.dismiss();
-        }
-      }).on('hidden.bs.modal', function() {
-        askForOverride = [];
-      });
-    }
-  };
-
-  var FileQueueItem = function(dragUploader, file, override) {
-    var me = this;
-    me.dragUploader = dragUploader;
-    me.file = file;
-    me.override = override;
-
-    me.$row = $('<tr />').addClass('upload-queue-item uploading');
-    me.$iconCol = $('<td />').addClass('col-icon').appendTo(me.$row);
-    me.$fileName = $('<td />').text(file.name).appendTo(me.$row);
-    me.$progress = $('<td />').attr('colspan', me.dragUploader.fileListColumnCount - 2).appendTo(me.$row);
-    me.$progressContainer = $('<div />').addClass('upload-queue-progress').appendTo(me.$progress);
-    me.$progressBar = $('<div />').addClass('upload-queue-progress-bar').appendTo(me.$progressContainer);
-    me.$progressPercentage = $('<span />').addClass('upload-queue-progress-percentage').appendTo(me.$progressContainer);
-    me.$progressMessage = $('<span />').addClass('upload-queue-progress-message').appendTo(me.$progressContainer);
-
-    me.updateMessage = function(message) {
-      me.$progressMessage.text(message);
-    };
-
-    me.removeProgress = function() {
-      if (me.$progress) {
-        me.$progress.remove();
-      }
-    };
-
-    me.uploadStart = function() {
-      me.$progressPercentage.text('(0%)');
-      me.$progressBar.width('1%');
-      me.dragUploader.$trigger.trigger('uploadStart', [me]);
-    };
-
-    me.uploadError = function(response) {
-      me.updateMessage(TYPO3.lang['file_upload.uploadFailed'].replace(/\{0\}/g, me.file.name));
-      var error = $(response.responseText);
-      if (error.is('t3err')) {
-        me.$progressPercentage.text(error.text());
-      } else {
-        me.$progressPercentage.text('(' + response.statusText + ')');
-      }
-      me.$row.addClass('error');
-      me.dragUploader.decrementQueueLength();
-      me.dragUploader.$trigger.trigger('uploadError', [me, response]);
-    };
-
-    me.updateProgress = function(event) {
-      var percentage = Math.round((event.loaded / event.total) * 100) + '%';
-      me.$progressBar.outerWidth(percentage);
-      me.$progressPercentage.text(percentage);
-      me.dragUploader.$trigger.trigger('updateProgress', [me, percentage, event]);
-    };
-
-    me.uploadSuccess = function(data) {
-      if (data.upload) {
-        me.dragUploader.decrementQueueLength();
-        me.$row.removeClass('uploading');
-        me.$fileName.text(data.upload[0].name);
-        me.$progressPercentage.text('');
-        me.$progressMessage.text('100%');
-        me.$progressBar.outerWidth('100%');
-
-        // replace file icon
-        if (data.upload[0].icon) {
-          me.$iconCol.html('<a href="#" class="t3js-contextmenutrigger" data-uid="' + data.upload[0].id + '" data-table="sys_file">' + data.upload[0].icon + '&nbsp;</span></a>');
-        }
-
-        if (me.dragUploader.irreObjectUid) {
-          DragUploader.addFileToIrre(
-            me.dragUploader.irreObjectUid,
-            data.upload[0]
-          );
-          setTimeout(function() {
-            me.$row.remove();
-            if ($('tr', me.dragUploader.$fileList).length === 0) {
-              me.dragUploader.$fileList.hide();
-              me.dragUploader.$trigger.trigger('uploadSuccess', [me, data]);
-            }
-          }, 3000);
-        } else {
-          setTimeout(function() {
-            me.showFileInfo(data.upload[0]);
-            me.dragUploader.$trigger.trigger('uploadSuccess', [me, data]);
-          }, 3000);
-        }
-      }
-    };
-
-    me.showFileInfo = function(fileInfo) {
-      me.removeProgress();
-      // add spacing cells when clibboard and/or extended view is enabled
-      for (i = 7; i < me.dragUploader.fileListColumnCount; i++) {
-        $('<td />').text('').appendTo(me.$row);
-      }
-      $('<td />').text(fileInfo.extension.toUpperCase()).appendTo(me.$row);
-      $('<td />').text(fileInfo.date).appendTo(me.$row);
-      $('<td />').text(DragUploader.fileSizeAsString(fileInfo.size)).appendTo(me.$row);
-      var permissions = '';
-      if (fileInfo.permissions.read) {
-        permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.read'] + '</strong>';
-      }
-      if (fileInfo.permissions.write) {
-        permissions += '<strong class="text-danger">' + TYPO3.lang['permissions.write'] + '</strong>';
-      }
-      $('<td />').html(permissions).appendTo(me.$row);
-      $('<td />').text('-').appendTo(me.$row);
-    };
-
-    me.checkAllowedExtensions = function() {
-      if (!me.dragUploader.filesExtensionsAllowed) {
-        return true;
-      }
-      var extension = me.file.name.split('.').pop();
-      var allowed = me.dragUploader.filesExtensionsAllowed.split(',');
-      if ($.inArray(extension.toLowerCase(), allowed) !== -1) {
-        return true;
-      }
-      return false;
-    };
-
-    // position queue item in filelist
-    if ($('tbody tr.upload-queue-item', me.dragUploader.$fileList).length === 0) {
-      me.$row.prependTo($('tbody', me.dragUploader.$fileList));
-      me.$row.addClass('last');
-    } else {
-      me.$row.insertBefore($('tbody tr.upload-queue-item:first', me.dragUploader.$fileList));
-    }
-
-    // set dummy file icon
-    me.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>')
-
-    // check file size
-    if (me.dragUploader.maxFileSize > 0 && me.file.size > me.dragUploader.maxFileSize) {
-      me.updateMessage(TYPO3.lang['file_upload.maxFileSizeExceeded']
-        .replace(/\{0\}/g, me.file.name)
-        .replace(/\{1\}/g, DragUploader.fileSizeAsString(me.dragUploader.maxFileSize)));
-      me.$row.addClass('error');
-
-      // check filename/extension against deny pattern
-    } else if (me.dragUploader.fileDenyPattern && me.file.name.match(me.dragUploader.fileDenyPattern)) {
-      me.updateMessage(TYPO3.lang['file_upload.fileNotAllowed'].replace(/\{0\}/g, me.file.name));
-      me.$row.addClass('error');
-
-    } else if (!me.checkAllowedExtensions()) {
-      me.updateMessage(TYPO3.lang['file_upload.fileExtensionExpected']
-        .replace(/\{0\}/g, me.dragUploader.filesExtensionsAllowed)
-      );
-      me.$row.addClass('error');
-    } else {
-      me.updateMessage('- ' + DragUploader.fileSizeAsString(me.file.size));
-
-      var formData = new FormData();
-      formData.append('data[upload][1][target]', me.dragUploader.target);
-      formData.append('data[upload][1][data]', '1');
-      formData.append('overwriteExistingFiles', me.override);
-      formData.append('redirect', '');
-      formData.append('upload_1', me.file);
-
-      var s = $.extend(true, {}, $.ajaxSettings, {
-        url: TYPO3.settings.ajaxUrls['file_process'],
-        contentType: false,
-        processData: false,
-        data: formData,
-        cache: false,
-        type: 'POST',
-        success: me.uploadSuccess,
-        error: me.uploadError
-      });
-
-      s.xhr = function() {
-        var xhr = $.ajaxSettings.xhr();
-        xhr.upload.addEventListener('progress', me.updateProgress);
-        return xhr;
-      };
-
-      // start upload
-      me.upload = $.ajax(s);
-    }
-  };
-
-  /**
-   * part 2: The main module of this file
-   * - initialize the DragUploader module and register
-   * the jQuery plugin in the jQuery global object
-   * when initializing the DragUploader module
-   */
-  var DragUploader = {};
-
-  DragUploader.options = {};
-
-  DragUploader.fileSizeAsString = function(size) {
-    var string = '',
-      sizeKB = size / 1024;
-
-    if (parseInt(sizeKB) > 1024) {
-      var sizeMB = sizeKB / 1024;
-      string = sizeMB.toFixed(1) + ' MB';
-    } else {
-      string = sizeKB.toFixed(1) + ' KB';
-    }
-    return string;
-  };
-
-  DragUploader.addFileToIrre = function(irre_object, file) {
-    inline.delayedImportElement(
-      irre_object,
-      'sys_file',
-      file.uid,
-      'file'
-    );
-  };
-
-  DragUploader.initialize = function() {
-    var me = this,
-      opts = me.options;
-
-    // register the jQuery plugin "DragUploaderPlugin"
-    $.fn.dragUploader = function(option) {
-      return this.each(function() {
-        var $this = $(this),
-          data = $this.data('DragUploaderPlugin');
-        if (!data) {
-          $this.data('DragUploaderPlugin', (data = new DragUploaderPlugin(this)));
-        }
-        if (typeof option === 'string') {
-          data[option]();
-        }
-      });
-    };
-
-    $(function() {
-      $('.t3js-drag-uploader').dragUploader();
-    });
-  };
-
-
-  /**
-   * part 3: initialize the RequireJS module, require possible post-initialize hooks,
-   * and return the main object
-   */
-  var initialize = function() {
-
-    DragUploader.initialize();
-
-    // load required modules to hook in the post initialize function
-    if (
-      'undefined' !== typeof TYPO3.settings
-      && 'undefined' !== typeof TYPO3.settings.RequireJS
-      && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules
-      && 'undefined' !== typeof TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader']
-    ) {
-      $.each(TYPO3.settings.RequireJS.PostInitializationModules['TYPO3/CMS/Backend/DragUploader'], function(pos, moduleName) {
-        require([moduleName]);
-      });
-    }
-
-    // return the object in the global space
-    return DragUploader;
-  };
-
-  // call the main initialize function and execute the hooks
-  return initialize();
-
-});
+define(["require","exports","jquery","moment","nprogress","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","TYPO3/CMS/Backend/Severity"],function(a,b,c,d,e,f,g,h){"use strict";Object.defineProperty(b,"__esModule",{value:!0});var i;!function(a){a.OVERRIDE="replace",a.RENAME="rename",a.SKIP="cancel",a.USE_EXISTING="useExisting"}(i||(i={}));var j=function(){function a(a){var b=this;this.askForOverride=[],this.percentagePerFile=1,this.dragFileIntoDocument=function(a){return a.stopPropagation(),a.preventDefault(),c(a.currentTarget).addClass("drop-in-progress"),b.showDropzone(),!1},this.dragAborted=function(a){return a.stopPropagation(),a.preventDefault(),c(a.currentTarget).removeClass("drop-in-progress"),!1},this.ignoreDrop=function(a){return a.stopPropagation(),a.preventDefault(),b.dragAborted(a),!1},this.handleDrop=function(a){b.ignoreDrop(a),b.processFiles(a.originalEvent.dataTransfer.files),b.$dropzone.removeClass("drop-status-ok")},this.fileInDropzone=function(){b.$dropzone.addClass("drop-status-ok")},this.fileOutOfDropzone=function(){b.$dropzone.removeClass("drop-status-ok")},this.$body=c("body"),this.$element=c(a);var d=void 0!==this.$element.data("dropzoneTrigger");this.$trigger=c(this.$element.data("dropzoneTrigger")),this.$dropzone=c("<div />").addClass("dropzone").hide(),this.irreObjectUid=this.$element.data("fileIrreObject");var e=this.$element.data("dropzoneTarget");return this.irreObjectUid&&0!==this.$element.nextAll(e).length?(this.dropZoneInsertBefore=!0,this.$dropzone.insertBefore(e)):(this.dropZoneInsertBefore=!1,this.$dropzone.insertAfter(e)),this.$dropzoneMask=c("<div />").addClass("dropzone-mask").appendTo(this.$dropzone),this.fileInput=document.createElement("input"),this.fileInput.setAttribute("type","file"),this.fileInput.setAttribute("multiple","multiple"),this.fileInput.setAttribute("name","files[]"),this.fileInput.classList.add("upload-file-picker"),this.$body.append(this.fileInput),this.$fileList=c(this.$element.data("progress-container")),this.fileListColumnCount=c("thead tr:first th",this.$fileList).length,this.filesExtensionsAllowed=this.$element.data("file-allowed"),this.fileDenyPattern=this.$element.data("file-deny-pattern")?new RegExp(this.$element.data("file-deny-pattern"),"i"):null,this.maxFileSize=parseInt(this.$element.data("max-file-size"),10),this.target=this.$element.data("target-folder"),this.browserCapabilities={fileReader:"undefined"!=typeof FileReader,DnD:"draggable"in document.createElement("span"),Progress:"upload"in new XMLHttpRequest},this.browserCapabilities.DnD?(this.$body.on("dragover",this.dragFileIntoDocument),this.$body.on("dragend",this.dragAborted),this.$body.on("drop",this.ignoreDrop),this.$dropzone.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragenter",this.fileInDropzone),this.$dropzoneMask.on("dragleave",this.fileOutOfDropzone),this.$dropzoneMask.on("drop",function(a){return b.handleDrop(a)}),this.$dropzone.prepend('<div class="dropzone-hint"><div class="dropzone-hint-media"><div class="dropzone-hint-icon"></div></div><div class="dropzone-hint-body"><h3 class="dropzone-hint-title">'+TYPO3.lang["file_upload.dropzonehint.title"]+'</h3><p class="dropzone-hint-message">'+TYPO3.lang["file_upload.dropzonehint.message"]+"</p></div></div>").click(function(){b.fileInput.click()}),c("<span />").addClass("dropzone-close").click(this.hideDropzone).appendTo(this.$dropzone),0===this.$fileList.length&&(this.$fileList=c("<table />").attr("id","typo3-filelist").addClass("table table-striped table-hover upload-queue").html("<tbody></tbody>").hide(),this.dropZoneInsertBefore?this.$fileList.insertAfter(this.$dropzone):this.$fileList.insertBefore(this.$dropzone),this.fileListColumnCount=7),this.fileInput.addEventListener("change",function(){b.processFiles(Array.apply(null,b.fileInput.files))}),void this.bindUploadButton(d===!0?this.$trigger:this.$element)):void console.warn("Browser has no Drag and drop capabilities; cannot initialize DragUploader")}return a.prototype.showDropzone=function(){this.$dropzone.show()},a.prototype.hideDropzone=function(a){a.stopPropagation(),a.preventDefault(),this.$dropzone.hide()},a.prototype.processFiles=function(a){var b=this;this.queueLength=a.length,this.$fileList.is(":visible")||this.$fileList.show(),e.start(),this.percentagePerFile=1/a.length;var d=[];c.each(a,function(a,f){d[parseInt(a,10)]=c.ajax({url:TYPO3.settings.ajaxUrls.file_exists,data:{fileName:f.name,fileTarget:b.target},cache:!1,success:function(a){var c="undefined"!=typeof a.uid;if(c)b.askForOverride.push({original:a,uploaded:f,action:b.irreObjectUid?i.USE_EXISTING:i.SKIP}),e.inc(b.percentagePerFile);else{new k(b,f,i.SKIP)}}})}),c.when.apply(c,d).done(function(){b.drawOverrideModal(),e.done()}),this.fileInput.value=""},a.prototype.bindUploadButton=function(a){var b=this;a.click(function(a){a.preventDefault(),b.fileInput.click(),b.showDropzone()})},a.prototype.decrementQueueLength=function(){this.queueLength>0&&(this.queueLength--,0===this.queueLength&&c.ajax({url:TYPO3.settings.ajaxUrls.flashmessages_render,cache:!1,success:function(a){c.each(a,function(a,b){g.showMessage(b.title,b.message,b.severity)})}}))},a.prototype.drawOverrideModal=function(){var a=this,b=Object.keys(this.askForOverride).length;if(0!==b){for(var e=c("<div/>").append(c("<p/>").text(TYPO3.lang["file_upload.existingfiles.description"]),c("<table/>",{class:"table"}).append(c("<thead/>").append(c("<tr />").append(c("<th/>"),c("<th/>").text(TYPO3.lang["file_upload.header.originalFile"]),c("<th/>").text(TYPO3.lang["file_upload.header.uploadedFile"]),c("<th/>").text(TYPO3.lang["file_upload.header.action"]))))),g=0;g<b;++g){var j=c("<tr />").append(c("<td />").append(""!==this.askForOverride[g].original.thumbUrl?c("<img />",{src:this.askForOverride[g].original.thumbUrl,height:40}):c(this.askForOverride[g].original.icon)),c("<td />").html(this.askForOverride[g].uploaded.name+" ("+l.fileSizeAsString(this.askForOverride[g].uploaded.size)+")<br>"+d(this.askForOverride[g].uploaded.lastModifiedDate,"x").format("YYYY-MM-DD HH:mm")),c("<td />").html(this.askForOverride[g].uploaded.name+" ("+l.fileSizeAsString(this.askForOverride[g].original.size)+")<br>"+d(this.askForOverride[g].original.mtime,"X").format("YYYY-MM-DD HH:mm")),c("<td />").append(c("<select />",{class:"form-control t3js-actions","data-override":g}).append(this.irreObjectUid?c("<option/>").val(i.USE_EXISTING).text(TYPO3.lang["file_upload.actions.use_existing"]):"",c("<option />").val(i.SKIP).text(TYPO3.lang["file_upload.actions.skip"]),c("<option />").val(i.RENAME).text(TYPO3.lang["file_upload.actions.rename"]),c("<option />").val(i.OVERRIDE).text(TYPO3.lang["file_upload.actions.override"]))));e.find("table").append("<tbody />").append(j)}var m=f.confirm(TYPO3.lang["file_upload.existingfiles.title"],e,h.warning,[{text:c(this).data("button-close-text")||TYPO3.lang["file_upload.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:c(this).data("button-ok-text")||TYPO3.lang["file_upload.button.continue"]||"Continue with selected actions",btnClass:"btn-warning",name:"continue"}],["modal-inner-scroll"]);m.find(".modal-dialog").addClass("modal-lg"),m.find(".modal-footer").prepend(c("<span/>").addClass("form-inline").append(c("<label/>").text(TYPO3.lang["file_upload.actions.all.label"]),c("<select/>",{class:"form-control t3js-actions-all"}).append(c("<option/>").val("").text(TYPO3.lang["file_upload.actions.all.empty"]),this.irreObjectUid?c("<option/>").val(i.USE_EXISTING).text(TYPO3.lang["file_upload.actions.all.use_existing"]):"",c("<option/>").val(i.SKIP).text(TYPO3.lang["file_upload.actions.all.skip"]),c("<option/>").val(i.RENAME).text(TYPO3.lang["file_upload.actions.all.rename"]),c("<option/>").val(i.OVERRIDE).text(TYPO3.lang["file_upload.actions.all.override"]))));var n=this;m.on("change",".t3js-actions-all",function(){var a=c(this),b=a.val();""!==b?m.find(".t3js-actions").each(function(a,d){var e=c(d),f=parseInt(e.data("override"),10);e.val(b).prop("disabled","disabled"),n.askForOverride[f].action=e.val()}):m.find(".t3js-actions").removeProp("disabled")}).on("change",".t3js-actions",function(){var a=c(this),b=parseInt(a.data("override"),10);n.askForOverride[b].action=a.val()}).on("button.clicked",function(a){"cancel"===a.target.name?(n.askForOverride=[],f.dismiss()):"continue"===a.target.name&&(c.each(n.askForOverride,function(a,b){if(b.action===i.USE_EXISTING)l.addFileToIrre(n.irreObjectUid,b.original);else if(b.action!==i.SKIP){new k(n,b.uploaded,b.action)}}),n.askForOverride=[],f.dismiss())}).on("hidden.bs.modal",function(){a.askForOverride=[]})}},a}(),k=function(){function a(a,b,d){var e=this;if(this.dragUploader=a,this.file=b,this.override=d,this.$row=c("<tr />").addClass("upload-queue-item uploading"),this.$iconCol=c("<td />").addClass("col-icon").appendTo(this.$row),this.$fileName=c("<td />").text(b.name).appendTo(this.$row),this.$progress=c("<td />").attr("colspan",this.dragUploader.fileListColumnCount-2).appendTo(this.$row),this.$progressContainer=c("<div />").addClass("upload-queue-progress").appendTo(this.$progress),this.$progressBar=c("<div />").addClass("upload-queue-progress-bar").appendTo(this.$progressContainer),this.$progressPercentage=c("<span />").addClass("upload-queue-progress-percentage").appendTo(this.$progressContainer),this.$progressMessage=c("<span />").addClass("upload-queue-progress-message").appendTo(this.$progressContainer),0===c("tbody tr.upload-queue-item",this.dragUploader.$fileList).length?(this.$row.prependTo(c("tbody",this.dragUploader.$fileList)),this.$row.addClass("last")):this.$row.insertBefore(c("tbody tr.upload-queue-item:first",this.dragUploader.$fileList)),this.$iconCol.html('<span class="t3-icon t3-icon-mimetypes t3-icon-other-other">&nbsp;</span>'),this.dragUploader.maxFileSize>0&&this.file.size>this.dragUploader.maxFileSize)this.updateMessage(TYPO3.lang["file_upload.maxFileSizeExceeded"].replace(/\{0\}/g,this.file.name).replace(/\{1\}/g,l.fileSizeAsString(this.dragUploader.maxFileSize))),this.$row.addClass("error");else if(this.dragUploader.fileDenyPattern&&this.file.name.match(this.dragUploader.fileDenyPattern))this.updateMessage(TYPO3.lang["file_upload.fileNotAllowed"].replace(/\{0\}/g,this.file.name)),this.$row.addClass("error");else if(this.checkAllowedExtensions()){this.updateMessage("- "+l.fileSizeAsString(this.file.size));var f=new FormData;f.append("data[upload][1][target]",this.dragUploader.target),f.append("data[upload][1][data]","1"),f.append("overwriteExistingFiles",this.override),f.append("redirect",""),f.append("upload_1",this.file);var g=c.extend(!0,{},c.ajaxSettings,{url:TYPO3.settings.ajaxUrls.file_process,contentType:!1,processData:!1,data:f,cache:!1,type:"POST",success:function(a){return e.uploadSuccess(a)},error:function(a){return e.uploadError(a)}});g.xhr=function(){var a=c.ajaxSettings.xhr();return a.upload.addEventListener("progress",function(a){return e.updateProgress(a)}),a},this.upload=c.ajax(g)}else this.updateMessage(TYPO3.lang["file_upload.fileExtensionExpected"].replace(/\{0\}/g,this.dragUploader.filesExtensionsAllowed)),this.$row.addClass("error")}return a.prototype.updateMessage=function(a){this.$progressMessage.text(a)},a.prototype.removeProgress=function(){this.$progress&&this.$progress.remove()},a.prototype.uploadStart=function(){this.$progressPercentage.text("(0%)"),this.$progressBar.width("1%"),this.dragUploader.$trigger.trigger("uploadStart",[this])},a.prototype.uploadError=function(a){this.updateMessage(TYPO3.lang["file_upload.uploadFailed"].replace(/\{0\}/g,this.file.name));var b=c(a.responseText);b.is("t3err")?this.$progressPercentage.text(b.text()):this.$progressPercentage.text("("+a.statusText+")"),this.$row.addClass("error"),this.dragUploader.decrementQueueLength(),this.dragUploader.$trigger.trigger("uploadError",[this,a])},a.prototype.updateProgress=function(a){var b=Math.round(a.loaded/a.total*100)+"%";this.$progressBar.outerWidth(b),this.$progressPercentage.text(b),this.dragUploader.$trigger.trigger("updateProgress",[this,b,a])},a.prototype.uploadSuccess=function(a){var b=this;a.upload&&(this.dragUploader.decrementQueueLength(),this.$row.removeClass("uploading"),this.$fileName.text(a.upload[0].name),this.$progressPercentage.text(""),this.$progressMessage.text("100%"),this.$progressBar.outerWidth("100%"),a.upload[0].icon&&this.$iconCol.html('<a href="#" class="t3js-contextmenutrigger" data-uid="'+a.upload[0].id+'" data-table="sys_file">'+a.upload[0].icon+"&nbsp;</span></a>"),this.dragUploader.irreObjectUid?(l.addFileToIrre(this.dragUploader.irreObjectUid,a.upload[0]),setTimeout(function(){b.$row.remove(),0===c("tr",b.dragUploader.$fileList).length&&(b.dragUploader.$fileList.hide(),b.dragUploader.$trigger.trigger("uploadSuccess",[b,a]))},3e3)):setTimeout(function(){b.showFileInfo(a.upload[0]),b.dragUploader.$trigger.trigger("uploadSuccess",[b,a])},3e3))},a.prototype.showFileInfo=function(a){this.removeProgress();for(var b=7;b<this.dragUploader.fileListColumnCount;b++)c("<td />").text("").appendTo(this.$row);c("<td />").text(a.extension.toUpperCase()).appendTo(this.$row),c("<td />").text(a.date).appendTo(this.$row),c("<td />").text(l.fileSizeAsString(a.size)).appendTo(this.$row);var d="";a.permissions.read&&(d+='<strong class="text-danger">'+TYPO3.lang["permissions.read"]+"</strong>"),a.permissions.write&&(d+='<strong class="text-danger">'+TYPO3.lang["permissions.write"]+"</strong>"),c("<td />").html(d).appendTo(this.$row),c("<td />").text("-").appendTo(this.$row)},a.prototype.checkAllowedExtensions=function(){if(!this.dragUploader.filesExtensionsAllowed)return!0;var a=this.file.name.split(".").pop(),b=this.dragUploader.filesExtensionsAllowed.split(",");return c.inArray(a.toLowerCase(),b)!==-1},a}(),l=function(){function a(){}return a.fileSizeAsString=function(a){var b=a/1024,c="";return c=b>1024?(b/1024).toFixed(1)+" MB":b.toFixed(1)+" KB"},a.addFileToIrre=function(a,b){window.inline.delayedImportElement(a,"sys_file",b.uid,"file")},a.init=function(){var a=this,b=a.options;c.fn.extend({dragUploader:function(a){return this.each(function(b,d){var e=c(d),f=e.data("DragUploaderPlugin");f||e.data("DragUploaderPlugin",f=new j(d)),"string"==typeof a&&f[a]()})}}),c(function(){c(".t3js-drag-uploader").dragUploader(b)})},a}();b.initialize=function(){l.init(),"undefined"!=typeof TYPO3.settings&&"undefined"!=typeof TYPO3.settings.RequireJS&&"undefined"!=typeof TYPO3.settings.RequireJS.PostInitializationModules&&"undefined"!=typeof TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"]&&c.each(TYPO3.settings.RequireJS.PostInitializationModules["TYPO3/CMS/Backend/DragUploader"],function(b,c){a([c])})},b.initialize()});
\ No newline at end of file
-- 
GitLab