// inspired by validation functions from youngpup.net
//  altered and expanded rather a lot by af, especially the email validator


var date_fmat = 'human';

var beasts = ['adelphia.net', 'alltel.com', 'ameritech.com', 'aol.com', 'att.net', 'bellsouth.net', 'charter.net', 'comcast.net', 'cox.net', 'cs.com', 'earthlink.net', 'excite.com', 'frontiernet.net', 'fuse.net', 'gmail.com', 'hotmail.com', 'insightbb.com', 'juno.net', 'mac.com', 'mchsi.com', 'msn.com', 'navy.mil', 'netscape.net', 'netzero.com', 'optonline.net', 'peoplepc.com', 'sbcglobal.net', 'us.army.mil', 'verizon.net', 'yahoo.com'];

var tlds = {'usonly': ['aero', 'arpa', 'biz', 'cat', 'com', 'coop', 'edu', 'gov', 'info', 'int', 'jobs', 'mil', 'mobi', 'museum', 'name', 'net', 'org', 'pro', 'travel'],

	'intl': ['ac', 'ad', 'ae', 'af', 'ag', 'ai', 'al', 'am', 'an', 'ao', 'aq', 'ar', 'as', 'at', 'au', 'aw', 'ax', 'az', 'ba', 'bb', 'bd', 'be', 'bf', 'bg', 'bh', 'bi', 'bj', 'bm', 'bn', 'bo', 'br', 'bs', 'bt', 'bv', 'bw', 'by', 'bz', 'ca', 'cc', 'cd', 'cf', 'cg', 'ch', 'ci', 'ck', 'cl', 'cm', 'cn', 'co', 'cr', 'cu', 'cv', 'cx', 'cy', 'cz', 'de', 'dj', 'dk', 'dm', 'do', 'dz', 'ec', 'ee', 'eg', 'eh', 'er', 'es', 'et', 'eu', 'fi', 'fj', 'fk', 'fm', 'fo', 'fr', 'ga', 'gb', 'gd', 'ge', 'gf', 'gg', 'gh', 'gi', 'gl', 'gm', 'gn', 'gp', 'gq', 'gr', 'gs', 'gt', 'gu', 'gw', 'gy', 'hk', 'hm', 'hn', 'hr', 'ht', 'hu', 'id', 'ie', 'il', 'im', 'in', 'io', 'iq', 'ir', 'is', 'it', 'je', 'jm', 'jo', 'jp', 'ke', 'kg', 'kh', 'ki', 'km', 'kn', 'kp', 'kr', 'kw', 'ky', 'kz', 'la', 'lb', 'lc', 'li', 'lk', 'lr', 'ls', 'lt', 'lu', 'lv', 'ly', 'ma', 'mc', 'md', 'me', 'mg', 'mh', 'mk', 'ml', 'mm', 'mn', 'mo', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'nc', 'ne', 'nf', 'ng', 'ni', 'nl', 'no', 'np', 'nr', 'nu', 'nz', 'om', 'pa', 'pe', 'pf', 'pg', 'ph', 'pk', 'pl', 'pm', 'pn', 'pr', 'ps', 'pt', 'pw', 'py', 'qa', 're', 'ro', 'rs', 'ru', 'rw', 'sa', 'sb', 'sc', 'sd', 'se', 'sg', 'sh', 'si', 'sj', 'sk', 'sl', 'sm', 'sn', 'so', 'sr', 'st', 'su', 'sv', 'sy', 'sz', 'tc', 'td', 'tf', 'tg', 'th', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'tp', 'tr', 'tt', 'tv', 'tw', 'tz', 'ua', 'ug', 'uk', 'um', 'us', 'uy', 'uz', 'va', 'vc', 've', 'vg', 'vi', 'vn', 'vu', 'wf', 'ws', 'ye', 'yt', 'yu', 'za', 'zm', 'zw']
	};

var intlLookup = {"ac": "Ascension Island", "ad": "Andorra", "ae": "United Arab Emirates", "af": "Afghanistan", "ag": "Antigua and Barbuda", "ai": "Anguilla", "al": "Albania", "am": "Armenia", "an": "Netherlands Antilles", "ao": "Angola", "aq": "Antarctica", "ar": "Argentina", "as": "American Samoa", "at": "Austria", "au": "Australia", "aw": "Aruba", "ax": "Aland Islands", "az": "Azerbaijan", "ba": "Bosnia and Herzegovina", "bb": "Barbados", "bd": "Bangladesh", "be": "Belgium", "bf": "Burkina Faso", "bg": "Bulgaria", "bh": "Bahrain", "bi": "Burundi", "bj": "Benin", "bm": "Bermuda", "bn": "Brunei Darussalam", "bo": "Bolivia", "br": "Brazil", "bs": "Bahamas", "bt": "Bhutan", "bv": "Bouvet Island", "bw": "Botswana", "by": "Belarus", "bz": "Belize", "ca": "Canada", "cc": "Cocos (Keeling) Islands", "cd": "Congo, The Democratic Republic of the", "cf": "Central African Republic", "cg": "Congo, Republic of", "ch": "Switzerland", "ci": "Cote d'Ivoire", "ck": "Cook Islands", "cl": "Chile", "cm": "Cameroon", "cn": "China", "co": "Colombia", "cr": "Costa Rica", "cu": "Cuba", "cv": "Cape Verde", "cx": "Christmas Island", "cy": "Cyprus", "cz": "Czech Republic", "de": "Germany", "dj": "Djibouti", "dk": "Denmark", "dm": "Dominica", "do": "Dominican Republic", "dz": "Algeria", "ec": "Ecuador", "ee": "Estonia", "eg": "Egypt", "eh": "Western Sahara", "er": "Eritrea", "es": "Spain", "et": "Ethiopia", "eu": "European Union", "fi": "Finland", "fj": "Fiji", "fk": "Falkland Islands (Malvinas)", "fm": "Micronesia, Federated States of", "fo": "Faroe Islands", "fr": "France", "ga": "Gabon", "gb": "United Kingdom", "gd": "Grenada", "ge": "Georgia", "gf": "French Guiana", "gg": "Guernsey", "gh": "Ghana", "gi": "Gibraltar", "gl": "Greenland", "gm": "Gambia", "gn": "Guinea", "gp": "Guadeloupe", "gq": "Equatorial Guinea", "gr": "Greece", "gs": "South Georgia and the South Sandwich Islands", "gt": "Guatemala", "gu": "Guam", "gw": "Guinea-Bissau", "gy": "Guyana", "hk": "Hong Kong", "hm": "Heard and McDonald Islands", "hn": "Honduras", "hr": "Croatia/Hrvatska", "ht": "Haiti", "hu": "Hungary", "id": "Indonesia", "ie": "Ireland", "il": "Israel", "im": "Isle of Man", "in": "India", "io": "British Indian Ocean Territory", "iq": "Iraq", "ir": "Iran, Islamic Republic of", "is": "Iceland", "it": "Italy", "je": "Jersey", "jm": "Jamaica", "jo": "Jordan", "jp": "Japan", "ke": "Kenya", "kg": "Kyrgyzstan", "kh": "Cambodia", "ki": "Kiribati", "km": "Comoros", "kn": "Saint Kitts and Nevis", "kp": "Korea, Democratic People's Republic", "kr": "Korea, Republic of", "kw": "Kuwait", "ky": "Cayman Islands", "kz": "Kazakhstan", "la": "Lao People's Democratic Republic", "lb": "Lebanon", "lc": "Saint Lucia", "li": "Liechtenstein", "lk": "Sri Lanka", "lr": "Liberia", "ls": "Lesotho", "lt": "Lithuania", "lu": "Luxembourg", "lv": "Latvia", "ly": "Libyan Arab Jamahiriya", "ma": "Morocco", "mc": "Monaco", "md": "Moldova, Republic of", "me": "Montenegro", "mg": "Madagascar", "mh": "Marshall Islands", "mk": "Macedonia, The Former Yugoslav Republic of", "ml": "Mali", "mm": "Myanmar", "mn": "Mongolia", "mo": "Macao", "mp": "Northern Mariana Islands", "mq": "Martinique", "mr": "Mauritania", "ms": "Montserrat", "mt": "Malta", "mu": "Mauritius", "mv": "Maldives", "mw": "Malawi", "mx": "Mexico", "my": "Malaysia", "mz": "Mozambique", "na": "Namibia", "nc": "New Caledonia", "ne": "Niger", "nf": "Norfolk Island", "ng": "Nigeria", "ni": "Nicaragua", "nl": "Netherlands", "no": "Norway", "np": "Nepal", "nr": "Nauru", "nu": "Niue", "nz": "New Zealand", "om": "Oman", "pa": "Panama", "pe": "Peru", "pf": "French Polynesia", "pg": "Papua New Guinea", "ph": "Philippines", "pk": "Pakistan", "pl": "Poland", "pm": "Saint Pierre and Miquelon", "pn": "Pitcairn Island", "pr": "Puerto Rico", "ps": "Palestinian Territory, Occupied", "pt": "Portugal", "pw": "Palau", "py": "Paraguay", "qa": "Qatar", "re": "Reunion Island", "ro": "Romania", "rs": "Serbia", "ru": "Russian Federation", "rw": "Rwanda", "sa": "Saudi Arabia", "sb": "Solomon Islands", "sc": "Seychelles", "sd": "Sudan", "se": "Sweden", "sg": "Singapore", "sh": "Saint Helena", "si": "Slovenia", "sj": "Svalbard and Jan Mayen Islands", "sk": "Slovak Republic", "sl": "Sierra Leone", "sm": "San Marino", "sn": "Senegal", "so": "Somalia", "sr": "Suriname", "st": "Sao Tome and Principe", "su": "Soviet Union (being phased out)", "sv": "El Salvador", "sy": "Syrian Arab Republic", "sz": "Swaziland", "tc": "Turks and Caicos Islands", "td": "Chad", "tf": "French Southern Territories", "tg": "Togo", "th": "Thailand", "tj": "Tajikistan", "tk": "Tokelau", "tl": "Timor-Leste", "tm": "Turkmenistan", "tn": "Tunisia", "to": "Tonga", "tp": "East Timor", "tr": "Turkey", "tt": "Trinidad and Tobago", "tv": "Tuvalu", "tw": "Taiwan", "tz": "Tanzania", "ua": "Ukraine", "ug": "Uganda", "uk": "United Kingdom", "um": "United States Minor Outlying Islands", "us": "United States", "uy": "Uruguay", "uz": "Uzbekistan", "va": "Holy See (Vatican City State)", "vc": "Saint Vincent and the Grenadines", "ve": "Venezuela", "vg": "Virgin Islands, British", "vi": "Virgin Islands, U.S.", "vn": "Vietnam", "vu": "Vanuatu", "wf": "Wallis and Futuna Islands", "ws": "Samoa", "ye": "Yemen", "yt": "Mayotte", "yu": "Yugoslavia", "za": "South Africa", "zm": "Zambia", "zw": "Zimbabwe"};


function isValid(type, str, reg) {
	str += '';
	str = str.trim();
	
	if (type == 'text') {
		if (reg != '') {
			return isValidReg(str, reg);
		} else {
			return str.length > 0;
		}
		
	} else if (type == 'num') {
		if (reg != '') {
			var bounds = reg.split(',');
			return isValidBoundedNumber(str, bounds[0], bounds[1]);
		} else {
			return isValidNumber(str);
		}
	
	} else if (type == 'phone') {
		return isValidPhoneNumber(str);
	
	} else if (type == 'email') {
		return isValidEmailAddress(str);
	
	} else if (type == 'zip') {
		return isValidZipCode(str);
	
	} else if (type == 'cc') {
		return isValidCCNumber(str);
	
	} else if (type == 'usd') {
		return isValidUSDAmount(str);
	
	} else if (type == 'ssn') {
		return isValidSSN(str);
	}
	// keep it from choking while I catch up with new types 
	//  added on the server side (date, vin, etc.)
	return true;
}


function isValidReg(s, reg) {
	var regEx = eval(reg);
	return s.match(regEx) != null;
}

function isValidNumber(s) {
	s = s.replace(/\D/g, "") + "";
	return (s != '' && !isNaN(s));
}
function isValidBoundedNumber(s, min, max) {
	s = s.replace(/\D/g, "");
	return (s != '' && !isNaN(s) && s >= min && s <= max);
}

function isValidPhoneNumber(s) {
	s = s.replace(/\D/g, "") + "";
	return (s.length == 10 
		&& (s.substring(0,1)-0) > 1 
		&& (s.substring(3,4)-0) > 0
		&& s.substring(3,8) != '55501');
}

function isValidZipCode(s) {
	s = s.replace(/\D/g, "");
	return (s.length == 5 || s.length == 9);
}

function isValidBox(varName) {
	var v = val(varName);
	return (v != '');
}

function isValidSelect(o) {
	if (o.options.length == 0) 
		return false;
	var v = o.options[o.selectedIndex].value;
	return (v.trim() != "" && !v.match(/Select.../));
}



var eBits;  // global so it's accessible for other purposes

function isValidEmailAddress(s) {

	// addresses with quotes or spaces are so verboten 
	//   that I'm almost comfortable kicking them out up-front,
	//   but let's just try removing the offending characters and see what happens
	s = s.replace(/[\s"]/g, "");
	
	var a = s.split('@');
	if (a.length != 2) 
		return false;
	
	// the character set you usually see is far too restrictive...
	//   the local part of the address can contain many special characters,
	//   except in certain domains where they're forbidden, e.g. hotmail.com 
	//   (it may be overstepping to try to validate against their rules, but...)
	
	if (s.match(/hotmail.com$/i)) 
		eBits = s.match(/^(([\w+_-]+)(\.[\w+_-]+)*)@(([\w-]+\.)*(\w\w+))$/);
	else 
		eBits = s.match(/^(([\w!#$%&'*+\/=?^_{|}~-]+)(\.[\w!#$%&'*+\/=?^_{|}~-]+)*)@(([\w-]+\.)*(\w\w+))$/);
	
	if (eBits == null) 
		return false;
	
	var domain = eBits[5] + eBits[6];
	
	// feedback (yummy)
	myOnLog(eBits.length + ' bits: [\n  "' + eBits.join('",\n  "') + '"]\ndomain: ' + domain + '\n');
	
	if (domain.within(beasts)) 
		return 'beast';
	
	if (eBits[6].within(tlds['usonly'])) 
		return 'us';
	
	if (eBits[6].within(tlds['intl'])) 
		return intlLookup[eBits[6]];
	
	return 'tld unknown';
}



function isValidUSDAmount(s) {
	s = s.replace(/[^\d.-]/g, "");
	return s.match(/^-?\d+\.\d\d$/) != null;
}

function isValidSSN(s) {
	s = fieldFormat(s, 'ssn');
	return (s.match(/^[0-9]{3}-[0-9]{2}-[0-9]{4}$/) 
		&& s.substring(0,3) != '000' && s.substring(4,6) != '00' && s.substring(7,11) != '0000' 
		&& s.substring(0,3) != '666' 
		&& s.substring(0,10) != '987-65-432' 
		&& s != '078-05-1120');
}

function isValidCCNumber(s) {
	s = s.replace(/\D/g, "");
	
	if (s.match(/^0+$/)) 
		return false;  // special case
	
	var ccType = getCCType(s);
	if (!ccType) 
		return false;

	if (mod10(s)) 
		return ccType;
	else return false;
}

function mod10(s) {
	var checksum = 0, digit;
	var even = false;
	
	for (var n = s.length - 1;  n >= 0;  n--) {
		digit = parseInt(s.charAt(n), 10);
		if (even) {
			if ((digit *= 2) > 9)
				digit -= 9;
		}
		checksum += digit;
		even = !even;
	}
	return (checksum % 10 == 0);
}

function getCCType(s) {
	s = s.replace(/\D/g, "");
	var len = s.length;
	
	var ccType = false;
	
	if (s.match(/^4/) && (len == 13 || len == 16)) 
		ccType = '1';  // visa
	
	else if (s.match(/^3[47]/) && len == 15) 
		ccType = '3';  // amex
	
	else if (s.match(/^5[1-5]/) && len == 16) 
		ccType = '2';  // mc
	
	else if (s.match(/^(30[0-5]|3[68])/) && len == 14) 
		ccType = '5';  // diners
	
	else if (s.match(/^6011/) && len == 16) 
		ccType = '4';  // discover
	
	else if (s.match(/^(3|1800|2131)/) && (len == 15 || len == 16)) 
		ccType = '7';  // jcb
	
	return ccType;
}

function ccFormat(s) {
	s = s.replace(/\D/g, "");
	s = s.substring(0,16);
	var t = getCCType(s);
	if (!t) {
		return s;
	}
	var out = '';
	if (s.length >= 16) {
		out = s.substring(0,4) + "-" + s.substring(4,8);
		out += "-" + s.substring(8,12) + "-" + s.substring(12,16);
	
	} else if (s.length == 15 && s.substring(0,1) == '3') {
		out = s.substring(0,4) + "-" + s.substring(4,10);
		out += "-" + s.substring(10,15);
	
	} else if (s.length == 13 && s.substring(0,1) == '4') {
		out = s.substring(0,4) + "-" + s.substring(4,7);
		out += "-" + s.substring(7,10) + "-" + s.substring(10,13);
	
	} else {
		out = s;
	}
	return out;
}






// masking functions - attempt to parse and reformat 
// element's values as a specific datatype
// to use, tack them to the form field's onblur handler.
// document.myForm.phone_number.onblur = phoneFieldBlurHandler	
//   -> this field will now mask for phone numbers onblur

function textFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'text');
}

function numFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'num');
}

function dateFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'date');
}

function timeFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'time');
}

function emailFieldBlurHandler() {
	this.value = this.value.trim();
	this.value = this.value.replace(/\s/g, "");
}

function phoneFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'phone');
}

function zipFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'zip');
}

function ccFieldBlurHandler() {
	this.value = ccFormat(this.value);
}

function usdFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'usd');
}

function vinFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'vin');
}

function ssnFieldBlurHandler() {
	this.value = fieldFormat(this.value, 'ssn');
}


function usd(n) {
	n += "";
	n = n.replace(/[^\d.-]/g, "") - 0;
	n = Math.round(n * 100) / 100;
	n += "";
	if (n.match(/^\d+$/)) 
		n += '.00';
	if (n.match(/^\./)) 
		n = '0' + n;
	if (n.match(/\.$/)) 
		n += '00';
	else if (n.match(/\.\d$/)) 
		n += '0';
	return n;
}


function fieldFormat(str, type) {
	str += '';
	
	if (type == 'text') {
		str = str.trim();
		str = str.replace(/[<>]/g, '');
	
	} else if (type == 'num') {
		str = str.replace(/\D/g, "");
		if (str != '0') str = str.replace(/^0+/g, "");
		if (isNaN(str - 0)) 
			str = "";
	
	} else if (type == 'numc') {
		str = numc(str);
	
	} else if (type == 'phone') {
		str = str.replace(/\D/g, "");
		str = str.replace(/^[01]+/, "");
	
		if (str.length >= 10) {
			//this.value = "(" + temp.substring(0,3) + ") ";
			str = str.substring(0,3) + "-" + str.substring(3,6) + "-" + str.substring(6,10);
		}
	
	} else if (type == 'ssn') {
		str = str.replace(/\D/g, "");
		str = str.substring(0,3) + "-" + str.substring(3,5) + "-" + str.substring(5,9);
		if (str == '--') 
			str = '';

	} else if (type == 'zip') {
		str = str.replace(/\D/g, "");
	
		if (str.length >= 5 && str.length < 9) {
			str = str.substring(0,5);
		
		} else if (str.length >= 9) {
			str = str.substring(0,5) + "-" + str.substring(5,9);
		}
	
	} else if (type == 'vin') {
		str = str.replace(/\s/g, '');
		str = str.toUpperCase();
		str = str.replace(/[^A-Z0-9]|[IOQ]/g, "");
		str = str.substring(0, 17);
	
	
	} else if (type == 'time') {
		var t = new Time(str);
		if (!isNaN(t)) 
			str = t.getHumanTimeString();
	
	
	} else if (type == 'date') {
		str = str.substitute('-', '/');
		
		if (!isNaN(str) && str.length == 5) 
			str = '0' + str + '';
		
		if (!isNaN(str) && str.length && str.indexOf('/') == -1)
			str = str.substring(0,2)+'/'+str.substring(2,4)+'/'+str.substring(4,8)
		
		var d = new Date(str);
		if (!isNaN(d)) {
			// if the user didn't specify the century/millenium, then assume the present.
			//if (str.search(/\d{3}/) == -1) {
			//	d.setYear(Math.floor((new Date()).getFullYear() / 100) * 100 + d.getFullYear() % 100);
			//}
			if (date_fmat == 'human') 
				str = d.getHumanDateString();
			else str = d.getDateString();
		}
	
	} else if (type == 'usd') {
		str = usd(str);
	
	} else if (type == 'cc') {
		str = ccFormat(str);
	
	} else if (typeof str == 'string') {
		str = str.trim();
	}
	return str;
}


function elFormat(el, type) {
	if (typeof el == 'string') 
		el = f[el];
	if (el.type != 'text') 
		return false;
	var v = fieldFormat(el.value, type);
	el.value = v;
	return v;
}


// convenience function.
// attaches textFieldBlurHandler to the onblur event 
// of every text or textarea element in f
// var myForm = document.myForm
// attachAllTextHandlers(myForm) 
//		-> every text element in myForm now will call 
//		   textFieldBlurHandler (above) onblur
function attachAllTextHandlers(f) {
	var el;
	for (var i = 0; (el = f.elements[i]); i++) {
		if (el.type == "text" || el.type == "password"/* || el.type == "textarea" */) 
			el.onblur = textFieldBlurHandler;
	}
}


// not currently used, but could be handy as keyup handler
function numOnly(el) {
	el.value = el.value.replace(/\D/g, "");
}





