PageSourceSearch

https://seminarbase.com/common/js/validationEngine/jquery.validationEngine.js

js seminarbase.com collected 2026-10-06 07:59:22 UTC 73,287 bytes, 2,162 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.