From 544ddf3747ef55d9171274f48d8ace444ce87b78 Mon Sep 17 00:00:00 2001
From: Andreas Fernandez <a.fernandez@scripting-base.de>
Date: Thu, 10 Nov 2022 16:38:03 +0100
Subject: [PATCH] [BUGFIX] Simplify some FormEngine validations
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch simplifies some JavaScript-side validations in FormEngine.
Most notably, parsing dates and times is now handled by moment.js
(which will be replaced by luxon in the future), allowing us to remove
decades-old mambo-jambo code nobody can reverse-engineer
nowadays.

In the same run, `ltrim()` and the unused `btrim()` methods are
removed in favor of modern JavaScript functions.

Previously, it was possible to supply the invalid date "13-13-2016"
which was magically fixed to "13-01-2017". However, this was only
possible by setting the value and triggering the `change` event via
JavaScript and NOT by using the backend as a sane user. The behavior
introduced with this patch is now streamlined to behave like using
the datepicker or entering the field in the input field.

Resolves: #99058
Releases: main
Change-Id: I98af867ee3bda91222d21bcee1bc1f4c92e41d7e
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/76544
Tested-by: core-ci <typo3@b13.com>
Tested-by: Roman Büchler <info@buechler.pro>
Tested-by: Nikita Hovratov <nikita.h@live.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Roman Büchler <info@buechler.pro>
Reviewed-by: Nikita Hovratov <nikita.h@live.de>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
---
 .../backend/form-engine-validation.ts         | 176 +-----------------
 .../tests/form-engine-validation-test.ts      |  45 -----
 .../JavaScript/form-engine-validation.js      |   2 +-
 .../JavaScript/form-engine-validation-test.js |   2 +-
 .../FormEngine/ElementsBasicInputDateCest.php |   8 -
 5 files changed, 12 insertions(+), 221 deletions(-)

diff --git a/Build/Sources/TypeScript/backend/form-engine-validation.ts b/Build/Sources/TypeScript/backend/form-engine-validation.ts
index 045ca4c02706..f55360e79e4e 100644
--- a/Build/Sources/TypeScript/backend/form-engine-validation.ts
+++ b/Build/Sources/TypeScript/backend/form-engine-validation.ts
@@ -266,7 +266,7 @@ export default (function() {
     let maxItems: number;
 
     if (!$.isArray(value)) {
-      value = FormEngineValidation.ltrim(value);
+      value = value.trimStart();
     }
 
     $.each(rules, function(k: number, rule: any): void|boolean {
@@ -603,7 +603,7 @@ export default (function() {
    * @param {String} string
    * @returns {Array}
    */
-  FormEngineValidation.trimExplode = function(delimiter: string, string: string): Array<string> {
+  FormEngineValidation.trimExplode = function(delimiter: string, string: string): string[] {
     const result = [];
     const items = string.split(delimiter);
     for (let i = 0; i < items.length; i++) {
@@ -663,34 +663,6 @@ export default (function() {
     return theVal;
   };
 
-  /**
-   * Trims leading whitespace characters
-   *
-   * @param {String} value
-   * @returns {String}
-   */
-  FormEngineValidation.ltrim = function(value: string): string {
-    const theVal = '' + value;
-    if (!value) {
-      return '';
-    }
-    return theVal.replace(/^\s+/, '');
-  };
-
-  /**
-   * Trims trailing whitespace characters
-   *
-   * @param {String} value
-   * @returns {String}
-   */
-  FormEngineValidation.btrim = function(value: string): string {
-    const theVal = '' + value;
-    if (!value) {
-      return '';
-    }
-    return theVal.replace(/\s+$/, '');
-  };
-
   /**
    * Parse datetime value
    *
@@ -716,21 +688,7 @@ export default (function() {
    * @returns {*}
    */
   FormEngineValidation.parseDate = function(value: string): number {
-    const today = new Date();
-    let values = FormEngineValidation.split(value);
-
-    if (values.values[1] && values.values[1].length > 2) {
-      const temp = values.values[1];
-      values = FormEngineValidation.splitSingle(temp);
-    }
-
-    const year = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : FormEngineValidation.getYear(today);
-    const month = (values.values[2]) ? FormEngineValidation.parseInt(values.values[2]) : today.getUTCMonth() + 1;
-    const day = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : today.getUTCDate();
-    const theTime = moment.utc();
-    // eslint-disable-next-line radix
-    theTime.year(parseInt(year)).month(parseInt(month) - 1).date(parseInt(day)).hour(0).minute(0).second(0);
-    FormEngineValidation.lastDate = theTime.unix();
+    FormEngineValidation.lastDate = moment.utc(value, 'DD-MM-YYYY').unix();
 
     return FormEngineValidation.lastDate;
   };
@@ -743,21 +701,8 @@ export default (function() {
    * @returns {*}
    */
   FormEngineValidation.parseTime = function(value: string, type: string): number {
-    const today = new Date();
-    let values = FormEngineValidation.split(value);
-
-    if (values.values[1] && values.values[1].length > 2) {
-      const temp = values.values[1];
-      values = FormEngineValidation.splitSingle(temp);
-    }
-
-    const sec = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : today.getUTCSeconds();
-    const min = (values.values[2]) ? FormEngineValidation.parseInt(values.values[2]) : today.getUTCMinutes();
-    const hour = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : today.getUTCHours();
-    const theTime = moment.utc();
-    theTime.year(1970).month(0).date(1).hour(hour).minute(min).second(type === 'timesec' ? sec : 0);
-
-    FormEngineValidation.lastTime = theTime.unix();
+    const format = type === 'timesec' ? 'hh:mm:ss' : 'hh:mm';
+    FormEngineValidation.lastTime = moment.utc(value, format).year(1970).month(0).date(1).unix();
     if (FormEngineValidation.lastTime < 0) {
       FormEngineValidation.lastTime += 24 * 60 * 60;
     }
@@ -771,10 +716,12 @@ export default (function() {
    * @returns {*}
    */
   FormEngineValidation.parseYear = function(value: string): number {
-    const today = new Date();
-    const values = FormEngineValidation.split(value);
+    let year = parseInt(value, 10);
+    if (isNaN(year)) {
+      year = FormEngineValidation.getYear(new Date());
+    }
 
-    FormEngineValidation.lastYear = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : FormEngineValidation.getYear(today);
+    FormEngineValidation.lastYear = year;
     return FormEngineValidation.lastYear;
   };
 
@@ -814,25 +761,6 @@ export default (function() {
     return eval(((foreign == '-') ? '-' : '') + value);
   };
 
-  /**
-   * Substract timezone offset from client to a timestamp to get UTC-timestamp to be send to server
-   *
-   * @param {Number} timestamp
-   * @param {Number} timeonly
-   * @returns {*}
-   */
-  FormEngineValidation.convertClientTimestampToUTC = function(timestamp: number, timeonly: number): number {
-    const timeObj = new Date(timestamp * 1000);
-    timeObj.setTime((timestamp - timeObj.getTimezoneOffset() * 60) * 1000);
-    if (timeonly) {
-      // only seconds since midnight
-      return FormEngineValidation.getTime(timeObj);
-    } else {
-      // seconds since the "unix-epoch"
-      return FormEngineValidation.getTimestamp(timeObj);
-    }
-  };
-
   /**
    * Parse date string or object and return unix timestamp
    *
@@ -893,90 +821,6 @@ export default (function() {
     });
   };
 
-  /**
-   *
-   * @param value
-   * @returns {{values: Array, pointer: number}}
-   */
-  FormEngineValidation.splitSingle = function(value: string): {values: Array<string>, pointer: number} {
-    const theVal = '' + value;
-    const result = {
-      values: <Array<string>>[],
-      pointer: 3
-    };
-    result.values[1] = theVal.substr(0, 2);
-    result.values[2] = theVal.substr(2, 2);
-    result.values[3] = theVal.substr(4, 10);
-    return result;
-  };
-
-  /**
-   *
-   * @param theStr1
-   * @param delim
-   * @param index
-   * @returns {*}
-   */
-  FormEngineValidation.splitStr = function(theStr1: string, delim: string, index: number): string {
-    const theStr = '' + theStr1;
-    const lengthOfDelim = delim.length;
-    let sPos = -lengthOfDelim;
-    if (index < 1) {
-      index = 1;
-    }
-    for (let a = 1; a < index; a++) {
-      sPos = theStr.indexOf(delim, sPos + lengthOfDelim);
-      if (sPos == -1) {
-        return null;
-      }
-    }
-    let ePos = theStr.indexOf(delim, sPos + lengthOfDelim);
-    if (ePos == -1) {
-      ePos = theStr.length;
-    }
-    return (theStr.substring(sPos + lengthOfDelim, ePos));
-  };
-
-  /**
-   *
-   * @param value
-   * @returns {{values: Array, valPol: Array, pointer: number, numberMode: number, theVal: string}}
-   */
-  FormEngineValidation.split = function(value: string): {
-    values: Array<string>,
-    valPol: Array<string>,
-    pointer: number,
-    numberMode: number,
-    theVal: string
-  } {
-    const result = {
-      values: <Array<string>>[],
-      valPol: <Array<string>>[],
-      pointer: 0,
-      numberMode: 0,
-      theVal: ''
-    };
-    value += ' ';
-    for (let a = 0; a < value.length; a++) {
-      const theChar = value.substr(a, 1);
-      if (theChar < '0' || theChar > '9') {
-        if (result.numberMode) {
-          result.pointer++;
-          result.values[result.pointer] = result.theVal;
-          result.theVal = '';
-          result.numberMode = 0;
-        }
-        if (theChar == '+' || theChar == '-') {
-          result.valPol[result.pointer + 1] = theChar;
-        }
-      } else {
-        result.theVal += theChar;
-        result.numberMode = 1;
-      }
-    }
-    return result;
-  };
-
   FormEngineValidation.registerSubmitCallback = function () {
     DocumentSaveActions.getInstance().addPreSubmitCallback(function (e: Event) {
       if ($('.' + FormEngineValidation.errorClass).length > 0) {
diff --git a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
index 840a3697a111..5e0491ea304e 100644
--- a/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
+++ b/Build/Sources/TypeScript/backend/tests/form-engine-validation-test.ts
@@ -268,28 +268,6 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => {
     });
   });
 
-  /**
-   * @test
-   */
-  describe('tests for btrim', () => {
-    const result = FormEngineValidation.btrim(' test ');
-
-    it('works for string with whitespace in begin and end', () => {
-      expect(result).toBe(' test');
-    });
-  });
-
-  /**
-   * @test
-   */
-  describe('tests for ltrim', () => {
-    const result = FormEngineValidation.ltrim(' test ');
-
-    it('works for string with whitespace in begin and end', () => {
-      expect(result).toBe('test ');
-    });
-  });
-
   /**
    * @test
    */
@@ -353,27 +331,4 @@ describe('TYPO3/CMS/Backend/FormEngineValidationTest:', () => {
       expect(FormEngineValidation.getDate(baseTime)).toBe(1382479200);
     })
   });
-
-  /**
-   * @test
-   */
-  describe('tests for splitStr', () => {
-    it('works for command and index', () => {
-      expect(FormEngineValidation.splitStr('foo,bar,baz', ',', -1)).toBe('foo');
-      expect(FormEngineValidation.splitStr('foo,bar,baz', ',', 0)).toBe('foo');
-      expect(FormEngineValidation.splitStr('foo,bar,baz', ',', 1)).toBe('foo');
-      expect(FormEngineValidation.splitStr('foo,bar,baz', ',', 2)).toBe('bar');
-      expect(FormEngineValidation.splitStr('foo,bar,baz', ',', 3)).toBe('baz');
-      expect(FormEngineValidation.splitStr(' foo , bar , baz ', ',', 1)).toBe(' foo ');
-      expect(FormEngineValidation.splitStr(' foo , bar , baz ', ',', 2)).toBe(' bar ');
-      expect(FormEngineValidation.splitStr(' foo , bar , baz ', ',', 3)).toBe(' baz ');
-    });
-  });
-
-  /**
-   * @test
-   */
-  xdescribe('tests for split', () => {
-    /* tslint:disable:no-empty */
-  });
 });
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 bd80d9853a4a..a59414ccc153 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 moment from"moment";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";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:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=moment.utc(n).format("DD-MM-YYYY")}else{if(a=1*n,!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.toString().indexOf("-")<=0&&!("number"==typeof n?n:parseInt(n)))return"";o=FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t);break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=moment.utc(n);else{if(a="number"==typeof n?n:parseInt(n),!a&&"0"!==n.toString())return"";r=moment.unix(a).utc()}o="timesec"===e?r.format("HH:mm:ss"):r.format("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i,o,r,l=!1,s=0,m=n;$.isArray(n)||(n=FormEngineValidation.ltrim(n)),$.each(a,(function(e,a){if(l)return!1;switch(a.type){case"required":""===n&&(l=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((a.minItems||a.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t.value,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0))),void 0!==a.lower){const e=1*a.lower;!isNaN(e)&&n<e&&(l=!0)}if(void 0!==a.upper){const e=1*a.upper;!isNaN(e)&&n>e&&(l=!0)}}break;case"select":case"category":(a.minItems||a.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"group":case"inline":(a.minItems||a.maxItems)&&(s=FormEngineValidation.trimExplode(",",t.value).length,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(l=!0)}}));const d=!l,g=t.closest(FormEngineValidation.markerSelector);return null!==g&&g.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),m},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o="",r=0,l=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",r=0;r<n.length;r++){const t=n.substr(r,1);let i="_"===t||"-"===t,o=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":o=!1,i=!1}(o||l||i)&&(a+=t)}a!==n&&(l=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;l=a;break;case"nospace":l=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(l=Md5.hash(n));break;case"upper":l=n.toUpperCase();break;case"lower":l=n.toLowerCase();break;case"integer":""!==n&&(l=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(l=FormEngineValidation.parseDouble(n));break;case"trim":l=String(n).trim();break;case"datetime":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?l=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(l=TBE_EDITOR.customEvalFunctions[e](n))}return l},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;$(n).find(FormEngineValidation.rulesSelector).each(((e,n)=>{const t=$(n);if(!t.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const n=t.val(),a=FormEngineValidation.validateField(t,n);if($.isArray(a)&&$.isArray(n)){if(a.length!==n.length)e=!0;else for(let t=0;t<a.length;t++)if(a[t]!==n[t]){e=!0;break}}else a.length&&n!==a&&(e=!0);e&&t.val(a)}}))},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){let n;return e?(n=parseInt(""+e,10),isNaN(n)?0:n):0},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,\.-]/g,"");const a="-"===t.substring(0,1);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.ltrim=function(e){return e?(""+e).replace(/^\s+/,""):""},FormEngineValidation.btrim=function(e){return e?(""+e).replace(/\s+$/,""):""},FormEngineValidation.parseDateTime=function(e){const n=e.indexOf(" ");if(-1!==n){const t=FormEngineValidation.parseDate(e.substr(n,e.length));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substr(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){const n=new Date;let t=FormEngineValidation.split(e);if(t.values[1]&&t.values[1].length>2){const e=t.values[1];t=FormEngineValidation.splitSingle(e)}const a=t.values[3]?FormEngineValidation.parseInt(t.values[3]):FormEngineValidation.getYear(n),i=t.values[2]?FormEngineValidation.parseInt(t.values[2]):n.getUTCMonth()+1,o=t.values[1]?FormEngineValidation.parseInt(t.values[1]):n.getUTCDate(),r=moment.utc();return r.year(parseInt(a)).month(parseInt(i)-1).date(parseInt(o)).hour(0).minute(0).second(0),FormEngineValidation.lastDate=r.unix(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t=new Date;let a=FormEngineValidation.split(e);if(a.values[1]&&a.values[1].length>2){const e=a.values[1];a=FormEngineValidation.splitSingle(e)}const i=a.values[3]?FormEngineValidation.parseInt(a.values[3]):t.getUTCSeconds(),o=a.values[2]?FormEngineValidation.parseInt(a.values[2]):t.getUTCMinutes(),r=a.values[1]?FormEngineValidation.parseInt(a.values[1]):t.getUTCHours(),l=moment.utc();return l.year(1970).month(0).date(1).hour(r).minute(o).second("timesec"===n?i:0),FormEngineValidation.lastTime=l.unix(),FormEngineValidation.lastTime<0&&(FormEngineValidation.lastTime+=86400),FormEngineValidation.lastTime},FormEngineValidation.parseYear=function(e){const n=new Date,t=FormEngineValidation.split(e);return FormEngineValidation.lastYear=t.values[1]?FormEngineValidation.parseInt(t.values[1]):FormEngineValidation.getYear(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.convertClientTimestampToUTC=function(e,n){const t=new Date(1e3*e);return t.setTime(1e3*(e-60*t.getTimezoneOffset())),n?FormEngineValidation.getTime(t):FormEngineValidation.getTimestamp(t)},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){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.splitSingle=function(e){const n=""+e,t={values:[],pointer:3};return t.values[1]=n.substr(0,2),t.values[2]=n.substr(2,2),t.values[3]=n.substr(4,10),t},FormEngineValidation.splitStr=function(e,n,t){const a=""+e,i=n.length;let o=-i;t<1&&(t=1);for(let e=1;e<t;e++)if(o=a.indexOf(n,o+i),-1==o)return null;let r=a.indexOf(n,o+i);return-1==r&&(r=a.length),a.substring(o+i,r)},FormEngineValidation.split=function(e){const n={values:[],valPol:[],pointer:0,numberMode:0,theVal:""};e+=" ";for(let t=0;t<e.length;t++){const a=e.substr(t,1);a<"0"||a>"9"?(n.numberMode&&(n.pointer++,n.values[n.pointer]=n.theVal,n.theVal="",n.numberMode=0),"+"!=a&&"-"!=a||(n.valPol[n.pointer+1]=a)):(n.theVal+=a,n.numberMode=1)}return n},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=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"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}());
\ No newline at end of file
+import $ from"jquery";import moment from"moment";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";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:"********"},customEvaluations=new Map;return FormEngineValidation.initialize=function(){$(document).find("."+FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass),FormEngineValidation.initializeInputFields().promise().done((function(){$(document).on("change",FormEngineValidation.rulesSelector,(e=>{FormEngineValidation.validateField(e.currentTarget),FormEngineValidation.markFieldAsChanged(e.currentTarget)})),FormEngineValidation.registerSubmitCallback()}));const e=new Date;FormEngineValidation.lastYear=FormEngineValidation.getYear(e),FormEngineValidation.lastDate=FormEngineValidation.getDate(e),FormEngineValidation.lastTime=0,FormEngineValidation.validate()},FormEngineValidation.initializeInputFields=function(){return $(document).find(FormEngineValidation.inputSelector).each((function(e,n){const t=$(n).data("formengine-input-params"),a=t.field,i=$('[name="'+a+'"]');void 0===i.data("main-field")&&(i.data("main-field",a),i.data("config",t),FormEngineValidation.initializeInputField(a))}))},FormEngineValidation.initializeInputField=function(e){const n=$('[name="'+e+'"]'),t=$('[data-formengine-input-name="'+e+'"]');let a=$('[name="'+n.data("main-field")+'"]');0===a.length&&(a=n);const i=a.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let a=n.val();for(let n=0;n<e.length;n++)a=FormEngineValidation.formatValue(e[n],a,i);a.length&&t.val(a)}t.data("main-field",e),t.data("config",i),t.on("change",(function(){FormEngineValidation.updateInputField(t.attr("data-formengine-input-name"))})),t.attr("data-formengine-input-initialized","true")},FormEngineValidation.registerCustomEvaluation=function(e,n){customEvaluations.has(e)||customEvaluations.set(e,n)},FormEngineValidation.formatValue=function(e,n,t){let a,i,o="";switch(e){case"date":if(n.toString().indexOf("-")>0){o=moment.utc(n).format("DD-MM-YYYY")}else{if(a=1*n,!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.toString().indexOf("-")<=0&&!("number"==typeof n?n:parseInt(n)))return"";o=FormEngineValidation.formatValue("time",n,t)+" "+FormEngineValidation.formatValue("date",n,t);break;case"time":case"timesec":let r;if(n.toString().indexOf("-")>0)r=moment.utc(n);else{if(a="number"==typeof n?n:parseInt(n),!a&&"0"!==n.toString())return"";r=moment.unix(a).utc()}o="timesec"===e?r.format("HH:mm:ss"):r.format("HH:mm");break;case"password":o=n?FormEngineValidation.passwordDummy:"";break;default:o=n}return o},FormEngineValidation.updateInputField=function(e){const n=$('[name="'+e+'"]');let t=$('[name="'+n.data("main-field")+'"]');0===t.length&&(t=n);const a=$('[data-formengine-input-name="'+t.attr("name")+'"]'),i=t.data("config");if(void 0!==i){const e=FormEngineValidation.trimExplode(",",i.evalList);let n=a.val();for(let t=0;t<e.length;t++)n=FormEngineValidation.processValue(e[t],n,i);let o=n;for(let n=0;n<e.length;n++)o=FormEngineValidation.formatValue(e[n],o,i);t.val(n),t.get(0).dispatchEvent(new Event("change")),a.val(o)}},FormEngineValidation.validateField=function(e,n){const t=e instanceof $?e.get(0):e;if(n=n||t.value||"",void 0===t.dataset.formengineValidationRules)return n;const a=JSON.parse(t.dataset.formengineValidationRules);let i,o,r,l=!1,s=0,m=n;$.isArray(n)||(n=n.trimStart()),$.each(a,(function(e,a){if(l)return!1;switch(a.type){case"required":""===n&&(l=!0,t.closest(FormEngineValidation.markerSelector).classList.add(FormEngineValidation.errorClass));break;case"range":if(""!==n){if((a.minItems||a.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t.value,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0))),void 0!==a.lower){const e=1*a.lower;!isNaN(e)&&n<e&&(l=!0)}if(void 0!==a.upper){const e=1*a.upper;!isNaN(e)&&n>e&&(l=!0)}}break;case"select":case"category":(a.minItems||a.maxItems)&&(i=$(document).find('[name="'+t.dataset.relatedfieldname+'"]'),s=i.length?FormEngineValidation.trimExplode(",",i.val()).length:t instanceof HTMLSelectElement?t.querySelectorAll("option:checked").length:t.querySelectorAll("input[value]:checked").length,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"group":case"inline":(a.minItems||a.maxItems)&&(s=FormEngineValidation.trimExplode(",",t.value).length,void 0!==a.minItems&&(o=1*a.minItems,!isNaN(o)&&s<o&&(l=!0)),void 0!==a.maxItems&&(r=1*a.maxItems,!isNaN(r)&&s>r&&(l=!0)));break;case"min":(t instanceof HTMLInputElement||t instanceof HTMLTextAreaElement)&&t.value.length>0&&t.value.length<t.minLength&&(l=!0)}}));const d=!l,c=t.closest(FormEngineValidation.markerSelector);return null!==c&&c.classList.toggle(FormEngineValidation.errorClass,!d),FormEngineValidation.markParentTab($(t),d),$(document).trigger("t3-formengine-postfieldvalidation"),m},FormEngineValidation.processValue=function(e,n,t){let a="",i="",o="",r=0,l=n;switch(e){case"alpha":case"num":case"alphanum":case"alphanum_x":for(a="",r=0;r<n.length;r++){const t=n.substr(r,1);let i="_"===t||"-"===t,o=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":o=!1,i=!1}(o||l||i)&&(a+=t)}a!==n&&(l=a);break;case"is_in":if(t.is_in){i=""+n,t.is_in=t.is_in.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&");const e=new RegExp("[^"+t.is_in+"]+","g");a=i.replace(e,"")}else a=i;l=a;break;case"nospace":l=(""+n).replace(/ /g,"");break;case"md5":""!==n&&(l=Md5.hash(n));break;case"upper":l=n.toUpperCase();break;case"lower":l=n.toLowerCase();break;case"integer":""!==n&&(l=FormEngineValidation.parseInt(n));break;case"decimal":""!==n&&(l=FormEngineValidation.parseDouble(n));break;case"trim":l=String(n).trim();break;case"datetime":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDateTime(n));break;case"date":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseDate(n));break;case"time":case"timesec":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseTime(n,e));break;case"year":""!==n&&(o=n.substr(0,1),l=FormEngineValidation.parseYear(n));break;case"null":case"password":break;default:customEvaluations.has(e)?l=customEvaluations.get(e).call(null,n):"object"==typeof TBE_EDITOR&&void 0!==TBE_EDITOR.customEvalFunctions&&"function"==typeof TBE_EDITOR.customEvalFunctions[e]&&(l=TBE_EDITOR.customEvalFunctions[e](n))}return l},FormEngineValidation.validate=function(e){(void 0===e||e instanceof Document)&&$(document).find(FormEngineValidation.markerSelector+", .t3js-tabmenu-item").removeClass(FormEngineValidation.errorClass).removeClass("has-validation-error");const n=e||document;$(n).find(FormEngineValidation.rulesSelector).each(((e,n)=>{const t=$(n);if(!t.closest(".t3js-flex-section-deleted, .t3js-inline-record-deleted, .t3js-file-reference-deleted").length){let e=!1;const n=t.val(),a=FormEngineValidation.validateField(t,n);if($.isArray(a)&&$.isArray(n)){if(a.length!==n.length)e=!0;else for(let t=0;t<a.length;t++)if(a[t]!==n[t]){e=!0;break}}else a.length&&n!==a&&(e=!0);e&&t.val(a)}}))},FormEngineValidation.markFieldAsChanged=function(e){if(e instanceof $&&(e=e.get(0)),!(e instanceof HTMLElement))return;const n=e.closest(".t3js-formengine-palette-field");null!==n&&n.classList.add("has-change")},FormEngineValidation.trimExplode=function(e,n){const t=[],a=n.split(e);for(let e=0;e<a.length;e++){const n=a[e].trim();n.length>0&&t.push(n)}return t},FormEngineValidation.parseInt=function(e){let n;return e?(n=parseInt(""+e,10),isNaN(n)?0:n):0},FormEngineValidation.parseDouble=function(e,n=2){let t=""+e;t=t.replace(/[^0-9,\.-]/g,"");const a="-"===t.substring(0,1);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.substr(n,e.length));FormEngineValidation.lastTime=t+FormEngineValidation.parseTime(e.substr(0,n),"time")}else FormEngineValidation.lastTime=FormEngineValidation.parseDate(e);return FormEngineValidation.lastTime},FormEngineValidation.parseDate=function(e){return FormEngineValidation.lastDate=moment.utc(e,"DD-MM-YYYY").unix(),FormEngineValidation.lastDate},FormEngineValidation.parseTime=function(e,n){const t="timesec"===n?"hh:mm:ss":"hh:mm";return FormEngineValidation.lastTime=moment.utc(e,t).year(1970).month(0).date(1).unix(),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){e.parents(".tab-pane").each((function(e,t){const a=$(t);n&&(n=0===a.find(".has-error").length);const i=a.attr("id");$(document).find('a[href="#'+i+'"]').closest(".t3js-tabmenu-item").toggleClass("has-validation-error",!n)}))},FormEngineValidation.registerSubmitCallback=function(){DocumentSaveActions.getInstance().addPreSubmitCallback((function(e){if($("."+FormEngineValidation.errorClass).length>0){const n=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"}]);n.addEventListener("button.clicked",(()=>n.hideModal())),e.stopImmediatePropagation()}}))},FormEngineValidation}());
\ No newline at end of file
diff --git a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js b/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js
index da1e25a8914e..e72e7932fa9b 100644
--- a/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js
+++ b/typo3/sysext/backend/Tests/JavaScript/form-engine-validation-test.js
@@ -10,4 +10,4 @@
  *
  * The TYPO3 project - inspiring people to share!
  */
-import FormEngineValidation from"@typo3/backend/form-engine-validation.js";describe("TYPO3/CMS/Backend/FormEngineValidationTest:",(()=>{const e=[{description:"works for type date",type:"date",value:0,config:{},result:""},{description:"works for type date with timestamp",type:"date",value:1e7,config:{},result:"26-04-1970"},{description:"works for type date with iso date",type:"date",value:"2016-12-02T11:16:06+00:00",config:{},result:"02-12-2016"},{description:"works for type datetime",type:"datetime",value:0,config:{},result:""},{description:"works for type datetime with timestamp",type:"datetime",value:1e7,config:{},result:"17:46 26-04-1970"},{description:"works for type datetime with iso date",type:"datetime",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16 02-12-2016"},{description:"resolves to empty result for zero value",type:"datetime",value:0,config:{},result:""},{description:"resolves to empty result for invalid value",type:"datetime",value:"invalid",config:{},result:""},{description:"works for type time",type:"time",value:0,config:{},result:"00:00"},{description:"works for type time with timestamp",type:"time",value:1e7,config:{},result:"17:46"},{description:"works for type time with iso date",type:"time",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16"}];describe("tests for formatValue",(()=>{using(e,(function(e){it(e.description,(()=>{FormEngineValidation.initialize();const t=FormEngineValidation.formatValue(e.type,e.value,e.config);expect(t).toBe(e.result)}))}))}));const t=[{description:"works for command alpha with numeric value",command:"alpha",value:"1234",config:{},result:""},{description:"works for command alpha with string value",command:"alpha",value:"abc",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"abc123",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"123abc123",config:{},result:"abc"}];describe("test for processValue",(()=>{using(t,(function(e){it(e.description,(()=>{const t=FormEngineValidation.processValue(e.command,e.value,e.config);expect(t).toBe(e.result)}))}))})),xdescribe("tests for validateField",(()=>{})),describe("tests for trimExplode",(()=>{it("works for comma as separator and list without spaces",(()=>{expect(FormEngineValidation.trimExplode(",","foo,bar,baz")).toEqual(["foo","bar","baz"])})),it("works for comma as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode(","," foo , bar , baz ")).toEqual(["foo","bar","baz"])})),it("works for pipe as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode("|"," foo | bar | baz ")).toEqual(["foo","bar","baz"])}))})),describe("tests for parseInt",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseInt(0)).toBe(0)})),it("works for value 1",(()=>{expect(FormEngineValidation.parseInt(1)).toBe(1)})),it("works for value -1",(()=>{expect(FormEngineValidation.parseInt(-1)).toBe(-1)})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseInt("0")).toBe(0)})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseInt("1")).toBe(1)})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseInt("-1")).toBe(-1)})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseInt(.5)).toBe(0)})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseInt("0.5")).toBe(0)})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseInt("foo")).toBe(0)})),it("works for value true",(()=>{expect(FormEngineValidation.parseInt(!0)).toBe(0)})),it("works for value false",(()=>{expect(FormEngineValidation.parseInt(!1)).toBe(0)})),it("works for value null",(()=>{expect(FormEngineValidation.parseInt(null)).toBe(0)}))})),describe("tests for parseDouble",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseDouble(0)).toBe("0.00")})),it("works for value 1",(()=>{expect(FormEngineValidation.parseDouble(1)).toBe("1.00")})),it("works for value -1",(()=>{expect(FormEngineValidation.parseDouble(-1)).toBe("-1.00")})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseDouble("0")).toBe("0.00")})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseDouble("1")).toBe("1.00")})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseDouble("-1")).toBe("-1.00")})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseDouble(.5)).toBe("0.50")})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseDouble("0.5")).toBe("0.50")})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseDouble("foo")).toBe("0.00")})),it("works for value true",(()=>{expect(FormEngineValidation.parseDouble(!0)).toBe("0.00")})),it("works for value false",(()=>{expect(FormEngineValidation.parseDouble(!1)).toBe("0.00")})),it("works for value null",(()=>{expect(FormEngineValidation.parseDouble(null)).toBe("0.00")}))})),describe("tests for btrim",(()=>{const e=FormEngineValidation.btrim(" test ");it("works for string with whitespace in begin and end",(()=>{expect(e).toBe(" test")}))})),describe("tests for ltrim",(()=>{const e=FormEngineValidation.ltrim(" test ");it("works for string with whitespace in begin and end",(()=>{expect(e).toBe("test ")}))})),xdescribe("tests for parseDateTime",(()=>{})),xdescribe("tests for parseDate",(()=>{})),xdescribe("tests for parseTime",(()=>{})),xdescribe("tests for parseYear",(()=>{})),describe("tests for getYear",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),it("works for current date",(()=>{const e=new Date;expect(FormEngineValidation.getYear(e)).toBe(e.getFullYear())})),it("works for year 2013",(()=>{const e=new Date(2013,9,23);jasmine.clock().mockDate(e),expect(FormEngineValidation.getYear(e)).toBe(2013)}))})),describe("tests for getDate",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),xit("works for year 2013",(()=>{const e=new Date(2013,9,23,13,13,13);jasmine.clock().mockDate(e),expect(FormEngineValidation.getDate(e)).toBe(1382479200)}))})),describe("tests for splitStr",(()=>{it("works for command and index",(()=>{expect(FormEngineValidation.splitStr("foo,bar,baz",",",-1)).toBe("foo"),expect(FormEngineValidation.splitStr("foo,bar,baz",",",0)).toBe("foo"),expect(FormEngineValidation.splitStr("foo,bar,baz",",",1)).toBe("foo"),expect(FormEngineValidation.splitStr("foo,bar,baz",",",2)).toBe("bar"),expect(FormEngineValidation.splitStr("foo,bar,baz",",",3)).toBe("baz"),expect(FormEngineValidation.splitStr(" foo , bar , baz ",",",1)).toBe(" foo "),expect(FormEngineValidation.splitStr(" foo , bar , baz ",",",2)).toBe(" bar "),expect(FormEngineValidation.splitStr(" foo , bar , baz ",",",3)).toBe(" baz ")}))})),xdescribe("tests for split",(()=>{}))}));
\ No newline at end of file
+import FormEngineValidation from"@typo3/backend/form-engine-validation.js";describe("TYPO3/CMS/Backend/FormEngineValidationTest:",(()=>{const e=[{description:"works for type date",type:"date",value:0,config:{},result:""},{description:"works for type date with timestamp",type:"date",value:1e7,config:{},result:"26-04-1970"},{description:"works for type date with iso date",type:"date",value:"2016-12-02T11:16:06+00:00",config:{},result:"02-12-2016"},{description:"works for type datetime",type:"datetime",value:0,config:{},result:""},{description:"works for type datetime with timestamp",type:"datetime",value:1e7,config:{},result:"17:46 26-04-1970"},{description:"works for type datetime with iso date",type:"datetime",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16 02-12-2016"},{description:"resolves to empty result for zero value",type:"datetime",value:0,config:{},result:""},{description:"resolves to empty result for invalid value",type:"datetime",value:"invalid",config:{},result:""},{description:"works for type time",type:"time",value:0,config:{},result:"00:00"},{description:"works for type time with timestamp",type:"time",value:1e7,config:{},result:"17:46"},{description:"works for type time with iso date",type:"time",value:"2016-12-02T11:16:06+00:00",config:{},result:"11:16"}];describe("tests for formatValue",(()=>{using(e,(function(e){it(e.description,(()=>{FormEngineValidation.initialize();const t=FormEngineValidation.formatValue(e.type,e.value,e.config);expect(t).toBe(e.result)}))}))}));const t=[{description:"works for command alpha with numeric value",command:"alpha",value:"1234",config:{},result:""},{description:"works for command alpha with string value",command:"alpha",value:"abc",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"abc123",config:{},result:"abc"},{description:"works for command alpha with alphanum input",command:"alpha",value:"123abc123",config:{},result:"abc"}];describe("test for processValue",(()=>{using(t,(function(e){it(e.description,(()=>{const t=FormEngineValidation.processValue(e.command,e.value,e.config);expect(t).toBe(e.result)}))}))})),xdescribe("tests for validateField",(()=>{})),describe("tests for trimExplode",(()=>{it("works for comma as separator and list without spaces",(()=>{expect(FormEngineValidation.trimExplode(",","foo,bar,baz")).toEqual(["foo","bar","baz"])})),it("works for comma as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode(","," foo , bar , baz ")).toEqual(["foo","bar","baz"])})),it("works for pipe as separator and list with spaces",(()=>{expect(FormEngineValidation.trimExplode("|"," foo | bar | baz ")).toEqual(["foo","bar","baz"])}))})),describe("tests for parseInt",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseInt(0)).toBe(0)})),it("works for value 1",(()=>{expect(FormEngineValidation.parseInt(1)).toBe(1)})),it("works for value -1",(()=>{expect(FormEngineValidation.parseInt(-1)).toBe(-1)})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseInt("0")).toBe(0)})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseInt("1")).toBe(1)})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseInt("-1")).toBe(-1)})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseInt(.5)).toBe(0)})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseInt("0.5")).toBe(0)})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseInt("foo")).toBe(0)})),it("works for value true",(()=>{expect(FormEngineValidation.parseInt(!0)).toBe(0)})),it("works for value false",(()=>{expect(FormEngineValidation.parseInt(!1)).toBe(0)})),it("works for value null",(()=>{expect(FormEngineValidation.parseInt(null)).toBe(0)}))})),describe("tests for parseDouble",(()=>{it("works for value 0",(()=>{expect(FormEngineValidation.parseDouble(0)).toBe("0.00")})),it("works for value 1",(()=>{expect(FormEngineValidation.parseDouble(1)).toBe("1.00")})),it("works for value -1",(()=>{expect(FormEngineValidation.parseDouble(-1)).toBe("-1.00")})),it('works for value "0"',(()=>{expect(FormEngineValidation.parseDouble("0")).toBe("0.00")})),it('works for value "1"',(()=>{expect(FormEngineValidation.parseDouble("1")).toBe("1.00")})),it('works for value "-1"',(()=>{expect(FormEngineValidation.parseDouble("-1")).toBe("-1.00")})),it("works for value 0.5",(()=>{expect(FormEngineValidation.parseDouble(.5)).toBe("0.50")})),it('works for value "0.5"',(()=>{expect(FormEngineValidation.parseDouble("0.5")).toBe("0.50")})),it('works for value "foo"',(()=>{expect(FormEngineValidation.parseDouble("foo")).toBe("0.00")})),it("works for value true",(()=>{expect(FormEngineValidation.parseDouble(!0)).toBe("0.00")})),it("works for value false",(()=>{expect(FormEngineValidation.parseDouble(!1)).toBe("0.00")})),it("works for value null",(()=>{expect(FormEngineValidation.parseDouble(null)).toBe("0.00")}))})),xdescribe("tests for parseDateTime",(()=>{})),xdescribe("tests for parseDate",(()=>{})),xdescribe("tests for parseTime",(()=>{})),xdescribe("tests for parseYear",(()=>{})),describe("tests for getYear",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),it("works for current date",(()=>{const e=new Date;expect(FormEngineValidation.getYear(e)).toBe(e.getFullYear())})),it("works for year 2013",(()=>{const e=new Date(2013,9,23);jasmine.clock().mockDate(e),expect(FormEngineValidation.getYear(e)).toBe(2013)}))})),describe("tests for getDate",(()=>{const e=new Date;afterEach((()=>{jasmine.clock().mockDate(e)})),xit("works for year 2013",(()=>{const e=new Date(2013,9,23,13,13,13);jasmine.clock().mockDate(e),expect(FormEngineValidation.getDate(e)).toBe(1382479200)}))}))}));
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputDateCest.php b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputDateCest.php
index 7a1a0d6e70a0..3b88d3055eec 100644
--- a/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputDateCest.php
+++ b/typo3/sysext/core/Tests/Acceptance/Application/FormEngine/ElementsBasicInputDateCest.php
@@ -68,14 +68,6 @@ class ElementsBasicInputDateCest extends AbstractElementsBasicCest
                 'expectedValueAfterSave' => '2016-01-29T00:00:00+00:00',
                 'comment' => 'inputdatetime_2 dbType=date eval=date simple input',
             ],
-            [
-                'label' => 'inputdatetime_2',
-                'inputValue' => '13-13-2016',
-                'expectedValue' => '13-01-2017',
-                'expectedInternalValue' => '2017-01-13T00:00:00Z',
-                'expectedValueAfterSave' => '2017-01-13T00:00:00+00:00',
-                'comment' => 'inputdatetime_2 dbType=date eval=date month transformation',
-            ],
             [
                 'label' => 'inputdatetime_2',
                 'inputValue' => '29-02-2016',
-- 
GitLab