ff6497ff7b268577f0cebb07e36be0c233c25ab2
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Resources / Public / JavaScript / FormEngineValidation.js
1 /*
2 * This file is part of the TYPO3 CMS project.
3 *
4 * It is free software; you can redistribute it and/or modify it under
5 * the terms of the GNU General Public License, either version 2
6 * of the License, or any later version.
7 *
8 * For the full copyright and license information, please read the
9 * LICENSE.txt file that was distributed with this source code.
10 *
11 * The TYPO3 project - inspiring people to share!
12 */
13
14 /**
15 * Module: TYPO3/CMS/Backend/FormEngineValidation
16 * Contains all JS functions related to TYPO3 TCEforms/FormEngineValidation
17 * @internal
18 */
19 define(['jquery', 'TYPO3/CMS/Backend/FormEngine', 'moment'], function ($, FormEngine, moment) {
20
21 /**
22 * The main FormEngineValidation object
23 *
24 * @type {{rulesSelector: string, inputSelector: string, markerSelector: string, dateTimeSelector: string, groupFieldHiddenElement: string, relatedFieldSelector: string, errorClass: string, lastYear: number, lastDate: number, lastTime: number, refDate: Date, USmode: number, passwordDummy: string}}
25 * @exports TYPO3/CMS/Backend/FormEngineValidation
26 */
27 var FormEngineValidation = {
28 rulesSelector: '[data-formengine-validation-rules]',
29 inputSelector: '[data-formengine-input-params]',
30 markerSelector: '.t3js-formengine-validation-marker',
31 dateTimeSelector: '.t3js-datetimepicker',
32 groupFieldHiddenElement: '.t3js-formengine-field-group input[type=hidden]',
33 relatedFieldSelector: '[data-relatedfieldname]',
34 errorClass: 'has-error',
35 lastYear: 0,
36 lastDate: 0,
37 lastTime: 0,
38 refDate: new Date(),
39 USmode: 0,
40 passwordDummy: '********'
41 };
42
43 /**
44 * Initialize validation for the first time
45 */
46 FormEngineValidation.initialize = function() {
47 $(document).find('.' + FormEngineValidation.errorClass).removeClass(FormEngineValidation.errorClass);
48
49 // Initialize input fields
50 FormEngineValidation.initializeInputFields().promise().done(function () {
51 // Bind to field changes
52 $(document).on('change', FormEngineValidation.rulesSelector, function() {
53 // we need to wait, because the update of the select fields needs some time
54 window.setTimeout(function() {
55 FormEngineValidation.validate();
56 }, 500);
57 var $paletteField = $(this).closest('.t3js-formengine-palette-field');
58 $paletteField.addClass('has-change');
59 });
60
61 // Bind to datepicker change event, but wait some milliseconds, because the init is not so fast
62 window.setTimeout(function() {
63 //noinspection JSUnusedLocalSymbols
64 $(document).on('dp.change', FormEngineValidation.dateTimeSelector, function(event) {
65 FormEngineValidation.validate();
66 var $paletteField = $(this).closest('.t3js-formengine-palette-field');
67 $paletteField.addClass('has-change');
68 });
69 }, 500);
70 });
71
72 var today = new Date();
73 FormEngineValidation.lastYear = FormEngineValidation.getYear(today);
74 FormEngineValidation.lastDate = FormEngineValidation.getDate(today);
75 FormEngineValidation.lastTime = 0;
76 FormEngineValidation.refDate = today;
77 FormEngineValidation.USmode = 0;
78 };
79
80 /**
81 * Initialize all input fields
82 *
83 * @returns {Object}
84 */
85 FormEngineValidation.initializeInputFields = function() {
86 return $(document).find(FormEngineValidation.inputSelector).each(function() {
87 var config = $(this).data('formengine-input-params');
88 var fieldName = config.field;
89 var $field = $('[name="' + fieldName + '"]');
90
91 // ignore fields which already have been initialized
92 if ($field.data('main-field') === undefined) {
93 $field.data('main-field', fieldName);
94 $field.data('config', config);
95 FormEngineValidation.initializeInputField(fieldName);
96 }
97 });
98 };
99
100 /**
101 *
102 * @param {Number} mode
103 */
104 FormEngineValidation.setUsMode = function(mode) {
105 FormEngineValidation.USmode = mode;
106 };
107
108 /**
109 * Initialize field by name
110 *
111 * @param {String} fieldName
112 */
113 FormEngineValidation.initializeInputField = function(fieldName) {
114 var $field = $('[name="' + fieldName + '"]');
115 var $humanReadableField = $('[data-formengine-input-name="' + fieldName + '"]');
116 var $mainField = $('[name="' + $field.data('main-field') + '"]');
117 if ($mainField.length === 0) {
118 $mainField = $field;
119 }
120
121 var config = $mainField.data('config');
122 if (typeof config !== 'undefined') {
123 var evalList = FormEngineValidation.trimExplode(',', config.evalList);
124 var value = $field.val();
125
126 for (var i = 0; i < evalList.length; i++) {
127 value = FormEngineValidation.formatValue(evalList[i], value, config)
128 }
129 // Prevent password fields to be overwritten with original value
130 if (value.length && $humanReadableField.attr('type') != 'password') {
131 $humanReadableField.val(value);
132 }
133 }
134 $humanReadableField.data('main-field', fieldName);
135 $humanReadableField.data('config', config);
136 $humanReadableField.on('change', function() {
137 FormEngineValidation.updateInputField($(this).attr('data-formengine-input-name'));
138 });
139 $humanReadableField.on('keyup', FormEngineValidation.validate);
140
141 // add the attribute so that acceptance tests can know when the field initialization has completed
142 $humanReadableField.attr('data-formengine-input-initialized', 'true');
143 };
144
145 /**
146 * Format field value
147 *
148 * @param {String} type
149 * @param {String} value
150 * @param {Object} config
151 * @returns {String}
152 */
153 FormEngineValidation.formatValue = function(type, value, config) {
154 var theString = '';
155 switch (type) {
156 case 'date':
157 // poor man’s ISO-8601 detection: if we have a "-" in it, it apparently is not an integer.
158 if (value.toString().indexOf('-') > 0) {
159 var date = moment(value).utc();
160 if (FormEngineValidation.USmode) {
161 theString = date.format('MM-DD-YYYY');
162 } else {
163 theString = date.format('DD-MM-YYYY');
164 }
165 } else {
166 var parsedInt = parseInt(value);
167 if (!parsedInt) {
168 return '';
169 }
170 theTime = new Date(parsedInt * 1000);
171 if (FormEngineValidation.USmode) {
172 theString = (theTime.getUTCMonth() + 1) + '-' + theTime.getUTCDate() + '-' + this.getYear(theTime);
173 } else {
174 theString = theTime.getUTCDate() + '-' + (theTime.getUTCMonth() + 1) + '-' + this.getYear(theTime);
175 }
176 }
177 break;
178 case 'datetime':
179 if (value.toString().indexOf('-') <= 0 && !parseInt(value)) {
180 return '';
181 }
182 theString = FormEngineValidation.formatValue('time', value, config) + ' ' + FormEngineValidation.formatValue('date', value, config);
183 break;
184 case 'time':
185 case 'timesec':
186 if (value.toString().indexOf('-') > 0) {
187 var date = moment(value).utc();
188 if (type == 'timesec') {
189 theString = date.format('HH:mm:ss');
190 } else {
191 theString = date.format('HH:mm');
192 }
193 } else {
194 var parsedInt = parseInt(value);
195 if (!parsedInt && value.toString() !== '0') {
196 return '';
197 }
198 var theTime = new Date(parsedInt * 1000);
199 var h = theTime.getUTCHours();
200 var m = theTime.getUTCMinutes();
201 var s = theTime.getUTCSeconds();
202 theString = h + ':' + ((m < 10) ? '0' : '') + m + ((type == 'timesec') ? ':' + ((s < 10) ? '0' : '') + s : '');
203 }
204 break;
205 case 'password':
206 theString = (value) ? FormEngineValidation.passwordDummy : '';
207 break;
208 default:
209 theString = value;
210 }
211 return theString;
212 };
213
214 /**
215 * Update input field after change
216 *
217 * @param {String} fieldName
218 */
219 FormEngineValidation.updateInputField = function(fieldName) {
220 var $field = $('[name="' + fieldName + '"]');
221 var $mainField = $('[name="' + $field.data('main-field') + '"]');
222 if ($mainField.length === 0) {
223 $mainField = $field;
224 }
225 var $humanReadableField = $('[data-formengine-input-name="' + $mainField.attr('name') + '"]');
226
227 var config = $mainField.data('config');
228 if (typeof config !== 'undefined') {
229 var evalList = FormEngineValidation.trimExplode(',', config.evalList);
230 var newValue = $humanReadableField.val();
231 var i;
232
233 for (i = 0; i < evalList.length; i++) {
234 newValue = FormEngineValidation.processValue(evalList[i], newValue, config);
235 }
236
237 var formattedValue = newValue;
238 for (i = 0; i < evalList.length; i++) {
239 formattedValue = FormEngineValidation.formatValue(evalList[i], formattedValue, config);
240 }
241
242 $mainField.val(newValue);
243 $humanReadableField.val(formattedValue);
244 }
245 };
246
247 /**
248 * Run validation for field
249 *
250 * @param {Object} $field
251 * @param {String} [value=$field.val()]
252 * @returns {String}
253 */
254 FormEngineValidation.validateField = function($field, value) {
255 value = value || $field.val() || '';
256
257 var rules = $field.data('formengine-validation-rules');
258 var markParent = false;
259 var selected = 0;
260 // keep the original value, validateField should not alter it
261 var returnValue = value;
262 var $relatedField;
263 var minItems;
264 var maxItems;
265
266 if (!$.isArray(value)) {
267 value = FormEngineValidation.ltrim(value);
268 }
269
270 $.each(rules, function(k, rule) {
271 switch (rule.type) {
272 case 'required':
273 if (value === '') {
274 markParent = true;
275 $field.closest(FormEngineValidation.markerSelector).addClass(FormEngineValidation.errorClass);
276 }
277 break;
278 case 'range':
279 if (value !== '') {
280 if (rule.minItems || rule.maxItems) {
281 $relatedField = $(document).find('[name="' + $field.data('relatedfieldname') + '"]');
282 if ($relatedField.length) {
283 selected = FormEngineValidation.trimExplode(',', $relatedField.val()).length;
284 } else {
285 selected = $field.val();
286 }
287 if (typeof rule.minItems !== 'undefined') {
288 minItems = rule.minItems * 1;
289 if (!isNaN(minItems) && selected < minItems) {
290 markParent = true;
291 }
292 }
293 if (typeof rule.maxItems !== 'undefined') {
294 maxItems = rule.maxItems * 1;
295 if (!isNaN(maxItems) && selected > maxItems) {
296 markParent = true;
297 }
298 }
299 }
300 if (typeof rule.lower !== 'undefined') {
301 var minValue = rule.lower * 1;
302 if (!isNaN(minValue) && value < minValue) {
303 markParent = true;
304 }
305 }
306 if (typeof rule.upper !== 'undefined') {
307 var maxValue = rule.upper * 1;
308 if (!isNaN(maxValue) && value > maxValue) {
309 markParent = true;
310 }
311 }
312 }
313 break;
314 case 'select':
315 if (rule.minItems || rule.maxItems) {
316 $relatedField = $(document).find('[name="' + $field.data('relatedfieldname') + '"]');
317 if ($relatedField.length) {
318 selected = FormEngineValidation.trimExplode(',', $relatedField.val()).length;
319 } else {
320 selected = $field.find('option:selected').length;
321 }
322 if (typeof rule.minItems !== 'undefined') {
323 minItems = rule.minItems * 1;
324 if (!isNaN(minItems) && selected < minItems) {
325 markParent = true;
326 }
327 }
328 if (typeof rule.maxItems !== 'undefined') {
329 maxItems = rule.maxItems * 1;
330 if (!isNaN(maxItems) && selected > maxItems) {
331 markParent = true;
332 }
333 }
334 }
335 break;
336 case 'group':
337 if (rule.minItems || rule.maxItems) {
338 selected = $field.find('option').length;
339 if (typeof rule.minItems !== 'undefined') {
340 minItems = rule.minItems * 1;
341 if (!isNaN(minItems) && selected < minItems) {
342 markParent = true;
343 }
344 }
345 if (typeof rule.maxItems !== 'undefined') {
346 maxItems = rule.maxItems * 1;
347 if (!isNaN(maxItems) && selected > maxItems) {
348 markParent = true;
349 }
350 }
351 }
352 break;
353 case 'inline':
354 if (rule.minItems || rule.maxItems) {
355 selected = FormEngineValidation.trimExplode(',', $field.val()).length;
356 if (typeof rule.minItems !== 'undefined') {
357 minItems = rule.minItems * 1;
358 if (!isNaN(minItems) && selected < minItems) {
359 markParent = true;
360 }
361 }
362 if (typeof rule.maxItems !== 'undefined') {
363 maxItems = rule.maxItems * 1;
364 if (!isNaN(maxItems) && selected > maxItems) {
365 markParent = true;
366 }
367 }
368 }
369 break;
370 case 'null':
371 // unknown type null, we ignore it
372 break;
373 }
374 });
375 if (markParent) {
376 // mark field
377 $field.closest(FormEngineValidation.markerSelector).addClass(FormEngineValidation.errorClass);
378
379 // check tabs
380 FormEngineValidation.markParentTab($field);
381 }
382 return returnValue;
383 };
384
385 /**
386 * Process a value by given command and config
387 *
388 * @param {String} command
389 * @param {String} value
390 * @param {Array} config
391 * @returns {String}
392 */
393 FormEngineValidation.processValue = function(command, value, config) {
394 var newString = '';
395 var theValue = '';
396 var theCmd = '';
397 var a = 0;
398 var returnValue = value;
399 switch (command) {
400 case 'alpha':
401 case 'num':
402 case 'alphanum':
403 case 'alphanum_x':
404 newString = '';
405 for (a = 0; a < value.length; a++) {
406 theChar = value.substr(a, 1);
407 var special = (theChar === '_' || theChar === '-');
408 var alpha = (theChar >= 'a' && theChar <= 'z') || (theChar >= 'A' && theChar <= 'Z');
409 var num = (theChar >= '0' && theChar <= '9');
410 switch (command) {
411 case 'alphanum':
412 special = 0;
413 break;
414 case 'alpha':
415 num = 0;
416 special = 0;
417 break;
418 case 'num':
419 alpha = 0;
420 special = 0;
421 break;
422 }
423 if (alpha || num || special) {
424 newString += theChar;
425 }
426 }
427 if (newString !== value) {
428 returnValue = newString;
429 }
430 break;
431 case 'is_in':
432 if (config.is_in) {
433 theValue = '' + value;
434 for (a = 0; a < theValue.length; a++) {
435 var theChar = theValue.substr(a, 1);
436 if (config.is_in.indexOf(theChar) != -1) {
437 newString += theChar;
438 }
439 }
440 } else {
441 newString = theValue;
442 }
443 returnValue = newString;
444 break;
445 case 'nospace':
446 returnValue = '' + value.replace(/ /g, '');
447 break;
448 case 'md5':
449 if (value !== '') {
450 returnValue = MD5(value);
451 }
452 break;
453 case 'upper':
454 returnValue = value.toUpperCase();
455 break;
456 case 'lower':
457 returnValue = value.toLowerCase();
458 break;
459 case 'int':
460 if (value !== '') {
461 returnValue = FormEngineValidation.parseInt(value);
462 }
463 break;
464 case 'double2':
465 if (value !== '') {
466 returnValue = FormEngineValidation.parseDouble(value);
467 }
468 break;
469 case 'trim':
470 returnValue = String(value).trim();
471 break;
472 case 'datetime':
473 if (value !== '') {
474 theCmd = value.substr(0, 1);
475 returnValue = FormEngineValidation.parseDateTime(value, theCmd);
476 }
477 break;
478 case 'date':
479 if (value !== '') {
480 theCmd = value.substr(0, 1);
481 returnValue = FormEngineValidation.parseDate(value, theCmd);
482 }
483 break;
484 case 'time':
485 case 'timesec':
486 if (value !== '') {
487 theCmd = value.substr(0, 1);
488 returnValue = FormEngineValidation.parseTime(value, theCmd, command);
489 }
490 break;
491 case 'year':
492 if (value !== '') {
493 theCmd = value.substr(0, 1);
494 returnValue = FormEngineValidation.parseYear(value, theCmd);
495 }
496 break;
497 case 'null':
498 // unknown type null, we ignore it
499 break;
500 case 'password':
501 // password is only a display evaluation, we ignore it
502 break;
503 default:
504 if (typeof TBE_EDITOR.customEvalFunctions !== 'undefined' && typeof TBE_EDITOR.customEvalFunctions[command] === 'function') {
505 returnValue = TBE_EDITOR.customEvalFunctions[command](value);
506 }
507 }
508 return returnValue;
509 };
510
511 /**
512 * Validate the complete form
513 */
514 FormEngineValidation.validate = function() {
515 $(document).find(FormEngineValidation.markerSelector + ', .t3js-tabmenu-item')
516 .removeClass(FormEngineValidation.errorClass)
517 .removeClass('has-validation-error');
518
519 $(FormEngineValidation.rulesSelector).each(function() {
520 var $field = $(this);
521 if (!$field.closest('.t3js-flex-section-deleted, .t3js-inline-record-deleted').length) {
522 var modified = false;
523 var currentValue = $field.val();
524 var newValue = FormEngineValidation.validateField($field, currentValue);
525 if ($.isArray(newValue) && $.isArray(currentValue)) {
526 // handling for multi-selects
527 if (newValue.length !== currentValue.length) {
528 modified = true;
529 } else {
530 for (var i = 0; i < newValue.length; i++) {
531 if (newValue[i] !== currentValue[i]) {
532 modified = true;
533 break;
534 }
535 }
536 }
537 } else if (newValue.length && currentValue !== newValue) {
538 modified = true;
539 }
540 if (modified) {
541 $field.val(newValue);
542 }
543 }
544 });
545 $(document).trigger('t3-formengine-postfieldvalidation');
546 };
547
548 /**
549 * Helper function to get clean trimmed array from comma list
550 *
551 * @param {String} delimiter
552 * @param {String} string
553 * @returns {Array}
554 */
555 FormEngineValidation.trimExplode = function(delimiter, string) {
556 var result = [];
557 var items = string.split(delimiter);
558 for (var i=0; i<items.length; i++) {
559 var item = items[i].trim();
560 if (item.length > 0) {
561 result.push(item);
562 }
563 }
564 return result;
565 };
566
567 /**
568 * Parse value to integer
569 *
570 * @param {(Number|String)} value
571 * @returns {Number}
572 */
573 FormEngineValidation.parseInt = function(value) {
574 var theVal = '' + value,
575 returnValue;
576
577 if (!value) {
578 return 0;
579 }
580
581 returnValue = parseInt(theVal, 10);
582 if (isNaN(returnValue)) {
583 return 0;
584 }
585 return returnValue;
586 };
587
588 /**
589 * Parse value to double
590 *
591 * @param {String} value
592 * @returns {String}
593 */
594 FormEngineValidation.parseDouble = function(value) {
595 var theVal = '' + value;
596 theVal = theVal.replace(/[^0-9,\.-]/g, '');
597 var negative = theVal.substring(0, 1) === '-';
598 theVal = theVal.replace(/-/g, '');
599 theVal = theVal.replace(/,/g, '.');
600 if (theVal.indexOf('.') === -1) {
601 theVal += '.0';
602 }
603 var parts = theVal.split('.');
604 var dec = parts.pop();
605 theVal = Number(parts.join('') + '.' + dec);
606 if (negative) {
607 theVal *= -1;
608 }
609 theVal = theVal.toFixed(2);
610
611 return theVal;
612 };
613
614 /**
615 *
616 * @param {String} value
617 * @returns {String}
618 */
619 FormEngineValidation.ltrim = function(value) {
620 var theVal = '' + value;
621 if (!value) {
622 return '';
623 }
624 return theVal.replace(/^\s+/, '');
625 };
626
627 /**
628 *
629 * @param {String} value
630 * @returns {String}
631 */
632 FormEngineValidation.btrim = function(value) {
633 var theVal = '' + value;
634 if (!value) {
635 return '';
636 }
637 return theVal.replace(/\s+$/, '');
638 };
639
640 /**
641 * Parse datetime value
642 *
643 * @param {String} value
644 * @param {String} command
645 * @returns {*}
646 */
647 FormEngineValidation.parseDateTime = function(value, command) {
648 var today = new Date();
649 var values = FormEngineValidation.split(value);
650 var add = 0;
651 switch (command) {
652 case 'd':
653 case 't':
654 case 'n':
655 FormEngineValidation.lastTime = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(today), 0);
656 if (values.valPol[1]) {
657 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
658 }
659 break;
660 case '+':
661 case '-':
662 if (FormEngineValidation.lastTime == 0) {
663 FormEngineValidation.lastTime = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(today), 0);
664 }
665 if (values.valPol[1]) {
666 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
667 }
668 break;
669 default:
670 var index = value.indexOf(' ');
671 if (index != -1) {
672 var dateVal = FormEngineValidation.parseDate(value.substr(index, value.length), value.substr(0, 1));
673 // set refDate so that evalFunc_input on time will work with correct DST information
674 FormEngineValidation.refDate = new Date(dateVal * 1000);
675 FormEngineValidation.lastTime = dateVal + FormEngineValidation.parseTime(value.substr(0,index), value.substr(0, 1), 'time');
676 } else {
677 // only date, no time
678 FormEngineValidation.lastTime = FormEngineValidation.parseDate(value, value.substr(0, 1));
679 }
680 }
681 FormEngineValidation.lastTime += add * 24 * 60 * 60;
682 return FormEngineValidation.lastTime;
683 };
684
685 /**
686 * Parse date value
687 *
688 * @param {String} value
689 * @param {String} command
690 * @returns {*}
691 */
692 FormEngineValidation.parseDate = function(value, command) {
693 var today = new Date();
694 var values = FormEngineValidation.split(value);
695 var add = 0;
696 switch (command) {
697 case 'd':
698 case 't':
699 case 'n':
700 FormEngineValidation.lastDate = FormEngineValidation.getTimestamp(today);
701 if (values.valPol[1]) {
702 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
703 }
704 break;
705 case '+':
706 case '-':
707 if (values.valPol[1]) {
708 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
709 }
710 break;
711 default:
712 var index = 4;
713 if (values.valPol[index]) {
714 add = FormEngineValidation.pol(values.valPol[index], FormEngineValidation.parseInt(values.values[index]));
715 }
716 if (values.values[1] && values.values[1].length > 2) {
717 if (values.valPol[2]) {
718 add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
719 }
720 var temp = values.values[1];
721 values = FormEngineValidation.splitSingle(temp);
722 }
723
724 var year = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : FormEngineValidation.getYear(today);
725 if ((year >= 0 && year < 38) || (year >= 70 && year < 100) || (year >= 1902 && year < 2038)) {
726 if (year < 100) {
727 year = (year < 38) ? year += 2000 : year += 1900;
728 }
729 } else {
730 year = FormEngineValidation.getYear(today);
731 }
732 var usMode = FormEngineValidation.USmode ? 1 : 2;
733 var month = (values.values[usMode]) ? FormEngineValidation.parseInt(values.values[usMode]) : today.getUTCMonth() + 1;
734 usMode = FormEngineValidation.USmode ? 2 : 1;
735 var day = (values.values[usMode]) ? FormEngineValidation.parseInt(values.values[usMode]) : today.getUTCDate();
736
737 var theTime = new Date(parseInt(year), parseInt(month)-1, parseInt(day));
738
739 // Substract timezone offset from client
740 FormEngineValidation.lastDate = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(theTime), 0);
741 }
742 FormEngineValidation.lastDate += add * 24 * 60 * 60;
743 return FormEngineValidation.lastDate;
744 };
745
746 /**
747 * Parse time value
748 *
749 * @param {String} value
750 * @param {String} command
751 * @param {String} type
752 * @returns {*}
753 */
754 FormEngineValidation.parseTime = function(value, command, type) {
755 var today = new Date();
756 var values = FormEngineValidation.split(value);
757 var add = 0;
758 switch (command) {
759 case 'd':
760 case 't':
761 case 'n':
762 FormEngineValidation.lastTime = FormEngineValidation.getTimeSecs(today);
763 if (values.valPol[1]) {
764 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
765 }
766 break;
767 case '+':
768 case '-':
769 if (FormEngineValidation.lastTime == 0) {
770 FormEngineValidation.lastTime = FormEngineValidation.getTimeSecs(today);
771 }
772 if (values.valPol[1]) {
773 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
774 }
775 break;
776 default:
777 var index = (type == 'timesec') ? 4 : 3;
778 if (values.valPol[index]) {
779 add = FormEngineValidation.pol(values.valPol[index], FormEngineValidation.parseInt(values.values[index]));
780 }
781 if (values.values[1] && values.values[1].length > 2) {
782 if (values.valPol[2]) {
783 add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
784 }
785 var temp = values.values[1];
786 values = FormEngineValidation.splitSingle(temp);
787 }
788 var sec = (values.values[3]) ? FormEngineValidation.parseInt(values.values[3]) : today.getUTCSeconds();
789 if (sec > 59) {
790 sec = 59;
791 }
792 var min = (values.values[2]) ? FormEngineValidation.parseInt(values.values[2]) : today.getUTCMinutes();
793 if (min > 59) {
794 min = 59;
795 }
796 var hour = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : today.getUTCHours();
797 if (hour >= 24) {
798 hour = 0;
799 }
800
801 var theTime = new Date(FormEngineValidation.getYear(FormEngineValidation.refDate), FormEngineValidation.refDate.getUTCMonth(), FormEngineValidation.refDate.getUTCDate(), hour, min, (( type == 'timesec' ) ? sec : 0));
802
803 // Substract timezone offset from client
804 FormEngineValidation.lastTime = FormEngineValidation.convertClientTimestampToUTC(FormEngineValidation.getTimestamp(theTime), 1);
805 }
806 FormEngineValidation.lastTime += add * 60;
807 if (FormEngineValidation.lastTime < 0) {
808 FormEngineValidation.lastTime += 24 * 60 * 60;
809 }
810 return FormEngineValidation.lastTime;
811 };
812
813 /**
814 * Parse year value
815 *
816 * @param {String} value
817 * @param {String} command
818 * @returns {*}
819 */
820 FormEngineValidation.parseYear = function(value, command) {
821 var today = new Date();
822 var values = FormEngineValidation.split(value);
823 var add = 0;
824 switch (command) {
825 case 'd':
826 case 't':
827 case 'n':
828 FormEngineValidation.lastYear = FormEngineValidation.getYear(today);
829 if (values.valPol[1]) {
830 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
831 }
832 break;
833 case '+':
834 case '-':
835 if (values.valPol[1]) {
836 add = FormEngineValidation.pol(values.valPol[1], FormEngineValidation.parseInt(values.values[1]));
837 }
838 break;
839 default:
840 if (values.valPol[2]) {
841 add = FormEngineValidation.pol(values.valPol[2], FormEngineValidation.parseInt(values.values[2]));
842 }
843 var year = (values.values[1]) ? FormEngineValidation.parseInt(values.values[1]) : FormEngineValidation.getYear(today);
844 if ((year >= 0 && year < 38) || (year >= 70 && year<100) || (year >= 1902 && year < 2038)) {
845 if (year < 100) {
846 year = (year < 38) ? year += 2000 : year += 1900;
847 }
848 } else {
849 year = FormEngineValidation.getYear(today);
850 }
851 FormEngineValidation.lastYear = year;
852 }
853 FormEngineValidation.lastYear += add;
854 return FormEngineValidation.lastYear;
855 };
856
857 /**
858 * Get year from date object
859 *
860 * @param {Date} timeObj
861 * @returns {?number}
862 */
863 FormEngineValidation.getYear = function(timeObj) {
864 if (timeObj === null) {
865 return null;
866 }
867 return timeObj.getUTCFullYear();
868 };
869
870 /**
871 * Get date as timestamp from Date object
872 *
873 * @param {Date} timeObj
874 * @returns {Number}
875 */
876 FormEngineValidation.getDate = function(timeObj) {
877 var theTime = new Date(FormEngineValidation.getYear(timeObj), timeObj.getUTCMonth(), timeObj.getUTCDate());
878 return FormEngineValidation.getTimestamp(theTime);
879 };
880
881 /**
882 *
883 * @param {String} foreign
884 * @param {String} value
885 * @returns {Object}
886 */
887 FormEngineValidation.pol = function(foreign, value) {
888 return eval(((foreign == '-') ? '-' : '') + value);
889 };
890
891 /**
892 * Substract timezone offset from client to a timestamp to get UTC-timestamp to be send to server
893 *
894 * @param {Number} timestamp
895 * @param {Number} timeonly
896 * @returns {*}
897 */
898 FormEngineValidation.convertClientTimestampToUTC = function(timestamp, timeonly) {
899 var timeObj = new Date(timestamp*1000);
900 timeObj.setTime((timestamp - timeObj.getTimezoneOffset()*60)*1000);
901 if (timeonly) {
902 // only seconds since midnight
903 return FormEngineValidation.getTime(timeObj);
904 } else {
905 // seconds since the "unix-epoch"
906 return FormEngineValidation.getTimestamp(timeObj);
907 }
908 };
909
910 /**
911 * Parse date string or object and return unix timestamp
912 *
913 * @param {(String|Date)} timeObj
914 * @returns {Number}
915 */
916 FormEngineValidation.getTimestamp = function(timeObj) {
917 return Date.parse(timeObj)/1000;
918 };
919
920 /**
921 * Seconds since midnight
922 *
923 * @param timeObj
924 * @returns {*}
925 */
926 FormEngineValidation.getTime = function(timeObj) {
927 return timeObj.getUTCHours() * 60 * 60 + timeObj.getUTCMinutes() * 60 + FormEngineValidation.getSecs(timeObj);
928 };
929
930 /**
931 *
932 * @param timeObj
933 * @returns {Number}
934 */
935 FormEngineValidation.getSecs = function(timeObj) {
936 return timeObj.getUTCSeconds();
937 };
938
939 /**
940 *
941 * @param timeObj
942 * @returns {Number}
943 */
944 FormEngineValidation.getTimeSecs = function(timeObj) {
945 return timeObj.getHours() * 60 * 60 + timeObj.getMinutes() * 60 + timeObj.getSeconds();
946 };
947
948 /**
949 * Find tab by field and mark it as has-validation-error
950 *
951 * @param {Object} $element
952 */
953 FormEngineValidation.markParentTab = function($element) {
954 var $panes = $element.parents('.tab-pane');
955 $panes.each(function() {
956 var $pane = $(this);
957 var id = $pane.attr('id');
958 $(document)
959 .find('a[href="#' + id + '"]')
960 .closest('.t3js-tabmenu-item')
961 .addClass('has-validation-error');
962 });
963 };
964
965 /**
966 *
967 * @param value
968 * @returns {{values: Array, pointer: number}}
969 */
970 FormEngineValidation.splitSingle = function(value) {
971 var theVal = '' + value;
972 var result = {
973 values: [],
974 pointer: 3
975 };
976 result.values[1] = theVal.substr(0,2);
977 result.values[2] = theVal.substr(2,2);
978 result.values[3] = theVal.substr(4,10);
979 return result;
980 };
981
982 /**
983 *
984 * @param theStr1
985 * @param delim
986 * @param index
987 * @returns {*}
988 */
989 FormEngineValidation.splitStr = function(theStr1, delim, index) {
990 var theStr = '' + theStr1;
991 var lengthOfDelim = delim.length;
992 var sPos = -lengthOfDelim;
993 if (index < 1) {
994 index = 1;
995 }
996 for (var a = 1; a < index; a++) {
997 sPos = theStr.indexOf(delim, sPos + lengthOfDelim);
998 if (sPos == -1) {
999 return null;
1000 }
1001 }
1002 var ePos = theStr.indexOf(delim, sPos + lengthOfDelim);
1003 if (ePos == -1) {
1004 ePos = theStr.length;
1005 }
1006 return (theStr.substring(sPos + lengthOfDelim, ePos));
1007 };
1008
1009 /**
1010 *
1011 * @param value
1012 * @returns {{values: Array, valPol: Array, pointer: number, numberMode: number, theVal: string}}
1013 */
1014 FormEngineValidation.split = function(value) {
1015 var result = {
1016 values: [],
1017 valPol: [],
1018 pointer: 0,
1019 numberMode: 0,
1020 theVal: ''
1021 };
1022 value += ' ';
1023 for (var a=0; a < value.length; a++) {
1024 var theChar = value.substr(a, 1);
1025 if (theChar < '0' || theChar > '9') {
1026 if (result.numberMode) {
1027 result.pointer++;
1028 result.values[result.pointer] = result.theVal;
1029 result.theVal = '';
1030 result.numberMode = 0;
1031 }
1032 if (theChar == '+' || theChar == '-') {
1033 result.valPol[result.pointer + 1] = theChar;
1034 }
1035 } else {
1036 result.theVal += theChar;
1037 result.numberMode = 1;
1038 }
1039 }
1040 return result;
1041 };
1042
1043 FormEngineValidation.registerReady = function() {
1044 $(function() {
1045 FormEngineValidation.initialize();
1046 // Start first validation after one second, because all fields are initial empty (typo3form.fieldSet)
1047 window.setTimeout(function() {
1048 FormEngineValidation.validate();
1049 }, 1000);
1050 });
1051 };
1052
1053 FormEngine.Validation = FormEngineValidation;
1054
1055 return FormEngine.Validation;
1056 });