diff --git a/Build/Sources/TypeScript/backend/document-save-actions.ts b/Build/Sources/TypeScript/backend/document-save-actions.ts index d7575d902d76ee9b428d732657e3c1ae45eedad8..e7bc9dc8b6d9a65fca0e74aecf13c65d3e3548dd 100644 --- a/Build/Sources/TypeScript/backend/document-save-actions.ts +++ b/Build/Sources/TypeScript/backend/document-save-actions.ts @@ -22,6 +22,7 @@ type SubmitTriggerHTMLElement = HTMLAnchorElement|HTMLButtonElement; /** * Module: @typo3/backend/document-save-actions + * @deprecated: use @typo3/backend/form/submit-interceptor instead */ class DocumentSaveActions { private static instance: DocumentSaveActions = null; @@ -29,6 +30,7 @@ class DocumentSaveActions { private readonly preSubmitCallbacks: PreSubmitCallback[] = []; private constructor() { + console.warn('The module `@typo3/backend/document-save-actions.js` has been deprecated and will be removed in TYPO3 v14. Please consider migrating to `@typo3/backend/form/submit-interceptor.js` instead.'); DocumentService.ready().then((): void => { this.initializeSaveHandling(); }); diff --git a/Build/Sources/TypeScript/backend/form-engine-validation.ts b/Build/Sources/TypeScript/backend/form-engine-validation.ts index 1499d0ca75e01f1e83cc30ab643d07762d5c7965..f9e75894736b0fa103d82d41255780d057fda787 100644 --- a/Build/Sources/TypeScript/backend/form-engine-validation.ts +++ b/Build/Sources/TypeScript/backend/form-engine-validation.ts @@ -19,13 +19,13 @@ import $ from 'jquery'; import { DateTime } from 'luxon'; import Md5 from '@typo3/backend/hashing/md5'; -import DocumentSaveActions from '@typo3/backend/document-save-actions'; import Modal from '@typo3/backend/modal'; import Severity from '@typo3/backend/severity'; import Utility from './utility'; import RegularEvent from '@typo3/core/event/regular-event'; import DomHelper from '@typo3/backend/utility/dom-helper'; import { selector } from '@typo3/core/literals'; +import SubmitInterceptor from '@typo3/backend/form/submit-interceptor'; type FormEngineFieldElement = HTMLInputElement|HTMLTextAreaElement|HTMLSelectElement; type CustomEvaluationCallback = (value: string) => string; @@ -747,7 +747,8 @@ export default (function() { }; FormEngineValidation.registerSubmitCallback = function () { - DocumentSaveActions.getInstance().addPreSubmitCallback((): boolean => { + const submitInterceptor = new SubmitInterceptor(formEngineFormElement); + submitInterceptor.addPreSubmitCallback((): boolean => { if (document.querySelector('.' + FormEngineValidation.errorClass) === null) { return true; } diff --git a/Build/Sources/TypeScript/backend/form/submit-interceptor.ts b/Build/Sources/TypeScript/backend/form/submit-interceptor.ts new file mode 100644 index 0000000000000000000000000000000000000000..5d632bab93e6fec0be08fcef684903db13e96398 --- /dev/null +++ b/Build/Sources/TypeScript/backend/form/submit-interceptor.ts @@ -0,0 +1,66 @@ +/* + * 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 Icons from '@typo3/backend/icons'; + +export type PreSubmitCallback = (e: Event) => boolean; + +/** + * Module: @typo3/backend/form/submit-interceptor + */ +export default class SubmitInterceptor { + private isSubmitting: boolean = false; + private readonly preSubmitCallbacks: PreSubmitCallback[] = []; + + constructor(form: HTMLFormElement) { + form.addEventListener('submit', this.submitHandler.bind(this)); + } + + public addPreSubmitCallback(callback: PreSubmitCallback): SubmitInterceptor { + if (typeof callback !== 'function') { + throw 'callback must be a function.'; + } + + this.preSubmitCallbacks.push(callback); + + return this; + } + + private submitHandler(e: SubmitEvent): void { + if (this.isSubmitting) { + return; + } + + for (const callback of this.preSubmitCallbacks) { + const callbackResult = callback(e); + if (!callbackResult) { + e.preventDefault(); + return; + } + } + + this.isSubmitting = true; + + if (e.submitter !== null) { + if (e.submitter instanceof HTMLInputElement || e.submitter instanceof HTMLButtonElement) { + e.submitter.disabled = true; + } + Icons.getIcon('spinner-circle', Icons.sizes.small).then((markup: string): void => { + e.submitter.replaceChild(document.createRange().createContextualFragment(markup), e.submitter.querySelector('.t3js-icon')); + }).catch(() => { + // Catch error in case the promise was not resolved + // e.g. loading a new page + }); + } + } +} diff --git a/Build/Sources/TypeScript/scheduler/scheduler.ts b/Build/Sources/TypeScript/scheduler/scheduler.ts index 1b77761ab3aac47642d5be6df38caa719de524bd..697e8fc133cec87a45a94ac1539bce3afdc5f3d9 100644 --- a/Build/Sources/TypeScript/scheduler/scheduler.ts +++ b/Build/Sources/TypeScript/scheduler/scheduler.ts @@ -12,7 +12,6 @@ */ import SortableTable from '@typo3/backend/sortable-table'; -import DocumentSaveActions from '@typo3/backend/document-save-actions'; import RegularEvent from '@typo3/core/event/regular-event'; import Modal from '@typo3/backend/modal'; import Icons from '@typo3/backend/icons'; @@ -23,6 +22,7 @@ import DateTimePicker from '@typo3/backend/date-time-picker'; import { MultiRecordSelectionSelectors } from '@typo3/backend/multi-record-selection'; import Severity from '@typo3/backend/severity'; import DocumentService from '@typo3/core/document-service'; +import SubmitInterceptor from '@typo3/backend/form/submit-interceptor'; interface TableNumberMapping { [s: string]: number; @@ -35,12 +35,11 @@ interface TableNumberMapping { class Scheduler { constructor() { DocumentService.ready().then((): void => { + this.initializeSubmitInterceptor(); this.initializeEvents(); this.initializeDefaultStates(); this.initializeCloseConfirm(); }); - - DocumentSaveActions.registerEvents(); } private static updateClearableInputs(): void { @@ -161,6 +160,15 @@ class Scheduler { (document.querySelector('#task_multiple_row') as HTMLElement).hidden = !taskIsRecurring; } + private initializeSubmitInterceptor(): void { + const schedulerForm: HTMLFormElement = document.querySelector('form[name=tx_scheduler_form]'); + if (!schedulerForm) { + return; + } + + new SubmitInterceptor(schedulerForm); + } + /** * Registers listeners */ @@ -233,6 +241,27 @@ class Scheduler { new RegularEvent('multiRecordSelection:action:go_cron', this.executeTasks.bind(this)).bindTo(document); window.addEventListener('message', this.listenOnElementBrowser.bind(this)); + + new RegularEvent('click', (e: Event): void => { + e.preventDefault(); + + this.saveDocument(e); + }).delegateTo(document, 'button[form]'); + } + + private saveDocument(e: Event): void { + const schedulerForm: HTMLFormElement = document.querySelector('form[name=tx_scheduler_form]'); + if (!schedulerForm) { + return; + } + + const hidden = document.createElement('input') + hidden.type = 'hidden'; + hidden.value = 'save'; + hidden.name = 'CMD'; + + schedulerForm.append(hidden); + schedulerForm.requestSubmit(e.target as HTMLElement); } /** @@ -366,7 +395,7 @@ class Scheduler { hidden.value = 'saveclose'; hidden.name = 'CMD'; - schedulerForm.append(hidden) + schedulerForm.append(hidden); schedulerForm.submit(); }, } diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js index 71b411c0b97135112a33974e407bbd307c0985f8..67bf51498529648dfc44c987bbfdafdb3ddd9720 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/document-save-actions.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import DocumentService from"@typo3/core/document-service.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{selector}from"@typo3/core/literals.js";class DocumentSaveActions{constructor(){this.preventDoubleClick=!1,this.preSubmitCallbacks=[],DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}static registerEvents(){DocumentSaveActions.getInstance()}addPreSubmitCallback(e){if("function"!=typeof e)throw"callback must be a function.";this.preSubmitCallbacks.push(e)}initializeSaveHandling(){const e=document.querySelector(".t3js-module-docheader");if(null===e)return;const t=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");new RegularEvent("click",((e,t)=>{if(this.preventDoubleClick)return;const n=this.getAttachedForm(t);if(null!==n){for(const t of this.preSubmitCallbacks){if(!t(e))return void e.preventDefault()}this.preventDoubleClick=!0,this.attachSaveFieldToForm(n,t),n.addEventListener("submit",(()=>{const e=t.closest(".t3js-splitbutton");let n;null!==e?(n=e.firstElementChild,e.querySelectorAll("button").forEach((e=>{e.disabled=!0}))):(n=t,n instanceof HTMLAnchorElement?n.classList.add("disabled"):n.disabled=!0),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{n.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))})).catch((()=>{}))}),{once:!0})}})).delegateTo(e,t)}getAttachedForm(e){let t;return t=e instanceof HTMLAnchorElement?document.querySelector(selector`#${e.dataset.form}`):e.form,t||(t=e.closest("form")),t}attachSaveFieldToForm(e,t){const n=e.name+"_save_field";let a=document.getElementById(n);null===a&&(a=document.createElement("input"),a.id=n,a.type="hidden",e.append(a)),a.name=t instanceof HTMLAnchorElement?t.dataset.name:t.name,a.value=t instanceof HTMLAnchorElement?t.dataset.value:t.value}}DocumentSaveActions.instance=null;export default DocumentSaveActions; \ No newline at end of file +import DocumentService from"@typo3/core/document-service.js";import Icons from"@typo3/backend/icons.js";import RegularEvent from"@typo3/core/event/regular-event.js";import{selector}from"@typo3/core/literals.js";class DocumentSaveActions{constructor(){this.preventDoubleClick=!1,this.preSubmitCallbacks=[],console.warn("The module `@typo3/backend/document-save-actions.js` has been deprecated and will be removed in TYPO3 v14. Please consider migrating to `@typo3/backend/form/submit-interceptor.js` instead."),DocumentService.ready().then((()=>{this.initializeSaveHandling()}))}static getInstance(){return null===DocumentSaveActions.instance&&(DocumentSaveActions.instance=new DocumentSaveActions),DocumentSaveActions.instance}static registerEvents(){DocumentSaveActions.getInstance()}addPreSubmitCallback(e){if("function"!=typeof e)throw"callback must be a function.";this.preSubmitCallbacks.push(e)}initializeSaveHandling(){const e=document.querySelector(".t3js-module-docheader");if(null===e)return;const t=["button[form]",'button[name^="_save"]','a[data-name^="_save"]','button[name="CMD"][value^="save"]','a[data-name="CMD"][data-value^="save"]'].join(",");new RegularEvent("click",((e,t)=>{if(this.preventDoubleClick)return;const n=this.getAttachedForm(t);if(null!==n){for(const t of this.preSubmitCallbacks){if(!t(e))return void e.preventDefault()}this.preventDoubleClick=!0,this.attachSaveFieldToForm(n,t),n.addEventListener("submit",(()=>{const e=t.closest(".t3js-splitbutton");let n;null!==e?(n=e.firstElementChild,e.querySelectorAll("button").forEach((e=>{e.disabled=!0}))):(n=t,n instanceof HTMLAnchorElement?n.classList.add("disabled"):n.disabled=!0),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{n.replaceChild(document.createRange().createContextualFragment(e),t.querySelector(".t3js-icon"))})).catch((()=>{}))}),{once:!0})}})).delegateTo(e,t)}getAttachedForm(e){let t;return t=e instanceof HTMLAnchorElement?document.querySelector(selector`#${e.dataset.form}`):e.form,t||(t=e.closest("form")),t}attachSaveFieldToForm(e,t){const n=e.name+"_save_field";let a=document.getElementById(n);null===a&&(a=document.createElement("input"),a.id=n,a.type="hidden",e.append(a)),a.name=t instanceof HTMLAnchorElement?t.dataset.name:t.name,a.value=t instanceof HTMLAnchorElement?t.dataset.value:t.value}}DocumentSaveActions.instance=null;export default DocumentSaveActions; \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js index 306f60f95db139422332536b5d22e820e6b37f81..65955c7e7c85b673d9f5026ecf9ca9539745e8dd 100644 --- a/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form-engine-validation.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{selector}from"@typo3/core/literals.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"};let formEngineFormElement;const customEvaluations=new Map;return FormEngineValidation.initialize=function(e){formEngineFormElement=e,formEngineFormElement.querySelectorAll("."+FormEngineValidation.errorClass).forEach((e=>e.classList.remove(FormEngineValidation.errorClass))),FormEngineValidation.initializeInputFields(),new RegularEvent("change",((e,n)=>{FormEngineValidation.validateField(n),FormEngineValidation.markFieldAsChanged(n)})).delegateTo(formEngineFormElement,FormEngineValidation.rulesSelector),FormEngineValidation.registerSubmitCallback();const n=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(n),FormEngineValidation.lastDate=FormEngineValidation.getDate(n),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){formEngineFormElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((e=>{const n=JSON.parse(e.dataset.formengineInputParams).field,t=formEngineFormElement.querySelector(selector`[name="${n}"]`);"formengineInputInitialized"in e.dataset||(t.dataset.config=e.dataset.formengineInputParams,FormEngineValidation.initializeInputField(n))}))},FormEngineValidation.initializeInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=FormEngineValidation.formatByEvals(e,n.value);a.length&&(t.value=a)}new RegularEvent("change",(()=>{FormEngineValidation.updateInputField(t.dataset.formengineInputName)})).bindTo(t),t.dataset.formengineInputInitialized="true"},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatByEvals=function(e,n){if(void 0!==e.evalList){const t=Utility.trimExplode(",",e.evalList);for(const a of t)n=FormEngineValidation.formatValue(a,n,e)}return n},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=FormEngineValidation.processByEvals(e,t.value),i=FormEngineValidation.formatByEvals(e,a);n.value!==a&&(n.disabled&&n.dataset.enableOnModification&&(n.disabled=!1),n.value=a,n.dispatchEvent(new Event("change")),t.value=i)}},FormEngineValidation.validateField=function(e,n){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.validateField() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return n;if(n=n||e.value||"",void 0===e.dataset.formengineValidationRules)return n;const t=JSON.parse(e.dataset.formengineValidationRules);let a=!1,i=0;const o=n;let r,l,s;Array.isArray(n)||(n=n.trimStart());for(const o of t){if(a)break;switch(o.type){case"required":""===n&&(a=!0,e.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:parseInt(e.value,10),void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0))),void 0!==o.lower){const e=1*o.lower;!isNaN(e)&&parseInt(n,10)<e&&(a=!0)}if(void 0!==o.upper){const e=1*o.upper;!isNaN(e)&&parseInt(n,10)>e&&(a=!0)}}break;case"select":case"category":(o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:e instanceof HTMLSelectElement?e.querySelectorAll("option:checked").length:e.querySelectorAll("input[value]:checked").length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"group":case"folder":case"inline":(o.minItems||o.maxItems)&&(i=Utility.trimExplode(",",e.value).length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"min":(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length>0&&e.value.length<e.minLength&&(a=!0)}}const m=!a,d=e.closest(FormEngineValidation.markerSelector);return null!==d&&d.classList.toggle(FormEngineValidation.errorClass,!m),FormEngineValidation.markParentTab(e,m),formEngineFormElement.dispatchEvent(new CustomEvent("t3-formengine-postfieldvalidation",{cancelable:!1,bubbles:!0})),o},FormEngineValidation.processByEvals=function(e,n){if(void 0!==e.evalList){const t=Utility.trimExplode(",",e.evalList);for(const a of t)n=FormEngineValidation.processValue(a,n,e)}return n},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&formEngineFormElement.querySelectorAll(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").forEach((e=>{e.classList.remove(FormEngineValidation.errorClass,"has-validation-error")}));const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector))if(null===e.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted")){let n=!1;const t=e.value,a=FormEngineValidation.validateField(e,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)n=!0;else for(let e=0;e<a.length;e++)if(a[e]!==t[e]){n=!0;break}}else a.length&&t!==a&&(n=!0);n&&(e.disabled&&e.dataset.enableOnModification&&(e.disabled=!1),e.value=a)}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.markFieldAsChanged() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a=t.startsWith("-");t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){DomHelper.parents(e,".tab-pane").forEach((e=>{n&&(n=null===e.querySelector(".has-error"));const t=e.id;formEngineFormElement.querySelector('a[href="#'+t+'"]').closest(".t3js-tabmenu-item").classList.toggle("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((()=>{if(null===document.querySelector("."+FormEngineValidation.errorClass))return!0;const e=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);return e.addEventListener("button.clicked",(()=>e.hideModal())),!1}))},FormEngineValidation}()); \ No newline at end of file +import $ from"jquery";import{DateTime}from"luxon";import Md5 from"@typo3/backend/hashing/md5.js";import Modal from"@typo3/backend/modal.js";import Severity from"@typo3/backend/severity.js";import Utility from"@typo3/backend/utility.js";import RegularEvent from"@typo3/core/event/regular-event.js";import DomHelper from"@typo3/backend/utility/dom-helper.js";import{selector}from"@typo3/core/literals.js";import SubmitInterceptor from"@typo3/backend/form/submit-interceptor.js";export default(function(){const FormEngineValidation={rulesSelector:"[data-formengine-validation-rules]",inputSelector:"[data-formengine-input-params]",markerSelector:".t3js-formengine-validation-marker",groupFieldHiddenElement:".t3js-formengine-field-group input[type=hidden]",relatedFieldSelector:"[data-relatedfieldname]",errorClass:"has-error",lastYear:0,lastDate:0,lastTime:0,passwordDummy:"********"};let formEngineFormElement;const customEvaluations=new Map;return FormEngineValidation.initialize=function(e){formEngineFormElement=e,formEngineFormElement.querySelectorAll("."+FormEngineValidation.errorClass).forEach((e=>e.classList.remove(FormEngineValidation.errorClass))),FormEngineValidation.initializeInputFields(),new RegularEvent("change",((e,n)=>{FormEngineValidation.validateField(n),FormEngineValidation.markFieldAsChanged(n)})).delegateTo(formEngineFormElement,FormEngineValidation.rulesSelector),FormEngineValidation.registerSubmitCallback();const n=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(n),FormEngineValidation.lastDate=FormEngineValidation.getDate(n),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){formEngineFormElement.querySelectorAll(FormEngineValidation.inputSelector).forEach((e=>{const n=JSON.parse(e.dataset.formengineInputParams).field,t=formEngineFormElement.querySelector(selector`[name="${n}"]`);"formengineInputInitialized"in e.dataset||(t.dataset.config=e.dataset.formengineInputParams,FormEngineValidation.initializeInputField(n))}))},FormEngineValidation.initializeInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=FormEngineValidation.formatByEvals(e,n.value);a.length&&(t.value=a)}new RegularEvent("change",(()=>{FormEngineValidation.updateInputField(t.dataset.formengineInputName)})).bindTo(t),t.dataset.formengineInputInitialized="true"},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatByEvals=function(e,n){if(void 0!==e.evalList){const t=Utility.trimExplode(",",e.evalList);for(const a of t)n=FormEngineValidation.formatValue(a,n,e)}return n},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=DateTime.fromISO(n.toString(),{zone:"utc"}).toFormat("dd-MM-yyyy")}else{if(""===n||"0"===n)return"";if(a=parseInt(n.toString(),10),isNaN(a))return"";i=new Date(1e3*a);o=i.getUTCDate().toString(10).padStart(2,"0")+"-"+(i.getUTCMonth()+1).toString(10).padStart(2,"0")+"-"+this.getYear(i)}break;case"datetime":if(""===n||"0"===n)return"";o=(FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t)).trim();break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=DateTime.fromISO(n.toString(),{zone:"utc"});else{if(""===n||"0"===n)return"";if(a="number"==typeof n?n:parseInt(n),isNaN(a))return"";r=DateTime.fromSeconds(a,{zone:"utc"})}o="timesec"===e?r.toFormat("HH:mm:ss"):r.toFormat("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n.toString()}return o},FormEngineValidation.updateInputField=function(e){const n=formEngineFormElement.querySelector(selector`[name="${e}"]`),t=formEngineFormElement.querySelector(selector`[data-formengine-input-name="${e}"]`);if(void 0!==n.dataset.config){const e=JSON.parse(n.dataset.config),a=FormEngineValidation.processByEvals(e,t.value),i=FormEngineValidation.formatByEvals(e,a);n.value!==a&&(n.disabled&&n.dataset.enableOnModification&&(n.disabled=!1),n.value=a,n.dispatchEvent(new Event("change")),t.value=i)}},FormEngineValidation.validateField=function(e,n){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.validateField() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return n;if(n=n||e.value||"",void 0===e.dataset.formengineValidationRules)return n;const t=JSON.parse(e.dataset.formengineValidationRules);let a=!1,i=0;const o=n;let r,l,s;Array.isArray(n)||(n=n.trimStart());for(const o of t){if(a)break;switch(o.type){case"required":""===n&&(a=!0,e.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:parseInt(e.value,10),void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0))),void 0!==o.lower){const e=1*o.lower;!isNaN(e)&&parseInt(n,10)<e&&(a=!0)}if(void 0!==o.upper){const e=1*o.upper;!isNaN(e)&&parseInt(n,10)>e&&(a=!0)}}break;case"select":case"category":(o.minItems||o.maxItems)&&(r=formEngineFormElement.querySelector(selector`[name="${e.dataset.relatedfieldname}"]`),i=null!==r?Utility.trimExplode(",",r.value).length:e instanceof HTMLSelectElement?e.querySelectorAll("option:checked").length:e.querySelectorAll("input[value]:checked").length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"group":case"folder":case"inline":(o.minItems||o.maxItems)&&(i=Utility.trimExplode(",",e.value).length,void 0!==o.minItems&&(l=1*o.minItems,!isNaN(l)&&i<l&&(a=!0)),void 0!==o.maxItems&&(s=1*o.maxItems,!isNaN(s)&&i>s&&(a=!0)));break;case"min":(e instanceof HTMLInputElement||e instanceof HTMLTextAreaElement)&&e.value.length>0&&e.value.length<e.minLength&&(a=!0)}}const m=!a,d=e.closest(FormEngineValidation.markerSelector);return null!==d&&d.classList.toggle(FormEngineValidation.errorClass,!m),FormEngineValidation.markParentTab(e,m),formEngineFormElement.dispatchEvent(new CustomEvent("t3-formengine-postfieldvalidation",{cancelable:!1,bubbles:!0})),o},FormEngineValidation.processByEvals=function(e,n){if(void 0!==e.evalList){const t=Utility.trimExplode(",",e.evalList);for(const a of t)n=FormEngineValidation.processValue(a,n,e)}return n},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o=0,r=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",o=0;o<n.length;o++){const t=n.substr(o,1);let i="_"===t||"-"===t,r=t>="a"&&t<="z"||t>="A"&&t<="Z",l=t>="0"&&t<="9";switch(e){case"alphanum":i=!1;break;case"alpha":l=!1,i=!1;break;case"num":r=!1,i=!1}(r||l||i)&&(a+=t)}a!==n&&(r=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;r=a;break;case"nospace":r=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(r=Md5.hash(n));break;case"upper":r=n.toUpperCase();break;case"lower":r=n.toLowerCase();break;case"integer":""!==n&&(r=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(r=FormEngineValidation.parseDouble(n));break;case"trim":r=String(n).trim();break;case"datetime":""!==n&&(r=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(r=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(r=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(r=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?r=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(r=TBE_EDITOR.customEvalFunctions[e](n))}return r},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&formEngineFormElement.querySelectorAll(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").forEach((e=>{e.classList.remove(FormEngineValidation.errorClass,"has-validation-error")}));const n=e||document;for(const e of n.querySelectorAll(FormEngineValidation.rulesSelector))if(null===e.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted")){let n=!1;const t=e.value,a=FormEngineValidation.validateField(e,t);if(Array.isArray(a)&&Array.isArray(t)){if(a.length!==t.length)n=!0;else for(let e=0;e<a.length;e++)if(a[e]!==t[e]){n=!0;break}}else a.length&&t!==a&&(n=!0);n&&(e.disabled&&e.dataset.enableOnModification&&(e.disabled=!1),e.value=a)}},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(console.warn("Passing a jQuery element to FormEngineValidation.markFieldAsChanged() is deprecated and will be removed in TYPO3 v14."),console.trace(),e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.parseInt=function(e){if(!e)return 0;const n=parseInt(""+e,10);return isNaN(n)?0:n},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,.-]/g,"");const a=t.startsWith("-");t=t.replace(/-/g,""),t=t.replace(/,/g,"."),-1===t.indexOf(".")&&(t+=".0");const i=t.split("."),o=i.pop();let r=Number(i.join("")+"."+o);return a&&(r*=-1),t=r.toFixed(n),t},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substring(n+1));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substring(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=DateTime.fromFormat(e,"dd-MM-yyyy",{zone:"utc"}).toUnixInteger(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"HH:mm:ss":"HH:mm";return FormEngineValidation.lastTime=DateTime.fromFormat(e,t,{zone:"utc"}).set({year:1970,month:1,day:1}).toUnixInteger(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){let n=parseInt(e,10);return isNaN(n)&&(n=FormEngineValidation.getYear(new Date)),FormEngineValidation.lastYear=n,FormEngineValidation.lastYear},FormEngineValidation.getYear=function(e){return null===e?null:e.getUTCFullYear()},FormEngineValidation.getDate=function(e){const n=new Date(FormEngineValidation.getYear(e),e.getUTCMonth(),e.getUTCDate());return FormEngineValidation.getTimestamp(n)},FormEngineValidation.pol=function(foreign,value){return eval(("-"==foreign?"-":"")+value)},FormEngineValidation.getTimestamp=function(e){return Date.parse(e instanceof Date?e.toISOString():e)/1e3},FormEngineValidation.getTime=function(e){return 60*e.getUTCHours()*60+60*e.getUTCMinutes()+FormEngineValidation.getSecs(e)},FormEngineValidation.getSecs=function(e){return e.getUTCSeconds()},FormEngineValidation.getTimeSecs=function(e){return 60*e.getHours()*60+60*e.getMinutes()+e.getSeconds()},FormEngineValidation.markParentTab=function(e,n){DomHelper.parents(e,".tab-pane").forEach((e=>{n&&(n=null===e.querySelector(".has-error"));const t=e.id;formEngineFormElement.querySelector('a[href="#'+t+'"]').closest(".t3js-tabmenu-item").classList.toggle("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){new SubmitInterceptor(formEngineFormElement).addPreSubmitCallback((()=>{if(null===document.querySelector("."+FormEngineValidation.errorClass))return!0;const e=Modal.confirm(TYPO3.lang.alert||"Alert",TYPO3.lang["FormEngine.fieldsMissing"],Severity.error,[{text:TYPO3.lang["button.ok"]||"OK",active:!0,btnClass:"btn-default",name:"ok"}]);return e.addEventListener("button.clicked",(()=>e.hideModal())),!1}))},FormEngineValidation}()); \ No newline at end of file diff --git a/typo3/sysext/backend/Resources/Public/JavaScript/form/submit-interceptor.js b/typo3/sysext/backend/Resources/Public/JavaScript/form/submit-interceptor.js new file mode 100644 index 0000000000000000000000000000000000000000..e4980e37fc7a3aefabbafb45299abb154ad663b1 --- /dev/null +++ b/typo3/sysext/backend/Resources/Public/JavaScript/form/submit-interceptor.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! + */ +import Icons from"@typo3/backend/icons.js";export default class SubmitInterceptor{constructor(t){this.isSubmitting=!1,this.preSubmitCallbacks=[],t.addEventListener("submit",this.submitHandler.bind(this))}addPreSubmitCallback(t){if("function"!=typeof t)throw"callback must be a function.";return this.preSubmitCallbacks.push(t),this}submitHandler(t){if(!this.isSubmitting){for(const e of this.preSubmitCallbacks){if(!e(t))return void t.preventDefault()}this.isSubmitting=!0,null!==t.submitter&&((t.submitter instanceof HTMLInputElement||t.submitter instanceof HTMLButtonElement)&&(t.submitter.disabled=!0),Icons.getIcon("spinner-circle",Icons.sizes.small).then((e=>{t.submitter.replaceChild(document.createRange().createContextualFragment(e),t.submitter.querySelector(".t3js-icon"))})).catch((()=>{})))}}} \ No newline at end of file diff --git a/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-103528-DeprecatedDocumentSaveActionsModule.rst b/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-103528-DeprecatedDocumentSaveActionsModule.rst new file mode 100644 index 0000000000000000000000000000000000000000..495650329b92abc9129d0cc4fa8b6cd7130e3b0c --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/13.1/Deprecation-103528-DeprecatedDocumentSaveActionsModule.rst @@ -0,0 +1,69 @@ +.. include:: /Includes.rst.txt + +.. _deprecation-103528-1712153304: + +============================================================== +Deprecation: #103528 - Deprecated `DocumentSaveActions` module +============================================================== + +See :issue:`103528` + +Description +=========== + +The JavaScript module :js:`@typo3/backend/document-save-actions.js` was +introduced in TYPO3 v7 to add some interactivity in FormEngine context. +At first it was only used to disable the submit button and render a +spinner icon instead. Over the course of some years, the module got more +functionality, for example to prevent saving when validation fails. + +Since some refactorings within FormEngine, the module rather became a +burden. This became visible with the introduction of the Hotkeys API, as +the :js:`@typo3/backend/document-save-actions.js` reacts on explicit :js:`click` +events on the save icon, that is not triggered when FormEngine invokes a +save action via keyboard shortcuts. Adjusting :js:`document-save-actions.js`'s +behavior is necessary, but would become a breaking change, which is +unacceptable after the 13.0 release. For this reason, said module has +been marked as deprecated and its usages are replaced by its successor +:js:`@typo3/backend/form/submit-interceptor.js`. + + +Impact +====== + +Using the JavaScript module :js:`@typo3/backend/document-save-actions.js` will +render a deprecation warning in the browser's console. + + +Affected installations +====================== + +All installations relying on :js:`@typo3/backend/document-save-actions.js` are +affected. + + +Migration +========= + +To migrate the interception of submit events, the successor module +:js:`@typo3/backend/form/submit-interceptor.js` shall be used instead. + +The usage is similar to :js:`@typo3/backend/document-save-actions.js`, but +requires the form HTML element in its constructor. + +Example +------- + +.. code-block:: js + + import '@typo3/backend/form/submit-interceptor.js'; + + // ... + + const formElement = document.querySelector('form'); + const submitInterceptor = new SubmitInterceptor(formElement); + submitInterceptor.addPreSubmitCallback(function() { + // the same handling as in @typo3/backend/document-save-actions.js + }); + +.. index:: Backend, JavaScript, NotScanned, ext:backend diff --git a/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js b/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js index 0f521eef236a8d430667ad5f3f2d9deb41e19e8d..dac136233460c33d36c23604fd9fca41c2d58e9f 100644 --- a/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js +++ b/typo3/sysext/scheduler/Resources/Public/JavaScript/scheduler.js @@ -10,4 +10,4 @@ * * The TYPO3 project - inspiring people to share! */ -import SortableTable from"@typo3/backend/sortable-table.js";import DocumentSaveActions from"@typo3/backend/document-save-actions.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";import Icons from"@typo3/backend/icons.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import DateTimePicker from"@typo3/backend/date-time-picker.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import Severity from"@typo3/backend/severity.js";import DocumentService from"@typo3/core/document-service.js";class Scheduler{constructor(){DocumentService.ready().then((()=>{this.initializeEvents(),this.initializeDefaultStates(),this.initializeCloseConfirm()})),DocumentSaveActions.registerEvents()}static updateClearableInputs(){const e=document.querySelectorAll(".t3js-clearable");e.length>0&&import("@typo3/backend/input/clearable.js").then((function(){e.forEach((e=>e.clearable()))}))}static updateElementBrowserTriggers(){document.querySelectorAll(".t3js-element-browser").forEach((e=>{const t=document.getElementById(e.dataset.triggerFor);e.dataset.params=t.name+"|||pages"}))}static resolveDefaultNumberOfDays(){const e=document.getElementById("task_tableGarbageCollection_numberOfDays");return null===e||void 0===e.dataset.defaultNumberOfDays?null:JSON.parse(e.dataset.defaultNumberOfDays)}static storeCollapseState(e,t){let a={};PersistentStorage.isset("moduleData.scheduler_manage")&&(a=PersistentStorage.get("moduleData.scheduler_manage"));const n={};n[e]=t?1:0,a={...a,...n},PersistentStorage.set("moduleData.scheduler_manage",a)}toggleTaskSettingFields(e){let t=e.value;t=t.toLowerCase().replace(/\\/g,"-");for(const e of document.querySelectorAll(".extraFields")){const a=e.classList.contains("extra_fields_"+t);e.querySelectorAll("input, textarea, select").forEach((e=>{e.disabled=!a})),e.hidden=!a}}actOnChangeSchedulerTableGarbageCollectionAllTables(e){const t=document.querySelector("#task_tableGarbageCollection_numberOfDays"),a=document.querySelector("#task_tableGarbageCollection_table");if(e.checked)a.disabled=!0,t.disabled=!0;else{let e=parseInt(t.value,10);if(e<1){const t=a.value,n=Scheduler.resolveDefaultNumberOfDays();null!==n&&(e=n[t])}a.disabled=!1,e>0&&(t.disabled=!1)}}actOnChangeSchedulerTableGarbageCollectionTable(e){const t=document.querySelector("#task_tableGarbageCollection_numberOfDays"),a=Scheduler.resolveDefaultNumberOfDays();null!==a&&a[e.value]>0?(t.disabled=!1,t.value=a[e.value].toString(10)):(t.disabled=!0,t.value="0")}toggleFieldsByTaskType(e){const t=2===(e=parseInt(e+"",10));document.querySelector("#task_end_col").hidden=!t,document.querySelector("#task_frequency_row").hidden=!t,document.querySelector("#task_multiple_row").hidden=!t}initializeEvents(){const e=document.querySelector("#task_class");e&&new RegularEvent("change",(e=>{this.toggleTaskSettingFields(e.target)})).bindTo(e);const t=document.querySelector("#task_type");t&&new RegularEvent("change",(e=>{this.toggleFieldsByTaskType(e.target.value)})).bindTo(t);const a=document.querySelector("#task_tableGarbageCollection_allTables");a&&new RegularEvent("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionAllTables(e.target)})).bindTo(a);const n=document.querySelector("#task_tableGarbageCollection_table");n&&new RegularEvent("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionTable(e.target)})).bindTo(n);const o=document.querySelector("[data-update-task-frequency]");o&&new RegularEvent("change",(e=>{const t=e.target;document.querySelector("#task_frequency").value=t.value,t.value="",t.blur()})).bindTo(o),document.querySelectorAll("[data-scheduler-table]").forEach((e=>{new SortableTable(e)})),document.querySelectorAll("#tx_scheduler_form .t3js-datetimepicker").forEach((e=>DateTimePicker.initialize(e))),new RegularEvent("click",((e,t)=>{e.preventDefault();const a=new URL(t.href,window.origin);a.searchParams.set("mode",t.dataset.mode),a.searchParams.set("bparams",t.dataset.params),Modal.advanced({type:Modal.types.iframe,content:a.toString(),size:Modal.sizes.large})})).delegateTo(document,".t3js-element-browser"),new RegularEvent("show.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("hide.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go",this.executeTasks.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go_cron",this.executeTasks.bind(this)).bindTo(document),window.addEventListener("message",this.listenOnElementBrowser.bind(this))}initializeDefaultStates(){const e=document.querySelector("#task_type");null!==e&&this.toggleFieldsByTaskType(e.value);const t=document.querySelector("#task_class");null!==t&&(this.toggleTaskSettingFields(t),Scheduler.updateClearableInputs(),Scheduler.updateElementBrowserTriggers())}listenOnElementBrowser(e){if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";document.querySelector('input[name="'+e.data.fieldName+'"]').value=e.data.value.split("_").pop()}}toggleCollapseIcon(e){const t="hide.bs.collapse"===e.type,a=document.querySelector('.t3js-toggle-table[data-bs-target="#'+e.target.id+'"] .t3js-icon');null!==a&&Icons.getIcon(t?"actions-view-list-expand":"actions-view-list-collapse",Icons.sizes.small).then((e=>{a.replaceWith(document.createRange().createContextualFragment(e))})),Scheduler.storeCollapseState(e.target.dataset.table,t)}executeTasks(e){const t=document.querySelector('[data-multi-record-selection-form="'+e.detail.identifier+'"]');if(null===t)return;const a=[];if(e.detail.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset.taskId&&a.push(t.dataset.taskId)})),a.length){if("multiRecordSelection:action:go_cron"===e.type){const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","scheduleCron"),e.setAttribute("value",a.join(",")),t.append(e)}else{const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","execute"),e.setAttribute("value",a.join(",")),t.append(e)}t.submit()}}initializeCloseConfirm(){const e=document.querySelector("form[name=tx_scheduler_form]");if(!e)return;const t=new FormData(e);document.querySelector(".t3js-scheduler-close").addEventListener("click",(a=>{const n=new FormData(e),o=Object.fromEntries(t.entries()),l=Object.fromEntries(n.entries());if(JSON.stringify(o)!==JSON.stringify(l)||e.querySelector('input[value="add"]')){a.preventDefault();const t=a.target.href;Modal.confirm(TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",Severity.warning,[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no",trigger:()=>Modal.dismiss()},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes",trigger:()=>{Modal.dismiss(),window.location.href=t}},{text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-primary",name:"save",active:!0,trigger:()=>{Modal.dismiss();const t=document.createElement("input");t.type="hidden",t.value="saveclose",t.name="CMD",e.append(t),e.submit()}}])}}))}}export default new Scheduler; \ No newline at end of file +import SortableTable from"@typo3/backend/sortable-table.js";import RegularEvent from"@typo3/core/event/regular-event.js";import Modal from"@typo3/backend/modal.js";import Icons from"@typo3/backend/icons.js";import{MessageUtility}from"@typo3/backend/utility/message-utility.js";import PersistentStorage from"@typo3/backend/storage/persistent.js";import DateTimePicker from"@typo3/backend/date-time-picker.js";import{MultiRecordSelectionSelectors}from"@typo3/backend/multi-record-selection.js";import Severity from"@typo3/backend/severity.js";import DocumentService from"@typo3/core/document-service.js";import SubmitInterceptor from"@typo3/backend/form/submit-interceptor.js";class Scheduler{constructor(){DocumentService.ready().then((()=>{this.initializeSubmitInterceptor(),this.initializeEvents(),this.initializeDefaultStates(),this.initializeCloseConfirm()}))}static updateClearableInputs(){const e=document.querySelectorAll(".t3js-clearable");e.length>0&&import("@typo3/backend/input/clearable.js").then((function(){e.forEach((e=>e.clearable()))}))}static updateElementBrowserTriggers(){document.querySelectorAll(".t3js-element-browser").forEach((e=>{const t=document.getElementById(e.dataset.triggerFor);e.dataset.params=t.name+"|||pages"}))}static resolveDefaultNumberOfDays(){const e=document.getElementById("task_tableGarbageCollection_numberOfDays");return null===e||void 0===e.dataset.defaultNumberOfDays?null:JSON.parse(e.dataset.defaultNumberOfDays)}static storeCollapseState(e,t){let a={};PersistentStorage.isset("moduleData.scheduler_manage")&&(a=PersistentStorage.get("moduleData.scheduler_manage"));const n={};n[e]=t?1:0,a={...a,...n},PersistentStorage.set("moduleData.scheduler_manage",a)}toggleTaskSettingFields(e){let t=e.value;t=t.toLowerCase().replace(/\\/g,"-");for(const e of document.querySelectorAll(".extraFields")){const a=e.classList.contains("extra_fields_"+t);e.querySelectorAll("input, textarea, select").forEach((e=>{e.disabled=!a})),e.hidden=!a}}actOnChangeSchedulerTableGarbageCollectionAllTables(e){const t=document.querySelector("#task_tableGarbageCollection_numberOfDays"),a=document.querySelector("#task_tableGarbageCollection_table");if(e.checked)a.disabled=!0,t.disabled=!0;else{let e=parseInt(t.value,10);if(e<1){const t=a.value,n=Scheduler.resolveDefaultNumberOfDays();null!==n&&(e=n[t])}a.disabled=!1,e>0&&(t.disabled=!1)}}actOnChangeSchedulerTableGarbageCollectionTable(e){const t=document.querySelector("#task_tableGarbageCollection_numberOfDays"),a=Scheduler.resolveDefaultNumberOfDays();null!==a&&a[e.value]>0?(t.disabled=!1,t.value=a[e.value].toString(10)):(t.disabled=!0,t.value="0")}toggleFieldsByTaskType(e){const t=2===(e=parseInt(e+"",10));document.querySelector("#task_end_col").hidden=!t,document.querySelector("#task_frequency_row").hidden=!t,document.querySelector("#task_multiple_row").hidden=!t}initializeSubmitInterceptor(){const e=document.querySelector("form[name=tx_scheduler_form]");e&&new SubmitInterceptor(e)}initializeEvents(){const e=document.querySelector("#task_class");e&&new RegularEvent("change",(e=>{this.toggleTaskSettingFields(e.target)})).bindTo(e);const t=document.querySelector("#task_type");t&&new RegularEvent("change",(e=>{this.toggleFieldsByTaskType(e.target.value)})).bindTo(t);const a=document.querySelector("#task_tableGarbageCollection_allTables");a&&new RegularEvent("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionAllTables(e.target)})).bindTo(a);const n=document.querySelector("#task_tableGarbageCollection_table");n&&new RegularEvent("change",(e=>{this.actOnChangeSchedulerTableGarbageCollectionTable(e.target)})).bindTo(n);const o=document.querySelector("[data-update-task-frequency]");o&&new RegularEvent("change",(e=>{const t=e.target;document.querySelector("#task_frequency").value=t.value,t.value="",t.blur()})).bindTo(o),document.querySelectorAll("[data-scheduler-table]").forEach((e=>{new SortableTable(e)})),document.querySelectorAll("#tx_scheduler_form .t3js-datetimepicker").forEach((e=>DateTimePicker.initialize(e))),new RegularEvent("click",((e,t)=>{e.preventDefault();const a=new URL(t.href,window.origin);a.searchParams.set("mode",t.dataset.mode),a.searchParams.set("bparams",t.dataset.params),Modal.advanced({type:Modal.types.iframe,content:a.toString(),size:Modal.sizes.large})})).delegateTo(document,".t3js-element-browser"),new RegularEvent("show.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("hide.bs.collapse",this.toggleCollapseIcon.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go",this.executeTasks.bind(this)).bindTo(document),new RegularEvent("multiRecordSelection:action:go_cron",this.executeTasks.bind(this)).bindTo(document),window.addEventListener("message",this.listenOnElementBrowser.bind(this)),new RegularEvent("click",(e=>{e.preventDefault(),this.saveDocument(e)})).delegateTo(document,"button[form]")}saveDocument(e){const t=document.querySelector("form[name=tx_scheduler_form]");if(!t)return;const a=document.createElement("input");a.type="hidden",a.value="save",a.name="CMD",t.append(a),t.requestSubmit(e.target)}initializeDefaultStates(){const e=document.querySelector("#task_type");null!==e&&this.toggleFieldsByTaskType(e.value);const t=document.querySelector("#task_class");null!==t&&(this.toggleTaskSettingFields(t),Scheduler.updateClearableInputs(),Scheduler.updateElementBrowserTriggers())}listenOnElementBrowser(e){if(!MessageUtility.verifyOrigin(e.origin))throw"Denied message sent by "+e.origin;if("typo3:elementBrowser:elementAdded"===e.data.actionName){if(void 0===e.data.fieldName)throw"fieldName not defined in message";if(void 0===e.data.value)throw"value not defined in message";document.querySelector('input[name="'+e.data.fieldName+'"]').value=e.data.value.split("_").pop()}}toggleCollapseIcon(e){const t="hide.bs.collapse"===e.type,a=document.querySelector('.t3js-toggle-table[data-bs-target="#'+e.target.id+'"] .t3js-icon');null!==a&&Icons.getIcon(t?"actions-view-list-expand":"actions-view-list-collapse",Icons.sizes.small).then((e=>{a.replaceWith(document.createRange().createContextualFragment(e))})),Scheduler.storeCollapseState(e.target.dataset.table,t)}executeTasks(e){const t=document.querySelector('[data-multi-record-selection-form="'+e.detail.identifier+'"]');if(null===t)return;const a=[];if(e.detail.checkboxes.forEach((e=>{const t=e.closest(MultiRecordSelectionSelectors.elementSelector);null!==t&&t.dataset.taskId&&a.push(t.dataset.taskId)})),a.length){if("multiRecordSelection:action:go_cron"===e.type){const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","scheduleCron"),e.setAttribute("value",a.join(",")),t.append(e)}else{const e=document.createElement("input");e.setAttribute("type","hidden"),e.setAttribute("name","execute"),e.setAttribute("value",a.join(",")),t.append(e)}t.submit()}}initializeCloseConfirm(){const e=document.querySelector("form[name=tx_scheduler_form]");if(!e)return;const t=new FormData(e);document.querySelector(".t3js-scheduler-close").addEventListener("click",(a=>{const n=new FormData(e),o=Object.fromEntries(t.entries()),l=Object.fromEntries(n.entries());if(JSON.stringify(o)!==JSON.stringify(l)||e.querySelector('input[value="add"]')){a.preventDefault();const t=a.target.href;Modal.confirm(TYPO3.lang["label.confirm.close_without_save.title"]||"Do you want to close without saving?",TYPO3.lang["label.confirm.close_without_save.content"]||"You currently have unsaved changes. Are you sure you want to discard these changes?",Severity.warning,[{text:TYPO3.lang["buttons.confirm.close_without_save.no"]||"No, I will continue editing",btnClass:"btn-default",name:"no",trigger:()=>Modal.dismiss()},{text:TYPO3.lang["buttons.confirm.close_without_save.yes"]||"Yes, discard my changes",btnClass:"btn-default",name:"yes",trigger:()=>{Modal.dismiss(),window.location.href=t}},{text:TYPO3.lang["buttons.confirm.save_and_close"]||"Save and close",btnClass:"btn-primary",name:"save",active:!0,trigger:()=>{Modal.dismiss();const t=document.createElement("input");t.type="hidden",t.value="saveclose",t.name="CMD",e.append(t),e.submit()}}])}}))}}export default new Scheduler; \ No newline at end of file