From a8861f19b7d0a34bd05a09290e19283ba8afe7c5 Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Tue, 8 Aug 2023 15:01:30 +0200
Subject: [PATCH] [TASK] Migrate from jQuery in
 `@typo3/backend/layout-module/paste`

This patch removes jQuery from the `@typo3/backend/layout-module/paste`
module in favor of native APIs.

Resolves: #101617
Releases: main
Change-Id: I201eaf575a328bc805dc0fb90a364a918686900a
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/80460
Reviewed-by: Andreas Nedbal <andy@pixelde.su>
Tested-by: Nikita Hovratov <nikita.h@live.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Nedbal <andy@pixelde.su>
Reviewed-by: Nikita Hovratov <nikita.h@live.de>
Tested-by: core-ci <typo3@b13.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
---
 .../TypeScript/backend/layout-module/paste.ts | 48 ++++++++-----------
 .../Public/JavaScript/layout-module/paste.js  |  2 +-
 2 files changed, 20 insertions(+), 30 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/layout-module/paste.ts b/Build/Sources/TypeScript/backend/layout-module/paste.ts
index 0f6b1f96e9fc..e96b161f77f5 100644
--- a/Build/Sources/TypeScript/backend/layout-module/paste.ts
+++ b/Build/Sources/TypeScript/backend/layout-module/paste.ts
@@ -18,13 +18,13 @@ import DocumentService from '@typo3/core/document-service';
  * and triggers a modal window. which then calls the AjaxDataHandler
  * to execute the action to paste the current clipboard contents.
  */
-import $ from 'jquery';
 import ResponseInterface from '../ajax-data-handler/response-interface';
 import DataHandler from '../ajax-data-handler';
 import { default as Modal, ModalElement, Button } from '@typo3/backend/modal';
 import Severity from '../severity';
 import '@typo3/backend/element/icon-element';
 import { SeverityEnum } from '../enum/severity';
+import RegularEvent from '@typo3/core/event/regular-event';
 
 type PasteOptions = {
   itemOnClipboardUid: number;
@@ -49,7 +49,7 @@ class Paste {
     this.copyMode = args.copyMode;
 
     DocumentService.ready().then((): void => {
-      if ($('.t3js-page-columns').length) {
+      if (document.querySelectorAll('.t3js-page-columns').length > 0) {
         this.generateButtonTemplates();
         this.activatePasteIcons();
         this.initializeEvents();
@@ -57,29 +57,21 @@ class Paste {
     });
   }
 
-  /**
-   * @param {JQuery} $element
-   * @return number
-   */
-  private static determineColumn($element: JQuery): number {
-    const $columnContainer = $element.closest('[data-colpos]');
-    if ($columnContainer.length && $columnContainer.data('colpos') !== 'undefined') {
-      return $columnContainer.data('colpos');
-    }
-
-    return 0;
+  private static determineColumn(element: HTMLElement): number {
+    const columnContainer = element.closest('[data-colpos]') as HTMLElement|null;
+    return parseInt(columnContainer?.dataset?.colpos ?? '0', 10);
   }
 
   private initializeEvents(): void
   {
-    $(document).on('click', '.t3js-paste', (evt: Event): void => {
+    new RegularEvent('click', (evt: Event, target: HTMLElement): void => {
       evt.preventDefault();
-      this.activatePasteModal($(evt.currentTarget));
-    });
+
+      this.activatePasteModal(target);
+    }).delegateTo(document, '.t3js-paste');
   }
 
-  private generateButtonTemplates(): void
-  {
+  private generateButtonTemplates(): void {
     if (!this.itemOnClipboardUid) {
       return;
     }
@@ -98,7 +90,7 @@ class Paste {
   }
 
   /**
-   * activates the paste into / paste after icons outside of the context menus
+   * activates the paste into / paste after icons outside the context menus
    */
   private activatePasteIcons(): void {
     if (this.pasteAfterLinkTemplate && this.pasteIntoLinkTemplate) {
@@ -112,7 +104,7 @@ class Paste {
   /**
    * generates the paste into / paste after modal
    */
-  private activatePasteModal($element: JQuery): void {
+  private activatePasteModal(element: HTMLElement): void {
     const title = (TYPO3.lang['paste.modal.title.paste'] || 'Paste record') + ': "' + this.itemOnClipboardTitle + '"';
     const content = TYPO3.lang['paste.modal.paste'] || 'Do you want to paste the record to this position?';
 
@@ -129,7 +121,7 @@ class Paste {
         btnClass: 'btn-' + Severity.getCssClass(SeverityEnum.warning),
         trigger: (e: Event, modal: ModalElement): void => {
           modal.hideModal();
-          this.execute($element);
+          this.execute(element);
         },
       },
     ];
@@ -139,20 +131,18 @@ class Paste {
 
   /**
    * Send an AJAX request via the AjaxDataHandler
-   *
-   * @param {JQuery} $element
    */
-  private execute($element: JQuery): void {
-    const colPos = Paste.determineColumn($element);
-    const closestElement = $element.closest(this.elementIdentifier);
-    const targetFound = closestElement.data('uid');
+  private execute(element: HTMLElement): void {
+    const colPos = Paste.determineColumn(element);
+    const closestElement = element.closest(this.elementIdentifier) as HTMLElement;
+    const targetFound = closestElement.dataset.uid;
     let targetPid;
     if (typeof targetFound === 'undefined') {
-      targetPid = parseInt(closestElement.data('page'), 10);
+      targetPid = parseInt(closestElement.dataset.page, 10);
     } else {
       targetPid = 0 - parseInt(targetFound, 10);
     }
-    const language = parseInt($element.closest('[data-language-uid]').data('language-uid'), 10);
+    const language = parseInt((element.closest('[data-language-uid]') as HTMLElement).dataset.languageUid, 10);
     const parameters = {
       CB: {
         paste: 'tt_content|' + targetPid,
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js
index 3da07f16b6bd..8e2132dc367a 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/layout-module/paste.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import DocumentService from"@typo3/core/document-service.js";import $ from"jquery";import DataHandler from"@typo3/backend/ajax-data-handler.js";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";class Paste{constructor(t){this.itemOnClipboardUid=0,this.itemOnClipboardTitle="",this.copyMode="",this.elementIdentifier=".t3js-page-ce",this.pasteAfterLinkTemplate="",this.pasteIntoLinkTemplate="",this.itemOnClipboardUid=t.itemOnClipboardUid,this.itemOnClipboardTitle=t.itemOnClipboardTitle,this.copyMode=t.copyMode,DocumentService.ready().then((()=>{$(".t3js-page-columns").length&&(this.generateButtonTemplates(),this.activatePasteIcons(),this.initializeEvents())}))}static determineColumn(t){const e=t.closest("[data-colpos]");return e.length&&"undefined"!==e.data("colpos")?e.data("colpos"):0}initializeEvents(){$(document).on("click",".t3js-paste",(t=>{t.preventDefault(),this.activatePasteModal($(t.currentTarget))}))}generateButtonTemplates(){this.itemOnClipboardUid&&(this.pasteAfterLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-after btn btn-default btn-sm" title="'+TYPO3.lang?.pasteAfterRecord+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>',this.pasteIntoLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-into btn btn-default btn-sm" title="'+TYPO3.lang?.pasteIntoColumn+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>')}activatePasteIcons(){this.pasteAfterLinkTemplate&&this.pasteIntoLinkTemplate&&document.querySelectorAll(".t3js-page-new-ce").forEach((t=>{const e=t.parentElement.dataset.page?this.pasteIntoLinkTemplate:this.pasteAfterLinkTemplate;t.append(document.createRange().createContextualFragment(e))}))}activatePasteModal(t){const e=(TYPO3.lang["paste.modal.title.paste"]||"Paste record")+': "'+this.itemOnClipboardTitle+'"',a=TYPO3.lang["paste.modal.paste"]||"Do you want to paste the record to this position?";let n=[];n=[{text:TYPO3.lang["paste.modal.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",trigger:(t,e)=>e.hideModal()},{text:TYPO3.lang["paste.modal.button.paste"]||"Paste",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(e,a)=>{a.hideModal(),this.execute(t)}}],Modal.show(e,a,SeverityEnum.warning,n)}execute(t){const e=Paste.determineColumn(t),a=t.closest(this.elementIdentifier),n=a.data("uid");let s;s=void 0===n?parseInt(a.data("page"),10):0-parseInt(n,10);const i={CB:{paste:"tt_content|"+s,pad:"normal",update:{colPos:e,sys_language_uid:parseInt(t.closest("[data-language-uid]").data("language-uid"),10)}}};DataHandler.process(i).then((t=>{t.hasErrors||window.location.reload()}))}}export default Paste;
\ No newline at end of file
+import DocumentService from"@typo3/core/document-service.js";import DataHandler from"@typo3/backend/ajax-data-handler.js";import{default as Modal}from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import"@typo3/backend/element/icon-element.js";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import RegularEvent from"@typo3/core/event/regular-event.js";class Paste{constructor(t){this.itemOnClipboardUid=0,this.itemOnClipboardTitle="",this.copyMode="",this.elementIdentifier=".t3js-page-ce",this.pasteAfterLinkTemplate="",this.pasteIntoLinkTemplate="",this.itemOnClipboardUid=t.itemOnClipboardUid,this.itemOnClipboardTitle=t.itemOnClipboardTitle,this.copyMode=t.copyMode,DocumentService.ready().then((()=>{document.querySelectorAll(".t3js-page-columns").length>0&&(this.generateButtonTemplates(),this.activatePasteIcons(),this.initializeEvents())}))}static determineColumn(t){const e=t.closest("[data-colpos]");return parseInt(e?.dataset?.colpos??"0",10)}initializeEvents(){new RegularEvent("click",((t,e)=>{t.preventDefault(),this.activatePasteModal(e)})).delegateTo(document,".t3js-paste")}generateButtonTemplates(){this.itemOnClipboardUid&&(this.pasteAfterLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-after btn btn-default btn-sm" title="'+TYPO3.lang?.pasteAfterRecord+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>',this.pasteIntoLinkTemplate='<button type="button" class="t3js-paste t3js-paste'+(this.copyMode?"-"+this.copyMode:"")+' t3js-paste-into btn btn-default btn-sm" title="'+TYPO3.lang?.pasteIntoColumn+'"><typo3-backend-icon identifier="actions-document-paste-into" size="small"></typo3-backend-icon></button>')}activatePasteIcons(){this.pasteAfterLinkTemplate&&this.pasteIntoLinkTemplate&&document.querySelectorAll(".t3js-page-new-ce").forEach((t=>{const e=t.parentElement.dataset.page?this.pasteIntoLinkTemplate:this.pasteAfterLinkTemplate;t.append(document.createRange().createContextualFragment(e))}))}activatePasteModal(t){const e=(TYPO3.lang["paste.modal.title.paste"]||"Paste record")+': "'+this.itemOnClipboardTitle+'"',a=TYPO3.lang["paste.modal.paste"]||"Do you want to paste the record to this position?";let n=[];n=[{text:TYPO3.lang["paste.modal.button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",trigger:(t,e)=>e.hideModal()},{text:TYPO3.lang["paste.modal.button.paste"]||"Paste",btnClass:"btn-"+Severity.getCssClass(SeverityEnum.warning),trigger:(e,a)=>{a.hideModal(),this.execute(t)}}],Modal.show(e,a,SeverityEnum.warning,n)}execute(t){const e=Paste.determineColumn(t),a=t.closest(this.elementIdentifier),n=a.dataset.uid;let s;s=void 0===n?parseInt(a.dataset.page,10):0-parseInt(n,10);const o={CB:{paste:"tt_content|"+s,pad:"normal",update:{colPos:e,sys_language_uid:parseInt(t.closest("[data-language-uid]").dataset.languageUid,10)}}};DataHandler.process(o).then((t=>{t.hasErrors||window.location.reload()}))}}export default Paste;
\ No newline at end of file
-- 
GitLab