/* Simple & extensible field validation
 * http://www.rich-waters.com/blog/
 * version: 0.9
 * updated: 12/5/06
 *
 * RegEx's from http://www.regexlib.com
 *
 * Copyright (c) 2006 Rich Waters
 * http://www.opensource.org/licenses/mit-license.php
 *
 */

var Validation = Class.create();
Validation.prototype = {
	initialize: function(form) {
		this.form = document.forms[form];
		// if form is not defined, forget the rest
		if (typeof this.form == 'undefined') {
			return false;
		}
		this.elems = document.getElementsByClassName("validate",form);
		this.configRules = [];
		this.keys = [];
		
		// Create the default validator objects
		this.registerValidator(new Validator("numeric",/^(\d|-)?(\d|,)*\.?\d*$/,"Please enter a valid number (only digits 0-9)"));		
		this.registerValidator(new Validator("date",
      /(((0[13578]|10|12)([-.\/])(0[1-9]|[12][0-9]|3[01])([-.\/])(\d{4}))|((0[469]|11)([-.\/])([0][1-9]|[12][0-9]|30)([-.\/])(\d{4}))|((2)([-.\/])(0[1-9]|1[0-9]|2[0-8])([-.\/])(\d{4}))|((2)(\.|-|\/)(29)([-.\/])([02468][048]00))|((2)([-.\/])(29)([-.\/])([13579][26]00))|((2)([-.\/])(29)([-.\/])([0-9][0-9][0][48]))|((2)([-.\/])(29)([-.\/])([0-9][0-9][2468][048]))|((2)([-.\/])(29)([-.\/])([0-9][0-9][13579][26])))/,
      "Please enter a valid date"));
		this.registerValidator(new Validator("email",/^[a-zA-Z0-9._-]+@([a-zA-Z0-9.-]+\.)+[a-zA-Z0-9.-]{2,4}$/,"Please enter a valid email address"));
		this.registerValidator(new Validator("phone",/^(\d{3}[-\.]\d{3}[-\.]\d{4})$/,"Please enter a valid phone number(xxx-xxx-xxxx)"));
		this.registerValidator(new Validator("zip",/(^(?!0{5})(\d{5})(?!-?0{4})(-?\d{4})?$)/,"Please enter a valid zip or zip+4 code"));
		this.registerValidator(new Validator("password",/^[a-zA-Z]\w{3,14}$/,"Password must contain alpha characters and be between 3-14 characters in length"));
		this.registerValidator(new Validator("name",/^([a-zA-z\s]{4,32})$/,"Name must be contain alpha characters and be between 4-32 characters in length"));
		this.registerValidator(new RequiredValidator());
		
		for (var i = 0;i < this.elems.length;i++) {
      Event.observe(this.elems[i], "blur", this.attach.bindAsEventListener(this));
		}
    
		// Add event listeners to the onsubmit and onreset events
		Event.observe(this.form, "submit", this.validate.bindAsEventListener(this));
    Event.observe(this.form, "reset", this.reset.bindAsEventListener(this));
	},
	
  /* Quick method to add a Validator object to this Validation object
	  * We push the actual object into the array 'configRules'
	  * then add the name into keys for easy access
	  */
	registerValidator: function(validator) {
		if (typeof this.form == 'undefined') {
			return false;
		}
		this.configRules.push(validator);
		this.keys.push(validator.toString());

	},

	/* Base method that we attach to all onchange events on the fields */	
	attach: function(e) {
		var target = Event.element(e);
		var sRules = Element.classNames(target).toArray(); // Grab all the class names from the element
		
		for (var i = 0;i < sRules.length;i++) { // Loop through the class names
			var key = this.keys.indexOf(sRules[i]);
			if (key != -1) { // See if the class name has a Validator defined
				this.configRules[key].handleValid(target); // It has a validator object, call that Validator's handleVald to do the checking
			}
		}
	},

  /* validate method - gets called onsubmit */
	validate: function(e) {
		Event.stop(e); // Stop submit from occuring
		var target = Event.element(e); // Get ahold of the form trying to be submitted
		var errors = 0;
		var sRules;
		
		for (var i = 0;i < this.elems.length;i++) { // Loop through the elements Validator is watching
			sRules = Element.classNames(this.elems[i]).toArray(); // Grab all the class names from the element
			for (var k = 0;k < sRules.length;k++) { // Loop through the class names
				var key = this.keys.indexOf(sRules[k]);
				if (key != -1) { // See if the class name has a Validator defined
					if (!this.configRules[key].isValid(this.elems[i])) { // Check to see if its invalid
						errors++; // Increment error count
						this.configRules[key].handleValid(this.elems[i]); // Call that Validators invalid handler
					}
				}
			}
		}
		if (errors == 0) { // If we didn't get an error, continue the sumbit
			target.submit();
		}
	},
	
  /* reset method - called when the reset button is clicked (clears any invalid messages displayed) */
  reset: function(e) {
		var sRules;
		for (var i = 0;i < this.elems.length;i++) { // Loop through the elements
			if (this.elems[i].error) {	// if the element has error set
				sRules = Element.classNames(this.elems[i]).toArray(); // grab its class names
				for (var k = 0;k < sRules.length;k++) { // loop through its classes
					var key = this.keys.indexOf(sRules[k]); // check the class to see if theres a rule (validator object) for it
					if (key != -1) { // if we found a rule
						this.configRules[key].clear(this.elems[i]); // call the clear method of that validator
					}
				}	
			}
		}
	}
};

/* Validator
  * base object to inherit validator objects from to handle specific cases of form validation
  * default isValid method expects the object to have been initialized with a regular expression
  * for any other validation override the isValid method with one that returns a boolean value
  * NOTE: make sure to include an empty initialize function in the new method otherwise this one
  * will get called twice, once when you create your object and once when that object extends
  * this one.
  */
var Validator = Class.create();
Validator.prototype = {
	initialize: function(key, reg, msg) {
		this.key = key;
		this.regex = reg;
		this.msg = msg;
	},
	
	isValid: function(field) {
		if (this.regex != "" && this.regex.test(field.value)) {
			return true;
		} else {
			return false;
		}
	},
	
	handleValid: function(field) {
		if(this.isValid(field)) {
			this.validHandler(field);
		} else {
			this.invalidHandler(field);
		}
	},
	
  validHandler: function(field) {
		// Pulled the code out of here so that reset wasn't calling valid handler in case we want some sort of valid response but don't want it to happen on reset
		this.clear(field);
	},
	
	invalidHandler: function(field) {
		if (!field.error) {
			Element.setStyle(field, { border: "1px solid red" } );
      field.saveBackground = Element.getStyle(field,'background-color');
			Element.setStyle(field, { backgroundColor: "#FFDBDB" });
			if(typeof Effect == 'undefined') {
        new Insertion.After(field, "<span id=\""+field.name+"-invalid\" class=\"invalid\" style=\"color:red;padding-left:10px\">"+this.msg+"</span>");
      } else {
      	var invalid = new Insertion.After(field, "<span id=\""+field.name+"-invalid\" class=\"invalid\" style=\"color:red;padding-left:10px;display:none\">"+this.msg+"</span>");
        new Effect.Opacity(invalid.element.nextSibling,{from: 0.0, to: 1.0, beforeSetup: function(effect) {effect.element.show();} });
      }
			field.error = true;
		}
	},
	
  clear: function(field) {
		if (field.error) {
			Element.setStyle(field, { border: "1px solid #ccc" } );
      Element.setStyle(field, { "background-color": field.saveBackground } );
			var elem = $(field.name+"-invalid");
      if(typeof Effect == 'undefined') {
        Element.remove(elem);
      } else {
        new Effect.Fade(elem, {afterFinish: function(effect) {Element.remove(effect.element)}});
      }
			field.error = false;
		}
	},
  
	toString: function() {
		return this.key;
	}
};


// Create a custom object extending the Validator Class because we don't want to bother with regex for required
var RequiredValidator = Class.create();
RequiredValidator.prototype = Object.extend(new Validator("required","","This field is required"), {
	initialize: function() {}, // MUST OVERRIDE initialize otherwise the base class one gets called twice!
	// Override isValid with one that checks to see if theres anything in the field
	isValid: function(field) {
		if(!Field.present(field)) {
			return false;
		} else {
			return true;
		}
	}
});

/** CHANGE CODE BELOW TO CORESPOND TO YOUR FORM **/	
Event.observe(window,"load",function() {
	var val = new Validation("_contact");
});
