//adapted by PGPotvin from formula.cpp
// --------------------------------- globals -----------------------------------------
var ALLELEMENTS = 111;
var TRUEELEMENTS = 104;
var ANALYZABLEELEMENTS = 10;
var ElementTable = new Array("C", "H", "Ac", "Ag", "Al", "Am", "Ar", "As", "At", "Au", "B", "Ba", "Be", "Bi", "Bk", "Br", "Ca", "Cd", "Ce", "Cf", "Cl", "Cm", "Co", "Cr", "Cs", "Cu", "D", "Dy", "Er", "Es", "Eu", "F", "Fe", "Fm", "Fr", "Ga", "Gd", "Ge", "He", "Hf", "Hg", "Ho", "I", "In", "Ir", "K", "Kr", "La", "Li", "Lr", "Lu", "Md", "Mg", "Mn", "Mo", "N", "Na", "Nb", "Ne", "Ni", "No", "Np", "O", "Os", "P", "Pa", "Pb", "Pd", "Pm", "Po", "Pr", "Pt", "Pu", "Ra", "Rb", "Re", "Rh", "Rn", "Ru", "S", "Sb", "Sc", "Se", "Si", "Sm", "Sn", "Sr", "T", "Ta", "Tb", "Tc", "Te", "Th", "Ti", "Tl", "Tm", "U", "V", "W", "Xe", "Y", "Yb", "Zn", "Zr", "Me", "Et", "Pp", "Bu", "Ph", "Ts", "Cp");
var AnalyzableElementTable = new Array(0, 1, 15, 20, 31, 42, 55, 62, 64, 79);
var pctObs = new Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var pct = new Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var pctdiff = new Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var pctmin = new Array(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
var AtomicWtTable = new Array(12.011, 1.00794, 227.0, 107.898, 26.98154, 243.0, 39.948, 74.9216, 210.0, 196.9665, 10.81, 137.34, 9.01218, 208.9804, 249.0, 79.904, 40.08, 112.40, 140.12, 251.0, 35.453, 247.0, 58.9332, 51.996, 132.9054, 63.546, 2.0, 162.50, 167.26, 254.0, 151.96, 18.9984, 55.847, 253.0, 223.0, 69.72, 157.25, 72.59, 4.00260, 178.49, 200.59, 164.9304, 126.9045, 114.82, 192.22, 39.098, 83.80, 138.9055, 6.941, 257.0, 174.97, 256.0, 24.305, 54.9380, 95.94, 14.0067, 22.98977, 92.9064, 20.179, 58.71, 259.0, 237.0, 15.9994, 190.2, 30.97376, 231.0359, 207.2, 106.4, 147.0, 210.0, 140.0977, 195.09, 242.0, 226.0254, 85.4678, 186.23, 102.9055, 222.0, 101.07, 32.06, 121.75, 44.9559, 78.96, 28.086, 150.4, 118.69, 87.62, 3.0, 180.9479, 158.9254, 98.9062, 127.60, 232.0381, 47.90, 204.37, 168.9342, 238.029, 50.9414, 183.85, 131.30, 88.9059, 173.04, 65.38, 91.22);
var Equivalents = new Array("(CH3)", "(C2H5)", "(C3H7)", "(C4H9)", "(C6H5)", "(C7H7SO2)", "(C5H5)");
function AnElementObj(PositionInTable,Subscript) {
	this.PositionInTable = PositionInTable;
	this.Subscript = Subscript;
}
Elements = new Array();
var bVer = parseInt(navigator.appVersion); 
var NS4 = (document.layers) ? 1 : 0; 
var IE4 = (document.all) ? 1 : 0; 
if (!IE4 && !NS4) {
// browser unsupported
}
function pop() {
  var lastElement = this[this.length - 1];
  this.length--;
  return lastElement;
}
if (IE4 && bVer < 5.5) {
  Array.prototype.pop = pop;
}
function Molecule() {
	this.Formula = new String("");
	this.SubscriptTable = new Array(104);
	var i;
	for(i=0;i<104;i++) {this.SubscriptTable[i]=0;}
	this.MW = 0.0;
}
var MainMolecule = new Molecule();
var Impurity1Molecule = new Molecule();
var Impurity2Molecule = new Molecule();
var Impurity3Molecule = new Molecule();
// --------------------------------- end globals -----------------------------------------

function fix2(nbr)
{
	return Math.round(nbr*100)/100;
}

function Analyze(Stage)
{
	var ResultWindow;
	if(!ProcessFormula(window.document.FormulaAnalysis.inFormula.value,MainMolecule)) return;
	var EofU = 0.0;
	
	var j=0;
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inC.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inH.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inBr.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inCl.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inF.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inI.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inN.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inO.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inP.value)
	pctObs[j++] = parseFloat(window.document.AnalysisResults.inS.value)

	EofU = (MainMolecule.SubscriptTable[0]*2+2 -(MainMolecule.SubscriptTable[1]+MainMolecule.SubscriptTable[15]+MainMolecule.SubscriptTable[20]+MainMolecule.SubscriptTable[31]+MainMolecule.SubscriptTable[42]-MainMolecule.SubscriptTable[55]))/2;

	if (Stage==1) ResultWindow=window.open("","Result", "menubar=no,width=300,height=400,top=200,left=200,scrollbars=yes,resizable=yes,location=no,directories=no,status=no")
	else if (Stage==2) ResultWindow=window.open("","Result", "menubar=no,width=600,height=400,top=100,left=100,scrollbars=yes,resizable=yes,location=no,directories=no,status=no")
	ResultWindow.document.write("<HTML><head><title>Results</title></head><BODY style='font-family:Verdana,Arial; font-size:10pt'><CENTER>")
	ResultWindow.document.write("<TABLE BORDER=1 cellpadding=5><TR><TD ALIGN=CENTER style='font-family:Verdana,Arial; font-size:10pt'>")
	ResultWindow.document.write("<FONT SIZE=+2>"+MainMolecule.Formula+"</FONT><BR>")
	ResultWindow.document.write("<B>Mass: "+fix2(MainMolecule.MW)+"</B><BR>")
	ResultWindow.document.write("<BR><B>Mass %:</B><BR><TABLE BORDER=1 cellpadding=5>")
	ResultWindow.document.write("<TR><TD><B></B></TD><TD style='font-family:Verdana,Arial; font-size:10pt'>calc'd</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>found</TD><td style='font-family:Verdana,Arial; font-size:10pt'>|diff|</td></TR>")
	var limit=parseFloat(window.document.Contaminants.inlimit.value);
	if (limit==0.0) limit=0.4;
	j=0;
	var sum=0;
	for (i=0;i<TRUEELEMENTS;i++) {
		var pct1 = fix2(MainMolecule.SubscriptTable[i]*AtomicWtTable[i]/MainMolecule.MW*100.0);
		var pct2 = "";
		if (AnalyzableElementTable[j] == i) {
			pct2 = fix2(pctObs[j]).toString();
			sum += pctObs[j];
			j++;
			}
		var diff = fix2(Math.abs(pct1-pct2));
		var difftext = "<font color="+(diff>limit?"red>":"blue>")+diff.toString()+"</font>";
		if (MainMolecule.SubscriptTable[i]) ResultWindow.document.write("<TR><TD style='font-family:Verdana,Arial; font-size:10pt' align=center><B>"+ElementTable[i]+"</B></TD><TD style='font-family:Verdana,Arial; font-size:10pt'>"+pct1+"</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>"+pct2+"</TD><td style='font-family:Verdana,Arial; font-size:10pt'>"+difftext+"</td></TR>");
		}
	ResultWindow.document.write("<TR><TD style='font-family:Verdana,Arial; font-size:10pt'><i>total</i></TD><TD style='font-family:Verdana,Arial; font-size:10pt'>100%</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>"+fix2(sum)+"</TD><td style='font-family:Verdana,Arial; font-size:10pt'>"+fix2(Math.abs(100-sum))+"</td></TR>");
	ResultWindow.document.write("</TABLE>")
	ResultWindow.document.write("<BR><B>elements of unsaturation: "+EofU+"</B><BR>")
	if ((EofU-parseInt(EofU))>0) { ResultWindow.document.write("<FONT COLOR=$FF0000><BLINK>WARNING! Non-integral elements of unsaturation; check formula!</BLINK></FONT><BR>") }
	if (Stage==2) {
		ResultWindow.document.write("</TD><TD ALIGN=CENTER style='font-family:Verdana,Arial; font-size:10pt'>")
		ResultWindow.document.write("<H3><font color=red>Possible Solvent/impurity</font></H3>")
		ProcessFormula(window.document.Contaminants.inImpurity1.value,Impurity1Molecule);
		ProcessFormula(window.document.Contaminants.inImpurity2.value,Impurity2Molecule);
		ProcessFormula(window.document.Contaminants.inImpurity3.value,Impurity3Molecule);
		var molstep1=parseFloat(window.document.Contaminants.inmolstep1.value)
		if (molstep1 ==0.0) molstep1 = 0.05;
		var molstep2=parseFloat(window.document.Contaminants.inmolstep2.value)
		if (molstep2 ==0.0) molstep2 = 0.05;
		var molstep3=parseFloat(window.document.Contaminants.inmolstep3.value)
		if (molstep3 ==0.0) molstep3 = 0.05;

		var cmin1=0;
		var cmin2=0;
		var cmin3=0;

		var cmax1=0;
		var cmax2=0;
		var cmax3=0;

		var numImpurities=0;
		if (Impurity1Molecule.Formula != "") {
			numImpurities++;
			cmin1 = parseFloat(window.document.Contaminants.inmolmin1.value)/molstep1;
			cmax1 = parseFloat(window.document.Contaminants.inmolmax1.value)/molstep1;
			}
		if (Impurity2Molecule.Formula != "") {
			numImpurities++;
			cmin2 = parseFloat(window.document.Contaminants.inmolmin2.value)/molstep2;
			cmax2 = parseFloat(window.document.Contaminants.inmolmax2.value)/molstep2;
			}
		if (Impurity3Molecule.Formula != "") {
			numImpurities++;
			cmin3 = parseFloat(window.document.Contaminants.inmolmin3.value)/molstep3;
			cmax3 = parseFloat(window.document.Contaminants.inmolmax3.value)/molstep3;
			}
if (numImpurities) {
		var gm1=0.0
		var gm2=0.0
		var gm3=0.0
		var molv1=0.0
		var molv2=0.0
		var molv3=0.0
		var LargestDifference=0;
		var AbsoluteMinimumLargestDifference=100; 
		var LocalMinimumLargestDifference=100;
		var SecondLocalMinimumLargestDifference=100;
		var PreviousLocalMinimumLargestDifference;
		var PreviousSecondLocalMinimumLargestDifference;
		var PreviousLargestDifference=100;
		var ResultFormula="";
		var c3; var c2; var c1;
		var c3AtMin=0; var c2AtMin=0; var c1AtMin=0;
		var numCycles = (cmax3-cmin3+1)*(cmax2-cmin2+1)*(cmax1-cmin1+1);
config='toolbar=no,location=no,directories=no,status=no,menubar=no,width=200,height=160,top=0,left=0,';
config += 'scrollbars=yes,resizable=yes,copyhistory=no';
pop = window.open ("","pop",config);
pop.document.write('<title>Please Wait...</title>');
pop.document.write('<body bgcolor=ltyellow style="font-family:Verdana,Arial; font-size:10pt">');
pop.document.write('<center><h3>Calculation in progress</h3>');
pop.document.write('<h5>Please wait... </h5>');
pop.document.write('<h6>This window will close itself...</h6>');
pop.document.write('<form name="output">step <input type=text value="0" name="monitor" size=5> of '+numCycles+'</form>');
pop.document.write('</center></body>');
pop.document.write('<script language="javascript">');
		for (c3=cmin3;c3<=cmax3;c3++) {
			molv3=c3*molstep3;
			var g3=MainMolecule.MW+Impurity3Molecule.MW*molv3;
			PreviousSecondLocalMinimumLargestDifference = SecondLocalMinimumLargestDifference;
			for (c2=cmin2;c2<=cmax2;c2++) {
				molv2=c2*molstep2;
				var g2=g3+Impurity2Molecule.MW*molv2;
				PreviousLocalMinimumLargestDifference=LocalMinimumLargestDifference;
				LocalMinimumLargestDifference=100;
				for (c1=cmin1;c1<=cmax1;c1++) {
pop.document.output.monitor.value=c3*(cmax2-cmin2+1)*(cmax1-cmin1+1)+c2*(cmax1-cmin1+1)+c1+1;
					molv1=c1*molstep1;
					var g1=g2+Impurity1Molecule.MW*molv1;
					var i;
					for(i=0;i<AnalyzableElementTable.length;i++) {
						pct[i] = MainMolecule.SubscriptTable[AnalyzableElementTable[i]];
						pct[i] += Impurity1Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv1;
						pct[i] += Impurity2Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv2;
						pct[i] += Impurity3Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv3;
						pct[i] *= AtomicWtTable[AnalyzableElementTable[i]]*100/g1;
						}
					PreviousLargestDifference=LargestDifference;
					LargestDifference=0;
					for(i=0;i<AnalyzableElementTable.length;i++) {
						if (pctObs[i]>0) {
							pctdiff[i] = Math.abs(pctObs[i] - pct[i]);
							if (pctdiff[i] > LargestDifference) { LargestDifference = pctdiff[i]; }
							}
						}
					if (LargestDifference < LocalMinimumLargestDifference) {
						LocalMinimumLargestDifference=LargestDifference;
						if (LocalMinimumLargestDifference < AbsoluteMinimumLargestDifference) {
							AbsoluteMinimumLargestDifference = LocalMinimumLargestDifference;
							c3AtMin = c3; c2AtMin=c2; c1AtMin = c1;
							}
						}
					else if (LargestDifference > PreviousLargestDifference){ c1 = cmax1+1; }
					}
				if (LocalMinimumLargestDifference > PreviousLocalMinimumLargestDifference) { 
					SecondLocalMinimumLargestDifference = PreviousLocalMinimumLargestDifference;
					c2 = cmax2+1; 
					}
				}
			if (SecondLocalMinimumLargestDifference > PreviousSecondLocalMinimumLargestDifference) { c3 = cmax3+1; }
			}
		molv3=c3AtMin*molstep3;
		molv2=c2AtMin*molstep2;
		molv1=c1AtMin*molstep1;
		var g1 = MainMolecule.MW+Impurity3Molecule.MW*molv3 + Impurity2Molecule.MW*molv2 + Impurity1Molecule.MW*molv1;
		for(i=0;i<AnalyzableElementTable.length;i++) {
			pctmin[i] = MainMolecule.SubscriptTable[AnalyzableElementTable[i]];
			pctmin[i] += Impurity1Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv1;
			pctmin[i] += Impurity2Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv2;
			pctmin[i] += Impurity3Molecule.SubscriptTable[AnalyzableElementTable[i]]*molv3;
			pctmin[i] *= AtomicWtTable[AnalyzableElementTable[i]]*100/g1;
			}
		ResultFormula="<B>"+MainMolecule.Formula;
		if (Impurity1Molecule.Formula.length!=0) { ResultFormula+=" &#183; "+fix2(molv1)+" "+Impurity1Molecule.Formula+" " }
		if (Impurity2Molecule.Formula.length!=0) { ResultFormula+=" &#183; "+fix2(molv2)+" "+Impurity2Molecule.Formula+" " }
		if (Impurity3Molecule.Formula.length!=0) { ResultFormula+=" &#183; "+fix2(molv3)+" "+Impurity3Molecule.Formula+" " }
		ResultFormula+="</B>"
pop.document.write('</');
pop.document.write('script>');
pop.close();
		ResultWindow.document.write("<H3>Best Result</H3>")
		ResultWindow.document.write(ResultFormula+"<BR>")
		diff = fix2(AbsoluteMinimumLargestDifference);
		difftext = "<font color="+(diff>limit?"red>":"blue>")+diff.toString()+"</font>";
		ResultWindow.document.write("max. diff: "+difftext+"<BR><BR>")
		ResultWindow.document.write("<TABLE BORDER=1>")
		ResultWindow.document.write("<TR><TD><B></B></TD><TD style='font-family:Verdana,Arial; font-size:10pt'>found</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>recalculated</TD></TR>")
		var i;
		for(i=0;i<AnalyzableElementTable.length;i++) {
			if (pctObs[i] > 0) { 
				ResultWindow.document.write("<TR><TD style='font-family:Verdana,Arial; font-size:10pt' align=center><B>"+ElementTable[AnalyzableElementTable[i]]+"</B></TD><TD style='font-family:Verdana,Arial; font-size:10pt'>"+fix2(pctObs[i])+"</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>"+fix2(pctmin[i])+"</TD></TR>");
				}
			}
		ResultWindow.document.write("</TABLE>")
	}
} // end of if (numImpurities)
	ResultWindow.document.write("</TD></TR></TABLE>")
	ResultWindow.document.write("<FORM><INPUT TYPE=BUTTON VALUE='Close Window' onClick='window.close()'></FORM>")
	ResultWindow.document.write("</CENTER></BODY></HTML>")
}

function RatioAnalysis() {
	if (MainMolecule.Formula=="" || MainMolecule.MW==0) {
		alert("You must calculate the nominal composition first (step 1)!");
		return;
	}
	RatioWindow=window.open("","Result", "menubar=no,width=400,height=300,top=200,left=200,scrollbars=yes,resizable=yes,location=no,directories=no,status=no");
	RatioWindow.document.write("<HTML><head><title>Ratios</title></head><BODY style='font-family:Verdana,Arial; font-size:10pt'><CENTER>")
	RatioWindow.document.write("<TABLE BORDER=1 cellpadding=5><TR><TD ALIGN=CENTER style='font-family:Verdana,Arial; font-size:10pt'>")
	RatioWindow.document.write(MainMolecule.Formula+"<BR>")
	RatioWindow.document.write("<B>Mass: "+fix2(MainMolecule.MW)+"</B><BR>")
	RatioWindow.document.write("<BR><TABLE BORDER=1 cellpadding=5>")
	var nObs=0;
	var i;
	for (i=0; i < AnalyzableElementTable.length; i++) {
		if (pctObs[i]) nObs+=1;
	}
	RatioWindow.document.write("<TR><TD style='font-family:Verdana,Arial; font-size:10pt'>analyte</TD><TD style='font-family:Verdana,Arial; font-size:10pt'>formula</TD><td style='font-family:Verdana,Arial; font-size:10pt'colspan="+nObs+">ratios fitting the analyses</td></TR>");
	var j;
	for (i=0; i < AnalyzableElementTable.length; i++) {
		var pct = pctObs[i];
		if (pct>0) {
			var mass = AtomicWtTable[AnalyzableElementTable[i]];
			var num = MainMolecule.SubscriptTable[AnalyzableElementTable[i]];
			RatioWindow.document.write("<TR><TD align=center style='font-family:Verdana,Arial; font-size:10pt'><B>"+ElementTable[AnalyzableElementTable[i]]+"</B></TD><TD style='font-family:Verdana,Arial; font-size:10pt'align=center>"+fix2(num)+"</TD>");
			for (j=0; j<AnalyzableElementTable.length; j++) {
				var pct2 = pctObs[j];
				if (pct2>0) {
					var mass2 = AtomicWtTable[AnalyzableElementTable[j]];
					var num2 = MainMolecule.SubscriptTable[AnalyzableElementTable[j]];
					var value= pct*mass2/pct2/mass*num2;
					if (i==j) RatioWindow.document.write("<TD style='font-family:Verdana,Arial; font-size:10pt'align=center><b>"+fix2(value)+"</b></TD>");
					else RatioWindow.document.write("<TD style='font-family:Verdana,Arial; font-size:10pt' align=right>"+fix2(value)+"</TD>");
				}
			}
		}
	}
	RatioWindow.document.write("</TR>");
	RatioWindow.document.write("</TABLE></TD></TR></TABLE>");
	RatioWindow.document.write("<FORM><INPUT TYPE=BUTTON VALUE='Close Window' onClick='window.close()'></FORM>");
	RatioWindow.document.write("</CENTER></BODY></HTML>")
}

// +++++++++++++++++++++++- ProcessFormula -++++++++++++++++++++
function ProcessFormula(RawFormula, ThisMolecule) {
//	if (RawFormula.length ==0) RawFormula = window.document.FormulaAnalysis.inFormula.value;
	var NumElementsInFormula = Parse(RawFormula);
	if (NumElementsInFormula == 0) { Empty(ThisMolecule); return 0; }
	var RealNumElements = Pack(NumElementsInFormula, ThisMolecule);
	if (RealNumElements == 0) { Empty(ThisMolecule); return 0; }
	var i;	
	if (NS4) Elements.splice(0,NumElementsInFormula); // could also use pop method with NS4
	else for (i=NumElementsInFormula-1; i>=0;i--) {Elements.pop();}
	ThisMolecule.MW=0.0;
	for (i = 0; i < TRUEELEMENTS; i++) {
		ThisMolecule.MW += AtomicWtTable[i]*ThisMolecule.SubscriptTable[i];
	}
	return 1;
}
function Empty(TheMolecule) {
	TheMolecule.MW = 0.0; 
	TheMolecule.Formula=""; 	
	var i;
	for(i=0;i<104;i++) {TheMolecule.SubscriptTable[i]=0;}
	}
// ++++++++++++++++++++++++- Parse -+++++++++++++++++++
function Parse(Formula) {
	var ElementNumber=ALLELEMENTS+1;
	var StrLen = Formula.length;
	if (StrLen == 0) {return 0;}
	var Error=0; var ExpectingUpperError=0; var BadFirstCharError=0;
	var NotAnElemError=0;
	var CharCount=0; var NumElementsInFormula=0; var OpenBracketLevel=0;
	var ExpectingUpper=0; var BadFirstChar=0; var NotAnElem=0;
	var FirstDottedElement = 0; var LastDottedElement = 0; var NextDottedElement = 0;
	var ThereIsADotPending = 0;
	// up to 32 bracket levels
	var FirstBracketedElem = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
	var LastBracketedElem = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
	var DottedPrefix = 1.0;
	do {
		var Char = Formula.charAt(CharCount++);
		if (Char == '(') {
			FirstBracketedElem[OpenBracketLevel++] = NumElementsInFormula;
		}
		else if (Char == ')') {
			if (OpenBracketLevel == 0) {
				Error = 1;
				alert("Error: Unexpected )");
				return 0;
			}
			LastBracketedElem[OpenBracketLevel-1] = NumElementsInFormula-1;
			var BracketSubscript = 1;
			var SubscriptText = "";
			var FirstDigit = CharCount; var NumDigits = 0;
			var NextChar = Formula.charAt(CharCount++);
			while ((NextChar>="0")&&(NextChar<="9")) {
				SubscriptText += NextChar;
				NumDigits++;
				NextChar = Formula.charAt(CharCount++);
			}
			if (NumDigits) BracketSubscript = parseInt(SubscriptText,10);
			var i;
			for (i=FirstBracketedElem[OpenBracketLevel-1]; i<= LastBracketedElem[OpenBracketLevel-1]; i++) {
				Elements[i].Subscript *= BracketSubscript;
			}
			// multiply through all subscripts within this bracket pair level
			CharCount--; OpenBracketLevel--;
		}
		else if (Char == '*') {
			if (OpenBracketLevel > 0) {
				Error = 1;
				alert("Unclosed bracket before *");
				return 0;
			}
			if (!ThereIsADotPending) {
				FirstDottedElement = NumElementsInFormula;
			}
			else {
				if (NextDottedElement > FirstDottedElement) FirstDottedElement = NextDottedElement;
				LastDottedElement = NumElementsInFormula-1;
				var i;
				for (i = FirstDottedElement; i <= LastDottedElement; i++) {
					Elements[i].Subscript *= DottedPrefix;
					}
				NextDottedElement = NumElementsInFormula;
			}
			DottedPrefix = 1.0;
			var FirstDigit = CharCount;
			var NumDigits = 0;
			var NextChar = Formula.charAt(CharCount++);
			while (!isNaN(parseFloat(NextChar)) || NextChar == '.') {
				NumDigits++;
				NextChar = Formula.charAt(CharCount++);
			}
			if (NumDigits) DottedPrefix = parseFloat(Formula.substr(FirstDigit,NumDigits));
			ThereIsADotPending = 1;
			CharCount--;
		}
		else if ( ((Char>="a")&&(Char<="z")) || !isNaN(parseFloat(Char)) ) {
			Error = 1;
			ExpectingUpperError = 1;
			ExpectingUpper = CharCount-1;
			alert("Error: Expecting uppercase character to begin an element name\nOffending character: " + Formula.charAt(ExpectingUpper));
			return 0;
		}
		else if (((Char>="A")&&(Char<="Z"))) {
			var NextChar = Formula.charAt(CharCount++);
			var NumLower=0;
			while (((NextChar>="a")&&(NextChar<="z"))) {
				NumLower++;
				NextChar = Formula.charAt(CharCount++);
			}
			// CharCount is one character too far: CharCount-1 is position of last relevant char
			var ElementStrLen = NumLower+1;
			var ElementName = Formula.substr(CharCount-1-ElementStrLen, ElementStrLen);
			ElementNumber = CheckName(ElementName);
			if (ElementNumber > ALLELEMENTS-1) {
				// not a recognized Element or Alias
				Error = 1;
				NotAnElemError = 1;
				NotAnElem = NumElementsInFormula;
				alert("Error: Unrecognized element\nOffending Name: " + ElementName);
				return 0;
			}
			else if (ElementNumber > TRUEELEMENTS-1) {
				// is an Alias
				var Alias = Equivalents[ElementNumber-TRUEELEMENTS]
				var AliasStrLen = Alias.length;
				Formula = Formula.replace(ElementName,Equivalents[ElementNumber-TRUEELEMENTS]);
				StrLen = Formula.length;
				// reposition to re-read alias as its elements
				CharCount -= ElementStrLen+1;
			}
			else {
				// is a recognized Element
				Elements[NumElementsInFormula] = new AnElementObj(ElementNumber,1,0.0,0.0);
				var FirstDigit = CharCount-1;
				var NumDigits = 0;
				while (!isNaN(parseFloat(NextChar))) {
					NumDigits++;
					NextChar = Formula.charAt(CharCount++);
				}
				if (NumDigits) {
					var SubscriptText = Formula.substr(FirstDigit,NumDigits);
					Elements[NumElementsInFormula].Subscript = parseInt(SubscriptText,10);
				}
				NumElementsInFormula++; CharCount--;
			}
		}
	} while (CharCount <= StrLen && !Error);
	if (ThereIsADotPending) {
		if (NextDottedElement > FirstDottedElement) FirstDottedElement = NextDottedElement;
		LastDottedElement = NumElementsInFormula-1;
		var i;
		for (i = FirstDottedElement; i <= LastDottedElement; i++) {
			Elements[i].Subscript *= DottedPrefix;
		}
	}
	if (OpenBracketLevel > 0) {
		Error = 1;
		alert("Error: Expecting )");
		return 0;
	}
	return NumElementsInFormula;
}
// +++++++++++++++++++++++- CheckName -++++++++++++++++++++
function CheckName(Name) {
	var i;
	for (i=0; i < ALLELEMENTS; i++) {
		if (ElementTable[i]==Name) return i;
	}
	return i;
}
// +++++++++++++++++++++++- Pack -++++++++++++++++++++++++++
function Pack(NumElements, ThisMolecule) {
	var i;
	var j=0;
	for (i = 0; i < TRUEELEMENTS; i++) {ThisMolecule.SubscriptTable[i] = 0;}
	for (i = 0; i < NumElements; i++) {
		ThisMolecule.SubscriptTable[Elements[i].PositionInTable] += Elements[i].Subscript;
	}
	// re-write the formula in order
	ThisMolecule.Formula = "";
	for (i = 0; i < TRUEELEMENTS; i++) {
		if (ThisMolecule.SubscriptTable[i] > 0) {
			j += 1;
			ThisMolecule.Formula = ThisMolecule.Formula.concat(ElementTable[i]);
			if (ThisMolecule.SubscriptTable[i] > 1) {
				ThisMolecule.Formula = ThisMolecule.Formula.concat('<sub>',ThisMolecule.SubscriptTable[i].toString(),'</sub>');
			}
		}
	}
	return j;
}

