diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Ajax/AjaxQueue.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Ajax/AjaxQueue.ts
index 8a31afa7ca11c3b1f427e6fdd6551d88ba3470fb..92e2fbd9ec129da20a76b9256968690b470446a7 100644
--- a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Ajax/AjaxQueue.ts
+++ b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Ajax/AjaxQueue.ts
@@ -26,33 +26,35 @@ interface Payload {
  * Module: TYPO3/CMS/Install/Module/AjaxQueue
  */
 class AjaxQueue {
+  private requests: Array<AjaxRequest> = [];
   private requestCount: number = 0;
-  private threshold: number = 10;
+  private threshold: number = 5;
   private queue: Array<Payload> = [];
 
-  public async add(payload: Payload): Promise<any> {
-    const oldFinally = payload.finally;
-    if (this.queue.length > 0 && this.requestCount <= this.threshold) {
-      this.sendRequest(this.queue.shift()).finally((): void => {
-        this.decrementRequestCount();
-      });
-    } else {
-      this.decrementRequestCount();
-    }
+  public add(payload: Payload): void {
+    this.queue.push(payload);
+    this.handleNext();
+  }
 
-    if (oldFinally) {
-      oldFinally(...arguments);
-    }
+  public flush(): void {
+    this.queue = [];
+    this.requests.map((request: AjaxRequest): void => {
+      request.abort();
+    });
+    this.requests = [];
+  }
 
-    if (this.requestCount >= this.threshold) {
-      this.queue.push(payload);
-    } else {
+  private handleNext(): void {
+    if (this.queue.length > 0 && this.requestCount < this.threshold) {
       this.incrementRequestCount();
-      this.sendRequest(payload);
+      this.sendRequest(this.queue.shift()).finally((): void => {
+        this.decrementRequestCount();
+        this.handleNext();
+      });
     }
   }
 
-  private async sendRequest(payload: Payload): Promise<any> {
+  private async sendRequest(payload: Payload): Promise<void> {
     const request = new AjaxRequest(payload.url);
     let response: any;
     if (typeof payload.method !== 'undefined' && payload.method.toUpperCase() === 'POST') {
@@ -61,7 +63,11 @@ class AjaxQueue {
       response = request.withQueryArguments(payload.data || {}).get();
     }
 
-    return response.then(payload.onfulfilled, payload.onrejected);
+    this.requests.push(request);
+    return response.then(payload.onfulfilled, payload.onrejected).then((): void => {
+      const idx = this.requests.indexOf(request);
+      delete this.requests[idx];
+    });
   }
 
   private incrementRequestCount(): void {
diff --git a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts
index a9480170e352de327f65cb3226d807d1e5fa9379..afce0653dfae01b1f2b0fd28e2cbd38b32d8d697 100644
--- a/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts
+++ b/Build/Sources/TypeScript/install/Resources/Public/TypeScript/Module/Upgrade/ExtensionScanner.ts
@@ -13,6 +13,7 @@
 
 import 'bootstrap';
 import * as $ from 'jquery';
+import AjaxRequest = require('TYPO3/CMS/Core/Ajax/AjaxRequest');
 import {AjaxResponse} from 'TYPO3/CMS/Core/Ajax/AjaxResponse';
 import {ResponseError} from 'TYPO3/CMS/Core/Ajax/ResponseError';
 import {AbstractInteractableModule} from '../AbstractInteractableModule';
@@ -67,6 +68,8 @@ class ExtensionScanner extends AbstractInteractableModule {
         this.scanSingleExtension(extension);
         $me.data('scanned', true);
       }
+    }).on('hide.bs.modal', (): void => {
+      AjaxQueue.flush();
     }).on('click', this.selectorScanSingleTrigger, (e: JQueryEventObject): void => {
       // Scan a single extension by clicking "Rescan"
       e.preventDefault();
@@ -83,9 +86,8 @@ class ExtensionScanner extends AbstractInteractableModule {
 
   private getData(): void {
     const modalContent = this.getModalBody();
-    AjaxQueue.add({
-      url: Router.getUrl('extensionScannerGetData'),
-      onfulfilled: async (response: AjaxResponse): Promise<any> => {
+    (new AjaxRequest(Router.getUrl('extensionScannerGetData'))).get().then(
+      async (response: AjaxResponse): Promise<any> => {
         const data = await response.resolve();
         if (data.success === true) {
           modalContent.empty().append(data.html);
@@ -94,10 +96,10 @@ class ExtensionScanner extends AbstractInteractableModule {
           Notification.error('Something went wrong');
         }
       },
-      onrejected: (error: ResponseError): void => {
+      (error: ResponseError): void => {
         Router.handleAjaxError(error, modalContent);
-      },
-    });
+      }
+    );
   }
 
   private getExtensionSelector(extension: string): string {
@@ -157,26 +159,24 @@ class ExtensionScanner extends AbstractInteractableModule {
     if (numberOfScannedExtensions === numberOfExtensions) {
       this.findInModal(this.selectorExtensionScanButton).removeClass('disabled').prop('disabled', false);
       Notification.success('Scan finished', 'All extensions have been scanned');
-      AjaxQueue.add({
-        url: Router.getUrl(),
-        method: 'POST',
-        data: {
-          install: {
-            action: 'extensionScannerMarkFullyScannedRestFiles',
-            token: this.getModuleContent().data('extension-scanner-mark-fully-scanned-rest-files-token'),
-            hashes: this.uniqueArray(this.listOfAffectedRestFileHashes),
-          },
+
+      (new AjaxRequest(Router.getUrl())).post({
+        install: {
+          action: 'extensionScannerMarkFullyScannedRestFiles',
+          token: this.getModuleContent().data('extension-scanner-mark-fully-scanned-rest-files-token'),
+          hashes: this.uniqueArray(this.listOfAffectedRestFileHashes),
         },
-        onfulfilled: async (response: AjaxResponse): Promise<any> => {
+      }).then(
+        async (response: AjaxResponse): Promise<any> => {
           const data = await response.resolve();
           if (data.success === true) {
             Notification.success('Marked not affected files', 'Marked ' + data.markedAsNotAffected + ' ReST files as not affected.');
           }
         },
-        onrejected: (error: ResponseError): void => {
+        (error: ResponseError): void => {
           Router.handleAjaxError(error, modalContent);
-        },
-      });
+        }
+      );
     }
   }
 
@@ -206,139 +206,136 @@ class ExtensionScanner extends AbstractInteractableModule {
     $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text('0');
     $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty().text('0');
     this.setProgressForAll();
-    AjaxQueue.add({
-      url: Router.getUrl(),
-      method: 'POST',
-      data: {
-        install: {
-          action: 'extensionScannerFiles',
-          token: executeToken,
-          extension: extension,
-        },
+    (new AjaxRequest(Router.getUrl())).post({
+      install: {
+        action: 'extensionScannerFiles',
+        token: executeToken,
+        extension: extension,
       },
-      onfulfilled: async (response: AjaxResponse): Promise<any> => {
+    }).then(
+      async (response: AjaxResponse): Promise<any> => {
         const data = await response.resolve();
         if (data.success === true && Array.isArray(data.files)) {
           const numberOfFiles = data.files.length;
-          if (numberOfFiles > 0) {
-            this.setStatusMessageForScan(extension, 0, numberOfFiles);
-            $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
-            let doneFiles = 0;
-            data.files.forEach((file: string): void => {
-              AjaxQueue.add({
-                method: 'POST',
-                data: {
-                  install: {
-                    action: 'extensionScannerScanFile',
-                    token: this.getModuleContent().data('extension-scanner-scan-file-token'),
-                    extension: extension,
-                    file: file,
-                  },
+          if (numberOfFiles <= 0) {
+            Notification.warning('No files found', 'The extension EXT:' + extension + ' contains no files we can scan');
+            return;
+          }
+
+          this.setStatusMessageForScan(extension, 0, numberOfFiles);
+          $extensionContainer.find('.t3js-extensionScanner-extension-body').text('');
+          let doneFiles = 0;
+          data.files.forEach((file: string): void => {
+            AjaxQueue.add({
+              method: 'POST',
+              data: {
+                install: {
+                  action: 'extensionScannerScanFile',
+                  token: this.getModuleContent().data('extension-scanner-scan-file-token'),
+                  extension: extension,
+                  file: file,
                 },
-                url: Router.getUrl(),
-                onfulfilled: async (response: AjaxResponse): Promise<any> => {
-                  const fileData: FileData = await response.resolve();
-                  doneFiles++;
-                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
-                  if (fileData.success && $.isArray(fileData.matches)) {
-                    fileData.matches.forEach((match: Match): void => {
-                      hitFound = true;
-                      const aMatch: any = modalContent.find(hitTemplate).clone();
-                      aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId);
-                      aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId);
-                      aMatch.find('.t3js-extensionScanner-hit-filename').text(file);
-                      aMatch.find('.t3js-extensionScanner-hit-message').text(match.message);
-                      if (match.indicator === 'strong') {
-                        aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
-                          .append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>');
-                      } else {
-                        aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
-                          .append('<span class="badge" title="Probable match, but can be a false positive">weak</span>');
-                      }
-                      if (match.silenced === true) {
-                        aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
-                          .append('<span class="badge" title="Match has been annotated by extension author' +
-                            ' as false positive match">silenced</span>');
-                      }
-                      aMatch.find('.t3js-extensionScanner-hit-file-lineContent').empty().text(match.lineContent);
-                      aMatch.find('.t3js-extensionScanner-hit-file-line').empty().text(match.line + ': ');
-                      if ($.isArray(match.restFiles)) {
-                        match.restFiles.forEach((restFile: RestFile): void => {
-                          const aRest = modalContent.find(restTemplate).clone();
-                          aRest.find('.t3js-extensionScanner-hit-rest-panel-head').attr('href', '#collapse' + restFile.uniqueId);
-                          aRest.find('.t3js-extensionScanner-hit-rest-panel-head .badge').empty().text(restFile.version);
-                          aRest.find('.t3js-extensionScanner-hit-rest-panel-body').attr('id', 'collapse' + restFile.uniqueId);
-                          aRest.find('.t3js-extensionScanner-hit-rest-headline').text(restFile.headline);
-                          aRest.find('.t3js-extensionScanner-hit-rest-body').text(restFile.content);
-                          aRest.addClass('panel-' + restFile.class);
-                          aMatch.find('.t3js-extensionScanner-hit-file-rest-container').append(aRest);
-                          this.listOfAffectedRestFileHashes.push(restFile.file_hash);
-                        });
-                      }
-                      const panelClass =
-                        aMatch.find('.panel-breaking', '.t3js-extensionScanner-hit-file-rest-container').length > 0
-                          ? 'panel-danger'
-                          : 'panel-warning';
-                      aMatch.addClass(panelClass);
-                      $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
-                      if (panelClass === 'panel-danger') {
-                        $extensionContainer.removeClass('panel-warning').addClass(panelClass);
-                      }
-                      if (panelClass === 'panel-warning' && !$extensionContainer.hasClass('panel-danger')) {
-                        $extensionContainer.addClass(panelClass);
-                      }
-                    });
-                  }
-                  if (fileData.success) {
-                    const currentLinesOfCode = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-loc').text(), 10);
-                    $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty()
-                      .text(currentLinesOfCode + fileData.effectiveCodeLines);
-                    if (fileData.isFileIgnored) {
-                      const currentIgnoredFiles = parseInt(
-                        $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').text(),
-                        10,
-                      );
-                      $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text(currentIgnoredFiles + 1);
+              },
+              url: Router.getUrl(),
+              onfulfilled: async (response: AjaxResponse): Promise<void> => {
+                const fileData: FileData = await response.resolve();
+                doneFiles++;
+                this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+                this.setProgressForScan(extension, doneFiles, numberOfFiles);
+                if (fileData.success && $.isArray(fileData.matches)) {
+                  fileData.matches.forEach((match: Match): void => {
+                    hitFound = true;
+                    const aMatch: any = modalContent.find(hitTemplate).clone();
+                    aMatch.find('.t3js-extensionScanner-hit-file-panel-head').attr('href', '#collapse' + match.uniqueId);
+                    aMatch.find('.t3js-extensionScanner-hit-file-panel-body').attr('id', 'collapse' + match.uniqueId);
+                    aMatch.find('.t3js-extensionScanner-hit-filename').text(file);
+                    aMatch.find('.t3js-extensionScanner-hit-message').text(match.message);
+                    if (match.indicator === 'strong') {
+                      aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
+                        .append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>');
+                    } else {
+                      aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
+                        .append('<span class="badge" title="Probable match, but can be a false positive">weak</span>');
                     }
-                    const currentIgnoredLines = parseInt(
-                      $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').text(),
+                    if (match.silenced === true) {
+                      aMatch.find('.t3js-extensionScanner-hit-file-panel-head .badges')
+                        .append('<span class="badge" title="Match has been annotated by extension author' +
+                          ' as false positive match">silenced</span>');
+                    }
+                    aMatch.find('.t3js-extensionScanner-hit-file-lineContent').empty().text(match.lineContent);
+                    aMatch.find('.t3js-extensionScanner-hit-file-line').empty().text(match.line + ': ');
+                    if ($.isArray(match.restFiles)) {
+                      match.restFiles.forEach((restFile: RestFile): void => {
+                        const aRest = modalContent.find(restTemplate).clone();
+                        aRest.find('.t3js-extensionScanner-hit-rest-panel-head').attr('href', '#collapse' + restFile.uniqueId);
+                        aRest.find('.t3js-extensionScanner-hit-rest-panel-head .badge').empty().text(restFile.version);
+                        aRest.find('.t3js-extensionScanner-hit-rest-panel-body').attr('id', 'collapse' + restFile.uniqueId);
+                        aRest.find('.t3js-extensionScanner-hit-rest-headline').text(restFile.headline);
+                        aRest.find('.t3js-extensionScanner-hit-rest-body').text(restFile.content);
+                        aRest.addClass('panel-' + restFile.class);
+                        aMatch.find('.t3js-extensionScanner-hit-file-rest-container').append(aRest);
+                        this.listOfAffectedRestFileHashes.push(restFile.file_hash);
+                      });
+                    }
+                    const panelClass =
+                      aMatch.find('.panel-breaking', '.t3js-extensionScanner-hit-file-rest-container').length > 0
+                        ? 'panel-danger'
+                        : 'panel-warning';
+                    aMatch.addClass(panelClass);
+                    $extensionContainer.find('.t3js-extensionScanner-extension-body').removeClass('hide').append(aMatch);
+                    if (panelClass === 'panel-danger') {
+                      $extensionContainer.removeClass('panel-warning').addClass(panelClass);
+                    }
+                    if (panelClass === 'panel-warning' && !$extensionContainer.hasClass('panel-danger')) {
+                      $extensionContainer.addClass(panelClass);
+                    }
+                  });
+                }
+                if (fileData.success) {
+                  const currentLinesOfCode = parseInt($extensionContainer.find('.t3js-extensionScanner-extension-body-loc').text(), 10);
+                  $extensionContainer.find('.t3js-extensionScanner-extension-body-loc').empty()
+                    .text(currentLinesOfCode + fileData.effectiveCodeLines);
+                  if (fileData.isFileIgnored) {
+                    const currentIgnoredFiles = parseInt(
+                      $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').text(),
                       10,
                     );
-                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty()
-                      .text(currentIgnoredLines + fileData.ignoredLines);
+                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-files').empty().text(currentIgnoredFiles + 1);
                   }
-                  if (doneFiles === numberOfFiles) {
-                    if (!hitFound) {
-                      $extensionContainer.addClass('panel-success');
-                    }
-                    $extensionContainer.addClass('t3js-extensionscan-finished');
-                    this.setProgressForAll();
-                    $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
+                  const currentIgnoredLines = parseInt(
+                    $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').text(),
+                    10,
+                  );
+                  $extensionContainer.find('.t3js-extensionScanner-extension-body-ignored-lines').empty()
+                    .text(currentIgnoredLines + fileData.ignoredLines);
+                }
+                if (doneFiles === numberOfFiles) {
+                  if (!hitFound) {
+                    $extensionContainer.addClass('panel-success');
                   }
-                },
-                onrejected: (reason: string): void => {
-                  doneFiles = doneFiles + 1;
-                  this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
-                  this.setProgressForScan(extension, doneFiles, numberOfFiles);
+                  $extensionContainer.addClass('t3js-extensionscan-finished');
                   this.setProgressForAll();
-                  Notification.error('Oops, an error occurred', 'Please look at the console output for details');
-                  console.error(reason);
-                },
-              });
+                  $extensionContainer.find('.t3js-extensionScanner-scan-single').text('Rescan').attr('disabled', null);
+                }
+              },
+              onrejected: (reason: string): void => {
+                doneFiles = doneFiles + 1;
+                this.setStatusMessageForScan(extension, doneFiles, numberOfFiles);
+                this.setProgressForScan(extension, doneFiles, numberOfFiles);
+                this.setProgressForAll();
+                console.error(reason);
+              },
             });
-          } else {
-            Notification.warning('No files found', 'The extension EXT:' + extension + ' contains no files we can scan');
-          }
+          });
         } else {
           Notification.error('Oops, an error occurred', 'Please look at the console output for details');
           console.error(data);
         }
       },
-      onrejected: (error: ResponseError): void => {
+      (error: ResponseError): void => {
         Router.handleAjaxError(error, modalContent);
-      },
-    });
+      }
+    );
   }
 }
 
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js b/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js
index 23c88a94f77016ded269157b44a7cccec4417677..68abd1451c50697b8649bf2a78bc00f96d6967d7 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/Ajax/AjaxQueue.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-define(["require","exports","TYPO3/CMS/Core/Ajax/AjaxRequest"],(function(e,t,s){"use strict";return new class{constructor(){this.requestCount=0,this.threshold=10,this.queue=[]}async add(e){const t=e.finally;this.queue.length>0&&this.requestCount<=this.threshold?this.sendRequest(this.queue.shift()).finally(()=>{this.decrementRequestCount()}):this.decrementRequestCount(),t&&t(...arguments),this.requestCount>=this.threshold?this.queue.push(e):(this.incrementRequestCount(),this.sendRequest(e))}async sendRequest(e){const t=new s(e.url);let u;return(u=void 0!==e.method&&"POST"===e.method.toUpperCase()?t.post(e.data):t.withQueryArguments(e.data||{}).get()).then(e.onfulfilled,e.onrejected)}incrementRequestCount(){this.requestCount++}decrementRequestCount(){this.requestCount>0&&this.requestCount--}}}));
\ No newline at end of file
+define(["require","exports","TYPO3/CMS/Core/Ajax/AjaxRequest"],(function(e,t,s){"use strict";return new class{constructor(){this.requests=[],this.requestCount=0,this.threshold=5,this.queue=[]}add(e){this.queue.push(e),this.handleNext()}flush(){this.queue=[],this.requests.map(e=>{e.abort()}),this.requests=[]}handleNext(){this.queue.length>0&&this.requestCount<this.threshold&&(this.incrementRequestCount(),this.sendRequest(this.queue.shift()).finally(()=>{this.decrementRequestCount(),this.handleNext()}))}async sendRequest(e){const t=new s(e.url);let u;return u=void 0!==e.method&&"POST"===e.method.toUpperCase()?t.post(e.data):t.withQueryArguments(e.data||{}).get(),this.requests.push(t),u.then(e.onfulfilled,e.onrejected).then(()=>{const e=this.requests.indexOf(t);delete this.requests[e]})}incrementRequestCount(){this.requestCount++}decrementRequestCount(){this.requestCount>0&&this.requestCount--}}}));
\ No newline at end of file
diff --git a/typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionScanner.js b/typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionScanner.js
index 4a9710c9706925a48ef0102034715e386fed280a..f5e66deae2d1624811f69cd4b926b745d6dd7899 100644
--- a/typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionScanner.js
+++ b/typo3/sysext/install/Resources/Public/JavaScript/Module/Upgrade/ExtensionScanner.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-define(["require","exports","jquery","../AbstractInteractableModule","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","../../Ajax/AjaxQueue","../../Router","bootstrap"],(function(e,n,t,s,i,a,o,r){"use strict";class l extends s.AbstractInteractableModule{constructor(){super(...arguments),this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single",this.selectorExtensionScanButton=".t3js-extensionScanner-scan-all"}initialize(e){this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,e=>{const n=t(e.currentTarget);if(void 0===n.data("scanned")){const e=n.data("extension");this.scanSingleExtension(e),n.data("scanned",!0)}}).on("click",this.selectorScanSingleTrigger,e=>{e.preventDefault();const n=t(e.currentTarget).closest(this.selectorExtensionContainer).data("extension");this.scanSingleExtension(n)}).on("click",this.selectorExtensionScanButton,n=>{n.preventDefault(),t(n.currentTarget).addClass("disabled").prop("disabled",!0);const s=e.find(this.selectorExtensionContainer);this.scanAll(s)})}getData(){const e=this.getModalBody();o.add({url:r.getUrl("extensionScannerGetData"),onfulfilled:async n=>{const t=await n.resolve();!0===t.success?(e.empty().append(t.html),i.setButtons(t.buttons)):a.error("Something went wrong")},onrejected:n=>{r.handleAjaxError(n,e)}})}getExtensionSelector(e){return this.selectorExtensionContainer+"-"+e}scanAll(e){this.findInModal(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll(),e.each((e,n)=>{const s=t(n),i=s.data("extension");this.scanSingleExtension(i),s.data("scanned",!0)})}setStatusMessageForScan(e,n,t){this.findInModal(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")}setProgressForScan(e,n,t){const s=n/t*100;this.findInModal(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")}setProgressForAll(){const e=this.findInModal(this.selectorExtensionContainer).length,n=this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,s=this.getModalBody();this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(this.findInModal(this.selectorExtensionScanButton).removeClass("disabled").prop("disabled",!1),a.success("Scan finished","All extensions have been scanned"),o.add({url:r.getUrl(),method:"POST",data:{install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.getModuleContent().data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:this.uniqueArray(this.listOfAffectedRestFileHashes)}},onfulfilled:async e=>{const n=await e.resolve();!0===n.success&&a.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")},onrejected:e=>{r.handleAjaxError(e,s)}}))}uniqueArray(e){return e.filter((e,n,t)=>t.indexOf(e)===n)}scanSingleExtension(e){const n=this.getModuleContent().data("extension-scanner-files-token"),s=this.getModalBody(),i=this.findInModal(this.getExtensionSelector(e));let l=!1;i.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),i.data("hasRun","true"),i.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),i.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),i.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),i.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll(),o.add({url:r.getUrl(),method:"POST",data:{install:{action:"extensionScannerFiles",token:n,extension:e}},onfulfilled:async n=>{const d=await n.resolve();if(!0===d.success&&Array.isArray(d.files)){const n=d.files.length;if(n>0){this.setStatusMessageForScan(e,0,n),i.find(".t3js-extensionScanner-extension-body").text("");let c=0;d.files.forEach(d=>{o.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:d}},url:r.getUrl(),onfulfilled:async a=>{const o=await a.resolve();if(c++,this.setStatusMessageForScan(e,c,n),this.setProgressForScan(e,c,n),o.success&&t.isArray(o.matches)&&o.matches.forEach(e=>{l=!0;const n=s.find("#t3js-extensionScanner-file-hit-template").clone();n.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-filename").text(d),n.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>'):n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Match has been annotated by extension author as false positive match">silenced</span>'),n.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),n.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),t.isArray(e.restFiles)&&e.restFiles.forEach(e=>{const t=s.find("#t3js-extensionScanner-file-hit-rest-template").clone();t.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),t.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),t.find(".t3js-extensionScanner-hit-rest-body").text(e.content),t.addClass("panel-"+e.class),n.find(".t3js-extensionScanner-hit-file-rest-container").append(t),this.listOfAffectedRestFileHashes.push(e.file_hash)});const a=n.find(".panel-breaking",".t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(a),i.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),"panel-danger"===a&&i.removeClass("panel-warning").addClass(a),"panel-warning"!==a||i.hasClass("panel-danger")||i.addClass(a)}),o.success){const e=parseInt(i.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(i.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+o.effectiveCodeLines),o.isFileIgnored){const e=parseInt(i.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);i.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(e+1)}const n=parseInt(i.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);i.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(n+o.ignoredLines)}c===n&&(l||i.addClass("panel-success"),i.addClass("t3js-extensionscan-finished"),this.setProgressForAll(),i.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null))},onrejected:t=>{c+=1,this.setStatusMessageForScan(e,c,n),this.setProgressForScan(e,c,n),this.setProgressForAll(),a.error("Oops, an error occurred","Please look at the console output for details"),console.error(t)}})})}else a.warning("No files found","The extension EXT:"+e+" contains no files we can scan")}else a.error("Oops, an error occurred","Please look at the console output for details"),console.error(d)},onrejected:e=>{r.handleAjaxError(e,s)}})}}return new l}));
\ No newline at end of file
+define(["require","exports","jquery","TYPO3/CMS/Core/Ajax/AjaxRequest","../AbstractInteractableModule","TYPO3/CMS/Backend/Modal","TYPO3/CMS/Backend/Notification","../../Ajax/AjaxQueue","../../Router","bootstrap"],(function(e,n,t,s,i,a,o,r,l){"use strict";class c extends i.AbstractInteractableModule{constructor(){super(...arguments),this.listOfAffectedRestFileHashes=[],this.selectorExtensionContainer=".t3js-extensionScanner-extension",this.selectorNumberOfFiles=".t3js-extensionScanner-number-of-files",this.selectorScanSingleTrigger=".t3js-extensionScanner-scan-single",this.selectorExtensionScanButton=".t3js-extensionScanner-scan-all"}initialize(e){this.currentModal=e,this.getData(),e.on("show.bs.collapse",this.selectorExtensionContainer,e=>{const n=t(e.currentTarget);if(void 0===n.data("scanned")){const e=n.data("extension");this.scanSingleExtension(e),n.data("scanned",!0)}}).on("hide.bs.modal",()=>{r.flush()}).on("click",this.selectorScanSingleTrigger,e=>{e.preventDefault();const n=t(e.currentTarget).closest(this.selectorExtensionContainer).data("extension");this.scanSingleExtension(n)}).on("click",this.selectorExtensionScanButton,n=>{n.preventDefault(),t(n.currentTarget).addClass("disabled").prop("disabled",!0);const s=e.find(this.selectorExtensionContainer);this.scanAll(s)})}getData(){const e=this.getModalBody();new s(l.getUrl("extensionScannerGetData")).get().then(async n=>{const t=await n.resolve();!0===t.success?(e.empty().append(t.html),a.setButtons(t.buttons)):o.error("Something went wrong")},n=>{l.handleAjaxError(n,e)})}getExtensionSelector(e){return this.selectorExtensionContainer+"-"+e}scanAll(e){this.findInModal(this.selectorExtensionContainer).removeClass("panel-danger panel-warning panel-success").find(".panel-progress-bar").css("width",0).attr("aria-valuenow",0).find("span").text("0%"),this.setProgressForAll(),e.each((e,n)=>{const s=t(n),i=s.data("extension");this.scanSingleExtension(i),s.data("scanned",!0)})}setStatusMessageForScan(e,n,t){this.findInModal(this.getExtensionSelector(e)).find(this.selectorNumberOfFiles).text("Checked "+n+" of "+t+" files")}setProgressForScan(e,n,t){const s=n/t*100;this.findInModal(this.getExtensionSelector(e)).find(".panel-progress-bar").css("width",s+"%").attr("aria-valuenow",s).find("span").text(s+"%")}setProgressForAll(){const e=this.findInModal(this.selectorExtensionContainer).length,n=this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-success").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-warning").length+this.findInModal(this.selectorExtensionContainer+".t3js-extensionscan-finished.panel-danger").length,t=n/e*100,i=this.getModalBody();this.findInModal(".t3js-extensionScanner-progress-all-extension .progress-bar").css("width",t+"%").attr("aria-valuenow",t).find("span").text(n+" of "+e+" scanned"),n===e&&(this.findInModal(this.selectorExtensionScanButton).removeClass("disabled").prop("disabled",!1),o.success("Scan finished","All extensions have been scanned"),new s(l.getUrl()).post({install:{action:"extensionScannerMarkFullyScannedRestFiles",token:this.getModuleContent().data("extension-scanner-mark-fully-scanned-rest-files-token"),hashes:this.uniqueArray(this.listOfAffectedRestFileHashes)}}).then(async e=>{const n=await e.resolve();!0===n.success&&o.success("Marked not affected files","Marked "+n.markedAsNotAffected+" ReST files as not affected.")},e=>{l.handleAjaxError(e,i)}))}uniqueArray(e){return e.filter((e,n,t)=>t.indexOf(e)===n)}scanSingleExtension(e){const n=this.getModuleContent().data("extension-scanner-files-token"),i=this.getModalBody(),a=this.findInModal(this.getExtensionSelector(e));let c=!1;a.removeClass("panel-danger panel-warning panel-success t3js-extensionscan-finished"),a.data("hasRun","true"),a.find(".t3js-extensionScanner-scan-single").text("Scanning...").attr("disabled","disabled"),a.find(".t3js-extensionScanner-extension-body-loc").empty().text("0"),a.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text("0"),a.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text("0"),this.setProgressForAll(),new s(l.getUrl()).post({install:{action:"extensionScannerFiles",token:n,extension:e}}).then(async n=>{const s=await n.resolve();if(!0===s.success&&Array.isArray(s.files)){const n=s.files.length;if(n<=0)return void o.warning("No files found","The extension EXT:"+e+" contains no files we can scan");this.setStatusMessageForScan(e,0,n),a.find(".t3js-extensionScanner-extension-body").text("");let d=0;s.files.forEach(s=>{r.add({method:"POST",data:{install:{action:"extensionScannerScanFile",token:this.getModuleContent().data("extension-scanner-scan-file-token"),extension:e,file:s}},url:l.getUrl(),onfulfilled:async o=>{const r=await o.resolve();if(d++,this.setStatusMessageForScan(e,d,n),this.setProgressForScan(e,d,n),r.success&&t.isArray(r.matches)&&r.matches.forEach(e=>{c=!0;const n=i.find("#t3js-extensionScanner-file-hit-template").clone();n.find(".t3js-extensionScanner-hit-file-panel-head").attr("href","#collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-file-panel-body").attr("id","collapse"+e.uniqueId),n.find(".t3js-extensionScanner-hit-filename").text(s),n.find(".t3js-extensionScanner-hit-message").text(e.message),"strong"===e.indicator?n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Reliable match, false positive unlikely">strong</span>'):n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Probable match, but can be a false positive">weak</span>'),!0===e.silenced&&n.find(".t3js-extensionScanner-hit-file-panel-head .badges").append('<span class="badge" title="Match has been annotated by extension author as false positive match">silenced</span>'),n.find(".t3js-extensionScanner-hit-file-lineContent").empty().text(e.lineContent),n.find(".t3js-extensionScanner-hit-file-line").empty().text(e.line+": "),t.isArray(e.restFiles)&&e.restFiles.forEach(e=>{const t=i.find("#t3js-extensionScanner-file-hit-rest-template").clone();t.find(".t3js-extensionScanner-hit-rest-panel-head").attr("href","#collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-panel-head .badge").empty().text(e.version),t.find(".t3js-extensionScanner-hit-rest-panel-body").attr("id","collapse"+e.uniqueId),t.find(".t3js-extensionScanner-hit-rest-headline").text(e.headline),t.find(".t3js-extensionScanner-hit-rest-body").text(e.content),t.addClass("panel-"+e.class),n.find(".t3js-extensionScanner-hit-file-rest-container").append(t),this.listOfAffectedRestFileHashes.push(e.file_hash)});const o=n.find(".panel-breaking",".t3js-extensionScanner-hit-file-rest-container").length>0?"panel-danger":"panel-warning";n.addClass(o),a.find(".t3js-extensionScanner-extension-body").removeClass("hide").append(n),"panel-danger"===o&&a.removeClass("panel-warning").addClass(o),"panel-warning"!==o||a.hasClass("panel-danger")||a.addClass(o)}),r.success){const e=parseInt(a.find(".t3js-extensionScanner-extension-body-loc").text(),10);if(a.find(".t3js-extensionScanner-extension-body-loc").empty().text(e+r.effectiveCodeLines),r.isFileIgnored){const e=parseInt(a.find(".t3js-extensionScanner-extension-body-ignored-files").text(),10);a.find(".t3js-extensionScanner-extension-body-ignored-files").empty().text(e+1)}const n=parseInt(a.find(".t3js-extensionScanner-extension-body-ignored-lines").text(),10);a.find(".t3js-extensionScanner-extension-body-ignored-lines").empty().text(n+r.ignoredLines)}d===n&&(c||a.addClass("panel-success"),a.addClass("t3js-extensionscan-finished"),this.setProgressForAll(),a.find(".t3js-extensionScanner-scan-single").text("Rescan").attr("disabled",null))},onrejected:t=>{d+=1,this.setStatusMessageForScan(e,d,n),this.setProgressForScan(e,d,n),this.setProgressForAll(),console.error(t)}})})}else o.error("Oops, an error occurred","Please look at the console output for details"),console.error(s)},e=>{l.handleAjaxError(e,i)})}}return new c}));
\ No newline at end of file