// wForms - a javascript extension to web forms.// v0.96 - May 03 2005// Copyright (c) 2005 Cédric Savarese <pro@4213miles.com>// This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>// Other Contributors: Michael Duff (fullmoondesigns.net)function wFORMS() { // wFORMS Class Constructor	var wu = new wUTILITY();	var self = this;	// CSS class name definitions. 	this.classNamePrefix_switch			= "switch";	this.classNamePrefix_offState		= "offstate";	this.classNamePrefix_onState		= "onstate";	this.className_repeat 				= "repeat";	this.className_delete 				= "removeable";	this.className_required 			= "required";	this.className_validationError_msg 	= "err-msg";		 	this.className_validationError_fld	= "err-fld";  	this.classNamePrefix_validation 	= "validate";	this.className_duplicateLink 		= "duplicateLink";	this.className_removeLink 			= "removeLink";	this.className_activeFieldHint 		= "field-hint";	this.className_inactiveFieldHint 	= "field-hint-inactive";	this.className_paging				= "wfPage";	this.className_pagingCurrent		= "wfCurrentPage";	this.className_pagingButtons		= "wfPageButton";	this.className_hideSubmit			= "wfHideSubmit";	// id attribute suffixes	this.idSuffix_fieldHint 			= "-H";	this.idSuffix_fieldLabel			= "-L";	this.idSuffix_fieldError			= "-E";	this.idSuffix_repeatCounter			= "-RC";	this.idPrefix_pageIndex				= "wfPgIndex-";	// Behavior configuration options	this.preserveRadioName				= true; 		// if true, Repeat behavior will preserve name attributes for radio input. 		// Form validation function name. May be overidden if you need to run your own validation routine (but make sure to run formValidation() in it).	this.functionName_formValidation = "this.formValidation";	this.showAlertOnError = true; // set to false to not show the alert when a validation error occurs.	this.preventSubmissionOnEnter = false; // prevent submission when pressing the 'enter' key. Set to true if pagination behavior is used.		// Error messages. This array may be overwritten in a separate js file for localization or customization purpose.	this.arrErrorMsg = new Array(); 	this.arrErrorMsg[0] = "this field is required. "; // required	this.arrErrorMsg[1] = "the text must use alphabetic characters only (a-z, A-Z). Numbers are not allowed. "; 	// validate_alpha	this.arrErrorMsg[2] = "this does not appear to be a valid email address.";									// validate_email	this.arrErrorMsg[3] = "please enter an integer.";															// validate_integer	this.arrErrorMsg[4] = "please enter a float (ex. 1.9).";	this.arrErrorMsg[5] = "Unsafe password. Your password should be between 4 and 12 characters long and use a combinaison of upper-case and lower-case letters.";	this.arrErrorMsg[6] = "please use alpha-numeric characters only [a-z 0-9].";	this.arrErrorMsg[7] = "this does not appear to be a valid date.";	this.arrErrorMsg[8] = "%% error(s) detected. your form has not been submitted yet.\n\nplease check the info you provided and try again."; // %% will be replaced by the actual number of errors.		// Other Messages	this.arrMsg = new Array();	this.arrMsg[0] = "Add a row"; 	// repeat link	this.arrMsg[1] = "Repeats the preceding field or field group." // title attribute on the repeat link 	this.arrMsg[2] = "Remove"; 		// remove link	this.arrMsg[3] = "Removes the preceding field or field group." // title attribute on the remove link	this.arrMsg[4] = "Next Page";	this.arrMsg[5] = "Previous Page";		this.utilities = wu;			// =======================================================================================	// Switch Behavior Methods	// =======================================================================================	// Privileged Instance Methods	//----------------------------	this.refreshAllStates = function(fId) {		var f=document.getElementById(fId);		if(!f) return;		// loop through the fields		var x = wu.getElements(f);		for (var i=0;i<x.length;i++) {			// add switch/state behavior			if (x[i].tagName.toUpperCase() == "SELECT" && wu.isEventHandled(x[i]) ) 				this.refreshState(null,x[i]);			if (x[i].className && x[i].className.indexOf(this.classNamePrefix_switch) != -1) {							switch(x[i].tagName.toUpperCase()) {					case "OPTION":						break;					default:						this.refreshState(null,x[i]);						break;				}			}		}	}		this.refreshState = function(e) {		if(!e && arguments.length==2)			var srcE = arguments[1];		else			var srcE = wu.getSrcElement(e);		switch(srcE.tagName.toUpperCase()) {			case "SELECT":				var selectedStateClass="";				var localScope = switchScope(srcE);				for(var i=0;i<srcE.options.length;i++) {					if(srcE.options[i].className.indexOf(self.classNamePrefix_switch) != -1 ) {						var s = srcE.options[i].className;						s = s.substr(s.indexOf(self.classNamePrefix_switch)).split(" ")[0].substr(self.classNamePrefix_switch.length);						var offStateClass = self.classNamePrefix_offState + s;						var onStateClass = self.classNamePrefix_onState + s;										if(i==srcE.selectedIndex) {												//alert("switch ON " + srcE.tagName+ ': ' + offStateClass + ' to ' + onStateClass);							switchState(localScope, offStateClass, onStateClass);							selectedStateClass = onStateClass; // prevents further switching off 						}						else if(onStateClass != selectedStateClass) {							//alert("switch OFF " + srcE.tagName+ ': ' + offStateClass + ' to ' + onStateClass);							switchState(localScope, onStateClass, offStateClass);						}					}							}				break;			case "INPUT":					if(srcE.type.toLowerCase() == 'radio') {					// Go through the radio group.					for(var i=0;i <srcE.form[srcE.name].length;i++) { 						var r = srcE.form[srcE.name][i];						if(r.className && r.className.indexOf(self.classNamePrefix_switch)!=-1) {							var s = r.className;							s = s.substr(s.indexOf(self.classNamePrefix_switch)).split(" ")[0].substr(self.classNamePrefix_switch.length);							var offStateClass = self.classNamePrefix_offState + s;							var onStateClass = self.classNamePrefix_onState + s;								if(r.checked) 								switchState(switchScope(r), offStateClass, onStateClass);							else {																switchState(switchScope(r), onStateClass, offStateClass); 							}												}					}				} else {					var s = srcE.className;					s = s.substr(s.indexOf(self.classNamePrefix_switch)).split(" ")[0].substr(self.classNamePrefix_switch.length);					var offStateClass = self.classNamePrefix_offState + s;					var onStateClass = self.classNamePrefix_onState + s;						if(srcE.checked) { // && !srcE.defaultChecked						switchState(switchScope(srcE), offStateClass, onStateClass);					}					else						switchState(switchScope(srcE), onStateClass, offStateClass); 				}				break;		}		}			// Switch Behavior Private Methods	// -------------------------------	// The switch scope limits the element tree on which the switch can operate.	// Because of interference issue, a SWITCH contained in a REPEATed block	// should not be allowed to operate outside of it.	function switchScope(n) {		while(n) {			 if (n.className && ( (' '+n.className+' ').indexOf(' '+self.className_repeat+' ') != -1 || (' '+n.className+' ').indexOf(' '+self.className_delete+' ') != -1)) 				return n;			 if (n.tagName.toUpperCase() == "FORM")				return n;			 n = n.parentNode;		}		return null; // should not happen. A form should exists.	}		// Recursive loop within the scope to switch classes	function switchState(n, oldStateClass, newStateClass) {				if(n.nodeType != 1) return;		if(n.className && n.className.indexOf(oldStateClass) != -1) {  					n.className = n.className.replace(oldStateClass, newStateClass);			// FAT support			// n.id not set.			// if(Fat && n.id) Fat.fade_element(n.id, 10, 600, "#FFFFCC", "#FFFFFF");			}		for (var i=0;i<n.childNodes.length;i++) 			switchState(n.childNodes[i], oldStateClass, newStateClass);	}	// =======================================================================================	// Repeat/Remove Behavior Methods	// =======================================================================================	// -------------------	// Priviledged Methods	// -------------------	this.duplicateFieldGroup = function(e) {		var srcE = wu.getSrcElement(e);		// Get Element to duplicate.		var sourceNode = srcE.parentNode;		while (sourceNode && (!sourceNode.className || (' '+sourceNode.className+' ').indexOf(' '+self.className_repeat+' ') == -1)) {			sourceNode = sourceNode.parentNode;		}			if (sourceNode && sourceNode.className.indexOf(self.className_repeat) != -1) {			// Extract row counter information			counterField = document.getElementById(sourceNode.id + self.idSuffix_repeatCounter);			if(!counterField) return; // should not happen.			var rowCount = parseInt(counterField.value) + 1;			// Prepare id suffix			var suffix = "-" + rowCount.toString()			// duplicate node tree 			var dupTree = replicateTree(sourceNode, null, suffix);  //  sourceNode.cloneNode(true); 			// find insert point in DOM tree (after existing repeated element)			var insertNode = sourceNode.nextSibling;			while(insertNode && insertNode.className && insertNode.className.indexOf(self.className_delete) != -1) {				insertNode = insertNode.nextSibling;			}								sourceNode.parentNode.insertBefore(dupTree,insertNode);	 // Buggy rendering in IE5/Mac			// if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1)						//						// the copy is not duplicable, it's removeable			dupTree.className = sourceNode.className.replace(self.className_repeat,self.className_delete);			// Save new row count 						document.getElementById(sourceNode.id + self.idSuffix_repeatCounter).value = rowCount;			// re-add behaviors			if(!dupTree.id) dupTree.id = wu.randomId() + suffix;  //  createAttribute()			self.addBehaviors(dupTree.id);			// FAT support			if(typeof Fat != 'undefined' && Fat && dupTree.id) Fat.fade_element(dupTree.id, 10, 600, "#FFFFCC", "#FFFFFF");			}		return wu.XBrowserPreventEventDefault(e);	}	this.removeFieldGroup = function(e) {		var srcE = wu.getSrcElement(e);	// Get Element to remove.		var delNode = srcE.parentNode;		while (delNode && (' '+delNode.className+' ').indexOf(' '+self.className_delete+' ') == -1) {			delNode = delNode.parentNode;		}		delNode.parentNode.removeChild(delNode);		return wu.XBrowserPreventEventDefault(e);	}	// Repeat Behavior Private Methods	// -------------------------------	function removeRepeatCountSuffix(str) {		return str.replace(/-\d$/,'');	}		function replicateTree(srcNode,dupParentNode, idSuffix) {		switch(srcNode.nodeType) {			case 1:	// ELEMENT-NODE				if(	srcNode.className.indexOf(self.className_duplicateLink) != -1 ||					srcNode.className.indexOf(self.className_removeLink) != -1  ) 												return null; // Exclude the 'duplicate/remove' links				// Create Element/'<br />					if(document.all && !window.opera) { 					// IE Specific : see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp					var tagHtml = srcNode.tagName;										if(srcNode.name) 											if (srcNode.tagName.toUpperCase()=="INPUT" && srcNode.type.toLowerCase()=="radio" && self.preserveRadioName)							tagHtml += " NAME='" + srcNode.name + "' ";						else							tagHtml += " NAME='" + removeRepeatCountSuffix(srcNode.name) + idSuffix + "' ";					if(srcNode.type) {						tagHtml += " TYPE='" + srcNode.type + "' ";					}					if(srcNode.selected) 						tagHtml += " SELECTED='SELECTED' ";					if(srcNode.checked)						tagHtml += " CHECKED='CHECKED' ";					if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac						var newNode = document.createElement(tagHtml);					else						var newNode = document.createElement("<" + tagHtml + "></"+ srcNode.tagName + ">"); 					try { newNode.type = srcNode.type; } catch(e) {}; // nail it down for IE5 ?, breaks in IE6 									}				else					var newNode = document.createElement(srcNode.tagName); 					// get attributes														for(var i=0; i< srcNode.attributes.length; i++) {						if(	srcNode.attributes[i].specified || // in IE, the attributes array contains all attributes in the DTD						srcNode.attributes[i].nodeName.toLowerCase() == 'value' ) { // attr.specified buggy in IE?  							if(	srcNode.attributes[i].nodeName.toLowerCase() == "id" || 							srcNode.attributes[i].nodeName.toLowerCase() == "name" ||							srcNode.attributes[i].nodeName.toLowerCase() == "for") {																					if(srcNode.attributes[i].nodeValue.indexOf(self.idSuffix_fieldHint) != -1)  {								//leave the field hint suffix at the end of the id.								var value = srcNode.attributes[i].nodeValue;								value= removeRepeatCountSuffix(value.substr(0,value.indexOf(self.idSuffix_fieldHint))) + idSuffix + self.idSuffix_fieldHint;							}							else {								if (srcNode.tagName.toUpperCase()=="INPUT" && srcNode.getAttribute('type',false).toLowerCase()=="radio" &&									srcNode.attributes[i].nodeName.toLowerCase() == "name" && self.preserveRadioName) {									var value = srcNode.attributes[i].nodeValue;														}								else {									var value = removeRepeatCountSuffix(srcNode.attributes[i].nodeValue) + idSuffix;																	}							}						} else {							// Do not copy the value attribute for text/textarea/password/file input							if(srcNode.attributes[i].nodeName.toLowerCase() == "value" &&							  (srcNode.tagName.toUpperCase()=='TEXTAREA' || 								(srcNode.tagName.toUpperCase()=='INPUT' &&  								  (srcNode.type.toLowerCase() == 'text' || srcNode.type.toLowerCase() == 'password' || srcNode.type.toLowerCase() == 'file'))))								var value='';   							else								var value = srcNode.attributes[i].nodeValue;						}						switch(srcNode.attributes[i].nodeName.toLowerCase()) {							case "class":								newNode.className = value; 								break;							case "style": // inline style attribute (fix for IE)								if(srcNode.style && srcNode.style.cssText) newNode.style.cssText = srcNode.style.cssText; 								break;															default:								newNode.setAttribute(srcNode.attributes[i].name, value, 0);//setAttribute(newNode, srcNode.attributes[i].name, value);						}					}				}								break;			case 3: // TEXT-NODE				var newNode = document.createTextNode(srcNode.data); 				break;		}		if(dupParentNode) dupParentNode.appendChild(newNode);		for(var i=0; i<srcNode.childNodes.length;i++) {			replicateTree(srcNode.childNodes[i],newNode,idSuffix);		}		return newNode;	}			// =======================================================================================	// Field Hint Behavior Methods	// =======================================================================================	this.activateFieldHint = function(e) {		var srcE = wu.getSrcElement(e);		var fh = document.getElementById(srcE.id +  self.idSuffix_fieldHint);		if(!fh && srcE.tagName.toUpperCase()=='INPUT' && srcE.type.toLowerCase() == 'radio') {			fh = document.getElementById(srcE.name + self.idSuffix_fieldHint);		}				if(fh) fh.className = fh.className.replace(self.className_inactiveFieldHint, self.className_activeFieldHint);	}	this.desactivateFieldHint = function(e) {		var srcE = wu.getSrcElement(e);		var fh = document.getElementById(srcE.id +  self.idSuffix_fieldHint);		if(!fh && srcE.tagName.toUpperCase()=='INPUT' && srcE.type.toLowerCase() == 'radio') {			fh = document.getElementById(srcE.name + self.idSuffix_fieldHint);		}				if(fh) fh.className = fh.className.replace(self.className_activeFieldHint,self.className_inactiveFieldHint);	}			// =======================================================================================	// Multi-page Forms Management	// =======================================================================================	this.pagingNext= function(e) {		var srcE = wu.getSrcElement(e);		var curPageDiv = srcE.parentNode;		var pageIndex = parseInt(curPageDiv.id.replace(/[\D]*/,"")) + 1;		var pageDiv = document.getElementById(self.idPrefix_pageIndex+pageIndex.toString());		if(pageDiv) {			if(eval(self.functionName_formValidation.replace("this","self"))(e)) {				curPageDiv.className = curPageDiv.className.replace(self.className_pagingCurrent,"");				pageDiv.className += ' ' + self.className_pagingCurrent;			}			// show submit button if the last page of the form is reached			pageIndex++;			var anotherpage = document.getElementById(self.idPrefix_pageIndex+pageIndex.toString());						if(!anotherpage) {								var submitButton = document.getElementById("submit-"+srcE.form.id);				if(submitButton) submitButton.className = submitButton.className.replace(self.className_hideSubmit,"");			}		}	}	this.pagingPrevious = function(e) {		var srcE = wu.getSrcElement(e);		var curPageDiv = srcE.parentNode;		var pageIndex = parseInt(curPageDiv.id.replace(/[\D]*/,"")) - 1;		var pageDiv = document.getElementById(self.idPrefix_pageIndex+pageIndex.toString());		if(pageDiv) {			curPageDiv.className = curPageDiv.className.replace(self.className_pagingCurrent,"");			pageDiv.className += ' ' + self.className_pagingCurrent;			// hide submit button if necessary			var submitButton = document.getElementById("submit-"+srcE.form.id);			if(submitButton && submitButton.className.indexOf(self.className_hideSubmit)==-1) submitButton.className += ' ' + self.className_hideSubmit;		}	}		// =======================================================================================	// FORM VALIDATION 	// =======================================================================================	this.formValidation = function(e) {				var srcE = wu.getSrcElement(e);				if(self.preventSubmissionOnEnter) { 			// prevent form submission if 'enter' key is pressed			// (doesn't work in Opera. Further tests needed in IE and Safari)			if(!e) e = window.event;						if(srcE.type && srcE.type.toLowerCase()=='text')				return wu.XBrowserPreventEventDefault(e); // works w/ Firefox.		}				while (srcE && srcE.tagName.toUpperCase() != 'FORM') {			srcE = srcE.parentNode;		}						var x = wu.getElements(srcE); //srcE.elements;  		var nbTotalErrors = 0;		var isVisible = false;				for (var i=0;i<x.length;i++) {			var nbErrors = 0;									if ((' '+x[i].className+' ').indexOf(' '+self.className_required+' ') != -1) {								if(wu.checkVisibility(x[i])) {					isVisible = true;					var v = true; // is Valid									switch(x[i].tagName.toUpperCase()) {						case "INPUT":							switch(x[i].getAttribute("type").toUpperCase()) {								case "CHECKBOX":									v = x[i].checked; 									break;								case "RADIO":									v = x[i].checked; 									break;								default:									v = !self.isEmpty(x[i].value);							}							break;						case "SELECT":							v = !self.isEmpty(x[i].options[x[i].selectedIndex].value);							break;						case "TEXTAREA":							v = !self.isEmpty(x[i].value);							break;						case "FIELDSET":							v = checkOneRequired(x[i]);							break;						case "DIV":							v = checkOneRequired(x[i]);							break;						case "SPAN":							v = checkOneRequired(x[i]);							break;					} // end switch					if(!v) {						// flag error						self.showError(x[i],self.arrErrorMsg[0]);						nbErrors++;					}  else { // remove required error flag if any.						var rErrClass = new RegExp(self.className_validationError_fld,"gi");						x[i].className = x[i].className.replace(rErrClass,"");						var fe = document.getElementById(x[i].id +  self.idSuffix_fieldError);						if(fe) fe.parentNode.removeChild(fe);					} 				} else { // not visible				}			} // end test=required			// input validation			if (x[i].className.indexOf(self.classNamePrefix_validation) != -1) {				if(!isVisible) isVisible = wu.checkVisibility(x[i]);				if(isVisible) {					var arrClasses = x[i].className.split(" ");					for (j=0;j<arrClasses.length;j++) {						switch(arrClasses[j]) {							case "validate-alpha":								if(!self.isAlpha(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[1]);									nbErrors++;								}								break;							case "validate-alphanum":								if(!self.isAlphaNum(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[6]);									nbErrors++;								}								break;							case "validate-date":								if(!self.isDate(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[7]);									nbErrors++;								}								break;							case "validate-time":								/* NOT IMPLEMENTED */								break;							case "validate-email":								if(!self.isEmail(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[2]);									nbErrors++;								}								break;							case "validate-integer":								if(!self.isInteger(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[3]);									nbErrors++;								}													break;							case "validate-float":								if(!self.isFloat(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[4]);									nbErrors++;								}								break;							case "validate-strongpassword": // NOT IMPLEMENTED								if(!self.isPassword(x[i].value)) {									self.showError(x[i],self.arrErrorMsg[5]);									nbErrors++;								}								break;						} // end switch					} // end for				}  else { // not visible				}			} // end validation check							if(nbErrors>0) {				nbTotalErrors+= nbErrors;			} else {				var rErrClass = new RegExp(self.className_validationError_fld,"gi");				x[i].className = x[i].className.replace(rErrClass,"");				var fe = document.getElementById(x[i].id +  self.idSuffix_fieldError);				if(fe) fe.parentNode.removeChild(fe);			} 		}		if (nbTotalErrors > 0) {			if(self.showAlertOnError){  self.showAlert(nbTotalErrors); }			return wu.XBrowserPreventEventDefault(e);		}		return true;	}	this.isEmpty = function(s) {		var regexpWhitespace = /^\s+$/;		return  ((s == null) || (s.length == 0) || regexpWhitespace.test(s));	}	this.isAlpha = function(s) {		var regexpAlphabetic = /^[a-zA-Z]+$/; // Add ' and - ?		return self.isEmpty(s) || regexpAlphabetic.test(s);	}	this.isAlphaNum = function(s) {		var illegalChars = /\W/;		return self.isEmpty(s) || !illegalChars.test(s);	}	this.isDate = function(s) {		var testDate = new Date(s);		return self.isEmpty(s) || !isNaN(testDate);	}	this.isEmail = function(s) {		var regexpEmail = /\w{1,}[@]\w{1,}([.](\w{1,})){1,3}$/;		return self.isEmpty(s) || regexpEmail.test(s);	}	this.isInteger =function(s) {		var regexp = /^[+]?\d+$/;		return self.isEmpty(s) || regexp.test(s);	}	this.isFloat = function(s) {				return self.isEmpty(s) || !isNaN(parseFloat(s));	}	// NOT IMPLEMENTED	this.isPassword = function(s) {		// Matches strong password : at least 1 upper case latter, one lower case letter. 4 characters minimum. 12 max.		//var regexp = /^(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{4,12}$/;  // <= breaks in IE5/Mac		return self.isEmpty(s);	}	this.showError = function (n,errorMsg) {				if(n.className.indexOf(self.className_validationError_fld)!= -1) {			return;		}		if (!n.id) n.id = wu.randomId(); // we'll need an id here.				// Add error flag to the field		n.className += " " + self.className_validationError_fld;		// Prepare error message		var msgNode = document.createTextNode(" " + errorMsg);		// Find error message placeholder.		var fe = document.getElementById(n.id +  self.idSuffix_fieldError);		if(!fe) { // create placeholder.			fe = document.createElement("div"); 			fe.setAttribute('id', n.id +  self.idSuffix_fieldError);						// attach the error message after the field label if possible			var fl = document.getElementById(n.id +  self.idSuffix_fieldLabel);			if(fl)				fl.parentNode.insertBefore(fe,fl.nextSibling);			else				// otherwise, attach it after the field tag.				n.parentNode.insertBefore(fe,n.nextSibling);		}		// Finish the error message.		fe.appendChild(msgNode);  			fe.className += " " + self.className_validationError_msg;	}	this.showAlert = function (nbTotalErrors) { 	   alert(self.arrErrorMsg[8].replace('%%',nbTotalErrors));	}	// Validation Private Method	// -------------------------	function checkOneRequired(n) {			var v=null;		if(n.nodeType != 1) return false;		if(n.tagName.toUpperCase() == "INPUT") {			switch(n.type.toLowerCase()) {				case "checkbox":					v = n.checked; 					break;				case "radio":					v = n.checked; 					break;				default:					v = n.getAttribute("value");			}		} else v = n.getAttribute("value");		if(v && !self.isEmpty(v)) {			return true;		}		for(var i=0; i<n.childNodes.length;i++) {			if(checkOneRequired(n.childNodes[i])) return true;		}		return false;	}}//------------------------------------------------------------------------------------------------------------------------// WFORMS Public Methods//------------------------------------------------------------------------------------------------------------------------wFORMS.prototype.onLoadHandler = function() {			for (var i=0;i<document.forms.length;i++) {		if(!document.forms[i].id) document.forms[i].id = this.utilities.randomId();		this.addBehaviors(document.forms[i].id);	}}wFORMS.prototype.addBehaviors = function (fId) {	var f=document.getElementById(fId);	if(!f) return;	var thisForm; 				// Pointer to keep track of the current form being processed.	var wu = this.utilities;	// Utiltiy class instance	wu.resetEventList();		// loop through the fields	var x = wu.getElements(f);		for (var i=0;i<x.length;i++) {		// add form validation behavior		if(x[i].tagName.toUpperCase()=="FORM") {						wu.XBrowserAddHandler(x[i],'submit',eval(this.functionName_formValidation));			thisForm = x[i];	// Pointer to keep track of the current form being processed.		}		// add fieldhint behavior		var fh = document.getElementById(x[i].id + this.idSuffix_fieldHint);					if(!fh && x[i].tagName.toUpperCase()=='INPUT' && x[i].type.toLowerCase() == 'radio') {			fh = document.getElementById(x[i].name + this.idSuffix_fieldHint);		}				if(fh) {					wu.XBrowserAddHandler(x[i],'focus',this.activateFieldHint);			wu.XBrowserAddHandler(x[i],'blur',this.desactivateFieldHint);					}		// add switch/state behavior		if (x[i].className && x[i].className.indexOf(this.classNamePrefix_switch) != -1) {			switch(x[i].tagName.toUpperCase()) {				case "OPTION":					var sel = x[i].parentNode;	// Get to the SELECT					if(sel.tagName.toUpperCase() == "OPTGROUP") sel = sel.parentNode; // try again.					if(!wu.isEventHandled(sel)) {						wu.XBrowserAddHandler(sel,'change',this.refreshState);				   	}					break;				case "INPUT":					if(x[i].type && x[i].type.toLowerCase() == 'radio') {						// Add the onclick event on radio inputs of the same group						for (var j=0;j<thisForm[x[i].name].length;j++) {							wu.XBrowserAddHandler(thisForm[x[i].name][j],'click',this.refreshState);						}					} else						wu.XBrowserAddHandler(x[i],'click',this.refreshState);					break;				default:										wu.XBrowserAddHandler(x[i],'click',this.refreshState);					break;			}		}		// add paging behavior		if (x[i].className && (' '+x[i].className+' ').indexOf(' '+ this.className_paging+' ') != -1) {			var currentPageIndex = parseInt(x[i].id.replace(/[\D]*/,""));			if(currentPageIndex > 1) {				// add previous page button							var actionNode = document.createElement("input"); 				actionNode.setAttribute('value',this.arrMsg[5]);					actionNode.setAttribute('type',"button");					actionNode.className = this.className_pagingButtons;				x[i].appendChild(actionNode);				// Add event handler							wu.XBrowserAddHandler(actionNode,'click',this.pagingPrevious);						} else {				// set current page class				x[i].className += ' ' + this.className_pagingCurrent;				// hide submit button until the last page of the form is reached				var submitButton = document.getElementById("submit-"+thisForm.id);				if(submitButton) submitButton.className = this.className_hideSubmit; 				// prevent submission of form with enter key.				this.preventSubmissionOnEnter = true;			}			if(document.getElementById(this.idPrefix_pageIndex+(currentPageIndex+1).toString())) {				// add next page button							var actionNode = document.createElement("input"); 				actionNode.setAttribute('value',this.arrMsg[4]);					actionNode.setAttribute('type',"button");					actionNode.className = this.className_pagingButtons;				x[i].appendChild(actionNode);				// Add event handler							wu.XBrowserAddHandler(actionNode,'click',this.pagingNext);						}		}				// add repeat behavior		if (x[i].className && (' '+x[i].className+' ').indexOf(' '+this.className_repeat+' ') != -1) {			// this element to be duplicated.			// add duplicate action			var actionNode = document.createElement("a"); 			var textNode = document.createTextNode(this.arrMsg[0]);			actionNode.setAttribute('href',"#");				actionNode.className = this.className_duplicateLink;			actionNode.setAttribute('title', this.arrMsg[1]);				if(x[i].tagName.toUpperCase()=="TR") {				// find the last TD				var n = x[i].lastChild;					while(n && n.nodeType != 1)  					n = n.previousSibling;				if(n && n.nodeType == 1) 					n.appendChild(actionNode);				// Else Couldn't find the TD. Table row malformed ?			} else				x[i].appendChild(actionNode);			actionNode.appendChild(textNode); 						// Add hidden counter field if necessary			var counterField = document.getElementById(x[i].id + this.idSuffix_repeatCounter);			if (!counterField) {				if(document.all && !window.opera) { // IE Specific :-(					// see http://msdn.microsoft.com/workshop/author/dhtml/reference/properties/name_2.asp					var counterFieldId = x[i].id + this.idSuffix_repeatCounter;					if(navigator.appVersion.indexOf("MSIE") != -1 && navigator.appVersion.indexOf("Windows") == -1) // IE5 Mac						counterField = document.createElement("INPUT NAME=\"" + counterFieldId + "\"");					else						counterField = document.createElement("<INPUT NAME=\"" + counterFieldId + "\"></INPUT>"); 										counterField.type='hidden';					counterField.id = counterFieldId; 					counterField.value = "1";				}				else {					counterField = document.createElement("INPUT"); 					counterField.setAttribute('type','hidden'); // hidden					counterField.setAttribute('value','1');					counterField.setAttribute('name', x[i].id + this.idSuffix_repeatCounter);					counterField.setAttribute('id', x[i].id + this.idSuffix_repeatCounter); 				}				thisForm.appendChild(counterField);			}			// Add event handler						wu.XBrowserAddHandler(actionNode,'click',this.duplicateFieldGroup);					}			// add remove behavior		if (x[i].className && (' '+x[i].className+' ').indexOf(' '+this.className_delete+' ') != -1) {			// this element can be removed			// add remove action			var actionNode = document.createElement("a"); 			var textNode = document.createTextNode(this.arrMsg[2]);			actionNode.setAttribute('href',"#");				actionNode.className = this.className_removeLink;			actionNode.setAttribute('title',this.arrMsg[3]);				if(x[i].tagName.toUpperCase()=="TR") {				// find the last TD				var n = x[i].lastChild;					while(n && n.nodeType != 1)  					n = n.previousSibling;				if(n && n.nodeType == 1) 					n.appendChild(actionNode);				// Else Couldn't find the TD. Table row malformed ?			} else				x[i].appendChild(actionNode);			actionNode.appendChild(textNode); 						wu.XBrowserAddHandler(actionNode,'click',this.removeFieldGroup);					}		 }	 this.refreshAllStates(fId);}// *************************************************************************************************************// UTILITY CLASS// *************************************************************************************************************function wUTILITY() {	// Event Handler utility list	this.handlerList = new Array(); }// Cross-Browser event handler management.// adapted from Andy Smith's (http://weblogs.asp.net/asmith/archive/2003/10/06/30744.aspx)wUTILITY.prototype.XBrowserAddHandler = function (target,eventName,handlerName) {	if(!target) return;	if (target.addEventListener) { 		target.addEventListener(eventName, function(e){eval(handlerName)(e);}, false);	} else if (target.attachEvent) { 		target.attachEvent("on" + eventName, function(e){eval(handlerName)(e);});		} else { 		// THIS CODE NOT TESTED 		var originalHandler = target["on" + eventName]; 		if (originalHandler) { 		  target["on" + eventName] = function(e){originalHandler(e);eval(handlerName)(e);}; 		} else { 		  target["on" + eventName] = eval(handlerName); 		} 	} 	// Keep track of added handlers.	this.handlerList[this.handlerList.length] = target;  }//wUTILITY.prototype.isEventHandled = function(n) {	for(var i=0; i < this.handlerList.length; i++) {		if(this.handlerList[i].id==n.id)			return true;	}	return false;}wUTILITY.prototype.resetEventList = function() {	this.handlerList = new Array(); }// Activating an Alternate Stylesheet (thx to: http://www.howtocreate.co.uk/tutorials/index.php?tut=0&part=27)// Use this to activate a CSS Stylesheet that shouldn't be used if javascript is turned off.// The stylesheet rel attribute should be 'alternate stylesheet'. The title attribute should be set.wUTILITY.prototype.activateStylesheet = function(sheetref) {	if(document.getElementsByTagName) {		var ss=document.getElementsByTagName('link');	} else if (document.styleSheets) {		var ss = document.styleSheets;	}	for(var i=0;ss[i];i++ ) {		if(ss[i].href.indexOf(sheetref) != -1) {			ss[i].disabled = true;			ss[i].disabled = false;					}	}}// Generates a random IDwUTILITY.prototype.randomId = function () {	var rId = "";	for (var i=0; i<6;i++)		rId += String.fromCharCode(97 + Math.floor((Math.random()*24)))	return rId;}// returns all child elements of a node.wUTILITY.prototype.getElements = function(n, list) {	if(!list) list = new Array();	if(n.nodeType==1) {		list[list.length]= n;		for(var i=0; i<n.childNodes.length;i++) 			this.getElements(n.childNodes[i], list);		return list;	}}// Returns the event's source element wUTILITY.prototype.getSrcElement = function(e) {		if(!e) 		e = window.event;		if(e.target)		var srcE = e.target;	else		var srcE = e.srcElement;	if(srcE.nodeType == 3) srcE = srcE.parentNode; // safari weirdness			if(srcE.tagName.toUpperCase()=='LABEL') { 		// when clicking a label, firefox fires the input onclick event		// but the label remains the source of the event. In Opera and IE 		// the source of the event is the input element. Which is the 		// expected behavior, I suppose.				if(srcE.getAttribute('for')) {			srcE = document.getElementById(srcE.getAttribute('for'));		}	}	return srcE;}// Cancel the default execution of an event.wUTILITY.prototype.XBrowserPreventEventDefault = function(e) {	if(!e) e = window.event;	if (e.preventDefault) e.preventDefault();	else e.returnValue = false;	return false;}wUTILITY.prototype.checkVisibility = function(n) {	// check if any of the element's ancestors is not visible.	if(window.getComputedStyle) {		var isVisible = window.getComputedStyle(n,"").getPropertyValue("display").toLowerCase()!="none";		isVisible = isVisible && window.getComputedStyle(n,"").getPropertyValue("visibility").toLowerCase()!="hidden";		// add visiblity!=collapse ?	}	else if(n.currentStyle) {				if(n.currentStyle.display=='') return false; // effectively disable validation on IE5/Mac.		var isVisible = n.currentStyle.display.toLowerCase() != "none";		isVisible = isVisible && n.currentStyle.visibility.toLowerCase() !="hidden";	}	else {		return false; // effectively disable validation for Safari		// try to focus or select...		// This code not finalized (doesn't work for Safari).		try {							if(n.focus) alert(n.focus());				if(n.select) alert(n.select());			}  		catch (e) {				// focus failed. Either not a field, or not visible.				alert("not a visible field. " + e);				return false;			}					return true; // focus ok. The field is definitively visible.	}	if(!n.parentNode) { return false; } ; // should not happen, unless we checking some removed elements.	if (n.parentNode.tagName.toUpperCase()=="BODY" || !isVisible)		return isVisible;	return this.checkVisibility(n.parentNode);}// =======================================================================================================================// LET's GOvar wf = new wFORMS();// Attach JS only stylesheet.wf.utilities.activateStylesheet('wforms-jsonly.css'); // onLoad event handlerwf.utilities.XBrowserAddHandler(window,'load',function() { wf.onLoadHandler();}  );	
