//CHECKJS  d:\bob\promote\profwork\software\first\js\acidbase\acidbase.js 6/7/02 2:42:05 AM
//bob hanson hansonr@stolaf.edu 11/25/98
//revised 6/15/2001 for divgraph
//revised 3/11/2002 for very dilute acids and bases
if(top==self)document.location.href="index.htm"
var s=top.document.URL+""
isstolaf=(s.indexOf("stolaf.edu")>0)
allowps=isstolaf

//stolaf allows a postscript option for printing that is not available from off campus.
//see function setserver() in graph.js

//user interface

function addspecialspecies(iwhich,mLfactor)
{
	var s=""
	var imL=0
	var iM=0
	var imLprev=10
	var stemp=""
	sinfo=strsub(soln[iwhich]," + ","0 mL of ")
	while(sinfo.length)
	{
		s=extractinfo("\n")
		stemp=sinfo
		sinfo=s
		imL=eval(extractinfo(" mL of "))
		if(imL==0)
		{
			mLtotal-=imLprev*mLfactor
			imL=imLprev
		}
		imLprev=imL
		iM=eval( extractinfo(" M "))
		addspecies(imL*mLfactor,iM,sinfo)
		sinfo=stemp
	}
}

function addspecies(mL,M,substance)
{
	var info=0
	mLtotal+=1*mL
	for(var i=0;i<nspecies;i++)
	{
		info=speciesinfo[substance][i]
		if(info>0)
		{
			var mmoles=mL*M*info
			mmol[i]+=mmoles
			if(weaktype[i])
			{
				HA=weaktype[i]
				mmolweak[0]+=mmoles
				mmolweak[HA]+=mmoles
				if(iweakfirst==0||iweakfirst>HA)iweakfirst=HA
				if(iweaklast<HA)iweaklast=HA
			}
		}
	}
}

function checkpH(pH0,pH1,pHstep)
{
	if(pH0==-99)return -99
	var overallcharge=0
	var charge=0
	for(var ipH=pH0;ipH<=pH1;ipH+=pHstep)
	{
		iopt++
		charge=setratios(ipH)
		//  sexplain0+=ipH+" "+charge+"\n"
		if(charge==0)return ipH
		if(charge*overallcharge<0)return ipH-pHstep
		overallcharge=charge
	}
	if(Math.abs(overallcharge)<1e-50)return ipH
	return -99
}

function clearspecies()
{
	for(var i=0;i<nspecies;i++)
	{
		mmol[i]=0
		mmolweak[i]=0
	}
	mLtotal=0
	iweaklast=0
	iweakfirst=0
}

function closeburetwindow(){
	if(!buretwin.closed)buretwin.close()
}

function doafter(iseq,ispt)
{
	showingequil=iseq
	if(showingonesoln)
	{
		dospecialpH(true)
	}else{
		if(showingmix){
			dospecialmix(1)
		}else{
			if(ispt){
				showpt(mL_2)
			}else{
				doit(-1)
			}
		}
	}
}

function docalc(){
	if(showingonesoln){
		dospecialpH(true)
	}else{
		doit(-1)
	}
}

function doexample(iwhich)
{
	if(iwhich==0)
	{
		var s="\n\nThese four examples illustrate how to produce buffers in different ways.\n\n"
		s+="1. A solution containing a weak acid and its conjugate base is a buffer.\n\n"
		s+="2. A buffer can be made by mixing a solution of a weak acid with a soluton of its conjugate base salt.\n\n"
		s+="3. A buffer can be made by adding a small amount of NaOH to a weak acid solution\n\n"
		s+="4. Mixing a weakly acidic salt with its conjugate weak base also forms a buffer.\n\nClick on one of the numbered links to see the example."
		alert(s)
		return
	}
	if(iwhich==1)soln[1]="20 mL of 0.05 M HC2H3O2\n + 0.05 M NaC2H3O2\n"
	if(iwhich==2)soln[1]="10 mL of 0.1 M HC2H3O2\n10 mL of 0.1 M NaC2H3O2\n"
	if(iwhich==3)soln[1]="10 mL of 0.1 M HC2H3O2\n10 mL of 0.05 M NaOH\n"
	if(iwhich==4)soln[1]="20 mL of 0.05 M NH4Cl\n + 0.05 M NH3\n"
	soln[2]="20 mL of 0.1 M NaOH\n"
	ithiscomponent[1]=0
	ithissoln=1
	dospecial(0)
	dospecialbefore()
}

function doit(imL,ispt)
{
	//-1 from most controls; -2 from check; ispt: clicking on a point
	if(ispt && titrating)return
	if(imL==-1 && showingspecial)return
	if(imL==-2)showingspecial=false  //check pressed
	if(titrating && imL<0){imL=stoptitration(imL,true);if(imL==0)return}
	titrating=(imL>=0 && !ispt)
	var s=""
	var s1=getselect("subs1")
	var s2=getselect("subs2")
	mL_1=getselect("mL1")
	mL_2=getselect("mL2")
	if(titrating || ispt)
	{
		mL_2=imL
		soln[1]=mL_1+" mL of 0.1 M "+s1+"\n"
		soln[2]=mL_2+" mL of 0.1 M "+s2+"\n"
	}
	clearspecies()
	addspecies(mL_1,0.1,s1)
	addspecies(mL_2,0.1,s2)
	s="<center>BEFORE RXN&nbsp;&nbsp;<br>"+tabulatespecies("("+mL_1+" + "+mL_2+" mL)")
	writeinfo(parent.fraBefore,s)
	reactspecies()
	showingonesoln=false
	showequilinfo(!titrating||imL==0||s2!="NaOH"||s1=="NaOH",ispt)
}

function donewacid()
{
	var Ka=""
	var s=""
	var sacidnew=""
	if(titrating){stoptitration(false);return}
	showingspecial=true
	showingsolns=false
	showingmix=false
	sacidnew=prompt("Acid to add or modify?",(sacid==""?"HA":sacid))
	if(sacidnew==null || sacidnew.length==0)return
	sacidnew=getspeciesname(sacidnew)
	sacid=sacidnew
	s="<small><form name=info>"
	s+="<table>"
	s+="<tr><th><a href='javascript:parent.fraMain.donewacidload(1)'>New</a> <a href='javascript:parent.fraMain.donewacid()'>Acid</a><th>Ka"
	s+="<tr><td><input type=text name=acid size=8 value='"+sacidnew+"'>"
	Ka="1e-5"
	s+="<td><input type=text name=a0 size=8 value='"+Ka+"'>"
	sacidnew=nextspecies(sacidnew)
	Ka=(sacidnew.charAt(0)=="H"?"1e-7":"")
	s+="<tr><td><input type=text name=a1 size=8 value='"+sacidnew+"'>"
	s+="<td><input type=text name=a2 size=8 value='"+Ka+"'>"
	if(Ka=="")
	{
		sacidnew=""
	}else{
		sacidnew=nextspecies(sacidnew)
	}
	Ka=(sacidnew.charAt(0)=="H"?"1e-9":"")
	s+="<tr><td><input type=text name=a3 size=8 value='"+sacidnew+"'>"
	s+="<td><input type=text name=a4 size=8 value='"+Ka+"'>"
	if(Ka=="")
	{
		sacidnew=""
	}else{
		sacidnew=nextspecies(sacidnew)
	}
	s+="<tr><td><input type=text name=a5 size=8 value='"+sacidnew+"'>"
	s+="<th><a href='javascript:parent.fraMain.donewacidok()'>OK</a> "
	s+="</table>"
	s+="</form>"
	writeinfo(parent.fraAfter,s)
	s="<p><p>Enter a series of species names on the left and their associated Ka values on the right. "
	s+="The only charge that is important is the one on the first species. Indicate its charge, if present, as \"+n\" where n is some number."
	s+="For the last species, do not enter a Ka. Ka values must be between 0 and 1."
	writeinfo(parent.fraBefore,s)
	
	s=","+subs.join(",")+","
	if(s.indexOf(","+sacid+",")<0)sacid=""
	if(sacid!="")setTimeout("donewacidload(0)",200)
}

function donewacidload(isnew)
{
	var f=parent.fraAfter.document.info
	var swhat=sclean(f.acid.value)
	if(isnew || swhat=="NH3")
	{
		f.acid.value=""
		for(var i=0;i<6;i++)eval("f.a"+i+".value=''")
		return
	}
	swhat=strsub(swhat,"Na","H")
	var s0=swhat
	var s=","+subs.join(",")+","
	if(s.indexOf(","+swhat+",")<0)
	{
		swhat=getselect("subs1")
		swhat=strsub(swhat,"Na","H")
		if(swhat=="NH4Cl")swhat=""
		var s=","+weakacids.join(",")+","
		if(s.indexOf(","+swhat+",")<0)
		{
			alert("Enter the formula or a real or fictious weak acid,\nsuch as \"H2A\" or \"HNO2,\"\nfor which you know Ka information.")
			for(var i=0;i<6;i++)eval("f.a"+i+".value=''")
			return
		}
		if(s0!="")alert("Substance "+s0+" is not one of the substances available. Picking an acid from the box on the left.")
		f.acid.value=swhat
	}
	for(var i=0;i<6;i+=2)eval("f.a"+i+".value='"+(i<Kainfo[swhat].length?roundoff(Kainfo[swhat][i],-2):"")+"'")
	for(var i=1;i<6;i+=2)eval("f.a"+i+".value='"+(i<Kainfo[swhat].length?Kainfo[swhat][i]:"")+"'")
}

function donewacidok()
{
	var f=parent.fraAfter.document.info
	var swhat=sclean(f.acid.value)
	var Ka=0
	swhat=strsub(swhat,"Na","H")
	s=swhat+"|"
	var iacid=(acidlist.indexOf(s)>0)
	if(!iacid)Kainfo[swhat]=new Array()
	var i=0
	while(s!="" && i<6)
	{
		s=eval("f.a"+i+".value")
		if(s.length){
			if(i%2){
				Kainfo[swhat][i]=s
			}else{
				Ka=parseFloat(s)
				if(isNaN(Ka))Ka=0
				if(Ka<=0)Ka=1e-99
				if(Ka<1 && (!iacid || iacid && Kainfo[swhat][i]<1e100))Kainfo[swhat][i]=""+Ka
			}
		}
		i++
	}
	if(i<2)return
	if(iacid)
	{
		HA=acidindex[swhat]
		for(i=0;i<weakpoly[HA];i++)
		{
			Kaval[HA+i]=(i*2<Kainfo[swhat].length?Kainfo[swhat][i*2]:1e-99)
			Kanew[HA+i]=true
		}
		alert("Information for " +swhat+" has been modified.")
		showkas()
	}else{
		setspeciesinfo(swhat)
		weakacids[weakacids.length]=swhat
		alert("Information for " +swhat+" has been added. This acid and its conjugate base salts are now at the end of the lists in the selection boxes, above.")
		showkas()
	}
	sacid=swhat
}

function dospecial(iwhich)
{
	if(titrating){stoptitration(false);return}
	showingspecial=true
	showingmix=false
	if((iwhich & 1) || soln[1]=="")
	{
		ithiscomponent[1]=0
		soln[1]=getselect("mL2")+" mL of 0.1 M "+getselect("subs2")+"\n"
	}
	if((iwhich & 2) || soln[2]=="")
	{
		ithiscomponent[2]=0
		soln[2]=getselect("mL1")+" mL of 0.1 M "+getselect("subs1")+"\n"
	}
	if(iwhich==0)showspecialform()
	ithissoln=(iwhich==0?1:iwhich)
	showsoln(0)
	var s=specialgetcomponent(ithissoln,ithiscomponent[ithissoln])
	loadspecialform(s)
}

function dospecialadd()
{
	if(!specialcheck())return
	var s=getspecialinfo()
	if(s=="")return
	specialsetcomponent(ithissoln,-1,s)
	specialshow()
}

function dospecialbefore()
{
	if(!specialcheck())return
	clearspecies()
	addspecialspecies(ithissoln,1)
	showsoln(-ithissoln)
	clearspecies()
	addspecialspecies(ithissoln,1)
	var s="<center>BEFORE RXN&nbsp;&nbsp;<br>"+tabulatespecies("("+mLtotal+" mL total)")
	reactspecies()
	var pH=getpH(true)
	var v=Math.abs(setratios(pH))
	s+="<p><center>EQUILIBRIUM&nbsp;&nbsp;<br>"+tabulatespecies("("+mLtotal+" mL total)")
	writeinfo(parent.fraStatus,s)
	showingonesoln=true
}

function dospecialdelete()
{
	if(!specialcheck())return
	specialsetcomponent(ithissoln,ithiscomponent[ithissoln],"")
	s=specialgetcomponent(ithissoln,0)
	loadspecialform(s)
	specialshow()
}

function dospecialload()
{
	if(!specialcheck())return
	var s=""
	if(ithissoln==1)
	{
		s=getselect("mL2")+" mL of 0.1 M "+getselect("subs2")+"\n"
	}else{
		s=getselect("mL1")+" mL of 0.1 M "+getselect("subs1")+"\n"
	}
	loadspecialform(s)
}

function dospecialmix(idopH)
{
	if(!specialcheck())return
	clearspecies()
	addspecialspecies(1,1)
	addspecialspecies(2,1)
	if(idopH)
	{
		//pH of mixed solutions
		reactspecies()
		showingonesoln=false
		showingmix=true
		showequilinfo(true)
	}else{
		//before reaction of mixed solutions
		showsoln(-3)
		clearspecies()
		addspecialspecies(1,1)
		addspecialspecies(2,1)
		var s="<center>BEFORE RXN&nbsp;&nbsp;<br>"+tabulatespecies("("+mLtotal+" mL total)")
		writeinfo(parent.fraStatus,s)
	}
}

function dospecialpH()
{
	if(!specialcheck())return
	clearspecies()
	addspecialspecies(ithissoln,1)
	reactspecies()
	showingonesoln=true
	showequilinfo(true)
}

function dospecialpick(iwhich,icomp)
{
	var i=icomp
	showingsolns=false
	parent.fraBefore.document.info.pick[iwhich-1].checked=true
	ithissoln=iwhich
	ithiscomponent[iwhich]=icomp
	if(i<0)
	{
		loadspecialform(" mL of  M ")
		showsoln(ithissoln)
	}else{
		dospecialselect(ithissoln)
	}
}

function dospecialreplace()
{
	if(!specialcheck())return
	var s=getspecialinfo()
	if(s=="")return
	specialsetcomponent(ithissoln,ithiscomponent[ithissoln],s)
	specialshow()
}

function dospecialselect(iwhich)
{
	if(!specialcheck())return
	ithissoln=iwhich
	showingsolns=false
	showingonesoln=false
	showingmix=false
	showsoln(iwhich)
	var s=specialgetcomponent(iwhich,ithiscomponent[iwhich])
	loadspecialform(s)
}

function dospecialtitrate(iwhich)
{
	if(!specialcheck())return
	isoln1=iwhich
	isoln2=3-iwhich
	//trick here is that we have to know soln two volume
	clearspecies()
	addspecialspecies(isoln2,1)
	mLtotal2=mLtotal
	specialtitr(-1)
}

function dotitrate(imL0)
{
	if(showingspecial){
		if(titrating){
			specialtitr(imL0)
			return
		}else{
			if(confirm("This will close the solution panels below. Is that OK?")){
				showingspecial=false
			}else{
				return		
			}	
		}
	}
	isoln1=1
	isoln2=2
	var imL=imL0
	var isfast=false//(document.info.fast.checked)
	showingselected=false
	document.status=titrating+" "+imL0
	if(titrating && imL<0){imL=stoptitration(imL,true);if(imL==0)return}
	if(imL<0)
	{
		writeinfo(parent.fraGraph,"")
		closeburetwindow()
		if(!setuptitration(eval(getselect("mL2"))))return
		imL=0
		openburetwindow()
	}
	
	doit(imL)
	npts++
	imL+=titrationstep
	imL=eval(roundoff(imL,3))
	mLthis=imL
	if(imL>titrationend+.0001){imL=stoptitration(imL,true);if(imL==0)return}
	titrationcommand="dotitrate("+imL+")"
	if(titrating)titrationtimer=setTimeout(titrationcommand,(isfast?10:titrationdelay))
}

function getcH()
{
	var i=0
	var icando=(iweakfirst==iweaklast)
	var ipt=0
	var icount=0
	var x=0
	HA=0
	A=0
	Ka=0
	if(mmol[H]/mLtotal>1.001e-7)
	{
		sexplain="the excess of strong acid and the hydrolysis of water"
		return mmol[H]/mLtotal
	}
	if(mmol[OH]/mLtotal>1.001e-7)
	{
		sexplain="the excess of strong base and the hydrolysis of water"
		return Kw/(mmol[OH]/mLtotal)
	}
	if(iweaklast==0)
	{
		sexplain="the solution being essentially neutral"
		return 1e-7
	}
	//must check for weak acid/base alone or conj pairs
	if(icando)
	{
		i=iweakfirst
		HA=weaktype[i]
		while(i<nspecies && HA==weaktype[i])
		{
			if(mmol[i])
			{
				icount++
				if(icount>2||icount==2 && mmol[i-1]==0){icando=false;i=nspecies}
				ipt=i
			}
			i++
		}
	}
	if(!icando)
	{
		sexplain0="having to go to charge/mass balance techniques here... "
		isbruteforce=true
		return -99
	}
	//full 1st-year method
	//ok, we either have a single species or a buffer
	//if a buffer, set HA to ipt-1
	//if a single species and it's the weak acid set HA to ipt
	//or if species is more acidic than basic, then also set HA to ipt
	HA=ipt-1
	if(icount==1&&(weaktype[ipt]==ipt||(ABtype[ipt]==3&&Kaval[ipt]>Kw/Kaval[HA])))HA=ipt
	A=HA+1
	Ka=Kaval[HA]
	if(mmol[HA]>0 && mmol[A]>0)
	{
		sexplain="using the HA/A- buffer approximation that Ka=[H+](mmol A-)o/(mmol HA)o"
		return Ka*mmol[HA]/mmol[A]
	}
	if(mmol[HA]>0)
	{
		sexplain="solving x^2/(C-x)=Ka \nwhere x=[H+] for the weak"+(issalt[HA]?"ly acidic ion":" acid")+" HA"
		return xsolve(Ka,mmol[HA])
	}
	if(mmol[A]>0)
	{
		sexplain="solving x^2/(C-x)=Kw/Ka \nwhere x=[OH-] for the weak"+(issalt[A]?"ly basic ion":" base")+" A-"
		return Kw/xsolve(Kw/Ka,mmol[A])
	}
	return -99 //impossible return
}

function getoverallcharge(type){
	var overallcharge=0
	var v=0
	if(type==0)totalcharge=0
	for(var i=0;i<nspecies;i++)
	{
		v=Charge[i]*mmol[i]
		overallcharge+=(type*v>=0?v:0)
		if(type==0)totalcharge+=Math.abs(v)
	}
	return overallcharge
}

function getpH(iresetpH)
{
	if(iresetpH)thepH=-2
	var cH=(isbruteforce && mmolweak[0]>0?-99:getcH())
	if(cH!=-99)
	{
		if(Ntype[HA])
		{
			sexplain=strsub(sexplain,"Kw/Ka","Kb")
		}
		sexplain=strsub(sexplain,"HA",speciesname[HA])
		sexplain=strsub(sexplain,"A-",speciesname[A])
		return pH(cH)
	}
	return pHfrombalance()
}

function getsolnpH(iwhich){
	if(!specialcheck())return
	clearspecies()
	addspecialspecies(iwhich,1)
	reactspecies()
	var pH=getpH(true)
	return "<font color="+pHcolor(pH)+">"+roundoff(pH,2)+"</font>"
}

function getspecialinfo()
{
	var f=parent.fraBefore.document.info
	var smL=nclean(f.mL.value)
	var s=smL+""
	if(s.charAt(0)==".")smL="0"+smL
	var sM=nclean(f.M.value)
	s=sM+""
	if(s.charAt(0)==".")sM="0"+sM
	s=f._subst[f._subst.selectedIndex].value
	return (smL=="0"?" + ":smL+" mL of ")+sM+" M "+s+"\n"
}

function hydrolyzewater(){
	var b=mmol[H]+mmol[OH]
	var x=(-b+Math.pow(b*b-4*(mmol[H]*mmol[OH]-Kw*mLtotal*mLtotal),0.5))/2
	mmol[H]+=x
	mmol[OH]+=x
}

function loadspecialform(swhat)
{
	var i=0
	var s=""
	var f=parent.fraBefore.document.info
	sinfo=strsub(swhat," + ","0 mL of ")
	sinfo=extractinfo("\n")
	s=extractinfo(" mL of ")
	f.mL.value=(s=="0"?"":s)
	f.M.value=extractinfo(" M ")
	setselect(f._subst,sinfo)
}

function openburetwindow(){
	buretwin=open("buret.htm?"+titrationdelay,"buretwin",woptionsburet)
}

function pHcolor(pH){
	return(pH>=9?"blue":pH<=5?"red":"green")
}

function pHfrombalance()
{
	iopt=0
	var ipH=0
	for(var i=0;i<nspecies;i++)mmol0[i]=mmol[i]
	ipH=checkpH(thepH,14,1)
	ipH=checkpH(ipH-.1,ipH+1.1,0.1)
	ipH=checkpH(ipH-.01,ipH+.11,0.01)
	ipH=checkpH(ipH-.001,ipH+.011,0.001)
	if(ipH==-99)
	{
		sexplain="Couldn't figure out the pH!"
		return "?"
	}else{
		sexplain=iopt+" optimizations of charge balance to determine the proper ratios of acid/base for each conjugate pair present."
	}
	return ipH
}

function reactspecies()
{
	var x=mmol[H]
	var y=0
	if(x>0)
	{
		for(var i=0;i<nspecies;i++)
		{
			if(x>0 && (ABtype[i]&2) && mmol[i]>0)
			{
				y=Math.min(mmol[i],x)
				mmol[i]-=y
				mmol[H]-=y
				if(AConj[i]){mmol[AConj[i]]+=y}
				x=mmol[H]
			}
		}
	}
	var x=mmol[OH]
	if(x>0)
	{
		for(var i=0;i<nspecies;i++)
		{
			if(x>0 && (ABtype[i]&1) && mmol[i]>0)
			{
				y=Math.min(mmol[i],x)
				mmol[i]-=y
				mmol[OH]-=y
				if(BConj[i]){mmol[BConj[i]]+=y}
				x=mmol[OH]
			}
		}
	}
	hydrolyzewater()
}

function setratios(ipH)
{
	var cH=Math.pow(10,-ipH)
	mmol[H]=cH*mLtotal
	mmol[OH]=(Kw/cH)*mLtotal
	havesetratios=true
	//first set up ratios of [A]/[HA]
	//we use B_HAratio[HA] here to store sum of all for denominator later
	for(var i=iweakfirst;i<nspecies;i++)
	{
		HA=weaktype[i]
		if(mmolweak[HA])
		{
			if(HA==i)
			{
				B_HAratio[i]=1
			}else{
				B_HAratio[i]=B_HAratio[i-1]*Kaval[i-1]/cH
				B_HAratio[HA]+=B_HAratio[i]
			}
		}
	}
	//ok, now we calc mmol[i] by multiplying the alphafractions by total HA
	for(var i=iweakfirst;i<nspecies;i++)
	{
		HA=weaktype[i]
		if(mmolweak[HA])
		{
			mmol[i]=mmolweak[HA]*(HA==i?1:B_HAratio[i])/B_HAratio[HA]
		}
	}
	return getoverallcharge(0)
}

function setuptitration(iend)
{
	titrating=false
	titrationinfo=""
	titrationend=iend
	if(titrationend==0)titrationend=deftitrationend
	if(titrationstep==0)titrationstep=deftitrationstep
	var istep=prompt("How many mL would you like to add at a time initially?",roundoff(titrationstep,1))
	if(istep==null)return false
	istep=parseFloat(istep)
	titrationstep=(istep>0?istep:deftitrationstep)
	titrationpts=Math.floor(titrationend/titrationstep)
	if(titrationpts>maxtitrationpts){
		alert("That would require "+titrationpts+" titration points. The maximum is "+maxtitrationpts)
		return false
	}
	if(!confirm(titrationpts+" data points will be collected, after each addition of "+titrationstep+" mL."))return false
	npts=0
	titrating=true
	quickgraph(iend)
	return true
}

function showequilinfo(iresetpH,ispt)
{
	var star=""
	var isactual=(titrating || showingequil)
	var safter=""
	sexplain=""
	sexplain0=""
	isbruteforce=(titrating||ispt?true:document.info.bruteforce[1].checked)
	if(!isactual)safter=tabulatespecies("("+eval(roundoff(mLtotal,2))+" mL total)")
	havesetratios=false
	thepH=getpH(iresetpH)
	if(!havesetratios)
	{
		var v=Math.abs(setratios(thepH))
		if(v/totalcharge>0.01)
		{
			s="<font color=#C00000><h3><p>A significant charge imbalance was detected! 1st-year methods may not be appropriate! Try using the mass/charge balance method.</font></h3>"
			star="?"
		}else{
			s="<p>No significant charge imbalance was detected. 1st-year methods are probably appropriate."
		}
		sexplain+=s
	}
	if (isactual)safter=tabulatespecies("("+eval(roundoff(mLtotal,2))+" mL total)")
	pHround=roundoff(thepH,2)
	s="<center>"+(showingonesoln?"<font color=#C00000>Solution "+ithissoln+"&nbsp;&nbsp;<br>":"")
	if(titrating)
	{
		s+="EQUILIBRIUM&nbsp;&nbsp;<br>"+safter
	}else{
		s+="<form><input type=radio "+(!isactual?"checked ":"")
		s+="onClick=\"parent.fraMain.setTimeout('doafter(false,"+ispt+")',200)\">Pre-"
		s+="<input type=radio "+(isactual?"checked ":"")
		s+="onClick=\"parent.fraMain.setTimeout('doafter(true,"+ispt+")',200)\">Post-"
		s+="Equilibrium<br>"+safter+"</form>"
	}
	writeinfo(parent.fraAfter,s)
	
	if(titrating){
		titrationinfo+=roundoff(mL_2,3)+","+pHround+"\n"
		quickpoint(mL_2,thepH)
	}
	salert="<h3>"+(titrating?"mL = "+roundoff(mL_2,2)+"</h3><h3>":"")+"pH = <font color="+pHcolor(thepH)+">"+pHround+"</font>"+star+"</h3>"
	salert+="<small>based on "+sexplain0+sexplain+"<small>"
	
	salert=strsub(salert,"\n","<br>")
	if(!titrating)salert=tabulateconcs()+salert
	if(!titrating)salert=salert+tabulatecharges()+tabulateratios()+tabulateQvals()
	if(ispt)salert+="<p><a href=javascript:parent.fraMain.showtitrationdata()>All Data</a>"
	writeinfo(parent.fraStatus,salert)
}

function showkas()
{
	var s="<pre><b>Ka</b>\n\n"
	var sb="<b>Kb</b>\n\n"
	for (var i=4;i<nspecies;i++)
	{
		if(ABtype[i] & 1)
		{
			if(Kanew[i])s+="&"
			if(Kaval[i]<1e100)s+=speciesname[i]+"="+roundoff(Kaval[i],-3)+"\n"
			if(Kanew[i])s+=";"
		}
		if(ABtype[i] & 2)
		{
			if(Kanew[i-1])sb+="&"
			if(Kaval[i-1]<1e100)sb+=speciesname[i]+"="+roundoff(Kw/Kaval[i-1],-3)+"\n"
			if(Kanew[i-1])sb+=";"
		}
	}
	s+="\n"+sb+"</pre>"
	s=strsub(s,"&;","")
	s=strsub(s,"&","<font color=#C00000>")
	s=strsub(s,";","</font>")
	s+="To design your own weak acid,<br>click on the <a href=\"javascript:void(parent.fraMain.setTimeout('donewacid()',200))\">new acid...</a> link."
	writeinfo(parent.fraStatus,s,false)
}

function showpt(imL){
	if(showingspecial){
		clearspecies()
		addspecialspecies(isoln1,1)
		addspecialspecies(isoln2,imL/mLtotal2)
		reactspecies()
		mL_2=imL
		showingonesoln=false
		showequilinfo(true,true)
	}else{
		doit(imL,true)
	}
}

function showsoln(iwhich)
{
	//iwhich=0 show both
	//iwhich=1 or 2 show it and other if not showingsolutions
	//iwhich=-1 or -2 show it in fraAfter
	//iwhich=-3 both in fraStatus
	
	var i=Math.abs(iwhich)
	if(i==0){showingsolns=false;i=2}
	var f=(i==2 && iwhich>=0?parent.fraStatus:parent.fraAfter)
	if(!showingsolns){showingsolns=true;showsoln(3-i)}
	var s=""
	if(i & 2)s+=tabulatesoln(2)
	if(i & 1)s+=tabulatesoln(1)
	writeinfo(f,s)
}

function showspecialform()
{
	var s="<small><form name=info>"
	s+="For solution <input type=radio name=pick value=1 checked=true onClick=parent.fraMain.dospecialselect(1)>1 or "
	s+="<input type=radio name=pick value=2 onClick=parent.fraMain.dospecialselect(2)>2..."
	s+="<br>...<a href='javascript:parent.fraMain.dospecialbefore()'>show principal species</a> "
	s+="<br>...<a href='javascript:parent.fraMain.dospecialpH(true)'>pre-/post-equilibrium detail</a> "
	// s+="<br>...<a href='javascript:void(parent.fraMain.setTimeout(\"dospecialload()\",200))'>load info from above</a> "
	s+="<table><tr><td><input type=text name=mL size=8 value=''> mL of"
	s+="<tr><td><input type=text name=M size=8 value=''> M "
	s+=addselect("subst","subs",slastsub,0)
	s+="</table>"
	
	s+="<a href='javascript:parent.fraMain.dospecialreplace()'>Replace</a> "
	s+="<a href='javascript:parent.fraMain.dospecialadd()'>Add</a> "
	s+="<a href='javascript:parent.fraMain.dospecialdelete()'>Remove</a> "
	
	s+="<p><a href=javascript:parent.fraMain.dospecialmix(0)>'Before Rxn' for mixed solutions</a>"
	s+="<br><a href=javascript:parent.fraMain.dospecialmix(1)>pH of mixed solutions</a>"
	s+="<br><a href=javascript:parent.fraMain.dospecialtitrate(2)>titrate solution 1 into solution 2</a>"
	s+="<br><a href=javascript:parent.fraMain.dospecialtitrate(1)>titrate solution 2 into solution 1</a>"
	s+="</form>"
	writeinfo(parent.fraBefore,s)
	
	s="For each solution, you can replace, add, or remove components as you like. "
	s+="Then check the principal species before reaction, check the pH of the mixture, "
	s+="or titrate one into the other and see what happens. Be creative!"
	writeinfo(parent.fraGraph,s)
}

function showtitrationdata(){
	
	Display=new Array()
	Display["PNG"]=-1  //disallows PNG
	Display["CSS"]=(allowps?0:-2)  //forces CSS (divgraph)
	Display["EPS"]=(allowps?1:-1)  //disallows EPS--ok if St. Olaf Cite
	
	var s="<b>Data</b><form name=info>"
	
	if(allowps){
		// s+="<input type=radio "+(ispng?" checked":"")+" name=o_display>PNG"
		s+=" <input type=radio "+(isjs?" checked":"")+" name=o_display><a href=\"javascript:alert('The Cascading Style Sheet option produces a graph using images found on this website.')\">CSS</a>"
		s+=" <input type=radio "+(iseps?" checked":"")+" name=o_display><a href=\"javascript:alert('The Encapulated PostScript option produces a graph that requires an external PostScript viewer.')\">EPS</a>"
	}
	s+="<br><a href=javascript:parent.fraMain.dograph()>setup plot</a>"
	s+="<br>mL added,pH<br><textarea name=xydata rows=8 cols=15>"+titrationinfo+"</textarea>"
	s+="<br>Click on a point on the graph for details."
	s+="</form>"
	stitle=strsub(soln[isoln2],"\n","|")+" added to|"+strsub(soln[isoln1],"\n","|")
	sxlabel=soln[isoln2]
	sxlabel=sxlabel.substring(sxlabel.indexOf(" ")+1,sxlabel.length)
	writeinfo(parent.fraStatus,s)
}

function specialcheck()
{
	if(titrating){stoptitration();return false}
	if(showingspecial)return true
	dospecial(0)
	return false
}

function specialgetcomponent(iwhich,icomp)
{
	var s=""
	var i=0
	sinfo=soln[iwhich]
	while(i<=icomp && sinfo.length)
	{
		s=extractinfo("\n")
		i++
	}
	ithiscomponent[iwhich]=icomp
	return s+"\n"
}

function specialsetcomponent(iwhich,icomp,swhat)
{
	var s=""
	var i=0
	var itotal=0
	if(swhat=="" && icomp<0)return
	sinfo=soln[iwhich]+"\n"
	if(soln[iwhich]=="")
	{
		snew=swhat
	}else{
		if(icomp<0)
		{
			sinfo=soln[iwhich]+swhat
			i=-1000
		}
		var snew=""
		while(i<=icomp && sinfo.length)
		{
			s=extractinfo("\n")+"\n"
			if(s.length){itotal++;snew+=(i==icomp?swhat:s)}
			i++
		}
		snew+=sinfo
		snew=strsub(snew,"\n\n","\n")
		if(snew.charAt(0)=="\n")snew=snew.substring(1,snew.length)
		if(i<0)ithiscomponent[iwhich]=itotal-1
	}
	if(snew.indexOf(" + ")==0)snew="0 mL of "+snew.substring(3,snew.length)
	soln[iwhich]=snew
}

function specialshow(){
	if(showingonesoln){
		dospecialpH(true)
	}else{
		showsoln(ithissoln)
	}
}

function specialtitr(imL)
{
	if(titrating && imL<0){imL=stoptitration(imL,true);if(imL==0)return}
	if(imL<0)
	{
		closeburetwindow()
		if(!setuptitration(mLtotal2))return
		imL=0
		openburetwindow()
	}
	clearspecies()
	addspecialspecies(isoln1,1)
	addspecialspecies(isoln2,imL/mLtotal2)
	reactspecies()
	mL_2=imL
	showingonesoln=false
	showequilinfo(true)
	npts++
	imL+=titrationstep
	imL=eval(roundoff(imL,3))
	if(imL>titrationend){imL=stoptitration(imL,true);if(imL==0)return}
	titrationcommand="specialtitr("+imL+")"
	isspecialtitration=true
	if(titrating)titrationtimer=setTimeout(titrationcommand,titrationdelay)
}

function stoptitration(imL,iask)
{
	titrating=false
	clearTimeout(titrationtimer)
	if(iask && !showingspecial){
		var s=prompt("Press 'Cancel' to stop the titration or 'OK' to continue to a new final number of mL.",titrationend)
		if(s!=null){
			titrationend=parseFloat(s)
			titrating=true
			return mLthis
		}
	}
	closeburetwindow()
	showtitrationdata()
	if(!showingspecial){soln[1]="";soln[2]=""}
	return 0
}

function tabulatecharges()
{
	var s="<p><b>Overall Charges (mmol)</b><table>"
	var i=0
	var v1=getoverallcharge(1)
	var v2=getoverallcharge(-1)
	var v3=getoverallcharge(0)
	s+="<tr><td>Positive</td><td align=right>+"+(v1>0.01?GRroundoff(v1,4):roundoff(v1,-4))+"</td></tr>"
	s+="<tr><td>Negative</td><td align=right>"+(v1>0.01?GRroundoff(v2,4):roundoff(v2,-4))+"</td></tr>"
	v3=(Math.abs(v3/(v1-v2))<1e-4?"&nbsp;0":v1>0.01?GRroundoff(v3,4):roundoff(v3,-4))
	s+="<tr><td><font color=red>Total</font></td><td align=right><font color=red>"+v3+"</font></td></tr>"
	s=s.replace(/\-/g,"&#150;")
	s+="</table>"
	return s
}

function tabulateconcs()
{
	var s="AT EQUILIBRIUM<p>"
	var i=0
	var v=0
	s+="<table>"
	v=mmol[H]/mLtotal//Math.pow(10,-eval(pHround))
	s+="<tr><td><small>H+</small></td><td align=right><small>"+roundoff(v,-3)+" M</small></td><td align=right><small>"+roundoff(v*mLtotal,-3)+" mmol</small></td></tr>"
	v=mmol[OH]/mLtotal//Math.pow(10,-14+eval(pHround))
	s+="<tr><td><small>OH-</small></td><td align=right><small>"+roundoff(v,-3)+" M</small></td><td align=right><small>"+roundoff(v*mLtotal,-3)+" mmol</small></td></tr>"
	
	for(var ii=4;ii<nspecies+4;ii++)
	{
		i=Order[ii % nspecies]
		if (i!=H && i!=OH && mmol[i]>0)
		{
			v=mmol[i]/mLtotal
			if(v>1e-20)s+="<tr><td><small>"+speciesname[i]+"</small></td><td align=right><small>"+roundoff(v,-3)+" M</small></td><td align=right><small>"+roundoff(mmol[i],-3)+" mmol</small></td></tr>"
		}
	}
	return s+"</table><p>"
}

function tabulateQvals()
{
	var s=""
	var Q=0
	var cH=Math.pow(10,-thepH)
	for(var i=iweakfirst;i<nspecies;i++)
	{
		HA=weaktype[i]
		if(HA!=i && mmolweak[HA] && Kaval[i-1]<1)
		{
			Q=cH*mmol[i]/mmol[i-1]
			s+="<tr><td>"+speciesname[i-1]+"<td> "+roundoff(Kaval[i-1],-3)+" <td> "+roundoff(Q,-3)+"<br>\n"
		}
	}
	s+="<tr><td>H2O<td> "+roundoff(Kw/55.5,-3)+" <td><font color=red> "+roundoff(mmol[H]/mLtotal*mmol[OH]/mLtotal/55.5,-3)+"</font><br>\n"
	return "<p>Ka should equal Q in each case:<p>\n<table><tr><th>species<th>Ka<th>Q"+s+"</table>"
}

function tabulateratios()
{
	var s=""
	var i=0
	var v=0
	for(var i=iweakfirst;i<nspecies;i++)
	{
		HA=weaktype[i]
		if(HA!=i && mmolweak[HA] && Kaval[i-1]<1)
		{
			v=mmol[i]/mmol[i-1]
			s+="<tr><td>["+speciesname[i]+"]/["+speciesname[i-1]+"]<td> "+roundoff(v,-3)+"<br>\n"
		}
	}
	if(s=="")return ""
	s="<p><b>Species Ratios</b><table>"+s+"</table>"
	return s
}

function tabulatesoln(iwhich)
{
	var s=(iwhich==ithissoln?"<font color=#C00000>":"")+"SOLUTION "+iwhich+"<form><pre>"
	//document.images[0].src=(iwhich==2?"sky.gif":"red.gif")
	//document.images[1].src=(iwhich==1?"sky.gif":"red.gif")
	var i=0
	var ish2o=0
	var sthis=""
	sinfo=soln[iwhich]
	while(sinfo.length)
	{
		s+="<input type=radio name=r "+(iwhich==ithissoln && i==ithiscomponent[iwhich]?"checked ":"")
		s+="onClick=parent.fraMain.setTimeout('dospecialpick("+iwhich+","+i+")',200)>"
		sthis=extractinfo("\n")+"\n"
		ish2o=sthis.indexOf(" M H2O\n")
		if(ish2o>=0)sthis=sthis.substring(0,sthis.indexOf(" of"))+" of H2O\n"
		s+=sthis
		i++
	}
	s+="<input type=radio name=r "+(iwhich==ithissoln && ithiscomponent[iwhich]<0?"checked ":"")
	s+="onClick=parent.fraMain.setTimeout('dospecialpick("+iwhich+",-1)',200)>new component\n"
	if(iwhich==ithissoln)s+="</font>"
	s+="</form></pre><p>"
	s+="<h3>pH = "+getsolnpH(iwhich)+"</h3>"
	return s
}

function tabulatespecies(slabel)
{
	var s="<table><tr><th>"+slabel
	var i=0
	for(var ii=4;ii<nspecies+4;ii++)
	{
		i=Order[ii % nspecies]
		if (mmol[i]>=.01)
		{
			
			var nrows=Math.floor(mmol[i]+.0001)
			s+="<tr><td valign=center bgcolor=#"+speciescolor[i]
			s+=" rowspan="+nrows+">"
			s+=roundoff(mmol[i],2)+" mmol "+speciesname[i]+"<td>&nbsp;"
			for (var irow=1;irow<nrows;irow++)s+="<tr><td>&nbsp;"
			s+="\n"
		}
	}
	s+="</table>"
	return s
}

function titratefaster(){
	if(titrationdelay<10)return
	titrationdelay/=2
}

function titratelarger(){
	titrationstep*=2
}

function titrateslower(){
	if(titrationdelay>4000)return
	titrationdelay*=2
}

function titratesmaller(){
	titrationstep/=2
}

function xsolve(K,mmoles)
{
	//direct quadratic solution
	return (-K+Math.pow(K*K+4*K*mmoles/mLtotal,0.5))/2
}

