// Copyright (C) 1998,1999,2000,2001,2002,2003,2004,2005
// Compu-Quote Inc.
// 4510 Rhodes Dr., Building 400
// Windsor, Ontario  N8W 5C2
// Phone : (519) 974-7283
// 
// Warning: This computer program is protected by copyright law
// and international treaties.  Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent
// possible under the law.

var digits = "0123456789";
var lowercaseLetters = "abcdefghijklmnopqrstuvwxyz";
var uppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var whitespace = " \t\n\r";

function isEmpty(s) {
	return ((s == null) || (s.length == 0));
}

function isWhitespace (s) {
	var i;
	if (isEmpty(s)) return true;
	for (i = 0;i < s.length; i++) {
		var c = s.charAt(i);
		if (whitespace.indexOf(c) == -1) 
		   return false;
	}
	return true;
}

function isControlCharacter(ch) {
	//return (ch.charCodeAt() < 32)
	return ch < " ";
}

function RFC822Token(tokenType, val) {
	this.tokenType = tokenType;
	this.value = val;
}

// method for stack (Cant use array.push and array.pop - they are JS 1.2)
function stackPush(v) {
	with (this) {
		values[top] = v;
		top++;
	}
}

function stackPop() {
	var v;
	with (this) {
		if (top > 0) {
			top--;
			v = values[top];
		}
	}
	return v;
}

function stackDepth() {
	return this.top;
}

function stack() {
	this.values = new Array ();
	this.top = 0;
	this.push = stackPush;
	this.pop = stackPop;
	this.depth = stackDepth;
}

// methods for a RFC822Scanner
function inSpecialCharacters(ch) {
	return (this.SpecialCharacters.indexOf(ch) != -1);
}

function scanStringMethod(terminator, exceptList) {
	var ch, theStr = "";
	with (this) {
		while (true) {
			if (cursor > inpStr.length) {
//				prompt(terminator + " expected");
				return false;
			}
			ch = inpStr.charAt(cursor);
			cursor += 1;
			if (exceptList.indexOf(ch) != -1) {
//				prompt(ch + " not permitted here");
				return false;
			}
			if  (ch == terminator) {
				break;
			}
			theStr = theStr + ch;
		}
	}
	return (theStr);
}

function scanNextTokenMethod() {
	var ch, tempStr;
	with (this) {
		while (true) {
			if (cursor >= inpStr.length) {
				return new RFC822Token(this.eofToken, "");
			}
			ch = inpStr.charAt(cursor);
			cursor +=1;
			if (ch == "(") {
				tempStr = scanString(")", "\r(");
				if (typeof tempStr != "string") {
					return new RFC822Token (errorToken, "");
				}
				continue;
			}
			if (ch == "\"") {
				tempStr = scanString("\"", "\r");
				if (typeof tempStr != "string") {
					return new RFC822Token (errorToken, "");
				}
				return (new RFC822Token(stringToken, tempStr));
			}
			if ((ch == " ") || (ch == "\t")) {
				continue
			}
			if (isSpecialCharacter(ch)) {
				return new RFC822Token(specialToken, ch);
			}
			if (isControlCharacter(ch)) {
				return (new RFC822Token(ctlToken, ch));
			}
			//	// must be an atom
			var st = cursor -1, cnt = 1;
			while (true) {
				if (cursor > inpStr.length) {
					break;
				}
				ch = inpStr.charAt(cursor);
				if ((ch == " ") || (isControlCharacter(ch)) || (isSpecialCharacter(ch))) {
					break;
				}
				cnt +=1;
				cursor +=1;
			}
			return (new RFC822Token (atomToken, inpStr.substring(st, st + cnt)));
		}
	}
	return new RFC822Token(errToken, "");
}

function scanLookAheadToMethod(specials) {
	var thisToken, i;
	with (this) {
		while (true) {
			thisToken = scanNextToken();
			lookAheadStack.push(thisToken);
			if (thisToken.tokenType == eofToken) {
				return false;
			}
			if (thisToken.tokenType == specialToken) {
				if (specials.indexOf(thisToken.value) != -1) {
					return true;
				}
			}
		}
		return false;
	}
}

function RFC822Scanner(theText) {
	this.inpStr = theText;
	this.cursor = 0;
	this.lookAheadStack = new stack();
	this.SpecialCharacters = "()<>@,;:\\\".[]";
	this.isSpecialCharacter = inSpecialCharacters;
	this.scanString = scanStringMethod;
	this.scanNextToken = scanNextTokenMethod;
	this.scanLookAheadTo = scanLookAheadToMethod;
	this.errToken = 0;
	this.atomToken = 1;
	this.stringToken = 2;
	this.specialToken = 3;
	this.ctlToken = 4;
	this.eofToken = 5;
	this.commentToken = 6;
	this.whiteSpaceToken = 7;
}

function isEmailDomain(scannedObj) {
	var thisToken, dotWanted = false;
	with (scannedObj) {
		while (true) {
			thisToken = scanNextToken();
			if (thisToken.tokenType == eofToken) {
				if (! dotWanted) {
//					prompt("An email domain cannot end with a period");
				}
				return dotWanted;
			}
			if (dotWanted) {
				if ((thisToken.tokenType != specialToken) || (thisToken.value != ".")) {
					lookAheadStack.push(thisToken);
					return (true);
				}
			} else {
				if ((thisToken.tokenType == specialToken) && (thisToken.value == "[")) {
					var tempStr = scanString("]", "[\r\"");
					if (typeof tempStr != "string") {
						return false;
					}
					thisToken = scanNextToken();
					if (thisToken.tokenType != eofToken) {
						lookAheadStack.push(thisToken);
					}
					return true;
				}
				if (thisToken.tokenType != atomToken) {
//					prompt("An email domain cannot end with a period");
					return false;
				}
			}
			dotWanted = ! dotWanted;
		}
	}
	return false;
}

function isAddrSpec(scannedObj) {
	//	// looking for : local-part @ domain
	//	// local-part is in the stack
	//	//domain has to be scanned
	var dotWanted = false, thisToken;
	with (scannedObj) {
		while (true) {
			if (lookAheadStack.depth() > 0) {
				thisToken = lookAheadStack.pop();
				if (dotWanted) {
					if ((thisToken.tokenType != specialToken) || (thisToken.value != ".")) {
//						prompt("period expected, found " + thisToken.value);
						return false;
					}
				} else {
					if ( ! ((thisToken.tokenType == atomToken) || (thisToken.tokenType == stringToken))) {
//						prompt("domain cannot contain " + thisToken.value);
						return false;
					}
				}
			} else {
				if (! dotWanted) {
//					prompt("Extra period");
					return false;
				}
				break;
			}
			dotWanted = ! dotWanted;
		}
		return isEmailDomain(scannedObj);
	}
}
		
function isRouteAddr(scannedObj) {
	//	//looking for : phrase route-addr
	//	//phrase may be empty, its in the stack
	//	//route-addr hasn't been scanned
	var thisToken, dotWanted;
	with (scannedObj) {
		while (true) {
			if (lookAheadStack.depth() == 0) {
				break;
			}
			thisToken = lookAheadStack.pop();
			if ( ! ((thisToken.tokenType == atomToken) || (thisToken.tokenType == stringToken))) {
//				prompt("string or word expected before '<'");
				return false;
			}
		}
		//	//Seeking
			//	optional route - starts with @ followed by :
			//	addr spec followed by >
		thisToken = scanNextToken();
		while ((thisToken.tokenType == specialToken) && (thisToken.value == "@")) {
			if (! isEmailDomain(scannedObj)) {
				return false;
			}
			if (lookAheadStack.depth() == 0) {
				return false;
			}
			thisToken = lookAheadStack.pop();
			if ((thisToken.tokenType == specialToken) && (thisToken.value == ":")) {
				thisToken = scanNextToken();
				break;
			}
			if ((thisToken.tokenType != specialToken) || (thisToken.value != ",")) {
//				prompt("comma or colon expected");
				return false;
			}
			thisToken = scanNextToken ();
		}
		//	// Now we have an addr spec 
		//	// first the local-part
		dotWanted = false;
		while (true) {
			if (dotWanted) {
				if ((thisToken.tokenType == specialToken) && (thisToken.value == "@")) {
					break;
				}; //terminate loop
				if ((thisToken.tokenType != specialToken) || (thisToken.value != ".")) {
//					prompt("period or @ expected, found " + thisToken.value);
					return false;
				}
			} else {
				if ((thisToken.tokenType != atomToken) && (thisToken.tokenType != stringToken)) {
//					prompt("word or string expected, found " + thisToken.value);
					return false;
				}
			}
			dotWanted = ! dotWanted;
			thisToken = scanNextToken();
		}
		//	// now the domain
		if (! isEmailDomain(scannedObj)) {
			return false;
		}
		//	// isDomain has stuck its terminator at the end
		if (lookAheadStack.depth() == 0) {
//			prompt("> expected");
			return false;
		}
		thisToken = lookAheadStack.pop();
		if ((thisToken.tokenType != specialToken) || (thisToken.value != ">")) {
//			prompt("> expected");
			return false;
		}
		return true;
	}
}


function isEmailAddress(inpStr) {
	if (isEmpty(inpStr))
	   return false;
	
	if (isWhitespace(inpStr)) 
	  return false;
	  
	//	// step thru address looking for @ or <
	var inpObj = new RFC822Scanner(inpStr);
	var gotOne = false;
	with (inpObj) {
		if (!scanLookAheadTo("@<")) {
			gotOne = false;
		} else {
			var thisToken  = lookAheadStack.pop();
			if ((thisToken.tokenType == specialToken) && (thisToken.value == "@")) {
				gotOne = isAddrSpec(inpObj);
			}
			if ((thisToken.tokenType == specialToken) && (thisToken.value == "<")) {
				gotOne = isRouteAddr(inpObj);
			}
			if (lookAheadStack.depth() != 0) {
				gotOne = false;
			}
			thisToken = scanNextToken();
			if (thisToken.tokenType != eofToken) {
//				prompt("Extraneous input after address:" + thisToken.value);
				gotOne = false;
			}
		}
	}
	
	//	// Javascript 1.2 only - delete inpObj
	return gotOne;
}

function validEmail(email) {
// This function simply checks for a @ and a . preceded, separated and followed by non-whitespace characters
//  after extensive research, this was determined to be the best solution.
	if (/^\S+@\S+\.\S+$/.test(email)) {
		return (true)
	}
	return (false)
}

