function Validator(form) {
	this.validation = []; //array of fields to validate
	this.isValid = true;
	this.pos = {start:0, end:0}; //position of caret
	
	if((this.formobj = $("#"+form)).size() <= 0) {
		if((this.formobj = $("form[name="+form+"]")).size() <= 0) {
			alert("ERROR: could not get form object: " + form);
			return;
		}
	}
	
	this.setup = this.addValidation = function() {
		
		for(var i=0; i<arguments.length; i++) {
			var fieldobj = $("#"+arguments[i].id);
			fieldobj.num = i;
			if(fieldobj.size() <= 0) {
				alert("ERROR: Did not find: " + arguments[i].id);
				return;
			}
			for (var name in arguments[i]) {
				if (name == "name") {
					alert("field names called name will not work.");
				}
				if(name == 'live' && arguments[i][name] == true) {
					fieldobj.bind("keypress",{num:i,validator:this}, function(e) {e.data.validator.validate("warning",e.data.num);}  );
					fieldobj.bind("focus",{num:i,validator:this}, function(e) {if (e.data.validator.isValid == true) {
						e.data.validator.validate("warning", e.data.num);
					}}  );
				}
			}
			fieldobj.bind("keydown change",{num:i,validator:this},function(e){
				e.data.validator.pos = getCaretPos($("#" + e.data.validator.validation[e.data.num].id)[0]);
			});
			if(val = arguments[i]['restrict'] ) {
				fieldobj.bind("input blur change keyup",{val:val,num:i,validator:this},function(e){
					
                    var value = $("#"+e.data.validator.validation[e.data.num].id).val();
                    var len = value.length;
					if (len > 0) {
						var newstring = '';
						for (var j = 0; j < len; j++) {
							var character = value.substr(j, 1);
							if (character.search(e.data.val) != -1) {
								newstring += character;
								if(e.data.validator.pos)
									e.data.validator.pos.start++;
							} else if (character.toLowerCase().search(e.data.val) != -1) { //see if lowercase matches
								newstring += character.toLowerCase();
								if(e.data.validator.pos)
									e.data.validator.pos.start++;
							} else if (character.toUpperCase().search(e.data.val) != -1) { //or upper case
								newstring += character.toUpperCase();
								if(e.data.validator.pos)
									e.data.validator.pos.start++;
							} else if (e.data.val.search(/ /) == -1 && e.data.val.search(/-/) != -1 && character == " ") { //replace spaces with -
								newstring += "-";
								if(e.data.validator.pos)
									e.data.validator.pos.start++;
							}
						}	
						if(value != newstring) {
							$("#" + e.data.validator.validation[e.data.num].id).val(newstring);
							setCaretPos($("#" + e.data.validator.validation[e.data.num].id)[0],e.data.validator.pos.start);
						}
					}
                });
			}
			if (val = arguments[i]['maxlength']) {
				fieldobj.bind("input blur change keyup keydown",{num:i,validator:this},function(e){
					 var value = $("#"+e.data.validator.validation[e.data.num].id).val();
					var maxlength = e.data.validator.validation[e.data.num].maxlength;
                   
					if( value.length > maxlength) {
						$("#" + e.data.validator.validation[e.data.num].id).val(value.substr(0,maxlength));
						setCaretPos($("#" + e.data.validator.validation[e.data.num].id)[0],e.data.validator.pos.start);
					} 
				});
			}
		}
		this.validation =  this.validation.concat(Array.prototype.slice.call(arguments, 0)); //arguments is a array object but I want an array
	};
	
	this.removeValidation = function() {
		for(var i=0; i<arguments.length; i++) {
			for(j=0, t = this.validation.length;j<t;j++ ) {
				if(this.validation[j].id == arguments[i]) {
					var fieldobj = $("#"+this.validation[j].id);
					//remove live keypress + focus events if any + remove keydown + change events
					fieldobj.unbind("keydown change keypress focus");

					//remove restrict "input blur change keyup" events
					if(this.validation[j].restrict != undefined) {
						fieldobj.unbind("input blur change keyup");
					}
					//remove maxlength "input blur change keyup keydown" events
					if(this.validation[j].maxlength != undefined) {
						fieldobj.unbind("input blur change keyup keydown");
					}
					var empty = new Array();
					this.validation =  empty.concat(this.validation.slice(0,j),this.validation.slice(j+1));
					break;
				}
			}
		}
	};
	
	this.addCustom = function(func, id) {
		found = false;
		for(i=0, len=this.validation.length; i<len; i++) {
			if(this.validation[i].id == id) {
				this.validation[i].custom  = func;
				found = true;
				break;
			}
		}
		if(found == false) {
			this.validation[i].id = id;
			this.validation[i].custom = func;
		}
	};
	
	this.validate = function (level) {
		this.isValid = true;
		if(arguments.length>1 && !isNaN(arguments[1])) {//specify a field to validate
		
			//field hasn't been removed and loop back through tree to see if field is visible
			if ($("#" + this.validation[arguments[1]].id).size() > 0 && $("#" + this.validation[arguments[1]].id).parents().map(function(){
				if (!$(this).is(":visible")) {
					return this;
				}
			}).size() == 0) {
			
				for (var name in this.validation[arguments[1]]) {
					errorstr = '';
					value = this.validation[arguments[1]][name];
					obj = $("#" + this.validation[arguments[1]].id);
					
					if (this.validation[arguments[1]].custom != undefined && name != 'id' && this.validation[arguments[1]].custom() == false || 
							this.validation[arguments[1]].required == true && name != "id" && (errorstr = check(obj, name, value)) != true || 
							obj.val() != "" && name != "id" && (errorstr = check(obj, name, value)) != true) {
						this.isValid = false;
						break;
					}
				}
			}
		} else { //validate all fields
			for(var i=0; i < this.validation.length && this.isValid == true; i++) {
				
				//field hasn't been removed and loop back through tree to see if field is visible
				if ($("#" + this.validation[i].id).size() > 0 && $("#" + this.validation[i].id).parents().map(function(){
					if (!$(this).is(":visible")) {
						return this;
					}
				}).size() == 0) {
				
					for (var name in this.validation[i]) {
						errorstr = '';
						value = this.validation[i][name];
						obj = $("#" + this.validation[i].id);
						
						if (this.validation[i].custom != undefined && name != 'id' && (errorstr = this.validation[i].custom()) != true || 
								this.validation[i].required == true && name != "id" && (errorstr = check(obj, name, value)) != true || 
								obj.val() != "" && name != "id" && (errorstr = check(obj, name, value)) != true) {
							this.isValid = false;
							if (this.validation[i]['showError'] != undefined) {
								obj = $("#" + this.validation[i]['showError']);
							}
							break;
						}
					}
				}
			}
		}
		
		if(this.isValid) {
			hideNotice();
		} else {
			if(level == "error") {
				obj.focus();
			}
			showNotice(level,errorstr,obj);
		}
		return this.isValid;
	};

	this.formobj.bind("submit",{validator:this}, function(e) {return e.data.validator.validate("error");}  );

}

function getCaretPos(input) {
	var result = { start: 0, end: 0 };
	if (input.setSelectionRange) {
		result.start = input.selectionStart;
		result.end = input.selectionEnd;
	} else if (document.selection && document.selection.createRange) {
		var range = document.selection.createRange();
		var r2 = range.duplicate();
		result.start = 0 - r2.moveStart('character', -100000);
		result.end = result.start + range.text.length;
	}
	return result;
}
function setCaretPos(field, pos) {
	if(field.setSelectionRange) {
		field.setSelectionRange(pos,pos);
		field.focus();
	} else if (field.createTextRange) {
		var range = field.createTextRange();
		range.collapse(true);
		range.moveEnd('character', pos);
		range.moveStart('character', pos);
		range.select();
	}
}
function validateEmail(email) {
	var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 
	return (email.match(re)==null?false:true); 
}

function check(fieldobj, type, value) {
	
	var fieldvalue = (fieldobj.attr("value")==undefined?"":fieldobj.attr("value"));
	switch(type) { 
		case "required": 
		case "req":
			if($(fieldobj).hasClass("multiSelect_selected") && $(fieldobj).find('option').size() > 0 ) {
				return true;
			}

			if($(fieldobj).attr("type")=="checkbox" && !$(fieldobj).is(':checked') || fieldvalue.length == 0) {

				return "This is a required field";

			}
		break;             
		case "maxlength": 
		case "maxlen":
			if(fieldvalue.length >  parseInt(value)) { 
				return "Too many characters entered. Please enter "+value; 
			}
		break;
        case "minlength": 
        case "minlen": 
			if(fieldvalue.length > 0 && fieldvalue.length <  parseInt(value)) { 
				return value+" characters are required";                 
			}
		break; 
        case "alnum": 
        case "alphanumeric"://contains alphabetic or numeric values
			var charpos = fieldvalue.search("[^A-Za-z0-9]");
			if(charpos == null) { 
				return false; 
			} 
		break;
		case "reqalphanumeric"://requires alphabetic and numeric values
			var charpos = fieldvalue.search("[^A-Za-z]");
			if(charpos == -1) { 
				return false; 
			} 
			var charpos = fieldvalue.search("[^0-9]");
			if(charpos == -1) { 
				return false; 
			} 
		break;
        case "num": 
        case "numeric":  
			var charpos = fieldvalue.search("[^0-9]"); 
			if(fieldvalue.length > 0 &&  charpos >= 0) { 
				return "Please enter a numeric value"; 
			}
		break;               
        case "alphabetic": 
        case "alpha": 
			var charpos = fieldvalue.search("[^A-Za-z]"); 
			if(fieldvalue.length > 0 &&  charpos >= 0) { 
				return false; 
			} 
		break;
		case "alnumhyphen":
			var charpos = fieldvalue.search("[^A-Za-z0-9\-_]"); 
			if(fieldvalue.length > 0 &&  charpos >= 0) { 
				return false; 
			}			
			break;
        case "email": 
			if(fieldvalue.length > 0 && !validateEmail(fieldvalue)) { 
				return "Please enter a valid email address"; 
			}
		break; 
        case "email_confirm": 
			if(fieldvalue != $('email').value) {
				return false; 
			}
		break;  
        case "lt": 
        case "lessthan": 
			if(isNaN(fieldvalue)) { 
				return false; 
			}
			if(fieldvalue >=  parseInt(value)) { 
				return false;                 
			}             
		break; 
        case "gt": 
        case "greaterthan": 
			if(isNaN(fieldvalue)) { 
				return false; 
			}
			if(fieldvalue <=  parseInt(value)) { 
				return false;                 
			}            
		break; 
		case "confirm": 
			if(fieldvalue != $("#"+value).val() ) {
				return "Confirm e-mail doesn't match";
			} 
		break; 
        case "regexp": 
		 	if(fieldvalue.length > 0) {
	            if(!fieldvalue.match(value)) { 
	              return false;                   
	            }
			}
		break; 
    } 
	return true;
}
function selectDay(field_id) {
	var year = $('#'+field_id+'_year').val();
	var month = $('#'+field_id+'_month').val();
	var dayObj = $('#'+field_id+'_day');
	if(year != "" && month != "") {
		var currentSel = dayObj[0].selectedIndex;
		var dim = days_in_month(year, month-1);

		html = '<option></option>';
		for(var i=1;i<=dim; i++) {
			html += '<option>'+i+'</option>';
		}
		dayObj.html(html);
		if(currentSel > dim) {
			dayObj[0].selectedIndex = dim;
		} else {
			dayObj[0].selectedIndex = currentSel;
		}
	}
	updateDate(field_id);
}
function updateDate(field_id) {
	var year = $('#'+field_id+'_year').val();
	var month = $('#'+field_id+'_month').val();
	var day = $('#'+field_id+'_day').val();
	dateString = '';
	if(year != '') {
		dateString += year + '-'; 
	} else {
		dateString += '0000-';
	}
	if(month != '') {
		dateString += month + '-';
	} else {
		dateString += '00-';
	}
	if(day != '') {
		dateString += day;
	} else {
		dateString += '00';
	}
	$('#'+field_id).attr('value',dateString);
}
function days_in_month (year, month) {
     return 32 - new Date(year, month, 32).getDate();
}
var fadeId = null;
function showNotice(type, message, obj){
	
	if($("#notice").length <= 0) {
		$("body").append('<div id="notice" class="warning"><table border="0" cellpadding="0" cellspacing="0"><tr><td></td><td class="tl"></td><td class="t"></td><td class="tr"></td></tr><tr><td class="arrow"></td><td class="l"></td><td class="c"></td><td class="r"></td></tr><tr><td></td><td class="bl"></td><td class="b"></td><td class="br"></td></tr></table></div>');
		$("#notice").click(hideNotice);
	}
	
	$("#notice").attr("className", type);
	positionNotice(obj);
	$(window).bind("resize", function(){
		positionNotice(obj);
	});
	$("#notice table tr td.c").html(message);
	if ($("#notice").css("display") != "block" || $("#notice").css("display") == "block"  && $("#notice").css("opacity") < 1) {
		$("#notice").fadeIn('slow');
	}
	clearInterval(fadeId);
	fadeId = setInterval(hideNotice,6000);
}

function hideNotice() {
	$("#notice").fadeOut('slow');
	$(window).unbind("resize");
}

function positionNotice(obj) {
	if($(obj).attr('type')=='hidden') {
obj = obj.prev();
}
	var offset = obj.offset();
	$('#notice').css('left', ((offset.left+obj.outerWidth())-5)+'px');
	$('#notice').css('top', offset.top+'px');
}

function countryChange(countryObj, stateObj) {
	var country = $('#'+countryObj+'_country');
	value = country.val().split(':');
	
	if(stateObj != null) {
		var stateSelect = $('#'+stateObj+'_stateSelect');
		var stateText = $('#'+stateObj);
	}
	
	if(value.length == 1) {
		if(stateObj != null) {
			//show textfield
			stateSelect.hide();
			stateText.show();
		}
		$('#'+countryObj).val(value);
	} else {
		$('#'+countryObj).val(value[0]);
		
		if(stateObj != null) {
			//show select
			stateSelect.show();
			stateText.hide();
			
			states = value[1].split(",");	
			html = '<option></option>';
			for(i=0;i<states.length;i++) {
				html += '<option value="'+states[i]+'">'+states[i]+'</option>';
			}
			stateSelect.html(html);
			stateSelect.val('option:first').attr('selected', 'selected');
		}
	}
}

function stateUpdate(obj) {
	$('#'+obj).val($('#'+obj+'_stateSelect').val());
}
/* Preload Images */
var preloaded = new Array();
function preload_images() {
	for (var i = 0; i < arguments.length; i++){
		preloaded[i] = document.createElement('img');
		preloaded[i].setAttribute('src',arguments[i]);
	};
};
//preload validation images
preload_images(
	"/puppy/images/validation/error.png",
	"/puppy/images/validation/warning.png"
);

