PageSourceSearch

https://www.vio.hr/mojvio/js/formcheck/formcheck-1.4.js?a=1

js vio.hr collected 2026-10-07 23:16:47 UTC 35,271 bytes, 966 lines download raw bytes

1/*
2	Class: FormCheck
3		Performs different tests on forms and indicates errors.
4		
5	Usage:
6		Works with these types of fields :
7		- input (text, radio, checkbox)
8		- textarea
9		- select
10		
11		You just need to add a specific class to each fields you want to check. 
12		For example, if you add the class
13			(code)
14			validate['required','length[4, -1]','differs[email]','digit']
15			(end code)
16		the value's field must be set (required) with a minimum length of four chars (4, -1), 
17		must differs of the input named email (differs[email]), and must be digit. 
18		
19		You can perform check during the datas entry or on the submit action, shows errors as tips or in a div before or after the field, 
20		show errors one by one or all together, show a list of all errors at the top of the form, localize error messages, add new regex check, ...
21		
22		The layout is design only with css. Now I added a hack to use transparent png with IE6, so you can use png images in formcheck.css (works only for theme, so the file must be named formcheck.css). It can also works with multiple forms on a single html page.
23		The class supports now internationalization. To use it, simply specify a new <script> element in your html head, like this : <script type="text/javascript" src="formcheck/lang/fr.js"></script>.
24
25		If you add the class
26			(code)
27			validate['submit']
28			(end code)
29		to an element like an anchor (or anything else), this element will act as a submit button.
30		
31		N.B. : you must load the language script before the formcheck and this method overpass the old way. You can create new languages following existing ones. You can otherwise still specifiy the alerts' strings when you initialize the Class, with options.
32		If you don't use a language script, the alert will be displayed in english.
33	
34	Test type:
35		You can perform various test on fields by addind them to the validate class. Be careful to *not use space chars*. Here is the list of them.
36			
37		required 					- The field becomes required. This is a regex, you can change it with class options.
38		alpha 						- The value is restricted to alphabetic chars. This is a regex, you can change it with class options.
39		alphanum 					- The value is restricted to alphanumeric characters only. This is a regex, you can change it with class options.
40		nodigit 					- The field doesn't accept digit chars. This is a regex, you can change it with class options.
41		digit 						- The value is restricted to digit (no floating point number) chars, you can pass two arguments (f.e. digit[21,65]) to limit the number between them. Use -1 as second argument to not set a maximum.
42		number 						- The value is restricted to number, including floating point number. This is a regex, you can change it with class options.
43		email 						- The value is restricted to valid email. This is a regex, you can change it with class options.
44		phone 						- The value is restricted to phone chars. This is a regex, you can change it with class options.
45		url: 						- The value is restricted to url. This is a regex, you can change it with class options.
46		confirm 					- The value has to be the same as the one passed in argument. f.e. confirm[password].
47		differs 					- The value has to be diferent as the one passed in argument. f.e. differs[user].
48		length 						- The value length is restricted by argument (f.e. length[6,10]). Use -1 as second argument to not set a maximum.
49		
50		You can also use a custom function to check a field. For example, if you have a field with class
51			(code)
52			validate['required','%customCheck']
53			(end code)
54		the function customCheck(el) will be called to validate the field. '%customcheck' works with other validate(s) together, and '~customcheck' works if the element pass the other validate(s). 
55		Here is an example of what customCheck could look : 
56			(code)
57			function customCheck(el){
58				if (!el.value.test(/^[A-Z]/)) {
59					el.errors.push("Username should begin with an uppercase letter"); 
60					return false;
61				} else {
62					return true;
63				}
64			}
65			(end code)
66		
67		It is now possible to register new fields after a new FormCheck call by using <FormCheck::register> (see <FormCheck::dispose> too).
68		
69	Parameters:
70		When you initialize the class with addEvent, you can set some options. If you want to modify regex, you must do it in a hash, like for display or alert. You can also add new regex check method by adding the regex and an alert with the same name.
71		
72		Required:
73			
74			form_id - The id of the formular. This is required.
75			
76		Optional:
77			
78			submitByAjax 			- you can set this to true if you want to submit your form with ajax. You should use provided events to handle the ajax request (see below). By default it is false.
79			ajaxResponseDiv 		- id of element to inject ajax response into (can also use onAjaxSuccess). By default it is false.
80			ajaxEvalScripts 		- use evalScripts in the Request response. Can be true or false, by default it is false.
81			onAjaxRequest 			- Function to fire when the Request event starts.
82			onAjaxSuccess 			- Function to fire when the Request receives .  Args: response [the request response] - see Mootools docs for Request.onSuccess.
83			onAjaxFailure 			- Function to fire if the Request fails.
84			
85			tipsClass 				- The class to apply to tipboxes' errors. By default it is 'fc-tbx'.
86			errorClass 				- The class to apply to alertbox (not tips). By default it is 'fc-error'.
87			fieldErrorClass 		- The class to apply to fields with errors, except for radios. You should also turn on  options.addClassErrorToField. By default it is 'fc-field-error'
88			
89			trimValue				- If set to true, strip whitespace (or other characters) from the beginning and end of values. By default it is false.
90			validateDisabled		- If set to true, disabled input will be validated too, otherwise not.
91
92		Display:
93			This is a hash of display settings. in here you can modify.
94			
95			showErrors 				- 0 : onSubmit, 1 : onSubmit & onBlur, by default it is 1.
96			titlesInsteadNames		- 0 : When you do a check using differs or confirm, it takes the field name for the alert. If it's set to 1, it will use the title instead of the name.
97			errorsLocation 			- 1 : tips, 2 : before, 3 : after, by default it is 1.
98			indicateErrors 			- 0 : none, 1 : one by one, 2 : all, by default it is 1.
99			indicateErrorsInit		- 0 : determine if the form must be checked on initialize. Could be usefull to force the user to update fields that don't validate.
100			keepFocusOnError 		- 0 : normal behaviour, 1 : the current field keep the focus as it remain errors. By default it is 0.
101			checkValueIfEmpty 		- 0 : When you leave a field and you have set the showErrors option to 1, the value is tested only if a value has been set. 1 : The value is tested  in any case.  By default it is 1.
102			addClassErrorToField 	- 0 : no class is added to the field, 1 : the options.fieldErrorClass is added to the field with an error (except for radio). By default it is 0.
103
104			fixPngForIe 			- 0 : do nothing, 1 : fix png alpha for IE6 in formcheck.css. By default it is 1.
105			replaceTipsEffect 		- 0 : No effect on tips replace when we resize the broswer, 1: tween transition on browser resize;
106			closeTipsButton 		- 0 : the close button of the tipbox is hidden, 1 : the close button of the tipbox is visible. By default it is 1.
107			flashTips 				- 0 : normal behaviour, 1 : the tipbox "flash" (disappear and reappear) if errors remain when the form is submitted. By default it is 0.
108			tipsPosition 			- 'right' : the tips box is placed on the right part of the field, 'left' to place it on the left part. By default it is 'right'.
109			tipsOffsetX 			- Horizontal position of the tips box (margin-left), , by default it is 100 (px).
110			tipsOffsetY				- Vertical position of the tips box (margin-bottom), , by default it is -10 (px).
111			
112			listErrorsAtTop 		- List all errors at the top of the form, , by default it is false.
113			scrollToFirst 			- Smooth scroll the page to first error and focus on it, by default it is true.
114			fadeDuration 			- Transition duration (in ms), by default it is 300.
115		
116		Alerts:
117			This is a hash of alerts settings. in here you can modify strings to localize or wathever else. %0 and %1 represent the argument.
118			
119			required 				- "This field is required."
120			alpha 					- "This field accepts alphabetic characters only."
121			alphanum 				- "This field accepts alphanumeric characters only."
122			nodigit 				- "No digits are accepted."
123			digit 					- "Please enter a valid integer."
124			digitmin 				- "The number must be at least %0"
125			digitltd 				- "The value must be between %0 and %1"
126			number 					- "Please enter a valid number."
127			email 					- "Please enter a valid email: <br /><span>E.g. [email protected]</span>"
128			phone 					- "Please enter a valid phone."
129			url 					- "Please enter a valid url: <br /><span>E.g. http://www.domain.com</span>"
130			confirm 				- "This field is different from %0"
131			differs 				- "This value must be different of %0"
132			length_str 				- "The length is incorrect, it must be between %0 and %1"
133			length_fix 				- "The length is incorrect, it must be exactly %0 characters"
134			lengthmax 				- "The length is incorrect, it must be at max %0"
135			lengthmin 				- "The length is incorrect, it must be at least %0"
136			checkbox 				- "Please check the box"
137			radios 					- "Please select a radio"
138			select 					- "Please choose a value"
139		
140	Example:
141		You can initialize a formcheck (no scroll, custom classes and alert) by adding for example this in your html head this code :
142		
143		(code)
144		<script type="text/javascript">
145			window.addEvent('domready', function() {
146				var myCheck = new FormCheck('form_id', {
147					tipsClass : 'tips_box',
148					display : {
149						scrollToFirst : false
150					},
151					alerts : {
152						required : 'This field is ablolutely required! Please enter a value'
153					}
154				})
155			});
156		</script>
157		(end code)
158	
159	About:
160		formcheck.js v.1.4.2 for mootools v1.2 - 12 / 2008
161		
162		by Floor SA (http://www.floor.ch) MIT-style license
163		
164		Created by Luca Pillonel, last modified by Luca Pillonel
165		
166	Credits:
167		This class was inspired by fValidator by Fabio Zendhi Nagao (http://zend.lojcomm.com.br)
168		
169		Thanks to oneZ, Blots, huughelmink, chdeliens, and everyone who contribute to this project...
170*/
171
172var FormCheck = new Class({
173	
174	Implements: [Options, Events],
175
176	options : {
177		
178		tipsClass: 'fc-tbx',				//tips error class
179		errorClass: 'fc-error',				//div error class
180		fieldErrorClass: 'fc-field-error',	//error class for elements
181		
182		trimValue : false,					//trim (remove whitespaces before and after) the value
183		validateDisabled : false,			//skip validation on disabled input if set to false.
184		
185		submitByAjax : false,				//false : standard submit way, true : submit by ajax
186		ajaxResponseDiv : false,			//element to inject ajax response into (can also use onAjaxSuccess) [cronix] 
187		ajaxEvalScripts : false,			//use evalScripts in the Request response [cronix] 
188		onAjaxRequest : $empty,				//Function to fire when the Request event starts 
189		onAjaxSuccess : $empty,				//Function to fire when the Request receives .  Args: response [the request response] - see Mootools docs for Request.onSuccess 
190		onAjaxFailure : $empty,				//Function to fire if the Request fails 
191
192		display : {
193			showErrors : 0,
194			titlesInsteadNames : 0,
195			errorsLocation : 1,
196			indicateErrors : 1,
197			indicateErrorsInit : 0,
198			keepFocusOnError : 0,
199			checkValueIfEmpty : 1,
200			addClassErrorToField : 0,
201			fixPngForIe : 1,
202			replaceTipsEffect : 1,
203			flashTips : 0,
204			closeTipsButton : 1,
205			tipsPosition : "right",
206			tipsOffsetX : -45,
207			tipsOffsetY : 0,
208			listErrorsAtTop : false,
209			scrollToFirst : true,
210			fadeDuration : 300
211		},
212		
213		alerts : {
214			required: "This field is required.",
215			alpha: "This field accepts alphabetic characters only.",
216			alphanum: "This field accepts alphanumeric characters only.",
217			nodigit: "No digits are accepted.",
218			digit: "Please enter a valid integer.",
219			digitltd: "The value must be between %0 and %1",
220			number: "Please enter a valid number.",
221			email: "Please enter a valid email.",
222			phone: "Please enter a valid phone.",
223			url: "Please enter a valid url.",
224			
225			confirm: "This field is different from %0",
226			differs: "This value must be different of %0",
227			length_str: "The length is incorrect, it must be between %0 and %1",
228			length_fix: "The length is incorrect, it must be exactly %0 characters",
229			lengthmax: "The length is incorrect, it must be at max %0",
230			lengthmin: "The length is incorrect, it must be at least %0",
231			checkbox: "Please check the box",
232			radios: "Please select a radio",
233			select: "Please choose a value"
234		},
235		
236		regexp : {
237			required : /[^.*]/,
238			alpha : /^[a-z ._-]+$/i,
239			alphanum : /^[a-z0-9 ._-]+$/i,
240			digit : /^[-+]?[0-9]+$/,
241			nodigit : /^[^0-9]+$/,
242			number : /^[-+]?\d*\.?\d+$/,
243			email : /^[a-z0-9._%\-+]+@[a-z0-9.-]+\.[a-z]{2,4}$/i,
244			phone : /^[\d\s ().-]+$/,
245			url : /^(http|https|ftp)\:\/\/[a-z0-9\-\.]+\.[a-z]{2,3}(:[a-z0-9]*)?\/?([a-z0-9\-\._\?\,\'\/\\\+&amp;%\$#\=~])*$/i
246		}
247	},
248	
249	/*
250	Constructor: initialize
251		Constructor
252	
253		Add event on formular and perform some stuff, you now, like settings, ...
254	*/
255	initialize : function(form, options) {
256		if (this.form = $(form)) {
257			
258			this.form.isValid = true;
259			this.regex = ['length'];
260			this.setOptions(options);
261			
262			//internalization
263			if (typeof(formcheckLanguage) != 'undefined') this.options.alerts = $merge(this.options.alerts, formcheckLanguage);
264			
265			this.validations = [];
266			this.alreadyIndicated = false;
267			this.firstError = false;
268			
269			var regex = new Hash(this.options.regexp);
270			regex.each(function(el, key) {
271				this.regex.push(key);
272			}, this);
273
274			this.form.getElements("*[class*=validate]").each(function(el) {
275				this.register(el);
276			}, this);
277			
278			this.form.addEvents({
279				"submit": this.onSubmit.bind(this)
280			});
281			
282			if(this.options.display.fixPngForIe) this.fixIeStuffs();
283			document.addEvent('mousewheel', function(){
284				this.isScrolling = false;
285			}.bind(this));
286		}
287	},
288	
289	/*
290	Function: register
291		Allows you to declare afterward new fields to the formcheck, to check dynamically loaded fields for example.
292	
293	Example:
294		(code)
295		<script type="text/javascript">
296			window.addEvent('domready', function() {
297				formcheck = new FormCheck('form_id');
298			});
299			
300			// ...some code...
301			
302			var newField = new Element('input', {
303				class	: "validate['required']",
304				name	: "new-field"
305			}).inject('form_id');
306			formcheck.register(newField);
307			
308			new Element('input', {
309				class	: "validate['required']",
310				name	: "another-field",
311				id		: "another-field"
312			}).inject('form_id');
313			formcheck.register($('another-field'));
314		</script>
315		(end code)
316	
317	See also:
318		<FormCheck::dispose>
319	*/
320	register : function(el) {
321		el.validation = [];
322		el.getProperty("class").split(' ').each(function(classX) {
323			if(classX.match(/^validate(\[.+\])$/)) {
324				var validators = eval(classX.match(/^validate(\[.+\])$/)[1]);
325				for(var i = 0; i < validators.length; i++) {
326					el.validation.push(validators[i]);
327					if (validators[i].match(/^confirm\[/)) {
328						var field = eval(validators[i].match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'"));
329						if (this.form[field].validation.contains('required')){
330							el.validation.push('required');
331						}
332							
333					}
334				}
335				this.addListener(el);
336			}
337		}, this);
338	},
339	
340	/*
341	Function: dispose
342		Allows you to remove a declared field from formCheck
343	
344	Example:
345		(code)
346		<script type="text/javascript">
347			window.addEvent('domready', function() {
348				formcheck = new FormCheck('form_id');
349			});
350			
351			// ...some code...
352			
353			formcheck.dispose($('obsolete-field'));
354		</script>
355		(end code)
356	
357	See also:
358		<FormCheck::register>
359	*/
360	dispose : function(element) {
361		this.validations.erase(element);
362	},
363	
364	/*
365	Function: addListener
366		Private method
367		
368		Add listener on fields
369	*/
370	addListener : function(el) {
371		this.validations.push(el);
372		el.errors = [];
373		
374		if (this.options.display.indicateErrorsInit) {
375			this.validations.each(function(el) {
376				if(!this.manageError(el,'submit')) this.form.isValid = false;
377			}, this);
378			return true;
379		} 
380	
381		if (el.validation[0] == 'submit') {
382			el.addEvent('click', function(e){
383				this.onSubmit(e);
384			}.bind(this));
385			return true;
386		}
387
388		if (this.isChildType(el) == false) el.addEvent('blur', function() {
389			(function(){
390				if(!this.fxRunning && (el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value))
391				this.manageError(el, 'blur')
392			}.bind(this)).delay(100);
393		}.bind(this))
394		//We manage errors on radio
395		else if (this.isChildType(el) == true) {
396			//We get all radio from the same group and add a blur option
397			var nlButtonGroup = this.form.getElements('input[name="'+ el.getProperty("name") +'"]');
398			nlButtonGroup.each(function(radio){
399				radio.addEvent('blur', function(){
400					(function(){
401						if((el.element || this.options.display.showErrors == 1) && (this.options.display.checkValueIfEmpty || el.value)) this.manageError(el, 'click');
402					}.bind(this)).delay(100);
403				}.bind(this))
404			},this);
405		}
406	},
407	
408	/*
409	Function: validate
410		Private method
411		
412		Dispatch check to other methods
413	*/
414	validate : function(el) {
415		el.errors = [];
416		el.isOk = true;
417		
418		//skip validation and trim if specified
419		if (!this.options.validateDisabled && el.get('disabled')) return true;
420		if (this.options.trimValue && el.value) el.value = el.value.trim();
421		
422		el.validation.each(function(rule) {
423			if(this.isChildType(el)) {
424				if (this.validateGroup(el) == false) {
425					el.isOk = false;
426				}
427			} else {
428				var ruleArgs = [];
429				
430				if(rule.match(/^.+\[/)) {
431					var ruleMethod = rule.split('[')[0];
432					ruleArgs = eval(rule.match(/^.+(\[.+\])$/)[1].replace(/([A-Z0-9\._-]+)/i, "'$1'"));
433				} else var ruleMethod = rule;
434				
435				if (this.regex.contains(ruleMethod) && el.get('tag') != "select") {
436					if (this.validateRegex(el, ruleMethod, ruleArgs) == false) {
437						el.isOk = false;
438					}
439				}
440				
441				if (ruleMethod == 'confirm') {
442					if (this.validateConfirm(el, ruleArgs) == false) {
443						el.isOk = false;
444					}
445				}
446				if (ruleMethod == 'differs') {
447					if (this.validateDiffers(el, ruleArgs) == false) {
448						el.isOk = false;
449					}
450				}
451				if (el.get('tag') == "select" || (el.type == "checkbox" && ruleMethod == 'required')) {
452					if (this.simpleValidate(el) == false) {
453						el.isOk = false;
454					}
455				}
456				if(rule.match(/%[A-Z0-9\._-]+$/i) || (el.isOk && rule.match(/~[A-Z0-9\._-]+$/i))) {
457					if(eval(rule.slice(1)+'(el)') == false) {
458						el.isOk = false;
459					}
460				}
461			}
462		}, this);
463		
464		if (el.isOk) return true;
465		else return false;
466	},
467	
468	/*
469	Function: simpleValidate
470		Private method
471		
472		Perform simple check for select fields and checkboxes
473	*/
474	simpleValidate : function(el) {
475		if (el.get('tag') == 'select' && el.selectedIndex <= 0) {
476			el.errors.push(this.options.alerts.select);
477			return false;
478		} else if (el.type == "checkbox" && el.checked == false) {
479			el.errors.push(this.options.alerts.checkbox);
480			return false;
481		}
482		return true;
483	},
484	
485	/*
486	Function: validateRegex
487		Private method
488		
489		Perform regex validations
490	*/
491	validateRegex : function(el, ruleMethod, ruleArgs) {
492		var msg = "";
493		if (ruleArgs[1] && ruleMethod == 'length') {
494			if (ruleArgs[1] == -1) {
495				this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +",}$");
496				msg = this.options.alerts.lengthmin.replace("%0",ruleArgs[0]);
497			} else if(ruleArgs[0] == ruleArgs[1]) {
498				this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +"}$");
499				msg = this.options.alerts.length_fix.replace("%0",ruleArgs[0]);
500			} else {
501				this.options.regexp.length = new RegExp("^[\\s\\S]{"+ ruleArgs[0] +","+ ruleArgs[1] +"}$");
502				msg = this.options.alerts.length_str.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]);
503			}
504		} else if (ruleArgs[0] && ruleMethod == 'length') {
505			this.options.regexp.length = new RegExp("^.{0,"+ ruleArgs[0] +"}$");
506			msg = this.options.alerts.lengthmax.replace("%0",ruleArgs[0]);
507		} else {
508			msg = this.options.alerts[ruleMethod];
509		}
510		if (ruleArgs[1] && ruleMethod == 'digit') {
511			var regres = true;
512			if (!this.options.regexp.digit.test(el.value)) {
513				el.errors.push(this.options.alerts[ruleMethod]);
514				regres = false;
515			}
516			if (ruleArgs[1] == -1) {
517				if (el.value >= ruleArgs[0]) var valueres = true; else var valueres = false;
518				msg = this.options.alerts.digitmin.replace("%0",ruleArgs[0]);
519			} else {
520				if (el.value >= ruleArgs[0] && el.value <= ruleArgs[1]) var valueres = true; else var valueres = false;
521				msg = this.options.alerts.digitltd.replace("%0",ruleArgs[0]).replace("%1",ruleArgs[1]);
522			}
523			if (regres == false || valueres == false) {
524				el.errors.push(msg);
525				return false;
526			}
527		} else if (this.options.regexp[ruleMethod].test(el.value) == false)  {
528			el.errors.push(msg);
529			return false;
530		}
531		return true;
532	},
533
534	/*
535	Function: validateConfirm
536		Private method
537		
538		Perform confirm validations
539	*/
540	validateConfirm: function(el,ruleArgs) {
541		
542		var confirm = ruleArgs[0];
543		if(el.value != this.form[confirm].value){
544			if (this.options.display.titlesInsteadNames)
545				var msg = this.options.alerts.confirm.replace("%0",this.form[confirm].getProperty('title'));
546			else
547				var msg = this.options.alerts.confirm.replace("%0",confirm);
548			el.errors.push(msg);
549			return false;
550		}
551		return true;
552	},
553	
554	/*
555	Function: validateDiffers
556		Private method
557		
558		Perform differs validations
559	*/
560	validateDiffers: function(el,ruleArgs) {
561		var differs = ruleArgs[0];
562		if(el.value == this.form[differs].value){
563			if (this.options.display.titlesInsteadNames)
564				var msg = this.options.alerts.differs.replace("%0",this.form[differs].getProperty('title'));
565			else
566				var msg = this.options.alerts.differs.replace("%0",differs);
567			el.errors.push(msg);
568			return false;
569		}
570		return true;
571	},
572	
573	/*
574	Function: isChildType
575		Private method
576		
577		Determine if the field is a group of radio or not.
578	*/
579	isChildType: function(el) {
580		return ($defined(el.type) && el.type == 'radio') ? true : false;
581	},
582	
583	/*
584	Function: validateGroup
585		Private method
586		
587		Perform radios validations
588	*/
589	validateGroup : function(el) {
590		el.errors = [];
591		var nlButtonGroup = this.form[el.getProperty("name")];
592		el.group = nlButtonGroup;
593		var cbCheckeds = false;
594		
595		for(var i = 0; i < nlButtonGroup.length; i++) {
596			if(nlButtonGroup[i].checked) {
597				cbCheckeds = true;
598			}
599		}
600		if(cbCheckeds == false) {
601			el.errors.push(this.options.alerts.radios);
602			return false;
603		} else {
604			return true;	
605		}
606	},
607	
608	/*
609	Function: listErrorsAtTop
610		Private method
611		
612		Display errors
613	*/
614	listErrorsAtTop : function(obj) {
615		if(!this.form.element) {
616			 this.form.element = new Element('div', {'id' : 'errorlist', 'class' : this.options.errorClass}).injectTop(this.form);
617		}
618		if ($type(obj) == 'collection') {
619			new Element('p').set('html',"<span>" + obj[0].name + " : </span>" + obj[0].errors[0]).injectInside(this.form.element);
620		} else {
621			if ((obj.validation.contains('required') && obj.errors.length > 0) || (obj.errors.length >
621 0 && obj.value && obj.validation.contains('required') == false)) {
622				obj.errors.each(function(error) {
623					new Element('p').set('html',"<span>" + obj.name + " : </span>" + error).injectInside(this.form.element);
624				}, this);
625			}
626		}
627	},
628	
629	/*
630	Function: manageError
631		Private method
632		
633		Manage display of errors boxes
634	*/
635	manageError : function(el, method) {
636		var isValid = this.validate(el);
637		if ((!isValid && el.validation.flatten()[0].contains('confirm[')) || (!isValid && el.validation.contains('required')) || (!el.validation.contains('required') && el.value && !isValid)) {
638			if(this.options.display.listErrorsAtTop == true && method == 'submit')
639				this.listErrorsAtTop(el, method);
640			if (this.options.display.indicateErrors == 2 ||this.alreadyIndicated == false || el.name == this.alreadyIndicated.name)
641			{
642				if(!this.firstError) this.firstError = el;
643				
644				this.alreadyIndicated = el;
645				
646				if (this.options.display.keepFocusOnError && el.name == this.firstError.name) (function(){el.focus()}).delay(20);
647				this.addError(el);
648				return false;
649			}
650		} else if ((isValid || (!el.validation.contains('required') && !el.value)) && el.element) {
651			this.removeError(el);
652			return true;
653		}
654		return true;
655	},
656	
657	/*
658	Function: addError
659		Private method
660		
661		Add error message
662	*/
663	addError : function(obj) {
664		if(!obj.element && this.options.display.indicateErrors != 0) {
665			if (this.options.display.errorsLocation == 1) {
666				var pos = (this.options.display.tipsPosition == 'left') ? obj.getCoordinates().left : obj.getCoordinates().right;
667				var options = {
668					'opacity' : 0,
669					'position' : 'absolute',
670					'float' : 'left',
671					'left' : pos + this.options.display.tipsOffsetX
672				}
673				obj.element = new Element('div', {'class' : this.options.tipsClass, 'styles' : options}).injectInside(document.body);
674				this.addPositionEvent(obj);
675			} else if (this.options.display.errorsLocation == 2){
676				obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}}).injectBefore(obj);
677			} else if (this.options.display.errorsLocation == 3){
678				obj.element = new Element('div', {'class' : this.options.errorClass, 'styles' : {'opacity' : 0}});
679				if ($type(obj.group) == 'object' || $type(obj.group) == 'collection')
680					obj.element.injectAfter(obj.group[obj.group.length-1]);
681				else
682					obj.element.injectAfter(obj);
683			}
684		}					
685		if (obj.element && obj.element != true) {
686			obj.element.empty();
687			if (this.options.display.errorsLocation == 1) {
688				var errors = [];
689				obj.errors.each(function(error) {
690					errors.push(new Element('p').set('html', error));
691				});
692				var tips = this.makeTips(errors).injectInside(obj.element);
693				if(this.options.display.closeTipsButton) {
694					tips.getElements('a.close').addEvent('mouseup', function(){
695						this.removeError(obj);
696					}.bind(this));
697				}
698				obj.element.setStyle('top', obj.getCoordinates().top - tips.getCoordinates().height + this.options.display.tipsOffsetY);
699			} else {
700				obj.errors.each(function(error) {
701					new Element('p').set('html',error).injectInside(obj.element);
702				});
703			}
704			
705			if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation < 2) {
706				obj.element.setStyle('opacity', 1);
707			} else {
708				obj.fx = new Fx.Tween(obj.element, {
709					'duration' : this.options.display.fadeDuration,
710					'ignore' : true,
711					'onStart' : function(){
712						this.fxRunning = true;
713					}.bind(this),
714					'onComplete' : function() {
715						this.fxRunning = false;
716						if (obj.element && obj.element.getStyle('opacity').toInt() == 0) {
717							obj.element.destroy();
718							obj.element = false;
719						}
720					}.bind(this)
721				})
722				if(obj.element.getStyle('opacity').toInt() != 1) obj.fx.start('opacity', 1);
723			}
724		}
725		if (this.options.display.addClassErrorToField && this.isChildType(obj) == false){
726			obj.addClass(this.options.fieldErrorClass);
727			obj.element = obj.element || true;
728		}
729			
730	},
731	
732	/*
733	Function: addPositionEvent
734		
735		Update tips position after a browser resize
736	*/
737	addPositionEvent : function(obj) {
738		if(this.options.display.replaceTipsEffect) {
739			obj.event = function(){
740				new Fx.Morph(obj.element, {
741					'duration' : this.options.display.fadeDuration
742				}).start({ 
743					'left':[obj.element.getStyle('left'), obj.getCoordinates().right + this.options.display.tipsOffsetX],
744					'top':[obj.element.getStyle('top'), obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY]
745				});
746			}.bind(this);
747			
748		} else {
749			obj.event = function(){
750				obj.element.setStyles({ 
751					'left':obj.getCoordinates().right + this.options.display.tipsOffsetX,
752					'top':obj.getCoordinates().top - obj.element.getCoordinates().height + this.options.display.tipsOffsetY
753				});
754			}.bind(this)
755		}
756		window.addEvent('resize', obj.event);
757	},
758	
759	/*
760	Function: removeError
761		Private method
762		
763		Remove the error display
764	*/
765	removeError : function(obj) {
766		this.alreadyIndicated = false;
767		obj.errors = [];
768		obj.isOK = true;
769		window.removeEvent('resize', obj.event);
770		if (this.options.display.errorsLocation >= 2 && obj.element) {
771			new Fx.Tween(obj.element, {
772				'duration': this.options.display.fadeDuration
773			}).start('height', 0);
774		}
775		if (!this.options.display.fadeDuration || Browser.Engine.trident && Browser.Engine.version == 5 && this.options.display.errorsLocation == 1 && obj.element) {
776			this.fxRunning = true;
777			obj.element.destroy();
778			obj.element = false;
779			(function(){this.fxRunning = false}.bind(this)).delay(200);
780		} else if (obj.element && obj.element != true) {
781			obj.fx.start('opacity', 0);
782		}
783		
784		if (this.options.display.addClassErrorToField && !this.isChildType(obj))
785			obj.removeClass(this.options.fieldErrorClass);
786	},
787	
788	/*
789	Function: focusOnError
790		Private method
791		
792		Create set the focus to the first field with an error if needed
793	*/
794	focusOnError : function (obj) {
795		if (this.options.display.scrollToFirst && !this.alreadyFocused && !this.isScrolling) {
796			if (!this.options.display.indicateErrors || !this.options.display.errorsLocation) {
797				var dest = obj.getCoordinates().top-30;
798			} else if (this.alreadyIndicated.element) {
799				switch (this.options.display.errorsLocation){
800					case 1 : 
801						var dest = obj.element.getCoordinates().top;
802						break;
803					case 2 :
804						var dest = obj.element.getCoordinates().top-30;
805						break;
806					case 3 :
807						var dest = obj.getCoordinates().top-30;
808						break;
809				}
810				this.isScrolling = true;
811			}
812			if (window.getScroll.y != dest) {
813				new Fx.Scroll(window, {
814					onComplete : function() {
815						this.isScrolling = false;
816						obj.focus();
817					}.bind(this)
818				}).start(0,dest);
819			} else {
820				this.isScrolling = false;
821				obj.focus();
822			}
823			this.alreadyFocused = true;
824		}
825	},
826	
827	/*
828	Function: fixIeStuffs
829		Private method
830		
831		Fix png for IE6
832	*/
833	fixIeStuffs : function () {
834		if (Browser.Engine.trident4) {
835			//We fix png stuffs
836			var rpng = new RegExp('url\\(([\.a-zA-Z0-9_/:-]+\.png)\\)');
837			var search = new RegExp('(.+)formcheck\.css');
838			for (var i = 0; i < document.styleSheets.length; i++){
839				if (document.styleSheets[i].href.match(/formcheck\.css$/)) {
840					var root = document.styleSheets[i].href.replace(search, '$1');
841					var count = document.styleSheets[i].rules.length;
842					for (var j = 0; j < count; j++){
843						var cssstyle = document.styleSheets[i].rules[j].style;
844						var bgimage = root + cssstyle.backgroundImage.replace(rpng, '$1');
845						if (bgimage && bgimage.match(/\.png/i)){
846							var scale = (cssstyle.backgroundRepeat == 'no-repeat') ? 'crop' : 'scale';
847							cssstyle.filter =  'progid:DXImageTransform.Microsoft.AlphaImageLoader(enabled=true, src=\'' + bgimage + '\', sizingMethod=\''+ scale +'\')';
848							cssstyle.backgroundImage = "none";
849						}
850					}
851				}
852			}
853		}
854	},
855	
856	/*
857	Function: makeTips
858		Private method
859		
860		Create tips boxes
861	*/
862	makeTips : function(txt) {
863		var table = new Element('table');
864			table.cellPadding ='0';
865			table.cellSpacing ='0';
866			table.border ='0';
867			
868			var tbody = new Element('tbody').injectInside(table);
869				var tr1 = new Element('tr').injectInside(tbody);
870					new Element('td', {'class' : 'tl'}).injectInside(tr1);
871					new Element('td', {'class' : 't'}).injectInside(tr1);
872					new Element('td', {'class' : 'tr'}).injectInside(tr1);
873				var tr2 = new Element('tr').injectInside(tbody);
874					new Element('td', {'class' : 'l'}).injectInside(tr2);
875					var cont = new Element('td', {'class' : 'c'}).injectInside(tr2);
876						var errors = new Element('div', {'class' : 'err'}).injectInside(cont);
877						txt.each(function(error) {
878							error.injectInside(errors);
879						});
880						if (this.options.display.closeTipsButton) new Element('a',{'class' : 'close'}).injectInside(cont);
881					new Element('td', {'class' : 'r'}).injectInside(tr2);
882				var tr3 = new Element('tr').injectInside(tbody);
883					new Element('td', {'class' : 'bl'}).injectInside(tr3);
884					new Element('td', {'class' : 'b'}).injectInside(tr3);
885					new Element('td', {'class' : 'br'}).injectInside(tr3);			
886		return table;
887	},
888	
889	/*
890	Function: reinitialize
891		Private method		
892		
893		Reinitialize form before submit check
894	*/
895	reinitialize: function() {
896		this.validations.each(function(el) {
897			if (el.element) {
898				el.errors = [];
899				el.isOK = true;
900				if(this.options.display.flashTips == 1) {
901					el.element.destroy();
902					el.element = false;
903				}
904			}
905		}, this);
906		if (this.form.element) this.form.element.empty();
907		this.alreadyFocused = false;
908		this.firstError = false;
909		this.elementToRemove = this.alreadyIndicated;
910		this.alreadyIndicated = false;
911		this.form.isValid = true;
912	},
913	
914	/*
915	Function: submitByAjax
916		Private method		
917		
918		Send the form by ajax, and replace the form with response
919	*/
920	
921	submitByAjax: function() {
922		var url = this.form.getProperty('action');
923		this.fireEvent('ajaxRequest');
924		new Request({
925			url: url,
926			method: this.form.getProperty('method'),
927			data : this.form.toQueryString(),
928			evalScripts: this.options.ajaxEvalScripts,
929			onFailure: function(instance){
930				this.fireEvent('ajaxFailure', instance);
931			}.bind(this),
932			onSuccess: function(result){
933				this.fireEvent('ajaxSuccess', result);
934				if(this.options.ajaxResponseDiv) $(this.options.ajaxResponseDiv).set('html',result);
935			}.bind(this)
936		}).send();
937	},
938	
939	/*
940	Function: onSubmit
941		Private method		
942		
943		Perform check on submit action
944	*/
945	onSubmit: function(event) {
946		this.reinitialize();
947	
948		this.validations.each(function(el) {
949			var validation = this.manageError(el,'submit');
950			if(!validation) this.form.isValid = false;
951		}, this);
952	    
953		if (this.form.isValid) {
954			if (this.options.submitByAjax) {
955				new Event(event).stop();
956				this.submitByAjax();
957			}
958		} else {
959			new Event(event).stop();
960			if (this.elementToRemove && this.elementToRemove != this.firstError && this.options.display.indicateErrors == 1) {
961				this.removeError(this.elementToRemove);
962			}
963			this.focusOnError(this.firstError)
964		}
965	}
966});

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.