/**
 * @namespace 
 */
var Validation = {}
/**
 * A baseline class that contains methods every validate object is going to have.  May need to be generalized more, the el parameter doesn't fit with checkboxes and radios)
 
 * 	Changes: (6/10/2009) - Brian - Updated the error box output in validateAll() so that the list items will never have huge gaps inbetween them and made the cursor change when hovering over them.  
 * 8/21/2009 - Brian - 1) Added a removeDblSpace, ltrim, rtrim and trim function to the Validator class.  
 					   2) Fixed MaxWordCount bug.  It counted leading and trailing whitespace as a word.  
					   It also counted double spaces twice, triple 3 times, etc.  Added a function
					   removeDblSpace to get rid of N amount of space and reduce it to 1.
 */
Validation.Validator = Class.create({
		/** @lends Validator.prototype */
		/**
		 * this object does not work by itself and is more of an abstract object that has any shared 			 functionality between validator objects
		 * @constructs 
		 * @param { HTMLElement } el The html input element to be validated
		*/
		initialize: function(el){
			
			this.el = document.getElementById(el);
			var styles = new Hash();		
			this.styles = styles;
			this.errorMessageTxt = null;
			this.styles.set('background', $(el).getStyle( 'background'));
			this.styles.set('border', $(el).getStyle('border'));
			this.styles.set('color', $(el).getStyle('color'));
			
			var invalidClass = "nogo";
			this.invalidClass = invalidClass;
			
			var fn = this.quickValidate();
			var obj = this;
			Element.observe(this.el, 'change', function() { fn(obj)});
			
		},
		
		/**
		 * used to call validate without running the function (may be improperly named)
		 * @return this.validate - method - the method that validates the object
		 */
		quickValidate: function(){
			return this.validate;
		},
		
		/**
		 * used to test that a string only contains certain symbols or characters
		 * @param {String} validChars a string of the allowed characters
		 * @param {string} sText the string to be tested
		 */
		hasValidChars: function(validChars, sText){
			var char;
		
			// While the characters are numbers
			for (i = 0; i < sText.length; i++) { 
	
				// Get the current character of the string.
				char = sText.charAt(i); 
				
				// If the current character is not a number then it's not numeric.
				if (validChars.indexOf(char) == -1) {
					return false;
				}
			}
			return true;
		},
		
		/**
		 * used to test that a string doesn't contain certain symbols or characters
		 * @param {String} invalidChars a string of the disallowed characters
		 * @param {string} sText the string to be tested
		 */
		hasInvalidChars: function(invalidChars, sText){
			
			
			var char;
		
			// While the characters are numbers
			for (i = 0; i < sText.length; i++) { 
	
				// Get the current character of the string.
				char = sText.charAt(i); 
				
				// If the current character is not a number then it's not numeric.
				if (invalidChars.indexOf(char) != -1) {
					
					return false;
				}
			}
			
			return true;
		},
			
		/**
		 * retrieves the value of the HTMLElement
		 * @return {string|int} this.el.value returns whatever value is currently in the input box of the HTMLElement
		 */
		getVal: function(){
			return this.el.value;
		},
		
		/**
		 * retrieves the id of the HTMLElement
		 * @return {string} this.el.id returns the id of the HTMLElement
		 */
		getId: function(){
			return this.el.id;
		},
		
		/**
		 * set the class name to be used when setting the class for invalid objects
		 * @param className - string - the desired name to set for the class of invalid objects
		 */
		setInvalidClass: function(className){
			this.invalidClass = className;
		},
		
		/**
		 * get the class name for invalid objects
		 * @return {string} this.invalidClass returns the invalid class name
		 */
		getInvalidClass: function(parent){
			this.parent = parent || false;

			if(this.invalidClass == 'nogo')
			{
				if(this.parent)
				{
					//alert(this.el.id);
					//lert(this.el.parentNode.id);
					$(this.el.parentNode).setStyle({
						background: '#FFFFCC',
						border: '1px solid #FF0000',
						color: '#333'
								  });
					
				}
				else
				{
					this.el.setStyle({
						background: '#FFFFCC',
						border: '1px solid #FF0000',
						color: '#333'
								  });
				}
				return this.invalidClass;
			}
			else
			{
				return this.invalidClass;
			}
		},
		
		/**
		 * sets the class to the error style
		 */
		setClass: function(){
			this.el.addClassName(this.getInvalidClass());
			
		},
		/**
		 * reset class
		 */
		unSetClass: function(){
			this.el.removeClassName(this.invalidClass);
			this.el.writeAttribute("style", {
						background: this.styles.get('background'),
						border: this.styles.get('border'),
						color: this.styles.get('color')
								  });
		},
		
		/**
		 * Sets the parents parent class to the error style
		 */
		setParentClass: function(){
			
			$(this.el.parentNode).className = this.getInvalidClass(true);
			
		},
		
		/**
		 * Resets the parents parent class
		 */
		unSetParentClass: function(){
			$(this.el.parentNode).writeAttribute({"style": ""});
			$(this.el.parentNode).className = "";
		},
		
		/**
		 * remove the event listener, used for grouping elements
		 */
		unObserve: function(){
			Event.stopObserving(this.el, 'change')
		},
		
		// Removes leading whitespaces
		LTrim: function(value) {
	
			var re = /\s*((\S+\s*)*)/;
			return value.replace(re, "$1");
		},
		
		RTrim: function(value) {
	
			var re = /((\s*\S+)*)\s*/;
			return value.replace(re, "$1");			
		},

		// Removes leading and ending whitespaces
		trim: function(value) {
			return this.LTrim(this.RTrim(value));
		},
		// The regular expression takes a group of whitespace and condenses it into 1 space.		
		removeDblSpace: function(value){
			return value.replace(/\s+/g, ' ');	
		}

			
	});
	
	

/**
 * NO DESC
 */
Validation.FormValidator = Class.create({
/** @lends FormValidator.prototype  */
	
	/**
	* The master container class that holds and handles the validation objects
	* @constructs
	* @param {HTMLElement} ID the forms id
	*/
	initialize: function(ID){
		var errorClass = null;
		this.errorClass = errorClass;
		this.container_list = new Array();
		var messageShown = false;
		this.messageShown = messageShown;
		var errorMsg = true;
		this.errorMsg = errorMsg;
		this.id = ID;
		this.validator_list = new Array(); //list of validator objects
		this.errorList = '';
		this.setErrorMessage('error', null, this.id, 'Top', 'There is an error on the page');
	},
	
	/**
	 * add an validator object to the list
	 * @param obj - takes in a validator object
	 *
	 */
	addObj: function(obj){
		this.validator_list.push(obj);
		return obj;
	},
	
	//remove an object from the validation list
	removeObj: function(ID){
		
		var index = 0;
		var containerLength = this.validator_list.length;
		var tmpContainer = new Array();
		while(ID != this.validator_list[index].getId())
		{
			index++;
			if(index >= containerLength)
			{
				return "Failed to find obj";
			}
		}
		//store the removed obj to be returned at the end
		var tmp = this.validator_list[index];
		
		this.validator_list[index] = null;
		//set validator_list to a new array that doesn't include the null value
		index = 0;
		
		for(index;index<this.validator_list.length;index++)
		{
			if(this.validator_list[index] != null)
			{
				tmpContainer.push(this.validator_list[index]);
			}
		}
		
		this.validator_list = tmpContainer;
		return tmp;
	},
	/**
	 * set the class name to be used when setting the class for invalid fields (applies invalid class to any validation objects currently in this FormValidator container
	 * @param {string} className the desired name to set for the class of invalid objects
	 */
	setInvalidClass: function(className){
		var i = 0;
		while(i < this.validator_list.length)
		{
			this.validator_list[i].setInvalidClass(className);
			i++;
		}
	},
	
	/**
	 * Used to retrieve an obj from the list using its Id
	 * @param {string HTMLAttribute value} ID id to look for
	 */
	getObj: function(ID){
		var index = 0;
		var containerLength = this.validator_list.length;
		while(ID != this.validator_list[index].getId())
		{
			index++;
			if(index >= containerLength)
			{
				return "Failed to find obj";
			}
		}
		return this.validator_list[index];
	},
	
	/**
	* This method will test that all objects/elements in its container are valid
	* If not valid will show error message box with the errors on page and sets 
	* the invalid class to any invalid fields
	*/
	validateAll: function(){
		
		var j = 0;
		var flag = false;
		this.errorList ='<ul>';
		//loop through validator list
		while(j < this.validator_list.length)
		{
			
			//if obj is invalid throw a flag
			if(!this.validator_list[j].validate(this.validator_list[j]))
			{
				flag = true;
				this.errorList += '<li> <a class="error_statement" style="margin:0 !important; cursor:pointer;" onclick=$(\''+this.validator_list[j].getId()+'\').focus()>'+this.validator_list[j].getErrorMessage()+'</a></li>';
			}
			j++;
		}
		
		this.errorList += '</ul>';
		if(flag == true && this.errorMsg == true){
			if(this.messageShown == false)
			{
				this.messageShown = true;
				this.showErrorMessage();
			}else{
				this.clearErrorMessage();
				this.showErrorMessage();
			}
		}
		if(flag == false && this.messageShown == true)
		{
			this.messageShown = false;
			this.clearErrorMessage();
		}
		
		return !flag; //! - because flag is true when a validate fails
		
	},
	
	/**
	 * Used to set an error message for the page, also sets it position
	 * @param {string HTMLAttribute} ID  the value which is set to the error message's id attribute
	 * @param {string  HTMLAttribute} className the value which is set to the error message's class attribute
	 * @param {string HTMLAttribute} Parent the id of the parent element which is used in positioning the message (usually the name of the form)
	 * @param {string} position values: Top, Bottom, After, Before - defaults to Top, the position of the error message with relation to the Parent
	 * @param {string} Message defaults to "There is an error on the page", the error message
	 */
	setErrorMessage: function(ID, className, Parent, position, Message){
		this.errorMsg = true;
		this.errorId = ID;
		this.errorClass = className;
		this.errorParent = Parent;
		this.errorPosition = position;
		this.errorMessage = Message;
	},
	
	/**
	 * used if the designer wants to dynamically remove the error message
	 */
	clearErrorMessage: function(){
		ID = this.errorId;
		focusPoint = this.focusPoint;
		$(focusPoint).remove();
		$(ID).remove();
		this.messageShown = false;
	},
	
	/**
	* This method is a universal method which will automatically detect the type of input the element is
	* and it will apply the proper validation to it. Also special is used in the case of wanting to apply
	* any special type of validation (i.e. InBetween, MaxLength, ect.) and hash is used in the case of the special
	* validation requiring parameters. Sent is as a hash.
	* @param  {HTMLInputElement} ele this is the id of the input element
	* @param {Hash object} special takes in a hash obj to specialize the item
	*/
	addValidationItem: function(ele, special, hash){
		var i; 
		var flag = false;
		
		var myhash = new Hash();
		myhash = hash || null;
		this.special = special;

		ele = document.getElementById(ele);
		this.ele = ele;
		switch(this.ele.type){
			case 'text':
				switch(special){
					
					case 'PID':
						return this.addObj(new Validation.PID(this.ele.id));
						break;
					case 'MaxLength':
						return this.addObj(new Validation.MaxLength(this.ele.id, myhash.maxLen));
						break;
					case 'Restricted':
						return this.addObj(new Validation.Restricted(this.ele.id, myhash.restriction));
						break;
					case 'InvalidChars':
						return this.addObj(new Validation.InvalidChars(this.ele.id, myhash.invalidChars));
						break;
					case 'InBetween':
						return this.addObj(new Validation.InBetween(this.ele.id, myhash.minLen, myhash.maxLen));
						break;
					case 'RestrictedInBetween':
						return this.addObj(new Validation.RestrictedInBetween(this.ele.id, myhash.minLen, myhash.maxLen, myhash.restriction));
						break;
					case 'MaxWordCount':
						return this.addObj(new Validation.MaxWordCount(this.ele.id, myhash.maxWords));
						break;
						
					default: 
						if(myhash != null)
						{
							return this.addObj(new Validation.Normal(this.ele.id));
							break;
						} else {
							return this.addObj(new Validation.Normal(this.ele.id));
							break;
						}							
				}
				break;
			case 'textarea':
				switch(special){
					
					case 'PID':
						return this.addObj(new Validation.PID(this.ele.id));
						break;
					case 'MaxLength':
						return this.addObj(new Validation.MaxLength(this.ele.id, myhash.maxLen));
						break;
					case 'Restricted':
						return this.addObj(new Validation.Restricted(this.ele.id, myhash.restriction));
						break;
					case 'InvalidChars':
						return this.addObj(new Validation.InvalidChars(this.ele.id, myhash.invalidChars));
						break;
					case 'InBetween':
						return this.addObj(new Validation.InBetween(this.ele.id, myhash.minLen, myhash.maxLen));
						break;
					case 'RestrictedInBetween':
						return this.addObj(new Validation.RestrictedInBetween(this.ele.id, myhash.minLen, myhash.maxLen, myhash.restriction));
						break;
					case 'MaxWordCount':
						return this.addObj(new Validation.MaxWordCount(this.ele.id, myhash.maxWords));
						break;
						
					default: 
						if(myhash != null)
						{
							return this.addObj(new Validation.Normal(this.ele.id));
							break;
						} else {
							return this.addObj(new Validation.Normal(this.ele.id));
							break;
						}							
				}
				break;
			case 'select-one':
				return this.addObj(new Validation.Select(this.ele.id, this.ele.options[0].value));
				break;
			case 'radio':
			
				if(myhash == null)
				{
					var myhash = {};
					myhash.numChecked = 1;
				}
				if(this.special != null)
				{
					//create a checkedContainer with this.special as the name of the containing
					//HTMLElement (i.e. the div)
					
					for(i=0;i<this.container_list.length;i++)
					{
						
						if(this.special == this.container_list[i].getId())
						{
							flag = true;
							break;
						}
					}
					if(flag)
					{
						return this.container_list[i].addChecked(new Validation.CheckBox(this.ele.id), this.container_list[i]);
					}
					else
					{
						
						this.special = new Validation.CheckedContainer(this.special, myhash.numChecked);
						this.addContainer(this.special);
						this.special.addChecked(new Validation.CheckBox(this.ele.id), this.special);
						return this.addObj(this.special);
					}
				}
				else
				{
					return this.addObj(new Validation.CheckBox(this.ele.id));
				}
				break;
			case 'checkbox':
				//this.special is the name of the container literally and html wise
				if(this.special != null)
				{
					//create a checkedContainer with this.special as the name of the containing
					//HTMLElement (i.e. the div)
					for(i=0;i<this.container_list.length;i++)
					{
						if(this.special == this.container_list[i].getId())
						{
							flag = true;
							break;
						}
					}
					//flag is true if container already exists
					if(flag)
					{
						return this.container_list[i].addChecked(new Validation.CheckBox(this.ele.id), this.container_list[i]);
					}
					else
					{
						this.special = new Validation.CheckedContainer(this.special, myhash.numChecked);
						this.addContainer(this.special);
						
						this.special.addChecked(new Validation.CheckBox(this.ele.id), this.special);
						return this.addObj(this.special);
					}
				}
				else
				{
					//create a single checkbox validation item
					return this.addObj(new Validation.CheckBox(this.ele.id));
				}
				break;
			default:
				alert(this.ele.id+' has an incompatable element type '+this.ele.type);
				break;
		}
				
	},
	
	/**
	 * create a container of container names to check against when using the abstract method call
	 * basically this is to add items to already existing containers instead of making more containers
	 * @param containerName the name of the container to be added to the list
	 */
	 addContainer: function(containerName){
		 this.container_list.push(containerName);
	 },
	
	/**
	 * The function which is called when the set error message is to be shown
	 * Shows the message and sets the focus to a hidden <a> right before it
	 */
	showErrorMessage: function(){
		
		this.messageShown = true;
		var ID = this.errorId 
		var className = this.errorClassName;
		var Parent = this.errorParent;
		
		var Position = this.errorPosition;
		var Message = this.errorMessage;
		if(Message == null){
			Message = "There is an error on the page";
		}
		switch(Position){
			case 'Top':
				
				new Insertion.Top(Parent, '<div id='+ID+'>'+Message+this.errorList+'</div>');
				new Insertion.Top(Parent, '<a  href=\"\"> </a>');
				var focusPoint = $(ID).previous().identify();
				this.focusPoint = focusPoint;
				$(this.focusPoint).focus();
				$(focusPoint).setStyle({ visibility: 'hidden'});
				
				break;
			case 'After':
				new Insertion.After(Parent, '<div id='+ID+'>'+Message+this.errorList+'</div>');
				new Insertion.After(Parent, '<a href=\"\"> </a>');
				var focusPoint = $(ID).previous().identify();
				this.focusPoint = focusPoint;
				$(this.focusPoint).focus();
				$(focusPoint).setStyle({ visibility: 'hidden'});
				break;
			case 'Before':
				new Insertion.Before(Parent, '<a hrsef=\"\"> </a>');
				new Insertion.Before(Parent, '<div id='+ID+'>'+Message+this.errorList+'</div>');
				var focusPoint = $(ID).previous().identify();
				this.focusPoint = focusPoint;
				$(this.focusPoint).focus();
				$(focusPoint).setStyle({ visibility: 'hidden'});
				break;
			case 'Bottom':
				new Insertion.Bottom(Parent, '<a href=\"\"> </a>');
				new Insertion.Bottom(Parent, '<div id='+ID+'>'+Message+this.errorList+'</div>');
				var focusPoint = $(ID).previous().identify();
				this.focusPoint = focusPoint;
				$(this.focusPoint).focus();
				$(focusPoint).setStyle({ visibility: 'hidden'});
				break;
			default:
				new Insertion.Top(Parent, '<div id='+ID+'>'+Message+this.errorList+'</div>');
				new Insertion.Top(Parent, '<a href=\"\"> </a>');
				var focusPoint = $(ID).previous().identify();
				this.focusPoint = focusPoint;
				$(this.focusPoint).focus();
				$(focusPoint).setStyle({ visibility: 'hidden'});
				break;
		}
		this.getErrorClass(ID);
				
	},
	
	/**
	 * This sets the proper class to the error message, either the default style or a given class name
	 * @param  {HTMLElementAttribute} ID the ID of the error message element
	 * @return {string} this.invalidClass returns the invalid class name
	 */
	getErrorClass: function(ID){
		element = $(ID);
		
		if(this.errorClass == null)
		{
				element.setStyle({
					margin: '5px auto',
					width: '450px',
					padding: '5px',
					background: '#FFCCCC',
					borderTop: '5px solid #FF6666',
					borderBottom: '5px solid #FF6666',
					borderRightStyle: 'none',
					borderLeftStyle: 'none',
					borderRightColor: '#FF6666',
					borderLeftColor: '#FF6666'
				});
			
		}
		else
		{
			element.addClassName(this.errorClass);
		}
	}
	
});

/**
 * NO DESC
 */
Validation.CheckedContainer = Class.create({
	/** @lends CheckedContainer.prototype */ 
	/**
	 * a container for holding and handling checkboxes
	 * @constructs
	 * @param {Integer} numChecked the minimum number required to be checked (1 for radios)
	 * @param {HTMLElementAttribute} el the containers element ID
	 */
	initialize: function(el, numChecked){
		var invalidClass = "nogo";
		this.invalidClass = invalidClass;
		this.errorMessageTxt = null;
		this.el = $(el);
		var styles = new Hash();
		this.styles = styles;
		
		this.styles.set('background', $(el).getStyle('background'));
		this.styles.set('border', $(el).getStyle('border'));
		this.styles.set('color', $(el).getStyle('color'));
		
		if(numChecked == null){
			this.numChecked = 1;
		} else {
			this.numChecked = numChecked;
		}
		this.checked_list = new Array(); //list of checkbox objects
	},
	
	/**
	* used to get the elements id anywhere
	* @return {HTMLElementAttribute} this.el.id the id of the container of checkboxes
	*/
	getId: function(){
		return this.el.id;
	},
	
	/**
	 * set the class name to be used when setting the class for invalid objects
	 * @param {string} className the desired name to set for the class of invalid objects
	 */
	setInvalidClass: function(className){
		
		this.invalidClass = className;
	},
	
	/**
	 * get the class name for invalid objects
	 * @return {string} this.invalidClass returns the invalid class name
	 */
	getInvalidClass: function(){
		if(this.invalidClass == 'nogo')
		{
			
			this.el.setStyle({
				background: '#FFFFCC',
				border: '1px solid #FF0000',
				color: '#333'
						  });
			
			return this.invalidClass;
		}

		else
		{
			return this.invalidClass;
		}
	},
	
	/**
	 * sets the class to the default or set invalid class
	 */
	setClass: function(){
		this.el.addClassName(this.getInvalidClass());//this doesn't work in ie
	},
	/**
	 * reset class to normal
	 */
	unSetClass: function(){
		
		this.el.removeClassName(this.invalidClass);
		this.el.writeAttribute("style", {
					background: this.styles.get('background'),
					border: this.styles.get('border'),
					color: this.styles.get('color')
							  });
		
	},
	
	/**
	* used to be able to call this.numChecked at any time
	* @return this.numChecked - the set minimum number of checks that need to be checked
	*/
	getNumChecked: function(){
		return this.numChecked;
	},
	
	
	
	/**
	 * add an element object to the list and stops the event listener on the elements
	 * @param {Object} obj - takes in a JS CheckBox object and adds it to the list
	 */
	addChecked: function(obj, container){
		
		
		obj.unObserve();
		if(container != null)
		{
			//console.log(this);
			//this.el = $(this.el.id);
			Element.observe($(obj.getId()), 'change', function(){ container.validate(container);} );
		}
		this.checked_list.push(obj);
		return obj;
	},
	
	/**
	 * checks to make sure at least numChecked are checked in the list
	 * @return {Boolean} true or false based on success or fail
	 */
	
	isChecked: function(){
		var obj = this;
		var j = 0;
		var k = 0;
		while(j < this.checked_list.length)
		{
			if(this.checked_list[j].isChecked())
			{
				k++;
			}
			j++;
		}
		if(k >= obj.getNumChecked())
		{
			return true;
		}
		else
		{
			return false;
		}
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'Please select at least '+this.numChecked+' option(s)';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * used when validateAll is called on a CheckedContainer
	 * @return {boolean} true if input is valid false otherwise
	 */
	validate: function(obj){
		//var obj = this;
		
		if(obj.isChecked())
		{
			obj.unSetClass();
			return true;
		}
		else
		{
			obj.setClass(); 
			return false;
		}
	}
});


/**
 * @augments Validator
 */
Validation.CheckBox = Class.create(Validation.Validator,{
	/** @lends CheckBox.prototype */ 
	/**
	 * a single checkbox element, varifies that the checkbox is checked
	 * @param {HTMLElementAttribute} el the html input element id
	 * @constructs 
	 */
	initialize: function($super, el){
		this.el = el;
		$super(this.el);
	},

	/**
	 * used to call this.el anywhere
	 * @return {HTMLElement} this.el - global variable passed to the object on creation
	 */
	getEl: function(){
		return this.el;
	},
	
	/**
	 * checks to see if the checkbox or radio is checked
	 * @return {boolean} if checked true falso otherwise
	 */
	isChecked: function(){
		var obj = this;
		
		if(obj.getEl().checked)
		{
			return true;
		}
		else
		{
			return false;
		}
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'The checkbox must be checked to continue';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},

	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj - must pass itself to validate
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function(obj){
		if(obj.isChecked())
		{
			obj.unSetParentClass();
			return true;
		}
		else
		{
			obj.setParentClass();
			return false;
		}
	}
	
});


	
/**
 * object that is for a standard select option dropdown menu where the defaultVal is the value which is not an option but a description (i.e. Please Choose)
 */
Validation.Select = Class.create(Validation.Validator, {
/** @lends Select.prototype */ 
	/**
	 * @param {HTMLElement} el the htm input element to be validated
	 * @param {String} defaultVal the default value of the select element
	 * @constructs 
	 */
	initialize: function($super, el, defaultVal){
		$super(el);
		this.defaultVal = defaultVal;
	},
	
	/**
	 * used to get this.defaultVal anywhere
	 * @return {string} this.defaultVal
	 */
	getDefaultVal: function(){
		return this.defaultVal;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'Select an option';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
		
	/**
	 * the validate function which checks against specific conditions
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function(obj){
		if(obj.getVal() == obj.getDefaultVal())
		{
			obj.setClass();
			return false;
		}
		else

		{
			obj.unSetClass();
			return true;
		}
	}
	
		
});


	
/**
 * object that is for normal text input without restrictions except it must contain something
 */
Validation.Normal = Class.create(Validation.Validator, {
/** @lends Normal.prototype */ 
	/**
	 * @param {HTMLElement} el the html input element id
	 * @constructs 
	 */
	initialize: function($super, el){
		
		this.flag = false;
		$super(el);
	},
		
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'A required field has not been filled in';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	/**
	 * call this method in order to toggle the requirement of filling something in off
	 */
	toggleOff: function(){
		this.flag = true;
		var fn = this.quickValidate();
		
		var obj = this;
		var func = function(){
			this.flag = true;
			this.validate(obj);
		}
		obj.unObserve();
		Element.observe(this.el, 'change', func.bindAsEventListener(obj));
	}, 
	
	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function(obj){
		if(this.flag)
		{
			obj.unSetClass();
			return true;
		}
		
		if(obj.getVal().length <= 0)
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}
	
		
});


/**
 * an input object which must be in the correct format that PIDs are in
 * (i.e. a1234567)
 */
Validation.PID = Class.create(Validation.Normal, {
/** @lends PID.prototype */ 
	/**
	 * Used to validate field with a PID format
	 * (i.e. a1234567)
	 * @constructs 
	 */
	initialize: function($super, el){
		$super(el);
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'You have entered an invalid PID. Valid format is a1234567';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} based on wether or not the input is valid
	 */
	validate: function($super, obj){
		
		//var pFn = $super(obj);
		//call parent function validate first
		if(!$super(obj))
			return false;
		
		if(this.flag && obj.getVal().length <= 0)
		{
			return true;
		}
		
		var pidFirstLetter = obj.getVal().substr( 0, 1);
		var pidNumbers = obj.getVal().substr( 1, 7);
		//the pid is a letter and 7 number so the length must be 8
		if(obj.getVal().length != 8 || !obj.hasValidChars("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",pidFirstLetter) || !obj.hasValidChars("0123456789",pidNumbers))
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}
});

/**
 * object that holds input elements which have a maximum length
 */
Validation.MaxLength = Class.create(Validation.Normal, {
/** @lends MaxLength.prototype */ 
	/**
	 * @param {HTMLElementAttribute} el the html input element id
	 * @param {integer} maxLen a number which is the maximum amount of characters allowed to be inputted
	 * @constructs 
	 */
	initialize: function($super, el, maxLen){
		$super(el);
		this.maxLen = maxLen;
	},
	
	/**
	 * used to call this.maxLen anywhere
	 * @return this.maxLen - global variable passed to the object on creation
	 */
	getMaxLen: function(){
		return this.maxLen;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'You may only enter up to '+this.maxLen+' characters';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){
		
		//call parent function validate first
		if(!$super(obj))
			return false;
			
		if(obj.getVal().length > obj.getMaxLen())
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}	
		
});


	
/**
 * NO DESC
 */
Validation.InBetween = Class.create(Validation.Normal, {
/** @lends InBetween.prototype */ 
	/**
	 * this function has a min length and max length and if the 2 are equal it can be used as a certain length object* 
	 * @constructs 
	 * 
	 * @param {HTMLElement} el the html input element id
	 * @param {integer} maxLen the maximum length of the input
	 * @param {integer} minLen the minimum length of the input
	 
*/
	initialize: function($super, el, minLen, maxLen){
		$super(el);
		this.minLen = minLen;
		this.maxLen = maxLen;
	},
	
	/**
	 * used to call this.maxLen anywhere
	 * @return {int} this.maxLen - global variable passed to the object on creation
	 */
	getMaxLen: function(){
		return this.maxLen;
	},
	
	/**
	 * used to call this.minLen anywhere
	 * @return this.minLen - global variable passed to the object on creation
	 */
	getMinLen: function(){
		return this.minLen;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'Amount of characters must be between '+this.getMinLen()+' and '+this.getMaxLen()+'.';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){
		
		//call parent function validate first
		if(!$super(obj))
			return false;
		
		if(this.flag && obj.getVal().length <= 0)
		{
			return true;
		}
		
		if((obj.getVal().length) > obj.getMaxLen() || (obj.getVal().length) < obj.getMinLen())
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}	
		
});



/**
 * object that holds input elements which have a maximum length and must only contain values in restriction parameter
 */
Validation.Restricted = Class.create(Validation.Normal, {
/** @lends Restricted.prototype */ 
	/**
	 * This object is used when the user can only enter certain symbols, letters or numbers
	 * @param {HTMLElement} el the html input element to be validated
	 * @param {integer} maxLen the maximum length of the input text
	 * @param {string} restriction what the input text is restricted to (i.e. numbers, letters)
	 * @constructs 
*/
	initialize: function($super, el, restriction){
		$super(el);
		this.restriction = restriction;
	},
	
	/**
	* used to get this.restriction anywhere
	* @return - this.restriction - int|string
	*/
	getRestriction: function(){
		return this.restriction;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'A field contains invalid characters.';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){
		
		if(!$super(obj))
			return false;
			
		if(!obj.hasValidChars(obj.getRestriction(), obj.getVal()))
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}
	
		
});


	/**
 * object that holds input elements which have a maximum length and must only contain values in restriction parameter
 */

Validation.RestrictedInBetween = Class.create(Validation.InBetween, {
/** @lends RestrictedInBetween.prototype */ 
	/**
	 * This object is used when the user can only enter certain symbols, letters, or numbers and must enter a certain amount (i.e. between 2-5)
	 * @param {HTMLElement} el the html input element to be validated
	 * @param {integer} maxLen the maximum length of the input text
	 * @param {integer} minLen the minimum length of the input text
	 * @param {string} restriction what the input text is restricted to (i.e. numbers, letters)
	 * @constructs 
*/
	initialize: function($super, el, minLen, maxLen, restriction){
		$super(el, minLen, maxLen);
		this.restriction = restriction;
	},
	
	/**
	* used to get this.restriction anywhere
	* @return - this.restriction - int|string
	*/
	getRestriction: function(){
		return this.restriction;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'A field contains invalid characters. And must contain between '+this.minLen+' and '+this.maxLen+' characters.';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){
		
		if(!$super(obj))
			return false;
		
		if(this.flag && obj.getVal().length <= 0)
		{
			return true;
		}
		
		if(!obj.hasValidChars(obj.getRestriction(), obj.getVal()))
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}
	
		
});

/**  
 * t that is for normal text input without restrictions except it must contain something
 */
Validation.MaxWordCount = Class.create(Validation.Normal, {
/** @lends MaxWordCount.prototype */ 
	/**
	 * This object is used when the user has a maximum word limit
	 * @param {HTMLElement} el the html input element id
	 * @param {integer} maxWords the maximum number of words allowed to be inputted
	 * @constructs 
*/
	initialize: function($super, el, maxWords){
		this.maxWords = maxWords;
		$super(el);
	},
		
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'Only allowed up to '+this.maxWords+' words.' ;
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){

		if(!$super(obj))
			return false;
		
		// First trim the string, otherwise each extra space at the beginning or
		// end of the string will count as a word.

		//alert(obj.getVal().split(" ").length);
		//alert(obj.trim(obj.getVal()).split(" ").length);
		//alert(obj.removeDblSpace(obj.trim(obj.getVal())).split(" ").length);
		var size = obj.removeDblSpace(obj.trim(obj.getVal())).split(" ").length;
		if(size > this.maxWords)
		{
			obj.setClass();
			return false;
		}
		else
		{
			obj.unSetClass();
			return true;
		}
	}
	
		
});
/*
Validation.RestrictChars = Class.create({

	initialize: function(chars){
		this.errorMessageText = null;
		this.chars = chars;
	},
	


	validate: function(obj){
		var value, flag = false;
	
		value = obj.getVal();
		// Look through all characters of the string and see if
		// any of them are invalid chars.
		for(i=0;i<value.length ; i++){
			// Look at each invalid character.
			for(j=0;j<this.chars.length;j++){
				// If invalid charcter found.
				if(value[i] == this.chars[j])
					// Error.
					return false;
			}		
		}
	}
});
	*/
/**
 * NO DESC
 */
Validation.CompareEqual = Class.create({
	/** @lends CompareEqual.prototype */ 
	/**
	 * This container will check that all its objects are equal, otherwise all return invalid
	 * @param {integer} numChecked the minimum number required to be checked (1 for radios)
	 * @param {HTMLElementAttribute} el the containers element id
	 * @constructs 
*/
	initialize: function(){
		this.errorMessageTxt = null;
		this.compare_equal_list = new Array(); //list of compare objects
	},		
	
	/**
	 * add an element object to the list and stops the event listener on the elements
	 * @param {Object} obj takes in a JS CheckBox object and adds it to the list
	 */
	addObj: function(obj, container){
		obj.unObserve();
		if(container != null)
		{
			Element.observe(obj.getId(), 'change', function(){ container.validate(container);} );
		}
		this.compare_equal_list.push(obj);
		return obj;
	},
	
	/**
	 * set the class name to be used when setting the class for invalid fields (applies invalid class to any validation objects currently in this FormValidator container
	 * @param {string} className the desired name to set for the class of invalid objects
	 */
	setInvalidClass: function(className){
		var i = 0;
		while(i < this.compare_equal_list.length)
		{
			this.compare_equal_list[i].setInvalidClass(className);
			i++;
		}
	},
	
	getId: function(){
		//if theres at least 1 in the list
		if(this.compare_equal_list[0])
			return this.compare_equal_list[0].getId();
		else
			return '';
	},
	
	

	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'Fiels must match.' ;
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * used when validateAll is called on this container
	 * @param {object} obj must pass itself to validate
	 * @return {boolean} true if input is valid false otherwise
	 */
	validate: function(obj){
		var value, newValue, i, j, flag = false;
		//var obj = this;
		for(i=0; i < obj.compare_equal_list.length; i++)
		{

			if(i == 0)
			{
				value = obj.compare_equal_list[i].getVal();
			}
			else
			{
				newValue = obj.compare_equal_list[i].getVal();
							
				if(newValue != value)
				{
					for(j=0; j < obj.compare_equal_list.length; j++)
					{
						obj.compare_equal_list[j].setClass();
					}
				
					return false;
				}
				
			}
			
			
		}
		for(j=0; j < obj.compare_equal_list.length; j++)
		{
			if(obj.compare_equal_list[j].validate(obj.compare_equal_list[j]) == false)
			{
				flag = true;
			}
		}
		return !flag;
	}
});


/**
 * object that holds input elements which must only contain values in restriction parameter
 */
Validation.InvalidChars = Class.create(Validation.Normal, {
/** @lends Restricted.prototype */ 
	/**
	 * This object is used when the user can only enter certain symbols, letters or numbers
	 * @param {HTMLElement} el the html input element to be validated
	 * @param {string} invalidChars what the input text is restricted to (i.e. numbers, letters)
	 * @constructs 
*/
	initialize: function($super, el, invalidChars){
		$super(el);
		this.invalidChars = invalidChars;
	},
	
	/**
	* used to get this.restriction anywhere
	* @return - this.restriction - int|string
	*/
	getInvalidChars: function(){
		return this.invalidChars;
	},
	
	/**
	* used to create the list of errors
	* @return {string} returns a string that is a description of required parameters
	*/
	getErrorMessage: function(){
		if(this.errorMessageTxt == null)
		{
			return 'A field contains invalid characters.';
		} else {
			return this.errorMessageTxt;
		}
	},
	
	/**
	* used to create the list of errors
	* sets a custom message
	*/
	setErrorMessage: function(str){
		this.errorMessageTxt = str;
	},
	
	/**
	 * the validate function which checks against specific conditions
	 * @return {boolean} Returns true if valid false otherwise
	 */
	validate: function($super, obj){
		
		if(!$super(obj))
			return false;
		
		
		if(!obj.hasInvalidChars(obj.getInvalidChars(), obj.getVal()))
		{
			obj.setClass();
			
			return false;
		}
		else
		{
			
			obj.unSetClass();
			return true;
		}
	}
	
		
});