var FUtils=function(){};
FUtils.Trim=function(str){
	str = FUtils.LTrim(str);
	return FUtils.RTrim(str);
}
FUtils.RTrim=function(str){
	while(str.charAt((str.length -1))==" "){
		str = str.substring(0,str.length-1);
	}
	return str;
}
FUtils.LTrim=function(str){
	while(str.charAt(0)==" "){
		str = str.replace(str.charAt(0),"");
	}
	return str;
}
var FError={
	INVALID_EMAIL:'please enter a valid Email',
	EMPTY:'%str%cannot be empty',
	PASS_TOO_SHORT:'%str%must be at least 6 characters',
	NO_MATCH:'%str%must match %match%',
	NO_VALUE:'%str%please select a value',
	NOT_CHECKED:'%str%must be selected',
	PASS_NO_NUMBERS:'%str%must contain at least one number',
	TOO_LONG:'%str%must be less than %length% characters',
	INVALID_NUMBER:'%str% must be a valid number',
	
	INVALID_START_DATE:'%str%start date is invalid',
	INVALID_END_DATE:'%str%end date is invalid',
	INVALID_START_TIME:'%str%start time is invalid',
	INVALID_END_TIME:'%str%end time is invalid',
	INVALID_DATE_RANGE:'%str%end date must be greater than start date'
}
function FField(qfield,qparent,qconfig){
	this.parent=qparent;
	this.id=qfield;
	this._config=qconfig;
	this.el=null;
	this.elType=null;
	this.feedback=null;
	this.tooltip=null;
	this.isTopFeedback=false;
	
	this.changeField=function(qtooltip){
		return this._validate(qtooltip);
	}
	this.resetValidation=function(){
		var fdiv=this.getFeedbackDiv();
		if(fdiv){
			YAHOO.util.Dom.setStyle(fdiv,'display','none');
			fdiv.innerHTML='';
		}
		var fdiv=this.getTooltipDiv();
		if(fdiv){
			YAHOO.util.Dom.setStyle(fdiv,'display','none');
			fdiv.innerHTML='';
		}
	}
	this._validate = function(qtooltip){
		var valid = true;
		var error;
		var args;
		var doValidation;
		if((this.elType=='text' || this.elType=='password') && !this._config.isRequired && FValidation.notEmpty(this.el)!=null){
			doValidation=false;
		}else{
			doValidation=true;
		}
		if(doValidation){
			for (var i = 0; i < this.validate.length; i++) {
				args = (this._config.validateArgs) ? this._config.validateArgs[i] : null;
				if (this._config.fieldGroup && !this._config.fieldGroupIgnore) {
					var fields = this._config.fieldGroup;
					for (var j = 0; j < fields.length; j++) {
						error = this.validate[i](CSDC.getElt(fields[j]), args, this._config);
						if (error != null) {
							valid = false;
							break;
						}
					}
					if (error != null) {
						break;
					}
				}
				else {
					error = this.validate[i](this.el, args, this._config);
					if (error != null) {
						valid = false;
						break;
					}
				}
			}
		}
		if(qtooltip){
			if(valid){
				this.getTooltipDiv().innerHTML='';
			}else{
				this.setTooltipError(error,i);
			} 
			fdiv=this.getFeedbackDiv();
			var visible=YAHOO.util.Dom.getStyle(fdiv,'display');
			if(visible!='none'){
				this.parent.checkError(-1);
				YAHOO.util.Dom.setStyle(fdiv,'display','none');
			}
		}else{
			var fdiv=this.getFeedbackDiv();
			this.getTooltipDiv().innerHTML='';
			var visibility=YAHOO.util.Dom.getStyle(fdiv,'display');
			if(valid){
				if(visibility!='none'){
					YAHOO.util.Dom.setStyle(fdiv,'display','none');
					this.parent.checkError(-1);
				}
			}else{
				this.setFeedbackError(error,i);
				if(visibility=='none'){
					var visible=this.getVisibleValue();
					YAHOO.util.Dom.setStyle(fdiv,'display',visible);
					this.parent.checkError(1);
				}
			}
			
			
		}
		return valid;
	}
	this.setFeedbackError=function(qcode,qatValidation){
		var error=FError[qcode] || qcode;
		if (this._config.feedbackOverride) {
			error = this._config.feedbackOverride.replace('%str%', this._config.name)
		}
		else {
			switch (qcode) {
				default:
					error = error.replace('%str%', this._config.name + ' ');
					break;
				case 'INVALID_START_DATE':
				case 'INVALID_END_DATE':
				case 'INVALID_START_TIME':
				case 'INVALID_END_TIME':
				case 'INVALID_DATE_RANGE':
				case 'NO_VALUE':
					error = error.replace('%str%', this._config.name + ': ');
					break;
				case 'NO_MATCH':
					error = error.replace('%str%', this._config.name + ' ');
					var validationArgs = this._config.validateArgs[qatValidation];
					var matchName = this.parent.get(validationArgs.field)._config.name;
					error = error.replace('%match%', matchName);
					break;
				case 'TOO_LONG':
					error = error.replace('%str%', this._config.name + ' ');
					var validationArgs = this._config.validateArgs[qatValidation];
					error = error.replace('%length%', validationArgs.maxLength);
				break;
			}
		}
		this.getFeedbackDiv().innerHTML=error;
	}
	
	this.setTooltipError=function(qcode,qatValidation){
		var error;
		if (this._config.feedbackOverride) {
			error = this._config.feedbackOverride.replace('%str%', this._config.name)
		}
		else {
			error = FError[qcode] || qcode;
			switch (qcode) {
				default:
					error = error.replace('%str%', '');
					break;
				case 'NO_MATCH':
					error = error.replace('%str%', '');
					var validationArgs = this._config.validateArgs[qatValidation];
					var matchName = this.parent.get(validationArgs.field)._config.name;
					error = error.replace('%match%', matchName);
				break;
				case 'TOO_LONG':
					error = error.replace('%str%', this._config.name + ' ');
					var validationArgs = this._config.validateArgs[qatValidation];
					error = error.replace('%length%', validationArgs.maxLength);
				break;
			}
		}
		this.getTooltipDiv().innerHTML=error;
	}
	this.getVisibleValue=function(){
		if(this.isTopFeedback){
			return 'block';
		}else{
			return 'inline';
		}
	}
	this.init=function(){
		this.el=CSDC.getElt(this.id);
		if(this.el){
			this.elType=this.el.type;
			if(this._config.validate){
				if(this._config.validate instanceof Function){
					this._config.validate=[_config.validate];
				}
				this.validate=this._config.validate;
			}else{
				this.validate=[function(qtooltip){return true;}];
			}
			
			if(this._config.fieldGroup){
				var fields=this._config.fieldGroup;
				for(var i=0;i<fields.length;i++){
					if(this._config.monitorKeyUp){
						var asFeedback=(this._config.keyUpAsFeedback)? true:false;
						YAHOO.util.Event.addListener(fields[i], "keyup", this.bindEvent('keyup',asFeedback));
					}
					if(this._config.monitorChange){
						var asFeedback=(this._config.changeAsFeedback)? true:false;
						YAHOO.util.Event.addListener(fields[i], "change", this.bindEvent('change',asFeedback));
					}
				}
			}else{
				if(this._config.monitorKeyUp){
					var asFeedback=(this._config.keyUpAsFeedback)? true:false;
					YAHOO.util.Event.addListener(this.el, "keyup", this.bindEvent('keyup',asFeedback));
				}
				if(this._config.monitorChange){
					var asFeedback=(this._config.changeAsFeedback)? true:false;
					YAHOO.util.Event.addListener(this.el, "change", this.bindEvent('change',asFeedback));
				}
			}
			this.getFeedbackDiv();
			if(this._config.isRequired && !this._config.ignoreAsterisk){
				var label=CSDC.getElt(this.id+'Label');
				if(label) label.innerHTML+='*';
				label=null;
			}
		}
	}
	
	this.getFeedbackDiv=function(qfeedbackHolder){
		if(this.feedback==null){
			this.feedback=CSDC.getElt(this.id+"Feedback");
			if(!this.feedback){
				var feedbackHolder=this.parent.getFeedbackHolder();
				if (feedbackHolder) {
					this.feedback = CSDC.makeDom('div', { id:this.id+"Feedback",className:'cFormFeedbackBig' });
					feedbackHolder.appendChild(this.feedback);
					this.isTopFeedback=true;
				}else {
					this.feedback = CSDC.makeDom('span', { id:this.id+"Feedback",className:'formFeedback' });
					this.el.parentNode.insertBefore(this.feedback, this.el.nextSibling);
				}
				YAHOO.util.Dom.setStyle(this.feedback,'display','none');
			}
		}
		return this.feedback;
	}
	
	this.getTooltipDiv=function(){
		if (this.tooltip == null) {
			this.tooltip = CSDC.getElt(this.id + "Tip");
			if(!this.tooltip){
				this.tooltip = CSDC.makeDom('span', { id:this.id+"Tip",className:'cFormFeedbackTip' });
				if(this._config.fieldGroup){
					var el=CSDC.getElt(this._config.fieldGroup[this._config.fieldGroup.length-1]);
					el.parentNode.insertBefore(this.tooltip, el.nextSibling);
				}else{
					this.el.parentNode.insertBefore(this.tooltip, this.el.nextSibling);
				}
			}			
		}
		return this.tooltip;
	}
	this.bindEvent=function(qevent,qasFeedback){
		var self=this;
		var asFeedback=qasFeedback;
		switch(qevent){
			case 'change':
				return function(e){self.changeField(!asFeedback);}
			break;
			case 'keyup':
				return function(e){self.changeField(!asFeedback);}
			break;
		}
	}
}


var FormMonitor=function(qform,qconfig){
	this.id=qform;
	this._config=qconfig;
	this.matrix=new Object();
	this.feedbackHolder=null;
	this.errorCount=0;
	this.feedbackInfo=null;

	this.add=function(qfield,qconfig){
		this.matrix[qfield]=new FField(qfield,this,qconfig);
		return this.matrix[qfield];
	}
	this.get=function(qfield){
		if(this.matrix[qfield]){
			return this.matrix[qfield];
		}else{
			return null;
		}
	}
	
	this.checkError=function(qinc){
		this.errorCount+=qinc;
		if(this.errorCount<0) this.errorCount=0;
		if(this.feedbackHolder){
			if(this.errorCount==1){
				YAHOO.util.Dom.setStyle(this.feedbackHolder,'display','block');
			}else if(this.errorCount==0){
				YAHOO.util.Dom.setStyle(this.feedbackHolder,'display','none');
			}
		}
	}

	
	this.init=function(){

		this._submit=(this._config.submit)? this._config.submit: function(){return true;};
		if(this._config.feedbackHolder){
			this.feedbackHolder=CSDC.getElt(this._config.feedbackHolder);
			if(!this.feedbackHolder){
				this.feedbackHolder=CSDC.makeDom('div',{id:this._config.feedbackHolder,className:'cFeedbackHolder'});
				var form=CSDC.getElt(this.id);
				form.insertBefore(this.feedbackHolder, form.firstChild);
			}
			YAHOO.util.Dom.setStyle(this.feedbackHolder,'display','none');
			if(this._config.addFeedbackInfo){
				this.feedbackInfo=CSDC.makeDom('div',{id:this.feedbackHolder.id+'Info',className:'cFeedbackInfo'})
				this.feedbackInfo.innerHTML='Some fields are invalid. Please enter the correct information before submitting';
				this.feedbackHolder.appendChild(this.feedbackInfo);
			}
		}
		for( var qfield in this.matrix){
			this.matrix[qfield].init(this.feedbackHolder);
		}
	}
	
	this.getFeedbackHolder=function(){
		return this.feedbackHolder;
	}
	
	this.validate=function(){
		var valid=true;
		var tmpvalid=true;
		for( var qfield in this.matrix){
			tmpvalid=this.matrix[qfield]._validate();
			if(!tmpvalid){
				valid=false;
			}
		}
		return valid;
	}
	
	this.resetValidation=function(){
		for( var qfield in this.matrix){
			this.matrix[qfield].resetValidation();
		}
	}
	
	this.submit=function(e){		
		if(this.validate()){
			return this._submit(e);
		}else{
			if(e) YAHOO.util.Event.stopEvent(e);
			if(this._config.scrollToFeedback && this.feedbackHolder){
				var pos=YAHOO.util.Dom.getXY(this.feedbackHolder);
				window.scrollTo(pos[0],pos[1])
			}
			return false;
		}
	}
	
	this.bindEvent=function(qevent){
		var self=this;
		switch(qevent){
			case 'submit':
				return function(){return self.submit();}
			break;
		}		
	}
	
	this.subscribeToSubmit=function(){
		var self=this;
		YAHOO.util.Event.addListener(this.id, "submit", function(e){
			return self.submit(e);
		});
	}
}

var FValidation=new function(){
	this.notEmpty=function(qel){
		var qvalue=qel.value;
		if(qvalue==''){
			return 'EMPTY';
		}else{
			return null;
		}
	}
	this.maxLength=function(qel,qconfig){
		var qvalue=qel.value;
		if(qvalue.length>qconfig.maxLength){
			return 'TOO_LONG';
		}else{
			return null;
		}
	}
	
	this.email = function(qel,qconfig){
		var qvalue=qel.value;
		if(!/^[a-zA-Z_0-9-'\+~]+(\.[a-zA-Z_0-9-'\+~]+)*@([a-zA-Z_0-9-]+\.)+[a-zA-Z]{2,7}$/.test(qvalue)){
			return 'INVALID_EMAIL';
		}else{
			return null;
		}
	}
	
	this.password=function(qel,qconfig){
		var qvalue=qel.value;
		if(qvalue.length<6){
			return 'PASS_TOO_SHORT';
		}else if (!/\d+/.test(qvalue)) {
			return 'PASS_NO_NUMBERS';
		}else {
			return null;
		}
	}
	
	this.matchField=function(qel,qconfig){
		var qvalue=qel.value;
		var qvalue2=CSDC.getElt(qconfig.field).value;
		if(qvalue!=qvalue2){
			return 'NO_MATCH';
		}else{
			return null;
		}
	}
	
	this.selectedValue=function(qel,qconfig){
		var qvalue=qel.value;
		if(qvalue==''){
			return 'NO_VALUE';
		}else{
			return null;
		}
	}
	
	this.checked=function(qel,qconfig){
		var qvalue=qel.checked;
		if(qvalue==false){
			return 'NOT_CHECKED';
		}else{
			return null;
		}
	}
	
	// +1/-1 hack on hour comparison for day light saving.
	this.compareDateToArr=function(date,arr,timeArr){
		return (date.getDate()==parseInt(arr[2], 10) && date.getMonth()==(parseInt(arr[1], 10)-1) && date.getFullYear()==parseInt(arr[3], 10) && (date.getHours()==timeArr[1]||date.getHours()==timeArr[1]-1||date.getHours()==timeArr[1]+1) && date.getMinutes()==timeArr[2]);
	}
	
	this.dateStr=function(qel,qconfig,fieldConfig){
		var dateStr=CSDC.getElt(qel).value;
		var dateRegEx=/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
		var dateArr=dateRegEx.exec(dateStr);
		if(dateArr==null){
			return 'INVALID_DATE';
		}
		
		var date=new Date(dateArr[3],dateArr[1]-1,dateArr[2],0,0,0);
		
		var isValidSDate=FValidation.compareDateToArr(date,dateArr,['',0,0]);
		if(!isValidSDate){
			return 'INVALID_START_DATE';
		}
		return null;
	}
	this.timeStr=function(qel,qconfig,fieldConfig){
		var timeRegEx=/^(\d{1,2}):(\d{1,2})(am|pm)$/;
		var sTimeArr=timeRegEx.exec(qel.value);
		if (sTimeArr == null || parseInt(sTimeArr[1], 10)>12 || parseInt(sTimeArr[1], 10)<=0) {
			return 'INVALID_TIME';
		}else{
			return null;
		}
	}
	this.selectedDateRange=function(qel,qconfig,fieldConfig){
		//date fields
		var sDateStr=CSDC.getElt(fieldConfig.fieldGroup[0]).value;
		var eDateStr=CSDC.getElt(fieldConfig.fieldGroup[2]).value;

		//timeFields
		var sTime=CSDC.getElt(fieldConfig.fieldGroup[1]);
		var eTime=CSDC.getElt(fieldConfig.fieldGroup[3]);
		
		//validation type, can be full, dates only, or start date only 
		//(the last doesn't need to be defined as it's always done)
		var doFullValidation;
		var doDateValidation;		
		if(fieldConfig.fieldGroup.length==5){
			doFullValidation=(CSDC.getElt(fieldConfig.fieldGroup[4]).checked)? false:true;
			doDateValidation=(doFullValidation)? false : true;
		}else{
			//doFullValidation=(qconfig.fullValidation.checked)? true:false;
			//doDateValidation=(qconfig.dateValidation.checked)? true : false;
			doFullValidation=true;
			doDateValidation=false;
		}		
		
		var dateRegEx=/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/;
		var sDateArr=dateRegEx.exec(sDateStr);
		var eDateArr=dateRegEx.exec(eDateStr);
		
		var sTimeArr;
		var eTimeArr;
		if(doFullValidation){
			var timeRegEx=/^(\d{1,2}):(\d{1,2})(am|pm)$/;
			var sTimeArr=timeRegEx.exec(sTime.value);
			var eTimeArr=timeRegEx.exec(eTime.value);
			if(sTimeArr==null){
				return 'INVALID_START_TIME';
			} else if(eTimeArr==null){
				return 'INVALID_END_TIME';
			}
			if(sTimeArr[3]=='pm' && sTimeArr[1]<12) sTimeArr[1]=parseInt(sTimeArr[1], 10)+12;
			if(sTimeArr[3]=='am' && sTimeArr[1]==12) sTimeArr[1]=0;
			sTimeArr[2]=parseInt(sTimeArr[2], 10);
			
			if(eTimeArr[3]=='pm' && eTimeArr[1]<12) eTimeArr[1]=parseInt(eTimeArr[1], 10)+12;
			if(eTimeArr[3]=='am' && eTimeArr[1]==12) eTimeArr[1]=0;
			eTimeArr[2]=parseInt(eTimeArr[2], 10);
		}else{
			sTimeArr=['',0,0];
			eTimeArr=['',0,0];
		}

		//one of the dates isn't valid
		if(sDateArr==null){
			return 'INVALID_START_DATE';
		}else if((doFullValidation || doDateValidation) && eDateArr==null){
			return 'INVALID_END_DATE';
		}
		
		var startDate=new Date(sDateArr[3],sDateArr[1]-1,sDateArr[2],sTimeArr[1],sTimeArr[2],0);
		//check if it is an existing
		var isValidSDate=FValidation.compareDateToArr(startDate,sDateArr,sTimeArr);
		if(!isValidSDate){
			return 'INVALID_START_DATE';
		}
		
		if (doFullValidation || doDateValidation) {
			var endDate = new Date(eDateArr[3], eDateArr[1] - 1, eDateArr[2], eTimeArr[1], eTimeArr[2], 0);
			//check if it is an existing
			var isValidEDate = FValidation.compareDateToArr(endDate, eDateArr, eTimeArr);
			if (!isValidEDate) {
				return 'INVALID_END_DATE';
			}
			
			//end date must be greater than start date
			if (endDate < startDate) {
				return 'INVALID_DATE_RANGE';
			}
		}
	}
	
	this.isNumber=function(qel,qconfig){
		var qvalue=qel.value;
		var match=qvalue.match(/^\d+$/);
		if(match===null){
			return 'INVALID_NUMBER';
		}else{
			return null;
		}
	}
}