diff --git a/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Main.ts b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Main.ts
new file mode 100644
index 0000000000000000000000000000000000000000..61d438de1a86defe8c6486ac1b5a9bee574b56ad
--- /dev/null
+++ b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Main.ts
@@ -0,0 +1,356 @@
+/*
+ * 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!
+ */
+
+import * as $ from 'jquery';
+import * as NProgress from 'nprogress';
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import SplitButtons = require('TYPO3/CMS/Backend/SplitButtons');
+import Tooltip = require('TYPO3/CMS/Backend/Tooltip');
+import Severity = require('TYPO3/CMS/Backend/Severity');
+import SecurityUtility = require('TYPO3/CMS/Core/SecurityUtility');
+import ExtensionManagerRepository = require('./Repository');
+import ExtensionManagerUpdate = require('./Update');
+import ExtensionManagerUploadForm = require('./UploadForm');
+import 'datatables';
+import 'TYPO3/CMS/Backend/jquery.clearable';
+
+const securityUtility = new SecurityUtility();
+
+enum ExtensionManagerIdentifier {
+  extensionlist = '#typo3-extension-list',
+  searchField = '#Tx_Extensionmanager_extensionkey',
+}
+
+/**
+ * Module: TYPO3/CMS/Extensionmanager/Main
+ * main logic holding everything together, consists of multiple parts
+ * ExtensionManager => Various functions for displaying the extension list / sorting
+ * Repository => Various AJAX functions for TER downloads
+ * ExtensionManager.Update => Various AJAX functions to display updates
+ * ExtensionManager.uploadForm => helper to show the upload form
+ */
+class ExtensionManager {
+  public Update: ExtensionManagerUpdate;
+  public UploadForm: ExtensionManagerUploadForm;
+  public Repository: ExtensionManagerRepository;
+
+  constructor() {
+    $(() => {
+      $.fn.dataTableExt.oSort['extension-asc'] = (a: string, b: string) => {
+        return ExtensionManager.extensionCompare(a, b);
+      };
+
+      $.fn.dataTableExt.oSort['extension-desc'] = (a: string, b: string) => {
+        let result = ExtensionManager.extensionCompare(a, b);
+        return result * -1;
+      };
+
+      $.fn.dataTableExt.oSort['version-asc'] = (a: string, b: string) => {
+        let result = ExtensionManager.versionCompare(a, b);
+        return result * -1;
+      };
+
+      $.fn.dataTableExt.oSort['version-desc'] = (a: string, b: string) => {
+        return ExtensionManager.versionCompare(a, b);
+      };
+      this.Update = new ExtensionManagerUpdate();
+      this.UploadForm = new ExtensionManagerUploadForm();
+      this.Repository = new ExtensionManagerRepository();
+
+      const dataTable: DataTables.Api = this.manageExtensionListing();
+      $(document).on('click', '.onClickMaskExtensionManager', (): void => {
+        NProgress.start();
+      }).on('click', 'a[data-action=update-extension]', (e: JQueryEventObject): void => {
+        e.preventDefault();
+        $.ajax({
+          url: $(this).attr('href'),
+          dataType: 'json',
+          beforeSend: (): void => {
+            NProgress.start();
+          },
+          success: this.updateExtension,
+        });
+      }).on('change', 'input[name=unlockDependencyIgnoreButton]', (e: JQueryEventObject): void => {
+        const $actionButton = $('.t3js-dependencies');
+        $actionButton.toggleClass('disabled', !$(e.currentTarget).prop('checked'));
+      });
+
+      $(ExtensionManagerIdentifier.searchField).clearable({
+        onClear: (): void => {
+          dataTable.search('').draw();
+        },
+      });
+
+      $(document).on('click', '.t3-button-action-installdistribution', (): void => {
+        NProgress.start();
+      });
+
+      SplitButtons.addPreSubmitCallback((e: JQueryEventObject): void => {
+        if ($(e.target).hasClass('t3js-save-close')) {
+          $('#configurationform').append($('<input />', {
+            type: 'hidden',
+            name: 'tx_extensionmanager_tools_extensionmanagerextensionmanager[action]',
+            value: 'saveAndClose',
+          }));
+        }
+      });
+
+      this.Repository.initDom();
+      this.Update.initializeEvents();
+      this.UploadForm.initializeEvents();
+
+      Tooltip.initialize('#typo3-extension-list [title]', {
+        delay: {
+          show: 500,
+          hide: 100,
+        },
+        trigger: 'hover',
+        container: 'body',
+      });
+    });
+  }
+
+  private manageExtensionListing(): DataTables.Api {
+    const $searchField = $(ExtensionManagerIdentifier.searchField);
+    const dataTable = $(ExtensionManagerIdentifier.extensionlist).DataTable({
+        paging: false,
+        dom: 'lrtip',
+        lengthChange: false,
+        pageLength: 15,
+        stateSave: true,
+        drawCallback: this.bindExtensionListActions,
+        columns: [
+          null,
+          null,
+          {
+            type: 'extension',
+          },
+          null,
+          {
+            type: 'version',
+          }, {
+            orderable: false,
+          },
+          null,
+          null,
+        ],
+      });
+
+    $searchField.parents('form').on('submit', () => {
+      return false;
+    });
+
+    const getVars: any = ExtensionManager.getUrlVars();
+
+    // restore filter
+    const currentSearch = (getVars.search ? getVars.search : dataTable.search());
+    $searchField.val(currentSearch);
+
+    $searchField.on('input', (e: JQueryEventObject): void => {
+      dataTable.search($(e.currentTarget).val()).draw();
+    });
+
+    return dataTable;
+  }
+
+  private bindExtensionListActions = (): void => {
+    $('.removeExtension').not('.transformed').each((index: number, element: any) => {
+      const $me = $(element);
+      $me.data('href', $me.attr('href'));
+      $me.attr('href', '#');
+      $me.addClass('transformed');
+      $me.click((): void => {
+        Modal.confirm(
+          TYPO3.lang['extensionList.removalConfirmation.title'],
+          TYPO3.lang['extensionList.removalConfirmation.question'],
+          Severity.error,
+          [
+            {
+              text: TYPO3.lang['button.cancel'],
+              active: true,
+              btnClass: 'btn-default',
+              trigger: (): void => {
+                Modal.dismiss();
+              },
+            }, {
+              text: TYPO3.lang['button.remove'],
+              btnClass: 'btn-danger',
+              trigger: (): void => {
+                this.removeExtensionFromDisk($me);
+                Modal.dismiss();
+              },
+            },
+          ],
+        );
+      });
+    });
+  }
+
+  private removeExtensionFromDisk($extension: JQuery): void {
+    $.ajax({
+      url: $extension.data('href'),
+      beforeSend: (): void => {
+        NProgress.start();
+      },
+      success: (): void => {
+        location.reload();
+      },
+      complete: (): void => {
+        NProgress.done();
+      },
+    });
+  }
+
+  private static getUrlVars(): any {
+    let vars: any = [];
+    let hash: Array<string>;
+    let hashes: Array<string> = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
+    for (let i = 0; i < hashes.length; i++) {
+      hash = hashes[i].split('=');
+      vars.push(hash[0]);
+      vars[hash[0]] = hash[1];
+    }
+    return vars;
+  }
+
+  /**
+   * Special sorting for the extension version column
+   */
+  private static versionCompare(a: string, b: string): number {
+    if (a === b) {
+      return 0;
+    }
+
+    const a_components = a.split('.');
+    const b_components = b.split('.');
+    const len = Math.min(a_components.length, b_components.length);
+
+    // loop while the components are equal
+    for (let i = 0; i < len; i++) {
+      // A bigger than B
+      if (parseInt(a_components[i], 10) > parseInt(b_components[i], 10)) {
+        return 1;
+      }
+
+      // B bigger than A
+      if (parseInt(a_components[i], 10) < parseInt(b_components[i], 10)) {
+        return -1;
+      }
+    }
+
+    // If one's a prefix of the other, the longer one is greaRepository.
+    if (a_components.length > b_components.length) {
+      return 1;
+    }
+
+    if (a_components.length < b_components.length) {
+      return -1;
+    }
+    // Otherwise they are the same.
+    return 0;
+  }
+
+  /**
+   * The extension name column can contain various forms of HTML that
+   * break a direct comparison of values
+   */
+  private static extensionCompare(a: string, b: string): number {
+    const div = document.createElement('div');
+    div.innerHTML = a;
+    const aStr = div.textContent || div.innerText || a;
+
+    div.innerHTML = b;
+    const bStr = div.textContent || div.innerText || b;
+
+    return aStr.trim().localeCompare(bStr.trim());
+  }
+
+  private updateExtension(data: any): void {
+    let i = 0;
+    const $form = $('<form>');
+    $.each(data.updateComments, (version: string, comment: string): void => {
+      const $input = $('<input>').attr({type: 'radio', name: 'version'}).val(version);
+      if (i === 0) {
+        $input.attr('checked', 'checked');
+      }
+      $form.append([
+        $('<h3>').append([
+          $input,
+          ' ' + securityUtility.encodeHtml(version),
+        ]),
+        $('<div>')
+          .append(
+            comment
+              .replace(/(\r\n|\n\r|\r|\n)/g, '\n')
+              .split(/\n/).map((line: string): string => {
+              return securityUtility.encodeHtml(line);
+            })
+              .join('<br>'),
+          ),
+      ]);
+      i++;
+    });
+    const $container = $('<div>').append([
+      $('<h1>').text(TYPO3.lang['extensionList.updateConfirmation.title']),
+      $('<h2>').text(TYPO3.lang['extensionList.updateConfirmation.message']),
+      $form,
+    ]);
+
+    NProgress.done();
+
+    Modal.confirm(
+      TYPO3.lang['extensionList.updateConfirmation.questionVersionComments'],
+      $container,
+      Severity.warning,
+      [
+        {
+          text: TYPO3.lang['button.cancel'],
+          active: true,
+          btnClass: 'btn-default',
+          trigger: (): void => {
+            Modal.dismiss();
+          },
+        }, {
+        text: TYPO3.lang['button.updateExtension'],
+        btnClass: 'btn-warning',
+        trigger: (): void => {
+          $.ajax({
+            url: data.url,
+            data: {
+              tx_extensionmanager_tools_extensionmanagerextensionmanager: {
+                version: $('input:radio[name=version]:checked', Modal.currentModal).val(),
+              },
+            },
+            dataType: 'json',
+            beforeSend: (): void => {
+              NProgress.start();
+            },
+            complete: (): void => {
+              location.reload();
+            },
+          });
+          Modal.dismiss();
+        },
+      },
+      ],
+    );
+  }
+}
+
+let extensionManagerObject = new ExtensionManager();
+
+if (typeof TYPO3.ExtensionManager === 'undefined') {
+  TYPO3.ExtensionManager = extensionManagerObject;
+}
+
+export = extensionManagerObject;
diff --git a/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Repository.ts b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Repository.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e54e116179534059acd37c14819b6ddce71cffa0
--- /dev/null
+++ b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Repository.ts
@@ -0,0 +1,203 @@
+/*
+ * 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!
+ */
+
+import * as $ from 'jquery';
+import * as NProgress from 'nprogress';
+import Modal = require('TYPO3/CMS/Backend/Modal');
+import Notification = require('TYPO3/CMS/Backend/Notification');
+import Severity = require('TYPO3/CMS/Backend/Severity');
+import 'datatables';
+import 'TYPO3/CMS/Backend/jquery.clearable';
+
+class Repository {
+  public downloadPath: string = '';
+
+  public initDom = (): void => {
+    NProgress.configure({parent: '.module-loading-indicator', showSpinner: false});
+
+    $('#terTable').DataTable({
+      lengthChange: false,
+      pageLength: 15,
+      stateSave: false,
+      info: false,
+      paging: false,
+      searching: false,
+      ordering: false,
+      drawCallback: this.bindDownload,
+    });
+
+    $('#terVersionTable').DataTable({
+      lengthChange: false,
+      pageLength: 15,
+      stateSave: false,
+      info: false,
+      paging: false,
+      searching: false,
+      drawCallback: this.bindDownload,
+      order: [
+        [2, 'asc'],
+      ],
+      columns: [
+        {orderable: false},
+        null,
+        {type: 'version'},
+        null,
+        null,
+        null,
+      ],
+    });
+
+    $('#terSearchTable').DataTable({
+      paging: false,
+      lengthChange: false,
+      stateSave: false,
+      searching: false,
+      language: {
+        search: 'Filter results:',
+      },
+      ordering: false,
+      drawCallback: this.bindDownload,
+    });
+
+    this.bindDownload();
+    this.bindSearchFieldResetter();
+  }
+
+  private bindDownload = (): void => {
+    const installButtons = $('.downloadFromTer form.download button[type=submit]');
+    installButtons.off('click');
+    installButtons.on('click', (event: JQueryEventObject): void => {
+      event.preventDefault();
+      const $element: any = $(event.currentTarget);
+      const url = $($element.form).attr('data-href');
+      this.downloadPath = $($element.form).find('input.downloadPath:checked').val();
+      $.ajax({
+        url: url,
+        dataType: 'json',
+        beforeSend: (): void => {
+          NProgress.start();
+        },
+        success: this.getDependencies,
+      });
+    });
+  }
+
+  private getDependencies = (data: any): boolean => {
+    NProgress.done();
+    if (data.hasDependencies) {
+      Modal.confirm(data.title, $(data.message), Severity.info, [
+        {
+          text: TYPO3.lang['button.cancel'],
+          active: true,
+          btnClass: 'btn-default',
+          trigger: (): void => {
+            Modal.dismiss();
+          },
+        }, {
+          text: TYPO3.lang['button.resolveDependencies'],
+          btnClass: 'btn-info',
+          trigger: (): void => {
+            this.getResolveDependenciesAndInstallResult(data.url
+              + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + this.downloadPath);
+            Modal.dismiss();
+          },
+        },
+      ]);
+    } else {
+      if (data.hasErrors) {
+        Notification.error(data.title, data.message, 15);
+      } else {
+        this.getResolveDependenciesAndInstallResult(data.url
+          + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + this.downloadPath);
+      }
+    }
+    return false;
+  }
+
+  private getResolveDependenciesAndInstallResult = (url: string) => {
+    $.ajax({
+      url: url,
+      dataType: 'json',
+      beforeSend: (): void => {
+        NProgress.start();
+      },
+      success: (data: any): void => {
+        if (data.errorCount > 0) {
+          Modal.confirm(data.errorTitle, $(data.errorMessage), Severity.error, [
+            {
+              text: TYPO3.lang['button.cancel'],
+              active: true,
+              btnClass: 'btn-default',
+              trigger: (): void => {
+                Modal.dismiss();
+              },
+            }, {
+              text: TYPO3.lang['button.resolveDependenciesIgnore'],
+              btnClass: 'btn-danger disabled t3js-dependencies',
+              trigger: (e: JQueryEventObject): void => {
+                if (!$(e.currentTarget).hasClass('disabled')) {
+                  this.getResolveDependenciesAndInstallResult(data.skipDependencyUri);
+                  Modal.dismiss();
+                }
+              },
+            },
+          ]);
+          Modal.currentModal.on('shown.bs.modal', (): void => {
+            const $actionButton = Modal.currentModal.find('.t3js-dependencies');
+            $('input[name="unlockDependencyIgnoreButton"]', Modal.currentModal).on('change', (e: JQueryEventObject): void => {
+              $actionButton.toggleClass('disabled', !$(e.currentTarget).prop('checked'));
+            });
+          });
+        } else {
+          let successMessage = TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.message'
+          + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension);
+
+          successMessage += '\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.header'] + ': ';
+          $.each(data.result, (index: number, value: any): void => {
+            successMessage += '\n\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.item'] + ' ' + index + ': ';
+            $.each(value, (extkey: string): void => {
+              successMessage += '\n* ' + extkey;
+            });
+          });
+          Notification.info(
+            TYPO3.lang['extensionList.dependenciesResolveFlashMessage.title' + data.installationTypeLanguageKey]
+              .replace(/\{0\}/g, data.extension),
+            successMessage,
+            15,
+          );
+          top.TYPO3.ModuleMenu.App.refreshMenu();
+        }
+      },
+      complete: (): void => {
+        NProgress.done();
+      },
+    });
+  }
+
+  private bindSearchFieldResetter(): void {
+    const $searchFields = $('.typo3-extensionmanager-searchTerForm input[type="text"]');
+    const searchResultShown = ('' !== $searchFields.first().val());
+
+    $searchFields.clearable(
+      {
+        onClear: (e: JQueryEventObject): void => {
+          if (searchResultShown) {
+            $(e.currentTarget).closest('form').submit();
+          }
+        },
+      },
+    );
+  }
+}
+
+export = Repository;
diff --git a/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Update.ts b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Update.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e9c0569177e46a56d8aa2a6d77cbed144cda4a30
--- /dev/null
+++ b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/Update.ts
@@ -0,0 +1,129 @@
+/*
+ * 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!
+ */
+
+import * as $ from 'jquery';
+import * as NProgress from 'nprogress';
+import Notification = require('TYPO3/CMS/Backend/Notification');
+import 'datatables';
+import 'TYPO3/CMS/Backend/jquery.clearable';
+
+enum ExtensionManagerUpdateIdentifier {
+  extensionTable = '#terTable',
+  terUpdateAction = '.update-from-ter',
+  pagination = '.pagination-wrap',
+  splashscreen = '.splash-receivedata',
+  terTableDataTableWrapper = '#terTableWrapper .dataTables_wrapper',
+}
+
+class ExtensionManagerUpdate {
+  /**
+   * Register "update from ter" action
+   */
+  public initializeEvents(): void {
+    $(ExtensionManagerUpdateIdentifier.terUpdateAction).each((index: number, element: any): void => {
+      // "this" is the form which updates the extension list from
+      // TER on submit
+      const $me = $(element);
+      const updateURL = $me.attr('action');
+
+      $me.attr('action', '#');
+      $me.submit((): boolean => {
+        // Force update on click.
+        this.updateFromTer(updateURL, true);
+
+        // Prevent normal submit action.
+        return false;
+      });
+
+      // This might give problems when there are more "update"-buttons,
+      // each one would trigger a TER-this.
+      this.updateFromTer(updateURL, false);
+    });
+  }
+
+  private updateFromTer(url: string, forceUpdate: boolean): void {
+    if (forceUpdate) {
+      url = url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1';
+    }
+
+    // Hide triggers for TER update
+    $(ExtensionManagerUpdateIdentifier.terUpdateAction).addClass('is-hidden');
+
+    // Hide extension table
+    $(ExtensionManagerUpdateIdentifier.extensionTable).hide();
+
+    // Show loaders
+    $(ExtensionManagerUpdateIdentifier.splashscreen).addClass('is-shown');
+    $(ExtensionManagerUpdateIdentifier.terTableDataTableWrapper).addClass('is-loading');
+    $(ExtensionManagerUpdateIdentifier.pagination).addClass('is-loading');
+
+    let reload = false;
+
+    $.ajax({
+      url: url,
+      dataType: 'json',
+      cache: false,
+      beforeSend: (): void => {
+        NProgress.start();
+      },
+      success: (data: any): void => {
+        // Something went wrong, show message
+        if (data.errorMessage.length) {
+          Notification.error(TYPO3.lang['extensionList.updateFromTerFlashMessage.title'], data.errorMessage, 10);
+        }
+
+        // Message with latest updates
+        const $lastUpdate = $(ExtensionManagerUpdateIdentifier.terUpdateAction + ' .time-since-last-update');
+        $lastUpdate.text(data.timeSinceLastUpdate);
+        $lastUpdate.attr(
+          'title',
+          TYPO3.lang['extensionList.updateFromTer.lastUpdate.timeOfLastUpdate'] + data.lastUpdateTime,
+        );
+
+        if (data.updated) {
+          // Reload page
+          reload = true;
+          window.location.replace(window.location.href);
+        }
+      },
+      error: (jqXHR: JQueryXHR, textStatus: string, errorThrown: string): void => {
+        // Create an error message with diagnosis info.
+        const errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
+
+        Notification.warning(
+          TYPO3.lang['extensionList.updateFromTerFlashMessage.title'],
+          errorMessage,
+          10,
+        );
+      },
+      complete: (): void => {
+        NProgress.done();
+
+        if (!reload) {
+          // Hide loaders
+          $(ExtensionManagerUpdateIdentifier.splashscreen).removeClass('is-shown');
+          $(ExtensionManagerUpdateIdentifier.terTableDataTableWrapper).removeClass('is-loading');
+          $(ExtensionManagerUpdateIdentifier.pagination).removeClass('is-loading');
+
+          // Show triggers for TER-update
+          $(ExtensionManagerUpdateIdentifier.terUpdateAction).removeClass('is-hidden');
+
+          // Show extension table
+          $(ExtensionManagerUpdateIdentifier.extensionTable).show();
+        }
+      },
+    });
+  }
+}
+
+export = ExtensionManagerUpdate;
diff --git a/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/UploadForm.ts b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/UploadForm.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5c290cfeecfc71c2fb87ec8712b004415bb795c4
--- /dev/null
+++ b/Build/Sources/TypeScript/extensionmanager/Resources/Public/TypeScript/UploadForm.ts
@@ -0,0 +1,47 @@
+/*
+ * 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!
+ */
+
+import * as $ from 'jquery';
+import 'datatables';
+import 'TYPO3/CMS/Backend/jquery.clearable';
+
+class UploadForm {
+  public expandedUploadFormClass: string = 'transformed';
+
+  public initializeEvents(): void {
+    // Show upload form
+    $(document).on('click', '.t3js-upload', (event: JQueryEventObject): void => {
+      const $me = $(event.currentTarget);
+      const $uploadForm = $('.uploadForm');
+
+      event.preventDefault();
+      if ($me.hasClass(this.expandedUploadFormClass)) {
+        $uploadForm.stop().slideUp();
+        $me.removeClass(this.expandedUploadFormClass);
+      } else {
+        $me.addClass(this.expandedUploadFormClass);
+        $uploadForm.stop().slideDown();
+
+        $.ajax({
+          url: $me.attr('href'),
+          dataType: 'html',
+          success: (data: any): void => {
+            $uploadForm.html(data);
+          },
+        });
+      }
+    });
+  }
+}
+
+export = UploadForm;
diff --git a/Build/types/TYPO3/index.d.ts b/Build/types/TYPO3/index.d.ts
index 447b8df4390a49d1be4568336279577c38e21c6f..2e41db07977ced82f0c97823533757fe3126520d 100644
--- a/Build/types/TYPO3/index.d.ts
+++ b/Build/types/TYPO3/index.d.ts
@@ -7,6 +7,7 @@
 declare namespace TYPO3 {
   export let Backend: any;
   export let DebugConsole: any;
+  export let ExtensionManager: any;
   export let Icons: any;
   export let InfoWindow: any;
   export let LoginRefresh: any;
@@ -129,6 +130,12 @@ interface JQueryTypedEvent<T extends Event> extends JQueryEventObject {
  * Required to make jQuery plugins "available" in TypeScript
  */
 interface JQuery {
+  dataTableExt: any;
+
+  search(term?: string): JQuery;
+
+  draw(): JQuery;
+
   clearable(options?: any): JQuery;
 
   datetimepicker(options?: any): JQuery;
diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Main.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Main.js
index a8c96c397b8fbe5266444bb74aa088a386beaa6b..022f57162536be93324f2892137a358a99c50482 100644
--- a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Main.js
+++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Main.js
@@ -10,746 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-/**
- * Module: TYPO3/CMS/Extensionmanager/Main
- * main logic holding everything together, consists of multiple parts
- * ExtensionManager => Various functions for displaying the extension list / sorting
- * Repository => Various AJAX functions for TER downloads
- * ExtensionManager.Update => Various AJAX functions to display updates
- * ExtensionManager.uploadForm => helper to show the upload form
- */
-define([
-  'jquery',
-  'nprogress',
-  'TYPO3/CMS/Backend/Modal',
-  'TYPO3/CMS/Backend/SplitButtons',
-  'TYPO3/CMS/Backend/Tooltip',
-  'TYPO3/CMS/Backend/Notification',
-  'TYPO3/CMS/Backend/Severity',
-  'TYPO3/CMS/Core/SecurityUtility',
-  'datatables',
-  'TYPO3/CMS/Backend/jquery.clearable'
-], function($, NProgress, Modal, SplitButtons, Tooltip, Notification, Severity, SecurityUtility) {
-
-  var securityUtility = new SecurityUtility();
-
-  /**
-   *
-   * @type {{identifier: {extensionlist: string, searchField: string, extensionManager: string}}}
-   * @exports TYPO3/CMS/Extensionmanager/Main
-   */
-  var ExtensionManager = {
-    identifier: {
-      extensionlist: '#typo3-extension-list',
-      searchField: '#Tx_Extensionmanager_extensionkey'
-    }
-  };
-
-  /**
-   *
-   * @returns {Object}
-   */
-  ExtensionManager.manageExtensionListing = function() {
-    var $searchField = $(this.identifier.searchField),
-      dataTable = $(this.identifier.extensionlist).DataTable({
-        paging: false,
-        dom: 'lrtip',
-        lengthChange: false,
-        pageLength: 15,
-        stateSave: true,
-        drawCallback: this.bindExtensionListActions,
-        columns: [
-          null,
-          null,
-          {
-            type: 'extension'
-          },
-          null,
-          {
-            type: 'version'
-          }, {
-            orderable: false
-          },
-          null,
-          null
-        ]
-      });
-
-    $searchField.parents('form').on('submit', function() {
-      return false;
-    });
-
-    var getVars = ExtensionManager.getUrlVars();
-
-    // restore filter
-    var currentSearch = (getVars['search'] ? getVars['search'] : dataTable.search());
-    $searchField.val(currentSearch);
-
-    $searchField.on('input', function(e) {
-      dataTable.search($(this).val()).draw();
-    });
-
-    return dataTable;
-  };
-
-  /**
-   *
-   */
-  ExtensionManager.bindExtensionListActions = function() {
-    $('.removeExtension').not('.transformed').each(function() {
-      var $me = $(this);
-      $me.data('href', $me.attr('href'));
-      $me.attr('href', '#');
-      $me.addClass('transformed');
-      $me.click(function() {
-        Modal.confirm(
-          TYPO3.lang['extensionList.removalConfirmation.title'],
-          TYPO3.lang['extensionList.removalConfirmation.question'],
-          Severity.error,
-          [
-            {
-              text: TYPO3.lang['button.cancel'],
-              active: true,
-              btnClass: 'btn-default',
-              trigger: function() {
-                Modal.dismiss();
-              }
-            }, {
-            text: TYPO3.lang['button.remove'],
-            btnClass: 'btn-danger',
-            trigger: function() {
-              ExtensionManager.removeExtensionFromDisk($me);
-              Modal.dismiss();
-            }
-          }
-          ]
-        );
-      });
-    });
-  };
-
-  /**
-   *
-   * @param {Object} $extension
-   */
-  ExtensionManager.removeExtensionFromDisk = function($extension) {
-    $.ajax({
-      url: $extension.data('href'),
-      beforeSend: function() {
-        NProgress.start();
-      },
-      success: function() {
-        location.reload();
-      },
-      complete: function() {
-        NProgress.done();
-      }
-    });
-  };
-
-  /**
-   *
-   * @returns {Array}
-   */
-  ExtensionManager.getUrlVars = function() {
-    var vars = [], hash;
-    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
-    for (var i = 0; i < hashes.length; i++) {
-      hash = hashes[i].split('=');
-      vars.push(hash[0]);
-      vars[hash[0]] = hash[1];
-    }
-    return vars;
-  };
-
-  $.fn.dataTableExt.oSort['extension-asc'] = function(a, b) {
-    return ExtensionManager.extensionCompare(a, b);
-  };
-
-  $.fn.dataTableExt.oSort['extension-desc'] = function(a, b) {
-    var result = ExtensionManager.extensionCompare(a, b);
-    return result * -1;
-  };
-
-  $.fn.dataTableExt.oSort['version-asc'] = function(a, b) {
-    var result = ExtensionManager.versionCompare(a, b);
-    return result * -1;
-  };
-
-  $.fn.dataTableExt.oSort['version-desc'] = function(a, b) {
-    return ExtensionManager.versionCompare(a, b);
-  };
-
-  /**
-   * Special sorting for the extension version column
-   *
-   * @param {String} a
-   * @param {String} b
-   * @returns {Number}
-   */
-  ExtensionManager.versionCompare = function(a, b) {
-    if (a === b) {
-      return 0;
-    }
-
-    var a_components = a.split(".");
-    var b_components = b.split(".");
-
-    var len = Math.min(a_components.length, b_components.length);
-
-    // loop while the components are equal
-    for (var i = 0; i < len; i++) {
-      // A bigger than B
-      if (parseInt(a_components[i]) > parseInt(b_components[i])) {
-        return 1;
-      }
-
-      // B bigger than A
-      if (parseInt(a_components[i]) < parseInt(b_components[i])) {
-        return -1;
-      }
-    }
-
-    // If one's a prefix of the other, the longer one is greaRepository.
-    if (a_components.length > b_components.length) {
-      return 1;
-    }
-
-    if (a_components.length < b_components.length) {
-      return -1;
-    }
-    // Otherwise they are the same.
-    return 0;
-  };
-
-  /**
-   * The extension name column can contain various forms of HTML that
-   * break a direct comparison of values
-   *
-   * @param {String} a
-   * @param {String} b
-   * @returns {Number}
-   */
-  ExtensionManager.extensionCompare = function(a, b) {
-    var div = document.createElement("div");
-    div.innerHTML = a;
-    var aStr = div.textContent || div.innerText || a;
-
-    div.innerHTML = b;
-    var bStr = div.textContent || div.innerText || b;
-
-    return aStr.trim().localeCompare(bStr.trim());
-  }
-
-  /**
-   *
-   * @param {Object} data
-   */
-  ExtensionManager.updateExtension = function(data) {
-    var i = 0;
-    var $form = $('<form>');
-    $.each(data.updateComments, function(version, comment) {
-      var $input = $('<input>').attr({type: 'radio', name: 'version'}).val(version);
-      if (i === 0) {
-        $input.attr('checked', 'checked');
-      }
-      $form.append([
-        $('<h3>').append([
-          $input,
-          ' ' + securityUtility.encodeHtml(version)
-        ]),
-        $('<div>')
-          .append(
-            comment
-              .replace(/(\r\n|\n\r|\r|\n)/g, '\n')
-              .split(/\n/).map(function(line) {
-                return securityUtility.encodeHtml(line);
-              })
-              .join('<br>')
-          )
-      ]);
-      i++;
-    });
-    var $container = $('<div>').append([
-      $('<h1>').text(TYPO3.lang['extensionList.updateConfirmation.title']),
-      $('<h2>').text(TYPO3.lang['extensionList.updateConfirmation.message']),
-      $form
-    ]);
-
-    NProgress.done();
-
-    Modal.confirm(
-      TYPO3.lang['extensionList.updateConfirmation.questionVersionComments'],
-      $container,
-      Severity.warning,
-      [
-        {
-          text: TYPO3.lang['button.cancel'],
-          active: true,
-          btnClass: 'btn-default',
-          trigger: function() {
-            Modal.dismiss();
-          }
-        }, {
-        text: TYPO3.lang['button.updateExtension'],
-        btnClass: 'btn-warning',
-        trigger: function() {
-          $.ajax({
-            url: data.url,
-            data: {
-              tx_extensionmanager_tools_extensionmanagerextensionmanager: {
-                version: $('input:radio[name=version]:checked', Modal.currentModal).val()
-              }
-            },
-            dataType: 'json',
-            beforeSend: function() {
-              NProgress.start();
-            },
-            complete: function() {
-              location.reload();
-            }
-          });
-          Modal.dismiss();
-        }
-      }
-      ]
-    );
-  };
-
-  /**
-   *
-   * @type {{downloadPath: string}}
-   */
-  var Repository = {
-    downloadPath: ''
-  };
-
-  /**
-   *
-   */
-  Repository.initDom = function() {
-    NProgress.configure({parent: '.module-loading-indicator', showSpinner: false});
-
-    $('#terTable').DataTable({
-      lengthChange: false,
-      pageLength: 15,
-      stateSave: false,
-      info: false,
-      paging: false,
-      searching: false,
-      ordering: false,
-      drawCallback: Repository.bindDownload
-    });
-
-    $('#terVersionTable').DataTable({
-      lengthChange: false,
-      pageLength: 15,
-      stateSave: false,
-      info: false,
-      paging: false,
-      searching: false,
-      drawCallback: Repository.bindDownload,
-      order: [
-        [2, 'asc']
-      ],
-      columns: [
-        {orderable: false},
-        null,
-        {type: 'version'},
-        null,
-        null,
-        null
-      ]
-    });
-
-    $('#terSearchTable').DataTable({
-      paging: false,
-      lengthChange: false,
-      stateSave: false,
-      searching: false,
-      language: {
-        search: 'Filter results:'
-      },
-      ordering: false,
-      drawCallback: Repository.bindDownload
-    });
-
-    Repository.bindDownload();
-    Repository.bindSearchFieldResetter();
-  };
-
-  /**
-   *
-   */
-  Repository.bindDownload = function() {
-    var installButtons = $('.downloadFromTer form.download button[type=submit]');
-    installButtons.off('click');
-    installButtons.on('click', function(event) {
-      event.preventDefault();
-      var url = $(event.currentTarget.form).attr('data-href');
-      Repository.downloadPath = $(event.currentTarget.form).find('input.downloadPath:checked').val();
-      $.ajax({
-        url: url,
-        dataType: 'json',
-        beforeSend: function() {
-          NProgress.start();
-        },
-        success: Repository.getDependencies
-      });
-    });
-  };
-
-  /**
-   *
-   * @param {Object} data
-   * @returns {Boolean}
-   */
-  Repository.getDependencies = function(data) {
-    NProgress.done();
-    if (data.hasDependencies) {
-      Modal.confirm(data.title, $(data.message), Severity.info, [
-        {
-          text: TYPO3.lang['button.cancel'],
-          active: true,
-          btnClass: 'btn-default',
-          trigger: function() {
-            Modal.dismiss();
-          }
-        }, {
-          text: TYPO3.lang['button.resolveDependencies'],
-          btnClass: 'btn-info',
-          trigger: function() {
-            Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
-            Modal.dismiss();
-          }
-        }
-      ]);
-    } else {
-      if (data.hasErrors) {
-        Notification.error(data.title, data.message, 15);
-      } else {
-        Repository.getResolveDependenciesAndInstallResult(data.url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]=' + Repository.downloadPath);
-      }
-    }
-    return false;
-  };
-
-  /**
-   *
-   * @param {String} url
-   */
-  Repository.getResolveDependenciesAndInstallResult = function(url) {
-    $.ajax({
-      url: url,
-      dataType: 'json',
-      beforeSend: function() {
-        NProgress.start();
-      },
-      success: function(data) {
-        if (data.errorCount > 0) {
-          Modal.confirm(data.errorTitle, $(data.errorMessage), Severity.error, [
-            {
-              text: TYPO3.lang['button.cancel'],
-              active: true,
-              btnClass: 'btn-default',
-              trigger: function() {
-                Modal.dismiss();
-              }
-            }, {
-              text: TYPO3.lang['button.resolveDependenciesIgnore'],
-              btnClass: 'btn-danger disabled t3js-dependencies',
-              trigger: function() {
-                if (!$(this).hasClass('disabled')) {
-                  Repository.getResolveDependenciesAndInstallResult(data.skipDependencyUri);
-                  Modal.dismiss();
-                }
-              }
-            }
-          ]);
-          Modal.currentModal.on('shown.bs.modal', function() {
-            var $actionButton = Modal.currentModal.find('.t3js-dependencies');
-            $('input[name="unlockDependencyIgnoreButton"]', Modal.currentModal).on('change', function() {
-              $actionButton.toggleClass('disabled', !$(this).prop('checked'));
-            });
-          });
-        } else {
-          var successMessage = TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.message' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension);
-
-          successMessage += '\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.header'] + ': ';
-          $.each(data.result, function(index, value) {
-            successMessage += '\n\n' + TYPO3.lang['extensionList.dependenciesResolveDownloadSuccess.item'] + ' ' + index + ': ';
-            $.each(value, function(extkey) {
-              successMessage += '\n* ' + extkey
-            });
-          });
-          Notification.info(TYPO3.lang['extensionList.dependenciesResolveFlashMessage.title' + data.installationTypeLanguageKey].replace(/\{0\}/g, data.extension), successMessage, 15);
-          top.TYPO3.ModuleMenu.App.refreshMenu();
-        }
-      },
-      complete: function() {
-        NProgress.done();
-      }
-    });
-  };
-
-  /**
-   *
-   */
-  Repository.bindSearchFieldResetter = function() {
-    var $searchFields = $('.typo3-extensionmanager-searchTerForm input[type="text"]');
-    var searchResultShown = ('' !== $searchFields.first().val());
-
-    $searchFields.clearable(
-      {
-        onClear: function() {
-          if (searchResultShown) {
-            $(this).closest('form').submit();
-          }
-        }
-      }
-    );
-  };
-
-  /**
-   *
-   * @type {{identifier: {extensionTable: string, terUpdateAction: string, pagination: string, splashscreen: string, terTableWrapper: string, terTableDataTableWrapper: string}}}
-   */
-  ExtensionManager.Update = {
-    identifier: {
-      extensionTable: '#terTable',
-      terUpdateAction: '.update-from-ter',
-      pagination: '.pagination-wrap',
-      splashscreen: '.splash-receivedata',
-      terTableWrapper: '#terTableWrapper',
-      terTableDataTableWrapper: '#terTableWrapper .dataTables_wrapper'
-    }
-  };
-
-  /**
-   * Register "update from ter" action
-   */
-  ExtensionManager.Update.initializeEvents = function() {
-    $(ExtensionManager.Update.identifier.terUpdateAction).each(function() {
-
-      // "this" is the form which updates the extension list from
-      // TER on submit
-      var $me = $(this),
-        updateURL = $(this).attr('action');
-
-      $me.attr('action', '#');
-      $me.submit(function() {
-        // Force update on click.
-        ExtensionManager.Update.updateFromTer(updateURL, true);
-
-        // Prevent normal submit action.
-        return false;
-      });
-
-      // This might give problems when there are more "update"-buttons,
-      // each one would trigger a TER-ExtensionManager.Update.
-      ExtensionManager.Update.updateFromTer(updateURL, false);
-    });
-  };
-
-  /**
-   *
-   * @param {String} url
-   * @param {Boolean} forceUpdate
-   */
-  ExtensionManager.Update.updateFromTer = function(url, forceUpdate) {
-    if (forceUpdate) {
-      url = url + '&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1';
-    }
-
-    // Hide triggers for TER update
-    $(ExtensionManager.Update.identifier.terUpdateAction).addClass('is-hidden');
-
-    // Hide extension table
-    $(ExtensionManager.Update.identifier.extensionTable).hide();
-
-    // Show loaders
-    $(ExtensionManager.Update.identifier.splashscreen).addClass('is-shown');
-    $(ExtensionManager.Update.identifier.terTableDataTableWrapper).addClass('is-loading');
-    $(ExtensionManager.Update.identifier.pagination).addClass('is-loading');
-
-    var reload = false;
-
-    $.ajax({
-      url: url,
-      dataType: 'json',
-      cache: false,
-      beforeSend: function() {
-        NProgress.start();
-      },
-      success: function(data) {
-        // Something went wrong, show message
-        if (data.errorMessage.length) {
-          Notification.error(TYPO3.lang['extensionList.updateFromTerFlashMessage.title'], data.errorMessage, 10);
-        }
-
-        // Message with latest updates
-        var $lastUpdate = $(ExtensionManager.Update.identifier.terUpdateAction + ' .time-since-last-update');
-        $lastUpdate.text(data.timeSinceLastUpdate);
-        $lastUpdate.attr(
-          'title',
-          TYPO3.lang['extensionList.updateFromTer.lastUpdate.timeOfLastUpdate'] + data.lastUpdateTime
-        );
-
-        if (data.updated) {
-          // Reload page
-          reload = true;
-          window.location.replace(window.location.href);
-        }
-      },
-      error: function(jqXHR, textStatus, errorThrown) {
-        // Create an error message with diagnosis info.
-        var errorMessage = textStatus + '(' + errorThrown + '): ' + jqXHR.responseText;
-
-        Notification.warning(
-          TYPO3.lang['extensionList.updateFromTerFlashMessage.title'],
-          errorMessage,
-          10
-        );
-      },
-      complete: function() {
-        NProgress.done();
-
-        if (!reload) {
-          // Hide loaders
-          $(ExtensionManager.Update.identifier.splashscreen).removeClass('is-shown');
-          $(ExtensionManager.Update.identifier.terTableDataTableWrapper).removeClass('is-loading');
-          $(ExtensionManager.Update.identifier.pagination).removeClass('is-loading');
-
-          // Show triggers for TER-update
-          $(ExtensionManager.Update.identifier.terUpdateAction).removeClass('is-hidden');
-
-          // Show extension table
-          $(ExtensionManager.Update.identifier.extensionTable).show();
-        }
-      }
-    });
-  };
-
-  /**
-   *
-   */
-  ExtensionManager.Update.transformPaginatorToAjax = function() {
-    $(ExtensionManager.Update.identifier.pagination + ' a').each(function() {
-      var $me = $(this);
-      $me.data('href', $(this).attr('href'));
-      $me.attr('href', '#');
-      $me.click(function() {
-        var $terTableWrapper = $(ExtensionManager.Update.identifier.terTableWrapper);
-        NProgress.start();
-        $.ajax({
-          url: $(this).data('href'),
-          dataType: 'json',
-          success: function(data) {
-            $terTableWrapper.html(data);
-            ExtensionManager.Update.transformPaginatorToAjax();
-          },
-          complete: function() {
-            NProgress.done();
-          }
-        });
-      });
-    });
-  };
-
-  /**
-   * show the uploading form
-   */
-  ExtensionManager.UploadForm = {
-    expandedUploadFormClass: 'transformed'
-  };
-
-  /**
-   *
-   */
-  ExtensionManager.UploadForm.initializeEvents = function() {
-    // Show upload form
-    $(document).on('click', '.t3js-upload', function(event) {
-      var $me = $(this),
-        $uploadForm = $('.uploadForm');
-
-      event.preventDefault();
-      if ($me.hasClass(ExtensionManager.UploadForm.expandedUploadFormClass)) {
-        $uploadForm.stop().slideUp();
-        $me.removeClass(ExtensionManager.UploadForm.expandedUploadFormClass);
-      } else {
-        $me.addClass(ExtensionManager.UploadForm.expandedUploadFormClass);
-        $uploadForm.stop().slideDown();
-
-        $.ajax({
-          url: $me.attr('href'),
-          dataType: 'html',
-          success: function(data) {
-            $uploadForm.html(data);
-          }
-        });
-      }
-    });
-  };
-
-  $(function() {
-    var dataTable = ExtensionManager.manageExtensionListing();
-
-    $(document).on('click', '.onClickMaskExtensionManager', function() {
-      NProgress.start();
-    }).on('click', 'a[data-action=update-extension]', function(e) {
-      e.preventDefault();
-      $.ajax({
-        url: $(this).attr('href'),
-        dataType: 'json',
-        beforeSend: function() {
-          NProgress.start();
-        },
-        success: ExtensionManager.updateExtension
-      });
-    }).on('change', 'input[name=unlockDependencyIgnoreButton]', function() {
-      var $actionButton = $('.t3js-dependencies');
-      $actionButton.toggleClass('disabled', !$(this).prop('checked'));
-    });
-
-    $(ExtensionManager.identifier.searchField).clearable({
-      onClear: function() {
-        dataTable.search('').draw();
-      }
-    });
-
-    $(document).on('click', '.t3-button-action-installdistribution', function() {
-      NProgress.start();
-    });
-
-    SplitButtons.addPreSubmitCallback(function(e) {
-      if ($(e.target).hasClass('t3js-save-close')) {
-        $('#configurationform').append($('<input />', {
-          type: 'hidden',
-          name: 'tx_extensionmanager_tools_extensionmanagerextensionmanager[action]',
-          value: 'saveAndClose'
-        }));
-      }
-    });
-
-    // initialize the repository
-    Repository.initDom();
-
-    ExtensionManager.Update.initializeEvents();
-    ExtensionManager.UploadForm.initializeEvents();
-
-    Tooltip.initialize('#typo3-extension-list [title]', {
-      delay: {
-        show: 500,
-        hide: 100
-      },
-      trigger: 'hover',
-      container: 'body'
-    });
-  });
-
-  if (typeof TYPO3.ExtensionManager === 'undefined') {
-    TYPO3.ExtensionManager = ExtensionManager;
-  }
-
-  return ExtensionManager;
-});
+define(["require","exports","jquery","nprogress","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/SplitButtons","TYPO3/CMS/Backend/Tooltip","TYPO3/CMS/Backend/Severity","TYPO3/CMS/Core/SecurityUtility","./Repository","./Update","./UploadForm","datatables","TYPO3/CMS/Backend/jquery.clearable"],function(n,e,t,a,o,i,r,s,l,c,u,d){"use strict";var p,f,m=new l;(f=p||(p={})).extensionlist="#typo3-extension-list",f.searchField="#Tx_Extensionmanager_extensionkey";var g=new(function(){function n(){var e=this;this.bindExtensionListActions=function(){t(".removeExtension").not(".transformed").each(function(n,a){var i=t(a);i.data("href",i.attr("href")),i.attr("href","#"),i.addClass("transformed"),i.click(function(){o.confirm(TYPO3.lang["extensionList.removalConfirmation.title"],TYPO3.lang["extensionList.removalConfirmation.question"],s.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:function(){o.dismiss()}},{text:TYPO3.lang["button.remove"],btnClass:"btn-danger",trigger:function(){e.removeExtensionFromDisk(i),o.dismiss()}}])})})},t(function(){t.fn.dataTableExt.oSort["extension-asc"]=function(e,t){return n.extensionCompare(e,t)},t.fn.dataTableExt.oSort["extension-desc"]=function(e,t){return-1*n.extensionCompare(e,t)},t.fn.dataTableExt.oSort["version-asc"]=function(e,t){return-1*n.versionCompare(e,t)},t.fn.dataTableExt.oSort["version-desc"]=function(e,t){return n.versionCompare(e,t)},e.Update=new u,e.UploadForm=new d,e.Repository=new c;var o=e.manageExtensionListing();t(document).on("click",".onClickMaskExtensionManager",function(){a.start()}).on("click","a[data-action=update-extension]",function(n){n.preventDefault(),t.ajax({url:t(e).attr("href"),dataType:"json",beforeSend:function(){a.start()},success:e.updateExtension})}).on("change","input[name=unlockDependencyIgnoreButton]",function(n){t(".t3js-dependencies").toggleClass("disabled",!t(n.currentTarget).prop("checked"))}),t(p.searchField).clearable({onClear:function(){o.search("").draw()}}),t(document).on("click",".t3-button-action-installdistribution",function(){a.start()}),i.addPreSubmitCallback(function(n){t(n.target).hasClass("t3js-save-close")&&t("#configurationform").append(t("<input />",{type:"hidden",name:"tx_extensionmanager_tools_extensionmanagerextensionmanager[action]",value:"saveAndClose"}))}),e.Repository.initDom(),e.Update.initializeEvents(),e.UploadForm.initializeEvents(),r.initialize("#typo3-extension-list [title]",{delay:{show:500,hide:100},trigger:"hover",container:"body"})})}return n.prototype.manageExtensionListing=function(){var e=t(p.searchField),a=t(p.extensionlist).DataTable({paging:!1,dom:"lrtip",lengthChange:!1,pageLength:15,stateSave:!0,drawCallback:this.bindExtensionListActions,columns:[null,null,{type:"extension"},null,{type:"version"},{orderable:!1},null,null]});e.parents("form").on("submit",function(){return!1});var o=n.getUrlVars(),i=o.search?o.search:a.search();return e.val(i),e.on("input",function(n){a.search(t(n.currentTarget).val()).draw()}),a},n.prototype.removeExtensionFromDisk=function(n){t.ajax({url:n.data("href"),beforeSend:function(){a.start()},success:function(){location.reload()},complete:function(){a.done()}})},n.getUrlVars=function(){for(var n,e=[],t=window.location.href.slice(window.location.href.indexOf("?")+1).split("&"),a=0;a<t.length;a++)n=t[a].split("="),e.push(n[0]),e[n[0]]=n[1];return e},n.versionCompare=function(n,e){if(n===e)return 0;for(var t=n.split("."),a=e.split("."),o=Math.min(t.length,a.length),i=0;i<o;i++){if(parseInt(t[i],10)>parseInt(a[i],10))return 1;if(parseInt(t[i],10)<parseInt(a[i],10))return-1}return t.length>a.length?1:t.length<a.length?-1:0},n.extensionCompare=function(n,e){var t=document.createElement("div");t.innerHTML=n;var a=t.textContent||t.innerText||n;t.innerHTML=e;var o=t.textContent||t.innerText||e;return a.trim().localeCompare(o.trim())},n.prototype.updateExtension=function(n){var e=0,i=t("<form>");t.each(n.updateComments,function(n,a){var o=t("<input>").attr({type:"radio",name:"version"}).val(n);0===e&&o.attr("checked","checked"),i.append([t("<h3>").append([o," "+m.encodeHtml(n)]),t("<div>").append(a.replace(/(\r\n|\n\r|\r|\n)/g,"\n").split(/\n/).map(function(n){return m.encodeHtml(n)}).join("<br>"))]),e++});var r=t("<div>").append([t("<h1>").text(TYPO3.lang["extensionList.updateConfirmation.title"]),t("<h2>").text(TYPO3.lang["extensionList.updateConfirmation.message"]),i]);a.done(),o.confirm(TYPO3.lang["extensionList.updateConfirmation.questionVersionComments"],r,s.warning,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:function(){o.dismiss()}},{text:TYPO3.lang["button.updateExtension"],btnClass:"btn-warning",trigger:function(){t.ajax({url:n.url,data:{tx_extensionmanager_tools_extensionmanagerextensionmanager:{version:t("input:radio[name=version]:checked",o.currentModal).val()}},dataType:"json",beforeSend:function(){a.start()},complete:function(){location.reload()}}),o.dismiss()}}])},n}());return void 0===TYPO3.ExtensionManager&&(TYPO3.ExtensionManager=g),g});
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Repository.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Repository.js
new file mode 100644
index 0000000000000000000000000000000000000000..7a55562ea86f1a06646968efac6f67dccbd9f4c2
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Repository.js
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","nprogress","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","TYPO3/CMS/Backend/Severity","datatables","TYPO3/CMS/Backend/jquery.clearable"],function(e,n,t,a,o,s,r){"use strict";return function(){function e(){var e=this;this.downloadPath="",this.initDom=function(){a.configure({parent:".module-loading-indicator",showSpinner:!1}),t("#terTable").DataTable({lengthChange:!1,pageLength:15,stateSave:!1,info:!1,paging:!1,searching:!1,ordering:!1,drawCallback:e.bindDownload}),t("#terVersionTable").DataTable({lengthChange:!1,pageLength:15,stateSave:!1,info:!1,paging:!1,searching:!1,drawCallback:e.bindDownload,order:[[2,"asc"]],columns:[{orderable:!1},null,{type:"version"},null,null,null]}),t("#terSearchTable").DataTable({paging:!1,lengthChange:!1,stateSave:!1,searching:!1,language:{search:"Filter results:"},ordering:!1,drawCallback:e.bindDownload}),e.bindDownload(),e.bindSearchFieldResetter()},this.bindDownload=function(){var n=t(".downloadFromTer form.download button[type=submit]");n.off("click"),n.on("click",function(n){n.preventDefault();var o=t(n.currentTarget),s=t(o.form).attr("data-href");e.downloadPath=t(o.form).find("input.downloadPath:checked").val(),t.ajax({url:s,dataType:"json",beforeSend:function(){a.start()},success:e.getDependencies})})},this.getDependencies=function(n){return a.done(),n.hasDependencies?o.confirm(n.title,t(n.message),r.info,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:function(){o.dismiss()}},{text:TYPO3.lang["button.resolveDependencies"],btnClass:"btn-info",trigger:function(){e.getResolveDependenciesAndInstallResult(n.url+"&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]="+e.downloadPath),o.dismiss()}}]):n.hasErrors?s.error(n.title,n.message,15):e.getResolveDependenciesAndInstallResult(n.url+"&tx_extensionmanager_tools_extensionmanagerextensionmanager[downloadPath]="+e.downloadPath),!1},this.getResolveDependenciesAndInstallResult=function(n){t.ajax({url:n,dataType:"json",beforeSend:function(){a.start()},success:function(n){if(n.errorCount>0)o.confirm(n.errorTitle,t(n.errorMessage),r.error,[{text:TYPO3.lang["button.cancel"],active:!0,btnClass:"btn-default",trigger:function(){o.dismiss()}},{text:TYPO3.lang["button.resolveDependenciesIgnore"],btnClass:"btn-danger disabled t3js-dependencies",trigger:function(a){t(a.currentTarget).hasClass("disabled")||(e.getResolveDependenciesAndInstallResult(n.skipDependencyUri),o.dismiss())}}]),o.currentModal.on("shown.bs.modal",function(){var e=o.currentModal.find(".t3js-dependencies");t('input[name="unlockDependencyIgnoreButton"]',o.currentModal).on("change",function(n){e.toggleClass("disabled",!t(n.currentTarget).prop("checked"))})});else{var a=TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.message"+n.installationTypeLanguageKey].replace(/\{0\}/g,n.extension);a+="\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.header"]+": ",t.each(n.result,function(e,n){a+="\n\n"+TYPO3.lang["extensionList.dependenciesResolveDownloadSuccess.item"]+" "+e+": ",t.each(n,function(e){a+="\n* "+e})}),s.info(TYPO3.lang["extensionList.dependenciesResolveFlashMessage.title"+n.installationTypeLanguageKey].replace(/\{0\}/g,n.extension),a,15),top.TYPO3.ModuleMenu.App.refreshMenu()}},complete:function(){a.done()}})}}return e.prototype.bindSearchFieldResetter=function(){var e=t('.typo3-extensionmanager-searchTerForm input[type="text"]'),n=""!==e.first().val();e.clearable({onClear:function(e){n&&t(e.currentTarget).closest("form").submit()}})},e}()});
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Update.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Update.js
new file mode 100644
index 0000000000000000000000000000000000000000..cd563edb4a8606a2381a1d03f7ed8ef6f50e6a00
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/Update.js
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","nprogress","TYPO3/CMS/Backend/Notification","datatables","TYPO3/CMS/Backend/jquery.clearable"],function(e,a,t,n,r){"use strict";var s,i;return(i=s||(s={})).extensionTable="#terTable",i.terUpdateAction=".update-from-ter",i.pagination=".pagination-wrap",i.splashscreen=".splash-receivedata",i.terTableDataTableWrapper="#terTableWrapper .dataTables_wrapper",function(){function e(){}return e.prototype.initializeEvents=function(){var e=this;t(s.terUpdateAction).each(function(a,n){var r=t(n),s=r.attr("action");r.attr("action","#"),r.submit(function(){return e.updateFromTer(s,!0),!1}),e.updateFromTer(s,!1)})},e.prototype.updateFromTer=function(e,a){a&&(e+="&tx_extensionmanager_tools_extensionmanagerextensionmanager%5BforceUpdateCheck%5D=1"),t(s.terUpdateAction).addClass("is-hidden"),t(s.extensionTable).hide(),t(s.splashscreen).addClass("is-shown"),t(s.terTableDataTableWrapper).addClass("is-loading"),t(s.pagination).addClass("is-loading");var i=!1;t.ajax({url:e,dataType:"json",cache:!1,beforeSend:function(){n.start()},success:function(e){e.errorMessage.length&&r.error(TYPO3.lang["extensionList.updateFromTerFlashMessage.title"],e.errorMessage,10);var a=t(s.terUpdateAction+" .time-since-last-update");a.text(e.timeSinceLastUpdate),a.attr("title",TYPO3.lang["extensionList.updateFromTer.lastUpdate.timeOfLastUpdate"]+e.lastUpdateTime),e.updated&&(i=!0,window.location.replace(window.location.href))},error:function(e,a,t){var n=a+"("+t+"): "+e.responseText;r.warning(TYPO3.lang["extensionList.updateFromTerFlashMessage.title"],n,10)},complete:function(){n.done(),i||(t(s.splashscreen).removeClass("is-shown"),t(s.terTableDataTableWrapper).removeClass("is-loading"),t(s.pagination).removeClass("is-loading"),t(s.terUpdateAction).removeClass("is-hidden"),t(s.extensionTable).show())}})},e}()});
\ No newline at end of file
diff --git a/typo3/sysext/extensionmanager/Resources/Public/JavaScript/UploadForm.js b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/UploadForm.js
new file mode 100644
index 0000000000000000000000000000000000000000..aa06ea689ba99381a04bbab6ff77489a1a56914c
--- /dev/null
+++ b/typo3/sysext/extensionmanager/Resources/Public/JavaScript/UploadForm.js
@@ -0,0 +1,13 @@
+/*
+ * 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!
+ */
+define(["require","exports","jquery","datatables","TYPO3/CMS/Backend/jquery.clearable"],function(e,a,t){"use strict";return function(){function e(){this.expandedUploadFormClass="transformed"}return e.prototype.initializeEvents=function(){var e=this;t(document).on("click",".t3js-upload",function(a){var s=t(a.currentTarget),r=t(".uploadForm");a.preventDefault(),s.hasClass(e.expandedUploadFormClass)?(r.stop().slideUp(),s.removeClass(e.expandedUploadFormClass)):(s.addClass(e.expandedUploadFormClass),r.stop().slideDown(),t.ajax({url:s.attr("href"),dataType:"html",success:function(e){r.html(e)}}))})},e}()});
\ No newline at end of file