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