function axes(f,jg){
	this.jg=jg;
	this.margin=0;
	this.gleft=0;
	this.gright=0;
	this.gwidth=498;
	this.gheight=498;
	this.gtop=0;
	this.gbottom=498;


	this.grid=true;
	this.largeGrid=true;
	this.axismarkers=true;
	this.minDiv=true;

	this.auto_yu=0;
	this.auto_yl=0;

	this.step=512;

	this.alphalower=['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8','9','.','(',','];
	this.alphaupper=['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','('];
	this.operators=['+','-','*','/','^','='];
	this.numeric=['0','1','2','3','4','5','6','7','8','9','.',','];
	this.multiplier=['0','1','2','3','4','5','6','7','8','9','.','x',','];
	this.constantlist=['a','b','c','d','e','m','n','o','p','q','r','s','u','v','w','z'];
	this.graphColour=["#903","#00f","#363","#90C","#c0c"]
	this.func=new Object();
	
	//this.auto_scale=document.getElementById("auto_scale").checked;
	this.degrees=document.getElementById("degrees").checked;
	this.piaxis=document.getElementById("piaxis").checked;
	//this.auto_clear=document.getElementById("auto_clear").checked;
	this.auto_clear=true;
	this.plot_axes=plotaxes;
	this.mark_axes=markaxes;
	this.make_marks=makemarks;
	this.basic_int=basicint;
	this.prepare_to_move=prepareToMove;
	this.move_axes=moveAxes;
	this.draw_graph=drawGraph;
	this.x_coord_to_screen=xCoordToScreen;
	this.y_coord_to_screen=yCoordToScreen;
	this.x_screen_to_coord=ScreenToCoordX
	this.y_screen_to_coord=ScreenToCoordY
	this.auto_scaler=autoScale;
	this.form_trans=formTrans;
	this.set_input=setInput;
	this.minus=Minus;
	this.replace_fn=replaceFn;
	this.separate_multiplied_functions=separateMultipliedFunctions;
	this.put_in_mult=PutInMult;
	this.draw_asymptote=drawAsymptote;
	this.replace_powers=replacePowers;
}



function formTrans(str){
var i;
var whereisit;
var funct;

this.func=this.set_input();

str=str.toLowerCase();
str=str.replace(/ /g,'');
str='('+str;
str=this.minus(str);
str=str+')';

if (Instr(str,'\'')>0) {str=this.derivative(str);}
if (Instr(str,'!')>0){str=this.factorial_calc(str);}
for (i=0;i<=this.func.usr.length;i++){
	whereisit=Instr(str,this.func.usr[i]);
	funct=this.func.usr[i];
	if (whereisit>0){
		str=this.replace_fn(str,funct,whereisit,i);
	}
}
str=this.separate_multiplied_functions(str);
str=this.put_in_mult(str);
//alert(str);
if (Instr(str,"^")>0){str=this.replace_powers(str);}
//alert(str);
return str;
}

function replacePowers(x){
	x = x.replace(/\{|\[/g,'(').replace(/\}|\]/g,')');
	while (/\^/.test(x))
	{
		var az = x.search(/\^/),
		a = x.substring(0, az), z = x.substring(az+1),
		p = 0, ch, lim = az-1;
		while (true)
		{
			ch = a.charAt(lim);
			if (ch == ')') p++;
			else if (ch == '(') p--;
			if (p < 0 || !p && (!(lim+1) || /[\+\-\*\/\,]/.test(ch))) break;
			lim--;
		}
		a = a.substring(0, lim+1) + 'POW(' + a.substring(lim+1, az);
		lim = 0; p = 0;
		while (true)
		{
			ch = z.charAt(lim);
			if (ch == '(') p++;
			else if (ch == ')') p--;
			if (p < 0 || !p && (lim == z.length || lim > 0 && /[\+\-\*\/\^\,]/.test(ch))) break;
			lim++;
		}
		z = z.substring(0, lim) + ')' + z.substring(lim);
		x = a+','+z;
	}
	return x;
}

function autoScale(){
	this.auto_yu=0;
	this.auto_yl=0;
	var increment=this.xRange/6;
	var x=this.xmin;
	var xold;
	var yold;
	do{
		var f=this.func;
		try{
			y=eval(f);
		}
		catch (error){}
		if (isNaN(y)==false && isNaN(yold)==false){
			if (y>this.auto_yu && y<130){this.auto_yu=y;}
			if (y<this.auto_yl && y>-130){this.auto_yl=y;}
			increment=this.xRange/this.step;
		}	
		xold=x;
		yold=y;
		if (yold>this.ymax || yold<this.ymin){
			increment=increment/10;
		}
		x+=increment;
	}while(x<this.xmax);
	this.auto_yu*=1.1;
	this.auto_yl*=1.1;
}

function drawGraph(ii){
	var x=this.xmin;
	var step=this.xRange/this.step;
	var xold;
	var yold;
	var xold2;
	var yold2;
	var sx;
	var sy;
	var sxo;
	var syo;
	var asymptote_drawn=false;
	var grad;
	//alert(this.func);
	do{
		var f=this.func;
		var y=eval(f);
		//debug(x+"   "+y+"    "+f);
		if (y!=undefined && y!=NaN && y!=Infinity){
			grad_old=((yold-yold2)/(xold-xold2));
			grad=((y-yold)/(x-xold));
			sx=this.x_coord_to_screen(x);
			sy=this.y_coord_to_screen(y);
			sxo=this.x_coord_to_screen(xold);
			syo=this.y_coord_to_screen(yold);
			//now sort the asymptotes
			if (Math.abs(grad)>500 && asymptote_drawn==false && (yold>0 && y<0 || yold<0 && y>0 || (sameSign(grad,grad_old)==false && yold>this.ymax && y>this.ymax )|| (sameSign(grad,grad_old)==false && yold<this.ymin && y<this.ymin ))){
				asymptote_drawn=this.draw_asymptote(xold,f);	
			}else{
					//debug(sxo+"   "+syo+"   "+sx+"   "+sy);
					if(Math.abs(syo)<5000 && Math.abs(sy)<5000){
						this.jg.setColor(this.graphColour[ii%5]);
						this.jg.drawLine(sxo,syo,sx,sy);
					}					
					asymptote_drawn=false;
			}
		}
		xold2=xold;
		yold2=yold;
		xold=x;
		yold=y;
		x=x+step;
	}while(x<this.xmax);
}

function drawAsymptote(x,f){
var y=eval(f);
var xold;
var yold;
var grad=0;
while (grad<5000){
	y=eval(f);
	if (y==Infinity || y==NaN || y==undefined){
		x=x+0.000001;
	}	
xold=x;
yold=y;
grad=Math.abs((y-yold)/(x-xold));
x=x+0.000001;

}
sx=this.x_coord_to_screen(x);
sy=this.y_coord_to_screen(y);
this.jg.setColor("#f00");
for (var j=0;j<=this.gheight;j=j+10){
	this.jg.drawLine(sx,this.gtop+j,sx,this.gtop+j+5);
}
this.jg.setColor("#000");


return true;
}

function xCoordToScreen(x){
	return Math.round((x-this.xmin)*this.gwidth/this.xRange)+this.gleft;
}

function ScreenToCoordX(X){
    return (X-this.gleft)*this.xRange/this.gwidth+this.xmin;
}

function yCoordToScreen(y){
	return this.gheight-Math.round((y-this.ymin)*this.gheight/this.yRange)+this.gtop;
}

function ScreenToCoordY(Y){
   return this.ymin-(Y-this.gheight-this.gtop)*this.yRange/this.gheight;
}

function prepareToMove(){
	
}

function moveAxes(){

}

function basicint(x){
   return Math.round(x)-(x<0);
}

function plotaxes(){
	this.xmin=parseFloat(document.getElementById("xmin").value);
	this.xmax=parseFloat(document.getElementById("xmax").value);
	this.xRange=this.xmax-this.xmin;

	this.func=Trim(document.getElementById("func").value);

	
	this.fns=this.func.split(";");
	//this.auto_scale=document.getElementById("auto_scale").checked;
	this.degrees=document.getElementById("degrees").checked;
	this.piaxis=document.getElementById("piaxis").checked;
	//this.auto_clear=document.getElementById("auto_clear").checked;
	if (this.auto_scale==true){
		this.auto_scaler();
		this.ymin=this.auto_yl;
		this.ymax=this.auto_yu;
	}else{


		this.ymin=parseFloat(document.getElementById("ymin").value);
		this.ymax=parseFloat(document.getElementById("ymax").value);
	}

	

	
	this.yRange=this.ymax-this.ymin;

	if (this.auto_clear){
		this.jg.clear();
	}
	//glm is graph units per screen unit for the x axis
    this.glm=(this.gwidth)/this.xRange;
	//glc is the screen x position of the y axis
    this.glc=-this.xmin*this.glm;

	//glp is graph units per screen unit for the y axis
    this.glp=(this.gheight)/this.yRange;
	//glc is the screen y position of the x axis
    this.glq=-this.ymin*this.glp;



	if (this.ymax<0){
         this.glV=this.gtop+10 //Dotted x-axis at the top
    }else if (this.ymin>0){
           this.glV=this.gbottom-10 //Dotted x-axis at the bottom
     }else{
           this.glV=this.gheight-Math.round(this.glq);
	}



	if (this.xmax<0){
		this.glU=this.gwidth-this.gright-10;
	}else if (this.xmin>0){
		this.glU=this.gleft+10
	}else{
		this.glU=Math.round(this.glc)+this.gleft;
	}



	this.mark_axes(1);
	this.mark_axes(2);
this.jg.drawLine(0,this.glV,this.gwidth,this.glV);
this.jg.drawLine(this.glU,0,this.glU,this.gheight);
for (var ii=0;ii<this.fns.length ;ii++ ){
	
	this.func=this.fns[ii];
	this.func=this.func.replace(/\s/gi,"");
	if (Trim(this.func)!=""){
		//alert(this.func);
		this.func=this.form_trans(this.func);
		this.draw_graph(ii);
	}
	
	
}


this.jg.paint();
}	

function markaxes(i){
	var min;
	var max;
	var K;
	var C;
	var S;
	var S0;
	var N;
	var QQ;
	var count;

	var QQ=0;
	var N=2;
	if (i==1){
		min=this.xmin;
		max=this.xmax;
	}else{
		min=this.ymin;
		max=this.ymax;
	}
	if (Math.abs((this.yRange/this.xRange)-(this.gheight/this.gwidth))>0.000001 || i==1){
		C=(max-min)/4;
		QQ=this.basic_int(Math.log(C)/Math.log(10));
		K=C/Math.pow(10,QQ);
		if (K>=2){N=2;}
		if (K>=4){N=4;}
		if (K>=10) {N=5;}
		if (i==1 && this.piaxis) {N=Math.PI;}
	}
	S=N/2;
	S0=S;
	
	C=-S*this.basic_int((-min/Math.pow(10,QQ))/S)-S;
	if (QQ>0){
		for (count=1;count<=QQ;count++){
			S=S*10;
			C=C*10;
		}
	}
	if (QQ<0){
		for (count=1;count<=-QQ;count++){
			S=S/10;
			C=C/10;
		}
	}


	if (i==1 && this.piaxis==true){
		S0=S/10;
	}else{
		S0=Math.pow(10,QQ)*Math.pow(10,-(S0==1))*Math.pow(2,-(S0==2));
	}
		
		if (S0==S/10){S0=S/5;}
		K=C;
		do{
			this.make_marks(i,K,S,S0,min,max);
			K=K+S;
		}while(K<max);
		

}

function makemarks(i,K,S,S0,min,max){
var X;
var Y;
var yy;
var pp;
var qq;
var qqp;
var xx;
var tlab;
var H=8;

if (i==1){
	X=this.glm*K+this.glc;
	yy=K;
	do{
		xx=4+4*2*(yy-K-(S==0));
		pp= Math.round(this.glm*yy+this.glc);
	
		if (this.grid==true && this.largeGrid==false && pp>0){
			//Vertical minor division grid lines	
			this.jg.drawLine(pp+this.gleft,this.height-this.gbottom,pp+this.gleft,this.gtop);
		}
		if ((this.minDiv==true || xx==8) && (pp>0 && pp<this.gwidth)){
			this.jg.drawLine(pp+this.gleft,this.glV,pp+this.gleft,this.glV-4);
		}
		yy=yy+S0;
	}while(yy<K+S);
           

if (X>0){//(X<gwidth+gleft) and (X>gleft) then
	if (this.grid==true){
		//Major division grid lines - vertical
		this.jg.setColor("#ccc"); 	this.jg.drawLine(Math.round(X)+this.gleft,this.gheight+this.gtop,Math.round(X)+this.gleft,this.gtop);
		this.jg.setColor("#000"); 
	}
	if (X>0){//(X>gleft) and (X<gwidth+gleft-12) then
		//x axis major divisions
		this.jg.drawLine(Math.round(X)+this.gleft,this.glV,Math.round(X)+this.gleft,this.glV-H);

	}
	if ((X>0) && (Math.abs(K)>S0)){

		if (this.piaxis==true){tlab=parseFloat((K)/Math.PI)+'pi';}else{ tlab=Math.round(K*1000)/1000;}
		if (this.axismarkers==true){
			if (this.glV<=10){
				this.jg.drawString(tlab,this.gleft+Math.round(X)-7,Math.round(this.glV+H)-12);

			}else{
				this.jg.drawString(tlab,this.gleft+Math.round(X)-7,this.glV+5);
			}
		}
	}
	}
}
if (i==2){
	
	Y=this.glp*K+this.glq;
	yy=K;
	do{
		xx=4+4*2*(yy-K-(S==0));
		pp= this.gtop+this.gheight-Math.round(this.glp*yy+this.glq);
		qq=Math.round(this.glU);
		qqp=Math.round(this.glU+4);
		if (this.grid && (pp<this.gheight-this.gbottom) && (pp>this.gtop)){
			this.jg.drawLine(this.gleft,pp,this.gleft+this.gwidth,pp);
		}
		if ((this.minDiv==true || (xx==8)) && (pp>this.gtop+10) && (pp<this.gheight+this.gtop)){ 
			this.jg.drawLine(qq,pp,qqp,pp);
		}
		yy=yy+S0;
	}while(yy<K+S);
	if (Y>0){
		if (this.grid==true){
			this.jg.setColor("#ccc"); 	this.jg.drawLine(this.gleft,this.gheight+this.gtop-Math.round(Y),this.gleft+this.gwidth,this.gheight+this.gtop-Math.round(Y));
			this.jg.setColor("#000"); 
		}
		if (Y>0){			this.jg.drawLine(this.glU+H,this.gheight+this.gtop-Math.round(Y),this.glU,this.gheight+this.gtop-Math.round(Y));
		}
		if ((Y>0) && (Math.abs(K)>S0)){
			if (this.axismarkers==true){
				if (this.glU<=this.gleft+10){
                       //y axis markers
						 this.jg.drawString(Math.round(K*10000)/10000,Math.round(this.glU),this.gtop+this.gheight-Math.round(Y)-7);
				}else{
					this.jg.drawString(Math.round(K*10000)/10000,Math.round(this.glU)-30,this.gtop+this.gheight-Math.round(Y)-7);
				}
			}
		}
	}
}
}




function PutInMult(str){
var p1;
var p2;
var bung1in;
var first;
var last;

  var i=0;
   bung1in=false;
   do{
         p1=str.substr(i,1);
         p2=str.substr(i+1,1);
		 //alert("p1="+p1+"  p2="+p2);
         if (this.alphalower.contains(p1) && this.alphalower.contains(p2)){bung1in=true;}
         if (p1==')' && p2=='('){bung1in=true;}
         if (this.alphalower.contains(p1) && this.alphaupper.contains(p2)){bung1in=true;}
         if (this.alphaupper.contains(p1) && this.alphaupper.contains(p2)){bung1in=false;}
         if (this.alphalower.contains(p1) && p2==')'){bung1in=false;}
         if (this.alphalower.contains(p1) && this.operators.contains(p2)){bung1in=false;}
         if (this.numeric.contains(p1) && this.numeric.contains(p2)){bung1in=false;}
         if (this.numeric.contains(p1) && this.alphaupper.contains(p2)){bung1in=true;}
         if (this.alphalower.contains(p1) && p2=='('){bung1in=true;}
         if (p1==')' && this.alphaupper.contains(p2)){bung1in=true;}
         if (this.numeric.contains(p1) && (p2=='e' || p2=='E')){bung1in=false;}
         if (p1=='e'){bung1in=false;}
         if (p1=='('){bung1in=false;}
         if (this.operators.contains(p1)){bung1in=false;}
         if (p1=='x' && p2==','){bung1in=false;}
         if (p1==',' && this.alphalower.contains(p2)){bung1in=false;}
         if (bung1in){
			first=str.substr(0,i+1);
			last=str.substr(i+1);
			if (this.operators.contains(first.right(1))==false && last!=''){
				 str=first+'*'+last;
			}
        }
    i++;
   }while (i<=str.length);
return str;
}


function separateMultipliedFunctions(str){
var p1;
var p2;
var first;
var last;

   //In here look for instances where an 'x' is followed by another lowercase letter
   for(var i=0;i<str.length-1;i++){
            p1=str.substr(i,1);
            p2=str.substr(i+1,1);

			

            if (p1=='x' && this.alphalower.contains(p2) && p2!=','){
                 first=str.substr(1,i);
                 last=str.right((str.length)-i);
				 //alert(first);
				 //alert(last);
                 if (this.operators.contains(first.right(1))==false  && last!=''){
					str=first+'*'+last;
				}
            }
	}
   return str;
}

function replaceFn(str,funct,where,i){
var l_put;
var pp;
l_put=false;
var j=where-1;
var first=str.substr(0,where);
var	last=str.right(str.length-funct.length-where);
if (last.substr(0,1)!='('){
	str=first+this.func.internal[i]+'('+last;
	l_put=true;

}else{
	str=first+this.func.internal[i]+last;
}
do{
	j=j+1;
}while(this.alphaupper.contains(str.substr(j,1)));

do{
	j=j+1;
}while(this.alphalower.contains(str.substr(j,1)) && (j<=str.length));

if (l_put){
	first=str.substr(0,j);
	last=str.right(str.length-j);
	str=first+')'+last;
}

pp=Instr(str,funct);
if (pp>0){str=this.replace_fn(str,funct,pp,i);}

return str;
}



function Minus(str){
var where;
do{
	where=str.search(/\(-/i);
	if (where>0){
		str=str.slice(1,where)+'0'+str.slice(where+1,str.length-where);
	}
}while(str.search(/\(-/i)>0);
return str;
}

function derivative(str){

}
function factorial_calc(str){

}


function setInput(){
	
var func=new Object();
func.usr=new Array();
func.internal=new Array();
func.inv=new Array();
var degrees=this.degrees;
  func.usr[0]='arcsinh';  func.internal[0]='ARCSINH'; func.inv[0]='SINH';
  func.usr[1]='arccosh';  func.internal[1]='ARCCOSH'; func.inv[1]='COSH';
  func.usr[2]='arctanh';  func.internal[2]='ARCTANH'; func.inv[2]='TANH';
  func.usr[3]='arcsech';  func.internal[3]='ARCSECH'; func.inv[3]='SECH';
  func.usr[4]='arccosech';  func.internal[4]='ARCCOSECH';func.inv[4]='COSECH';
  func.usr[5]='arccoth';  func.internal[5]='ARCCOTH';func.inv[5]='COTH';
  func.usr[6]='arcsin';  if (degrees){func.internal[6]='DEGARCSIN'}else{func.internal[6]='ARCSIN';func.inv[6]='SIN';}
  func.usr[7]='arccos';  if (degrees){func.internal[7]='DEGACOS'}else{func.internal[7]='ARCCOS';func.inv[7]='COS';}
  func.usr[8]='arctan';  if (degrees){func.internal[8]='DEGATAN'}else{func.internal[8]='ARCTAN';func.inv[8]='TAN';}
  func.usr[9]='arccot';  if (degrees){func.internal[9]='DEGACOT'}else{func.internal[9]='ARCCOT';func.inv[9]='COT';}
  func.usr[10]='arcsec';  if (degrees){func.internal[10]='DEGASEC'}else{func.internal[10]='ARCSEC';func.inv[10]='SEC';}
  func.usr[11]='arccosec';  if (degrees){func.internal[11]='DEGACOSEC'}else{func.internal[11]='ARCCOSEC';func.inv[11]='COSEC';}
 
  func.usr[14]='sinh';  func.internal[14]='SINH';func.inv[14]='ARCSINH';
  
  func.usr[17]='cosh';  func.internal[17]='COSH';func.inv[17]='ARCCOSH';
  
  func.usr[20]='cosech';  func.internal[20]='COSECH';func.inv[20]='ARCCOSECH';
  
  func.usr[23]='sech';  func.internal[23]='SECH';func.inv[23]='ARCSECH';
  
  func.usr[26]='tanh';  func.internal[26]='TANH';func.inv[26]='ARCTANH';
  
  func.usr[29]='coth';  func.internal[29]='COTH';func.inv[29]='ARCCOTH';
  func.usr[32]='cosec';  if (degrees){func.internal[32]='COSECRAD'}else{func.internal[32]='COSEC';func.inv[32]='ARCCOSEC';}
  func.usr[39]='cos';  if (degrees){func.internal[39]='COSRAD'}else{func.internal[39]='COS';func.inv[39]='ARCCOS';}
  func.usr[42]='tan';  if (degrees){func.internal[42]='TANRAD'}else{func.internal[42]='TAN';func.inv[42]='ARCTAN';}
  func.usr[43]='sec';  if (degrees){func.internal[43]='SECRAD'}else{func.internal[43]='SEC';func.inv[43]='ARCSEC';}
  func.usr[46]='cot';  if (degrees){func.internal[46]='COTRAD'}else{func.internal[46]='COT';func.inv[46]='ARCCOT';}
  func.usr[47]='sin';  if (degrees){func.internal[47]='SINRAD'}else{func.internal[47]='SIN';func.inv[47]='ARCSIN';}
  func.usr[48]='integ';  func.internal[48]='INTEG';func.inv[48]='DifF';
  func.usr[49]='diff';  func.internal[49]='DifF';func.inv[49]='INTEG';
  func.usr[50]='log';  func.internal[50]='LOG';
  func.usr[51]='ln';  func.internal[51]='LN';func.inv[51]='EXP';
  func.usr[52]='sqrt';  func.internal[52]='SQRT';func.inv[52]='SQR';
  func.usr[53]='sqr';  func.internal[53]='SQR';func.inv[53]='SQRT';
  func.usr[54]='int';  func.internal[54]='INT';
  func.usr[55]='exp';  func.internal[55]='EXP';func.inv[55]='LN';
  func.usr[56]='abs';  func.internal[56]='ABS';
  func.usr[57]='func.inv';  func.internal[57]='func.inv';func.inv[57]='func.inv';
  func.usr[58]='blanc';  func.internal[58]='BLANC';
  func.usr[59]='f';  func.internal[59]='F';
  func.usr[60]='g';  func.internal[60]='G';
  func.usr[61]='h';  func.internal[61]='H';
  func.usr[62]='i';  func.internal[62]='I';
  func.usr[63]='j';  func.internal[63]='J';
  func.usr[64]='k';  func.internal[64]='K';
  func.usr[65]='l';  func.internal[65]='L';
  func.usr[66]='root';func.internal[66]='ROOT';
  func.usr[67]='power';func.internal[67]='POWER';
  func.usr[68]='cbrt';func.internal[68]='CBRT';

  return func;
}

