text: /*
   * 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!
   */
  
  /**
   * Module: TYPO3/CMS/Backend/FormEngineValidation
   * Contains all JS functions related to TYPO3 TCEforms/FormEngineValidation
   * @internal
   */
  define(['jquery', 'moment'], function($, moment) {
  
    /**
     * The main FormEngineValidation object
     *
     * @type {{rulesSelector: string, inputSelector: string, markerSelector: string, groupFieldHiddenElement: string, relatedFieldSelector: string, errorClass: string, lastYear: number, lastDate: number, lastTime: number, USmode: number, passwordDummy: string}}
     * @exports TYPO3/CMS/Backend/FormEngineValidation
     */
    var 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,
      USmode: 0,
      passwordDummy: '********'
    };
  
    /**
     * Initialize validation for the first time
     */
    FormEngineValidation.initialize = function() {
      $(document).find('.' + FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass);
  
      // Initialize input fields
      FormEngineValidation.initializeInputFields().promise().done(function() {
        // Bind to field changes
        $(document).on('change', FormEngineValidation.rulesSelector, function() {
          FormEngineValidation.validate();
          FormEngineValidation.markFieldAsChanged($(this));
        });
      });
  
      var today = new Date();
      FormEngineValidation.lastYear = FormEngineValidation.getYear(today);
      FormEngineValidation.lastDate = FormEngineValidation.getDate(today);
      FormEngineValidation.lastTime = 0;
      FormEngineValidation.USmode = 0;
      FormEngineValidation.validate();
    };
  
    /**
     * Initialize all input fields
     *
     * @returns {Object}
     */
    FormEngineValidation.initializeInputFields = function() {
      return $(document).find(FormEngineValidation.inputSelector).each(function() {
        var config = $(this).data('formengine-input-params');
        var fieldName = config.field;
        var $field = $('[name="' + fieldName + '"]');
  
        // ignore fields which already have been initialized
        if ($field.data('main-field') === undefined) {
          $field.data('main-field', fieldName);
          $field.data('config', config);
          FormEngineValidation.initializeInputField(fieldName);
        }
      });
    };
  
    /**
     *
     * @param {Number} mode
     */
    FormEngineValidation.setUsMode = function(mode) {
      FormEngineValidation.USmode = mode;
    };
  
    /**
     * Initialize field by name
     *
     * @param {String} fieldName
     */
    FormEngineValidation.initializeInputField = function(fieldName) {
      var $field = $('[name="' + fieldName + '"]');
      var $humanReadableField = $('[data-formengine-input-name="' + fieldName + '"]');
      var $mainField = $('[name="' + $field.data('main-field') + '"]');
      if ($mainField.length === 0) {
        $mainField = $field;
      }
  
      var config = $mainField.data('config');
      if (typeof config !== 'undefined') {
        var evalList = FormEngineValidation.trimExplode(',', config.evalList);
        var value = $field.val();
  
        for (var i = 0; i < evalList.length; i++) {
          value = FormEngineValidation.formatValue(evalList[i], value, config);
        }
        // Prevent password fields to be overwritten with original value
        if (value.length && $humanReadableField.attr('type') !== 'password') {
          $humanReadableField.val(value);
        }
      }
      $humanReadableField.data('main-field', fieldName);
      $humanReadableField.data('config', config);
      $humanReadableField.on('change', function() {
        FormEngineValidation.updateInputField($(this).attr('data-formengine-input-name'));
      });
      $humanReadableField.on('keyup', FormEngineValidation.validate);
  
      // add the attribute so that acceptance tests can know when the field initialization has completed
      $humanReadableField.attr('data-formengine-input-initialized', 'true');
    };
  
    /**
     * Format field value
     *
     * @param {String} type
     * @param {String} value
     * @param {Object} config
     * @returns {String}
     */
    FormEngineValidation.formatValue = function(type, value, config) {
      var theString = '';
      var parsedInt, theTime;
      switch (type) {
        case 'date':
          // poor man’s ISO-8601 detection: if we have a "-" in it, it apparently is not an integer.
          if (value.toString().indexOf('-') > 0) {
            var date = moment.utc(value);
            if (FormEngineValidation.USmode) {
              theString = date.format('MM-DD-YYYY');
            } else {
              theString = date.format('DD-MM-YYYY');
            }
          } else {
            parsedInt = parseInt(value);
            if (!parsedInt) {
              return '';
            }
            theTime = new Date(parsedInt * 1000);
            if (FormEngineValidation.USmode) {
              theString = (theTime.getUTCMonth() + 1) + '-' + theTime.getUTCDate() + '-' + this.getYear(theTime);
            } else {
              theString = theTime.getUTCDate() + '-' + (theTime.getUTCMonth() + 1) + '-' + this.getYear(theTime);
            }
          }
          break;
        case 'datetime':
          if (value.toString().indexOf('-') <= 0 && !parseInt(value)) {
            return '';
          }
          theString = FormEngineValidation.formatValue('time', value, config) + ' ' + FormEngineValidation.formatValue('date', value, config);
          break;
        case 'time':
        case 'timesec':
          var dateValue;
          if (value.toString().indexOf('-') > 0) {
            dateValue = moment.utc(value);
          } else {
            parsedInt = parseInt(value);
            if (!parsedInt && value.toString() !== '0') {
              return '';
            }
            dateValue = moment.unix(parsedInt).utc();
          }
          if (type === 'timesec') {
            theString = dateValue.format('HH:mm:ss');
          } else {
            theString = dateValue.format('HH:mm');
          }
          break;
        case 'password':
          theString = (value) ? FormEngineValidation.passwordDummy : '';
          break;
        default:
          theString = value;
      }
      return theString;
    };
  
    /**
     * Update input field after change
     *
     * @param {String} fieldName
     */
    FormEngineValidation.updateInputField = function(fieldName) {
      var $field = $('[name="' + fieldName + '"]');
      var $mainField = $('[name="' + $field.data('main-field') + '"]');
      if ($mainField.length === 0) {
        $mainField = $field;
      }
      var $humanReadableField = $('[data-formengine-input-name="' + $mainField.attr('name') + '"]');
  
      var config = $mainField.data('config');
      if (typeof config !== 'undefined') {
        var evalList = FormEngineValidation.trimExplode(',', config.evalList);
        var newValue = $humanReadableField.val();
        var i;
  
        for (i = 0; i < evalList.length; i++) {
          newValue = FormEngineValidation.processValue(evalList[i], newValue, config);
        }
  
        var formattedValue = newValue;
        for (i = 0; i < evalList.length; i++) {
          formattedValue = FormEngineValidation.formatValue(evalList[i], formattedValue, config);
        }
  
        $mainField.val(newValue);
        $humanReadableField.val(formattedValue);
      }
    };
  
    /**
     * Run validation for field
     *
     * @param {Object} $field
     * @param {String} [value=$field.val()]
     * @returns {String}
     */
    FormEngineValidation.validateField = function($field, value) {
      value = value || $field.val() || '';
  
      var rules = $field.data('formengine-validation-rules');
      var markParent = false;
      var selected = 0;
      // keep the original value, validateField should not alter it
      var returnValue = value;
      var $relatedField;
      var minItems;
      var maxItems;
  
      if (!$.isArray(value)) {
        value = FormEngineValidation.ltrim(value);
      }
  
      $.each(rules, function(k, rule) {
        switch (rule.type) {
          case 'required':
            if (value === '') {
              markParent = true;
              $field.closest(FormEngineValidation.markerSelector).addClass(FormEngineValidation.errorClass);
            }
            break;
          case 'range':
            if (value !== '') {
              if (rule.minItems || rule.maxItems) {
                $relatedField = $(document).find('[name="' + $field.data('relatedfieldname') + '"]');
                if ($relatedField.length) {
                  selected = FormEngineValidation.trimExplode(',', $relatedField.val()).length;
                } else {
                  selected = $field.val();
                }
                if (typeof rule.minItems !== 'undefined') {
                  minItems = rule.minItems * 1;
                  if (!isNaN(minItems) && selected < minItems) {
                    markParent = true;
                  }
                }
                if (typeof rule.maxItems !== 'undefined') {
                  maxItems = rule.maxItems * 1;
                  if (!isNaN(maxItems) && selected > maxItems) {
                    markParent = true;
                  }
                }
              }
              if (typeof rule.lower !== 'undefined') {
                var minValue = rule.lower * 1;
                if (!isNaN(minValue) && value < minValue) {
                  markParent = true;
                }
              }
              if (typeof rule.upper !== 'undefined') {
                var maxValue = rule.upper * 1;
                if (!isNaN(maxValue) && value > maxValue) {
                  markParent = true;
                }
              }
            }
            break;
          case 'select':
            if (rule.minItems || rule.maxItems) {
              $relatedField = $(document).find('[name="' + $field.data('relatedfieldname') + '"]');
              if ($relatedField.length) {
                selected = FormEngineValidation.trimExplode(',', $relatedField.val()).length;
              } else {
                selected = $field.find('option:selected').length;
              }
              if (typeof rule.minItems !== 'undefined') {
                minItems = rule.minItems * 1;
                if (!isNaN(minItems) && selected < minItems) {
                  markParent = true;
                }
              }
              if (typeof rule.maxItems !== 'undefined') {
                maxItems = rule.maxItems * 1;
                if (!isNaN(maxItems) && selected > maxItems) {
                  markParent = true;
                }
              }
            }
            break;
          case 'group':
            if (rule.minItems || rule.maxItems) {
              selected = $field.find('option').length;
              if (typeof rule.minItems !== 'undefined') {
                minItems = rule.minItems * 1;
                if (!isNaN(minItems) && selected < minItems) {
                  markParent = true;
                }
              }
              if (typeof rule.maxItems !== 'undefined') {
                maxItems = rule.maxItems * 1;
                if (!isNaN(maxItems) && selected > maxItems) {
                  markParent = true;
                }
              }
            }
            break;
          case 'inline':
            if (rule.minItems || rule.maxItems) {
              selected = FormEngineValidation.trimExplode(',', $field.val()).length;
              if (typeof rule.minItems !== 'undefined') {
                minItems = rule.minItems * 1;
                if (!isNaN(minItems) && selected < minItems) {
                  markParent = true;
                }
              }
              if (typeof rule.maxItems !== 'undefined') {
                maxItems = rule.maxItems * 1;
                if (!isNaN(maxItems) && selected > maxItems) {
                  markParent = true;
                }
              }
            }
            break;
          case 'null':
            // unknown type null, we ignore it
            break;
        }
      });
      if (markParent) {
        // mark field
        $field.closest(FormEngineValidation.markerSelector).addClass(FormEngineValidation.errorClass);
  
        // check tabs
        FormEngineValidation.markParentTab($field);
      }
      return returnValue;
    };
  
    /**
     * Process a value by given command and config
     *
     * @param {String} command
     * @param {String} value
     * @param {Array} config
     * @returns {String}
     */
    FormEngineValidation.processValue = function(command, value, config) {
      var newString = '';
      var theValue = '';
      var theCmd = '';
      var a = 0;
      var returnValue = value;
      switch (command) {
        case 'alpha':
        case 'num':
        case 'alphanum':
        case 'alphanum_x':
          newString = '';
          for (a = 0; a < value.length; a++) {
            theChar = value.substr(a, 1);
            var special = (theChar === '_' || theChar === '-');
            var alpha = (theChar >= 'a' && theChar <= 'z') || (theChar >= 'A' && theChar <= 'Z');
            var num = (theChar >= '0' && theChar <= '9');
            switch (command) {
              case 'alphanum':
                special = 0;
                break;
              case 'alpha':
                num = 0;
                special = 0;
                break;
              case 'num':
                alpha = 0;
                special = 0;
                break;
            }
            if (alpha || num || special) {
              newString += theChar;
            }
          }
          if (newString !== value) {
            returnValue = newString;
          }
          break;
        case 'is_in':
          if (config.is_in) {
            theValue = '' + value;
            for (a = 0; a < theValue.length; a++) {
              var theChar = theValue.substr(a, 1);
              if (config.is_in.indexOf(theChar) != -1) {
                newString += theChar;
              }
            }
          } else {
            newString = theValue;
          }
          returnValue = newString;
          break;
        case 'nospace':
          returnValue = ('' + value).replace(/ /g, '');
          break;
        case 'md5':
          if (value !== '') {
            returnValue = MD5(value);
          }
          break;
        case 'upper':
          returnValue = value.toUpperCase();
          break;
        case 'lower':
          returnValue = value.toLowerCase();
          break;
        case 'int':
          if (value !== '') {
            returnValue = FormEngineValidation.parseInt(value);
          }
          break;
        case 'double2':
          if (value !== '') {
            returnValue = FormEngineValidation.parseDouble(value);
          }
          break;
        case 'trim':
          returnValue = String(value).trim();
          break;
        case 'datetime':
          if (value !== '') {
            theCmd = value.substr(0, 1);
            returnValue = FormEngineValidation.parseDateTime(value, theCmd);
          }
          break;
        case 'date':
          if (value !== '') {
            theCmd = value.substr(0, 1);
            returnValue = FormEngineValidation.parseDate(value, theCmd);
          }
          break;
        case 'time':
        case 'timesec':
          if (value !== '') {
            theCmd = value.substr(0, 1);
            returnValue = FormEngineValidation.parseTime(value, theCmd, command);
          }
          break;
        case 'year':
          if (value !== '') {
            theCmd = value.substr(0, 1);
            returnValue = FormEngineValidation.parseYear(value, theCmd);
          }
          break;
        case 'null':
          // unknown type null, we ignore it
          break;
        case 'password':
          // password is only a display evaluation, we ignore it
          break;
        default:
          if (typeof TBE_EDITOR.customEvalFunctions !== 'undefined' && typeof TBE_EDITOR.customEvalFunctions[command] === 'function') {
            returnValue = TBE_EDITOR.customEvalFunctions[command](value);
          }
      }
      return returnValue;
    };
  
    /**
     * Validate the complete form
     */
    FormEngineValidation.validate = function() {
      $(document).find(FormEngineValidation.markerSelector + ', .t3js-tabmenu-item')
        .removeClass(FormEngineValidation.errorClass)
        .removeClass('has-validation-error');
  
      $(FormEngineValidation.rulesSelector).each(function() {
        var $field = $(this);
        if (!$field.closest('.t3js-flex-section-deleted, .t3js-inline-record-deleted').length) {
          var modified = false;
          var currentValue = $field.val();
          var newValue = FormEngineValidation.validateField($field, currentValue);
          if ($.isArray(newValue) && $.isArray(currentValue)) {
            // handling for multi-selects
            if (newValue.length !== currentValue.length) {
              modified = true;
            } else {
              for (var i = 0; i < newValue.length; i++) {
                if (newValue[i] !== currentValue[i]) {
                  modified = true;
                  break;
                }
              }
            }
          } else if (newValue.length && currentValue !== newValue) {
            modified = true;
          }
          if (modified) {
            $field.val(newValue);
          }
        }
      });
      $(document).trigger('t3-formengine-postfieldvalidation');
    };
  
    /**
     * Helper function to mark a field as changed.
     *
     * @param {Object} $field
     */
    FormEngineValidation.markFieldAsChanged = function($field) {
      var $paletteField = $field.closest('.t3js-formengine-palette-field');
      $paletteField.addClass('has-change');
    };
  
    /**
     * Helper function to get clean trimmed array from comma list
     *
     * @param {String} delimiter
     * @param {String} string
     * @returns {Array}
     */
    FormEngineValidation.trimExplode = function(delimiter, string) {
      var result = [];
      var items = string.split(delimiter);
      for (var i = 0; i < items.length; i++) {
        var item = items[i].trim();
        if (item.length > 0) {
          result.push(item);
        }
      }
      return result;
    };
  
    /**
     * Parse value to integer
     *
     * @param {(Number|String)} value
     * @returns {Number}
     */
    FormEngineValidation.parseInt = function(value) {
      var theVal = '' + value,
        returnValue;
  
      if (!value) {
        return 0;
      }
  
      returnValue = parseInt(theVal, 10);
      if (isNaN(returnValue)) {
        return 0;
      }
      return returnValue;
    };
  
    /**
     * Parse value to double
     *
     * @param {String} value
     * @returns {String}
     */
    FormEngineValidation.parseDouble = function(value) {
      var theVal = '' + value;
      theVal = theVal.replace(/[^0-9,\.-]/g, '');
      var negative = theVal.substring(0, 1) === '-';
      theVal = theVal.replace(/-/g, '');
      theVal = theVal.replace(/,/g, '.');
      if (theVal.indexOf('.') === -1) {
        theVal += '.0';
      }
      var parts = theVal.split('.');
      var dec = parts.pop();
      theVal = Number(parts.join('') + '.' + dec);
      if (negative) {
        theVal *= -1;
      }
      theVal = theVal.toFixed(2);
  
      return theVal;
    };
  
    /**
     *
     * @param {String} value
     * @returns {String}
     */
    FormEngineValidation.ltrim = function(value) {
      var theVal = '' + value;
      if (!value) {
        return '';
      }
      return theVal.replace(/^\s+/, '');
    };
  
    /**
     *
     * @param {String} value
     * @returns {String}
     */
    FormEngineValidation.btrim = function(value) {
      var theVal = '' + value;
      if (!value) {
        return '';
      }
      return theVal.replace(/\s+$/, '');
    };
  
    /**
     * Parse datetime value
     *
     * @param {String} value
     * @param {String} command
     * @returns {*}
     */
    FormEngineValidation.parseDateTime = function(value, command) {
      var today = new Date();
      var values = FormEngineValidation.split(value);
      var add = 0;
      switch (command) {
        case 'd':
        case 't':
        case 'n':
          FormEngineValidation.lastTime = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(today), 0);
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        case '+':
        case '-':
          if (FormEngineValidation.lastTime === 0) {
            FormEngineValidation.lastTime = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(today), 0);
          }
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        default:
          var index = value.indexOf(' ');
          if (index !== -1) {
            var dateVal = FormEngineValidation.parseDate(value.substr(index, value.length), value.substr(0, 1));
            FormEngineValidation.lastTime = dateVal + FormEngineValidation.parseTime(value.substr(0, index), value.substr(0, 1), 'time');
          } else {
            // only date, no time
            FormEngineValidation.lastTime = FormEngineValidation.parseDate(value, value.substr(0, 1));
          }
      }
      FormEngineValidation.lastTime += add * 24 * 60 * 60;
      return FormEngineValidation.lastTime;
    };
  
    /**
     * Parse date value
     *
     * @param {String} value
     * @param {String} command
     * @returns {*}
     */
    FormEngineValidation.parseDate = function(value, command) {
      var today = new Date();
      var values = FormEngineValidation.split(value);
      var add = 0;
      switch (command) {
        case 'd':
        case 't':
        case 'n':
          FormEngineValidation.lastDate = FormEngineValidation.getTimestamp(today);
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        case '+':
        case '-':
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        default:
          var index = 4;
          if (values.valPol[index]) {
            add = FormEngineValidation.pol(values.valPol[index], FormEngineValidation.parseInt(values.values[index]));
          }
          if (values.values[1] && values.values[1].length > 2) {
            if (values.valPol[2]) {
              add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
            }
            var temp = values.values[1];
            values = FormEngineValidation.splitSingle(temp);
          }
  
          var year = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : FormEngineValidation.getYear(today);
          if ((year >= 0 && year < 38) || (year >= 70 && year < 100) || (year >= 1902 && year < 2038)) {
            if (year < 100) {
              year = (year < 38) ? year += 2000 : year += 1900;
            }
          } else {
            year = FormEngineValidation.getYear(today);
          }
          var usMode = FormEngineValidation.USmode ? 1 : 2;
          var month = (values.values[usMode]) ? FormEngineValidation.parseInt(values.values[usMode]) : today.getUTCMonth() + 1;
          usMode = FormEngineValidation.USmode ? 2 : 1;
          var day = (values.values[usMode]) ? FormEngineValidation.parseInt(values.values[usMode]) : today.getUTCDate();
  
  
          var theTime = moment.utc();
          theTime.year(parseInt(year)).month(parseInt(month) - 1).date(parseInt(day)).hour(0).minute(0).second(0);
  
          FormEngineValidation.lastDate = theTime.unix();
      }
      FormEngineValidation.lastDate += add * 24 * 60 * 60;
      return FormEngineValidation.lastDate;
    };
  
    /**
     * Parse time value
     *
     * @param {String} value
     * @param {String} command
     * @param {String} type
     * @returns {*}
     */
    FormEngineValidation.parseTime = function(value, command, type) {
      var today = new Date();
      var values = FormEngineValidation.split(value);
      var add = 0;
      switch (command) {
        case 'd':
        case 't':
        case 'n':
          FormEngineValidation.lastTime = FormEngineValidation.getTimeSecs(today);
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        case '+':
        case '-':
          if (FormEngineValidation.lastTime == 0) {
            FormEngineValidation.lastTime = FormEngineValidation.getTimeSecs(today);
          }
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        default:
          var index = (type === 'timesec') ? 4 : 3;
          if (values.valPol[index]) {
            add = FormEngineValidation.pol(values.valPol[index], FormEngineValidation.parseInt(values.values[index]));
          }
          if (values.values[1] && values.values[1].length > 2) {
            if (values.valPol[2]) {
              add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
            }
            var temp = values.values[1];
            values = FormEngineValidation.splitSingle(temp);
          }
          var sec = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : today.getUTCSeconds();
          if (sec > 59) {
            sec = 59;
          }
          var min = (values.values[2]) ? FormEngineValidation.parseInt(values.values[2]) : today.getUTCMinutes();
          if (min > 59) {
            min = 59;
          }
          var hour = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : today.getUTCHours();
          if (hour >= 24) {
            hour = 0;
          }
  
          var theTime = moment.utc();
          theTime.year(1970).month(0).date(1).hour(hour).minute(min).second(type === 'timesec' ? sec : 0);
  
          FormEngineValidation.lastTime = theTime.unix();
      }
      FormEngineValidation.lastTime += add * 60;
      if (FormEngineValidation.lastTime < 0) {
        FormEngineValidation.lastTime += 24 * 60 * 60;
      }
      return FormEngineValidation.lastTime;
    };
  
    /**
     * Parse year value
     *
     * @param {String} value
     * @param {String} command
     * @returns {*}
     */
    FormEngineValidation.parseYear = function(value, command) {
      var today = new Date();
      var values = FormEngineValidation.split(value);
      var add = 0;
      switch (command) {
        case 'd':
        case 't':
        case 'n':
          FormEngineValidation.lastYear = FormEngineValidation.getYear(today);
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        case '+':
        case '-':
          if (values.valPol[1]) {
            add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
          }
          break;
        default:
          if (values.valPol[2]) {
            add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
          }
          var year = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : FormEngineValidation.getYear(today);
          if ((year >= 0 && year < 38) || (year >= 70 && year < 100) || (year >= 1902 && year < 2038)) {
            if (year < 100) {
              year = (year < 38) ? year += 2000 : year += 1900;
            }
          } else {
            year = FormEngineValidation.getYear(today);
          }
          FormEngineValidation.lastYear = year;
      }
      FormEngineValidation.lastYear += add;
      return FormEngineValidation.lastYear;
    };
  
    /**
     * Get year from date object
     *
     * @param {Date} timeObj
     * @returns {?number}
     */
    FormEngineValidation.getYear = function(timeObj) {
      if (timeObj === null) {
        return null;
      }
      return timeObj.getUTCFullYear();
    };
  
    /**
     * Get date as timestamp from Date object
     *
     * @param {Date} timeObj
     * @returns {Number}
     */
    FormEngineValidation.getDate = function(timeObj) {
      var theTime = new Date(FormEngineValidation.getYear(timeObj), timeObj.getUTCMonth(), timeObj.getUTCDate());
      return FormEngineValidation.getTimestamp(theTime);
    };
  
    /**
     *
     * @param {String} foreign
     * @param {String} value
     * @returns {Object}
     */
    FormEngineValidation.pol = function(foreign, value) {
      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, timeonly) {
      var 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
     *
     * @param {(String|Date)} timeObj
     * @returns {Number}
     */
    FormEngineValidation.getTimestamp = function(timeObj) {
      return Date.parse(timeObj) / 1000;
    };
  
    /**
     * Seconds since midnight
     *
     * @param timeObj
     * @returns {*}
     */
    FormEngineValidation.getTime = function(timeObj) {
      return timeObj.getUTCHours() * 60 * 60 + timeObj.getUTCMinutes() * 60 + FormEngineValidation.getSecs(timeObj);
    };
  
    /**
     *
     * @param timeObj
     * @returns {Number}
     */
    FormEngineValidation.getSecs = function(timeObj) {
      return timeObj.getUTCSeconds();
    };
  
    /**
     *
     * @param timeObj
     * @returns {Number}
     */
    FormEngineValidation.getTimeSecs = function(timeObj) {
      return timeObj.getHours() * 60 * 60 + timeObj.getMinutes() * 60 + timeObj.getSeconds();
    };
  
    /**
     * Find tab by field and mark it as has-validation-error
     *
     * @param {Object} $element
     */
    FormEngineValidation.markParentTab = function($element) {
      var $panes = $element.parents('.tab-pane');
      $panes.each(function() {
        var $pane = $(this);
        var id = $pane.attr('id');
        $(document)
          .find('a[href="#' + id + '"]')
          .closest('.t3js-tabmenu-item')
          .addClass('has-validation-error');
      });
    };
  
    /**
     *
     * @param value
     * @returns {{values: Array, pointer: number}}
     */
    FormEngineValidation.splitSingle = function(value) {
      var theVal = '' + value;
      var result = {
        values: [],
        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, delim, index) {
      var theStr = '' + theStr1;
      var lengthOfDelim = delim.length;
      var sPos = -lengthOfDelim;
      if (index < 1) {
        index = 1;
      }
      for (var a = 1; a < index; a++) {
        sPos = theStr.indexOf(delim, sPos + lengthOfDelim);
        if (sPos == -1) {
          return null;
        }
      }
      var 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) {
      var result = {
        values: [],
        valPol: [],
        pointer: 0,
        numberMode: 0,
        theVal: ''
      };
      value += ' ';
      for (var a = 0; a < value.length; a++) {
        var 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;
    };
  
    return FormEngineValidation;
  });
  