// Copyright (C) 2007 Cognos Incorporated. All rights reserved.
// Cognos (R) is a trademark of Cognos Incorporated.

/*
	CText.js

	This script is used to provide interactivity for the textBox prompt control
*/

//Constructor to create a textBox component
//	oSubmitField: the form control that will be submit a value to the server
//	oFormField: this is the the form control that the user interacts with
//	oImgTest: the image object used for validation handling
//	sDataType: the type of data to validate (e.g. number)
//	sDefaultValue: the starting value for the form control
//	bRequired: a flag to determine whether input is required
//	sSubmitType: 'default' will submit as a standard form
//				'XML' will convert the submission to XML and submit
//	bMultiline: [true|false] does this prompt take a values on multiple lines (e.g. textarea)
//	bShowThousandSeparator: [true|false] control whether to add the thousands separator to the formatted text input
//	bAllowZero: [true|false] control whether we allow the user to type in a zero for input (there are cases where they may end up dividing by zero)
//	sRef: string that represents promptId
//	bReadOnly: [true|false] a flag to determine if prompt is read only

function CText(oSubmitField, oFormField, oImgTest, sDataType, sDefaultValue, bRequired, sSubmitType, bMultiline, bShowThousandSeparator, bAllowZero, oTableContainer, sRef, bReadOnly)
{
	this.m_oSubmitField = oSubmitField;
	this.m_oFormField = oFormField;
	this.m_sRef = sRef;
	if (oTableContainer)
	{
		this.m_oTableContainer = oTableContainer;
	}
	else
	{
		this.m_oTableContainer = oFormField;
	}

	this.m_sDataType = sDataType;

	//is this a currency?
	this.m_bIsCurrency = false;

	// by default we allow zeros in the text areas
	this.m_bAllowZero = true;
	if (bAllowZero == false)
	{
		this.m_bAllowZero = false;
	}

	this.m_bRequired = bRequired;
	this.m_bValid = false;
	this.m_sSubmitType = sSubmitType;
	this.m_bMultiline = false;
	sDefaultValue = sDefaultValue.replace(/\\U003E/g, ">").replace(/\\U003C/g, "<");
	this.m_sCurrentValue = sDefaultValue;
	if (bMultiline == true)
	{
		this.m_bMultiline = true;
	}

	this.m_bShowThousandSeparator = true;
	if (bShowThousandSeparator == false)
	{
		this.m_bShowThousandSeparator = false;
	}

	//skin folder
	this.m_sSkin = (typeof getPromptSkin != "undefined" ? getPromptSkin() : "../skins/corporate");

	//images for handling errors
	this.m_oImgCheckDate = oImgTest;
	if (this.m_oImgCheckDate != null)
	{
		this.m_oImgErrorFalse= new Image();
		this.m_oImgErrorFalse.src = this.m_sSkin + "/prompting/images/error_timed_small_off.gif";
		this.m_oImgErrorTrue = new Image();
		this.m_oImgErrorTrue.src = this.m_sSkin + "/prompting/images/error_timed_small.gif";
	}

	//create regular expressions for parsing with current locale
	initParsingRegularExpressions();

	//set the default parameter values
	if (sDefaultValue)
	{

		//convert the default value to the locale
		var sLocaleDefaultValue = convertSQLToLocale(sDefaultValue, this.m_sDataType);
		if (this.bParse(sLocaleDefaultValue, this.m_sDataType) == true)
		{
			this.draw(this.m_sCurrentValue);
		}
		else
		{
			this.draw(sLocaleDefaultValue);
		}
	}
	else
	{
		this.m_sCurrentValue = "";
	}

	//set the control as read only if specified
	this.setReadOnly(bReadOnly);

	//check the default value to see if it is valid
	this.checkData();
}

//this function is used to restore the
//control to a valid value
function CText_setCurrentValue()
{
	this.m_oFormField.value = this.m_sCurrentValue;
}

//parse the current input and validate the data
function CText_bParse(sValue, sDataType)
{
	var sTestString = sParseByDataType(sValue, sDataType, this.m_bAllowZero);

	if (sTestString == false && typeof(sTestString) == "boolean")
	{
		this.m_bIsCurrency = false;
		return false;
	}
	else
	{
		sTestString = sTestString.toString();
		//persist currency character?
		var z = sTestString.search(rCurrencySymbol);
		if (z != -1)
		{
			this.m_bIsCurrency = true;
		}
		else
		{
			this.m_bIsCurrency = false;
		}

		this.m_sCurrentValue = sTestString;
		return true;
	}

}

function sParseByDataType(sValue, sDataType, bAllowZero)
{
	switch (sDataType)
	{
		case 'number':
			var sTestString = sParseNumber(sValue);
			break;

		//the data type is money
		case 'currency':
			var sTestString = sParseCurrency(sValue);
			break;

		//the data type is an integer
		case 'integer':
			var sTestString = sParseInteger(sValue);
			break;

		//the data type is natural number (non negative integers)
		case 'natural':
			var sTestString = sParseNaturalNumber(sValue);
			break;

		//the data type is whole number (positive integers)
		case 'whole':
			var sTestString = sParseWholeNumber(sValue);
			break;

		case 'percentage':
			var sTestString = sParsePercentNumber(sValue);
			break;

		default:
			//no data type specified
			//don't do any special parsing
			return sValue;
			break;
	}
	if (sTestString == false && typeof(sTestString) == "boolean")
	{
		return(false);
	}
	if (!bAllowZero && bIsZero(sTestString))
	{
		return(false);
	}
	return(sTestString);
}

//handle focus given to the control
function CText_setCurrentFocus(obj)
{
	this.m_oCurrentFocus = obj;
	this.m_oCurrentFocus.select();
}

//handle focus lost from the control
function CText_lostFocus()
{
	//dynamically format values in the text box
	if (this.m_bMultiline != true)
	{
		if (this.bParse(this.m_oFormField.value, this.m_sDataType)!=false)
		{
			if (this.sGetFormatValue() !=false)
			{
				this.m_oFormField.value=this.sGetFormatValue();
			}
		}
	}
	else
		this.checkData();
}

//format the text
function CText_draw(sValue)
{
	this.m_oFormField.value = getFormatByDataType(sValue, this.m_sDataType, this.m_bIsCurrency, this.m_bShowThousandSeparator);
}

//catch the backspace key
//some browsers (IE5.5 don't capture this event)
function CText_keyPress(evt)
{
	//trap the tab key and shift-tab
	evt = (evt) ? evt : ((event) ? event : null);
	if (evt)
	{
		var keyCode = (evt.keyCode) ? evt.keyCode : evt.which;

		if (keyCode=='8')
		{
			//check the data that has been typed in
			this.checkData();
		}
	}

	return true;
}

//set an interval in which the control will check for changed/valid data
function CText_startCheckDataInterval(interval)
{
	this.m_sCurrentValue = this.m_oFormField.value;
	functionString = this.m_sRef + ".checkValueChange()";
	this.m_intervalId = setInterval(functionString, interval);
}

//stop the control from checking for changed/valid data
function CText_endCheckDataInterval()
{
	if (typeof this.m_intervalId != "undefined")
		clearInterval(this.m_intervalId);
	this.checkData();
}

//check if value has changed
function CText_checkValueChange()
{
	if (this.m_oFormField.value != this.m_sCurrentValue)
		this.endCheckDataInterval();
}

//render the validated state
function CText_checkPass()
{
	if ((this.m_oImgCheckDate != null) && (this.m_oImgCheckDate.src != this.m_oImgErrorFalse.src))
	{
		this.m_oImgCheckDate.src = this.m_oImgErrorFalse.src;
	}
	this.m_oTableContainer.className = "";
	//notify any observers
	if (typeof notify == "function")
	{
		notify(gPASS, this);
	}
}

//render the validation error state
function CText_checkFail()
{
	if (this.m_oImgCheckDate != null)
	{
		this.m_oImgCheckDate.src = this.m_oImgErrorTrue.src + '?' + Math.random();
	}
	this.m_oTableContainer.className = "clsTextWidgetParseError";

	//notify any observers
	if (typeof notify == "function")
	{
		notify(gFAIL, this);
	}
}

//validate the input into the control
function CText_checkData()
{
	if ((this.m_bRequired == true) && (this.m_oFormField.value==''))
	{
		this.m_bValid = false;
		this.checkFail();
		return false;
	}

	else
	{
		//determine whether this is a single value or multiple values
		var rMultiline = /[\n\f\r]/;
		var arInsertText = this.m_oFormField.value.split(rMultiline);
		if (arInsertText.length > 1)
		{
			var bParseValid = true;
			for (i=0; i < arInsertText.length; i++)
			{
				var bTest = sParseByDataType(arInsertText[i], this.m_sDataType);
				if (bTest == false)
				{
					bParseValid = false;
				}
			}
			if (bParseValid == false)
			{
				this.m_bValid = false;
				this.checkFail();
				return false;
			}
			else
			{
				this.m_bValid = true;
				this.checkPass();
				this.m_sCurrentValue = this.m_oFormField.value;

				//set the form value for non-xml prompts
				if (this.m_sSubmitType != 'XML')
				{
					this.m_oSubmitField.value = this.m_sCurrentValue;
				}
				return true;
			}
		}
		else
		{
			var sValue = this.bParse(this.m_oFormField.value, this.m_sDataType);
			if ((sValue == false) && ((this.m_oFormField.value!='')))
			{
				this.m_bValid = false;
				this.checkFail();
				return false;
			}
			else
			{
				this.m_bValid = true;
				this.checkPass();
				//set the form value for non-xml prompts
				if (this.m_sSubmitType != 'XML')
				{
					this.m_oSubmitField.value = this.m_sCurrentValue;
				}
				return true;
			}
		}
	}
}

//perform any special processing for the server.
function CText_preProcess()
{
	if (this.m_sSubmitType == 'XML')
	{
		var sURLValues = "";
		if (this.m_oFormField.value != '')
		{
			sURLValues += '<selectOption';
			sURLValues += ' displayValue="' + sXmlEncode(this.sGetFormatValue()) +'"';
			sURLValues += ' useValue="' + sXmlEncode(this.sGetValue()) +'"';
			sURLValues += ' selected="true" />';
		}
		addSelectChoices(this.m_oSubmitField, sURLValues);
	}
	else
	{
		this.m_oSubmitField.value = this.m_sCurrentValue;
	}
}

//return a valid value
function CText_sGetValue()
{
	this.checkData();
	if (this.m_bValid == true)
	{
		return new String(this.m_sCurrentValue);
	}
	else
	{
		return false;
	}
}

//return a valid value
function CText_sGetFormatValue()
{
	this.checkData();
	if (this.m_bValid == true)
	{
		return getFormatByDataType(this.sGetValue(), this.m_sDataType, this.m_bIsCurrency, this.m_bShowThousandSeparator);
	}
	else
	{
		return false;
	}
}

//remove all values from the prompt control
function CText_clear()
{
	this.m_oSubmitField.value = '';
	this.m_oFormField.value = '';
	this.m_sCurrentValue = '';
}

function CText_getDecimalSize()
{
	var sTest = this.sGetValue();
	// we are testing agains the use value which will always have a value of "." no matter which locale we're in
	var iDecimalPos = -1;
	if (sTest !== false)
	{
		iDecimalPos = sTest.lastIndexOf(".");
	}

	//test for a decimal place
	if (iDecimalPos != -1)
	{
		//strip the leading characters
		sTest= sTest.substring (iDecimalPos +1);

		//strip any non numerics
		var iLastNumber = sTest.search(/\D/);
		if (iLastNumber != -1)
		{
			sTest = sTest.substring (0, iLastNumber);
		}
		return sTest.length;
	}
	else
	{
		return 0;
	}
}

function CText_hasValue()
{
	if (this.m_oFormField.value == '')
	{
		return false;
	}
	else
	{
		return true;
	}
}

function CText_setReadOnly(bValue)
{
	if (typeof bValue == "boolean")
		this.m_oFormField.readOnly = bValue;
}

//Prototypes to assign methods to new instances of the object
CText.prototype.setCurrentValue = CText_setCurrentValue;
CText.prototype.bParse = CText_bParse;
CText.prototype.setCurrentFocus = CText_setCurrentFocus;
CText.prototype.lostFocus = CText_lostFocus;
CText.prototype.keyPress = CText_keyPress;
CText.prototype.checkData = CText_checkData;
CText.prototype.checkPass = CText_checkPass;
CText.prototype.checkFail = CText_checkFail;
CText.prototype.preProcess = CText_preProcess;
CText.prototype.sGetValue = CText_sGetValue;
CText.prototype.sGetFormatValue = CText_sGetFormatValue;
CText.prototype.isValid = CPromptControl_isValid;
CText.prototype.isRequired = CPromptControl_isRequired;
CText.prototype.draw = CText_draw;
CText.prototype.getValid = CPromptControl_getValid;
CText.prototype.clear = CText_clear;
CText.prototype.getDecimalSize = CText_getDecimalSize;
CText.prototype.hasValue = CText_hasValue;
CText.prototype.keyPress = CText_keyPress;
CText.prototype.startCheckDataInterval = CText_startCheckDataInterval;
CText.prototype.endCheckDataInterval = CText_endCheckDataInterval;
CText.prototype.checkValueChange = CText_checkValueChange;
CText.prototype.setReadOnly = CText_setReadOnly;


/* Utility Functions */

function sParseNumber(sNumber)
{
	var sTestString = sNumber;
	var percentSymbol = sTestString.search(rPercentSymbol);
	sTestString = sParseOutCommon(sNumber);
	// scienceExp (integer | float) ("e" | "E") ("+" | "-")? (0-9)+
	var rLittleE = /e/g;
	sTestString = sTestString.replace(rLittleE, g_exponentialSymbol);
	var exponentSymbol = sTestString.search(rExponentSymbol);
	if (exponentSymbol != -1)
	{
		sTestString = sParseNumberExponentCommon(sTestString);
	}
	else
	{
		sTestString = sParseNumberCommon(sTestString);
	}
	if (sTestString != false && percentSymbol != -1)
	{
		sTestString = sDivideBy100(sTestString);
	}
	//remove any trailing decimals
	if (sTestString != false)
	{
		sTestString = sTestString.replace(/\.$/, '');
	}

	return(sTestString);
}

//this function will return a number formatted in the user's locale
//note that a valid parsed number must be supplied as input
function sFormatNumber (sSQLnumber, bCurrency, bShowThousandSeparator)
{

	var sTestString = sSQLnumber.toString();
	var bIsNegative = false;
	var bIsExponent = false;
	var bIsNegativeExponent = false;
	var bIsDecimal = false;
	var sPowerPart = '';
	var sNumberPart = '';
	var sDecimalPart = '';
	var bIsCurrency = false;

	//is this currency
	if (bCurrency == true)
	{
		bIsCurrency = true;
	}

	//is this an exponent?
	var x = sTestString.search(rExponentSymbol);
	if (x != -1)
	{
		bIsExponent=true;
		var arEparts = sTestString.split(rExponentSymbol);
		if (arEparts.length == 2)
		{
			sTestString = arEparts[0];
			sPowerPart = arEparts[1];
			//is the exponent negative?
			var xp = sPowerPart.search(/-/);
			if (xp != -1)
			{
				bIsNegativeExponent = true;
				sPowerPart = sPowerPart.replace('-', '');
			}
		}
		else
		{
			return false;
		}
	}

	//is this a negative number?
	var y = sTestString.search(/-/);
	if (y != -1)
	{
		bIsNegative = true;
		sTestString = sTestString.replace('-', '');
	}

	//is there a decimal portion?
	var w = sTestString.search(/\./);
	if (w != -1)
	{
		bIsDecimal=true;
		var arDecparts = sTestString.split('.');
		if (arDecparts.length == 2)
		{
			sTestString = arDecparts[0];
			sDecimalPart = arDecparts[1];
		}
		else
		{
			return false;
		}
	}

	//add group separators
	if (bShowThousandSeparator == true)
	{
		//add group separators
		var sCurString = sTestString;
		sNumberPart = sAddGroupingSeparators(sCurString);
	}
	else
	{
		sNumberPart = sTestString;
	}
	var sFormatString = '';
	if (bIsNegative == true)
	{
		sFormatString += g_minusSign;
	}
	if (bIsCurrency == true)
	{
		sFormatString += g_currencySymbol;
	}

	sFormatString += sNumberPart;
	if (bIsDecimal == true)
	{
		sFormatString += g_decimalSeparator;
		sFormatString += sDecimalPart;
	}

	if (bIsExponent == true)
	{
		sFormatString += g_exponentialSymbol;
		if (bIsNegativeExponent == true)
		{
			sFormatString += g_minusSign;
		}
		sFormatString += sPowerPart;
	}

	sTestString = sFormatString;

	return sTestString;
}


function sAddGroupingSeparators(s)
{
	var sCurString = s;
	//divide the length by the group size to get the modulus
	if (sCurString.length > g_groupingSize)
	{
		var iRemainder = sCurString.length % g_groupingSize;
		var sFirstPart = sCurString.substring(0, sCurString.length % g_groupingSize);

		var sMeatyPart = sCurString.substring(iRemainder, sCurString.length);

		var arrayMeatyParts = new Array();
		for (var myCount = 0; myCount < sMeatyPart.length; myCount += g_groupingSize)
		{
			 arrayMeatyParts[arrayMeatyParts.length] = sMeatyPart.substring(myCount, myCount + g_groupingSize);
		}

		if ((arrayMeatyParts) && (arrayMeatyParts.length > 0))
		{
				if (sFirstPart)
				{
					sCurString = sFirstPart + g_groupingSeparator;
				}
				else
				{
					sCurString = "";
				}
				for (var thePart=0; thePart < arrayMeatyParts.length - 1; thePart++)
				{

					sCurString += arrayMeatyParts[thePart] + g_groupingSeparator;
				}
				sCurString += arrayMeatyParts[arrayMeatyParts.length - 1];
		}
	}

	return sCurString;
}


//parse integer format
//confirm that the value passed in is a string and return
//a version in canonical format
//this function will need to be revised if support for non-arabic numbers
//becomes a requirement
function sParseInteger(s)
{
	var sTestString = s.replace(rgroupingSeparator, '');
	var bNegative = false;

	if (bIsNegative(sTestString) == true)
	{
		//test for negative integer
		//remove the negative prefix and suffix
		sTestString = sTestString.replace (rMinusSign,'');
		sTestString = sTestString.replace (rNegativePrefix,'');
		sTestString = sTestString.replace (rNegativeSuffix,'');
		sTestString = sTestString.replace(/[\(\)]/g,'');
		bNegative = true;
	}
	else
	{
		//remove the positive prefix and suffix
		sTestString = sTestString.replace (rPositivePrefix,'');
		sTestString = sTestString.replace (rPositiveSuffix,'');
		sTestString = sTestString.replace (rPlusSign,'');
	}

	//test to see if there are any numbers
	if (bNumbersOnly(sTestString) == true)
	{
		if (bNegative == true)
		{
			sTestString = "-" + sTestString;
		}
		return sTestString;
	}
	else
	{
		return false;
	}
}

//Natural Numbers: test a string to see if it is a non negative integers
//This is the set of positive numbers including 0
function sParseNaturalNumber(s)
{
	//remove any grouping separators
	var sTestString = s.replace(rgroupingSeparator, '');
	//remove the positive prefix and suffix
	sTestString = sTestString.replace (rPositivePrefix,'');
	sTestString = sTestString.replace (rPositiveSuffix,'');
	sTestString = sTestString.replace (rPlusSign,'');

	//test to see if there are any numbers
	if (bNumbersOnly(sTestString) == true)
	{
		return sTestString;
	}
	else
	{
		return false;
	}
}

//Whole npositive integers
//This is the set of positive numbers excluding 0
function sParseWholeNumber(s)
{
	var sTestString = sParseNaturalNumber(s).toString();
	if ((sTestString == 'false') || (sTestString == '0'))
	{
		return false;
	}
	else
	{
		return sTestString;
	}
}

//test to see if the string is a negative number
function bIsNegative(s)
{

	//strip any white space
	var sTestString = sStripWhitespace(s);

	//is this a positive or negative value?
	//check the locale specific info and the financial negative (123)
	if ((sTestString.search(rMinusSign) != -1) || ((sTestString.search(rNegativeSuffix) != -1) && (sTestString.search(rNegativePrefix) != -1)) || (sTestString.match(rFinancialNegative) != null))
	{
		return true;
	}
	else
	{
		return false;
	}
}

//this function will return an integer formatted in the user's locale
//note that a valid parsed number must be supplied as input
function sFormatInteger (s)
{
	var sTestString = s.toString();
	if (sTestString == 'false')
	{
		return false;
	}
	if (sTestString == '')
	{
		return sTestString;
	}

	//remove separators for integers
	sTestString = sTestString.replace(rgroupingSeparator, '');

	var bNegative = false;

	var rMinusSign = new RegExp('-', 'g'); //non numerals
	var z = sTestString.search(rMinusSign);
	if (z != -1)
	{
		bNegative = true;
		sTestString = sTestString.replace(rMinusSign, '');
	}

	//add negative and positive formatting
	//based on locale
	if (bNegative == true)
	{
		sTestString = g_negativePrefix + sTestString;
		sTestString = sTestString + g_negativeSuffix;
	}
	else
	{
		sTestString = g_positivePrefix + sTestString;
		sTestString = sTestString + g_positiveSuffix;
	}

	//return the formatted value
	return sTestString;
}


//parse currency
//prefix part, number part and a suffix part
function sParseCurrency(s)
{
	//declare variables and regular expressions
	var bIsNegative = false;

	var rCurrencyMinusSign = new RegExp(sEscapeRegularExpression(g_currencyMinusSign), 'g');
	var rCurrencyNegativePrefix = new RegExp(sEscapeRegularExpression('^' + g_currencyNegativePrefix));
	var rCurrencyNegativeSuffix = new RegExp(sEscapeRegularExpression(g_currencyNegativeSuffix), 'g');
	var rCurrencyPositivePrefix = new RegExp(sEscapeRegularExpression('^' + g_currencyPositivePrefix));
	var rCurrencyPositiveSuffix = new RegExp(sEscapeRegularExpression(g_currencyPositiveSuffix), 'g');
	var rCurrencyGroupingSeparator = new RegExp(sEscapeRegularExpression(g_currencyGroupingSeparator, 'g'));
	var rMonetaryDecimalSeparator = new RegExp(sEscapeRegularExpression(g_monetaryDecimalSeparator));

	//strip any white space
	var sTestString = sStripWhitespace(s);

	//is this a positive or negative value?
	//check the locale specific info and the financial negative (123)
	if ((sTestString.search(rCurrencyMinusSign) != -1) || ((sTestString.search(rCurrencyNegativePrefix) != -1) && (sTestString.search(rCurrencyNegativeSuffix) != -1)) || (sTestString.match(rFinancialNegative) != null))
	{
		bIsNegative = true;
	}

	//strip prefix, suffix (including the monetary symbol)
	sTestString =  sTestString.replace(rCurrencySymbol,'');
	sTestString =  sTestString.replace(rCurrencyMinusSign,'');
	sTestString =  sTestString.replace(rCurrencyNegativePrefix,'');
	sTestString =  sTestString.replace(rCurrencyNegativeSuffix,'');
	sTestString =  sTestString.replace(rCurrencyPositivePrefix,'');
	sTestString =  sTestString.replace(rCurrencyPositiveSuffix,'');
	sTestString =  sTestString.replace(/[\(\)]/g,'');

	//remove grouping separators
	sTestString =  sTestString.replace(rCurrencyGroupingSeparator,'');

	//split on the decimal symbol and confirm that these are numbers
	var arTestTokens = sTestString.split(rMonetaryDecimalSeparator);
	var m = 0;
	for (m=0; m < arTestTokens.length; m++)
	{
		if (bNumbersOnly(arTestTokens[m]) == false)
		{
			//this is not a valid currency
			return false;
		}
	}

	//reconstruct the string as a currency datatype
	var parsedCurrencyString = "";
	if (bIsNegative == true)
	{
		parsedCurrencyString += "-";
	}
	if ((arTestTokens.length == 1) && (sTestString.indexOf(g_monetaryDecimalSeparator) == 0))
	{
		parsedCurrencyString += "0." + arTestTokens[0];
	}
	else
	{
		parsedCurrencyString += arTestTokens[0];
		if (arTestTokens.length > 1)
		{
			//add the decimal
			parsedCurrencyString += ".";
			parsedCurrencyString += arTestTokens[1];
		}
	}
	return parsedCurrencyString;
}

//this function will return an integer formatted in the user's locale
//note that a valid parsed number must be supplied as input
function sFormatCurrency (s, bShowThousandSeparator)
{
	var sTestString = s.toString();

	if (sTestString == 'false')
	{
		return false;
	}

	if (sTestString == '')
	{
		return sTestString;
	}

	var bNegative = false;

	var rMinusSign = new RegExp('-', 'g'); //non numerals
	var z = sTestString.search(rMinusSign);
	if (z != -1)
	{
		bNegative = true;
		sTestString = sTestString.replace(rMinusSign, '');
	}

	//split on the decimal
	var bDecimalOnly = false;
	var arParts = sTestString.split(".");
	var returnString = "";

	if (bShowThousandSeparator == true)
	{
		//get the grouping format
		arParts[0] = sAddGroupingSeparators(arParts[0]);
	}
	if (arParts.length > 1)
	{
		returnString = arParts[0] + g_currencyDecimalSeparator + arParts[1];
	}
	else
	{
		returnString = arParts[0];
	}

	//add negative and positive formatting
	//based on locale
	if (bNegative == true)
	{
		returnString = g_currencyNegativePrefix + returnString;
		returnString = returnString + g_currencyNegativeSuffix;
	}
	else
	{
		returnString = g_currencyPositivePrefix + returnString;
		returnString = returnString + g_currencyPositiveSuffix;
	}

	//return the formatted value
	return returnString;
}

function sParsePercentNumber(sNumber)
{
	var sTestString = sParseOutCommon(sNumber);
	sTestString = sParseNumberCommon(sTestString);
	if (sTestString != false)
	{
		sTestString = sDivideBy100(sTestString);
	}
	return(sTestString);
}


function sParseOutCommon(sNumber)
{
	var sTestString = sNumber;

	// strip out the following
	// currency unit
	sTestString = sTestString.replace(rCurrencySymbol, '');

	// group separator
	sTestString = sTestString.replace(rgroupingSeparator, '');

	// remove whitespace
	sTestString = sStripWhitespace(sTestString);

	// is there a decimal point, replace with SQL format
	sTestString = sTestString.replace(rdecimalSeparator, '.');

	// remove the percent symbol
	var percentSymbol = sTestString.search(rPercentSymbol);
	if (percentSymbol != -1)
	{
		sTestString = sTestString.replace(rPercentSymbol, '');
	}

	return(sTestString);
}

function sParseNumberExponentCommon(sTestString)
{
	// split on the exponent symbol into number (mantissa) and power
	var arExponentParts = sTestString.split(g_exponentialSymbol);
	if (arExponentParts.length == 2)
	{
		// convert to correct format
		var numberPart = arExponentParts[0];
		var bNumberPartNegative = false;

		if ((numberPart.search(rMinusSign) != -1) ||
		    (numberPart.search(rNegativePrefix) != -1))
		{
			bNumberPartNegative = true;
			numberPart = numberPart.replace(rMinusSign, '');
			numberPart = numberPart.replace(rNegativePrefix, '');
		}
		if (numberPart.length == 0)
		{
			return(false);
		}

		// validate number part
		var arNumberPartTest = numberPart.split('.');
		if (arNumberPartTest.length < 3)
		{
			for (var sIndex = 0; sIndex < arNumberPartTest.length; sIndex++)
			{
				if (bNumbersOnly(arNumberPartTest[sIndex]) == false)
				{
					return(false);
				}
			}
		}

		// exponent part
		var powerPart = arExponentParts[1];
		if (powerPart.length == 0)
		{
			return(false);
		}

		// strip out the positive symbol
		powerPart = powerPart.replace(rPlusSign, '');

		// determine if the exponent is negative
		var bPowerPartNegative = false;
		if ((powerPart.search(rMinusSign) != -1) ||
		    (powerPart.search(rNegativePrefix) != -1))
		{
			bPowerPartNegative = true;
			powerPart = powerPart.replace(rMinusSign, '');
			powerPart = powerPart.replace(rNegativePrefix, '');
		}

		var sPowerPartTest = bNumbersOnly(powerPart);
		if (!sPowerPartTest)
		{
			return(false);
		}

		var sNewExponent = '';
		if (bNumberPartNegative)
		{
			sNewExponent += '-';
		}
		sNewExponent += numberPart;
		sNewExponent += 'E';
		if (bPowerPartNegative)
		{
			sNewExponent += '-';
		}
		sNewExponent += powerPart;
		sTestString = sNewExponent;
	}
	else
	{
		return(false);
	}
	return(sTestString);
}

function sParseNumberCommon(sTestString)
{
	var bIsNegative = false;
	// is the number positive or negative
	// test for negative numbers
	if ((sTestString.match(rFinancialNegative) != null) ||
	    (sTestString.search(rMinusSign) != -1) ||
		(sTestString.search(rNegativePrefix) != -1))
	{
		bIsNegative = true;
		sTestString = sTestString.replace(rMinusSign, '');
		sTestString = sTestString.replace(rNegativePrefix, '');
		// remove financial negative
		sTestString = sTestString.replace(/[\(\)]/g,'');
		if (sTestString.length == 0)
		{
			return(false);
		}
	}

	// does the string start with a decimal
	var bDecimalOnly = false;
	if (sTestString.indexOf('.') == 0)
	{
		bDecimalOnly = true;
	}

	var arNewNumber = sTestString.split('.');
	var numberPart = '';
	var decimalPart = '';
	var sFormattedNumber = '';

	if (arNewNumber.length == 2)
	{
		numberPart = arNewNumber[0];
		decimalPart = arNewNumber[1];

		if (!bNumbersOnly(numberPart) || !bNumbersOnly(decimalPart))
		{
			// this is not a valid number
			return(false);
		}
		if (bIsNegative)
		{
			sFormattedNumber += '-';
		}
		if (bDecimalOnly)
		{
			sFormattedNumber += '0.';
		}
		else
		{
			sFormattedNumber += numberPart;
			sFormattedNumber += '.';
		}
		sFormattedNumber += decimalPart;
		sTestString = sFormattedNumber;
	}
	else if (arNewNumber.length == 1)
	{
		numberPart = arNewNumber[0];
		if (!bNumbersOnly(numberPart))
		{
			// this is not a valid number
			return(false);
		}
		if (bIsNegative)
		{
			sFormattedNumber += '-';
		}
		if (bDecimalOnly)
		{
			sFormattedNumber += '0.';
		}
		sFormattedNumber += numberPart;
		sTestString = sFormattedNumber;
	}
	else
	{
		return(false);
	}
	return(sTestString);
}

function sFormatPercentNumber(sSQLnumber)
{
	// change from decimal to a percentage number
	var sTestString = sSQLnumber.toString();
	sTestString = sMultiplyBy100(sTestString);
	if (g_decimalSeparator != '.')
	{
		// put back the original decimal separator so we don't lose the locale specific formatting
		rResetDecimalSeparator = new RegExp(sEscapeRegularExpression('.'), 'g');
		sTestString = sTestString.replace(rResetDecimalSeparator, g_decimalSeparator);
	}
	var x = sTestString.search(rPercentSymbol);
	if (x == -1 && sTestString.length > 0)
	{
		sTestString += g_percentSymbol;
	}
	if (sTestString == "NaN%")
	{
		sTestString = "";
	}
	return(sTestString);
}

//remove any white space from the
//data
function sStripWhitespace(s)
{
	var rWhiteSpace = /[\s]/g; //whitespace characters [\t\n\r\f\v]
	s = s.replace(rWhiteSpace,'');
	return s;
}

//set up regular expressions with locale specific
//values.  These are loaded at runtime.
function initParsingRegularExpressions()
{
	//define regular expressions for string parsing
	rCurrencySymbol = new RegExp(sEscapeRegularExpression(g_currencySymbol), 'g');
	rgroupingSeparator = new RegExp(sEscapeRegularExpression(g_groupingSeparator), 'g');
	rdecimalSeparator = new RegExp(sEscapeRegularExpression(g_decimalSeparator), 'g');
	rMinusSign = new RegExp(sEscapeRegularExpression(g_minusSign), 'g');
	rPlusSign = new RegExp(sEscapeRegularExpression(g_plusSign), 'g');
	rNegativePrefix = new RegExp(sEscapeRegularExpression(g_negativePrefix), 'g');
	rNegativeSuffix = new RegExp(sEscapeRegularExpression(g_negativeSuffix), 'g');
	rPositivePrefix = new RegExp(sEscapeRegularExpression(g_positivePrefix), 'g');
	rPositiveSuffix = new RegExp(sEscapeRegularExpression(g_positiveSuffix), 'g');
	rPercentSymbol = new RegExp(sEscapeRegularExpression(g_percentSymbol), 'g');
	rExponentSymbol = new RegExp(sEscapeRegularExpression(g_exponentialSymbol), 'gi');

}

//this function can be called to return a formatted
//value given a particular input type
function getFormatByDataType(sValue, sDataType, bIsCurrency, bShowThousandSeparator)
{
		switch (sDataType)
		{
			case 'number':
				return sFormatNumber(sValue, bIsCurrency, bShowThousandSeparator);
				break;
			case 'currency':
				return sFormatCurrency(sValue, bShowThousandSeparator);
				break;
			case 'integer':
				return sFormatInteger(sValue);
				break;
			case 'natural':
				return sFormatInteger(sValue);
				break;
			case 'whole':
				return sFormatInteger(sValue);
				break;
			case 'percentage':
				return sFormatPercentNumber(sValue);
				break;
			default:
				return sValue;
				break;
		}
}

//convert from the database format to one that is locale based
function convertSQLToLocale(sDefaultValue, sDataType)
{
	var sTestString = sDefaultValue;
	if (sTestString)
	{
		if ((sDataType == 'number') ||(sDataType == 'currency') ||(sDataType == 'integer'))
		{
			sTestString = sTestString.replace(/\./g, g_decimalSeparator);
			sTestString = sTestString.replace(rgroupingSeparator, '');
			var z = sTestString.search("-");
			var e = sTestString.search("E");
			if (z != -1 && e == -1)
			{
				sTestString = g_negativePrefix + sTestString + g_negativeSuffix;
			}

		}
	}
	return sTestString;
}

function sDivideBy100(sNumber)
{
	var bIsNegative = false;
	if (sNumber == "0" || sNumber == "")
	{
		return(sNumber);
	}
	// since this is done after all the formatting there is a chance that a
	// negative sign could be in here somewhere
	if ((sNumber.match(rFinancialNegative) != null) ||
	    (sNumber.search(rMinusSign) != -1) ||
		(sNumber.search(rNegativePrefix) != -1))
	{
		bIsNegative = true;
		sNumber = sNumber.replace(rMinusSign, '');
		sNumber = sNumber.replace(rNegativePrefix);
		// remove financial negative
		sNumber = sNumber.replace(/[\(\)]/g, '');
		if (sNumber.length == 0)
		{
			return(false);
		}
	}
	var decimalLocation = sNumber.indexOf(".");
	var sNewNum;
	// if there is no decimal
	if (decimalLocation == -1)
	{
		sNewNum = sMoveDecimalLeftByTwo(sNumber);
	}
	else
	{
		var arNewNumber = sNumber.split(".");
		if (arNewNumber.length > 2)
		{
			return(false); // not a valid decimal number
		}
		else
		{
			var numPart = arNewNumber[0];
			var decPart = arNewNumber[1];
			numPart = sMoveDecimalLeftByTwo(numPart);
			sNewNum = numPart + decPart;
		}
	}
	if (bIsNegative)
	{
		sNewNum = "-" + sNewNum;
	}
	return(sNewNum);
}

function sMoveDecimalLeftByTwo(sNumber)
{
	var numLength = sNumber.length;
	var sNewNum;
	// split the string 2 characters from the end, put a "." in and then stick the end back on
	if (numLength > 2)
	{
		sNewNum = sNumber.substr(0, numLength - 2);
		sNewNum += ".";
		sNewNum += sNumber.substr(numLength - 2);
	}
	else if (numLength == 2)
	{
		sNewNum = "0." + sNumber;
	}
	else
	{
		sNewNum = "0.0" + sNumber;
	}
	return(sNewNum);
}

function sMultiplyBy100(sNumber)
{
	if (sNumber == "0" || sNumber == "")
	{
		return(sNumber);
	}
	var decimalLocation = sNumber.indexOf(".");
	var sNewNum;
	var bIsNegative = false;
	if ((sNumber.match(rFinancialNegative) != null) ||
	    (sNumber.search(rMinusSign) != -1) ||
		(sNumber.search(rNegativePrefix) != -1))
	{
		bIsNegative = true;
		sNumber = sNumber.replace(rMinusSign, '');
		sNumber = sNumber.replace(rNegativePrefix);
		// remove financial negative
		sNumber = sNumber.replace(/[\(\)]/g, '');
		if (sNumber.length == 0)
		{
			return(false);
		}
	}
	// if there is no decimal
	if (decimalLocation == -1)
	{
		sNewNum = sNumber + "00";
	}
	else
	{
		var arNewNumber = sNumber.split(".");
		if (arNewNumber.length > 2)
		{
			return(false); // not a valid decimal number
		}
		else
		{
			var numPart = arNewNumber[0];
			var decPart = arNewNumber[1];
			if (decPart.length == 2)
			{
				// ex. 12.21
				sNewNum = numPart + decPart;
			}
			else if (decPart.length > 2)
			{
				// ex. 12.2121
				sNewNum = numPart + decPart.substr(0, 2);
				// ex. 0.0095
				sNewNum += "." + decPart.substr(2);
			}
			else
			{
				// ex. 12.2
				sNewNum = numPart + decPart + "0";
			}
		}
	}
	// parse out leading zeroes
	var zeroCounter = 0;
	for (var i = 0; i < sNewNum.length; i++)
	{
		if (sNewNum.charAt(i) == "0")
		{
			zeroCounter++;
		}
		else
		{
			break;
		}
	}
	sNewNum = sNewNum.substr(zeroCounter);
	if (sNewNum.length == 0)
	{
		sNewNum = "0";
	}
	// if the string now starts with "." then prepend it with "0"
	if (sNewNum.charAt(0) == "." && sNewNum.length > 1)
	{
		sNewNum = "0" + sNewNum;
	}
	if (bIsNegative)
	{
		sNewNum = "-" + sNewNum;
	}
	return(sNewNum);
}

function bIsZero(sNumber)
{
	if (sNumber == "")
	{
		return(false);
	}
	var fNumber = parseFloat(sNumber);
	if (fNumber == NaN)
	{
		return(false);
	}
	else if (fNumber == 0)
	{
		return(true);
	}
	return(false);
}

/* Constants */

//
//Numeric Formatting
//
var g_decimalSeparator = ".";
var g_groupingSeparator = ",";
var g_groupingSize = 3;
var g_secondaryGroupingSize = 0;
var g_maximumIntegerDigits = 309;
var g_minimumIntegerDigits = 1;
var g_maximumFractionDigits = 3;
var g_minimumFractionDigits = 0;
var g_positivePrefix = "";
var g_positiveSuffix = "";
var g_negativePrefix = "-";
var g_negativeSuffix = "";
var g_minusSign = "-";
var g_plusSign = "+";
var g_zeroDigit = "0";
var g_listSeparator = ",";
var g_negativeLocation = "left";
var g_positiveLocation = "none";

//
// Currency Formatting
//
var g_currencySymbol = "$";
var g_monetaryDecimalSeparator = ".";
var g_currencyLocation = "left";
var g_currencyDecimalSeparator = ".";
var g_currencyGroupingSeparator = ",";
var g_currencyGroupingSize = 3;
var g_currencySecondaryGroupingSize = 0;
var g_currencyPositivePrefix = "$";
var g_currencyPositiveSuffix = "";
var g_currencyNegativePrefix = "-$";
var g_currencyNegativeSuffix = "";
var g_currencyMinusSign = "-";
var g_currencyPlusSign = "+";
var g_currencyZeroDigit = "0";
var g_currencyNegativeLocation = "left";
var g_currencyPositiveLocation = "none";

//
// Percentage Formatting
//
var g_percentSymbol = "%";
var g_percentLocation = "right";
var g_percentageDecimalSeparator = ".";
var g_percentageGroupingSeparator = ",";
var g_percentageGroupingSize = 3;
var g_percentageSecondaryGroupingSize = 0;
var g_percentagePositivePrefix = "";
var g_percentagePositiveSuffix = "%";
var g_percentageNegativePrefix = "-";
var g_percentageNegativeSuffix = "%";
var g_percentageMinusSign = "-";
var g_percentagePlusSign = "+";
var g_percentageZeroDigit = "0";
var g_percentageNegativeLocation = "both";
var g_percentagePositiveLocation = "right";

//
// Scientific Formatting
//
var g_exponentialSymbol = "E";
var g_scientificDecimalSeparator = ".";
var g_scientificGroupingSeparator = ",";
var g_scientificGroupingSize = 0;
var g_scientificSecondaryGroupingSize = 0;
var g_scientificPositivePrefix = "";
var g_scientificPositiveSuffix = "";
var g_scientificNegativePrefix = "-";
var g_scientificNegativeSuffix = "";
var g_scientificMinusSign = "-";
var g_pscientificPlusSign = "+";
var g_scientificZeroDigit = "0";
var g_scientificNegativeLocation = "left";
var g_scientificPositiveLocation = "none";


//define regular expressions for string parsing
//some of the values may be regular expression syntax
var rCurrencySymbol = new RegExp(sEscapeRegularExpression(g_currencySymbol), 'g');
var rgroupingSeparator = new RegExp(sEscapeRegularExpression(g_groupingSeparator), 'g');
var rdecimalSeparator = new RegExp(sEscapeRegularExpression(g_decimalSeparator), 'g');
var rMinusSign = new RegExp(sEscapeRegularExpression(g_minusSign), 'g');
var	rPlusSign = new RegExp(sEscapeRegularExpression(g_plusSign), 'g');
var rNegativePrefix = new RegExp(sEscapeRegularExpression(g_negativePrefix), 'g');
var rNegativeSuffix = new RegExp(sEscapeRegularExpression(g_negativeSuffix), 'g');
var rPositivePrefix = new RegExp(sEscapeRegularExpression(g_positivePrefix), 'g');
var rPositiveSuffix = new RegExp(sEscapeRegularExpression(g_positiveSuffix), 'g');
var rPercentSymbol = new RegExp(sEscapeRegularExpression(g_percentSymbol), 'g');
var rExponentSymbol = new RegExp(sEscapeRegularExpression(g_exponentialSymbol), 'gi');
//test for financial negative (13.44)
var rFinancialNegative = new RegExp(/^\([^)]*\)$/);

