// Form Validation Object Class 
// by Cyrus Collier




// Constructor function

function FormValidation(f,e)
{

// Store document's form object and container to display validation errors

	f = document.getElementById(f) || document.forms[f] || document.forms[0];
	if(!f) throw new Error('Undefined FormValidation() object. No forms exist in this document.');
	this.object = f;
	this.error = document.getElementById(e);
	if(this.error.getElementsByTagName('li')[0].innerHTML != '') this.error.parentNode.style.display = 'block';
	this.errorMessages = "";
	
	this.requiredFields = [];
	this.checkFields = {
		'email': { fields: [], allowed: /^[A-Za-z0-9_\-\.]+@[A-Za-z0-9\-\.]+\.[A-Za-z]{2,4}$/, addHandler: false },
		'date': { fields: [], allowed: /[0-9\/]/, addHandler: true },
		'phone': { fields: [], allowed: /[0-9\-]/, addHandler: this.formatPhone },
		'integer': { fields: [], allowed: /[0-9]/, addHandler: true },
		'float': { fields: [], allowed: /[0-9\.]/, addHandler: true },
		'password': { fields: [], allowed: null, addHandler: false }
	}
}
// Check existence of element/object

FormValidation.prototype.exists = function(id)
{
	return (typeof id == 'object') ? id : (this.object[id] || document.getElementById(id));
}

// Check for multiple input groups

FormValidation.prototype.multiple = function(obj)
{
	return (obj.length && obj.length>1) ? obj : false;
}

FormValidation.prototype.getContainer = function(obj,t)
{
	if(!this.multiple(obj)) return obj;
	var span = obj[0];
	do span = span.parentNode; while(t && (!!(span.cellIndex+1) || !!(span.rowIndex+1) || !!span.rows)); // get multiple item field parent container (escape tables)
	return span;
}


// Submit form (boolean arg: validate first?)

FormValidation.prototype.submit = function(v)
{
	var s = v ? this.validate() : true;
	if(s) this.object.submit();
}

// Reset form (string arg: reset message (opt))

FormValidation.prototype.reset = function(m)
{
	var message = m || "Are you sure you want to reset all fields?";
	if(window.confirm(message)) this.object.reset();
}

// Get all form elements from within a container

FormValidation.prototype.getInputs = function(id)
{
	obj = this.exists(id);
	if(!obj) return;
	var elements = [];
	var inputs = obj.getElementsByTagName('input');
	for(i=0;i<inputs.length;i++) elements.push(inputs[i]);
	inputs = obj.getElementsByTagName('select');
	for(i=0;i<inputs.length;i++) elements.push(inputs[i]);
	inputs = obj.getElementsByTagName('textarea');
	for(i=0;i<inputs.length;i++) elements.push(inputs[i]);
	inputs = obj.getElementsByTagName('button');
	for(i=0;i<inputs.length;i++) elements.push(inputs[i]);
	return elements;
}

// check if object is a form element

FormValidation.prototype.isInput = function(id)
{
	obj = this.exists(id);
	if(!obj) return false;
	return (obj.tagName=='INPUT' || obj.tagName=='SELECT' || obj.tagName=='TEXTAREA' || obj.tagName=='BUTTON');
}


// Add required fields

FormValidation.prototype.addRequired = function()
{
	var a = arguments;
	for(var i=0;i<a.length; i++)
	{
		var input = this.exists(a[i]);
		var index = this.requiredFields.indexOf(a[i]);
		if(input && index == -1) this.requiredFields.push(a[i]);
	}
}

// Remove required fields

FormValidation.prototype.removeRequired = function()
{
	var a = arguments;
	for(var i=0;i<a.length; i++)
	{
		var input = this.exists(a[i]);
		var index = this.requiredFields.indexOf(a[i]);
		if(input && index != -1) this.requiredFields.splice(index,1);
	}	
}

// Add additional field validation type

FormValidation.prototype.addFieldCheck = function(id,type)
{
	var obj = this.exists(id);
	if(!obj || !(type in this.checkFields)) return false;
	var index = this.checkFields[type].fields.indexOf(id);
	if(index == -1) this.checkFields[type].fields.push(id);
	if(this.checkFields[type].allowed && this.checkFields[type].addHandler)
	{
				
		if(!this.multiple(obj)) obj = new Array(obj);
		for(var i=0;i<obj.length;i++)
		{
			obj[i].onkeypress = filter(this.checkFields[type].allowed);
			if(typeof this.checkFields[type].addHandler == "function") this.checkFields[type].addHandler(obj[i]);
		}
	}
	
	function filter(regexp)
	{
		return function(event)
		{
			var e = event || window.event;
			var code = e.charCode || e.keyCode;
			if(e.charCode == 0) return true;
			if(e.ctrlKey || e.altKey) return true;
			if(code < 32) return true;
			
			var c = String.fromCharCode(code);
			if(regexp.test(c)) return true;
			else
			{
				if(e.preventDefault) e.preventDefault();
				if(e.returnValue) e.returnValue = false;
				return false;
			}
		}
	}
}

// Remove additional field validation type

FormValidation.prototype.removeFieldCheck = function(id,type)
{
	if(!this.exists(id) || !(type in this.checkFields)) return false;
	var index = this.checkFields[type].indexOf(id);
	if(index != -1) this.checkFields[type].splice(index,1);
}

// Add class to element

FormValidation.prototype.addClass = function(id,c)
{
	var obj = this.exists(id);
	if(!obj) return;
	var regexp = new RegExp(c);
	if(!obj.className.match(regexp)) obj.className += ' '+c;
}

// Remove class from an element

FormValidation.prototype.removeClass = function(id,c)
{
	var obj = this.exists(id);
	if(!obj) return;
	var regexp = new RegExp(c,"g");
	obj.className = obj.className.replace(regexp,'');
}

//Check for class name

FormValidation.prototype.checkClass = function(id,c)
{
	var obj = this.exists(id);
	if(!obj) return;
	var regexp = new RegExp(c,"g");
	return regexp.test(obj.className);
}

// toggle required field
FormValidation.prototype.toggleField = function(target,on,off,hide,req,reverse,func)
{
	if(typeof target == "string") target = new Array(target);
	if(typeof on == "string") on = new Array(on);
	if(typeof off == "string") off = new Array(off);
	var inputs = [];
	for(var i=0;i<target.length;i++)
	{
		var obj = this.exists(target[i]); if(!obj) continue;
		if(!this.isInput(obj)) obj = this.getInputs(obj);
		if(!this.multiple(obj)) obj = new Array(obj);
		inputs = inputs.concat(obj);
		if(hide) obj.style.display == "none";
	}
	if(!reverse) for(var i=0;i<inputs.length;i++) { inputs[i].disabled = "true"; }
	for(var i=0;i<on.length;i++)
	{
		var obj = this.exists(on[i]); if(!obj) continue;
		obj.onclick = toggle(true, target, inputs, hide, req, reverse, this, func);
	}
	for(var i=0;i<off.length;i++)
	{
		var obj = this.exists(off[i]); if(!obj) continue;
		obj.onclick = toggle(false, target, inputs, hide, req, reverse, this, func);
	}

	function toggle(toggle,target,inputs,hide,req,reverse,obj, func)
	{
		return function()
		{
			if(this.type=="checkbox") toggle = this.checked;
			if(reverse) toggle = !toggle;
			for(var i=0;i<target.length;i++) if(hide) target.style.display = toggle ? "block" : "none";
			for(var i=0;i<inputs.length;i++)
			{
				inputs[i].disabled = !toggle;
				if(req) toggle ? obj.addRequired(inputs[i]) : obj.removeRequired(inputs[i]);
			}
			if(func) func(this);
		}
	}
}

// autoformat phone numbers

FormValidation.prototype.formatPhone = function(obj)
{	
	obj.maxLength = "12";
	if(obj.addEventListener) obj.addEventListener("keyup",format,false);
	else obj.onkeyup = format;
	function format(event)
	{
		var e = event || window.event;
		var code = e.charCode || e.keyCode;
		var l = this.value.length;
		if((l==3 || l==7) && code!=8) this.value += "-";
	}
}
	

  ///////////////////////
 //// Validate form ////
///////////////////////

FormValidation.prototype.validate = function()
{
	var complete = true;
	this.error.innerHTML = '';
	this.errorMessages = '';
	this.error.parentNode.style.display = 'none';
	var tags = this.object.getElementsByTagName("*");
	for (var i=0;i<tags.length;i++) this.removeClass(tags[i],'error'); //scan all tags within form to clear error class
	for(var i in this.requiredFields) //scan all required fields
	{
		var obj = this.exists(this.requiredFields[i]);
		if(!obj) continue;
		var c = false;
		var span = this.getContainer(obj,true); // get multiple item field parent container (escape tables)
		if(!this.isInput(obj) && !this.multiple(obj)) obj = this.getInputs(obj);// get inputs from non-form container
		if(span != obj)
		{
			for(var i=0; i<obj.length;i++)
			{
				if(this.checkClass(obj,'optional')) continue;
				if(obj[i].checked || ((obj[i].type == 'text') && obj[i].value)) c = true; // check for empty value/unchecked
			}
		}
		else c = !!obj.value;  //check for empty values		

		if(!c)
		{
			this.addClass(span,'error');
			complete = false;
		}
		
		if(!c && obj.type == 'file') this.errorMessages += '<br />Please select a file to upload.';
	}
	
	if(!complete) this.error.innerHTML = '<li>Please complete all required fields.</li>';
	
	//email address validation
	
	for(var i=0;i<this.checkFields.email.fields.length; i++)
	{
		var obj = this.exists(this.checkFields.email.fields[i]);
		if(!this.checkFields.email.allowed.test(obj.value))
		{
			this.addClass(obj,'error');
			this.errorMessages += '<li>Please enter a valid e-mail address.</li>';
			complete = false;
		}
	}
	
	//password confirm validation
	
	if(this.checkFields.password.fields.length==2)
	{
		var password = this.exists(this.checkFields.password.fields[0]);
		var password2 = this.exists(this.checkFields.password.fields[1]);
		if(password.value != password2.value)
		{ 
			this.errorMessages += '<li>The two passwords you entered do not match.</li>';
			this.addClass(password,'error');
			this.addClass(password2,'error');
			complete = false;
		}
	}
	
	//date validation
	for(var i=0;i<this.checkFields.date.fields.length; i++) 
	{
		var obj = this.exists(this.checkFields.date.fields[i]);
		if(!this.multiple(obj)) obj = new Array(obj); 
		for(var j=0;j<obj.length;j++)
		{
			
			var datearray = [];
			var valid = true;
			var c = true;
			var span = this.getContainer(obj[j]);
			if(!obj[j].value) continue;
			var temparray = obj[j].value.split("/");
			for (var k=0; k<temparray.length; k++) 
			{
				if (temparray[k].value != '') datearray.push(temparray[k]);
				if (temparray[k].value < 1) valid = false;
			}
			if (datearray.length == 3) //check MM/DD/YY format
			{
				if(datearray[0] > 12) valid = false; //valid month?
				if(datearray[1] > 31) valid = false; //valid day?
			} else valid = false;
			if(!valid)
			{
				this.errorMessages += '<li>Please enter a valid date (MM/DD/YYYY).</li>';
				c = false;
			} else {
				if(datearray[2].length < 4)
				{
					var year =  2000 + 1*datearray[2];
					if(this.multiple(obj[i])) obj[i][2].value = year;
					else obj[i].value = obj[i].value.replace(new RegExp(datearray[2]),year);
				}
				else
				{
					var year = 1*datearray[2];
				}
				var date = new Date(year,datearray[0]-1,datearray[1]);
				var now = new Date();
				if(date.getTime() <= now.getTime())
				{ 
					this.errorMessages += '<li>Please enter a future date.</li>';
					c = false;
				}
			}
			if(!c)
			{
				this.addClass(span,'error');
				complete = false;
			}
		}
	}

	if(!complete)
	{
		this.error.innerHTML += this.errorMessages;
		window.scrollTo(0,0);
		this.error.parentNode.style.display = 'block';
	}
	return complete;
}

/* add Array.indexOf() for older browsers */

if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}