
//Buffer Calculator Script follows
<!-- 
      // initialise global variables and objects
      //========================================
      var I=0      // ionic strength
      var pH=0   // pH of buffer
      var pKa=0// pKa of chosen buffer
      var TU=0   // temperature at which buffer is used
      var TP=0   // temperature at which prepared
      var za=0   // charge on acid species
      var C=0    // total concentration of buffer species
      var Salt=""
      var chosen=""
      var totalBuffers=0 
      var name = ""
      //  set up buffer objects
      //  Bxx= new doBuffer("name",pKa,dpKadt,za,common substance,Mr)
      //  NB! Edit select list in form too!!
         var B01= new doBuffer("Maleate",2.0,0,0,"Maleic acid",116.1)
         var B02= new doBuffer("Phosphate",2.15,0.0044,0,"Phosphoric acid",98.0)
         var B03= new doBuffer("Chloroacetate",2.88,0.0023,0,"Chloroacetic acid",94.5)
	 	 	   var B04= new doBuffer("Citrate",3.14,0,0,"Citric acid, monohydrate",210.1)
         var B05= new doBuffer("Formate",3.75,0,0,"Formic acid",46.03)
	 				var B06= new doBuffer("Succinate",4.19,-0.0018,0,"Succinic Acid",118.1)
         var B07= new doBuffer("Benzoate",4.2,0.018,0,"Benzoic acid",122.1)
         var B08= new doBuffer("Acetate",4.76,-0.0002,0,"Acetic acid",60.05)
				 var B09= new doBuffer("Citrate",4.76,-0.0016,-1,"Citric acid, monohydrate",210.1)
         var B10= new doBuffer("Propionate",4.86,-0.0002,0,"Propionic acid",74.05)
         var B11= new doBuffer("Pyridine",5.23,-0.014,1,"Pyridine",79.10)
         var B12= new doBuffer("Piperazine",5.55,-0.015,0,"Piperazine",86.14)
	 				var B13= new doBuffer("Succinate",5.57,0,-1,"Succinic Acid",118.1)
         var B14= new doBuffer("MES",6.21,-0.011,0,"MES free acid",195.2)
	 				var B15= new doBuffer("Carbonate",6.37,-0.0055,0,"Carbonic acid",62)
	 				var B16= new doBuffer("Citrate",6.39,0,-2,"Citric acid, monohydrate",210.1)
         var B17= new doBuffer("Bis-tris",6.46,-0.02,1,"Bis-tris base",209.2)
         var B18= new doBuffer("ADA",6.91,-0.011,-1,"ADA free acid",190.2)
         var B19= new doBuffer("PIPES",7.1,-0.0085,-1,"PIPES free acid",302.4)
         var B20= new doBuffer("ACES",6.99,-0.02,0,"ACES free acid",182.2)
         var B21= new doBuffer("BES",7.26,-0.016,0,"BES free acid",213.3)
         var B22= new doBuffer("MOPS",7.31,-0.011,0,"MOPS free acid",209.3)
         var B23= new doBuffer("Phosphate",7.2,-0.0028,-1,"e.g. NaH2PO4",120.0)   
         var B24= new doBuffer("TES",7.61,-0.02,0,"TES free acid",229.25)
         var B25= new doBuffer("HEPES",7.66,-0.014,0,"HEPES free acid",238.3)
         var B26= new doBuffer("Tris",8.06,-0.028,1,"Tris base",121.14)
         var B27= new doBuffer("Tricine",8.26,-0.021,0,"Tricine free acid",179.2)
         var B28= new doBuffer("Bicine",8.46,-0.018,0,"Bicine free acid",163.2)
         var B29= new doBuffer("TAPS",8.51,-0.02,0,"TAPS free acid",243.3)
          var B30= new doBuffer("Ethanolamine",9.5,-0.029,1,"Ethanolamine",61.1)
          var B31= new doBuffer("CHES",9.41,-0.018,0,"CHES free acid",207.3)
	  			var B32= new doBuffer("Carbonate",10.25,-0.0090,-1,"Carbonic acid",62)
          var B33= new doBuffer("CAPS",10.51,-0.018,0,"CAPS free acid",221.3)
          var B34= new doBuffer("Methylamine",10.62,-0.031,1,"Methylamine",31.1)
          var B35= new doBuffer("Piperidine",11.12,-0.031,1,"Piperidine",85.15)
          var B36= new doBuffer("Phosphate",12.33,-0.026,-2,"e.g. Na2HPO4.7H2O",268.07)
         var B37= new doBuffer(null,null,null,null,null,null)
      
			function doBuffer(name,pKa,tc,za,S,M) {
         this.name=name;
         this.pKa=pKa;
         this.dpKadT=tc;
         this.za=za;
         this.Subs=S;
         this.Mr=M;
         }
         
      function getza(i) {
         i++
         var ti = "";
         if (i<10) ti="0"+i
         else ti = "" + i;
         p = eval("B" + ti +".za")
         return(p)
         }
         
      function getpKa(i) {
         i++
         var ti = "";
         if (i<10) ti="0"+i
         else ti = "" + i;
         p = eval("B" + ti +".pKa")
         return(p)
         }
         
      function gettc(i) {
         i++
         var ti = "";
         if (i<10) ti="0"+i
         else ti = "" + i;
         p = eval("B" + ti +".dpKadT")
         return(p)
         }
         
      function getSubstance(i) {
         i++
         var ti = "";
         if (i<10) ti="0"+i
         else ti = "" + i;
         p = eval("B" + ti +".Subs")
         return(p)
         }
         
      function getMr(i) {
         i++
         var ti = "";
         if (i<10) ti="0"+i
         else ti = "" + i;
         p = eval("B" + ti +".Mr")
         return(p)
         }
         
         
      // Takes each 'select' list and 
      // finds the one selected..
      
      function getSelected(obj) {
         var index = obj.selectedIndex;
         return(index)
         }
         
      // use a second order polynomial to approximate (*very* closely)
      // the Debye Huckel parameter at any temperature
      
      function DebyeHuckel(temp) {
         DH = (0.4918 + 0.0006614 * temp + 0.000004975 * (temp*temp))
         return DH
         }
         
      // calculate the ionic strength due to all buffer species
      // N.B. assumes that all counterions are monovalent
      // e.g. Na+,K+,Cl-
      // z = charge on acid species
      // Conc = total concentration of all buffer species
      // I = ionic strength due to buffer species
      
      function CalculateI(pH,pK,z,Conc) {
         R = Math.pow(10,(pH-pK));
         I1 = (R/(1+R)) * Conc * (Math.pow((z-1),2));    // basic species
         I2 = (1/(1+R)) * Conc * (Math.pow(z,2));        // acidic species
         I3 = (R/(1+R)) * Conc * (Math.abs(z-1));        // counterion, basic species
         I4 = (1/(1+R)) * Conc * (Math.abs(z));          // counterion, acidic species
         I = (I1 + I2 + I3 + I4)/2;
         return I
         }
      
      // makes temperature correction to pKa
	  // only need to be called once for each buffer
	  function tempcomp(pK,dpKadT,T) {
	  pKp = (pK + (dpKadT * (T-25)))  // temp correction first
	  return pKp
	  }
	  
	  
	  // calculates the pKa' from pKa according to
      // Debye-Huckel modifications
      // T = buffer used temperature in centigrade
      function newpKa(pK,I,T,z) {
         var corr = 0
         var P = 0
         A = DebyeHuckel(T)
         corr = (2 * z - 1) * ((A * Math.sqrt(I))/(1 + Math.sqrt(I)) - (0.1 * I))
         P = pK + corr
         return P
         }
         
      // optimises a pKa by cyclical variation of I and pka calculations
      // for when no control of ionic strength
         
      function optpKa(pKa,T,z,C,pH) {
         var pKtemp1 = pKa
         var pKtemp2 = 0
         var d = 10
         var Itemp = 0
         while (d > 0.0001) {
            Itemp   = CalculateI(pH,pKtemp1,z,C)
            pKtemp2 = newpKa(pKa,Itemp,T,z)
            d       = Math.abs(pKtemp1 - pKtemp2)
            pKtemp1 = pKtemp2
            }
         return(pKtemp2)
         }
         
      function howMuchSalt(s,Is,V) {
         if (s == "NaCl" ) txt = "Add " + numR((Is * 58.55 * V/1000),3) + " g NaCl."
         if (s == "KCl"  ) txt = "Add " + numR((Is * 74.56 * V/1000),3) + " g KCl."
         if (s == "Na2SO4")txt = "Add " + numR((Is/3 * 142.0 * V/1000),3) + " g Na2SO4."
         if (s == "Other salt")  txt = "Add salt to I = " + numR(Is,3) + " M."
         return(txt)
         }
         
      // rounding function 
	  function numR(n,d) {
         var p = parseInt(Math.pow(10,d))
         var t = parseInt(n * p)
            t = t/p
         return(t)
         }
         
      // textport
      // We'll need scrollbars, since the output is quite large
	function textWindow(url,wname,text) {
					document.clear();
					document.writeln(text);
         }
         
         
      //  ===============================
      //    MAIN ROUTINES START HERE!!!
      
      function calcBuffer(frm) {
         
         var Vi = getSelected(frm.V)                           // index to volume required
         var thisBuffer = getSelected(frm.chosen)              // index to buffer selected
         var Salti= getSelected(frm.Salt)                      // index to salt needed
         
         var Vol  = frm.V.options[Vi].text                     // volume?
         var name = frm.chosen.options[thisBuffer].text        // name?
         var Salt = frm.Salt.options[Salti].text               // which extra salt?
         
         var TP  = frm.TP.value                                // Temp prep
         var TU  = frm.TU.value                                // Temp use
         var pH  = frm.pH.value                                // pH needed
         var I   = frm.I.value/1000                            // I needed (mM -> M)
         var C   = frm.C.value/1000                            // Conc needed (mM -> M)
         var Ico = frm.IC.checked                              // I control? (T/F)
         
         var za  = getza(thisBuffer)                           // za
         var pKa = getpKa(thisBuffer)                          // pKa
         var dpKadT = gettc(thisBuffer)                        // dpKa/dT
         var Subst  = getSubstance(thisBuffer)                 // Common substance
         var Mr  = getMr(thisBuffer)                           // MW of common substance
         var pKaPrime = 0                                      // corrected pKa
         var MolAcid  = 0                                      // mol acid species
         var MolBase  = 0                                      // mol basic species
         var MolSalt  = 0                                      // mol neutral salt species
         var SaltMass = ""                                     // weight, in words
         var IBuffer  = 0                                      // Ionic strength due to buffer
         var ISalt = 0                                         // Ionic strength due to Salt
         
      //  now all parameters and conditions set in global variables...
      //  start range checking
      
         helptext = "\n" + "Buffer calculation error:" + "\n"
         var cancelBuffer=false
         
         if (Math.abs(pH-pKa) > 1) {
         alert(helptext + name + " is not suitable at pH " + pH + "\n" + "(You should use a buffer with a pKa' +/-1 pH unit of your chosen value)");
         cancelBuffer = true;
            }
         if ((TU<0) || (TU>60)) {
            alert(helptext + "The temperature exceeds the program limits of 0-60 degrees C!");
            cancelBuffer = true;
	  }
         if (C<0.005){
	  alert("Hmm..homeopathy! The concentration is too low for a meaningful buffer, or for the calculations to be reliable.");
            cancelBuffer = true;
            }
	  
	  if (C>2){
	  alert("Now you're just being silly!");
	  cancelBuffer = true;
            }
         
      //buffer passes checks..now start calculations
      //Do we want to control ionic strength (Ico)
	  //If ionic strength is set by the user...
         
         if (Ico == true) {
            pKaP     = tempcomp(pKa,dpKadT,TU);
	  pKaPrime = newpKa(pKaP,I,TU,za);
            IBuffer  = CalculateI(pH,pKaPrime,za,C);
            ISalt    = I - IBuffer;
            SaltMass = howMuchSalt(Salt,ISalt,Vol);
            } 
            
	//If ionic strength is solely due to buffer species
	
         else {
             pKaPrime = tempcomp(pKa,dpKadT,TU);
	   pKaPrime = optpKa(pKaPrime,TU,za,C,pH);
             I = CalculateI(pH,pKaPrime,za,C);
             IBuffer = I;
             SaltMass = "(No added neutral salts, I due to buffer alone.)";
             }
            
         if ((Ico == true) && (IBuffer >= I)) {
            alert("\n" + helptext + "\n" + "The ionic strength due to the buffer species will exceed the I needed!\n");
            cancelBuffer = true}
         
         var R = Math.pow(10,(pH-pKaPrime));
         var molBase = C * R/(1 + R) * Vol/1000;
         var molAcid = C * 1/(1 + R) * Vol/1000;
         var BuffMass= C * Mr  * Vol/1000
         
         var LabpKa = pKaPrime + (dpKadT * (TP - TU))
         var LabpH  = LabpKa + Math.log(R)/2.3
         
       tx  = "<HTML><HEAD><TITLE>Buffer Recipe</TITLE><link rel=stylesheet type=text/css href=/styles/bioweb.css>"
			 tx += "</HEAD>"
       tx += "<BODY BGCOLOR=#FFFFFF MARGINHEIGHT=4 MARGINWIDTH=4 TOPMARGIN=8 LINK=#000000 ALINK=#FF9933 VLINK=#999999><FONT SIZE='2' FACE='Verdana, arial, sans-serif'>"    
       tx += "<TABLE BORDER=0 CELLPADDING=4 CELLSPACING=0 WIDTH=100%><TR><TD valign='top' BGCOLOR=CCCCCC>&nbsp;&nbsp;&nbsp;&nbsp;<FONT FACE='verdana' SIZE='2' COLOR='black'><A HREF='javascript:history.back()'><IMG SRC='backbutton.gif' WIDTH='25' HEIGHT='25' BORDER='0' ALT='back' NAME='back'></A></FONT>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT FACE='verdana' SIZE='2' COLOR='black'><A HREF='javascript:self.close()'><IMG SRC='closebutton.gif' WIDTH='25' HEIGHT='25' BORDER='0' ALT=Close></A></FONT></TD><TD BGCOLOR=#CCCCCC ALIGN=left>&nbsp;<FONT FACE=verdana, arial, sans-serif SIZE=2 CLASS=hedL01><STRONG>Buffer Recipe</STRONG></FONT></TD></TR></TABLE>"
       tx += "<TABLE BORDER=0 BGCOLOR=000000 CELLPADDING=0 CELLSPACING=0 WIDTH=100%><TR BGCOLOR=000000><TD><TABLE BGCOLOR=000000 BORDER=0 CELLPADDING=4 CELLSPACING=1 WIDTH=100%><TR BGCOLOR=FFFFFF><TD VALIGN=TOP><B><FONT FACE='verdana, arial, sans-serif' size='-1'>Buffer:</B></TD><TD><FONT FACE='verdana, arial, sans-serif' size='2'><UL>To make " +  Vol + " ml of " + numR(C,3) + " M " + name + " Buffer: <LI>pH= " + numR(pH,3) + "<SUB>&nbsp</SUB></LI>" 
       tx += "<LI>Ionic strength = " + numR(I,3) + " M<SUB>&nbsp</SUB>" + "(Ionic strength due to the buffer = " + numR(IBuffer,3) + "M<SUB>&nbsp;</SUB>)</LI>" 
       tx += "<LI>Thermodynamic p<ITALIC>K</ITALIC><SUB>a</SUB> = " + numR(pKa,2) + ", Apparent p<ITALIC>K</ITALIC><SUB>a</SUB>' = " + numR(pKaPrime,2) + "</LI>"
       tx += "<LI>Temperature coefficient = " + numR(dpKadT,4) + " per <SUP>o</SUP>C" + "<BR></LI>"
       tx += "<LI>Prepared at " + numR(TP,1) +"<SUP>o</SUP>C, used at " + numR(TU,1) + " <SUP>o</SUP>C</LI></UL>"
       tx += "</TD></TR>"
       tx += "<TR BGCOLOR=FFFFFF><TD VALIGN=TOP><font face='verdana' size='-1'><B>Recipe:</B></TD>"
       tx += "<TD><font face='verdana' size='-1'><UL><LI>Dissolve " + numR(BuffMass,3) + " g of " + Subst + " (Mr = " + Mr + ")"
       tx += " in approx. " + numR((0.9 * Vol),3) + " ml of pure water.</LI>"
       tx += "<LI>" + SaltMass + "</LI>"
       tx += "<LI>Titrate to pH " + numR(LabpH,2) + " at the lab temperature of " + TP + "<SUP>o</SUP>C"
       tx += " with monovalent strong base or acid as needed. </LI>"
       tx += "<LI>Make up volume to " + Vol + " ml with pure water</LI>"
       tx += "<LI>Buffer will, of course, be pH " + pH + " at " + TU + "<SUP>o</SUP>C</LI></UL></TD></TR>"
       tx += "<TR BGCOLOR=FFFFFF><TD VALIGN=TOP><B><font face='verdana' size='-1'>Alternative Recipe:</B></TD>"
       tx += "<TD><font face='verdana' size='-1'><UL>" +  "<LI>Dissolve " + numR(molAcid,4) + " mol of acid component</LI>"
       tx += "<LI>Dissolve " + numR(molBase,4) + " mol of basic component</LI>"
       tx += "<LI>" + SaltMass + "</LI>"
       tx += "<LI> Make up to " + Vol + " ml with pure water</LI></UL></TD></TR>"
			 tx += "</TABLE></TD></TR></TABLE></BODY></HTML>"
         
       if (cancelBuffer) {
          cancelBuffer=false}
          else {
             textWindow('','Recipe',tx) // and finally, display the recipe!!
                }
          }
          
      //    MAIN ROUTINES END HERE!!!
      //  ===============================
      <!-- -->
