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