Subversion Repositories wpShopGermany4

Rev

Rev 4943 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1306 daniel 1
/*
1913 daniel 2
 * Inline Form Validation Engine 2.6.2, jQuery plugin
1306 daniel 3
 *
4
 * Copyright(c) 2010, Cedric Dugas
5
 * http://www.position-absolute.com
6
 *
7
 * 2.0 Rewrite by Olivier Refalo
8
 * http://www.crionics.com
9
 *
10
 * Form validation engine allowing custom regex rules to be added.
11
 * Licensed under the MIT License
12
 */
13
 (function($) {
14
 
2955 daniel 15
	"use strict";
1306 daniel 16
 
2955 daniel 17
	var methods = {
1306 daniel 18
 
2955 daniel 19
		/**
20
		* Kind of the constructor, called before any action
21
		* @param {Map} user options
22
		*/
23
		init: function(options) {
24
			var form = this;
25
			if (!form.data('jqv') || form.data('jqv') == null ) {
26
				options = methods._saveOptions(form, options);
27
				// bind all formError elements to close on click
28
				$(document).on("click", ".formError", function() {
29
					$(this).fadeOut(150, function() {
30
						// remove prompt once invisible
31
						$(this).parent('.formErrorOuter').remove();
32
						$(this).remove();
33
					});
34
				});
35
			}
36
			return this;
1306 daniel 37
		 },
38
		/**
39
		* Attachs jQuery.validationEngine to form.submit and field.blur events
40
		* Takes an optional params: a list of options
41
		* ie. jQuery("#formID1").validationEngine('attach', {promptPosition : "centerRight"});
42
		*/
43
		attach: function(userOptions) {
44
 
45
			var form = this;
46
			var options;
47
 
48
			if(userOptions)
49
				options = methods._saveOptions(form, userOptions);
50
			else
51
				options = form.data('jqv');
52
 
53
			options.validateAttribute = (form.find("[data-validation-engine*=validate]").length) ? "data-validation-engine" : "class";
54
			if (options.binded) {
55
 
1913 daniel 56
				// delegate fields
57
				form.on(options.validationEventTrigger, "["+options.validateAttribute+"*=validate]:not([type=checkbox]):not([type=radio]):not(.datepicker)", methods._onFieldEvent);
58
				form.on("click", "["+options.validateAttribute+"*=validate][type=checkbox],["+options.validateAttribute+"*=validate][type=radio]", methods._onFieldEvent);
59
				form.on(options.validationEventTrigger,"["+options.validateAttribute+"*=validate][class*=datepicker]", {"delay": 300}, methods._onFieldEvent);
1306 daniel 60
			}
61
			if (options.autoPositionUpdate) {
62
				$(window).bind("resize", {
63
					"noAnimation": true,
64
					"formElem": form
65
				}, methods.updatePromptsPosition);
66
			}
1913 daniel 67
			form.on("click","a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
68
			form.removeData('jqv_submitButton');
69
 
1306 daniel 70
			// bind form.submit
1913 daniel 71
			form.on("submit", methods._onSubmitEvent);
1306 daniel 72
			return this;
73
		},
74
		/**
75
		* Unregisters any bindings that may point to jQuery.validaitonEngine
76
		*/
77
		detach: function() {
78
 
79
			var form = this;
80
			var options = form.data('jqv');
81
 
82
			// unbind fields
1913 daniel 83
			form.find("["+options.validateAttribute+"*=validate]").not("[type=checkbox]").off(options.validationEventTrigger, methods._onFieldEvent);
84
			form.find("["+options.validateAttribute+"*=validate][type=checkbox],[class*=validate][type=radio]").off("click", methods._onFieldEvent);
1306 daniel 85
 
86
			// unbind form.submit
2955 daniel 87
			form.off("submit", methods._onSubmitEvent);
1306 daniel 88
			form.removeData('jqv');
1913 daniel 89
 
90
			form.off("click", "a[data-validation-engine-skip], a[class*='validate-skip'], button[data-validation-engine-skip], button[class*='validate-skip'], input[data-validation-engine-skip], input[class*='validate-skip']", methods._submitButtonClick);
91
			form.removeData('jqv_submitButton');
1306 daniel 92
 
93
			if (options.autoPositionUpdate)
2955 daniel 94
				$(window).off("resize", methods.updatePromptsPosition);
1306 daniel 95
 
96
			return this;
97
		},
98
		/**
99
		* Validates either a form or a list of fields, shows prompts accordingly.
100
		* Note: There is no ajax form validation with this method, only field ajax validation are evaluated
101
		*
102
		* @return true if the form validates, false if it fails
103
		*/
104
		validate: function() {
1913 daniel 105
			var element = $(this);
106
			var valid = null;
1306 daniel 107
 
2955 daniel 108
			if (element.is("form") || element.hasClass("validationEngineContainer")) {
109
				if (element.hasClass('validating')) {
110
					// form is already validating.
111
					// Should abort old validation and start new one. I don't know how to implement it.
112
					return false;
113
				} else {
114
					element.addClass('validating');
115
					var options = element.data('jqv');
116
					var valid = methods._validateFields(this);
117
 
118
					// If the form doesn't validate, clear the 'validating' class before the user has a chance to submit again
119
					setTimeout(function(){
120
						element.removeClass('validating');
121
					}, 100);
122
					if (valid && options.onSuccess) {
123
						options.onSuccess();
124
					} else if (!valid && options.onFailure) {
125
						options.onFailure();
126
					}
1913 daniel 127
				}
128
			} else if (element.is('form') || element.hasClass('validationEngineContainer')) {
129
				element.removeClass('validating');
130
			} else {
131
				// field validation
132
				var form = element.closest('form, .validationEngineContainer'),
133
					options = (form.data('jqv')) ? form.data('jqv') : $.validationEngine.defaults,
134
					valid = methods._validateField(element, options);
1306 daniel 135
 
1913 daniel 136
				if (valid && options.onFieldSuccess)
137
					options.onFieldSuccess();
138
				else if (options.onFieldFailure && options.InvalidFields.length > 0) {
139
					options.onFieldFailure();
140
				}
1306 daniel 141
			}
1913 daniel 142
			if(options.onValidationComplete) {
143
				// !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
144
				return !!options.onValidationComplete(form, valid);
145
			}
146
			return valid;
1306 daniel 147
		},
148
		/**
149
		*  Redraw prompts position, useful when you change the DOM state when validating
150
		*/
151
		updatePromptsPosition: function(event) {
152
 
153
			if (event && this == window) {
154
				var form = event.data.formElem;
155
				var noAnimation = event.data.noAnimation;
156
			}
157
			else
1913 daniel 158
				var form = $(this.closest('form, .validationEngineContainer'));
1306 daniel 159
 
160
			var options = form.data('jqv');
161
			// No option, take default one
1913 daniel 162
			form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each(function(){
1306 daniel 163
				var field = $(this);
1913 daniel 164
				if (options.prettySelect && field.is(":hidden"))
165
				  field = form.find("#" + options.usePrefix + field.attr('id') + options.useSuffix);
1306 daniel 166
				var prompt = methods._getPrompt(field);
167
				var promptText = $(prompt).find(".formErrorContent").html();
168
 
169
				if(prompt)
170
					methods._updatePrompt(field, $(prompt), promptText, undefined, false, options, noAnimation);
171
			});
172
			return this;
173
		},
174
		/**
175
		* Displays a prompt on a element.
176
		* Note that the element needs an id!
177
		*
178
		* @param {String} promptText html text to display type
179
		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
180
		* @param {String} possible values topLeft, topRight, bottomLeft, centerRight, bottomRight
181
		*/
182
		showPrompt: function(promptText, type, promptPosition, showArrow) {
1913 daniel 183
			var form = this.closest('form, .validationEngineContainer');
1306 daniel 184
			var options = form.data('jqv');
185
			// No option, take default one
186
			if(!options)
187
				options = methods._saveOptions(this, options);
188
			if(promptPosition)
189
				options.promptPosition=promptPosition;
190
			options.showArrow = showArrow==true;
191
 
192
			methods._showPrompt(this, promptText, type, false, options);
193
			return this;
194
		},
195
		/**
196
		* Closes form error prompts, CAN be invidual
197
		*/
198
		hide: function() {
1913 daniel 199
			 var form = $(this).closest('form, .validationEngineContainer');
1306 daniel 200
			 var options = form.data('jqv');
1913 daniel 201
			 var fadeDuration = (options && options.fadeDuration) ? options.fadeDuration : 0.3;
1306 daniel 202
			 var closingtag;
1913 daniel 203
 
204
			 if($(this).is("form") || $(this).hasClass("validationEngineContainer")) {
1306 daniel 205
				 closingtag = "parentForm"+methods._getClassName($(this).attr("id"));
206
			 } else {
207
				 closingtag = methods._getClassName($(this).attr("id")) +"formError";
208
			 }
1913 daniel 209
			 $('.'+closingtag).fadeTo(fadeDuration, 0.3, function() {
1306 daniel 210
				 $(this).parent('.formErrorOuter').remove();
211
				 $(this).remove();
212
			 });
213
			 return this;
214
		 },
215
		 /**
216
		 * Closes all error prompts on the page
217
		 */
218
		 hideAll: function() {
219
 
220
			 var form = this;
221
			 var options = form.data('jqv');
1913 daniel 222
			 var duration = options ? options.fadeDuration:300;
223
			 $('.formError').fadeTo(duration, 300, function() {
1306 daniel 224
				 $(this).parent('.formErrorOuter').remove();
225
				 $(this).remove();
226
			 });
227
			 return this;
228
		 },
229
		/**
230
		* Typically called when user exists a field using tab or a mouse click, triggers a field
231
		* validation
232
		*/
233
		_onFieldEvent: function(event) {
234
			var field = $(this);
1913 daniel 235
			var form = field.closest('form, .validationEngineContainer');
1306 daniel 236
			var options = form.data('jqv');
1913 daniel 237
			options.eventTrigger = "field";
1306 daniel 238
			// validate the current field
239
			window.setTimeout(function() {
240
				methods._validateField(field, options);
1913 daniel 241
				if (options.InvalidFields.length == 0 && options.onFieldSuccess) {
242
					options.onFieldSuccess();
243
				} else if (options.InvalidFields.length > 0 && options.onFieldFailure) {
244
					options.onFieldFailure();
1306 daniel 245
				}
246
			}, (event.data) ? event.data.delay : 0);
247
 
248
		},
249
		/**
250
		* Called when the form is submited, shows prompts accordingly
251
		*
252
		* @param {jqObject}
253
		*            form
254
		* @return false if form submission needs to be cancelled
255
		*/
256
		_onSubmitEvent: function() {
257
			var form = $(this);
258
			var options = form.data('jqv');
1913 daniel 259
 
260
			//check if it is trigger from skipped button
261
			if (form.data("jqv_submitButton")){
262
				var submitButton = $("#" + form.data("jqv_submitButton"));
263
				if (submitButton){
264
					if (submitButton.length > 0){
265
						if (submitButton.hasClass("validate-skip") || submitButton.attr("data-validation-engine-skip") == "true")
266
							return true;
267
					}
268
				}
269
			}
1306 daniel 270
 
1913 daniel 271
			options.eventTrigger = "submit";
272
 
1306 daniel 273
			// validate each field
274
			// (- skip field ajax validation, not necessary IF we will perform an ajax form validation)
1913 daniel 275
			var r=methods._validateFields(form);
1306 daniel 276
 
277
			if (r && options.ajaxFormValidation) {
278
				methods._validateFormWithAjax(form, options);
279
				// cancel form auto-submission - process with async call onAjaxFormComplete
280
				return false;
281
			}
282
 
283
			if(options.onValidationComplete) {
1913 daniel 284
				// !! ensures that an undefined return is interpreted as return false but allows a onValidationComplete() to possibly return true and have form continue processing
285
				return !!options.onValidationComplete(form, r);
1306 daniel 286
			}
287
			return r;
288
		},
289
		/**
290
		* Return true if the ajax field validations passed so far
291
		* @param {Object} options
292
		* @return true, is all ajax validation passed so far (remember ajax is async)
293
		*/
294
		_checkAjaxStatus: function(options) {
295
			var status = true;
296
			$.each(options.ajaxValidCache, function(key, value) {
297
				if (!value) {
298
					status = false;
299
					// break the each
300
					return false;
301
				}
302
			});
303
			return status;
304
		},
1913 daniel 305
 
1306 daniel 306
		/**
1913 daniel 307
		* Return true if the ajax field is validated
308
		* @param {String} fieldid
309
		* @param {Object} options
310
		* @return true, if validation passed, false if false or doesn't exist
311
		*/
312
		_checkAjaxFieldStatus: function(fieldid, options) {
313
			return options.ajaxValidCache[fieldid] == true;
314
		},
315
		/**
1306 daniel 316
		* Validates form fields, shows prompts accordingly
317
		*
318
		* @param {jqObject}
319
		*            form
320
		* @param {skipAjaxFieldValidation}
321
		*            boolean - when set to true, ajax field validation is skipped, typically used when the submit button is clicked
322
		*
323
		* @return true if form is valid, false if not, undefined if ajax form validation is done
324
		*/
1913 daniel 325
		_validateFields: function(form) {
1306 daniel 326
			var options = form.data('jqv');
327
 
328
			// this variable is set to true if an error is found
329
			var errorFound = false;
330
 
331
			// Trigger hook, start validation
332
			form.trigger("jqv.form.validating");
333
			// first, evaluate status of non ajax fields
334
			var first_err=null;
1913 daniel 335
			form.find('['+options.validateAttribute+'*=validate]').not(":disabled").each( function() {
1306 daniel 336
				var field = $(this);
337
				var names = [];
338
				if ($.inArray(field.attr('name'), names) < 0) {
1913 daniel 339
					errorFound |= methods._validateField(field, options);
1306 daniel 340
					if (errorFound && first_err==null)
1913 daniel 341
						if (field.is(":hidden") && options.prettySelect)
2955 daniel 342
							first_err = field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
343
						else {
344
 
345
							//Check if we need to adjust what element to show the prompt on
346
							//and and such scroll to instead
347
							if(field.data('jqv-prompt-at') instanceof jQuery ){
348
								field = field.data('jqv-prompt-at');
349
							} else if(field.data('jqv-prompt-at')) {
350
								field = $(field.data('jqv-prompt-at'));
351
							}
352
							first_err=field;
353
						}
1306 daniel 354
					if (options.doNotShowAllErrosOnSubmit)
355
						return false;
356
					names.push(field.attr('name'));
1913 daniel 357
 
358
					//if option set, stop checking validation rules after one error is found
359
					if(options.showOneMessage == true && errorFound){
360
						return false;
361
					}
1306 daniel 362
				}
363
			});
364
 
365
			// second, check to see if all ajax calls completed ok
366
			// errorFound |= !methods._checkAjaxStatus(options);
367
 
368
			// third, check status and scroll the container accordingly
369
			form.trigger("jqv.form.result", [errorFound]);
370
 
371
			if (errorFound) {
372
				if (options.scroll) {
373
					var destination=first_err.offset().top;
374
					var fixleft = first_err.offset().left;
375
 
376
					//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
377
					var positionType=options.promptPosition;
378
					if (typeof(positionType)=='string' && positionType.indexOf(":")!=-1)
379
						positionType=positionType.substring(0,positionType.indexOf(":"));
380
 
381
					if (positionType!="bottomRight" && positionType!="bottomLeft") {
382
						var prompt_err= methods._getPrompt(first_err);
1913 daniel 383
						if (prompt_err) {
384
							destination=prompt_err.offset().top;
385
						}
1306 daniel 386
					}
1913 daniel 387
 
388
					// Offset the amount the page scrolls by an amount in px to accomodate fixed elements at top of page
389
					if (options.scrollOffset) {
390
						destination -= options.scrollOffset;
391
					}
1306 daniel 392
 
393
					// get the position of the first error, there should be at least one, no need to check this
394
					//var destination = form.find(".formError:not('.greenPopup'):first").offset().top;
395
					if (options.isOverflown) {
396
						var overflowDIV = $(options.overflownDIV);
397
						if(!overflowDIV.length) return false;
398
						var scrollContainerScroll = overflowDIV.scrollTop();
399
						var scrollContainerPos = -parseInt(overflowDIV.offset().top);
400
 
401
						destination += scrollContainerScroll + scrollContainerPos - 5;
402
						var scrollContainer = $(options.overflownDIV + ":not(:animated)");
403
 
404
						scrollContainer.animate({ scrollTop: destination }, 1100, function(){
405
							if(options.focusFirstField) first_err.focus();
406
						});
1913 daniel 407
 
1306 daniel 408
					} else {
1913 daniel 409
						$("html, body").animate({
410
							scrollTop: destination
1306 daniel 411
						}, 1100, function(){
412
							if(options.focusFirstField) first_err.focus();
413
						});
1913 daniel 414
						$("html, body").animate({scrollLeft: fixleft},1100)
1306 daniel 415
					}
416
 
417
				} else if(options.focusFirstField)
418
					first_err.focus();
419
				return false;
420
			}
421
			return true;
422
		},
423
		/**
424
		* This method is called to perform an ajax form validation.
425
		* During this process all the (field, value) pairs are sent to the server which returns a list of invalid fields or true
426
		*
427
		* @param {jqObject} form
428
		* @param {Map} options
429
		*/
430
		_validateFormWithAjax: function(form, options) {
431
 
432
			var data = form.serialize();
1913 daniel 433
									var type = (options.ajaxFormValidationMethod) ? options.ajaxFormValidationMethod : "GET";
1306 daniel 434
			var url = (options.ajaxFormValidationURL) ? options.ajaxFormValidationURL : form.attr("action");
1913 daniel 435
									var dataType = (options.dataType) ? options.dataType : "json";
1306 daniel 436
			$.ajax({
1913 daniel 437
				type: type,
1306 daniel 438
				url: url,
439
				cache: false,
1913 daniel 440
				dataType: dataType,
1306 daniel 441
				data: data,
442
				form: form,
443
				methods: methods,
444
				options: options,
445
				beforeSend: function() {
446
					return options.onBeforeAjaxFormValidation(form, options);
447
				},
448
				error: function(data, transport) {
2955 daniel 449
					if (options.onFailure) {
450
						options.onFailure(data, transport);
451
					} else {
452
						methods._ajaxError(data, transport);
453
					}
1306 daniel 454
				},
455
				success: function(json) {
1913 daniel 456
					if ((dataType == "json") && (json !== true)) {
1306 daniel 457
						// getting to this case doesn't necessary means that the form is invalid
458
						// the server may return green or closing prompt actions
459
						// this flag helps figuring it out
460
						var errorInForm=false;
461
						for (var i = 0; i < json.length; i++) {
462
							var value = json[i];
463
 
464
							var errorFieldId = value[0];
465
							var errorField = $($("#" + errorFieldId)[0]);
466
 
467
							// make sure we found the element
468
							if (errorField.length == 1) {
469
 
470
								// promptText or selector
471
								var msg = value[2];
472
								// if the field is valid
473
								if (value[1] == true) {
474
 
475
									if (msg == ""  || !msg){
476
										// if for some reason, status==true and error="", just close the prompt
477
										methods._closePrompt(errorField);
478
									} else {
479
										// the field is valid, but we are displaying a green prompt
480
										if (options.allrules[msg]) {
481
											var txt = options.allrules[msg].alertTextOk;
482
											if (txt)
483
												msg = txt;
484
										}
1913 daniel 485
										if (options.showPrompts) methods._showPrompt(errorField, msg, "pass", false, options, true);
1306 daniel 486
									}
487
								} else {
488
									// the field is invalid, show the red error prompt
489
									errorInForm|=true;
490
									if (options.allrules[msg]) {
491
										var txt = options.allrules[msg].alertText;
492
										if (txt)
493
											msg = txt;
494
									}
1913 daniel 495
									if(options.showPrompts) methods._showPrompt(errorField, msg, "", false, options, true);
1306 daniel 496
								}
497
							}
498
						}
499
						options.onAjaxFormComplete(!errorInForm, form, json, options);
500
					} else
1913 daniel 501
						options.onAjaxFormComplete(true, form, json, options);
502
 
1306 daniel 503
				}
504
			});
505
 
506
		},
507
		/**
508
		* Validates field, shows prompts accordingly
509
		*
510
		* @param {jqObject}
511
		*            field
512
		* @param {Array[String]}
513
		*            field's validation rules
514
		* @param {Map}
515
		*            user options
1913 daniel 516
		* @return false if field is valid (It is inversed for *fields*, it return false on validate and true on errors.)
1306 daniel 517
		*/
518
		_validateField: function(field, options, skipAjaxValidation) {
519
			if (!field.attr("id")) {
520
				field.attr("id", "form-validation-field-" + $.validationEngine.fieldIdCounter);
521
				++$.validationEngine.fieldIdCounter;
522
			}
523
 
1913 daniel 524
           if (!options.validateNonVisibleFields && (field.is(":hidden") && !options.prettySelect || field.parent().is(":hidden")))
525
				return false;
526
 
1306 daniel 527
			var rulesParsing = field.attr(options.validateAttribute);
528
			var getRules = /validate\[(.*)\]/.exec(rulesParsing);
529
 
530
			if (!getRules)
531
				return false;
532
			var str = getRules[1];
533
			var rules = str.split(/\[|,|\]/);
534
 
535
			// true if we ran the ajax validation, tells the logic to stop messing with prompts
536
			var isAjaxValidator = false;
537
			var fieldName = field.attr("name");
538
			var promptText = "";
1913 daniel 539
			var promptType = "";
1306 daniel 540
			var required = false;
1913 daniel 541
			var limitErrors = false;
1306 daniel 542
			options.isError = false;
543
			options.showArrow = true;
1913 daniel 544
 
545
			// If the programmer wants to limit the amount of error messages per field,
546
			if (options.maxErrorsPerField > 0) {
547
				limitErrors = true;
548
			}
1306 daniel 549
 
1913 daniel 550
			var form = $(field.closest("form, .validationEngineContainer"));
551
			// Fix for adding spaces in the rules
552
			for (var i = 0; i < rules.length; i++) {
553
				rules[i] = rules[i].replace(" ", "");
554
				// Remove any parsing errors
555
				if (rules[i] === '') {
556
					delete rules[i];
557
				}
558
			}
1306 daniel 559
 
1913 daniel 560
			for (var i = 0, field_errors = 0; i < rules.length; i++) {
561
 
562
				// If we are limiting errors, and have hit the max, break
563
				if (limitErrors && field_errors >= options.maxErrorsPerField) {
564
					// If we haven't hit a required yet, check to see if there is one in the validation rules for this
565
					// field and that it's index is greater or equal to our current index
566
					if (!required) {
567
						var have_required = $.inArray('required', rules);
568
						required = (have_required != -1 &&  have_required >= i);
569
					}
570
					break;
571
				}
572
 
573
 
1306 daniel 574
				var errorMsg = undefined;
575
				switch (rules[i]) {
576
 
577
					case "required":
578
						required = true;
1913 daniel 579
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._required);
1306 daniel 580
						break;
581
					case "custom":
1913 daniel 582
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._custom);
1306 daniel 583
						break;
584
					case "groupRequired":
585
						// Check is its the first of group, if not, reload validation with first field
586
						// AND continue normal validation on present field
587
						var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
588
						var firstOfGroup = form.find(classGroup).eq(0);
589
						if(firstOfGroup[0] != field[0]){
1913 daniel 590
 
591
							methods._validateField(firstOfGroup, options, skipAjaxValidation);
1306 daniel 592
							options.showArrow = true;
1913 daniel 593
 
594
						}
595
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._groupRequired);
596
						if(errorMsg)  required = true;
1306 daniel 597
						options.showArrow = false;
598
						break;
599
					case "ajax":
1913 daniel 600
						// AJAX defaults to returning it's loading message
601
						errorMsg = methods._ajax(field, rules, i, options);
602
						if (errorMsg) {
603
							promptType = "load";
1306 daniel 604
						}
605
						break;
606
					case "minSize":
1913 daniel 607
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minSize);
1306 daniel 608
						break;
609
					case "maxSize":
1913 daniel 610
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxSize);
1306 daniel 611
						break;
612
					case "min":
1913 daniel 613
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._min);
1306 daniel 614
						break;
615
					case "max":
1913 daniel 616
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._max);
1306 daniel 617
						break;
618
					case "past":
1913 daniel 619
						errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._past);
1306 daniel 620
						break;
621
					case "future":
1913 daniel 622
						errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._future);
1306 daniel 623
						break;
624
					case "dateRange":
625
						var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
1913 daniel 626
						options.firstOfGroup = form.find(classGroup).eq(0);
627
						options.secondOfGroup = form.find(classGroup).eq(1);
1306 daniel 628
 
629
						//if one entry out of the pair has value then proceed to run through validation
1913 daniel 630
						if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
631
							errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateRange);
1306 daniel 632
						}
633
						if (errorMsg) required = true;
634
						options.showArrow = false;
635
						break;
636
 
637
					case "dateTimeRange":
638
						var classGroup = "["+options.validateAttribute+"*=" + rules[i + 1] + "]";
1913 daniel 639
						options.firstOfGroup = form.find(classGroup).eq(0);
640
						options.secondOfGroup = form.find(classGroup).eq(1);
1306 daniel 641
 
642
						//if one entry out of the pair has value then proceed to run through validation
1913 daniel 643
						if (options.firstOfGroup[0].value || options.secondOfGroup[0].value) {
644
							errorMsg = methods._getErrorMessage(form, field,rules[i], rules, i, options, methods._dateTimeRange);
1306 daniel 645
						}
646
						if (errorMsg) required = true;
647
						options.showArrow = false;
648
						break;
649
					case "maxCheckbox":
650
						field = $(form.find("input[name='" + fieldName + "']"));
1913 daniel 651
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._maxCheckbox);
1306 daniel 652
						break;
653
					case "minCheckbox":
654
						field = $(form.find("input[name='" + fieldName + "']"));
1913 daniel 655
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._minCheckbox);
1306 daniel 656
						break;
657
					case "equals":
1913 daniel 658
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._equals);
1306 daniel 659
						break;
660
					case "funcCall":
1913 daniel 661
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._funcCall);
1306 daniel 662
						break;
663
					case "creditCard":
1913 daniel 664
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._creditCard);
1306 daniel 665
						break;
1913 daniel 666
					case "condRequired":
667
						errorMsg = methods._getErrorMessage(form, field, rules[i], rules, i, options, methods._condRequired);
668
						if (errorMsg !== undefined) {
669
							required = true;
670
						}
671
						break;
1306 daniel 672
 
673
					default:
674
				}
1913 daniel 675
 
676
				var end_validation = false;
677
 
678
				// If we were passed back an message object, check what the status was to determine what to do
679
				if (typeof errorMsg == "object") {
680
					switch (errorMsg.status) {
681
						case "_break":
682
							end_validation = true;
683
							break;
684
						// If we have an error message, set errorMsg to the error message
685
						case "_error":
686
							errorMsg = errorMsg.message;
687
							break;
688
						// If we want to throw an error, but not show a prompt, return early with true
689
						case "_error_no_prompt":
690
							return true;
691
							break;
692
						// Anything else we continue on
693
						default:
694
							break;
695
					}
696
				}
697
 
698
				// If it has been specified that validation should end now, break
699
				if (end_validation) {
700
					break;
701
				}
702
 
703
				// If we have a string, that means that we have an error, so add it to the error message.
704
				if (typeof errorMsg == 'string') {
1306 daniel 705
					promptText += errorMsg + "<br/>";
706
					options.isError = true;
1913 daniel 707
					field_errors++;
708
				}
1306 daniel 709
			}
710
			// If the rules required is not added, an empty field is not validated
2955 daniel 711
			//the 3rd condition is added so that even empty password fields should be equal
712
			//otherwise if one is filled and another left empty, the "equal" condition would fail
713
			//which does not make any sense
714
			if(!required && !(field.val()) && field.val().length < 1 && rules.indexOf("equals") < 0) options.isError = false;
1306 daniel 715
 
716
			// Hack for radio/checkbox group button, the validation go into the
717
			// first radio/checkbox of the group
718
			var fieldType = field.prop("type");
1913 daniel 719
			var positionType=field.data("promptPosition") || options.promptPosition;
1306 daniel 720
 
721
			if ((fieldType == "radio" || fieldType == "checkbox") && form.find("input[name='" + fieldName + "']").size() > 1) {
1913 daniel 722
				if(positionType === 'inline') {
723
					field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:last"));
724
				} else {
1306 daniel 725
				field = $(form.find("input[name='" + fieldName + "'][type!=hidden]:first"));
1913 daniel 726
				}
1306 daniel 727
				options.showArrow = false;
728
			}
729
 
1913 daniel 730
			if(field.is(":hidden") && options.prettySelect) {
731
				field = form.find("#" + options.usePrefix + methods._jqSelector(field.attr('id')) + options.useSuffix);
732
			}
733
 
734
			if (options.isError && options.showPrompts){
735
				methods._showPrompt(field, promptText, promptType, false, options);
1306 daniel 736
			}else{
737
				if (!isAjaxValidator) methods._closePrompt(field);
738
			}
739
 
740
			if (!isAjaxValidator) {
741
				field.trigger("jqv.field.result", [field, options.isError, promptText]);
742
			}
743
 
744
			/* Record error */
745
			var errindex = $.inArray(field[0], options.InvalidFields);
746
			if (errindex == -1) {
747
				if (options.isError)
748
				options.InvalidFields.push(field[0]);
749
			} else if (!options.isError) {
750
				options.InvalidFields.splice(errindex, 1);
751
			}
1913 daniel 752
 
753
			methods._handleStatusCssClasses(field, options);
754
 
755
			/* run callback function for each field */
756
			if (options.isError && options.onFieldFailure)
757
				options.onFieldFailure(field);
1306 daniel 758
 
1913 daniel 759
			if (!options.isError && options.onFieldSuccess)
760
				options.onFieldSuccess(field);
761
 
1306 daniel 762
			return options.isError;
763
		},
764
		/**
1913 daniel 765
		* Handling css classes of fields indicating result of validation
766
		*
767
		* @param {jqObject}
768
		*            field
769
		* @param {Array[String]}
770
		*            field's validation rules
771
		* @private
772
		*/
773
		_handleStatusCssClasses: function(field, options) {
774
			/* remove all classes */
775
			if(options.addSuccessCssClassToField)
776
				field.removeClass(options.addSuccessCssClassToField);
777
 
778
			if(options.addFailureCssClassToField)
779
				field.removeClass(options.addFailureCssClassToField);
780
 
781
			/* Add classes */
782
			if (options.addSuccessCssClassToField && !options.isError)
783
				field.addClass(options.addSuccessCssClassToField);
784
 
785
			if (options.addFailureCssClassToField && options.isError)
786
				field.addClass(options.addFailureCssClassToField);
787
		},
788
 
789
		 /********************
790
		  * _getErrorMessage
791
		  *
792
		  * @param form
793
		  * @param field
794
		  * @param rule
795
		  * @param rules
796
		  * @param i
797
		  * @param options
798
		  * @param originalValidationMethod
799
		  * @return {*}
800
		  * @private
801
		  */
802
		 _getErrorMessage:function (form, field, rule, rules, i, options, originalValidationMethod) {
803
			 // If we are using the custon validation type, build the index for the rule.
804
			 // Otherwise if we are doing a function call, make the call and return the object
805
			 // that is passed back.
806
	 		 var rule_index = jQuery.inArray(rule, rules);
807
			 if (rule === "custom" || rule === "funcCall") {
808
				 var custom_validation_type = rules[rule_index + 1];
809
				 rule = rule + "[" + custom_validation_type + "]";
810
				 // Delete the rule from the rules array so that it doesn't try to call the
811
			    // same rule over again
812
			    delete(rules[rule_index]);
813
			 }
814
			 // Change the rule to the composite rule, if it was different from the original
815
			 var alteredRule = rule;
816
 
817
 
818
			 var element_classes = (field.attr("data-validation-engine")) ? field.attr("data-validation-engine") : field.attr("class");
819
			 var element_classes_array = element_classes.split(" ");
820
 
821
			 // Call the original validation method. If we are dealing with dates or checkboxes, also pass the form
822
			 var errorMsg;
823
			 if (rule == "future" || rule == "past"  || rule == "maxCheckbox" || rule == "minCheckbox") {
824
				 errorMsg = originalValidationMethod(form, field, rules, i, options);
825
			 } else {
826
				 errorMsg = originalValidationMethod(field, rules, i, options);
827
			 }
828
 
829
			 // If the original validation method returned an error and we have a custom error message,
830
			 // return the custom message instead. Otherwise return the original error message.
831
			 if (errorMsg != undefined) {
832
				 var custom_message = methods._getCustomErrorMessage($(field), element_classes_array, alteredRule, options);
833
				 if (custom_message) errorMsg = custom_message;
834
			 }
835
			 return errorMsg;
836
 
837
		 },
838
		 _getCustomErrorMessage:function (field, classes, rule, options) {
839
			var custom_message = false;
2955 daniel 840
			var validityProp = /^custom\[.*\]$/.test(rule) ? methods._validityProp["custom"] : methods._validityProp[rule];
1913 daniel 841
			 // If there is a validityProp for this rule, check to see if the field has an attribute for it
842
			if (validityProp != undefined) {
843
				custom_message = field.attr("data-errormessage-"+validityProp);
844
				// If there was an error message for it, return the message
845
				if (custom_message != undefined)
846
					return custom_message;
847
			}
848
			custom_message = field.attr("data-errormessage");
849
			 // If there is an inline custom error message, return it
850
			if (custom_message != undefined)
851
				return custom_message;
852
			var id = '#' + field.attr("id");
853
			// If we have custom messages for the element's id, get the message for the rule from the id.
854
			// Otherwise, if we have custom messages for the element's classes, use the first class message we find instead.
855
			if (typeof options.custom_error_messages[id] != "undefined" &&
856
				typeof options.custom_error_messages[id][rule] != "undefined" ) {
857
						  custom_message = options.custom_error_messages[id][rule]['message'];
858
			} else if (classes.length > 0) {
859
				for (var i = 0; i < classes.length && classes.length > 0; i++) {
860
					 var element_class = "." + classes[i];
861
					if (typeof options.custom_error_messages[element_class] != "undefined" &&
862
						typeof options.custom_error_messages[element_class][rule] != "undefined") {
863
							custom_message = options.custom_error_messages[element_class][rule]['message'];
864
							break;
865
					}
866
				}
867
			}
868
			if (!custom_message &&
869
				typeof options.custom_error_messages[rule] != "undefined" &&
870
				typeof options.custom_error_messages[rule]['message'] != "undefined"){
871
					 custom_message = options.custom_error_messages[rule]['message'];
872
			 }
873
			 return custom_message;
874
		 },
875
		 _validityProp: {
876
			 "required": "value-missing",
877
			 "custom": "custom-error",
878
			 "groupRequired": "value-missing",
879
			 "ajax": "custom-error",
880
			 "minSize": "range-underflow",
881
			 "maxSize": "range-overflow",
882
			 "min": "range-underflow",
883
			 "max": "range-overflow",
884
			 "past": "type-mismatch",
885
			 "future": "type-mismatch",
886
			 "dateRange": "type-mismatch",
887
			 "dateTimeRange": "type-mismatch",
888
			 "maxCheckbox": "range-overflow",
889
			 "minCheckbox": "range-underflow",
890
			 "equals": "pattern-mismatch",
891
			 "funcCall": "custom-error",
892
			 "creditCard": "pattern-mismatch",
893
			 "condRequired": "value-missing"
894
		 },
895
		/**
1306 daniel 896
		* Required validation
897
		*
898
		* @param {jqObject} field
899
		* @param {Array[String]} rules
900
		* @param {int} i rules index
901
		* @param {Map}
902
		*            user options
1913 daniel 903
		* @param {bool} condRequired flag when method is used for internal purpose in condRequired check
1306 daniel 904
		* @return an error string if validation failed
905
		*/
1913 daniel 906
		_required: function(field, rules, i, options, condRequired) {
1306 daniel 907
			switch (field.prop("type")) {
908
				case "text":
909
				case "password":
910
				case "textarea":
911
				case "file":
1913 daniel 912
				case "select-one":
913
				case "select-multiple":
1306 daniel 914
				default:
1913 daniel 915
					var field_val      = $.trim( field.val()                               );
916
					var dv_placeholder = $.trim( field.attr("data-validation-placeholder") );
917
					var placeholder    = $.trim( field.attr("placeholder")                 );
918
					if (
919
						   ( !field_val                                    )
920
						|| ( dv_placeholder && field_val == dv_placeholder )
921
						|| ( placeholder    && field_val == placeholder    )
922
					) {
1306 daniel 923
						return options.allrules[rules[i]].alertText;
1913 daniel 924
					}
1306 daniel 925
					break;
926
				case "radio":
927
				case "checkbox":
1913 daniel 928
					// new validation style to only check dependent field
929
					if (condRequired) {
930
						if (!field.attr('checked')) {
931
							return options.allrules[rules[i]].alertTextCheckboxMultiple;
932
						}
933
						break;
934
					}
935
					// old validation style
936
					var form = field.closest("form, .validationEngineContainer");
1306 daniel 937
					var name = field.attr("name");
938
					if (form.find("input[name='" + name + "']:checked").size() == 0) {
1913 daniel 939
						if (form.find("input[name='" + name + "']:visible").size() == 1)
1306 daniel 940
							return options.allrules[rules[i]].alertTextCheckboxe;
941
						else
942
							return options.allrules[rules[i]].alertTextCheckboxMultiple;
943
					}
944
					break;
945
			}
946
		},
947
		/**
948
		* Validate that 1 from the group field is required
949
		*
950
		* @param {jqObject} field
951
		* @param {Array[String]} rules
952
		* @param {int} i rules index
953
		* @param {Map}
954
		*            user options
955
		* @return an error string if validation failed
956
		*/
957
		_groupRequired: function(field, rules, i, options) {
958
			var classGroup = "["+options.validateAttribute+"*=" +rules[i + 1] +"]";
959
			var isValid = false;
960
			// Check all fields from the group
1913 daniel 961
			field.closest("form, .validationEngineContainer").find(classGroup).each(function(){
1306 daniel 962
				if(!methods._required($(this), rules, i, options)){
963
					isValid = true;
964
					return false;
965
				}
1913 daniel 966
			});
1306 daniel 967
 
1913 daniel 968
			if(!isValid) {
969
		  return options.allrules[rules[i]].alertText;
970
		}
1306 daniel 971
		},
972
		/**
1913 daniel 973
		* Validate rules
1306 daniel 974
		*
975
		* @param {jqObject} field
976
		* @param {Array[String]} rules
977
		* @param {int} i rules index
978
		* @param {Map}
979
		*            user options
980
		* @return an error string if validation failed
981
		*/
1913 daniel 982
		_custom: function(field, rules, i, options) {
1306 daniel 983
			var customRule = rules[i + 1];
984
			var rule = options.allrules[customRule];
1913 daniel 985
			var fn;
1306 daniel 986
			if(!rule) {
1913 daniel 987
				alert("jqv:custom rule not found - "+customRule);
1306 daniel 988
				return;
989
			}
1913 daniel 990
 
991
			if(rule["regex"]) {
992
				 var ex=rule.regex;
993
					if(!ex) {
994
						alert("jqv:custom regex not found - "+customRule);
995
						return;
996
					}
997
					var pattern = new RegExp(ex);
1306 daniel 998
 
1913 daniel 999
					if (!pattern.test(field.val())) return options.allrules[customRule].alertText;
1000
 
1001
			} else if(rule["func"]) {
1002
				fn = rule["func"];
1003
 
1004
				if (typeof(fn) !== "function") {
1005
					alert("jqv:custom parameter 'function' is no function - "+customRule);
1006
						return;
1007
				}
1008
 
1009
				if (!fn(field, rules, i, options))
1010
					return options.allrules[customRule].alertText;
1011
			} else {
1012
				alert("jqv:custom type not allowed "+customRule);
1013
					return;
1306 daniel 1014
			}
1015
		},
1016
		/**
1017
		* Validate custom function outside of the engine scope
1018
		*
1019
		* @param {jqObject} field
1020
		* @param {Array[String]} rules
1021
		* @param {int} i rules index
1022
		* @param {Map}
1023
		*            user options
1024
		* @return an error string if validation failed
1025
		*/
1026
		_funcCall: function(field, rules, i, options) {
1027
			var functionName = rules[i + 1];
1028
			var fn;
1913 daniel 1029
			if(functionName.indexOf('.') >-1)
1306 daniel 1030
			{
1031
				var namespaces = functionName.split('.');
1032
				var scope = window;
1033
				while(namespaces.length)
1034
				{
1035
					scope = scope[namespaces.shift()];
1036
				}
1037
				fn = scope;
1038
			}
1039
			else
1040
				fn = window[functionName] || options.customFunctions[functionName];
1041
			if (typeof(fn) == 'function')
1042
				return fn(field, rules, i, options);
1043
 
1044
		},
1045
		/**
1046
		* Field match
1047
		*
1048
		* @param {jqObject} field
1049
		* @param {Array[String]} rules
1050
		* @param {int} i rules index
1051
		* @param {Map}
1052
		*            user options
1053
		* @return an error string if validation failed
1054
		*/
1055
		_equals: function(field, rules, i, options) {
1056
			var equalsField = rules[i + 1];
1057
 
1058
			if (field.val() != $("#" + equalsField).val())
1059
				return options.allrules.equals.alertText;
1060
		},
1061
		/**
1062
		* Check the maximum size (in characters)
1063
		*
1064
		* @param {jqObject} field
1065
		* @param {Array[String]} rules
1066
		* @param {int} i rules index
1067
		* @param {Map}
1068
		*            user options
1069
		* @return an error string if validation failed
1070
		*/
1071
		_maxSize: function(field, rules, i, options) {
1072
			var max = rules[i + 1];
1073
			var len = field.val().length;
1074
 
1075
			if (len > max) {
1076
				var rule = options.allrules.maxSize;
1077
				return rule.alertText + max + rule.alertText2;
1078
			}
1079
		},
1080
		/**
1081
		* Check the minimum size (in characters)
1082
		*
1083
		* @param {jqObject} field
1084
		* @param {Array[String]} rules
1085
		* @param {int} i rules index
1086
		* @param {Map}
1087
		*            user options
1088
		* @return an error string if validation failed
1089
		*/
1090
		_minSize: function(field, rules, i, options) {
1091
			var min = rules[i + 1];
1092
			var len = field.val().length;
1093
 
1094
			if (len < min) {
1095
				var rule = options.allrules.minSize;
1096
				return rule.alertText + min + rule.alertText2;
1097
			}
1098
		},
1099
		/**
1100
		* Check number minimum value
1101
		*
1102
		* @param {jqObject} field
1103
		* @param {Array[String]} rules
1104
		* @param {int} i rules index
1105
		* @param {Map}
1106
		*            user options
1107
		* @return an error string if validation failed
1108
		*/
1109
		_min: function(field, rules, i, options) {
1110
			var min = parseFloat(rules[i + 1]);
1111
			var len = parseFloat(field.val());
1112
 
1113
			if (len < min) {
1114
				var rule = options.allrules.min;
1115
				if (rule.alertText2) return rule.alertText + min + rule.alertText2;
1116
				return rule.alertText + min;
1117
			}
1118
		},
1119
		/**
1120
		* Check number maximum value
1121
		*
1122
		* @param {jqObject} field
1123
		* @param {Array[String]} rules
1124
		* @param {int} i rules index
1125
		* @param {Map}
1126
		*            user options
1127
		* @return an error string if validation failed
1128
		*/
1129
		_max: function(field, rules, i, options) {
1130
			var max = parseFloat(rules[i + 1]);
1131
			var len = parseFloat(field.val());
1132
 
1133
			if (len >max ) {
1134
				var rule = options.allrules.max;
1135
				if (rule.alertText2) return rule.alertText + max + rule.alertText2;
1136
				//orefalo: to review, also do the translations
1137
				return rule.alertText + max;
1138
			}
1139
		},
1140
		/**
1141
		* Checks date is in the past
1142
		*
1143
		* @param {jqObject} field
1144
		* @param {Array[String]} rules
1145
		* @param {int} i rules index
1146
		* @param {Map}
1147
		*            user options
1148
		* @return an error string if validation failed
1149
		*/
1913 daniel 1150
		_past: function(form, field, rules, i, options) {
1306 daniel 1151
 
1152
			var p=rules[i + 1];
2955 daniel 1153
			var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
1913 daniel 1154
			var pdate;
1155
 
1156
			if (p.toLowerCase() == "now") {
1157
				pdate = new Date();
1158
			} else if (undefined != fieldAlt.val()) {
1159
				if (fieldAlt.is(":disabled"))
1160
					return;
1161
				pdate = methods._parseDate(fieldAlt.val());
1162
			} else {
1163
				pdate = methods._parseDate(p);
1164
			}
1306 daniel 1165
			var vdate = methods._parseDate(field.val());
1166
 
1167
			if (vdate > pdate ) {
1168
				var rule = options.allrules.past;
1169
				if (rule.alertText2) return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
1170
				return rule.alertText + methods._dateToString(pdate);
1171
			}
1172
		},
1173
		/**
1174
		* Checks date is in the future
1175
		*
1176
		* @param {jqObject} field
1177
		* @param {Array[String]} rules
1178
		* @param {int} i rules index
1179
		* @param {Map}
1180
		*            user options
1181
		* @return an error string if validation failed
1182
		*/
1913 daniel 1183
		_future: function(form, field, rules, i, options) {
1306 daniel 1184
 
1185
			var p=rules[i + 1];
2955 daniel 1186
			var fieldAlt = $(form.find("*[name='" + p.replace(/^#+/, '') + "']"));
1913 daniel 1187
			var pdate;
1188
 
1189
			if (p.toLowerCase() == "now") {
1190
				pdate = new Date();
1191
			} else if (undefined != fieldAlt.val()) {
1192
				if (fieldAlt.is(":disabled"))
1193
					return;
1194
				pdate = methods._parseDate(fieldAlt.val());
1195
			} else {
1196
				pdate = methods._parseDate(p);
1197
			}
1306 daniel 1198
			var vdate = methods._parseDate(field.val());
1199
 
1200
			if (vdate < pdate ) {
1201
				var rule = options.allrules.future;
1202
				if (rule.alertText2)
1203
					return rule.alertText + methods._dateToString(pdate) + rule.alertText2;
1204
				return rule.alertText + methods._dateToString(pdate);
1205
			}
1206
		},
1207
		/**
1208
		* Checks if valid date
1209
		*
1210
		* @param {string} date string
1211
		* @return a bool based on determination of valid date
1212
		*/
1213
		_isDate: function (value) {
1214
			var dateRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(?:(?:0?[1-9]|1[0-2])(\/|-)(?:0?[1-9]|1\d|2[0-8]))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^(0?2(\/|-)29)(\/|-)(?:(?:0[48]00|[13579][26]00|[2468][048]00)|(?:\d\d)?(?:0[48]|[2468][048]|[13579][26]))$/);
1215
			return dateRegEx.test(value);
1216
		},
1217
		/**
1218
		* Checks if valid date time
1219
		*
1220
		* @param {string} date string
1221
		* @return a bool based on determination of valid date time
1222
		*/
1223
		_isDateTime: function (value){
1224
			var dateTimeRegEx = new RegExp(/^\d{4}[\/\-](0?[1-9]|1[012])[\/\-](0?[1-9]|[12][0-9]|3[01])\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1}$|^(?:(?:(?:0?[13578]|1[02])(\/|-)31)|(?:(?:0?[1,3-9]|1[0-2])(\/|-)(?:29|30)))(\/|-)(?:[1-9]\d\d\d|\d[1-9]\d\d|\d\d[1-9]\d|\d\d\d[1-9])$|^((1[012]|0?[1-9]){1}\/(0?[1-9]|[12][0-9]|3[01]){1}\/\d{2,4}\s+(1[012]|0?[1-9]){1}:(0?[1-5]|[0-6][0-9]){1}:(0?[0-6]|[0-6][0-9]){1}\s+(am|pm|AM|PM){1})$/);
1225
			return dateTimeRegEx.test(value);
1226
		},
1227
		//Checks if the start date is before the end date
1228
		//returns true if end is later than start
1229
		_dateCompare: function (start, end) {
1230
			return (new Date(start.toString()) < new Date(end.toString()));
1231
		},
1232
		/**
1233
		* Checks date range
1234
		*
1235
		* @param {jqObject} first field name
1236
		* @param {jqObject} second field name
1237
		* @return an error string if validation failed
1238
		*/
1913 daniel 1239
		_dateRange: function (field, rules, i, options) {
1306 daniel 1240
			//are not both populated
1913 daniel 1241
			if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
1306 daniel 1242
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1243
			}
1244
 
1245
			//are not both dates
1913 daniel 1246
			if (!methods._isDate(options.firstOfGroup[0].value) || !methods._isDate(options.secondOfGroup[0].value)) {
1306 daniel 1247
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1248
			}
1249
 
1250
			//are both dates but range is off
1913 daniel 1251
			if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
1306 daniel 1252
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1253
			}
1254
		},
1255
		/**
1256
		* Checks date time range
1257
		*
1258
		* @param {jqObject} first field name
1259
		* @param {jqObject} second field name
1260
		* @return an error string if validation failed
1261
		*/
1913 daniel 1262
		_dateTimeRange: function (field, rules, i, options) {
1306 daniel 1263
			//are not both populated
1913 daniel 1264
			if ((!options.firstOfGroup[0].value && options.secondOfGroup[0].value) || (options.firstOfGroup[0].value && !options.secondOfGroup[0].value)) {
1306 daniel 1265
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1266
			}
1267
			//are not both dates
1913 daniel 1268
			if (!methods._isDateTime(options.firstOfGroup[0].value) || !methods._isDateTime(options.secondOfGroup[0].value)) {
1306 daniel 1269
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1270
			}
1271
			//are both dates but range is off
1913 daniel 1272
			if (!methods._dateCompare(options.firstOfGroup[0].value, options.secondOfGroup[0].value)) {
1306 daniel 1273
				return options.allrules[rules[i]].alertText + options.allrules[rules[i]].alertText2;
1274
			}
1275
		},
1276
		/**
1277
		* Max number of checkbox selected
1278
		*
1279
		* @param {jqObject} field
1280
		* @param {Array[String]} rules
1281
		* @param {int} i rules index
1282
		* @param {Map}
1283
		*            user options
1284
		* @return an error string if validation failed
1285
		*/
1286
		_maxCheckbox: function(form, field, rules, i, options) {
1287
 
1288
			var nbCheck = rules[i + 1];
1289
			var groupname = field.attr("name");
1290
			var groupSize = form.find("input[name='" + groupname + "']:checked").size();
1291
			if (groupSize > nbCheck) {
1292
				options.showArrow = false;
1293
				if (options.allrules.maxCheckbox.alertText2)
1294
					 return options.allrules.maxCheckbox.alertText + " " + nbCheck + " " + options.allrules.maxCheckbox.alertText2;
1295
				return options.allrules.maxCheckbox.alertText;
1296
			}
1297
		},
1298
		/**
1299
		* Min number of checkbox selected
1300
		*
1301
		* @param {jqObject} field
1302
		* @param {Array[String]} rules
1303
		* @param {int} i rules index
1304
		* @param {Map}
1305
		*            user options
1306
		* @return an error string if validation failed
1307
		*/
1308
		_minCheckbox: function(form, field, rules, i, options) {
1309
 
1310
			var nbCheck = rules[i + 1];
1311
			var groupname = field.attr("name");
1312
			var groupSize = form.find("input[name='" + groupname + "']:checked").size();
1313
			if (groupSize < nbCheck) {
1314
				options.showArrow = false;
1315
				return options.allrules.minCheckbox.alertText + " " + nbCheck + " " + options.allrules.minCheckbox.alertText2;
1316
			}
1317
		},
1318
		/**
1319
		* Checks that it is a valid credit card number according to the
1320
		* Luhn checksum algorithm.
1321
		*
1322
		* @param {jqObject} field
1323
		* @param {Array[String]} rules
1324
		* @param {int} i rules index
1325
		* @param {Map}
1326
		*            user options
1327
		* @return an error string if validation failed
1328
		*/
1329
		_creditCard: function(field, rules, i, options) {
1330
			//spaces and dashes may be valid characters, but must be stripped to calculate the checksum.
1331
			var valid = false, cardNumber = field.val().replace(/ +/g, '').replace(/-+/g, '');
1332
 
1333
			var numDigits = cardNumber.length;
1334
			if (numDigits >= 14 && numDigits <= 16 && parseInt(cardNumber) > 0) {
1335
 
1336
				var sum = 0, i = numDigits - 1, pos = 1, digit, luhn = new String();
1337
				do {
1338
					digit = parseInt(cardNumber.charAt(i));
1339
					luhn += (pos++ % 2 == 0) ? digit * 2 : digit;
1340
				} while (--i >= 0)
1341
 
1342
				for (i = 0; i < luhn.length; i++) {
1343
					sum += parseInt(luhn.charAt(i));
1344
				}
1345
				valid = sum % 10 == 0;
1346
			}
1347
			if (!valid) return options.allrules.creditCard.alertText;
1348
		},
1349
		/**
1350
		* Ajax field validation
1351
		*
1352
		* @param {jqObject} field
1353
		* @param {Array[String]} rules
1354
		* @param {int} i rules index
1355
		* @param {Map}
1356
		*            user options
1357
		* @return nothing! the ajax validator handles the prompts itself
1358
		*/
1359
		 _ajax: function(field, rules, i, options) {
1360
 
1361
			 var errorSelector = rules[i + 1];
1362
			 var rule = options.allrules[errorSelector];
1363
			 var extraData = rule.extraData;
1364
			 var extraDataDynamic = rule.extraDataDynamic;
1913 daniel 1365
			 var data = {
1366
				"fieldId" : field.attr("id"),
1367
				"fieldValue" : field.val()
1368
			 };
1306 daniel 1369
 
1913 daniel 1370
			 if (typeof extraData === "object") {
1371
				$.extend(data, extraData);
1372
			 } else if (typeof extraData === "string") {
1373
				var tempData = extraData.split("&");
1374
				for(var i = 0; i < tempData.length; i++) {
1375
					var values = tempData[i].split("=");
1376
					if (values[0] && values[0]) {
1377
						data[values[0]] = values[1];
1378
					}
1379
				}
1380
			 }
1306 daniel 1381
 
1382
			 if (extraDataDynamic) {
1383
				 var tmpData = [];
1384
				 var domIds = String(extraDataDynamic).split(",");
1385
				 for (var i = 0; i < domIds.length; i++) {
1386
					 var id = domIds[i];
1387
					 if ($(id).length) {
1913 daniel 1388
						 var inputValue = field.closest("form, .validationEngineContainer").find(id).val();
1306 daniel 1389
						 var keyValue = id.replace('#', '') + '=' + escape(inputValue);
1913 daniel 1390
						 data[id.replace('#', '')] = inputValue;
1306 daniel 1391
					 }
1392
				 }
1393
			 }
1913 daniel 1394
 
1395
			 // If a field change event triggered this we want to clear the cache for this ID
1396
			 if (options.eventTrigger == "field") {
1397
				delete(options.ajaxValidCache[field.attr("id")]);
1398
			 }
1306 daniel 1399
 
1913 daniel 1400
			 // If there is an error or if the the field is already validated, do not re-execute AJAX
1401
			 if (!options.isError && !methods._checkAjaxFieldStatus(field.attr("id"), options)) {
1306 daniel 1402
				 $.ajax({
1403
					 type: options.ajaxFormValidationMethod,
1404
					 url: rule.url,
1405
					 cache: false,
1406
					 dataType: "json",
1913 daniel 1407
					 data: data,
1306 daniel 1408
					 field: field,
1409
					 rule: rule,
1410
					 methods: methods,
1411
					 options: options,
1913 daniel 1412
					 beforeSend: function() {},
1306 daniel 1413
					 error: function(data, transport) {
2955 daniel 1414
						if (options.onFailure) {
1415
							options.onFailure(data, transport);
1416
						} else {
1417
							methods._ajaxError(data, transport);
1418
						}
1306 daniel 1419
					 },
1420
					 success: function(json) {
1421
 
1422
						 // asynchronously called on success, data is the json answer from the server
1423
						 var errorFieldId = json[0];
1424
						 //var errorField = $($("#" + errorFieldId)[0]);
1913 daniel 1425
						 var errorField = $("#"+ errorFieldId).eq(0);
1306 daniel 1426
 
1427
						 // make sure we found the element
1428
						 if (errorField.length == 1) {
1429
							 var status = json[1];
1430
							 // read the optional msg from the server
1431
							 var msg = json[2];
1432
							 if (!status) {
1433
								 // Houston we got a problem - display an red prompt
1434
								 options.ajaxValidCache[errorFieldId] = false;
1435
								 options.isError = true;
1436
 
1437
								 // resolve the msg prompt
1438
								 if(msg) {
1439
									 if (options.allrules[msg]) {
1440
										 var txt = options.allrules[msg].alertText;
1913 daniel 1441
										 if (txt) {
1442
											msg = txt;
1443
							}
1306 daniel 1444
									 }
1445
								 }
1446
								 else
1913 daniel 1447
									msg = rule.alertText;
1306 daniel 1448
 
1913 daniel 1449
								 if (options.showPrompts) methods._showPrompt(errorField, msg, "", true, options);
1306 daniel 1450
							 } else {
1913 daniel 1451
								 options.ajaxValidCache[errorFieldId] = true;
1306 daniel 1452
 
1453
								 // resolves the msg prompt
1454
								 if(msg) {
1455
									 if (options.allrules[msg]) {
1456
										 var txt = options.allrules[msg].alertTextOk;
1913 daniel 1457
										 if (txt) {
1458
											msg = txt;
1459
							}
1306 daniel 1460
									 }
1461
								 }
1462
								 else
1463
								 msg = rule.alertTextOk;
1464
 
1913 daniel 1465
								 if (options.showPrompts) {
1466
									 // see if we should display a green prompt
1467
									 if (msg)
1468
										methods._showPrompt(errorField, msg, "pass", true, options);
1469
									 else
1470
										methods._closePrompt(errorField);
1471
								}
1472
 
1473
								 // If a submit form triggered this, we want to re-submit the form
1474
								 if (options.eventTrigger == "submit")
1475
									field.closest("form").submit();
1306 daniel 1476
							 }
1477
						 }
1478
						 errorField.trigger("jqv.field.result", [errorField, options.isError, msg]);
1479
					 }
1480
				 });
1913 daniel 1481
 
1482
				 return rule.alertTextLoad;
1306 daniel 1483
			 }
1484
		 },
1485
		/**
1486
		* Common method to handle ajax errors
1487
		*
1488
		* @param {Object} data
1489
		* @param {Object} transport
1490
		*/
1491
		_ajaxError: function(data, transport) {
1492
			if(data.status == 0 && transport == null)
1493
				alert("The page is not served from a server! ajax call failed");
1494
			else if(typeof console != "undefined")
1495
				console.log("Ajax error: " + data.status + " " + transport);
1496
		},
1497
		/**
1498
		* date -> string
1499
		*
1500
		* @param {Object} date
1501
		*/
1502
		_dateToString: function(date) {
1503
			return date.getFullYear()+"-"+(date.getMonth()+1)+"-"+date.getDate();
1504
		},
1505
		/**
1506
		* Parses an ISO date
1507
		* @param {String} d
1508
		*/
1509
		_parseDate: function(d) {
1510
 
1511
			var dateParts = d.split("-");
1512
			if(dateParts==d)
1513
				dateParts = d.split("/");
2955 daniel 1514
			if(dateParts==d) {
1515
				dateParts = d.split(".");
1516
				return new Date(dateParts[2], (dateParts[1] - 1), dateParts[0]);
1517
			}
1306 daniel 1518
			return new Date(dateParts[0], (dateParts[1] - 1) ,dateParts[2]);
1519
		},
1520
		/**
1521
		* Builds or updates a prompt with the given information
1522
		*
1523
		* @param {jqObject} field
1524
		* @param {String} promptText html text to display type
1525
		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
1526
		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
1527
		* @param {Map} options user options
1528
		*/
1529
		 _showPrompt: function(field, promptText, type, ajaxed, options, ajaxform) {
2955 daniel 1530
		 	//Check if we need to adjust what element to show the prompt on
1531
			if(field.data('jqv-prompt-at') instanceof jQuery ){
1532
				field = field.data('jqv-prompt-at');
1533
			} else if(field.data('jqv-prompt-at')) {
1534
				field = $(field.data('jqv-prompt-at'));
1535
			}
1536
 
1306 daniel 1537
			 var prompt = methods._getPrompt(field);
1538
			 // The ajax submit errors are not see has an error in the form,
1539
			 // When the form errors are returned, the engine see 2 bubbles, but those are ebing closed by the engine at the same time
1540
			 // Because no error was found befor submitting
1541
			 if(ajaxform) prompt = false;
1913 daniel 1542
			 // Check that there is indded text
1543
			 if($.trim(promptText)){
1544
				 if (prompt)
1545
					methods._updatePrompt(field, prompt, promptText, type, ajaxed, options);
1546
				 else
1547
					methods._buildPrompt(field, promptText, type, ajaxed, options);
1548
			}
1306 daniel 1549
		 },
1550
		/**
1551
		* Builds and shades a prompt for the given field.
1552
		*
1553
		* @param {jqObject} field
1554
		* @param {String} promptText html text to display type
1555
		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
1556
		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
1557
		* @param {Map} options user options
1558
		*/
1559
		_buildPrompt: function(field, promptText, type, ajaxed, options) {
1560
 
1561
			// create the prompt
1562
			var prompt = $('<div>');
1563
			prompt.addClass(methods._getClassName(field.attr("id")) + "formError");
1564
			// add a class name to identify the parent form of the prompt
1913 daniel 1565
			prompt.addClass("parentForm"+methods._getClassName(field.closest('form, .validationEngineContainer').attr("id")));
1306 daniel 1566
			prompt.addClass("formError");
1567
 
1568
			switch (type) {
1569
				case "pass":
1570
					prompt.addClass("greenPopup");
1571
					break;
1572
				case "load":
1573
					prompt.addClass("blackPopup");
1574
					break;
1575
				default:
1576
					/* it has error  */
1577
					//alert("unknown popup type:"+type);
1578
			}
1579
			if (ajaxed)
1580
				prompt.addClass("ajaxed");
1581
 
1582
			// create the prompt content
1583
			var promptContent = $('<div>').addClass("formErrorContent").html(promptText).appendTo(prompt);
1913 daniel 1584
 
1585
			// determine position type
1586
			var positionType=field.data("promptPosition") || options.promptPosition;
1587
 
1306 daniel 1588
			// create the css arrow pointing at the field
1589
			// note that there is no triangle on max-checkbox and radio
1590
			if (options.showArrow) {
1913 daniel 1591
				var arrow = $('<div>').addClass("formErrorArrow");
1306 daniel 1592
 
1593
				//prompt positioning adjustment support. Usage: positionType:Xshift,Yshift (for ex.: bottomLeft:+20 or bottomLeft:-20,+10)
1594
				if (typeof(positionType)=='string')
1595
				{
1596
					var pos=positionType.indexOf(":");
1913 daniel 1597
					if(pos!=-1)
1306 daniel 1598
						positionType=positionType.substring(0,pos);
1599
				}
1600
 
1601
				switch (positionType) {
1602
					case "bottomLeft":
1603
					case "bottomRight":
1604
						prompt.find(".formErrorContent").before(arrow);
1605
						arrow.addClass("formErrorArrowBottom").html('<div class="line1"><!-- --></div><div class="line2"><!-- --></div><div class="line3"><!-- --></div><div class="line4"><!-- --></div><div class="line5"><!-- --></div><div class="line6"><!-- --></div><div class="line7"><!-- --></div><div class="line8"><!-- --></div><div class="line9"><!-- --></div><div class="line10"><!-- --></div>');
1606
						break;
1607
					case "topLeft":
1608
					case "topRight":
1609
						arrow.html('<div class="line10"><!-- --></div><div class="line9"><!-- --></div><div class="line8"><!-- --></div><div class="line7"><!-- --></div><div class="line6"><!-- --></div><div class="line5"><!-- --></div><div class="line4"><!-- --></div><div class="line3"><!-- --></div><div class="line2"><!-- --></div><div class="line1"><!-- --></div>');
1610
						prompt.append(arrow);
1611
						break;
1612
				}
1613
			}
1913 daniel 1614
			// Add custom prompt class
1615
			if (options.addPromptClass)
1616
				prompt.addClass(options.addPromptClass);
1306 daniel 1617
 
1913 daniel 1618
            // Add custom prompt class defined in element
1619
            var requiredOverride = field.attr('data-required-class');
1620
            if(requiredOverride !== undefined) {
1621
                prompt.addClass(requiredOverride);
1622
            } else {
1623
                if(options.prettySelect) {
1624
                    if($('#' + field.attr('id')).next().is('select')) {
1625
                        var prettyOverrideClass = $('#' + field.attr('id').substr(options.usePrefix.length).substring(options.useSuffix.length)).attr('data-required-class');
1626
                        if(prettyOverrideClass !== undefined) {
1627
                            prompt.addClass(prettyOverrideClass);
1628
                        }
1629
                    }
1630
                }
1631
            }
1632
 
1306 daniel 1633
			prompt.css({
1913 daniel 1634
				"opacity": 0
1306 daniel 1635
			});
1913 daniel 1636
			if(positionType === 'inline') {
1637
				prompt.addClass("inline");
1638
				if(typeof field.attr('data-prompt-target') !== 'undefined' && $('#'+field.attr('data-prompt-target')).length > 0) {
1639
					prompt.appendTo($('#'+field.attr('data-prompt-target')));
1640
				} else {
1641
					field.after(prompt);
1642
				}
1643
			} else {
1644
				field.before(prompt);
1645
			}
1306 daniel 1646
 
1647
			var pos = methods._calculatePosition(field, prompt, options);
1648
			prompt.css({
1913 daniel 1649
				'position': positionType === 'inline' ? 'relative' : 'absolute',
1306 daniel 1650
				"top": pos.callerTopPosition,
1651
				"left": pos.callerleftPosition,
1652
				"marginTop": pos.marginTopSize,
1653
				"opacity": 0
1654
			}).data("callerField", field);
1913 daniel 1655
 
1306 daniel 1656
 
1657
			if (options.autoHidePrompt) {
1658
				setTimeout(function(){
1659
					prompt.animate({
1660
						"opacity": 0
1661
					},function(){
1662
						prompt.closest('.formErrorOuter').remove();
1663
						prompt.remove();
1664
					});
1665
				}, options.autoHideDelay);
1666
			}
1667
			return prompt.animate({
1913 daniel 1668
				"opacity": 0.87
1306 daniel 1669
			});
1670
		},
1671
		/**
1672
		* Updates the prompt text field - the field for which the prompt
1673
		* @param {jqObject} field
1674
		* @param {String} promptText html text to display type
1675
		* @param {String} type the type of bubble: 'pass' (green), 'load' (black) anything else (red)
1676
		* @param {boolean} ajaxed - use to mark fields than being validated with ajax
1677
		* @param {Map} options user options
1678
		*/
1679
		_updatePrompt: function(field, prompt, promptText, type, ajaxed, options, noAnimation) {
1680
 
1681
			if (prompt) {
1682
				if (typeof type !== "undefined") {
1683
					if (type == "pass")
1684
						prompt.addClass("greenPopup");
1685
					else
1686
						prompt.removeClass("greenPopup");
1687
 
1688
					if (type == "load")
1689
						prompt.addClass("blackPopup");
1690
					else
1691
						prompt.removeClass("blackPopup");
1692
				}
1693
				if (ajaxed)
1694
					prompt.addClass("ajaxed");
1695
				else
1696
					prompt.removeClass("ajaxed");
1697
 
1698
				prompt.find(".formErrorContent").html(promptText);
1699
 
1700
				var pos = methods._calculatePosition(field, prompt, options);
1701
				var css = {"top": pos.callerTopPosition,
1702
				"left": pos.callerleftPosition,
1703
				"marginTop": pos.marginTopSize};
1704
 
1705
				if (noAnimation)
1706
					prompt.css(css);
1707
				else
1708
					prompt.animate(css);
1709
			}
1710
		},
1711
		/**
1712
		* Closes the prompt associated with the given field
1713
		*
1714
		* @param {jqObject}
1715
		*            field
1716
		*/
1717
		 _closePrompt: function(field) {
1718
			 var prompt = methods._getPrompt(field);
1719
			 if (prompt)
1720
				 prompt.fadeTo("fast", 0, function() {
1721
					 prompt.parent('.formErrorOuter').remove();
1722
					 prompt.remove();
1723
				 });
1724
		 },
1725
		 closePrompt: function(field) {
1726
			 return methods._closePrompt(field);
1727
		 },
1728
		/**
1729
		* Returns the error prompt matching the field if any
1730
		*
1731
		* @param {jqObject}
1732
		*            field
1733
		* @return undefined or the error prompt (jqObject)
1734
		*/
1735
		_getPrompt: function(field) {
1913 daniel 1736
				var formId = $(field).closest('form, .validationEngineContainer').attr('id');
1306 daniel 1737
			var className = methods._getClassName(field.attr("id")) + "formError";
2955 daniel 1738
				var match = $("." + methods._escapeExpression(className) + '.parentForm' + methods._getClassName(formId))[0];
1306 daniel 1739
			if (match)
1740
			return $(match);
1741
		},
1742
		/**
1743
		  * Returns the escapade classname
1744
		  *
1745
		  * @param {selector}
1746
		  *            className
1747
		  */
1748
		  _escapeExpression: function (selector) {
1749
			  return selector.replace(/([#;&,\.\+\*\~':"\!\^$\[\]\(\)=>\|])/g, "\\$1");
1750
		  },
1751
		/**
1752
		 * returns true if we are in a RTLed document
1753
		 *
1754
		 * @param {jqObject} field
1755
		 */
1756
		isRTL: function(field)
1757
		{
1758
			var $document = $(document);
1759
			var $body = $('body');
1760
			var rtl =
1761
				(field && field.hasClass('rtl')) ||
1762
				(field && (field.attr('dir') || '').toLowerCase()==='rtl') ||
1763
				$document.hasClass('rtl') ||
1764
				($document.attr('dir') || '').toLowerCase()==='rtl' ||
1765
				$body.hasClass('rtl') ||
1766
				($body.attr('dir') || '').toLowerCase()==='rtl';
1767
			return Boolean(rtl);
1768
		},
1769
		/**
1770
		* Calculates prompt position
1771
		*
1772
		* @param {jqObject}
1773
		*            field
1774
		* @param {jqObject}
1775
		*            the prompt
1776
		* @param {Map}
1777
		*            options
1778
		* @return positions
1779
		*/
1780
		_calculatePosition: function (field, promptElmt, options) {
1781
 
1782
			var promptTopPosition, promptleftPosition, marginTopSize;
1783
			var fieldWidth 	= field.width();
1913 daniel 1784
			var fieldLeft 	= field.position().left;
1306 daniel 1785
			var fieldTop 	=  field.position().top;
1786
			var fieldHeight 	=  field.height();
1787
			var promptHeight = promptElmt.height();
1788
 
1789
 
1790
			// is the form contained in an overflown container?
1791
			promptTopPosition = promptleftPosition = 0;
1792
			// compensation for the arrow
1793
			marginTopSize = -promptHeight;
1794
 
1795
 
1796
			//prompt positioning adjustment support
1797
			//now you can adjust prompt position
1798
			//usage: positionType:Xshift,Yshift
1799
			//for example:
1800
			//   bottomLeft:+20 means bottomLeft position shifted by 20 pixels right horizontally
1801
			//   topRight:20, -15 means topRight position shifted by 20 pixels to right and 15 pixels to top
1802
			//You can use +pixels, - pixels. If no sign is provided than + is default.
1803
			var positionType=field.data("promptPosition") || options.promptPosition;
1804
			var shift1="";
1805
			var shift2="";
1806
			var shiftX=0;
1807
			var shiftY=0;
1808
			if (typeof(positionType)=='string') {
1809
				//do we have any position adjustments ?
1810
				if (positionType.indexOf(":")!=-1) {
1811
					shift1=positionType.substring(positionType.indexOf(":")+1);
1812
					positionType=positionType.substring(0,positionType.indexOf(":"));
1813
 
1814
					//if any advanced positioning will be needed (percents or something else) - parser should be added here
1815
					//for now we use simple parseInt()
1816
 
1817
					//do we have second parameter?
1913 daniel 1818
					if (shift1.indexOf(",") !=-1) {
1819
						shift2=shift1.substring(shift1.indexOf(",") +1);
1306 daniel 1820
						shift1=shift1.substring(0,shift1.indexOf(","));
1821
						shiftY=parseInt(shift2);
1822
						if (isNaN(shiftY)) shiftY=0;
1823
					};
1824
 
1825
					shiftX=parseInt(shift1);
1826
					if (isNaN(shift1)) shift1=0;
1827
 
1828
				};
1829
			};
1830
 
1831
 
1832
			switch (positionType) {
1833
				default:
1834
				case "topRight":
1835
					promptleftPosition +=  fieldLeft + fieldWidth - 30;
1836
					promptTopPosition +=  fieldTop;
1837
					break;
1838
 
1839
				case "topLeft":
1840
					promptTopPosition +=  fieldTop;
1913 daniel 1841
					promptleftPosition += fieldLeft;
1306 daniel 1842
					break;
1843
 
1844
				case "centerRight":
1913 daniel 1845
					promptTopPosition = fieldTop+4;
1306 daniel 1846
					marginTopSize = 0;
1913 daniel 1847
					promptleftPosition= fieldLeft + field.outerWidth(true)+5;
1306 daniel 1848
					break;
1849
				case "centerLeft":
1850
					promptleftPosition = fieldLeft - (promptElmt.width() + 2);
1851
					promptTopPosition = fieldTop+4;
1852
					marginTopSize = 0;
1853
 
1854
					break;
1855
 
1856
				case "bottomLeft":
1857
					promptTopPosition = fieldTop + field.height() + 5;
1858
					marginTopSize = 0;
1859
					promptleftPosition = fieldLeft;
1860
					break;
1861
				case "bottomRight":
1862
					promptleftPosition = fieldLeft + fieldWidth - 30;
1863
					promptTopPosition =  fieldTop +  field.height() + 5;
1864
					marginTopSize = 0;
1913 daniel 1865
					break;
1866
				case "inline":
1867
					promptleftPosition = 0;
1868
					promptTopPosition = 0;
1869
					marginTopSize = 0;
1870
			};
1871
 
1306 daniel 1872
 
1873
 
1874
			//apply adjusments if any
1875
			promptleftPosition += shiftX;
1876
			promptTopPosition  += shiftY;
1877
 
1878
			return {
1879
				"callerTopPosition": promptTopPosition + "px",
1880
				"callerleftPosition": promptleftPosition + "px",
1881
				"marginTopSize": marginTopSize + "px"
1882
			};
1883
		},
1884
		/**
1885
		* Saves the user options and variables in the form.data
1886
		*
1887
		* @param {jqObject}
1888
		*            form - the form where the user option should be saved
1889
		* @param {Map}
1890
		*            options - the user options
1891
		* @return the user options (extended from the defaults)
1892
		*/
1893
		 _saveOptions: function(form, options) {
1894
 
1895
			 // is there a language localisation ?
1896
			 if ($.validationEngineLanguage)
1897
			 var allRules = $.validationEngineLanguage.allRules;
1898
			 else
1899
			 $.error("jQuery.validationEngine rules are not loaded, plz add localization files to the page");
1900
			 // --- Internals DO NOT TOUCH or OVERLOAD ---
1901
			 // validation rules and i18
1902
			 $.validationEngine.defaults.allrules = allRules;
1903
 
1904
			 var userOptions = $.extend(true,{},$.validationEngine.defaults,options);
1905
 
1906
			 form.data('jqv', userOptions);
1907
			 return userOptions;
1908
		 },
1909
 
1910
		 /**
1911
		 * Removes forbidden characters from class name
1912
		 * @param {String} className
1913
		 */
1914
		 _getClassName: function(className) {
1915
			 if(className)
1916
				 return className.replace(/:/g, "_").replace(/\./g, "_");
1913 daniel 1917
					  },
1918
		/**
1919
		 * Escape special character for jQuery selector
1920
		 * http://totaldev.com/content/escaping-characters-get-valid-jquery-id
1921
		 * @param {String} selector
1922
		 */
1923
		 _jqSelector: function(str){
1924
			return str.replace(/([;&,\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
1925
		},
1926
		/**
1927
		* Conditionally required field
1928
		*
1929
		* @param {jqObject} field
1930
		* @param {Array[String]} rules
1931
		* @param {int} i rules index
1932
		* @param {Map}
1933
		* user options
1934
		* @return an error string if validation failed
1935
		*/
1936
		_condRequired: function(field, rules, i, options) {
1937
			var idx, dependingField;
1306 daniel 1938
 
1913 daniel 1939
			for(idx = (i + 1); idx < rules.length; idx++) {
1940
				dependingField = jQuery("#" + rules[idx]).first();
1941
 
1942
				/* Use _required for determining wether dependingField has a value.
1943
				 * There is logic there for handling all field types, and default value; so we won't replicate that here
1944
				 * Indicate this special use by setting the last parameter to true so we only validate the dependingField on chackboxes and radio buttons (#462)
1945
				 */
1946
				if (dependingField.length && methods._required(dependingField, ["required"], 0, options, true) == undefined) {
1947
					/* We now know any of the depending fields has a value,
1948
					 * so we can validate this field as per normal required code
1949
					 */
1950
					return methods._required(field, ["required"], 0, options);
1951
				}
1952
			}
1953
		},
1954
 
1955
	    _submitButtonClick: function(event) {
1956
	        var button = $(this);
1957
	        var form = button.closest('form, .validationEngineContainer');
1958
	        form.data("jqv_submitButton", button.attr("id"));
1959
	    }
1960
		  };
1961
 
1306 daniel 1962
	 /**
1963
	 * Plugin entry point.
1964
	 * You may pass an action as a parameter or a list of options.
1965
	 * if none, the init and attach methods are being called.
1966
	 * Remember: if you pass options, the attached method is NOT called automatically
1967
	 *
1968
	 * @param {String}
1969
	 *            method (optional) action
1970
	 */
1971
	 $.fn.validationEngine = function(method) {
1972
 
1973
		 var form = $(this);
1913 daniel 1974
		 if(!form[0]) return form;  // stop here if the form does not exist
1306 daniel 1975
 
1976
		 if (typeof(method) == 'string' && method.charAt(0) != '_' && methods[method]) {
1977
 
1978
			 // make sure init is called once
1979
			 if(method != "showPrompt" && method != "hide" && method != "hideAll")
1980
			 methods.init.apply(form);
1981
 
1982
			 return methods[method].apply(form, Array.prototype.slice.call(arguments, 1));
1983
		 } else if (typeof method == 'object' || !method) {
1984
 
1985
			 // default constructor with or without arguments
1986
			 methods.init.apply(form, arguments);
1987
			 return methods.attach.apply(form);
1988
		 } else {
1989
			 $.error('Method ' + method + ' does not exist in jQuery.validationEngine');
1990
		 }
1991
	};
1992
 
1993
 
1994
 
1995
	// LEAK GLOBAL OPTIONS
1996
	$.validationEngine= {fieldIdCounter: 0,defaults:{
1997
 
1998
		// Name of the event triggering field validation
1999
		validationEventTrigger: "blur",
2000
		// Automatically scroll viewport to the first error
2001
		scroll: true,
2002
		// Focus on the first input
2003
		focusFirstField:true,
1913 daniel 2004
		// Show prompts, set to false to disable prompts
2005
		showPrompts: true,
2006
       // Should we attempt to validate non-visible input fields contained in the form? (Useful in cases of tabbed containers, e.g. jQuery-UI tabs)
2007
       validateNonVisibleFields: false,
1306 daniel 2008
		// Opening box position, possible locations are: topLeft,
1913 daniel 2009
		// topRight, bottomLeft, centerRight, bottomRight, inline
2010
		// inline gets inserted after the validated field or into an element specified in data-prompt-target
1306 daniel 2011
		promptPosition: "topRight",
2012
		bindMethod:"bind",
2013
		// internal, automatically set to true when it parse a _ajax rule
2014
		inlineAjax: false,
2015
		// if set to true, the form data is sent asynchronously via ajax to the form.action url (get)
2016
		ajaxFormValidation: false,
2017
		// The url to send the submit ajax validation (default to action)
2018
		ajaxFormValidationURL: false,
2019
		// HTTP method used for ajax validation
2020
		ajaxFormValidationMethod: 'get',
2021
		// Ajax form validation callback method: boolean onComplete(form, status, errors, options)
2022
		// retuns false if the form.submit event needs to be canceled.
2023
		onAjaxFormComplete: $.noop,
2024
		// called right before the ajax call, may return false to cancel
2025
		onBeforeAjaxFormValidation: $.noop,
2026
		// Stops form from submitting and execute function assiciated with it
2027
		onValidationComplete: false,
2028
 
2029
		// Used when you have a form fields too close and the errors messages are on top of other disturbing viewing messages
2030
		doNotShowAllErrosOnSubmit: false,
1913 daniel 2031
		// Object where you store custom messages to override the default error messages
2032
		custom_error_messages:{},
1306 daniel 2033
		// true if you want to vind the input fields
2034
		binded: true,
2035
		// set to true, when the prompt arrow needs to be displayed
2036
		showArrow: true,
2037
		// did one of the validation fail ? kept global to stop further ajax validations
2038
		isError: false,
1913 daniel 2039
		// Limit how many displayed errors a field can have
2040
		maxErrorsPerField: false,
2041
 
1306 daniel 2042
		// Caches field validation status, typically only bad status are created.
2043
		// the array is used during ajax form validation to detect issues early and prevent an expensive submit
2044
		ajaxValidCache: {},
2045
		// Auto update prompt position after window resize
2046
		autoPositionUpdate: false,
2047
 
2048
		InvalidFields: [],
1913 daniel 2049
		onFieldSuccess: false,
2050
		onFieldFailure: false,
1306 daniel 2051
		onSuccess: false,
2052
		onFailure: false,
1913 daniel 2053
		validateAttribute: "class",
2054
		addSuccessCssClassToField: "",
2055
		addFailureCssClassToField: "",
2056
 
1306 daniel 2057
		// Auto-hide prompt
2058
		autoHidePrompt: false,
2059
		// Delay before auto-hide
2060
		autoHideDelay: 10000,
2061
		// Fade out duration while hiding the validations
1913 daniel 2062
		fadeDuration: 0.3,
2063
	 // Use Prettify select library
2064
	 prettySelect: false,
2065
	 // Add css class on prompt
2066
	 addPromptClass : "",
2067
	 // Custom ID uses prefix
2068
	 usePrefix: "",
2069
	 // Custom ID uses suffix
2070
	 useSuffix: "",
2071
	 // Only show one message per error prompt
2072
	 showOneMessage: false
1306 daniel 2073
	}};
2074
	$(function(){$.validationEngine.defaults.promptPosition = methods.isRTL()?'topLeft':"topRight"});
2955 daniel 2075
})(jQuery);
2076
 
2077