var exp,val,pWin,cWin,symbolIn,vs,foo;
try{

function FOO( textArea, valElement )
{
	this.tokens = [];
	this.offsets = [];
	this.nTokens = 0;
	this.pos = 0;
	this.caretPos;
	this.textArea = textArea;
	this.valElement = valElement;
	this.externalExpr = "";
	this.internalExpr = "";
	this.valid = false;
	this.invalidSymbol = "??";
	this.literals = ["sin","cos","tan","asin","acos","atan","ln","exp","random","concat",
					"abs","sqrt","sgn","round","floor","ceil","max","min","select","rgb","red","green","blue",
//					"len","area","slope","radius","dia","circum","peri","coorx","coory",
					"radians","degrees","pixels","cm","inches","true","false","pi","e","x"];
	
	this.insert = function( el )
	{
		var i;
		for ( i=this.nTokens-1; i>=this.pos; i-- )
			this.tokens[i+1] = this.tokens[i];
		this.tokens[this.pos++] = el;
		this.nTokens++;
//opener.__debug( el + " " + this.nTokens + " " + this.pos + "\n" );
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
	this.replace = function( el )
	{
		this.tokens[this.pos-1] = el;
//opener.__debug( el + " " + this.nTokens + " " + this.pos + "\n" );
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
	this.del = function()
	{
		if ( this.pos == this.nTokens )
			return;
		for ( var i=this.pos; i<this.nTokens-1; i++ )
			this.tokens[i] = this.tokens[i+1];
		this.nTokens--;
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
	this.backspace = function()
	{
		if ( this.pos == 0 )
			return;
		for ( var i=this.pos-1; i<this.nTokens-1; i++ )
			this.tokens[i] = this.tokens[i+1];
		this.nTokens--;
		this.pos--;
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
	this.left = function()
	{
		if ( this.pos == 0 )
			return;
		this.pos--;
		this.setCaretPosition();
	};
	this.right = function()
	{
		if ( this.pos == foo.nTokens )
			return;
		this.pos++;
		this.setCaretPosition();
	}
	this.getInternalExpr = function()
	{
		return this.internalExpr;
	};
	this.toString = function()
	{
		return this.getInternalExpr();
	};
	this.getExternalExpr = function()
	{
		return this.externalExpr;
	};

	// this method will cause problem if the calculator is set to display:none
	this.setCaretPosition = function()
	{
		var ctrl = this.textArea, pos = this.offsets[this.pos];
//		window.focus();
		if(	ctrl.setSelectionRange )
		{
//			ctrl.focus();
			ctrl.setSelectionRange( pos, pos );
		}
		else if (ctrl.createTextRange) 
		{
//			ctrl.focus();
			var range = ctrl.createTextRange();
			range.collapse( true );
			range.moveEnd( 'character', pos );
			range.moveStart( 'character', pos );
			range.select();
		}
	};
	this.getCaretPosition = function() 
	{
		var t = this.textArea, bookmark = "~", orig = t.value, tmpText, i;
		if (document.selection) // IE hack
		{	
			this.saveCaret();
			this.range.text = bookmark;
			// new line characters are two characters in IE and the cursor was 1 position off for each line
			tmpText = t.value.replace(new RegExp(/\n/g), '');
			
		    i = tmpText.search( bookmark );
		    t.value = orig;
			return i;
		}
		else if ( t.selectionStart || t.selectionStart == '0' ) // Firefox
		{
			return t.selectionStart;
		}
	};
	this.saveCaret = function()
	{
		this.range = document.selection.createRange();
	};
	this.adjustPos = function( caretPos )
	{
		for ( var i=0; i<this.nTokens; i++ )
			if ( caretPos < this.offsets[i+1] )
			{
				this.pos = i;
				break;
			}
		if ( i == this.nTokens )
			this.pos = this.nTokens;
		this.setCaretPosition();
	};
	this.clear = function()
	{
		this.textArea.value = "";
		this.nTokens = 0;
		this.pos = 0;
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
	this.updateTextArea = function()
	{
		var t;
		this.internalExpr = "";
		this.externalExpr = "";
		for ( var i=0; i<this.nTokens; i++ )
		{
			this.offsets[i] = this.externalExpr.length;
			t = this.tokens[i];
			this.internalExpr += t;
			// need to test more charaters such as operators +/; use regular expression
			// need to update parseSrc2 in Expr
			//need to test function first before testing the following condition
			//if ( /\+|-|\*|\/| /.test( t ) && t.length != 1 && t.charAt(t.length-1) != ")" )
			if ( needFence( t ) )
//				this.externalExpr += "("+t+")";
				this.externalExpr += "["+t+"]";
			else
				this.externalExpr += t;
		}
		this.offsets[this.nTokens] = this.externalExpr.length;
		this.textArea.value = this.externalExpr;
//opener.__debug( "updateTextArea: " + this.internalExpr + " " + this.textArea.value + "\n" );
		this.setCaretPosition();
	};
	this.evaluate = function( symbolIn )
	{
		var t, t1, t2;
		this.valid = true;
try{
		if ( this.internalExpr == "" )
		{
			t1 = "";
		}
		else
		{
			lxgEP.setExpr( this.internalExpr );
			lxgEP.findTokens();
			lxgEP.infixToPostfix();
			lxgEP.evaluate();
			t1 = (symbolIn!="")?this.externalExpr:lxgEP.result.toString1(2);
		}
		this.valid = true;
}catch(err)
{	
		this.valid = false;
		document.getElementById( "calStat" ).firstChild.nodeValue = err+":"+FOO.errors[err];
}
		//if ( symbolIn == "" )
		//	this.valElement.firstChild.nodeValue ="= "+ ( t?lxgEP.result.toString1(2):this.invalidSymbol );
		if ( t1 == "" )
			this.valElement.firstChild.nodeValue = "";
		else
			this.valElement.firstChild.nodeValue ="= "+ ( this.valid?t1:this.invalidSymbol );

try{
		if ( this.valid )
		{
			t2 = document.getElementById( "mathml" );
			while( t2.lastChild )
				t2.removeChild( t2.lastChild );
			if ( t1 != "" )
				t2.appendChild( lxgEP.postfixToMathML( document ) );
			document.getElementById( "calStat" ).firstChild.nodeValue = "";
		}
}catch(e)
{alert("call postfixToMathML " + e.message + " " + e.name + " " + e.lineNumber + " " + e.fileName + " " + e.stack );}
		return t;
	}
	this.init = function( exp, symbolIn )
	{
		var re, i, j, t;
		this.nTokens = 0;
try{
		lxgEP.setExpr( exp );
		lxgEP.findTokens();			// doesn't throw error until all tokens are found
		lxgEP.infixToPostfix();
		lxgEP.evaluate();
}catch(e)		// let the later evaluate to catch
{
//alert(e);
}

		for ( i=0; i<lxgEP.nTokens; i++ )		// tokens in infix order
		{
			re = /^([-+]?\d*\.?\d+)(cm|inches|pixels|degrees|radians)?$/;
			if ( re.test(lxgEP.tokens[i].toLowerCase()) )
			{
				t = RegExp.$1;
//alert( RegExp.$1 + " " + RegExp.$2 + " " + t.length );
				for ( j=0; j<t.length; j++ )
					this.tokens[this.nTokens++] = t.charAt(j);
				if ( RegExp.$2 != "" )
					this.tokens[this.nTokens++] = RegExp.$2;
			}
			else
				this.tokens[this.nTokens++] = lxgEP.tokens[i];
		}
		this.pos = this.nTokens;
		this.updateTextArea();
		this.evaluate( symbolIn );
	};
};
FOO.errors = {
	"1":	'Not enough operand for an operator or a function',
	"2":	'Not enough operand for an operator or a function',
	"3":	'Operand is of a wrong type',
	"4":	'Too many operands for a function',
	"21":	'An operand with angle unit (degrees or radians) is expected',
	"22":	'No operand for function max/min',
	"23":	'Function pick requires last two operands to be the same type',
	"5":	'Unrecognized literal',
	"6":	'Nested function call error',
	"7":	'Object cannot be measured in this way',
	"8":	'Measurement function needed to apply to a geometric object',
	"9":	'Expression is empty',
	"10":	'Circular dependency',
	"101":	'Unbalanced parentheses',
	"102":	'"==" is needed instead of a single "=" to check equality',
	"201":	'"(" missing',
	"202":	'Illegal object reference',
	"203":	'Object does NOT have an associated Result',
	"204":	'Unrecognized literal'
};
	


function initCalculator()
{
try{
	val = document.getElementById( "calVal" );
	exp = document.getElementById( "calExp" );
	exp.value = "";
	cWin = window;
	foo = new FOO( exp, val );
/*	
	if ( dlgObj.openType == mainWin.LX_WM.OPENBYDIALOG  )
	{
		pWin = dlgObj.dlgOpener.win;
		vs = pWin.VS.getCurVS();
		val.firstChild.nodeValue = vs.value;
		symbolIn = vs.symbol;
		foo.init( vs.expression, symbolIn );
	}
	else
	{
		symbolIn = (dlgObj.openSubType==1)?"x":"";
	}
	if ( symbolIn != "" )
	{
		lxgEP.setVarAllowed( true );
		lxgEP.addVariable( symbolIn );
	}
	else
		lxgEP.setVarAllowed( false );

*/
	symbolIn = false;	
	
	if ( exp.addEventListener )
	{
		exp.addEventListener( "keypress", filterInput, false );
		exp.addEventListener( "click", textAreaClickHandler, false );
		exp.addEventListener( "select", textAreaClickHandler, false );
		document.getElementById( "functions" ).addEventListener( "change", selectHandler, false );
		document.getElementById( "units" ).addEventListener( "change", selectHandler, false );
		document.getElementById( "values" ).addEventListener( "change", selectHandler, false );
	}
	else
	{
		exp.attachEvent( "onkeydown", filterInput );
		exp.attachEvent( "onkeypress", filterInput );
		exp.attachEvent( "onclick", textAreaClickHandler );
		exp.attachEvent( "onselect", textAreaClickHandler );
		document.getElementById( "functions" ).attachEvent( "onchange", selectHandler );
		document.getElementById( "units" ).attachEvent( "onchange", selectHandler );
		document.getElementById( "values" ).attachEvent( "onchange", selectHandler );
	}
//	exp.focus();
}catch(e)
{alert("dlgCalculator " + e.message + " " + e.name + " " + e.lineNumber + " " + e.fileName + " " + e.stack );}
}



function selectHandler( evt )
{
	if ( !evt )
		var evt = window.event;
//opener.__debug( evt + "\n" )
	var t = evt.target || evt.srcElement;
//opener.__debug( "selectHandler: " + t.id + " " + t.selectedIndex );
	if ( t.selectedIndex == 0 )
		return;
	foo.insert( t.options[t.selectedIndex].value );
	t.selectedIndex = 0;
}

function insert( t )
{
	foo.insert( t );
}

function textAreaClickHandler( evt )
{
	foo.adjustPos( foo.getCaretPosition() );
}

var KEY_LEFT = 500;
var KEY_RIGHT = 501;
var KEY_DELETE = 502;
var KEY_BACKSPACE = 503;


var fun = "";
var lastOp = "done";

function filterInput( evt )
{
	if ( !evt )
		var evt = window.event;
//opener.__debug( evt + "\n" )
	var t = evt.target || evt.srcElement, t1, t2;

//opener.__debug( "type=" + evt.type + " which=" + evt.which + " charCode=" + evt.charCode + " keyCode=" + evt.keyCode + "\n" );

	var code;
	
	if ( evt.which != null && evt.which != "undefined" )	// FF
	{
		switch( evt.keyCode )
		{
			case 0: code = evt.which; // normal charaters
					break;
			case 13: code = evt.which;	// both which and keyCode == 13
					break;
			case 37: code = KEY_LEFT;
					break;
			case 39: code = KEY_RIGHT;
					break;
			case 46: code = KEY_DELETE;
					break;
			case 8: code = KEY_BACKSPACE;
					break;
			default:
					break;
		}
	}
	else
	{
//opener.__debug( "IE key event: " );
		if ( evt.type == "keydown" )
		{
			switch( evt.keyCode )
			{
				case 8: code = KEY_BACKSPACE;
						break;
				case 37: code = KEY_LEFT;
						break;
				case 39: code = KEY_RIGHT;
						break;
				case 46: code = KEY_DELETE;
						break;
				default:
						return true;	// return false will stop the keypress event from happening in IE
			}
		}
		else
			code = evt.keyCode;
	}

//opener.__debug( code + " " );

	var i, n, s = "abcdefghijklmnopqrstuvwxyz", t1, t2;

	switch( code )
	{
		case KEY_BACKSPACE:
			fun = "";
			lastOp = "done";
			foo.backspace();
			break;
		case KEY_DELETE:
			fun = "";
			lastOp = "done";
			foo.del();
			break;
		case KEY_RIGHT:
			fun = "";
			lastOp = "done";
			foo.right();
			break;
		case KEY_LEFT:
			fun = "";
			lastOp = "done";
			foo.left();
			break;
		case 8:
			fun = "";
			lastOp = "done";
			break;
		default:
			var ch = String.fromCharCode(code);
			switch ( ch )
			{
				case "+":
				case "-":
				case "*":
				case "/":
				case "%":
				case "^":
				case "&":
				case "|":
				case "!":
//				case "~":
				case "=":
				case ">":
				case "<":
				case ".":
				case ",":
				case "(":
				case ")":
				case "0":
				case "1":
				case "2":
				case "3":
				case "4":
				case "5":
				case "6":
				case "7":
				case "8":
				case "9":
					fun = "";
					lastOp = "done";
					foo.insert( ch );
					break;

				default:
					ch = ch.toLowerCase();
					if ( s.indexOf(ch) != -1 )
					{
						fun += ch;				
						n = fun.length;

						while ( true )
						{
							if ( fun.length == 0 )
								break;

							t1 = 0;
							for ( i=0; i<foo.literals.length; i++ )
								if ( foo.literals[i].substr( 0, n )	== fun )
								{
									t1 ++;
									if ( t1 == 1 || t2.length > foo.literals[i].length )
										t2 = foo.literals[i];
								}
//alert( lastOp + " " + fun + " " + t1 + " " + t2 );		
							if ( t1 == 0 )
							{
								if ( lastOp == "insert" || lastOp == "replace" )
									fun = ch;
								else
									fun = fun.substr(1);
								n = fun.length;
								lastOp = "done";
							}
							else
							{
								if ( lastOp == "insert" || lastOp == "replace" )
								{
									foo.replace( t2 );
									lastOp = "replace";
								}
								else
								{	
									foo.insert( t2 );
									lastOp = "insert";
								}
								break;	
							}
						}
					}
					break;
			}
			break;	
	}
	return false;

}


}catch(e)
{alert("dlgCalculator " + e.message + " " + e.name + " " + e.lineNumber + " " + e.fileName + " " + e.stack );}