//Copyright (c) 2009 Robert M. Hanson, St. Olaf College, Northfield, MN 55057
//all rights reserved. Contact hansonr@stolaf.edu for permission to use.

//the charge on the fully protonated species is first,
//then the pKa values, IN INCREASING ORDER

AAs=new Array([]
,["Alanine","ala",1,2.34,9.69]
,["Cysteine","cys",1,1.71,8.33,10.78]
,["Glutamate", "glu",1,2.19,4.25,9.67]
,["Histidine", "his",2,1.8,6.04,9.17]
,["Lysine", "lys",2,2.18,8.95,10.79]
,["Tyrosine", "tyr",1,2.2,9.11,10.7]
)

function setSelect() {
	var s = "<select name=aao id=aa onchange=donew()>"
	for (var i = 1; i < AAs.length; i++)
		s += "<option id=aa" + i + " value=" + AAs[i][1] + (i == 0 ? " selected" : "") + ">" + AAs[i][0] + "</option>"
	s += "</select>"
	document.write(s)
}

function findaa(sname){
	for(var i=1;i<AAs.length;i++)
		if(AAs[i][1]==sname)return i
	return 1
}

x0=50+4
xunit=(484-54)/14
phtop=140

function setStyles() {
	var s = "<style>"
	+"#arrow {z-index:1;position:absolute;left:50;top:" + (phtop - 2) + "}"
	+"#ph0 {z-index:1;position:absolute;left:20;top:" + (phtop + 40+20)+"}"
	+"#ph {z-index:1;position:absolute;left:50;top:" + (phtop + 30+20)+"}"
	+"#pKa0 {z-index:1;position:absolute;left:20;top:" + (phtop + 70+20)+"}"
	+"#pKa1 {z-index:1;position:absolute;left:50;top:" + (phtop + 50+20)+"}"
	+"#pKa2 {z-index:1;position:absolute;left:50;top:" + (phtop + 50+20)+"}"
	+"#pKa3 {z-index:1;position:absolute;left:50;top:" + (phtop + 50+20)+"}"
	+"#display {z-index:1;position:absolute;left:50;top:" + (phtop + 110+20)+"}"
	+"#cover {z-index:1000;position:absolute;left:0;top:" + (phtop)+"}"
	+"</style>"
	document.write(s)
}setStyles()

defpH=7
defaa="his"
thisfile=""

function setdefaults(){
	var S = (unescape(document.location) + "?" + defaa + "@" + defpH).split("?")
	thisfile = S[0]
	S = S[1].split("@")
	defaa = S[0]
	defpH=parseFloat(S[1])
}setdefaults()

function doload(){
	var d=div('aa')
	var di=0
	for(var i=0;i<d.length;i++){
		if(d[i].value==defaa){
			setTimeout("div('aa').selectedIndex="+i+";docalc(defpH,true)",100)
			return
		}
	} 
}

function donew(){
	window.location=thisfile+"?"+sel('aa')+"@"+roundoff(defpH,2)
}

function checkpH(){
	if (!mousedown)return
	if(mouseX < 54 || mouseX > 424)return
	if(mouseY < phtop || mouseY > phtop + 50+25)return
	setTimeout("docalc(pHof(mouseX, mouseY > phtop+60),false)",50)
}

function docalc(pH,doall){
 var iaa=findaa(defaa)
 var B=new Array()
 if(doall)showpKa(AAs[iaa])
 var charge=calcCharge(pH,AAs[iaa],B)
 showgraph(pH,AAs[iaa],B,-1)
 setpH(pH)
 setExplain("<a href=javascript:findpI(1)>Calculate pI</a>","white")
}

function pHof(x, doround){
	pH= (x-x0)/xunit
	if (Math.abs(pH-Math.floor(pH))<0.05)pH=Math.floor(pH)
	if (Math.abs(pH-Math.floor(pH))>0.95)pH=Math.floor(pH)+1
	return (doround ? Math.round(pH) : pH)
}

function xof(pH){
	return x0 + pH * xunit
}

function setExplain(info,color) {
	writediv("explain",info)
	div("explain").style.backgroundColor = color
}

function findpI(isExact){
 //pseudosimplex really unnecessary, but interesting
 var A = AAs[findaa(defaa)]
 var B=new Array()
 var charge0 = A[2]
 var pH1=A[charge0 + 2]
 var pH2=A[charge0 + 3]
 var charge1=calcCharge(pH1,A,B)
 var charge2=calcCharge(pH2,A,B)
 var n=0
 var info = "<table cellpadding=3><tr><td width=250>pI is calculated as the average of the p<i>K</i>a values of the forms of the amino acid having 1+ and 0 charge. " 
	+ "<br><br>" + A[0] + " in its fully protonated form has a charge of " + chargeof(charge0,0) 
	+ ". So for the pI we average its <b>" + (charge0 == 1 ? "first and second" : "second and third")
	+ "</b> pKa values: <br><br><center>pI = (<b><font color=blue>"+pH1+"</font></b> + <b><font color=blue>"+pH2+"</font></b>)/2 = "
	+ "<b><font color=red>" + roundoff((pH1 + pH2)/2, 2) + "</font></b></center></td></tr></table>"
 setExplain(info,"yellow")
 if (!isExact) {
	charge2 = calcCharge(pH2 = (pH1 + pH2)/2, A, B)
	n = 20
 }

 while(n<20 && Math.abs(charge2)>0.0005){
	if(charge2>0){
		pH2+=(pH2-pH1)/2
		pH1=(pH2+pH1)/2
		charge1=calcCharge(pH1,A,B)
	}else{if(charge1<0){
		pH1-=(pH2-pH1)/2
		pH2=(pH2+pH1)/2
		charge1=calcCharge(pH1,A,B)
	}else{
		pH2=(pH2+pH1)/2
	}}
	charge2=calcCharge(pH2,A,B)
	n++
 }
 showgraph(pH2,A,B,charge0)
 setpH(pH2)
}

function calcCharge(pH,A,B){
 defpH=pH
 B[A.length-2]=1
 var total=1
 B[0]=0
 for (var i=A.length-1;i>2;i--){
	B[i-2]=Math.pow(10,A[i]-pH)*B[i-1]
	total+=B[i-2]
 }
 for (var i=1;i<B.length;i++){
	B[i]=B[i]/total
	B[0]+=B[i]*(A[2]-i+1)
 }
 return B[0]
}

function showpKa(AA){
	movedivleft("pKa3",-200)
	for(var i=3;i<AA.length;i++){
 		movedivleft("pKa"+(i-2),xof(AA[i])-10)
		writediv("pKa"+(i-2),"<img src=img/arrow2.gif><br>"+AA[i])
	}
}

function showgraph(pH,AA,B,charge0){
 var charge=chargeof(B[0],3)
 var s=""
 s="<hr><table>"
 s+="<tr><td colspan=10 align=center><h3>pH " + roundoff(pH,2) + "</h3>"
 s+="AVERAGE CHARGE = <font color="+(charge == "0" ? "black" : charge.indexOf("+") == 0 ? "red" : "blue") + ">" + charge + "</font></td></tr>"

 s+="<tr><td valign=bottom>Percent<br>of Each Form<br>in Solution:</td>"
 for (var i=1;i<B.length;i++){
	s+="<td align=center valign=bottom><img src=img/red.gif width=20 height="+Math.floor(B[i]*50+1)+"></td>"
 }
 s+="<td align=center valign=bottom><img src=img/white.gif height=50 width=20></td>"
 s+="</tr>"

 s+="<tr><td></td>"
 for (var i=1;i<B.length;i++){
	s+="<td align=center>"+(B[i]<0.001?"<0.1":roundoff(B[i]*100,1))+"%</td>"
 }
 s+="</tr>"

 s+="<tr><td>Charge:"
 for (var i=1;i<B.length;i++){
	var c = (charge0 < 0 || !(i == charge0 || i == charge0 + 1) ? "black" : "blue")
	s+="<td align=center style='font-size:16pt;color:" + c + ";font-weight:bold'>"+chargeof(AA[2]-i+1,0)+"<br><img src=img/white.gif height=1 width=50></td>"
 }
 s+="</tr>"

 s+="<tr><td></td>"
 for (var i=1;i<B.length-1;i++){
	var c = (charge0 < 0 || !(i == charge0 || i == charge0 + 1) ? "black" : "blue")
	s+="<td align=center style='font-size:16pt;color:" + c + ";font-weight:bold'>p<I>K</I><sub>a</sub> = "+AA[i+2] + "</td>"
 }
 s+="</tr>"

 s+="<tr><td></td>"
 for (var i=1;i<B.length;i++){
	s+="<td align=center><img width=182 height=260 src=img/"+AA[1]+(AA[2]-i+1)+".gif></td>"
 }
 s+="</tr>"
 s+="</table>"
 writediv("display",s)
}

function setpH(pH){
 movedivleft("arrow",xof(pH)-10)
 writediv("arrow",roundoff(pH,2) + "<br>" + "<img src=img/arrow.gif>")
}

//math,string utilities
function roundoff(x,ndec)
{
 //round x to ndec decimal places (+) fixed; (-) floating
 if(x==0)return 0
 if(ndec==0)return Math.round(x)
 var neg=(x<0?"-":"")
 var xs=Math.abs(x)+""
 var i=(xs.indexOf("E") & xs.indexOf("e"))
 if(ndec<0 && i<0)
 {
  var xs=roundoff(Math.abs(x)*1e-100,-ndec)
  var i=(xs.indexOf("E") & xs.indexOf("e"))
  var e=(eval(xs.substring(i+1,xs.length))+100)
  return neg+xs.substring(0,i)+(e!=0?"E"+e:"")
 }
 if (i>0)
 {
  var s=roundoff(xs.substring(0,i),Math.abs(ndec)-1)+"E"+xs.substring(i+1,xs.length)
  return neg+s 
 }
 i=xs.indexOf(".")
 if (i<0) 
 {
  xs=xs+"."
  i=xs.indexOf(".")
 }
 xs=xs+"000000000"
 var s="."+xs.substring(i+1+ndec,xs.length)
 xs=xs.substring(0,i)+xs.substring(i+1,i+1+ndec)
 var add1=(xs.charAt(0)=="0")
 if(add1)xs="1"+xs
 xs=eval(xs)+Math.round(eval(s))+""
 if(add1)xs=xs.substring(1,xs.length)
 xs=xs.substring(0,xs.length-ndec)+"."+xs.substring(xs.length-ndec,xs.length)
 if(xs.substring(0,1)==".")xs="0"+xs
 return neg+xs
}

function chargeof(c,n){
	if (Math.abs(c)<0.001)c=0
	if (c == 0) return "0"
	var sc = roundoff(Math.abs(c),n)
	var s = (c > 0 ? "+" : "&#150;")
	return (n == 0 ? sc + s : s + sc)
}

// div stuff //

function div(id){
 return  document.getElementById(id)
}

function sel(id) {
 var d = div(id);
 return d[d.selectedIndex].value
}

function writediv(id,sinfo){
 div(id).innerHTML = sinfo
}

function movedivleft(id,left){
 var ds=div(id)
 ds.style.left = left
}

var mouseX = 0;
var mouseY = 0;
var mouse_clientx;
var mouse_clienty;

function getCoordinates(evt) {
	if (!evt)return
	var scrollTop = window.pageYOffset || document.documentElement.scrollTop || 0
	mouseX = (evt.pageX ? evt.pageX : document.body.scrollLeft+evt.clientX);
	mouseY = (evt.pageY ? evt.pageY : scrollTop+evt.clientY);
	mouse_clientx =(document.body.scrollLeft? mouseX - document.body.scrollLeft : evt.clientX);
	mouse_clienty =(scrollTop? mouseY - scrollTop : evt.clientY);  
	//  Safari: clientY is not dependable.
}

mousedown=false
function domouseup(){
	mousedown=false
}

function domousemove(e){
	if (!mousedown)return
	if (!e) e = window.event
	var src = (!e ? 0 : e.target ? e.target : e.srcElement)
	var id = (src && src.id ? src.id : "")
	if (id.indexOf("aa") == 0)return
	getCoordinates(e)
	domousedown(e)
}

function domousedown(e){
	if (!e) e = window.event
	var src = (!e ? 0 : e.target ? e.target : e.srcElement)
	var id = (src && src.id ? src.id : "")
	if (id.indexOf("aa") == 0)return
	getCoordinates(e)
	mousedown=true;
	checkpH()
}

document.addEventListener("mousedown",domousedown,true)
document.addEventListener("mouseup",domouseup,true)
document.addEventListener("mousemove",domousemove,true)


