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