From 438dea9ce45af4bfb6c9b7626695bc3fb277dddd Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Thu, 18 Aug 2022 12:22:23 +0200
Subject: [PATCH] [TASK] Allow `Element` and `DocumentFragment` in now stricter
 Modal API
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The configuration definition of `content` in the Modal API is extended
to accept objects of types `Element` and `DocumentFragment` as well,
which allows passing arbitrary DOM nodes to the API, if TypeScript is
used. Since jQuery is still the main actor in the Modal API, no further
adjustments are required for now as jQuery can consume the added types.

In the same run, the API definition got stricter as `Modal.advanced()`
allowed to pass any arbitrary configuration not covered by the internal
`Configuration` interface.

Resolves: #98164
Releases: main, 11.5
Change-Id: Ibf7cc836fcffa870a1eb4c5d4615c44e046cf192
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/75488
Tested-by: core-ci <typo3@b13.com>
Tested-by: Frank Nägler <frank.naegler@typo3.com>
Tested-by: Benjamin Franzke <bfr@qbus.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Frank Nägler <frank.naegler@typo3.com>
Reviewed-by: Benjamin Franzke <bfr@qbus.de>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
---
 Build/Sources/TypeScript/backend/modal.ts     | 53 +++++++++----------
 .../Resources/Public/JavaScript/modal.js      |  2 +-
 2 files changed, 26 insertions(+), 29 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/modal.ts b/Build/Sources/TypeScript/backend/modal.ts
index 7616664cf432..5fcbe4b6fd7f 100644
--- a/Build/Sources/TypeScript/backend/modal.ts
+++ b/Build/Sources/TypeScript/backend/modal.ts
@@ -56,19 +56,19 @@ enum Types {
 
 interface Button {
   text: string;
-  active: boolean;
+  active?: boolean;
   btnClass: string;
-  name: string;
-  trigger: (e: JQueryEventObject) => {};
-  dataAttributes: { [key: string]: string };
-  icon: string;
-  action: AbstractAction;
+  name?: string;
+  trigger?: (e?: JQueryEventObject) => void;
+  dataAttributes?: { [key: string]: string };
+  icon?: string;
+  action?: AbstractAction;
 }
 
 interface Configuration {
   type: Types;
   title: string;
-  content: string | JQuery;
+  content: string | JQuery | Element | DocumentFragment;
   severity: SeverityEnum;
   buttons: Array<Button>;
   style: string;
@@ -170,7 +170,7 @@ class Modal {
    * - confirm.button.ok
    *
    * @param {string} title The title for the confirm modal
-   * @param {string | JQuery} content The content for the conform modal, e.g. the main question
+   * @param {string | JQuery | Element | DocumentFragment} content The content for the conform modal, e.g. the main question
    * @param {SeverityEnum} severity Default SeverityEnum.warning
    * @param {Array<Button>} buttons An array with buttons, default no buttons
    * @param {Array<string>} additionalCssClasses Additional css classes to add to the modal
@@ -178,20 +178,20 @@ class Modal {
    */
   public confirm(
     title: string,
-    content: string | JQuery,
+    content: string | JQuery | Element | DocumentFragment,
     severity: SeverityEnum = SeverityEnum.warning,
-    buttons: Array<Object> = [],
+    buttons: Array<Button> = [],
     additionalCssClasses?: Array<string>,
   ): JQuery {
     if (buttons.length === 0) {
       buttons.push(
-        {
+        <Button>{
           text: $(this).data('button-close-text') || TYPO3.lang['button.cancel'] || 'Cancel',
           active: true,
           btnClass: 'btn-default',
           name: 'cancel',
         },
-        {
+        <Button>{
           text: $(this).data('button-ok-text') || TYPO3.lang['button.ok'] || 'OK',
           btnClass: 'btn-' + Severity.getCssClass(severity),
           name: 'ok',
@@ -232,7 +232,7 @@ class Modal {
   public loadUrl(
     title: string,
     severity: SeverityEnum = SeverityEnum.info,
-    buttons: Array<Object>,
+    buttons: Array<Button>,
     url: string,
     callback?: Function,
     target?: string,
@@ -252,17 +252,17 @@ class Modal {
    * Shows a dialog
    *
    * @param {string} title
-   * @param {string | JQuery} content
+   * @param {string | JQuery | Element | DocumentFragment} content
    * @param {number} severity
-   * @param {Array<Object>} buttons
+   * @param {Array<Button>} buttons
    * @param {Array<string>} additionalCssClasses
    * @returns {JQuery}
    */
   public show(
     title: string,
-    content: string | JQuery,
+    content: string | JQuery | Element | DocumentFragment,
     severity: SeverityEnum = SeverityEnum.info,
-    buttons?: Array<Object>,
+    buttons?: Array<Button>,
     additionalCssClasses?: Array<string>,
   ): JQuery {
     return this.advanced({
@@ -277,10 +277,8 @@ class Modal {
 
   /**
    * Loads modal by configuration
-   *
-   * @param {object} configuration configuration for the modal
    */
-  public advanced(configuration: { [key: string]: any }): JQuery {
+  public advanced(configuration: Partial<Configuration>): JQuery {
     // Validation of configuration
     configuration.type = typeof configuration.type === 'string' && configuration.type in Types
       ? configuration.type
@@ -310,7 +308,7 @@ class Modal {
       ? configuration.ajaxTarget
       : this.defaultConfiguration.ajaxTarget;
 
-    return this.generate(<Configuration>configuration);
+    return this.generate(configuration);
   }
 
   /**
@@ -380,9 +378,11 @@ class Modal {
       evt.preventDefault();
       const $element = $(evt.currentTarget);
       const content = $element.data('bs-content') || 'Are you sure?';
-      const severity = typeof SeverityEnum[$element.data('severity')] !== 'undefined'
-        ? SeverityEnum[$element.data('severity')]
-        : SeverityEnum.info;
+      let severity = SeverityEnum.info;
+      if ($element.data('severity') in SeverityEnum) {
+        const severityKey: keyof typeof SeverityEnum = $element.data('severity');
+        severity = SeverityEnum[severityKey];
+      }
       let url = $element.data('url') || null;
       if (url !== null) {
         const separator = url.includes('?') ? '&' : '?';
@@ -445,10 +445,7 @@ class Modal {
     });
   }
 
-  /**
-   * @param {Configuration} configuration
-   */
-  private generate(configuration: Configuration): JQuery {
+  private generate(configuration: Partial<Configuration>): JQuery {
     const currentModal = this.$template.clone();
     if (configuration.additionalCssClasses.length > 0) {
       for (let additionalClass of configuration.additionalCssClasses) {
diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
index ae991d7a8894..cf1801e4c437 100644
--- a/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
+++ b/typo3/sysext/backend/Resources/Public/JavaScript/modal.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import"bootstrap";import $ from"jquery";import{html,render}from"lit";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import Icons from"@typo3/backend/icons.js";import Severity from"@typo3/backend/severity.js";var Identifiers,Sizes,Styles,Types;!function(t){t.modal=".t3js-modal",t.content=".t3js-modal-content",t.title=".t3js-modal-title",t.close=".t3js-modal-close",t.body=".t3js-modal-body",t.footer=".t3js-modal-footer",t.iframe=".t3js-modal-iframe",t.iconPlaceholder=".t3js-modal-icon-placeholder"}(Identifiers||(Identifiers={})),function(t){t.small="small",t.default="default",t.medium="medium",t.large="large",t.full="full"}(Sizes||(Sizes={})),function(t){t.default="default",t.light="light",t.dark="dark"}(Styles||(Styles={})),function(t){t.default="default",t.ajax="ajax",t.iframe="iframe"}(Types||(Types={}));class Modal{constructor(t){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.$template=$('\n    <div class="t3js-modal modal fade">\n        <div class="modal-dialog">\n            <div class="t3js-modal-content modal-content">\n                <div class="modal-header">\n                    <h4 class="t3js-modal-title modal-title"></h4>\n                    <button class="t3js-modal-close close">\n                        <span aria-hidden="true">\n                            <span class="t3js-modal-icon-placeholder" data-icon="actions-close"></span>\n                        </span>\n                        <span class="visually-hidden"></span>\n                    </button>\n                </div>\n                <div class="t3js-modal-body modal-body"></div>\n                <div class="t3js-modal-footer modal-footer"></div>\n            </div>\n        </div>\n    </div>'),this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:$.noop(),ajaxCallback:$.noop(),ajaxTarget:null},this.securityUtility=t,$(document).on("modal-dismiss",this.dismiss),this.initializeMarkupTrigger(document)}static resolveEventNameTargetElement(t){const e=t.target,a=t.currentTarget;return e.dataset&&e.dataset.eventName?e:a.dataset&&a.dataset.eventName?a:null}static createModalResponseEventFromElement(t,e){return t&&t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.modal("hide")}confirm(t,e,a=SeverityEnum.warning,n=[],s){return 0===n.length&&n.push({text:$(this).data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:$(this).data("button-ok-text")||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"}),this.advanced({title:t,content:e,severity:a,buttons:n,additionalCssClasses:s,callback:t=>{t.on("button.clicked",t=>{"cancel"===t.target.getAttribute("name")?$(t.currentTarget).trigger("confirm.button.cancel"):"ok"===t.target.getAttribute("name")&&$(t.currentTarget).trigger("confirm.button.ok")})}})}loadUrl(t,e=SeverityEnum.info,a,n,s,i){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,ajaxTarget:i,content:n})}show(t,e,a=SeverityEnum.info,n,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:n,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.ajaxTarget="string"==typeof t.ajaxTarget?t.ajaxTarget:this.defaultConfiguration.ajaxTarget,this.generate(t)}setButtons(t){const e=this.currentModal.find(Identifiers.footer);if(t.length>0){e.empty();for(let a=0;a<t.length;a++){const n=t[a],s=$("<button />",{class:"btn"});s.html("<span>"+this.securityUtility.encodeHtml(n.text,!1)+"</span>"),n.active&&s.addClass("t3js-active"),""!==n.btnClass&&s.addClass(n.btnClass),""!==n.name&&s.attr("name",n.name),n.action?s.on("click",()=>{e.find("button").not(s).addClass("disabled"),n.action.execute(s.get(0)).then(()=>{this.currentModal.modal("hide")})}):n.trigger&&s.on("click",n.trigger),n.dataAttributes&&Object.keys(n.dataAttributes).length>0&&Object.keys(n.dataAttributes).map(t=>{s.attr("data-"+t,n.dataAttributes[t])}),n.icon&&s.prepend('<span class="t3js-modal-icon-placeholder" data-icon="'+n.icon+'"></span>'),e.append(s)}e.show(),e.find("button").on("click",t=>{$(t.currentTarget).trigger("button.clicked")})}else e.hide();return this.currentModal}initializeMarkupTrigger(t){$(t).on("click",".t3js-modal-trigger",t=>{t.preventDefault();const e=$(t.currentTarget),a=e.data("bs-content")||"Are you sure?",n=void 0!==SeverityEnum[e.data("severity")]?SeverityEnum[e.data("severity")]:SeverityEnum.info;let s=e.data("url")||null;if(null!==s){const t=s.includes("?")?"&":"?";s=s+t+$.param({data:e.data()})}this.advanced({type:null!==s?Types.ajax:Types.default,title:e.data("title")||"Alert",content:null!==s?s:a,severity:n,buttons:[{text:e.data("button-close-text")||TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:()=>{this.currentModal.trigger("modal-dismiss");const e=Modal.resolveEventNameTargetElement(t),a=Modal.createModalResponseEventFromElement(e,!1);null!==a&&e.dispatchEvent(a)}},{text:e.data("button-ok-text")||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(n),trigger:()=>{this.currentModal.trigger("modal-dismiss");const a=Modal.resolveEventNameTargetElement(t),n=Modal.createModalResponseEventFromElement(a,!0);null!==n&&a.dispatchEvent(n);let s=e.attr("data-uri")||e.data("href")||e.attr("href");s&&"#"!==s&&(t.target.ownerDocument.location.href=s),"submit"===t.currentTarget.getAttribute("type")&&(t.currentTarget.closest("form")?.submit(),"BUTTON"===t.currentTarget.tagName&&t.currentTarget.hasAttribute("form")&&t.target.ownerDocument.querySelector("form#"+t.currentTarget.getAttribute("form"))?.submit()),t.currentTarget.hasAttribute("data-target-form")&&t.target.ownerDocument.querySelector("form#"+t.currentTarget.getAttribute("data-target-form"))?.submit()}}]})})}generate(t){const e=this.$template.clone();if(t.additionalCssClasses.length>0)for(let a of t.additionalCssClasses)e.addClass(a);if(e.addClass("modal-type-"+t.type),e.addClass("modal-severity-"+Severity.getCssClass(t.severity)),e.addClass("modal-style-"+t.style),e.addClass("modal-size-"+t.size),e.attr("tabindex","-1"),e.find(Identifiers.title).text(t.title),e.find(Identifiers.close).on("click",()=>{e.modal("hide")}),"ajax"===t.type){const a=t.ajaxTarget?t.ajaxTarget:Identifiers.body,n=e.find(a);Icons.getIcon("spinner-circle",Icons.sizes.default,null,null,Icons.markupIdentifiers.inline).then(e=>{n.html('<div class="modal-loading">'+e+"</div>"),new AjaxRequest(t.content).get().finally(async()=>{this.currentModal.parent().length||this.currentModal.appendTo("body")}).then(async e=>{const n=await e.raw().text();this.currentModal.find(a).empty().append(n),t.ajaxCallback&&t.ajaxCallback(),this.currentModal.trigger("modal-loaded")}).catch(async e=>{const n=await e.raw().text(),s=this.currentModal.find(a).empty();n?s.append(n):render(html`<p><strong>Oops, received a ${e.response.status} response from </strong> <span class="text-break">${t.content}</span>.</p>`,s[0])})})}else"iframe"===t.type?(e.find(Identifiers.body).append($("<iframe />",{src:t.content,name:"modal_frame",class:"modal-iframe t3js-modal-iframe"})),e.find(Identifiers.iframe).on("load",()=>{e.find(Identifiers.title).text(e.find(Identifiers.iframe).get(0).contentDocument.title)})):("string"==typeof t.content&&(t.content=$("<p />").html(this.securityUtility.encodeHtml(t.content))),e.find(Identifiers.body).append(t.content));return e.on("shown.bs.modal",t=>{const e=$(t.currentTarget),a=e.prev(".modal-backdrop"),n=1e3+10*this.instances.length,s=n-10;e.css("z-index",n),a.css("z-index",s),e.find(Identifiers.footer).find(".t3js-active").first().focus(),e.find(Identifiers.iconPlaceholder).each((t,e)=>{Icons.getIcon($(e).data("icon"),Icons.sizes.small,null,null,Icons.markupIdentifiers.inline).then(t=>{this.currentModal.find(Identifiers.iconPlaceholder+"[data-icon="+$(t).data("identifier")+"]").replaceWith(t)})})}),e.on("hide.bs.modal",()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}}),e.on("hidden.bs.modal",t=>{e.trigger("modal-destroyed"),$(t.currentTarget).remove(),this.instances.length>0&&$("body").addClass("modal-open")}),e.on("show.bs.modal",e=>{this.currentModal=$(e.currentTarget),this.setButtons(t.buttons),this.instances.push(this.currentModal)}),e.on("modal-dismiss",t=>{$(t.currentTarget).modal("hide")}),t.callback&&t.callback(e),e.modal("show"),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal(new SecurityUtility),TYPO3.Modal=modalObject);export default modalObject;
\ No newline at end of file
+import"bootstrap";import $ from"jquery";import{html,render}from"lit";import{SeverityEnum}from"@typo3/backend/enum/severity.js";import AjaxRequest from"@typo3/core/ajax/ajax-request.js";import SecurityUtility from"@typo3/core/security-utility.js";import Icons from"@typo3/backend/icons.js";import Severity from"@typo3/backend/severity.js";var Identifiers,Sizes,Styles,Types;!function(t){t.modal=".t3js-modal",t.content=".t3js-modal-content",t.title=".t3js-modal-title",t.close=".t3js-modal-close",t.body=".t3js-modal-body",t.footer=".t3js-modal-footer",t.iframe=".t3js-modal-iframe",t.iconPlaceholder=".t3js-modal-icon-placeholder"}(Identifiers||(Identifiers={})),function(t){t.small="small",t.default="default",t.medium="medium",t.large="large",t.full="full"}(Sizes||(Sizes={})),function(t){t.default="default",t.light="light",t.dark="dark"}(Styles||(Styles={})),function(t){t.default="default",t.ajax="ajax",t.iframe="iframe"}(Types||(Types={}));class Modal{constructor(t){this.sizes=Sizes,this.styles=Styles,this.types=Types,this.currentModal=null,this.instances=[],this.$template=$('\n    <div class="t3js-modal modal fade">\n        <div class="modal-dialog">\n            <div class="t3js-modal-content modal-content">\n                <div class="modal-header">\n                    <h4 class="t3js-modal-title modal-title"></h4>\n                    <button class="t3js-modal-close close">\n                        <span aria-hidden="true">\n                            <span class="t3js-modal-icon-placeholder" data-icon="actions-close"></span>\n                        </span>\n                        <span class="visually-hidden"></span>\n                    </button>\n                </div>\n                <div class="t3js-modal-body modal-body"></div>\n                <div class="t3js-modal-footer modal-footer"></div>\n            </div>\n        </div>\n    </div>'),this.defaultConfiguration={type:Types.default,title:"Information",content:"No content provided, please check your <code>Modal</code> configuration.",severity:SeverityEnum.notice,buttons:[],style:Styles.default,size:Sizes.default,additionalCssClasses:[],callback:$.noop(),ajaxCallback:$.noop(),ajaxTarget:null},this.securityUtility=t,$(document).on("modal-dismiss",this.dismiss),this.initializeMarkupTrigger(document)}static resolveEventNameTargetElement(t){const e=t.target,a=t.currentTarget;return e.dataset&&e.dataset.eventName?e:a.dataset&&a.dataset.eventName?a:null}static createModalResponseEventFromElement(t,e){return t&&t.dataset.eventName?new CustomEvent(t.dataset.eventName,{bubbles:!0,detail:{result:e,payload:t.dataset.eventPayload||null}}):null}dismiss(){this.currentModal&&this.currentModal.modal("hide")}confirm(t,e,a=SeverityEnum.warning,n=[],s){return 0===n.length&&n.push({text:$(this).data("button-close-text")||TYPO3.lang["button.cancel"]||"Cancel",active:!0,btnClass:"btn-default",name:"cancel"},{text:$(this).data("button-ok-text")||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(a),name:"ok"}),this.advanced({title:t,content:e,severity:a,buttons:n,additionalCssClasses:s,callback:t=>{t.on("button.clicked",t=>{"cancel"===t.target.getAttribute("name")?$(t.currentTarget).trigger("confirm.button.cancel"):"ok"===t.target.getAttribute("name")&&$(t.currentTarget).trigger("confirm.button.ok")})}})}loadUrl(t,e=SeverityEnum.info,a,n,s,i){return this.advanced({type:Types.ajax,title:t,severity:e,buttons:a,ajaxCallback:s,ajaxTarget:i,content:n})}show(t,e,a=SeverityEnum.info,n,s){return this.advanced({type:Types.default,title:t,content:e,severity:a,buttons:n,additionalCssClasses:s})}advanced(t){return t.type="string"==typeof t.type&&t.type in Types?t.type:this.defaultConfiguration.type,t.title="string"==typeof t.title?t.title:this.defaultConfiguration.title,t.content="string"==typeof t.content||"object"==typeof t.content?t.content:this.defaultConfiguration.content,t.severity=void 0!==t.severity?t.severity:this.defaultConfiguration.severity,t.buttons=t.buttons||this.defaultConfiguration.buttons,t.size="string"==typeof t.size&&t.size in Sizes?t.size:this.defaultConfiguration.size,t.style="string"==typeof t.style&&t.style in Styles?t.style:this.defaultConfiguration.style,t.additionalCssClasses=t.additionalCssClasses||this.defaultConfiguration.additionalCssClasses,t.callback="function"==typeof t.callback?t.callback:this.defaultConfiguration.callback,t.ajaxCallback="function"==typeof t.ajaxCallback?t.ajaxCallback:this.defaultConfiguration.ajaxCallback,t.ajaxTarget="string"==typeof t.ajaxTarget?t.ajaxTarget:this.defaultConfiguration.ajaxTarget,this.generate(t)}setButtons(t){const e=this.currentModal.find(Identifiers.footer);if(t.length>0){e.empty();for(let a=0;a<t.length;a++){const n=t[a],s=$("<button />",{class:"btn"});s.html("<span>"+this.securityUtility.encodeHtml(n.text,!1)+"</span>"),n.active&&s.addClass("t3js-active"),""!==n.btnClass&&s.addClass(n.btnClass),""!==n.name&&s.attr("name",n.name),n.action?s.on("click",()=>{e.find("button").not(s).addClass("disabled"),n.action.execute(s.get(0)).then(()=>{this.currentModal.modal("hide")})}):n.trigger&&s.on("click",n.trigger),n.dataAttributes&&Object.keys(n.dataAttributes).length>0&&Object.keys(n.dataAttributes).map(t=>{s.attr("data-"+t,n.dataAttributes[t])}),n.icon&&s.prepend('<span class="t3js-modal-icon-placeholder" data-icon="'+n.icon+'"></span>'),e.append(s)}e.show(),e.find("button").on("click",t=>{$(t.currentTarget).trigger("button.clicked")})}else e.hide();return this.currentModal}initializeMarkupTrigger(t){$(t).on("click",".t3js-modal-trigger",t=>{t.preventDefault();const e=$(t.currentTarget),a=e.data("bs-content")||"Are you sure?";let n=SeverityEnum.info;if(e.data("severity")in SeverityEnum){const t=e.data("severity");n=SeverityEnum[t]}let s=e.data("url")||null;if(null!==s){const t=s.includes("?")?"&":"?";s=s+t+$.param({data:e.data()})}this.advanced({type:null!==s?Types.ajax:Types.default,title:e.data("title")||"Alert",content:null!==s?s:a,severity:n,buttons:[{text:e.data("button-close-text")||TYPO3.lang["button.close"]||"Close",active:!0,btnClass:"btn-default",trigger:()=>{this.currentModal.trigger("modal-dismiss");const e=Modal.resolveEventNameTargetElement(t),a=Modal.createModalResponseEventFromElement(e,!1);null!==a&&e.dispatchEvent(a)}},{text:e.data("button-ok-text")||TYPO3.lang["button.ok"]||"OK",btnClass:"btn-"+Severity.getCssClass(n),trigger:()=>{this.currentModal.trigger("modal-dismiss");const a=Modal.resolveEventNameTargetElement(t),n=Modal.createModalResponseEventFromElement(a,!0);null!==n&&a.dispatchEvent(n);let s=e.attr("data-uri")||e.data("href")||e.attr("href");s&&"#"!==s&&(t.target.ownerDocument.location.href=s),"submit"===t.currentTarget.getAttribute("type")&&(t.currentTarget.closest("form")?.submit(),"BUTTON"===t.currentTarget.tagName&&t.currentTarget.hasAttribute("form")&&t.target.ownerDocument.querySelector("form#"+t.currentTarget.getAttribute("form"))?.submit()),t.currentTarget.hasAttribute("data-target-form")&&t.target.ownerDocument.querySelector("form#"+t.currentTarget.getAttribute("data-target-form"))?.submit()}}]})})}generate(t){const e=this.$template.clone();if(t.additionalCssClasses.length>0)for(let a of t.additionalCssClasses)e.addClass(a);if(e.addClass("modal-type-"+t.type),e.addClass("modal-severity-"+Severity.getCssClass(t.severity)),e.addClass("modal-style-"+t.style),e.addClass("modal-size-"+t.size),e.attr("tabindex","-1"),e.find(Identifiers.title).text(t.title),e.find(Identifiers.close).on("click",()=>{e.modal("hide")}),"ajax"===t.type){const a=t.ajaxTarget?t.ajaxTarget:Identifiers.body,n=e.find(a);Icons.getIcon("spinner-circle",Icons.sizes.default,null,null,Icons.markupIdentifiers.inline).then(e=>{n.html('<div class="modal-loading">'+e+"</div>"),new AjaxRequest(t.content).get().finally(async()=>{this.currentModal.parent().length||this.currentModal.appendTo("body")}).then(async e=>{const n=await e.raw().text();this.currentModal.find(a).empty().append(n),t.ajaxCallback&&t.ajaxCallback(),this.currentModal.trigger("modal-loaded")}).catch(async e=>{const n=await e.raw().text(),s=this.currentModal.find(a).empty();n?s.append(n):render(html`<p><strong>Oops, received a ${e.response.status} response from </strong> <span class="text-break">${t.content}</span>.</p>`,s[0])})})}else"iframe"===t.type?(e.find(Identifiers.body).append($("<iframe />",{src:t.content,name:"modal_frame",class:"modal-iframe t3js-modal-iframe"})),e.find(Identifiers.iframe).on("load",()=>{e.find(Identifiers.title).text(e.find(Identifiers.iframe).get(0).contentDocument.title)})):("string"==typeof t.content&&(t.content=$("<p />").html(this.securityUtility.encodeHtml(t.content))),e.find(Identifiers.body).append(t.content));return e.on("shown.bs.modal",t=>{const e=$(t.currentTarget),a=e.prev(".modal-backdrop"),n=1e3+10*this.instances.length,s=n-10;e.css("z-index",n),a.css("z-index",s),e.find(Identifiers.footer).find(".t3js-active").first().focus(),e.find(Identifiers.iconPlaceholder).each((t,e)=>{Icons.getIcon($(e).data("icon"),Icons.sizes.small,null,null,Icons.markupIdentifiers.inline).then(t=>{this.currentModal.find(Identifiers.iconPlaceholder+"[data-icon="+$(t).data("identifier")+"]").replaceWith(t)})})}),e.on("hide.bs.modal",()=>{if(this.instances.length>0){const t=this.instances.length-1;this.instances.splice(t,1),this.currentModal=this.instances[t-1]}}),e.on("hidden.bs.modal",t=>{e.trigger("modal-destroyed"),$(t.currentTarget).remove(),this.instances.length>0&&$("body").addClass("modal-open")}),e.on("show.bs.modal",e=>{this.currentModal=$(e.currentTarget),this.setButtons(t.buttons),this.instances.push(this.currentModal)}),e.on("modal-dismiss",t=>{$(t.currentTarget).modal("hide")}),t.callback&&t.callback(e),e.modal("show"),e}}let modalObject=null;try{parent&&parent.window.TYPO3&&parent.window.TYPO3.Modal?(parent.window.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=parent.window.TYPO3.Modal):top&&top.TYPO3.Modal&&(top.TYPO3.Modal.initializeMarkupTrigger(document),modalObject=top.TYPO3.Modal)}catch{}modalObject||(modalObject=new Modal(new SecurityUtility),TYPO3.Modal=modalObject);export default modalObject;
\ No newline at end of file
-- 
GitLab