/**********************************************/
/* This file holds the Object 'ChemCalc' and associated
functions used by multiple chemistry calculators */
/**********************************************/
var ChemCalc = {};	// Object to hold chemistry calculator common functions
ChemCalc.FW = {};	// Object for formula weight data and functions
ChemCalc.FW.weight = {}; // Object to become associative array for molecular masses e.g. weight["H"]=1.008
ChemCalc.FW.elements = new Array("H","Li","B","C","N","O","F","Na","Mg","Al","Si","P","S","Cl","K","Ca","V","Cr","Mn","Fe","Co","Ni","Cu","Zn","Ga","Br","Ag","I","Cs","Au","Hg","Pb");
ChemCalc.FW.masses = new Array(1.00794,6.941,10.811,12.0107,14.0067,15.9994,18.9984032,22.989770,24.3050,26.981538,28.0855,30.973761,32.065,35.453,39.0983,40.078,50.9415,51.9961,54.938049,55.845,58.9332,58.6934,63.546,65.409,69.723,79.904,107.8682,126.90447,132.90545,196.96655,200.59,207.2);

/* **************************************************************************************** */
/* Formula Weight Functions													*/
/* **************************************************************************************** */
/* ChemCalc.FW.initialize() : initializes the "global" object ChemCalc.FW.weight with the atomic weights	 */
/* **************************************************************************************** */
ChemCalc.FW.initialize = function() {
  for (var i=0; i<ChemCalc.FW.elements.length; i++)
    ChemCalc.FW.weight[ChemCalc.FW.elements[i]]=ChemCalc.FW.masses[i];
	// call the LabCalc initializer also
	LabCalc.initialize();
}
/* **************************************************************************************** */
/* Checks for a valid acceptable chemical formula									 */
/* returns a boolean														 */
/* **************************************************************************************** */
ChemCalc.FW.checkFormula = function(f) {
  var pattern = /[A-Z][a-z]?/g;
  var rx;
  
  pattern.lastIndex=0;
  
  while ((rx = pattern.exec(f)) != null) {
    if (!(rx in ChemCalc.FW.weight)) {
	  window.alert("Error: Molecular weight of " + rx + " is not available.");
	  return false; }
  }
  
  pattern = /^[0-9]/g;
  if (pattern.test(f)) {
    window.alert("Numbers at the start of the formula are not permitted.");
	return false; }

  pattern = /(^[a-z])|\W/g;  //  small letter at start of line or non-word character
    if (pattern.test(f)) {
    window.alert("Invalid or unsupported formula detected.");
	return false; }

  pattern = /[a-z]{2,}/g;  // two lower-case letters in a row
	if (pattern.test(f)) {
    window.alert("Error detected in formula.");
	return false; }

  pattern = /[0-9][a-z]/g; // lower case letter after a number
	if (pattern.test(f)) {
    window.alert("Element symbol can not begin with lower case.");
	return false; }
	
  return true;
}

/* **************************************************************************************** */
/* Main calculation of formula mass from a chemical formula								 */
/* Takes an optional argument specifying the ID of the element to place the result in.				*/
/* Otherwise assumes target is an input element with id="CalculatorResult"						*/
/* **************************************************************************************** */
ChemCalc.FW.weightOf = function(Formula) {
  var fw = 0; // to hold the forumula weight
  var R = new RegExp();  // to hold the RegExp for each element
  var pattern = new String; // temp string to generate each RegExp
  var rx_result;  // temp to hold result of RegExp matching
  var isnumber = /^[0-9]+$/; // RegExp to test for numbers
  
  // check if formula is valid
  if (!ChemCalc.FW.checkFormula(Formula)) return false;
  
  // for each element
  for (var i=0; i<ChemCalc.FW.elements.length; i++) {
    // create a regular expression
	pattern=ChemCalc.FW.elements[i]+"([0-9]+|[A-Z][a-z]?|$)";
	R = RegExp(pattern,"g"); // g sets mode to global
	
	while ((rx_result = R.exec(Formula)) != null) {
	  if (isnumber.test(rx_result[1])) {
	    fw+=ChemCalc.FW.weight[ChemCalc.FW.elements[i]]*rx_result[1]; }
	  else if (rx_result[1]==ChemCalc.FW.elements[i]) { // this accounts for tandem elements e.g. COOH and CaCa
	    fw+=ChemCalc.FW.weight[ChemCalc.FW.elements[i]];
		R.lastIndex-=ChemCalc.FW.elements[i].length; }		
	  else
	    fw+=ChemCalc.FW.weight[ChemCalc.FW.elements[i]];
	}
  }
  
  if (fw>0)
  return fw.toPrecision(6); // fw result with nice formatting
  else
  return false;
}

