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