// Returns the given string without any leading or trailing white space
function TrimString(strString)
{
	return (strString.replace(/^\s*([\s\S]*\S+)\s*$|^\s*$/, "$1"));
}

// Returns true if the given string only contains white space
function IsEmptyString(strString)
{
	var reWhiteSpace = /^\s+$/;

	return ((strString == null) || (strString.length == 0) || (reWhiteSpace.test(strString)));
}

// Returns true if the given number is within the given range of values
function IsNumWithinBounds(strNumber, intLower, intUpper)
{
	var intNumber = parseInt(strNumber, 10);

	return ((intNumber >= intLower) && (intNumber <= intUpper));
}


// Returns the number of days in the given month (accounting for leap years)
function GetNumDaysInMonth(intMonth, intYear)
{
	var aintDaysInMonth = new Array(31,29,31,30,31,30,31,31,30,31,30,31);


	// Check for the special case of February
	if (intMonth == 2)
		return (((intYear % 4 == 0) && ((!(intYear % 100 == 0)) || (intYear % 400 == 0))) ? 29 : 28);
	else
		return aintDaysInMonth[intMonth - 1];
}

// Returns true if the given string is a valid URI scheme
function IsURIScheme(strURIScheme)
{
	if (IsEmptyString(strURIScheme))
	{
		// An empty string isn't a valid URI scheme
		return false;
	}
	else
	{
		// Test this string against the most common schemes
		if ((strURIScheme == "ftp:") || (strURIScheme == "http:") || (strURIScheme == "gopher:") || (strURIScheme == "mailto:") || (strURIScheme == "news:") || (strURIScheme == "telnet:") || (strURIScheme == "wais:") || (strURIScheme == "file:") || (strURIScheme == "https:") || (strURIScheme == "urn:"))
			return true;
		else
			return false;
	}
}

// Returns true if the given string is a valid domain name
function IsDomain(strDomain)
{
	var cintNumIPAddressParts = 4;

	var i = 0;
	var blnIsIPAddress = false;
	var astrDomainParts = "";
	var reDomain = /^([0-2]?[\d]{0,2}\.[0-2]?[\d]{0,2}\.[0-2]?[\d]{0,2}\.[0-2]?[\d]{0,2})$|^(([a-zA-Z\d-]+\.)+[a-zA-Z]{2,})$/;


	if (IsEmptyString(strDomain))
	{
		// An empty string isn't a valid domain name
		return false;
	}
	else
	{
		// Test against the regular expression first
		if (!reDomain.test(strDomain)) return false;

		// Break apart the domain name
		astrDomainParts = strDomain.split(".");

		// Check for a possible IP address (i.e. exactly 4 parts)
		if (astrDomainParts.length == cintNumIPAddressParts)
		{
			blnIsIPAddress = true; // Assume we have an IP address

			// If each of these parts is numeric then we must have an IP address
			for (i = 0; i < cintNumIPAddressParts; i++)
				if (!IsNumber(astrDomainParts[i])) blnIsIPAddress = false;

			// Do we have an IP address?
			if (blnIsIPAddress)
			{
				// Make sure each of these parts is between 0 and 255
				for (i = 0; i < cintNumIPAddressParts; i++)
					if (!IsNumWithinBounds(astrDomainParts[i], 0, 255)) return false;

				// Also the first value can't be 0
				if (astrDomainParts[0] == 0) return false;
			}
		}

		// If we get here then the domain name must be valid
		return true;
	}
}


// Checks if the given string is a valid number
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsNumber(strNumber)
{
	var cintSmallest = -2147483648 // The smallest possible number
	var cintLargest = 2147483647 // The largest possible number

	var reNumber = /^[+-]?(\d+(\.\d+)?|\.\d+)$/;


	if (IsEmptyString(strNumber))
	{
		// Decide what to return in the case of an empty string
		if (IsNumber.arguments.length == 1) return false;
		else return (IsNumber.arguments[1] == true);
	}
	else
	{
		strNumber = TrimString(strNumber);

		// Test against the regular expression, and also make sure it's a valid number
		return (reNumber.test(strNumber) && !isNaN(strNumber) && IsNumWithinBounds(strNumber, cintSmallest, cintLargest));
	}
}


// Checks if the given string is a valid date
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsDate(strDate)
{
	var intYear = 0, intMonth = 0, intDay = 0;

	var astrDateParts = "";
	var reDate = /^[\d]{3,4}-[0-1]?[\d]-[0-3]?[\d]$/; // Date in the format YYYY-MM-DD


	if (IsEmptyString(strDate))
	{
		// Decide what to return in the case of an empty string
		if (IsDate.arguments.length == 1) return false;
		else return (IsDate.arguments[1] == true);
	}
	else
	{
		strDate = TrimString(strDate);

		// Test against the regular expression first, so we know we're dealing with valid numbers
		if (!reDate.test(strDate)) return false;

		// Break apart the date
		astrDateParts = strDate.split("-");

		// Put these parts into separate variables
		intYear = parseInt(astrDateParts[0], 10);
		intMonth = parseInt(astrDateParts[1], 10);
		intDay = parseInt(astrDateParts[2], 10);

		// We should have exactly 3 elements in the array, otherwise the regular expression would have found an error
		// Test each part of the date to make sure it's valid
		if (!IsNumWithinBounds(intYear, 100, 9999)) return false; // Year must be between 100 and 9999
		if (!IsNumWithinBounds(intMonth, 1, 12)) return false; // Month must be between 1 and 12
		if (!IsNumWithinBounds(intDay, 1, 31)) return false; // Day must be between 1 and 31

		// Check against the number of days in the given month
		if (intDay > GetNumDaysInMonth(intMonth, intYear)) return false;

		// If we get here then the date must be valid
		return true;
	}
}


// Checks if the given string is a valid phone number
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsPhoneNumber(strPhoneNumber)
{
	var rePhoneNumber = /^\+?[\d() -.]{3,30}$/;


	if (IsEmptyString(strPhoneNumber))
	{
		// Decide what to return in the case of an empty string
		if (IsPhoneNumber.arguments.length == 1) return false;
		else return (IsPhoneNumber.arguments[1] == true);
	}
	else
	{
		strPhoneNumber = TrimString(strPhoneNumber);
		return (rePhoneNumber.test(strPhoneNumber));
	}
}

// Checks if the given string is a valid postal code
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsPostalCode(strPostalCode)
{
	var rePostalCode = /^[A-Za-z\d -]{2,10}$/;


	if (IsEmptyString(strPostalCode))
	{
		// Decide what to return in the case of an empty string
		if (IsPostalCode.arguments.length == 1) return false;
		else return (IsPostalCode.arguments[1] == true);
	}
	else
	{
		strPostalCode = TrimString(strPostalCode);
		return (rePostalCode.test(strPostalCode));
	}
}

// Checks if the given string is a valid internet URI
// There is a third optional boolean argument which is used to decide what to return when the given string is empty
function IsURI(strURI, blnAllowRelativePaths)
{
	var intPathIndex = 0;
	var astrURIParts = "", astrURIPath = "";
	var reWhiteSpace = /\s+/;


	if (IsEmptyString(strURI))
	{
		// Decide what to return in the case of an empty string
		if (IsURI.arguments.length == 2) return false;
		else return (IsURI.arguments[2] == true);
	}
	else
	{
		strURI = TrimString(strURI);

		// Separate the scheme from the rest of the URI so it can be tested separately
		astrURIParts = strURI.split("://");

		// Was a scheme defined? That is, is there more than one item in the array?
		if (astrURIParts.length > 1)
		{
			// Make sure the scheme is valid, otherwise this isn't a URI
			if (!IsURIScheme(astrURIParts[0] + ":")) return false;

			// There's more than one item in the arrary, so the path info must be in the second element (as opposed to the first)
			intPathIndex = 1;
		}

		// Break up the domain and path information. The domain name will be in the first array element
		astrURIPath = astrURIParts[intPathIndex].split("/");

		// Check all of the sections for spaces (which shouldn't be present anywhere)
		for (var i = 0; i < astrURIPath.length; i++)
			if (reWhiteSpace.test(astrURIPath[i])) return false;

		// Are we allowing relative paths?
		if (blnAllowRelativePaths)
			return true; // If we get to here then everything is ok
		else
			return (IsDomain(astrURIPath[0])); // Check the domain
	}
}

// Checks if the given string is a valid e-mail address
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsEmailAddress(strEmail)
{
	var astrEmailParts = "";
	var reEmail = /^([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)@([a-zA-Z\d.-])+$/; // We don't need to test the domain since it'll be done separately


	if (IsEmptyString(strEmail))
	{
		// Decide what to return in the case of an empty string
		if (IsEmailAddress.arguments.length == 1) return false;
		else return (IsEmailAddress.arguments[1] == true);
	}
	else
	{
		strEmail = TrimString(strEmail);

		// Test against the regular expression first, so we know we have a semi-valid e-mail address
		if (!reEmail.test(strEmail)) return false;

		// Break the e-mail address into 2 parts
		astrEmailParts = strEmail.split("@");

		// Check that the second half of the address is a valid domain
		return (IsDomain(astrEmailParts[1]));
	}
}

// Checks if the given string is a valid Australian Business Number
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsABN(strABN)
{
	var reABN = /^\+?[\d ]{11,14}$/;


	if (IsEmptyString(strABN))
	{
		// Decide what to return in the case of an empty string
		if (IsABN.arguments.length == 1) return false;
		else return (IsABN.arguments[1] == true);
	}
	else
	{
		strABN = TrimString(strABN);
		return (reABN.test(strABN));
	}
}

// Checks if the given string is a valid credit card number
// There is a second optional boolean argument which is used to decide what to return when the given string is empty
function IsCreditCardNumber(strCreditCardNumber)
{
	var reCreditCardNumber = /^\+?[\d -]{16,19}$/;


	if (IsEmptyString(strCreditCardNumber))
	{
		// Decide what to return in the case of an empty string
		if (IsCreditCardNumber.arguments.length == 1) return false;
		else return (IsCreditCardNumber.arguments[1] == true);
	}
	else
	{
		strCreditCardNumber = TrimString(strCreditCardNumber);
		return (reCreditCardNumber.test(strCreditCardNumber));
	}
}


// Turns auto complete off for the given form or element
function TurnOffAutoComplete(strElementID)
{
	document.getElementById(strElementID).setAttribute("autocomplete", "off");
}
