// wForms - a javascript extension to web forms.
// see http://www.formassembly.com/wForms
// This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>

function wHELPERS() {};

// addEvent adapated from http://ejohn.org/projects/flexible-javascript-events/
// and  Andy Smith's (http://weblogs.asp.net/asmith/archive/2003/10/06/30744.aspx)
wHELPERS.prototype.addEvent = function(obj, type, fn) {
	if(!obj) { return; }
	
	if (obj.attachEvent) {
		obj['e'+type+fn] = fn;
		obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
		obj.attachEvent( 'on'+type, obj[type+fn] );
	} else if(obj.addEventListener) {			
		obj.addEventListener( type,fn, false );
	} else {
		var originalHandler = obj["on" + type]; 
		if (originalHandler) { 
		  obj["on" + type] = function(e){originalHandler(e);fn(e);}; 
		} else { 
		  obj["on" + type] = fn; 
		} 
	}
}

wHELPERS.prototype.removeEvent = function(obj, type, fn) {
	if (obj.detachEvent) {
		if(obj[type+fn]) {
			obj.detachEvent( 'on'+type, obj[type+fn] );
			obj[type+fn] = null;
		}
	} else if(obj.removeEventListener)
		obj.removeEventListener( type, fn, false );
	else {
		obj["on" + type] = null;
	}
}

// Returns the event's source element 
wHELPERS.prototype.getSourceElement = function(e) {	
	if(!e) e = window.event;	
	if(e.target)
		var srcE = e.target;
	else
		var srcE = e.srcElement;
	if(!srcE) return null;
	if(srcE.nodeType == 3) srcE = srcE.parentNode; // safari weirdness		
	if(srcE.tagName.toUpperCase()=='LABEL' && e.type=='click') { 
		// 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.
wHELPERS.prototype.preventEvent = function(e) {
	if (!e) e = window.event;
	if (e.preventDefault) e.preventDefault();
	else e.returnValue = false;
	return false;
}

// Cancel the propagation of the event
wHELPERS.prototype.stopPropagation = function(e) {
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();
}

// Generates a random ID
wHELPERS.prototype.randomId = function () {
	var seed = (new Date()).getTime();
	seed = seed.toString().substr(6);
	for (var i=0; i<6;i++)
		seed += String.fromCharCode(48 + Math.floor((Math.random()*10)));
	return "id-" + seed;
}

// 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 MUST be set.
wHELPERS.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;			
		}
	}
}

// hasClass
wHELPERS.prototype.hasClass = function(element,className) {
	if(element && element.className) {
		if((' ' + element.className + ' ').indexOf(' ' + className +' ') != -1) {
			return true;
		}
	}
	return false;
}
wHELPERS.prototype.hasClassPrefix = function(element,className) {
	if(element && element.className) {
		if((' ' + element.className).indexOf(' ' + className) != -1) {
			return true;
		}
	}
	return false;
}
	wHELPERS.prototype.hasIdPrefix = function(element,idPrefix) {
		if(element && element.id) {
			if(element.id.indexOf(idPrefix) != -1) {
				return true;
			}
		}
		return false;
	}


// getTop / getLeft  
// Returns pixel coordinates from the top-left window corner.
wHELPERS.prototype.getTop = function(obj) {
	var cur = 0;
	if(obj.offsetParent) {		
		while(obj.offsetParent) {
			if((new wHELPERS()).getComputedStyle(obj,'position') == 'relative' ) {
				// relatively postioned element
				return cur;
			}
			cur+=obj.offsetTop;
			obj = obj.offsetParent;
		}
	}
	return cur;
}
wHELPERS.prototype.getLeft = function(obj) {
	var cur = 0;
	if(obj.offsetParent) {		
		while(obj.offsetParent) {
			if((new wHELPERS()).getComputedStyle(obj,'position') == 'relative' ) {
				// relatively postioned element
				return cur;
			}
			cur+=obj.offsetLeft;
			obj = obj.offsetParent;
		}
	}
	return cur;
}

wHELPERS.prototype.getComputedStyle = function(element, styleName) {
	if(window.getComputedStyle) {
		return window.getComputedStyle(element,"").getPropertyValue(styleName);
	} else if(element.currentStyle) {	
		return element.currentStyle[styleName];
	}
	return false;
}
// backward compatibility
	var wHelpers = wHELPERS;   


   /* 
	* MISC FUNCTIONS 
   /* ------------------------------------------------------------------------------------------ */

// Push implementation for IE5/mac
if (!Array.prototype.push) { 
	Array.prototype.push = function() { 
		for (var i = 0; i < arguments.length; ++i) { 
			this[this.length] = arguments[i]; 
		} 
		return this.length; 
	}; 
}


  if(wHELPERS) {
	  var wFORMS = { 
	  
	  debugLevel     : 0, /* 0: Inactive, 1+: Debug Level */
	  
	  helpers        : new wHELPERS(),     
	  behaviors      : {},
	  onLoadComplete : new Array(),  /* stack of functions to call once all behaviors have been applied */
	  processedForm  : null,
	  
	  onLoadHandler  : function() {
		  for(var behaviorName in  wFORMS.behaviors) {
			   wFORMS.debug('wForms/loaded behavior: ' + behaviorName);
		  }
		 
		  for (var i=0;i<document.forms.length;i++) {
			  wFORMS.debug('wForms/initialize: '+ (document.forms[i].name || document.forms[i].id) );
			  	wFORMS.processedForm = document.forms[i];
			  wFORMS.addBehaviors(document.forms[i]);
		  }
	  },
	  
	  addBehaviors : function (node) {
		 if(!node) return;
		 
		 var deep = arguments[1]?arguments[1]:true;
		 if(!node.nodeType) {
			 // argument is not a node. probably an id string. 
			 // (typeof not used for IE5/mac compatibility)
			 node = document.getElementById(node);
		 }
			if(!node || node.nodeType!=1) return;
			
			deep=(arguments.length>1)?arguments[1]:true;	
				   
			wFORMS._addBehaviors(node, deep);					
		  },
		  
	  _addBehaviors : function (node, deep) {
		  if(node.getAttribute('rel')=='no-behavior') {
		  	return false;
		  }
		
		 // Process element nodes only
		 if(node.nodeType == 1) { 
			  for(var behaviorName in wFORMS.behaviors) {
				  wFORMS.behaviors[behaviorName].evaluate(node);
			  }
			 
			  if(deep) {
				  for (var i=0, l=node.childNodes.length, cn=node.childNodes; i<l; i++) {
				  	 if(cn[i].nodeType==1)
					 	wFORMS._addBehaviors(cn[i], deep);
				  }
			  }
			  
			  if(node.tagName.toUpperCase() == 'FORM') {
				  // wFORMS.debug('wForms/processed: ' + node.id);
				  // run the init stack
				  for (var i=0;i<wFORMS.onLoadComplete.length;i++) {
					  wFORMS.onLoadComplete[i]();
				  }
				  // empty the stack					  
				  if(wFORMS.onLoadComplete.length > 0) {
					  wFORMS.onLoadComplete = new Array();
				  }
			  }
		  }
	  },
	  
	  hasBehavior: function(behaviorName) {
		  if(wFORMS.behaviors[behaviorName]) return true;
		  return false;
	  },
	  
	  /* 
	   * DEBUG FUNCTIONS 
	   * ------------------------------------------------------------------------------------------ */
	  debug : function(txt) { 
		msgLevel = arguments[1] || 10; 	// 1 = least importance, X = most important
		
		if(wFORMS.debugLevel > 0 && msgLevel >= wFORMS.debugLevel) {
			if(!wFORMS.debugOutput)
				wFORMS.initDebug();
			if(wFORMS.debugOutput)
				wFORMS.debugOutput.innerHTML += "<br />" + txt;
		}
	  },
	  
	  initDebug : function() {
		var output = document.getElementById('debugOutput');
		if(!output) {
			output = document.createElement('div');
			output.id = 'debugOutput';
			output.style.position   = 'absolute';
			output.style.right      = '10px';
			output.style.top        = '10px';
			output.style.zIndex     = '300';
			output.style.fontSize   = 'x-small';
			output.style.fontFamily = 'courier';
			output.style.backgroundColor = '#DDD';
			output.style.padding    = '5px';
			if(document.body) // if page fully loaded
				wFORMS.debugOutput = document.body.appendChild(output);
		}
		if(wFORMS.debugOutput)
			wFORMS.debugOutput.ondblclick = function() { this.innerHTML = '' };
	}
  };
  
  wFORMS.NAME     = "wForms";
  wFORMS.VERSION  = "2.0";
  wFORMS.__repr__ = function () {
	return "[" + this.NAME + " " + this.VERSION + "]";
  };
  wFORMS.toString = function () {
	return this.__repr__();
  };
 
 
  // For backward compatibility
  wFORMS.utilities = wFORMS.helpers;
  var wf           = wFORMS; 
  wf.utilities.getSrcElement				= wFORMS.helpers.getSourceElement;
  wf.utilities.XBrowserPreventEventDefault	= wFORMS.helpers.preventEvent;
  
  // Initializations:
  
  // Attach JS only stylesheet.
  wFORMS.helpers.activateStylesheet('wforms-jsonly.css');
  // Parse document and apply wForms behavior
  wFORMS.helpers.addEvent(window,'load',wFORMS.onLoadHandler);
  } 



// ------------------------------------------------------------------------------------------
// Form Validation Behavior
// ------------------------------------------------------------------------------------------
  
   if(wFORMS) {
		// Component properties 
		// wFORMS.functionName_formValidation  is defined at the bottom of this file
		// Those should be moved inside wFORMS.behaviors['validation']. Stays here for now for backward compatibility
       	wFORMS.preventSubmissionOnEnter   		= false; 			// prevents submission when pressing the 'enter' key. Set to true if pagination behavior is used.
	   	wFORMS.showAlertOnError 			  	= true; 			// sets to false to not show the alert when a validation error occurs.
		wFORMS.className_required 			 	= "required";
		wFORMS.className_validationError_msg 	= "errMsg";		 
		wFORMS.className_validationError_fld	= "errFld";  
		wFORMS.classNamePrefix_validation 		= "validate";	
		wFORMS.idSuffix_fieldError				= "-E";

		wFORMS.behaviors['validation'] = {
			
			// Error messages. This may be overwritten in a separate js file for localization or customization purposes.			
			errMsg_required 	: "This field is required. ",
			errMsg_alpha 		: "The text must use alphabetic characters only (a-z, A-Z). Numbers are not allowed.",
			errMsg_email 		: "This does not appear to be a valid email address.",
			errMsg_integer 		: "Please enter an integer.",
			errMsg_float 		: "Please enter a number (ex. 1.9).",
			errMsg_password 	: "Unsafe password. Your password should be between 4 and 12 characters long and use a combinaison of upper-case and lower-case letters.",
			errMsg_alphanum 	: "Please use alpha-numeric characters only [a-z 0-9].",
			errMsg_date 		: "This does not appear to be a valid date.",
			errMsg_notification : "%% error(s) detected. Your form has not been submitted yet.\nPlease check the information you provided.",  // %% will be replaced by the actual number of errors.
			errMsg_custom		: "Please enter a valid value.",
			
			// Class Names
			className_allRequired : "allrequired",
			
			// first page w/ error in a multi-page form
			jumpToErrorOnPage : null,
			currentPageIndex  : -1,
			
			// do not submit fields turned off by switch behavior
			submitSwitchedOffFields : false,
			switchedOffFields : [],
			
		   // ------------------------------------------------------------------------------------------
		   // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
		   // ------------------------------------------------------------------------------------------
			evaluate: function(node) {
               if(node.tagName.toUpperCase()=="FORM") {
				   // functionName_formValidation can be a reference to a function, or a string with the name of the function.
				   // avoid using typeof
				   if(wFORMS.functionName_formValidation.toString()==wFORMS.functionName_formValidation) {
					   // this is a string, not a function
					   wFORMS.functionName_formValidation = eval(wFORMS.functionName_formValidation);
				   }
                   wFORMS.helpers.addEvent(node,'submit',wFORMS.functionName_formValidation);
				   //wFORMS.debug('validation/evaluate: FORM '+ node.id,3);
               }
           },
		   // ------------------------------------------------------------------------------------------
           // init: executed once evaluate has been applied to all elements
		   // ------------------------------------------------------------------------------------------	   
		   init: function() {
		   },
		   
		   // ------------------------------------------------------------------------------------------
           // run: executed when the behavior is activated
		   // ------------------------------------------------------------------------------------------	   		   
           run: function(e) {
				var element  = wFORMS.helpers.getSourceElement(e);
				if(!element) element = e;
				//wFORMS.debug('validation/run: ' + element.id , 5);	
				
				var currentPageOnly = arguments.length>1 ? arguments[1]:false;
				// arguments[1] : (wFORMS.hasBehavior('paging') && wFORMS.behaviors['paging'].behaviorInUse);

				wFORMS.behaviors['validation'].switchedOffFields = [];
				wFORMS.behaviors['validation'].jumpToErrorOnPage = null;

				// on multi-page forms we need to prevent the submission when the 'enter' key is pressed
				// (doesn't work in Opera. Further tests needed in IE and Safari)
				if(wFORMS.preventSubmissionOnEnter) { 
					if(element.type && element.type.toLowerCase()=='text') 
						// source element is a text field, the form was submitted with the 'enter' key.
						return wFORMS.preventEvent(e); 
				}
				// make sure we have the form element
				while (element && element.tagName.toUpperCase() != 'FORM') {
					element = element.parentNode;
				}		
				
				var nbErrors = wFORMS.behaviors['validation'].validateElement(element, currentPageOnly, true);
				
				// save the value in a property if someone else needs it.
				wFORMS.behaviors['validation'].errorCount = nbErrors;
				
				if (nbErrors > 0) {					
					if(wFORMS.behaviors['validation'].jumpToErrorOnPage) {					
						wFORMS.behaviors['paging'].gotoPage(wFORMS.behaviors['validation'].jumpToErrorOnPage);
					}
					if(wFORMS.showAlertOnError){ wFORMS.behaviors['validation'].showAlert(nbErrors); }
					return wFORMS.helpers.preventEvent(e); 
				}

				// Remove switched-off content if any
				// Note: in multi-page behavior the validation is run on "page next" without submitting the form.
				//       In this situation (currentPageOnly==true) switched-off conditionals should not be removed. 
				if(!wFORMS.behaviors['validation'].submitSwitchedOffFields && !currentPageOnly) {
					for(var i=0; i < wFORMS.behaviors['validation'].switchedOffFields.length; i++) {
						var element = wFORMS.behaviors['validation'].switchedOffFields[i];
						while(element.childNodes[0]) 
							element.removeChild(element.childNodes[0]);
					}
				}				
				return true;
			},
		   
			// ------------------------------------------------------------------------------------------
			// remove: executed if the behavior should not be applied anymore
			// ------------------------------------------------------------------------------------------
			remove: function() {
			},
		   
		   
			// ------------------------------------------------------------------------------------------
			// validation functions
			// ------------------------------------------------------------------------------------------
			validateElement: function(element /*, currentPageOnly, deep */) {

				var deep = arguments.length>2 ? arguments[2] : true;
				
				// used in multi-page forms
				var currentPageOnly = arguments[1] ? arguments[1] : false;				
				
				var wBehavior = wFORMS.behaviors['validation'];		// shortcut
				
				// do not validate elements that are in a OFF-Switch
				// Note: what happens if an element is the target of 2+ switches, some ON and some OFF ?
				if(wFORMS.hasBehavior('switch') && wFORMS.helpers.hasClassPrefix(element,wFORMS.classNamePrefix_offState)) {
					if(!wBehavior.submitSwitchedOffFields) {
						wBehavior.switchedOffFields.push(element);
					}
					return 0;
				}
				// do not validate elements that are not in the current page (Paging Behavior)
				if(wFORMS.hasBehavior('paging') && wFORMS.helpers.hasClass(element,wFORMS.className_paging)) {
					if(!wFORMS.helpers.hasClass(element,wFORMS.className_pagingCurrent) && currentPageOnly)
						return 0;
					wBehavior.currentPageIndex = wFORMS.behaviors['paging'].getPageIndex(element);
				}
				
				var nbErrors = 0;
				
				// check if required
				if(!wBehavior.checkRequired(element)) {
					wBehavior.showError(element, wBehavior.errMsg_required);
					nbErrors++;
					//wFORMS.debug('validation/error: [required]' + element.id + '('+nbErrors+')' , 5);
				} else {
				
					// input format validation
					if (wFORMS.helpers.hasClassPrefix(element,wFORMS.classNamePrefix_validation)) {
		
						var arrClasses = element.className.split(" ");
						for (j=0;j<arrClasses.length;j++) {
							switch(arrClasses[j]) {
								case "validate-alpha":
									if(!wBehavior.isAlpha(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_alpha);
										nbErrors++;
									}
									break;
								case "validate-alphanum":
									if(!wBehavior.isAlphaNum(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_alphanum);
										nbErrors++;
									}
									break;
								case "validate-date":
									if(!wBehavior.isDate(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_date);
										nbErrors++;
									}
									break;
								case "validate-time":
									/* NOT IMPLEMENTED */
									break;
								case "validate-email":
									if(!wBehavior.isEmail(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_email);
										nbErrors++;
									}
									break;
								case "validate-integer":
									if(!wBehavior.isInteger(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_integer);
										nbErrors++;
									}					
									break;
								case "validate-float":
									if(!wBehavior.isFloat(element.value)) {
										wBehavior.showError(element,wBehavior.errMsg_float);
										nbErrors++;
									}
									break;
								case "validate-strongpassword": // NOT IMPLEMENTED
									if(!wBehavior.isPassword(element.value)) {
										wBehavior.showError(element, wBehavior.errMsg_password);
										nbErrors++;
									}
									break;
								case "validate-custom": 
									var pattern = new RegExp("\/([^\/]*)\/([gi]*)");
									var matches = element.className.match(pattern);
									if(matches[0]) {										
										var validationPattern = new RegExp(matches[1],matches[2]);
										if(!element.value.match(validationPattern)) {
											wBehavior.showError(element, wBehavior.errMsg_custom);
											nbErrors++;											
										}
									}															
									break;									
							} // end switch
						} // end for
					}
				} // end validation check
				
				// remove previous error flags if any.
				if(nbErrors==0) {
					wBehavior.removeErrorMessage(element);
				} else {
					// flag the first page with an error (if multi-page form)
					if(wBehavior.currentPageIndex>0 && !wBehavior.jumpToErrorOnPage) {
						wBehavior.jumpToErrorOnPage = wBehavior.currentPageIndex;
					}
				}
					
				// recursive loop					
				if(deep) {
					for(var i=0; i < element.childNodes.length; i++) {
						if(element.childNodes[i].nodeType==1) { // Element Nodes only
							nbErrors += wBehavior.validateElement(element.childNodes[i], currentPageOnly, deep);
						}
					}
				}
				
				return nbErrors;
			},
			
			// ------------------------------------------------------------------------------------------
			checkRequired: function(element) {
				var wBehavior = wFORMS.behaviors['validation'];		// shortcut				

				if(wFORMS.helpers.hasClass(element,wFORMS.className_required)) {
					switch(element.tagName.toUpperCase()) {
						case "INPUT":
							var inputType = element.getAttribute("type");
							if(!inputType) inputType = 'text'; // handles lame markup
							switch(inputType.toLowerCase()) {
								case "checkbox":
									return element.checked; 
									break;
								case "radio":
									return element.checked; 
									break;
								default:
									return !wBehavior.isEmpty(element.value);
							}
							break;
						case "SELECT":							
							if(element.selectedIndex==-1) {
								// multiple select with no selection
								return false;
							} else 												
								return !wBehavior.isEmpty(element.options[element.selectedIndex].value);
							break;
						case "TEXTAREA":
							return !wBehavior.isEmpty(element.value);
							break;
						default:
							return wBehavior.checkOneRequired(element);
							break;
					} 	
				} else if(wFORMS.helpers.hasClass(element,wBehavior.className_allRequired)) {
					return wBehavior.checkAllRequired(element);
				}
				return true;
			},
			checkOneRequired: function(element) {	
				if(element.nodeType != 1) return false;
				var tagName = element.tagName.toUpperCase();
				var wBehavior = wFORMS.behaviors['validation'];
				
				if(tagName == "INPUT" || tagName == "SELECT" || tagName == "TEXTAREA" ) {
					var value = wBehavior.getFieldValue(element);	
					if(!wBehavior.isEmpty(value)) {					
						return true;
					}			
				}
				for(var i=0; i<element.childNodes.length;i++) {
					if(wBehavior.checkOneRequired(element.childNodes[i])) return true;
				}
				return false;
			},
			checkAllRequired: function(element)	{
				
				if(element.nodeType != 1) return true;
				var tagName = element.tagName.toUpperCase();
				var wBehavior = wFORMS.behaviors['validation'];

				if(tagName == "INPUT" || tagName == "SELECT" || tagName == "TEXTAREA" ) {
					var value = wBehavior.getFieldValue(element);	
					if(wBehavior.isEmpty(value)) {					
						return false;
					}			
				}
				for(var i=0; i<element.childNodes.length;i++) {
					if(!wBehavior.checkAllRequired(element.childNodes[i])) return false;
				}
				return true;
			},
			getFieldValue: function(element) {
				var value = null;
				if(element && element.tagName) {
					if(element.tagName.toUpperCase() == "INPUT") {
						var inputType = element.getAttribute("type");
						if(!inputType) inputType = 'text'; // handles lame markup
					
						switch(inputType.toLowerCase()) {
							case "checkbox": 
								value = element.checked?element.value:null; 
								break;
							case "radio":								
								var radioGroup = element.form[element.name]; 							
								for (var i = 0; i< radioGroup.length; i++) {
								    if (radioGroup[i].checked) {
								    	if(!value) value = new Array();
										value[value.length] = radioGroup[i].value;
								    }
								} 								
								break;
							default:
								value = element.value;
						}
					} else if(element.tagName.toUpperCase() == "SELECT") {	
						if(element.selectedIndex!=-1)																
							value = element.options[element.selectedIndex].value						
						else
							value = null; // multiple select with no selection
					} else if(element.tagName.toUpperCase() == "TEXTAREA") {
						value = element.value;
					}
				}
				return value;
			},
			// ------------------------------------------------------------------------------------------
			isEmpty: function(s) {				
				var regexpWhitespace = /^\s+$/;
				return  ((s == null) || (s.length == 0) || regexpWhitespace.test(s));
			},
			isAlpha: function(s) {
				var regexpAlphabetic = /^[a-zA-Z\s]+$/; // Add ' and - ?
				return wFORMS.behaviors['validation'].isEmpty(s) || regexpAlphabetic.test(s);
			},
			isAlphaNum: function(s) {
				var validChars = /^[\w\s]+$/;
				return wFORMS.behaviors['validation'].isEmpty(s) || validChars.test(s);
			},
			isDate: function(s) {
				var testDate = new Date(s);
				return wFORMS.behaviors['validation'].isEmpty(s) || !isNaN(testDate);
			},
			isEmail: function(s) {
				var regexpEmail = /\w{1,}[@][\w\-]{1,}([.]([\w\-]{1,})){1,3}$/;
				return wFORMS.behaviors['validation'].isEmpty(s) || regexpEmail.test(s);
			},
			isInteger: function(s) {
				var regexp = /^[+]?\d+$/;
				return wFORMS.behaviors['validation'].isEmpty(s) || regexp.test(s);
			},
			isFloat: function(s) {		
				return wFORMS.behaviors['validation'].isEmpty(s) || !isNaN(parseFloat(s));
			},
			// NOT IMPLEMENTED
			isPassword: function(s) {
				// Matches strong password : at least 1 upper case letter, one lower case letter. 4 characters minimum. 12 max.
				//var regexp = /^(?=.*[a-z])(?=.*[A-Z])(?!.*\s).{4,12}$/;  // <= breaks in IE5/Mac
				return wFORMS.behaviors['validation'].isEmpty(s);
			},
			
			// ------------------------------------------------------------------------------------------		
			// Error Alert Functions
			// ------------------------------------------------------------------------------------------		
			showError: function (element,errorMsg) {		
				// remove existing error message if any.
				wFORMS.behaviors['validation'].removeErrorMessage(element);
				
				if (!element.id) element.id = wFORMS.helpers.randomId(); // we'll need an id here.		
				// Add error flag to the field
				element.className += " " + wFORMS.className_validationError_fld;
				// Prepare error message
				var msgNode = document.createTextNode(" " + errorMsg);
				// Find error message placeholder.
				var fe = document.getElementById(element.id +  wFORMS.idSuffix_fieldError);
				if(!fe) { // create placeholder.
					fe = document.createElement("div"); 
					fe.setAttribute('id', element.id +  wFORMS.idSuffix_fieldError);			
					// attach the error message after the field label if possible
					var fl = document.getElementById(element.id +  wFORMS.idSuffix_fieldLabel);
					if(fl)
						fl.parentNode.insertBefore(fe,fl.nextSibling);
					else
						// otherwise, attach it after the field tag.
						element.parentNode.insertBefore(fe,element.nextSibling);
				}
				// Finish the error message.
				fe.appendChild(msgNode);  	
				fe.className += " " + wFORMS.className_validationError_msg;
			},
			
			showAlert: function (nbTotalErrors) {
			   alert(wFORMS.behaviors['validation'].errMsg_notification.replace('%%',nbTotalErrors));
			},
			
			removeErrorMessage: function(element) {
				if(wFORMS.helpers.hasClass(element,wFORMS.className_validationError_fld)) {
					var rErrClass     = new RegExp(wFORMS.className_validationError_fld,"gi");
					element.className = element.className.replace(rErrClass,"");
					var errorMessage  = document.getElementById(element.id + wFORMS.idSuffix_fieldError);
					if(errorMessage)  {				
						errorMessage.innerHTML=""; 
					}
				}
			}
					
       } // End wFORMS.behaviors['validation']
	   
		wFORMS.functionName_formValidation = wFORMS.behaviors['validation'].run;


		// ----------------------------------------------------------------------
		// wForms 1.0 backward compatibility
		// ----------------------------------------------------------------------
		wFORMS.formValidation = wFORMS.behaviors['validation'].run;
		
		// Error messages. 
		wFORMS.arrErrorMsg = new Array(); 
		wFORMS.arrErrorMsg[0] = wFORMS.behaviors['validation'].errMsg_required;	
		wFORMS.arrErrorMsg[1] = wFORMS.behaviors['validation'].errMsg_alpha; 			
		wFORMS.arrErrorMsg[2] = wFORMS.behaviors['validation'].errMsg_email;		
		wFORMS.arrErrorMsg[3] = wFORMS.behaviors['validation'].errMsg_integer;		
		wFORMS.arrErrorMsg[4] = wFORMS.behaviors['validation'].errMsg_float;
		wFORMS.arrErrorMsg[5] = wFORMS.behaviors['validation'].errMsg_password;
		wFORMS.arrErrorMsg[6] = wFORMS.behaviors['validation'].errMsg_alphanum;
		wFORMS.arrErrorMsg[7] = wFORMS.behaviors['validation'].errMsg_date;
		wFORMS.arrErrorMsg[8] = wFORMS.behaviors['validation'].errMsg_notification;
		
   }
   
   
   
   
   // ------------------------------------------------------------------------------------------
// Switch Behavior
// ------------------------------------------------------------------------------------------

 if(wFORMS) {

		// Component properties 
		wFORMS.classNamePrefix_switch 		= "switch";
		wFORMS.className_switchIsOn         = "swtchIsOn";    // used to keep track of the switch state on buttons and links (where the checked attribute is not available)
		wFORMS.className_switchIsOff        = "swtchIsOff";
		wFORMS.classNamePrefix_offState		= "offstate";
		wFORMS.classNamePrefix_onState		= "onstate";
		wFORMS.switchScopeRootTag           = "";         	  // deprecated.	
		
		wFORMS.switchTriggers               = {};			  // associative multi-dimensional array (switchname->element Ids)
		wFORMS.switchTargets                = {};			  // associative multi-dimensional array (switchname->element Ids)
		
	
		wFORMS.behaviors['switch'] = {
		   
		   // ------------------------------------------------------------------------------------------
		   // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
		   // ------------------------------------------------------------------------------------------
		   evaluate: function(node) {
               
			    // Handle Switch Triggers
				// add event handles and populate the wFORMS.switchTriggers 
				// associative array (switchname->element Ids)
				// ------------------------------------------------------------------------------------------				
				if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_switch)) {

					if(!node.id) node.id = wFORMS.helpers.randomId();
					
					//wFORMS.debug('switch/evaluate: '+ node.className + ' ' + node.tagName);
					
					// Go through each class (one element can have more than one switch trigger).
					var switchNames = wFORMS.behaviors['switch'].getSwitchNames(node);
					for(var i=0; i < switchNames.length; i++) {
						if(!wFORMS.switchTriggers[switchNames[i]]) 
							wFORMS.switchTriggers[switchNames[i]] = new Array();
						if(!wFORMS.switchTriggers[switchNames[i]][node.id])
							wFORMS.switchTriggers[switchNames[i]].push(node.id);
						//wFORMS.debug('switch/evaluate: [trigger] '+ switchNames[i] + ' ' + node.id,3);
					}

					switch(node.tagName.toUpperCase()) {
							
						case "OPTION":
							// get the SELECT element
							var selectNode = node.parentNode;
							while(selectNode && selectNode.tagName.toUpperCase() != 'SELECT') {
								var selectNode = selectNode.parentNode;
							}
							if(!selectNode) { alert('Error: invalid markup in SELECT field ?'); return false;  } // invalid markup
							if(!selectNode.id) selectNode.id = wFORMS.helpers.randomId();

							// Make sure we have only one event handler for the select.
							if(!selectNode.getAttribute('rel') || selectNode.getAttribute('rel').indexOf('wfHandled')==-1) {
								//wFORMS.debug('switch/add event: '+ selectNode.className + ' ' + selectNode.tagName);
								selectNode.setAttribute('rel', (selectNode.getAttribute('rel')||"") + ' wfHandled');
								wFORMS.helpers.addEvent(selectNode, 'change', wFORMS.behaviors['switch'].run);
							}							
							break;

						case "INPUT":							
							if(node.type && node.type.toLowerCase() == 'radio') {
								// Add the onclick event on radio inputs of the same group
								var formElement = node.form;	
								for (var j=0; j<formElement[node.name].length; j++) {
									var radioNode = formElement[node.name][j];
									// prevents conflicts with elements with an id = name of radio group
									if(radioNode.type.toLowerCase() == 'radio') {
										// Make sure we have only one event handler for this radio input.
										if(!radioNode.getAttribute('rel') || radioNode.getAttribute('rel').indexOf('wfHandled')==-1) {								
											wFORMS.helpers.addEvent(radioNode, 'click', wFORMS.behaviors['switch'].run);
											// flag the node 
											radioNode.setAttribute('rel', (radioNode.getAttribute('rel')||"") + ' wfHandled');
										} 
									}
								}
							} else {
								wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['switch'].run);
							}
							break;
						
						default:		
							wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['switch'].run);
							break;
					}
				}
				
				// Push targets in the wFORMS.switchTargets array 
				// (associative array with switchname -> element ids)
				// ------------------------------------------------------------------------------------------
				if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_offState) ||
				    wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_onState)) {
					
					if(!node.id) node.id = wFORMS.helpers.randomId();
					
					// Go through each class (one element can be the target of more than one switch).
					var switchNames = wFORMS.behaviors['switch'].getSwitchNames(node);
					
					for(var i=0; i < switchNames.length; i++) {
						if(!wFORMS.switchTargets[switchNames[i]]) 
							wFORMS.switchTargets[switchNames[i]] = new Array();
						if(!wFORMS.switchTargets[switchNames[i]][node.id]) 
							wFORMS.switchTargets[switchNames[i]].push(node.id);
						//wFORMS.debug('switch/evaluate: [target] '+ switchNames[i],3);
					}										
				}
				
				if(node.tagName && node.tagName.toUpperCase()=='FORM') {
					// function to be called when all behaviors for this form have been applied
					//wFORMS.debug('switch/push init');
					wFORMS.onLoadComplete.push(wFORMS.behaviors['switch'].init); 
				}
           },
		   
		   // ------------------------------------------------------------------------------------------
           // init: executed once evaluate has been applied to all elements
		   // ------------------------------------------------------------------------------------------	   
		   init: function() {
			   // go through all switch triggers and activate those who are already ON
			   //wFORMS.debug('switch/init: '+ (wFORMS.switchTriggers.length));
			   for(var switchName in wFORMS.switchTriggers) {
					// go through all triggers for the current switch
					for(var i=0; i< wFORMS.switchTriggers[switchName].length; i++) {		   
					   	var element = document.getElementById(wFORMS.switchTriggers[switchName][i]);
						//wFORMS.debug('switch/init: ' + element + ' ' + switchName , 5);	
					   	if(wFORMS.behaviors['switch'].isTriggerOn(element,switchName)) {
							// if it's a select option, get the select element
							if(element.tagName.toUpperCase()=='OPTION') {
								var element = element.parentNode;
								while(element && element.tagName.toUpperCase() != 'SELECT') {
									var element = element.parentNode;
								}
							}
							// run the trigger
							wFORMS.behaviors['switch'].run(element);
						}
				   }
			   }
		   },
		   
		   // ------------------------------------------------------------------------------------------
           // run: executed when the behavior is activated
		   // ------------------------------------------------------------------------------------------	   
           run: function(e) {
                var element   = wFORMS.helpers.getSourceElement(e);
				if(!element) element = e;
			    //wFORMS.debug('switch/run: ' + element.id , 5);	

				var switches_ON  = new Array();
				var switches_OFF = new Array();
				
				// Get list of triggered switches (some ON, some OFF)
				switch(element.tagName.toUpperCase()) {
					case 'SELECT':
						for(var i=0;i<element.options.length;i++) {
							if(i==element.selectedIndex) {	
								switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(element.options[i]));
							} else {
								switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(element.options[i]));
							}
						}

						break;
					case 'INPUT':
						if(element.type.toLowerCase() == 'radio') {
							// Go through the radio group.
							
							for(var i=0;i <element.form[element.name].length;i++) { 
								var radioElement = element.form[element.name][i];
								if(radioElement.checked) {
									switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(radioElement));
								} else {
									//wFORMS.debug(wFORMS.behaviors['switch'].getSwitchNames(radioElement).length,1);
									switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(radioElement));
								}
							}
						} else {
							if(element.checked || wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn)) {
								switches_ON  = switches_ON.concat(wFORMS.behaviors['switch'].getSwitchNames(element));
							} else {
								switches_OFF = switches_OFF.concat(wFORMS.behaviors['switch'].getSwitchNames(element));
							}							
						}
						break;
					default:
						break;
				}
				
				// Turn off switches first
				for(var i=0; i < switches_OFF.length; i++) {
					// Go through all targets of the switch 
					var elements = wFORMS.behaviors['switch'].getElementsBySwitchName(switches_OFF[i]);
					for(var j=0;j<elements.length;j++) {
																									
						// only turn off a target if all its triggers are off
						var triggers = wFORMS.switchTriggers[switches_OFF[i]];												
						var doSwitch = true;
							
						for (var k=0; k < triggers.length; k++) {
							var trigger = document.getElementById(triggers[k]);
							if(wFORMS.behaviors['switch'].isTriggerOn(trigger, switches_OFF[i])) {
								// An element with the REPEAT behavior limits the scope of switches 
								// targets outside of the scope of the switch are not affected. 
								if(wFORMS.behaviors['switch'].isWithinSwitchScope(trigger, elements[j])) {
									// one of the trigger is still ON. no switch off
									doSwitch = false;
								}
							}							
						}
						if(doSwitch) {
							wFORMS.behaviors['switch'].switchState(elements[j], wFORMS.classNamePrefix_onState, wFORMS.classNamePrefix_offState);
						}
					}
				}
				// Turn on
				for(var i=0; i < switches_ON.length; i++) {
					var elements = wFORMS.behaviors['switch'].getElementsBySwitchName(switches_ON[i]);
					for(var j=0;j<elements.length;j++) {
						// An element with the REPEAT behavior limits the scope of switches 
						// targets outside of the scope of the switch are not affected. 
						if(wFORMS.behaviors['switch'].isWithinSwitchScope(element, elements[j])) {

							wFORMS.behaviors['switch'].switchState(elements[j], wFORMS.classNamePrefix_offState, wFORMS.classNamePrefix_onState);
							//wFORMS.debug('switch/run: [turn on ' + switches_ON[i] + '] ' + elements[j].id , 3);	
						}
					}
				}
           },

		   // ------------------------------------------------------------------------------------------
           // clear: executed if the behavior should not be applied anymore
		   // ------------------------------------------------------------------------------------------
		   clear: function(e) {
             	// @TODO: go through wFORMS.switchTriggers to remove events.
             	wFORMS.switchTriggers = {};		
             	wFORMS.switchTargets = {};
             	
           },
		   
		   
		   // ------------------------------------------------------------------------------------------
		   // Get the list of switches 
		   // Note: potential conflict if an element is both a switch and a target.
		   getSwitchNames: function(element) {
				var switchNames = new Array();
				var classNames  = element.className.split(' ');
				for(var i=0; i < classNames.length; i++) {
					// Note: Might be worth keeping a prefix on switchName to prevent collision with reserved names						
					if(classNames[i].indexOf(wFORMS.classNamePrefix_switch) == 0) {
						switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_switch.length+1));
					}
					if(classNames[i].indexOf(wFORMS.classNamePrefix_onState) == 0) {
						switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_onState.length+1));
					}
					else if(classNames[i].indexOf(wFORMS.classNamePrefix_offState) == 0) {
						switchNames.push(classNames[i].substr(wFORMS.classNamePrefix_offState.length+1));
					}
				}
				return switchNames;
			},
			
		   // ------------------------------------------------------------------------------------------
			switchState: function(element, oldStateClass, newStateClass) {		
				if(!element || element.nodeType != 1) return;
				if(element.className) {  		
					element.className = element.className.replace(oldStateClass, newStateClass);
				}		
				// For  elements that don't have a native state variable (like checked, or selectedIndex)
				if(wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOff)) {
					element.className = element.className.replace(wFORMS.className_switchIsOff, wFORMS.className_switchIsOn);
				} else if(wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn)) {
					element.className = element.className.replace(wFORMS.className_switchIsOn, wFORMS.className_switchIsOff);
				}
			},
			
			// ------------------------------------------------------------------------------------------
			getElementsBySwitchName: function(switchName) {
				var elements = new Array();
				if(wFORMS.switchTargets[switchName]) {
					for (var i=0; i<wFORMS.switchTargets[switchName].length; i++) {
						var element = document.getElementById(wFORMS.switchTargets[switchName][i]);
						if(element)
							elements.push(element);
					}
				}
				return elements;
			},
			
			// ------------------------------------------------------------------------------------------
			isTriggerOn: function(element, triggerName) {
				if(!element) return false;
				if(element.tagName.toUpperCase()=='OPTION') {
					var selectElement = element.parentNode;
					while(selectElement && selectElement.tagName.toUpperCase() != 'SELECT') {
						var selectElement = selectElement.parentNode;
					}
					if(!selectElement) return false; // invalid markup					
					if(selectElement.selectedIndex==-1) return false; // nothing selected
					// TODO: handle multiple-select
					if(wFORMS.helpers.hasClass(selectElement.options[selectElement.selectedIndex],
											   wFORMS.classNamePrefix_switch + '-' + triggerName)) {
						return true;
					}
				} else {
					if(element.checked || wFORMS.helpers.hasClass(element, wFORMS.className_switchIsOn)) 
						return true;
				}
				return false;
			},
			
			// isWithinSwitchScope: An element with the REPEAT behavior limits the scope of switches 
			// targets outside of the scope of the switch are not affected. 
			// ------------------------------------------------------------------------------------------			
			isWithinSwitchScope: function(trigger, target) {
				
				if(wFORMS.hasBehavior('repeat') && wFORMS.limitSwitchScope == true) { 
					// check if the trigger is in a repeatable/removeable element
					var scope = trigger;
				
					while(scope && scope.tagName && scope.tagName.toUpperCase() != 'FORM' && 
						  !wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) &&
					      !wFORMS.helpers.hasClass(scope, wFORMS.className_delete) ) {
						scope = scope.parentNode;
					}
					if(wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) || 
					   wFORMS.helpers.hasClass(scope, wFORMS.className_delete)) {
						// yes, the trigger is nested in a repeat/remove element
						
						// check if the target is in the same element.
						var scope2 = target;
						while(scope2 && scope2.tagName && scope2.tagName.toUpperCase() != 'FORM' && 
							  !wFORMS.helpers.hasClass(scope2, wFORMS.className_repeat) &&
							  !wFORMS.helpers.hasClass(scope2, wFORMS.className_delete) ) {
							scope2 = scope2.parentNode;
						}
						if(scope == scope2) {
							return true;  // target & trigger are in the same repeat/remove element		
						} else {
							return false; // target not in the same repeat/remove element,					
						}
					} else {
						return true;	  // trigger is not nested in a repeat/remove element, scope unaffected
					}
				} else 
					return true;
			}
       } // END wFORMS.behaviors['switch'] object

  	   
   }
   
   
   
   // wForms - a javascript extension to web forms.
// Sync Behavior
// v2.0 beta - April 23rd 2006
// This software is licensed under the CC-GNU LGPL <http://creativecommons.org/licenses/LGPL/2.1/>
// Thanks to recidive.com.

 if(wFORMS) {

		// Component properties 
		wFORMS.classNamePrefix_sync	      = "sync";
		wFORMS.classNamePrefix_target     = "target";
		wFORMS.className_syncIsOn	      = "sncIsOn";   // used to keep track of the sync state on buttons and links (where the checked attribute is not available)
		wFORMS.className_syncIsOff	      = "sncIsOff";
		wFORMS.syncScopeRootTag		      = "";     	 // deprecated.	
		
		wFORMS.syncTriggers               = {};	 	     // associative multi-dimensional array (syncname->element Ids)
		wFORMS.syncTargets                = {};		     // associative multi-dimensional array (syncname->element Ids)
		
	
		wFORMS.behaviors['sync'] = {
		   
		   // ------------------------------------------------------------------------------------------
		   // evaluate: check if the behavior applies to the given node. Adds event handlers if appropriate
		   // ------------------------------------------------------------------------------------------
		   evaluate: function(node) {
               
			    // Handle Sync Triggers
				// add event handles and populate the wFORMS.syncTriggers 
				// associative array (syncname->element Ids)
				// ------------------------------------------------------------------------------------------				
				if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_sync)) {

					if(!node.id) node.id = wFORMS.helpers.randomId();
					
					//wFORMS.debug('sync/evaluate: '+ node.className + ' ' + node.tagName);
					
					// Go through each class (one element can have more than one sync trigger).
					var syncNames = wFORMS.behaviors['sync'].getSyncNames(node);
					for(var i=0; i < syncNames.length; i++) {
						if(!wFORMS.syncTriggers[syncNames[i]]) 
							wFORMS.syncTriggers[syncNames[i]] = new Array();
						if(!wFORMS.syncTriggers[syncNames[i]][node.id]) 
							wFORMS.syncTriggers[syncNames[i]].push(node.id);
						//wFORMS.debug('sync/evaluate: [trigger] '+ syncNames[i],3);
					}

					switch(node.tagName.toUpperCase()) {
							
						case "OPTION":
							// get the SELECT element
							var selectNode = node.parentNode;
							while(selectNode && selectNode.tagName.toUpperCase() != 'SELECT') {
								var selectNode = selectNode.parentNode;
							}
							if(!selectNode) { alert('Error: invalid markup in SELECT field ?'); return false;  } // invalid markup
							if(!selectNode.id) selectNode.id = wFORMS.helpers.randomId();

							// Make sure we have only one event handler for the select.
							if(!selectNode.getAttribute('rel') || selectNode.getAttribute('rel').indexOf('wfHandled')==-1) {
								//wFORMS.debug('sync/add event: '+ selectNode.className + ' ' + selectNode.tagName);
								selectNode.setAttribute('rel', (selectNode.getAttribute('rel')||"") + ' wfHandled');
								wFORMS.helpers.addEvent(selectNode, 'change', wFORMS.behaviors['sync'].run);
							}							
							break;

						case "INPUT":							
							if(node.type && node.type.toLowerCase() == 'radio') {
								// Add the onclick event on radio inputs of the same group
								var formElement = node.form;	
								for (var j=0; j<formElement[node.name].length; j++) {
									var radioNode = formElement[node.name][j];
									// prevents conflicts with elements with an id = name of radio group
									if(radioNode.type.toLowerCase() == 'radio') {
										// Make sure we have only one event handler for this radio input.
										if(!radioNode.getAttribute('rel') || radioNode.getAttribute('rel').indexOf('wfHandled')==-1) {								
											wFORMS.helpers.addEvent(radioNode, 'click', wFORMS.behaviors['sync'].run);
											// flag the node 
											radioNode.setAttribute('rel', (radioNode.getAttribute('rel')||"") + ' wfHandled');
										} 
									}
								}
							} else if(node.type && node.type.toLowerCase() == 'text') {
								wFORMS.helpers.addEvent(node, 'keyup', wFORMS.behaviors['sync'].run);
							} else {
								wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['sync'].run);
							}
							break;
						case "TEXTAREA":
							wFORMS.helpers.addEvent(node, 'keyup', wFORMS.behaviors['sync'].run);
							break;
						default:		
							wFORMS.helpers.addEvent(node, 'click', wFORMS.behaviors['sync'].run);
							break;
					}
				}
				
				// Push targets in the wFORMS.syncTargets array 
				// (associative array with syncname -> element ids)
				// ------------------------------------------------------------------------------------------
				if (wFORMS.helpers.hasClassPrefix(node, wFORMS.classNamePrefix_target)) {
					
					if(!node.id) node.id = wFORMS.helpers.randomId();
					
					// Go through each class (one element can be the target of more than one sync).
					var syncNames = wFORMS.behaviors['sync'].getSyncNames(node);
					
					for(var i=0; i < syncNames.length; i++) {
						if(!wFORMS.syncTargets[syncNames[i]]) 
							wFORMS.syncTargets[syncNames[i]] = new Array();
						wFORMS.syncTargets[syncNames[i]].push(node.id);
						//wFORMS.debug('sync/evaluate: [target] '+ syncNames[i],3);
					}										
				}
				
				if(node.tagName && node.tagName.toUpperCase()=='FORM') {
					// function to be called when all behaviors for this form have been applied
					//wFORMS.debug('sync/push init');
					wFORMS.onLoadComplete.push(wFORMS.behaviors['sync'].init); 
				}
           },
		   
		   // ------------------------------------------------------------------------------------------
           // init: executed once evaluate has been applied to all elements
		   // ------------------------------------------------------------------------------------------	   
		   init: function() {
			   // go through all sync triggers and activate those who are already ON
			   //wFORMS.debug('sync/init: '+ (wFORMS.syncTriggers.length));
			   for(var syncName in wFORMS.syncTriggers) {
					// go through all triggers for the current sync
					for(var i=0; i< wFORMS.syncTriggers[syncName].length; i++) {		   
					   	var element = document.getElementById(wFORMS.syncTriggers[syncName][i]);
						//wFORMS.debug('sync/init: ' + element + ' ' + syncName , 5);	
					   	if(wFORMS.behaviors['sync'].isTriggerOn(element,syncName)) {
							// if it's a select option, get the select element
							if(element.tagName.toUpperCase()=='OPTION') {
								var element = element.parentNode;
								while(element && element.tagName.toUpperCase() != 'SELECT') {
									var element = element.parentNode;
								}
							}
							// run the trigger
							wFORMS.behaviors['sync'].run(element);
						}
				   }
			   }
		   },
		   
		   // ------------------------------------------------------------------------------------------
           // run: executed when the behavior is activated
		   // ------------------------------------------------------------------------------------------	   
           run: function(e) {
                var element   = wFORMS.helpers.getSourceElement(e);
				if(!element) element = e;
			    //wFORMS.debug('sync/run: ' + element.id , 5);	

				var syncs  = new Array();
				// Get list of triggered syncs
				switch(element.tagName.toUpperCase()) {
					case 'SELECT':
						for(var i=0;i<element.options.length;i++) {
							syncs = syncs.concat(wFORMS.behaviors['sync'].getSyncNames(element.options[i]));
						}
						break;
					case 'INPUT':
						if(element.type.toLowerCase() == 'radio') {
							// Go through the radio group.
							for(var i=0;i <element.form[element.name].length;i++) { 
								var radioElement = element.form[element.name][i];
								syncs = syncs.concat(wFORMS.behaviors['sync'].getSyncNames(radioElement));
							}
						} else {
							syncs = syncs.concat(wFORMS.behaviors['sync'].getSyncNames(element));
						}
						break;
					default:
						break;
				}

				// Do sync
				for(var i=0; i < syncs.length; i++) {
					var elements = wFORMS.behaviors['sync'].getTargetsBySyncName(syncs[i]);
					for(var j=0;j<elements.length;j++) {
						// An element with the REPEAT behavior limits the scope of syncs 
						// targets outside of the scope of the sync are not affected. 
						if(wFORMS.behaviors['sync'].isWithinSyncScope(element, elements[j])) {
							wFORMS.behaviors['sync'].sync(element, elements[j], syncs[i]);
							//wFORMS.debug('sync/run: [turn on ' + syncs_ON[i] + '] ' + elements[j].id , 3);	
						}
					}
				}
           },

		   // ------------------------------------------------------------------------------------------
           // remove: executed if the behavior should not be applied anymore
		   // ------------------------------------------------------------------------------------------
		   remove: function(e) {
               var element   = wFORMS.helpers.getSourceElement(e);
			  //wFORMS.debug('sync/remove: ' + element.id , 5);				   
           },
		   
		   
		   // ------------------------------------------------------------------------------------------
		   // Get the list of syncs 
		   // Note: potential conflict if an element is both a sync and a target.
		   getSyncNames: function(element) {
				var syncNames = new Array();
				var classNames  = element.className.split(' ');
				for(var i=0; i < classNames.length; i++) {
					// Note: Might be worth keeping a prefix on syncName to prevent collision with reserved names						
					if(classNames[i].indexOf(wFORMS.classNamePrefix_sync) == 0) {
						syncNames.push(classNames[i].substr(wFORMS.classNamePrefix_sync.length+1));
					}
					if(classNames[i].indexOf(wFORMS.classNamePrefix_target) == 0) {
						syncNames.push(classNames[i].substr(wFORMS.classNamePrefix_target.length+1));
					}
				}
				return syncNames;
			},

			// ------------------------------------------------------------------------------------------
			sync: function(source, target, syncName) {
				if(!source || source.nodeType != 1) return;
				if(!target || target.nodeType != 1) return;
				var value, state;
				// read in source data
				switch(source.tagName.toUpperCase()) {
					case 'SELECT':
						// get de value of the select, I think we need to change
						// this to get the value of the source option
						value = source.options[source.selectedIndex].value;
						state = wFORMS.helpers.hasClass(source.options[source.selectedIndex], wFORMS.classNamePrefix_sync + '-' + syncName);
						break;
					case 'INPUT':
						value = source.value;
						if(source.type.toLowerCase() == 'radio' || source.type.toLowerCase() == 'checkbox') {
							state = source.checked;
						} else {
							state = (value.lenght > 0);
						}
						break;
					default:
						value = source.innerHTML;
						state = (value.lenght > 0);
						break;
				}

				// sync up the target
				switch(target.tagName.toUpperCase()) {
					case 'OPTION':
						wFORMS.behaviors['sync'].syncState(target, state);
						break;
					case 'INPUT':
						if(target.type.toLowerCase() == 'radio' || target.type.toLowerCase() == 'checkbox') {
							wFORMS.behaviors['sync'].syncState(target, state);
						} else {
							wFORMS.behaviors['sync'].syncValue(target, value);
						}
						break;
					default:
						wFORMS.behaviors['sync'].syncValue(target, value);
						break;
				}

				// For  elements that don't have a native state variable (like checked, or selectedIndex)
				if(wFORMS.helpers.hasClass(target, wFORMS.className_syncIsOff)) {
					element.className = target.className.replace(wFORMS.className_syncIsOff, wFORMS.className_syncIsOn);
				} else if(wFORMS.helpers.hasClass(target, wFORMS.className_syncIsOn)) {
					element.className = target.className.replace(wFORMS.className_syncIsOn, wFORMS.className_syncIsOff);
				}
			},

			// ------------------------------------------------------------------------------------------
			syncState: function(element, state) {
				if(element.tagName.toUpperCase() == 'OPTION') {
					element.selected = state;
				} else {
					element.checked = state;
				}
			},

			// ------------------------------------------------------------------------------------------
			syncValue: function(element, value) {		
				if(element.tagName.toUpperCase() == 'INPUT') {
					element.value = value;
				} else {
					element.innerHTML = value;
				}
			},
			
			// ------------------------------------------------------------------------------------------
			getTargetsBySyncName: function(syncName) {
				var elements = new Array();
				if(wFORMS.syncTargets[syncName]) {
					for (var i=0; i<wFORMS.syncTargets[syncName].length; i++) {
						var element = document.getElementById(wFORMS.syncTargets[syncName][i]);
						if(element)
							elements.push(element);
					}
				}
				return elements;
			},
			
			// ------------------------------------------------------------------------------------------
			isTriggerOn: function(element, triggerName) {
				if(!element) return false;
				if(element.tagName.toUpperCase()=='OPTION') {
					var selectElement = element.parentNode;
					while(selectElement && selectElement.tagName.toUpperCase() != 'SELECT') {
						var selectElement = selectElement.parentNode;
					}
					if(!selectElement) return false; // invalid markup					
					if(selectElement.selectedIndex==-1) return false; // nothing selected
					// TODO: handle multiple-select
					if(wFORMS.helpers.hasClass(selectElement.options[selectElement.selectedIndex],
											   wFORMS.classNamePrefix_sync + '-' + triggerName)) {
						return true;
					}
				} else {
					if(element.checked || wFORMS.helpers.hasClass(element, wFORMS.className_syncIsOn)) 
						return true;
				}
				return false;
			},
			
			// isWithinSyncScope: An element with the REPEAT behavior limits the scope of syncs 
			// targets outside of the scope of the sync are not affected. 
			// ------------------------------------------------------------------------------------------			
			isWithinSyncScope: function(trigger, target) {
				if(wFORMS.hasBehavior('repeat') && wFORMS.limitSyncScope == true) { 
					// check if the trigger is in a repeatable/removeable element
					var scope = trigger;
				
					while(scope && scope.tagName && scope.tagName.toUpperCase() != 'FORM' && 
						  !wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) &&
					      !wFORMS.helpers.hasClass(scope, wFORMS.className_delete) ) {
						scope = scope.parentNode;
					}
					if(wFORMS.helpers.hasClass(scope, wFORMS.className_repeat) || 
					   wFORMS.helpers.hasClass(scope, wFORMS.className_delete)) {
						// yes, the trigger is nested in a repeat/remove element
						
						// check if the target is in the same element.
						var scope2 = target;
						while(scope2 && scope2.tagName && scope2.tagName.toUpperCase() != 'FORM' && 
							  !wFORMS.helpers.hasClass(scope2, wFORMS.className_repeat) &&
							  !wFORMS.helpers.hasClass(scope2, wFORMS.className_delete) ) {
							scope2 = scope2.parentNode;
						}
						if(scope == scope2) {
							return true;  // target & trigger are in the same repeat/remove element		
						} else {
							return false; // target not in the same repeat/remove element,					
						}
					} else {
						return true;	  // trigger is not nested in a repeat/remove element, scope unaffected
					}
				} else 
					return true;
			}
       } // END wFORMS.behaviors['sync'] object

  	   
   }



/*
Clear default form value script- by JavaScriptKit.com
Featured on JavaScript Kit (http://javascriptkit.com)
Visit javascriptkit.com for 400+ free scripts!
*/

function clearText(thefield){
if (thefield.defaultValue==thefield.value)
thefield.value = ""
} 



/* 
Print Page Dialogue launcher */
var da = (document.all) ? 1 : 0;
var pr = (window.print) ? 1 : 0;
var mac = (navigator.userAgent.indexOf("Mac") != -1); 

function printPage() {
  if (pr) // NS4, IE5
    window.print()
  else if (da && !mac) // IE4 (Windows)
    vbPrintPage()
  else // other browsers
    alert("Sorry, your browser doesn't support this feature.");
  return false;
}

if (da && !pr && !mac) with (document) {
  writeln('<OBJECT ID="WB" WIDTH="0" HEIGHT="0" CLASSID="clsid:8856F961-340A-11D0-A96B-00C04FD705A2"></OBJECT>');
  writeln('<' + 'SCRIPT LANGUAGE="VBScript">');
  writeln('Sub window_onunload');
  writeln('  On Error Resume Next');
  writeln('  Set WB = nothing');
  writeln('End Sub');
  writeln('Sub vbPrintPage');
  writeln('  OLECMDID_PRINT = 6');
  writeln('  OLECMDEXECOPT_DONTPROMPTUSER = 2');
  writeln('  OLECMDEXECOPT_PROMPTUSER = 1');
  writeln('  On Error Resume Next');
  writeln('  WB.ExecWB OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER');
  writeln('End Sub');
  writeln('<' + '/SCRIPT>');
}