// string.js -- add functionality to the base javascript string object

// public methods

function string_trimLeft()
{
	return this.replace(/^\s+/, "");
}

function string_trimRight()
{
	return this.replace(/\s+$/, "");
}

function string_trim()
{
	return this.trimLeft().trimRight();
}

function string_pad(length, character, begin)
{
	var value = this;

	var padLength = length - value.length;
	if (padLength > 0)
	{
		if (character == null)
		{
			character = "0";
		}

		var padding = "";
		for (var i=0; i<padLength; i++)
		{
			padding += character;
		}

		if (begin == null)
		{
			begin = true;
		}

		if (begin)
		{
			value = padding + value;
		}
		else
		{
			value += padding;
		}
	}

	return value;
}

function string_unpad(length, begin)
{
	var value = this;

	if (length < value.length)
	{
		if (begin == null)
		{
			begin = true;
		}

		if (begin)
		{
			value = value.substr(0, length);
		}
		else
		{
			value = value.substring(value.length - length);
		}
	}

	return value;
}

function string_escapeSQL()
{
	return this.replace(/'/g, "''");
}

function string_unescapeSQL()
{
	return this.replace(/''/g, "'");
}

function string_escapeXML()
{
	return value.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
}

function string_unescapeXML()
{
	return value.replace("&lt;", "<").replace("&gt;", ">").replace("&amp;", "&");
}

function string_formatNumber(trailingDecimals, showGroupSeparator)
{
	var value = this;

	if (trailingDecimals == null)
	{
		trailingDecimals = 0;
	}

	if (showGroupSeparator == null)
	{
		showGroupSeparator = true;
	}

	var parts = this.split(".");
	if (parts && (parts.length == 1 || parts.length == 2))
	{
		var whole = parts[0];
		if (whole == "")
		{
			whole = "0";
		}

		var fractional = 0;
		if (parts.length == 2)
		{
			fractional = Number(string_private_roundFractionAtDigit("." + parts[1], trailingDecimals));
		}

		if (fractional >= 1)
		{
			whole = string_private_roundToNextWholeNumber(whole);
			fractional -= 1;
		}

		fractional = string_private_roundFractionAtDigit(String(fractional), trailingDecimals);

		if (trailingDecimals > 0)
		{
			if (showGroupSeparator)
			{
				value = string_private_addGroupSeparator(whole, ",", ".") + fractional.replace("0.", ".");
			}
			else
			{
				value = whole + fractional.replace("0.", ".");
			}
		}
		else
		{
			if (showGroupSeparator)
			{
				value = string_private_addGroupSeparator(whole, ",", ".");
			}
			else
			{
				value = whole;
			}
		}
	}

	return value;
}

function string_formatInteger(showGroupSeparator)
{
	return this.formatNumber(0, showGroupSeparator);
}

function string_formatFloat(trailingDecimals, showGroupSeparator)
{
	if (trailingDecimals == null)
	{
		trailingDecimals = 2;
	}

	return this.formatNumber(trailingDecimals, showGroupSeparator);
}

function string_formatCurrency(trailingDecimals, showGroupSeparator)
{
	if (trailingDecimals == null)
	{
		trailingDecimals = 2;
	}

	return "$" + this.formatNumber(trailingDecimals, showGroupSeparator);
}

function string_formatPercentage(trailingDecimals, showGroupSeparator)
{
	return this.formatNumber(trailingDecimals, showGroupSeparator) + "%";
}

function string_toInteger()
{
	return parseInt(this);
}

function string_toFloat()
{
	return parseFloat(this);
}

function string_isEmpty()
{
	return (this.length == 0);
}

function string_isLength(length)
{
	return (this.length == length);
}

function string_isAlpha()
{
	return /^[A-Za-z]+$/.test(this);
}

function string_isAlphaNumeric()
{
	return /^[A-Za-z0-9]+$/.test(this);
}

function string_isNumeric()
{
	return /^[-|+]?(\d*)(\.?)(\d*)$/.test(this);
}

function string_isEmail()
{
	return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*\.(\w{2}|(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum))$/.test(this);
}

// NOTE:
// (1) this only works with date strings which pads the month and day to two digits and use a four digit year
// (2) the format string must contain the specifiers YYYY, MM, and DD
function string_isDate(format)
{
	var result = false;

	format = String(format).toUpperCase();

	if (this.length == format.length)
	{
		result = true;

		for (var i=0; i<this.length; i++)
		{
			if (format.charAt(i) == "Y" || format.charAt(i) == "M" || format.charAt(i) == "D")
			{
				if (isNaN(this.charAt(i)))
				{
					result = false;
					break;
				}
			}
			else if (format.charAt(i) != this.charAt(i))
			{
				result = false;
				break;
			}
		}

		if (result)
		{
			result = false;

			var yearPosition = format.indexOf("YYYY");
			var monthPosition = format.indexOf("MM");
			var dayPosition = format.indexOf("DD");

			if (yearPosition != -1 && monthPosition != -1 && dayPosition != -1)
			{
				var year = Number(this.substr(yearPosition, 4));
				var month = Number(this.substr(monthPosition, 2)) - 1; // javascript months are 0 based
				var day = Number(this.substr(dayPosition, 2));

				var objDate = new Date(year, month, day);

				if (objDate.getFullYear() == year && objDate.getMonth() == month && objDate.getDate() == day)
				{
					result = true;
				}
			}
		}
	}

	return result;
}

// public interface

String.prototype.trimLeft = string_trimLeft;
String.prototype.trimRight = string_trimRight;
String.prototype.trim = string_trim;

String.prototype.pad = string_pad;
String.prototype.unpad = string_unpad;

String.prototype.escapeSQL = string_escapeSQL;
String.prototype.unescapeSQL = string_unescapeSQL;
String.prototype.escapeXML = string_escapeXML;
String.prototype.unescapeXML = string_unescapeXML;

String.prototype.formatNumber = string_formatNumber;
String.prototype.formatInteger = string_formatInteger;
String.prototype.formatFloat = string_formatFloat;
String.prototype.formatCurrency = string_formatCurrency;
String.prototype.formatPercentage = string_formatPercentage;

String.prototype.toInteger = string_toInteger;
String.prototype.toFloat = string_toFloat;

String.prototype.isEmpty = string_isEmpty;
String.prototype.isLength = string_isLength;
String.prototype.isAlpha = string_isAlpha;
String.prototype.isAlphaNumeric = string_isAlphaNumeric;
String.prototype.isNumeric = string_isNumeric;
String.prototype.isEmail = string_isEmail;
String.prototype.isDate = string_isDate;

// private methods

function string_private_roundToNextWholeNumber(value)
{
	var carry = true;
	for (var i=value.length - 1; i > -1 && carry == true; i--)
	{
		var digit = parseInt(value.charAt(i)) + 1;
		if (digit < 10)
		{
			carry = false;
		}
		else
		{
			digit -= 10;
		}

		value = value.substr(0, i) + digit + value.substr(i + 1);
	}

	if (carry == true)
	{
		value = "1" + value;
	}

	return value;
}

function string_private_roundFractionAtDigit(value, trailingDecimals)
{
	var parts = value.split(".");
	if (parts && (parts.length == 1 || parts.length == 2))
	{
		if (parts.length == 1)
		{
			value = "0";
		}
		else
		{
			value = parts[1];
		}

		if (value.length < trailingDecimals)
		{ 
			value = value.pad(trailingDecimals, "0", false);

			if (parts)
			{
				value = "." + value;
			}
		}
		else if (value.length >= trailingDecimals)
		{
			var remaining = value.substr(trailingDecimals);
			if (remaining == "")
			{
				remaining = "0";
			}
			var carry = Number(remaining.charAt(0)) > 4;

			value = value.substr(0, trailingDecimals);

			for (var i=value.length - 1; i > -1 && carry == true; i--)
			{
				var digit = parseInt(value.charAt(i)) + 1;
				if (digit < 10)
				{
					carry = false;
				}
				else
				{
					digit -= 10;
				}

				value = value.substr(0, i) + digit + value.substr(i + 1);
			}

			if (carry == true)
			{
				value = "1" + (parts ? "." : "") + value;
			}
			else if (parts)
			{
				value = "." + value;
			}
		}

	}

	return value;
}

function string_private_addGroupSeparator(value, groupSeparator, fractionalSeparator)
{
	if (groupSeparator == null)
	{
		groupSeparator = ",";
	}

	if (fractionalSeparator == null)
	{
		fractionalSeparator = ".";
	}

	var parts = value.split(fractionalSeparator);
	if (parts && (parts.length == 1 || parts.length == 2))
	{
		var formattedWholeNumber = "";

		for (var i=parts[0].length - 1, ii=0; i>-1; i--, ii++)
		{
			if (ii != 0 && ii%3 == 0)
			{
				formattedWholeNumber = groupSeparator + formattedWholeNumber;
			}

			formattedWholeNumber = parts[0].charAt(i) + formattedWholeNumber;
		}

		if (parts.length == 1)
		{
			value = formattedWholeNumber;
		}
		else
		{
			value = formattedWholeNumber + fractionalSeparator + parts[1];
		}
	}

	return value;
}

