www/js/rec_edit.js

Fri, 28 Dec 2018 23:18:42 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 28 Dec 2018 23:18:42 +0100
changeset 155
041af8a82d77
parent 154
ef298b5aa994
child 156
35860890224c
permissions
-rw-r--r--

Some pH calculations cane use twice as much loops, probably because we have better float precision then the original pascal code. More variables to prevent html reads. Use predicted mash pH (the grist in demi water) to calculate the pH shift from the tapwater to the target pH. It's better but not right yet. Or it is right and brouwhulp is far off.

/*****************************************************************************
 * Copyright (C) 2018
 *
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of BMS
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2, or (at your option) any
 * later version.
 *
 * BrewCloud is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ThermFerm; see the file COPYING.  If not, write to the Free
 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *****************************************************************************/


function createDelElements() {
	$('#eventWindow').jqxWindow({
		theme: theme,
		position: { x: 490, y: 210 },
		width: 300,
		height: 175,
		resizable: false,
		isModal: true,
		modalOpacity: 0.4,
		okButton: $('#delOk'),
		cancelButton: $('#delCancel'),
		initContent: function () {
			$('#delOk').jqxButton({ template: "danger", width: '65px', theme: theme });
			$('#delCancel').jqxButton({ template: "success", width: '65px', theme: theme });
			$('#delCancel').focus();
		}
	});
	$('#eventWindow').jqxWindow('hide');
}


$(document).ready(function () {

	var	to_100 = false;		// Fermentables adjust to 100%
	var	preboil_sg = 0;
	var	sugarsm = 0;		// Sugars after mash
	var	sugarsf = 0;		// Sugars after boil
	var     psugar = 0;     	// Percentage real sugars
	var     pcara = 0;      	// Percentage cara/crystal malts
	var	svg = 77;		// Default attenuation
	var	mashkg = 0;		// Malt in mash weight
	var	hop_flavour = 0;
	var	hop_aroma = 0;
	var	mash_infuse = 0;
	var	last_base = '';
	var	last_acid = '';

	var     MMCa = 40.048;
	var     MMMg = 24.305;
	var     MMNa = 22.98976928;
	var     MMCl = 35.453;
	var     MMSO4 = 96.0626;
	var     MMCO3 = 60.01684;
	var     MMHCO3 = 61.01684;
	var     MMCaSO4 = 172.171;
	var     MMCaCl2 = 147.015;
	var     MMCaCO3 = 100.087;
	var     MMMgSO4 = 246.475;
	var     MMNaHCO3 = 84.007;
	var     MMNa2CO3 = 105.996;
	var     MMNaCl = 58.443;
	var     MMCaOH2 = 74.06268;

	console.log("record:" + my_record + "  return:" + my_return + "  theme:" + theme);
	$("#jqxLoader").jqxLoader({
		width: 250,
		height: 150,
		isModal: true,
		text: "Laden recept ...",
		theme: theme
	});

	function calcFermentables() {
		console.log("calcFermentables()");
		sugarsf = 0;
		sugarsm = 0;
		psugar = 0;
		pcara = 0;
		mashkg = 0;
		var colorw = 0;	// Colors working
		var my_100 = false;

		var rows = $('#fermentableGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.f_adjust_to_total_100)
				my_100 = true;
			if (row.f_type == "Sugar")
				psugar += row.f_percentage;
			if (row.f_graintype == "Crystal")
				pcara += row.f_percentage;
			var d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
			if (row.f_added == "Mash") {
				d = parseFloat(dataRecord.efficiency) / 100 * d;
				sugarsm += d;
				mashkg += row.f_amount;
			}
			sugarsf += d;
			colorw += row.f_amount * ebc_to_srm(row.f_color) / parseFloat(dataRecord.batch_size) * 8.34436;
		}
		to_100 = my_100;
		var est_og = estimate_sg(sugarsf, parseFloat(dataRecord.batch_size));
		$('#est_og').val(est_og);
		$('#est_og2').val(est_og);
		preboil_sg = estimate_sg(sugarsm, dataRecord.boil_size);
		var color = kw_to_ebc(dataRecord.color_method, colorw);
		$('#est_color').val(color);
		$('#est_color2').val(color);
		var scolor = ebc_to_color(color);
		document.getElementById("bcolor").style.background= scolor;
		document.getElementById("bcolor2").style.background= scolor;
		// Use boil_size instead of mash_max from equipment.
		pmalts = mashkg / (dataRecord.boil_size / 3) * 100;
		$("#perc_malts").jqxProgressBar('val', pmalts);
		$("#perc_sugars").jqxProgressBar('val', psugar);
		$("#perc_cara").jqxProgressBar('val', pcara);
	};

	function hopFlavourContribution(bt, vol, use, amount) {
		var result;

		if ((use == "First Wort") || (use == "First wort")) {
			result = 0.15;		// assume 15% flavourcontribution for fwh
		} else if (bt > 50) {
			result = 0.10;		// assume 10% flavourcontribution as a minimum
		} else {
			result = 15.25 / (6 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 21) /6, 2));
			if (result < 0.10)
				result = 0.10;	// assume 10% flavourcontribution as a minimum
		}
		result = (result * amount * 1000) / vol;
	//	console.log("hopFlavourContribution("+bt+","+vol+","+use+","+amount+"): "+result);
		return result;
	}

	function hopAromaContribution(bt, vol, use, amount) {
		var result = 0;

		if (bt > 20) {
			result = 0;
		} else if (bt > 7.5) {
			result = 10.03 / (4 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 7.5) /4, 2));
		} else if (use == "Boil") {
			result = 1;
		} else if (use == "Aroma") {
			result = 1.2;
		} else if (use == "Whirlpool") {
			result = 1.2;
		} else if ((use == "Dry Hop") || (use == "Dry hop")) {
			result = 1.33;
		}
		result = (result * amount * 1000) / vol;
	//	console.log("hopAromaContribution("+bt+","+vol+","+use+","+amount+"): "+result);
		return result;
	}

	function calcIBUs() {
		var total_ibus = 0;
		hop_aroma = hop_flavour = 0;
		var rows = $('#hopGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			total_ibus += toIBU(row.h_useat, row.h_form, preboil_sg, parseFloat(dataRecord.batch_size),
					parseFloat(row.h_amount), parseFloat(row.h_time), parseFloat(row.h_alpha), dataRecord.ibu_method);
			hop_flavour += hopFlavourContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size),
						row.h_useat, parseFloat(row.h_amount));
			hop_aroma += hopAromaContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size),
					        row.h_useat, parseFloat(row.h_amount));
		}
		total_ibus = Math.round(total_ibus);
		console.log("calcIBUs(): " + total_ibus + "  flavour: " + hop_flavour + "  aroma: " + hop_aroma);
		dataRecord.est_ibu = total_ibus;
		$('#est_ibu').val(total_ibus);
		$('#est_ibu2').val(total_ibus);
		$("#hop_flavour").jqxProgressBar('val', hop_flavour * 10);
		$("#hop_aroma").jqxProgressBar('val', hop_aroma * 10);
	};

	function calcSVG() {
		var rows = $('#yeastGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.y_use == "Primary")
				svg = parseFloat(row.y_attenuation);
		}
	}

/*	function GetBUGUMin() {

		var Result = 0;

		if (((dataRecord.st_og_max + dataRecord.st_og_min) > 0) && ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) > 0)) {
			var G = (dataRecord.st_og_max - dataRecord.st_og_min) / ((dataRecord.st_og_max + dataRecord.st_og_min) / 2);
			var B = (dataRecord.st_ibu_max - dataRecord.st_ibu_min) / ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2);
			if (G > B)
				Result = ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2) / (1000 * (dataRecord.st_og_max - 1));
			else
				Result = dataRecord.st_ibu_min / (1000 * (((dataRecord.st_og_max + dataRecord.st_og_min) / 2) - 1));
		}
		console.log("GetBUGUMin(): "+Result);
		return Result;
	}

	function GetBUGUMax() {

		var Result = 0;

		if (((dataRecord.st_og_max + dataRecord.st_og_min) > 0) && ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) > 0) && (dataRecord.st_og_min > 0)) {
			var G = (dataRecord.st_og_max - dataRecord.st_og_min) / ((dataRecord.st_og_max + dataRecord.st_og_min) / 2);
			var B = (dataRecord.st_ibu_max - dataRecord.st_ibu_min) / ((dataRecord.st_ibu_max + dataRecord.st_ibu_min) / 2);
			if (G > B)
				Result = ((dataRecord.st_ibu_min + dataRecord.st_ibu_max) / 2) / (1000 * (dataRecord.st_og_min - 1));
			else
				Result = dataRecord.st_ibu_max / (1000 * (((dataRecord.st_og_max + dataRecord.st_og_min) / 2) - 1));

		}
		console.log("GetBUGUMax(): "+Result);
		return Result;
	} */

	function GetBUGU() {
		var gu = (dataRecord.est_og - 1) * 1000;
		if (gu > 0)
			return dataRecord.est_ibu / gu;
		else
			return 0.5;
	}

	function GetOptClSO4ratio() {
		var BUGU = GetBUGU();
		return (-1.2 * BUGU + 1.4);
	}

	function setWaterAgent(name, amount) {
		console.log("setWaterAgent(" + name + ", " + amount + ")");
		var rows = $('#miscGrid').jqxGrid('getrows');
		if (amount == 0) {
			for (var i = 0; i < rows.length; i++) {
				var row = rows[i];
				if (row.m_name == name) {
					var id = $("#miscGrid").jqxGrid('getrowid', i);
					var commit = $("#miscGrid").jqxGrid('deleterow', id);
				}
			}
		} else {
			var found = false;
			for (var i = 0; i < rows.length; i++) {
				var row = rows[i];
				if (row.m_name == name) {
					found = true;
					$("#miscGrid").jqxGrid('setcellvalue', i, 'm_weight', amount);
					$("#miscGrid").jqxGrid('setcellvalue', i, 'm_amount', amount / 1000);
					break;
				}
			}
//			console.log("set something, found: "+found);
			if (! found) {
//				console.log("need to add this misc");
				var miscs = new $.jqx.dataAdapter(miscInvSource, {
					loadComplete: function () {
						var records = miscs.records;
						for (var i = 0; i < records.length; i++) {
							var record = records[i];
							if (record.name == name) {
								var row = {};
								row["m_name"] = record.name;
								row["m_amount"] = amount / 1000;
								row["m_cost"] = record.cost;
								row["m_type"] = record.type;
								row["m_use_use"] = record.use_use;
								row["m_time"] = 0;
								row["m_weight"] = amount;
								row["m_amount_is_weight"] = record.amount_is_weight;
								var commit = $("#miscGrid").jqxGrid('addrow', null, row);
							}
						}
					}
				});
				miscs.dataBind();
				return;
			}
		}
	}

	function setRangeIndicator(ion, rangeCode) {
		$("#wr_" + ion).html("<img src='images/checkmark_range_" + rangeCode + ".gif'><span style='font-size: 10px; font-style: italic;'>" + rangeCode + "</span>");
	}

	function mix(v1, v2, c1, c2) {
		if ((v1 + v2) > 0) {
			return ((v1 * c1) + (v2 * c2)) / (v1 + v2);
		}
		return 0;
	}

	var Ka1 = 0.0000004445;
	var Ka2 = 0.0000000000468;

	function PartCO3(pH) {
		var H = Math.pow(10, -pH);
		return 100 * Ka1 * Ka2 / (H*H + H * Ka1 + Ka1 * Ka2);
	}

	function PartHCO3(pH) {
		var H = Math.pow(10, -pH);
		return 100 * Ka1 * H / (H*H + H * Ka1 + Ka1 * Ka2);
	}

	function Charge(pH) {
		return (-2 * PartCO3(pH) - PartHCO3(pH));
	}

	//Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH)
	function ZAlkalinity(pHZ, WpH) {
		var C43 = Charge(4.3);
		var Cw = Charge(WpH);
		var Cz = Charge(pHZ);
		var DeltaCNaught = -C43+Cw;
		var CT = parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal')) / 50 / DeltaCNaught;
		var DeltaCZ = -Cz+Cw;
		return CT * DeltaCZ;
	}

	//Z Residual alkalinity is the amount of acid (in mEq/l) needed to bring the water in the mash to the target pH (Z pH)
	function ZRA(pHZ, WpH) {

		var Calc = parseFloat($("#wg_calcium").jqxNumberInput('decimal')) / (MMCa / 2);
		var Magn = parseFloat($("#wg_magnesium").jqxNumberInput('decimal')) / (MMMg / 2);
		var Z = ZAlkalinity(pHZ, WpH);
		return Z - (Calc / 3.5 + Magn / 7);
	}

	function ProtonDeficit(pHZ) {

		var Result = ZRA(pHZ, parseFloat($("#wg_ph").jqxNumberInput('decimal'))) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
		var rows = $('#fermentableGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.f_added == 'Mash' && row.f_graintype != 'No malt') {
				// Check if acid is required
				var C1 = 0;
				if ((row.f_di_ph != 5.7) && ((row.f_acid_to_ph_57 < - 0.1) || (row.f_acid_to_ph_57 > 0.1))) {
					C1 = row.f_acid_to_ph_57 / (row.f_di_ph - 5.7);
	//				console.log("formula C1: "+C1);
				} else {
					// If the acid_to_ph_5.7 is unknown from the malter, guess the required acid.
					var ebc = row.f_color;
					switch (row.f_graintype) {
						case 'Base':
						case 'Special':
						case 'Kilned':	C1 = 0.014 * ebc - 34.192;
								break;
						case 'Crystal':	C1 = -0.0597 * ebc - 32.457;
								break;
						case 'Roast':	C1 = 0.0107 * ebc - 54.768;
								break;
						case 'Sour':	C1 = -149;
								break;
					}
	//				console.log("Logic C1: "+C1);
				}
				x = C1 * (pHZ - row.f_di_ph);	// AcidRequired(ZpH)
	//			console.log(row.f_name+" C1: "+C1+" ZpH: "+pHZ+" di_ph: "+row.f_di_ph+" acid rquired: "+x);
				Result += x * row.f_amount;
			}
		}
	//	console.log("Final: "+Result);
		return Result;
	}

	function MashpH() {
		var n = 0;
		var pH = 5.4;
		var deltapH = 0.001;
		var deltapd = 0.1;
		var pd = ProtonDeficit(pH);
		while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) {
			n++;
			if (pd < -deltapd)
				pH -= deltapH;
			else if (pd > deltapd)
				pH += deltapH;
			pd = ProtonDeficit(pH);
		}
		console.log("MashpH() n: "+n+" pH: "+pH);
		return pH;
	}

	function GetAcidSpecs(AT) {
		switch(AT) {
			case 'Melkzuur':	return {
							pK1: 3.08,
							pK2: 20,
							pK3: 20,
							MolWt: 90.08,
							AcidSG: 1214,
							AcidPrc: 0.88
						};
			case 'Zoutzuur':	return {
							pK1: -10,
							pK2: 20,
							pK3: 20,
							MolWt: 36.46,
							AcidSG: 1142,
							AcidPrc: 0.28
						};
			case 'Fosforzuur':	return {
							pK1: 2.12,
							pK2: 7.20,
							pK3: 12.44,
							MolWt: 98.00,
							AcidSG: 1170,
							AcidPrc: 0.25
						};
			case 'Zwavelzuur':	return {
							pK1: -10,
							pK2: 1.92,
							pK3: 20,
							MolWt: 98.07,
							AcidSG: 1700,
							AcidPrc: 0.93
						};
		}
	}

	// Procedure TFrmWaterAdjustment.CalcWater2;
	function calcWater() {

		console.log("calcWater()");
		var liters = 0;
		var calcium = 0;
		var magnesium = 0;
		var sodium = 0;
		var total_alkalinity = 0;
		var bicarbonate = 0;
		var chloride = 0;
		var sulfate = 0;
		var ph = 0;
		var RA = 0;
		var acid = 0;
		var frac = 0;
		var MolWt = 0;
		var pK1 = 0;
		var pK2 = 0;
		var pK3 = 0;
		var TpH = 0;
		var AcidSG = 0;
		var AcidPrc = 0;
		var protonDeficit = 0;

		if (dataRecord.w1_name == "") {
			return;
		}
		// Check for a recipe too. Fermentables, Mash schedule?

		// If there is a dillute water source, mix the waters.
		if (dataRecord.w2_name != "") {
			liters = dataRecord.w1_amount + dataRecord.w2_amount;
			calcium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_calcium, dataRecord.w2_calcium);
			magnesium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_magnesium, dataRecord.w2_magnesium);
			sodium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sodium, dataRecord.w2_sodium);
			chloride = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_chloride, dataRecord.w2_chloride);
			sulfate = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sulfate, dataRecord.w2_sulfate);
			total_alkalinity = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_total_alkalinity, dataRecord.w2_total_alkalinity);
			ph = -Math.log10(((Math.pow(10, -dataRecord.w1_ph) * dataRecord.w1_amount) + (Math.pow(10, -dataRecord.w2_ph) * dataRecord.w2_amount))  / liters);
		} else {
			liters = dataRecord.w1_amount;
			calcium = dataRecord.w1_calcium;
			magnesium = dataRecord.w1_magnesium;
			sodium = dataRecord.w1_sodium;
			chloride = dataRecord.w1_chloride;
			sulfate = dataRecord.w1_sulfate;
			total_alkalinity = dataRecord.w1_total_alkalinity;
			ph = dataRecord.w1_ph;
		}
		$('#wg_amount').val(liters);
		var wg_calcium = calcium;
		$('#wg_calcium').val(Math.round(calcium * 10) / 10);
		var wg_magnesium = magnesium;
		$('#wg_magnesium').val(Math.round(magnesium * 10) / 10);
		var wg_sodium = sodium;
		$('#wg_sodium').val(Math.round(sodium * 10) / 10);
		var wg_total_alkalinity = total_alkalinity;
		$('#wg_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10);
		var wg_chloride = chloride;
		$('#wg_chloride').val(Math.round(chloride * 10) / 10);
		var wg_sulfate = sulfate;
		$('#wg_sulfate').val(Math.round(sulfate * 10) / 10);
		// Note: brouwhulp has the malts included here in the result.
		var wg_ph = ph;
		$('#wg_ph').val(Math.round(ph * 10) / 10);
//		$('#wb_ph').val(Math.round(ph * 10) / 10);
		$('#wb_ph').val(Math.round(MashpH() * 10) / 10);
		bicarbonate = total_alkalinity * 1.22;
		var wg_bicarbonate = bicarbonate;

		// Noot: de volgende berekeningen geven bijna gelijke resultaten in Brun'water.
		// Calculate Ca
		RA = parseFloat($("#wa_cacl2").jqxNumberInput('decimal')) * MMCa / MMCaCl2 +
		     parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMCa / MMCaSO4;
		calcium += 1000 * RA / liters;

		// Calculate Mg
		RA = parseFloat($("#wa_mgso4").jqxNumberInput('decimal')) * MMMg / MMMgSO4;
		magnesium += 1000 * RA / liters;

		// Calculate Na
		RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl;
		sodium += 1000 * RA / liters;

		// Calculate SO4
		RA = parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 +
		     parseFloat($("#wa_mgso4").jqxNumberInput('decimal')) * MMSO4 / MMMgSO4;
		sulfate += 1000 * RA / liters;

		// Calculate Cl
		RA = 2 * parseFloat($("#wa_cacl2").jqxNumberInput('decimal')) * MMCl / MMCaCl2 +
		     parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMCl / MMNaCl;
		chloride += 1000 * RA / liters;
		// Einde noot.

		if ($("#wa_acid_name").val() == "") {
			$("#wa_acid_name").val('Melkzuur');
			last_acid = 'Melkzuur';
		}
		if ($("#wa_base_name").val() == "") {
			$("#wa_base_name").val('NaHCO3');
			last_base = 'NaHCO3';
		}

		var AT = $("#wa_acid_name").val();
		var BT = $("#wa_base_name").val();

		var result = GetAcidSpecs(AT);
		pK1 = result.pK1;
		pK2 = result.pK2;
		pK3 = result.pK3;
		MolWt = result.MolWt;
		AcidSG = result.AcidSG;
		AcidPrc = result.AcidPrc;
		console.log(AT+" pK1: "+pK1+" pK2: "+pK2+" pK3: "+pK3+" MolWt: "+MolWt+" AcidSG: "+AcidSG+" AcidPrc: "+AcidPrc);

		if (dataRecord.calc_acid) {
			TpH = parseFloat(dataRecord.mash_ph);
			protonDeficit = ProtonDeficit(TpH);
			console.log("calc_acid tgt: "+TpH+" protonDeficit: "+protonDeficit);
			if (protonDeficit > 0) { // Add acid
				$("#wa_base").val(0);
				setWaterAgent(last_base, 0);
				frac = CalcFrac(TpH, pK1, pK2, pK3);
				Acid = protonDeficit / frac;
	//			console.log("Required moles: "+Acid);
				Acid *= MolWt; // mg
				Acidmg = Acid;
	//			console.log("Required mg: "+Acidmg);
				Acid = Acid / AcidSG; // ml

				if (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) == 0)
					$("#wa_acid_perc").val(AcidPrc);
				Acid = Acid * AcidPrc / (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) / 100); // ml
				console.log("Final ml: "+Acid);
				$("#wa_acid").val(Math.round(Acid * 100) / 100);
				setWaterAgent(AT, Math.round(Acid * 100) / 100);

				bicarbonate = bicarbonate - protonDeficit * frac / liters;
				total_alkalinity = bicarbonate * 50 / 61;
			} else if (protonDeficit < 0) { //Add base
				$("#wa_acid").val(0);
				setWaterAgent(last_acid, 0);
				r1d = Math.pow(10, (TpH - 6.38));
				r2d = Math.pow(10, (TpH - 10.38));
				f1d = 1 / (1 + r1d + r1d * r2d);
				f2d = f1d * r1d;
				f3d = f2d * r2d;
				switch (BT) {
					case 'NaHCO3':	base = -protonDeficit / (f1d - f3d); //mmol totaal
							base = base * MMNaHCO3/1000; //gram
							$("#wa_base").val(Math.round(base * 100) / 100);
							setWaterAgent(BT, Math.round(base * 100) / 100);
							if (liters > 0) {
								// Na
								RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl +
								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMNa / MMNaHCO3;
								RA = 1000 * RA / liters;
								sodium = wg_sodium + RA;
								// HCO3
								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3;
								RA = 1000 * RA / liters;
								bicarbonate = wg_bicarbonate + RA;
								total_alkalinity = bicarbonate * 50 / 61;
							}
							break;
					case 'Na2CO3':	base = -protonDeficit / (2 * f1d + f2d); //mmol totaal
							base = base * MMNa2CO3/1000; //gram
							$("#wa_base").val(Math.round(base * 100) / 100);
							setWaterAgent(BT, Math.round(base * 100) / 100);
							if (liters > 0) {
								RA = parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMNa / MMNaCl +
								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * 2 * MMNa / MMNa2CO3;
								RA = 1000 * RA / liters;
								sodium = wg_sodium + RA;
								// HCO3
								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMHCO3 / MMNa2CO3;
								RA = 1000 * RA / liters;
								bicarbonate = wg_bicarbonate + RA;
								total_alkalinity = bicarbonate * 50 / 61;
							}
							break;
					case 'CaCO3':	base = -protonDeficit * (f1d - f3d); //mmol totaal
							base = base * MMCaCO3/1000; //gram
							//but only 1/3 is effective, so add 3 times as much
							base = 3 * base;
							$("#wa_base").val(Math.round(base * 100) / 100);
							setWaterAgent(BT, Math.round(base * 100) / 100);
							if (liters > 0) {
								//Bicarbonate
								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3;
								RA = 1000 * RA / liters;
								bicarbonate = wg_bicarbonate + RA;
								total_alkalinity = bicarbonate * 50 / 61;
								//Ca precipitates out as Ca10(PO4)6(OH)2
								RA = parseFloat($("#wa_cacl2").jqxNumberInput('decimal')) * MMCa / MMCaCl2 +
								     parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMCa / MMCaSO4 +
								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMCa / MMCaCO3;
								RA = 1000 * RA / liters;
								calcium = wg_calcium + RA;
							}
							break;
					case 'Ca(OH)2':	base = -protonDeficit / 19.3; // g
							$("#wa_base").val(Math.round(base * 100) / 100);
							setWaterAgent(BT, Math.round(base * 100) / 100);
							if (liters > 0) {
								// Bicarbonate
								RA = -protonDeficit / liters;
								total_alkalinity = wg_total_alkalinity + RA;
								bicarbonate = total_alkalinity * 61 / 50;
								// Calcium
								RA = parseFloat($("#wa_cacl2").jqxNumberInput('decimal')) * MMCa / MMCaCl2 +
								     parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMCa / MMCaSO4 +
								     parseFloat($("#wa_base").jqxNumberInput('decimal')) * MMCa / MMCaOH2;
								RA = 1000 * RA / liters;
								calcium = wg_calcium + RA;
							}
							break;
				}
			}
			ph = TpH;
			$('#wb_ph').val(Math.round(ph * 10) / 10);
		} else { // Manual
			console.log("calc_acid no");
			// First add base salts
			if (parseFloat($("#wa_base").jqxNumberInput('decimal')) > 0) {

			}

			TpH = parseFloat(dataRecord.mash_ph);
		//	pHa = MashpH();
			pHa = parseFloat($("#wb_ph").jqxNumberInput('decimal'));
			// Then calculate the new pH with added acids
			if (parseFloat($("#wa_acid").jqxNumberInput('decimal')) > 0) {
				console.log("TpH: "+TpH+" water: "+pHa);
				Acid = parseFloat($("#wa_acid").jqxNumberInput('decimal'));
				if (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) == 0)
					$("#wa_acid_perc").val(AcidPrc);
				Acid = Acid / AcidPrc * (parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')) / 100); // ml
				Acid = Acid * AcidSG; // ml
				Acid = Acid / MolWt;  // mg
				Acidmg = Acid;

				//find the pH where the protondeficit = protondeficit by the acid
				frac = CalcFrac(pHa, pK1, pK2, pK3);
				protonDeficit = Acid * frac;

				deltapH = 0.001;
				deltapd = 0.1;
				pd = ProtonDeficit(TpH);
				n = 0;
				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);

				while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 2000)) {
					n++;
					if (pd < (protonDeficit-deltapd))
						pHa = pHa - deltapH;
					else if (pd > (protonDeficit+deltapd))
						pHa = pHa + deltapH;
					frac = CalcFrac(pHa, pK1, pK2, pK3);
					protonDeficit = Acid * frac;
					pd = ProtonDeficit(pHa);
			//		console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
				}
				console.log("n: "+n+" pd: "+pd+" protonDeficit: "+protonDeficit+" frac: "+frac+" pHa: "+pHa);
				RA = wg_bicarbonate - protonDeficit * frac / liters;
				bicarbonate = RA;
				total_alkalinity = RA * 50 / 61;
				ph = pHa;
				$('#wb_ph').val(Math.round(ph * 10) / 10);
			}
		}

		if ((AT == 'Zwavelzuur') && (liters > 0)) {
			RA = parseFloat($("#wa_caso4").jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 +
			     parseFloat($("#wa_mgso4").jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 +
			     Acidmg / 1000 * MMSO4 / (MMSO4 + 2);
			RA = 1000 * RA / liters;
			sulfate = wg_sulfate + RA;	// Not add to sulfate??
		} else if ((AT == 'Zoutzuur') && (liters > 0)) {
			RA = parseFloat($("#wa_cacl2").jqxNumberInput('decimal')) * MMCl / MMCaCl2 +
			     parseFloat($("#wa_nacl").jqxNumberInput('decimal')) * MMCl / MMNaCl +
			     Acidmg / 1000 * MMCl / (MMCL + 1);
			RA = 1000 * RA / liters;
			chloride = wg_chloride + RA;
		}

		$('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
		$('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);	// Show real value too

                $('#wb_calcium').val(Math.round(calcium * 10) / 10);
                $('#wb_magnesium').val(Math.round(magnesium * 10) / 10);
                $('#wb_sodium').val(Math.round(sodium * 10) / 10);
                $('#wb_sulfate').val(Math.round(sulfate * 10) / 10);
                $('#wb_chloride').val(Math.round(chloride * 10) / 10);
		$('#wb_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10);

                if (calcium < 40) {
			setRangeIndicator("calcium", "low");
		} else if (calcium > 150) {
			setRangeIndicator("calcium", "high");
		} else {
			setRangeIndicator("calcium", "normal");
		}
                if (magnesium >= 0 && magnesium <= 30) {
			setRangeIndicator("magnesium", "normal");
		} else {
			setRangeIndicator("magnesium", "high");
		}
                if (sodium <= 150) {
			setRangeIndicator("sodium", "normal");
		} else {
			setRangeIndicator("sodium", "high");
		}
                if (chloride <= 100) {
			setRangeIndicator("chloride", "normal");
		} else {
			setRangeIndicator("chloride", "high");
		}
                if (sulfate <= 350) {
			setRangeIndicator("sulfate", "normal");
		} else {
			setRangeIndicator("sulfate", "high");
		}
		if (ph < 5.2) {
			setRangeIndicator("ph", "low");
		} else if (ph > 5.6) {
			setRangeIndicator("ph", "high");
		} else {
			setRangeIndicator("ph", "normal");
		}
	}

	function calcFermentablesFromOG(OG) {

		console.log("calcFermentablesFromOG("+OG+")");
		var	i;
		var	efficiency = parseFloat($("#efficiency").jqxNumberInput('decimal'));
		var	rows = $('#fermentableGrid').jqxGrid('getrows');
		var	sug = sg_to_plato(OG) * parseFloat($("#batch_size").jqxNumberInput('decimal')) * OG / 100;	//total amount of sugars in kg
		var	tot = 0;
		var	d;
		for (i = 0; i < rows.length; i++) {
			row = rows[i];
			d = row.f_percentage / 100 * (row.f_yield / 100) * (1 - row.f_moisture / 100);
			if (row.f_added == "Mash")
				d = efficiency / 100 * d;
			tot += d;
		}

		var	totmass = 0;
		if (tot)
			totmass = sug / tot;

		if (totmass) {
			for (i = 0; i < rows.length; i++) {
				row = rows[i];
				$("#fermentableGrid").jqxGrid('setcellvalue', i, "f_amount", row.f_percentage / 100 * totmass);
			}
		}
		//CalcWaterBalance;
	};

	function calcABV() {
		$("#est_abv").val(abvol(parseFloat($("#est_og").jqxNumberInput('decimal')), parseFloat($("#est_fg").jqxNumberInput('decimal'))));
	};

	function calcInit () {
		console.log("calc.init()");

		$("#calc_acid").on('checked', function (event) {
			dataRecord.calc_acid = true;
			calcWater();
		});
		$("#calc_acid").on('unchecked', function (event) {
			dataRecord.calc_acid = false;
			calcWater();
		});
		$("#w1_name").jqxDropDownList('selectItem', dataRecord.w1_name);
		$("#w2_name").jqxDropDownList('selectItem', dataRecord.w2_name);
		// Fix tap water if zero using mash infuse amount.
		if (parseFloat($("#wg_amount").jqxNumberInput('decimal')) == 0 && mash_infuse > 0) {
			$("#w1_amount").val(mash_infuse);
			dataRecord.w1_amount = mash_infuse;
			$("#wg_amount").val(mash_infuse);
			$("#w2_amount").val(0);
			dataRecord.w2_amount = 0;
		}
		calcWater();
		$("#w2_amount").on('change', function (event) {
			var newval = parseFloat(event.args.value);

			if (newval > mash_infuse) {
				$("#w2_amount").val(dataRecord.w2_amount);
				return;
			}
			dataRecord.w1_amount = parseFloat($("#wg_amount").jqxNumberInput('decimal')) - newval;
			$("#w1_amount").val(dataRecord.w1_amount);
			dataRecord.w2_amount = newval;
			console.log("new: "+event.args.value+" w1: "+dataRecord.w1_amount+"  w2: "+dataRecord.w2_amount);
			calcWater();
		});
		$('#wa_cacl2').on('change', function (event) {
			setWaterAgent('CaCl2', event.args.value);
			calcWater();
		});
		$('#wa_caso4').on('change', function (event) {
			setWaterAgent('CaSO4', event.args.value);
			calcWater();
		});
		$('#wa_mgso4').on('change', function (event) {
			setWaterAgent('MgSO4', event.args.value);
			calcWater();
		});
		$('#wa_nacl').on('change', function (event) {
			setWaterAgent('NaCl', event.args.value);
			calcWater();
		});
		$('#wa_base_name').on('change', function (event) {
			setWaterAgent(last_base, 0);
			last_base = event.args.item.value;
			setWaterAgent(last_base, parseFloat($("#wa_base").jqxNumberInput('decimal')));
			calcWater();
		});
		$('#wa_base').on('change', function (event) {
			setWaterAgent($("#wa_base_name").val(), parseFloat(event.args.value));
			calcWater();
		});
		$('#wa_acid_name').on('change', function (event) {
			setWaterAgent(last_acid, 0);
			last_acid = event.args.item.value;
			setWaterAgent(last_acid, parseFloat($("#wa_acid").jqxNumberInput('decimal')));
			calcWater();
		});
		$('#wa_acid').on('change', function (event) {
			setWaterAgent($("#wa_acid_name").val(), parseFloat(event.args.value));
			calcWater();
		});
		$('#wa_acid_perc').on('change', function (event) { calcWater(); });

		$('#color_method').on('change', function (event) { calcFermentables(); });
		$('#ibu_method').on('change', function (event) {
			calcFermentables();
			calcIBUs(); 
		});
		$('#batch_size').on('change', function (event) {
			console.log("batch_size change:"+event.args.value+" old:"+dataRecord.batch_size);
			var new_boil = parseFloat(event.args.value) + dataRecord.boil_size - dataRecord.batch_size;
			dataRecord.boil_size = new_boil;
			$("#boil_size").val(Math.round(new_boil * 100) / 100);
			dataRecord.batch_size = parseFloat(event.args.value);
			calcFermentablesFromOG(parseFloat($("#est_og").jqxNumberInput('decimal')));     // Keep the OG
			calcFermentables();
			calcSVG();
			calcABV();
			// TODO: adjust the hops, miscs, yeast, water.
			calcIBUs();
		});
		$('#boil_time').on('change', function (event) {
			console.log("boil_time change:"+parseFloat(event.args.value)+" old:"+dataRecord.boil_time);
			var old_evap = parseFloat(dataRecord.boil_size) - parseFloat(dataRecord.batch_size);
			var new_evap = old_evap * (parseFloat(event.args.value) / dataRecord.boil_time);
			var new_boil = parseFloat(dataRecord.batch_size) + new_evap;
	//		console.log("old_evap:"+old_evap+" new_evap:"+new_evap+" new_boil:"+new_boil);
			dataRecord.boil_time = parseFloat(event.args.value);
			dataRecord.boil_size = new_boil;
			$("#boil_size").val(Math.round(new_boil * 100) / 100);
			calcFermentables();
			calcSVG();
			calcABV();
			// TODO: adjust the hops, miscs, yeast, water.
			calcIBUs();
		});
		$('#efficiency').on('change', function (event) {
			console.log("efficiency change:"+event.args.value);
			calcFermentables();
			calcSVG();
			calcABV();
			calcIBUs();
		});
		$('#est_og').on('change', function (event) {
			console.log("est_og change:"+event.args.value);
			calcFermentablesFromOG(event.args.value);       // Adjust fermentables amounts
			calcFermentables();                             // Update the recipe details
			calcSVG();
			calcABV();                                      // and ABV
			calcIBUs();                                     // and the IBU's.
		});
		$('#mash_ph').on('change', function (event) {
			dataRecord.mash_ph = parseFloat(event.args.value);
			calcWater();
		});
	};

	$("#styleSelect").jqxDropDownList({
		placeHolder: "Kies bierstijl:",
		theme: theme,
		source: styleslist,
		displayMember: "name",
		width: 150,
		height: 27,
		dropDownVerticalAlignment: 'top',
		dropDownWidth: 500,
		dropDownHeight: 380,
		renderer: function (index, label, value) {
			var datarecord = styleslist.records[index];
			return datarecord.style_guide + " " + datarecord.style_letter+ " " + datarecord.name;
		}
	});
	$("#styleSelect").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = styleslist.records[index];
			$("#st_name").val(datarecord.name);
			$("#st_category").val(datarecord.category);
			$("#st_category_number").val(datarecord.category_number);
			$("#st_letter").val(datarecord.style_letter);
			$("#st_guide").val(datarecord.style_guide);
			$("#st_type").val(datarecord.type);
			$("#st_og_min").val(datarecord.og_min);
			$("#st_og_max").val(datarecord.og_max);
			$("#st_fg_min").val(datarecord.fg_min);
			$("#st_fg_max").val(datarecord.fg_max);
			$("#st_ibu_min").val(datarecord.ibu_min);
			$("#st_ibu_max").val(datarecord.ibu_max);
			$("#st_color_min").val(datarecord.color_min);
			$("#st_color_max").val(datarecord.color_max);
			$("#st_carb_min").val(datarecord.carb_min);
			$("#st_carb_max").val(datarecord.carb_max);
			$("#st_abv_min").val(datarecord.abv_min);
			$("#st_abv_max").val(datarecord.abv_max);
		}
	});

	var dataRecord = {};
	var url = "includes/db_recipes.php";
	// tooltips
	$("#name").jqxTooltip({ content: 'De naam voor dit recept.' });
	$("#notes").jqxTooltip({ content: 'De uitgebreide opmerkingen over dit recept.' });
	$("#type").jqxTooltip({ content: 'Het brouw type van dit recept.' });
	$("#batch_size").jqxTooltip({ content: 'Het volume van het gekoelde wort na het koken.' });
	$("#boil_time").jqxTooltip({ content: 'De kooktijd in minuten.' });
	$("#boil_size").jqxTooltip({ content: 'Het volume van het wort voor het koken.' });
	$("#efficiency").jqxTooltip({ content: 'Het rendement van maischen en koken.' });
	$("#est_og").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
	$("#est_og2").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
	$("#est_fg").jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' });
	$("#est_color").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
	$("#est_color2").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
	$("#est_ibu").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
	$("#est_ibu2").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
	$("#est_abv").jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
	$("#est_carb").jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' });

	$("#st_name").jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
	$("#st_letter").jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
	$("#st_guide").jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
	$("#st_category").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
	$("#st_category_number").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
	$("#st_type").jqxTooltip({ content: 'Het bierstijl type.'});
	$("#st_og_min").jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'});
	$("#st_og_max").jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'});
	$("#st_fg_min").jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'});
	$("#st_fg_max").jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'});
	$("#st_color_min").jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
	$("#st_color_max").jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
	$("#st_ibu_min").jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
	$("#st_ibu_max").jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
	$("#st_abv_min").jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
	$("#st_abv_max").jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
	$("#st_carb_min").jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
	$("#st_carb_max").jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});
	$("#mash_ph").jqxTooltip({ content: 'Maisch pH tussen 5.2 en 5.6. Gebruik 5.2 voor lichte en 5.5 voor donkere bieren.'});
	$("#wa_cacl2").jqxTooltip({ content: 'Voor het maken van een ander waterprofiel. Voegt calcium en chloride toe. Voor het verbeteren van zoetere bieren.'});
	$("#wa_caso4").jqxTooltip({ content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.'});
	$("#wa_mgso4").jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'});
	$("#wa_nacl").jqxTooltip({ content: 'Keukenzout. Voor het maken van een ander waterprofiel. Voegt natrium en chloride toe. Voor het accentueren van zoetheid. Bij hoge dosering wordt het bier ziltig.'});
	$("#w2_amount").jqxTooltip({ content: 'De verdeling van het hoofd en meng water. Het totale maisch water volume blijft gelijk.'});

	$("#wb_calcium").jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'});
	$("#wb_magnesium").jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is lager dan 30.'});
	$("#wb_sodium").jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'});
	$("#wb_chloride").jqxTooltip({ content: 'De ideale hoeveelheid Chloride is lager dan 100.'});
	$("#wb_sulfate").jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is lager dan 350.'});

	// prepare the data
	var source = {
		datatype: "json",
		cache: false,
		datafields: [
			{ name: 'record', type: 'number' },
			{ name: 'st_name', type: 'string' },
			{ name: 'st_letter', type: 'string' },
			{ name: 'st_guide', type: 'string' },
			{ name: 'st_type', type: 'string' },
			{ name: 'st_category', type: 'string' },
			{ name: 'st_category_number', type: 'float' },
			{ name: 'st_og_min', type: 'float' },
			{ name: 'st_og_max', type: 'float' },
			{ name: 'st_fg_min', type: 'float' },
			{ name: 'st_fg_max', type: 'float' },
			{ name: 'st_ibu_min', type: 'float' },
			{ name: 'st_ibu_max', type: 'float' },
			{ name: 'st_color_min', type: 'float' },
			{ name: 'st_color_max', type: 'float' },
			{ name: 'st_carb_min', type: 'float' },
			{ name: 'st_carb_max', type: 'float' },
			{ name: 'st_abv_min', type: 'float' },
			{ name: 'st_abv_max', type: 'float' },
			{ name: 'name', type: 'string' },
			{ name: 'notes', type: 'string' },
			{ name: 'type', type: 'string' },
			{ name: 'batch_size', type: 'float' },
			{ name: 'boil_size', type: 'float' },
			{ name: 'boil_time', type: 'float' },
			{ name: 'efficiency', type: 'float' },
			{ name: 'est_og', type: 'float' },
			{ name: 'est_fg', type: 'float' },
			{ name: 'est_abv', type: 'float' },
			{ name: 'est_color', type: 'float' },
			{ name: 'color_method', type: 'string' },
			{ name: 'est_ibu', type: 'float' },
			{ name: 'ibu_method', type: 'string' },
			{ name: 'est_carb', type: 'float' },
			{ name: 'sparge_temp', type: 'float' },
			{ name: 'sparge_ph', type: 'float' },
			{ name: 'sparge_volume', type: 'float' },
			{ name: 'sparge_acid_type', type: 'string' },
			{ name: 'sparge_acid_perc', type: 'float' },
			{ name: 'sparge_acid_amount', type: 'float' },
			{ name: 'mash_ph', type: 'float' },
			{ name: 'mash_name', type: 'string' },
			{ name: 'calc_acid', type: 'bool' },
			{ name: 'w1_name', type: 'string' },
			{ name: 'w1_amount', type: 'float' },
			{ name: 'w1_calcium', type: 'float' },
			{ name: 'w1_sulfate', type: 'float' },
			{ name: 'w1_chloride', type: 'float' },
			{ name: 'w1_sodium', type: 'float' },
			{ name: 'w1_magnesium', type: 'float' },
			{ name: 'w1_total_alkalinity', type: 'float' },
			{ name: 'w1_ph', type: 'float' },
			{ name: 'w1_cost', type: 'float' },
			{ name: 'w2_name', type: 'string' },
			{ name: 'w2_amount', type: 'float' },
			{ name: 'w2_calcium', type: 'float' },
			{ name: 'w2_sulfate', type: 'float' },
			{ name: 'w2_chloride', type: 'float' },
			{ name: 'w2_sodium', type: 'float' },
			{ name: 'w2_magnesium', type: 'float' },
			{ name: 'w2_total_alkalinity', type: 'float' },
			{ name: 'w2_ph', type: 'float' },
			{ name: 'w2_cost', type: 'float' },
			{ name: 'fermentables', type: 'array' },
			{ name: 'hops', type: 'string' },
			{ name: 'miscs', type: 'string' },
			{ name: 'yeasts', type: 'string' },
			{ name: 'mashs', type: 'string' }
		],
		id: 'record',
		url: url + '?record=' + my_record
	};
	// Load data and select one record.
	var dataAdapter = new $.jqx.dataAdapter(source, {
		loadComplete: function () {
			var records = dataAdapter.records;
			dataRecord = records[0];
			$("#name").val(dataRecord.name);
			$("#notes").val(dataRecord.notes);
			$("#st_name").val(dataRecord.st_name);
			$("#st_letter").val(dataRecord.st_letter);
			$("#st_guide").val(dataRecord.st_guide);
			$("#st_category").val(dataRecord.st_category);
			$("#st_category_number").val(dataRecord.st_category_number);
			$("#st_type").val(dataRecord.st_type);
			$("#type").val(dataRecord.type);
			$("#batch_size").val(dataRecord.batch_size);
			$("#boil_size").val(dataRecord.boil_size);
			$("#boil_time").val(dataRecord.boil_time);
			$("#efficiency").val(dataRecord.efficiency);
			$("#est_og").val(dataRecord.est_og);
			$("#est_og2").val(dataRecord.est_og);
			$("#st_og_min").val(dataRecord.st_og_min);
			$("#st_og_max").val(dataRecord.st_og_max);
			$("#est_fg").val(dataRecord.est_fg);
			$("#st_fg_min").val(dataRecord.st_fg_min);
			$("#st_fg_max").val(dataRecord.st_fg_max);
			$("#est_color").val(dataRecord.est_color);
			$("#est_color2").val(dataRecord.est_color);
			$("#est_abv").val(dataRecord.est_abv);
			$("#st_abv_min").val(dataRecord.st_abv_min);
			$("#st_abv_max").val(dataRecord.st_abv_max);
			$("#st_color_min").val(dataRecord.st_color_min);
			$("#st_color_max").val(dataRecord.st_color_max);
			$("#color_method").val(dataRecord.color_method);
			$("#est_ibu").val(dataRecord.est_ibu);
			$("#est_ibu2").val(dataRecord.est_ibu);
			$("#st_ibu_min").val(dataRecord.st_ibu_min);
			$("#st_ibu_max").val(dataRecord.st_ibu_max);
			$("#ibu_method").val(dataRecord.ibu_method);
			$("#est_carb").val(dataRecord.est_carb);
			$("#st_carb_min").val(dataRecord.st_carb_min);
			$("#st_carb_max").val(dataRecord.st_carb_max);
			$("#mash_name").val(dataRecord.mash_name);
			$("#mash_ph").val(dataRecord.mash_ph);
			$("#sparge_temp").val(dataRecord.sparge_temp);
			$("#sparge_ph").val(dataRecord.sparge_ph);
			$("#sparge_volume").val(dataRecord.sparge_volume);
			$("#sparge_acid_type").val(dataRecord.sparge_acid_type);
			$("#sparge_acid_perc").val(dataRecord.sparge_acid_perc);
			$("#sparge_acid_amount").val(dataRecord.sparge_acid_amount);
			$("#calc_acid").val(dataRecord.calc_acid);
			$("#w1_name").val(dataRecord.w1_name);
			$("#w1_amount").val(dataRecord.w1_amount);
			$("#w1_calcium").val(dataRecord.w1_calcium);
			$("#w1_sulfate").val(dataRecord.w1_sulfate);
			$("#w1_chloride").val(dataRecord.w1_chloride);
			$("#w1_sodium").val(dataRecord.w1_sodium);
			$("#w1_magnesium").val(dataRecord.w1_magnesium);
			$("#w1_total_alkalinity").val(dataRecord.w1_total_alkalinity);
			$("#w1_ph").val(dataRecord.w1_ph);
			$("#w1_cost").val(dataRecord.w1_cost);
			$("#w2_name").val(dataRecord.w2_name);
			$("#w2_amount").val(dataRecord.w2_amount);
			$("#w2_calcium").val(dataRecord.w2_calcium);
			$("#w2_sulfate").val(dataRecord.w2_sulfate);
			$("#w2_chloride").val(dataRecord.w2_chloride);
			$("#w2_sodium").val(dataRecord.w2_sodium);
			$("#w2_magnesium").val(dataRecord.w2_magnesium);
			$("#w2_total_alkalinity").val(dataRecord.w2_total_alkalinity);
			$("#w2_ph").val(dataRecord.w2_ph);
			$("#w2_cost").val(dataRecord.w2_cost);
			editFermentable(dataRecord);
			editHop(dataRecord);
			editMisc(dataRecord);
			editYeast(dataRecord);
			editMash(dataRecord);
			$('#jqxTabs').jqxTabs('next');
		},
		loadError: function (jqXHR, status, error) {
		},
		beforeLoadComplete: function (records) {
			$('#jqxLoader').jqxLoader('open');
		}
	});
	dataAdapter.dataBind();

	// Inline fermentables editor
	var editFermentable = function (data) {
		var fermentableSource = {
			localdata: data.fermentables,
			datatype: "local",
			datafields: [
				{ name: 'f_name', type: 'string' },
				{ name: 'f_origin', type: 'string' },
				{ name: 'f_supplier', type: 'string' },
				{ name: 'f_amount', type: 'float' },
				{ name: 'f_cost', type: 'float' },
				{ name: 'f_type', type: 'string' },
				{ name: 'f_yield', type: 'float' },
				{ name: 'f_color', type: 'float' },
				{ name: 'f_coarse_fine_diff', type: 'float' },
				{ name: 'f_moisture', type: 'float' },
				{ name: 'f_diastatic_power', type: 'float' },
				{ name: 'f_protein', type: 'float' },
				{ name: 'f_max_in_batch', type: 'float' },
				{ name: 'f_graintype', type: 'string' },
				{ name: 'f_added', type: 'string' },
				{ name: 'f_dissolved_protein', type: 'float' },
				{ name: 'f_recommend_mash', type: 'bool' },
				{ name: 'f_add_after_boil', type: 'bool' },
				{ name: 'f_adjust_to_total_100', type: 'bool' },
				{ name: 'f_percentage', type: 'float' },
				{ name: 'f_di_ph', type: 'float' },
				{ name: 'f_acid_to_ph_57', type: 'float' }
			],
			addrow: function (rowid, rowdata, position, commit) {
				commit(true);
			},
			deleterow: function (rowid, commit) {
				commit(true);
			}
		};
		var fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);
		$("#fermentableGrid").jqxGrid({
			width: 1150,
			height: 400,
			source: fermentableAdapter,
			theme: theme,
			selectionmode: 'singlerow',
			editmode: 'selectedcell',
			editable: true,
			localization: getLocalization(),
			showtoolbar: true,
			rendertoolbar: function (toolbar) {
				var me = this;
				var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
				toolbar.append(container);
				container.append('<div style="float: left; margin-left: 165px;" id="faddrowbutton"></div>');
				container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
				container.append('<div style="float: left; margin-left: 10px;" id="finstockbutton"></div>');
				container.append('<input style="float: left; margin-left: 400px;" id="fdeleterowbutton" type="button" value="Verwijder mout" />');
				// add fermentable from dropdownlist.
				$("#faddrowbutton").jqxDropDownList({
					placeHolder: "Kies mout:",
					theme: theme,
					source: fermentablelist,
					displayMember: "name",
					width: 150,
					height: 27,
					dropDownWidth: 500,
					dropDownHeight: 500,
					renderer: function (index, label, value) {
						var datarecord = fermentablelist.records[index];
						return datarecord.supplier+ " / " + datarecord.name + " (" + datarecord.color + " EBC)";
					}
				});
				$("#faddrowbutton").on('select', function (event) {
					if (event.args) {
						var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
						var index = event.args.index;
						var datarecord = fermentablelist.records[index];
						var row = {};
						row["f_name"] = datarecord.name;
						row["f_origin"] = datarecord.origin;
						row["f_supplier"] = datarecord.supplier;
						row["f_amount"] = 0;
						row["f_cost"] = datarecord.cost;
						row["f_type"] = datarecord.type;
						row["f_yield"] = datarecord.yield;
						row["f_color"] = datarecord.color;
						row["f_coarse_fine_diff"] = datarecord.coarse_fine_diff;
						row["f_moisture"] = datarecord.moisture;
						row["f_diastatic_power"] = datarecord.diastatic_power;
						row["f_protein"] = datarecord.protein;
						row["f_max_in_batch"] = datarecord.max_in_batch;
						row["f_graintype"] = datarecord.graintype;
						if (datarecord.add_after_boil) {
							row["f_added"] = "Primary";
						} else if ((datarecord.type == "Sugar") || (datarecord.type == "Adjunct")) {
							row["f_added"] = "Boil";
						} else {
							row["f_added"] = "Mash";
						}
						row["f_dissolved_protein"] = 0;
						row["f_recommend_mash"] = datarecord.recommend_mash;
						row["f_add_after_boil"] = datarecord.add_after_boil;
						if (rowscount == 0) {
							// The first fermentable
							row["f_adjust_to_total_100"] = 1;
							row["f_percentage"] = 100;
						} else {
							row["f_adjust_to_total_100"] = 0;
							row["f_percentage"] = 0;
						}
						row["f_di_ph"] = datarecord.di_ph;
						row["f_acid_to_ph_57"] = datarecord.acid_to_ph_57;
						var commit = $("#fermentableGrid").jqxGrid('addrow', null, row);
					}
				});

				$("#finstockbutton").jqxCheckBox({ theme: theme, height: 27 });
				$("#finstockbutton").on('change', function (event) {
					fermentableinstock = event.args.checked;
					fermentablelist.dataBind();
				});

				// delete selected fermentable.
				$("#fdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
				$("#fdeleterowbutton").on('click', function () {
					var selectedrowindex = $("#fermentableGrid").jqxGrid('getselectedrowindex');
					var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
					if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
						var id = $("#fermentableGrid").jqxGrid('getrowid', selectedrowindex);
						var percent = $('#fermentableGrid').jqxGrid('getcellvalue', id, "f_percentage");
						var amount = $('#fermentableGrid').jqxGrid('getcellvalue', id, "f_amount");
						var commit = $("#fermentableGrid").jqxGrid('deleterow', id);
					}
					rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
					if (rowscount > 1) {
						if (to_100) {
							for (var i = 0; i < rowscount; i++) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								if (rowdata.f_adjust_to_total_100) {
									rowdata.f_percentage += percent;
									rowdata.f_amount += amount;
								}
							}
						} else {
							var tw = 0;
							for (i = 0; i < rowscount; i++) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								tw += rowdata.f_amount;
							};
							for (i = 0; i < rowscount; i++) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								var percentage = Math.round(rowdata.f_amount / tw * 1000) / 10.0;
								$("#fermentableGrid").jqxGrid('setcellvalue', i, "f_percentage", percentage);
							};
						}
					} else {
						$("#fermentableGrid").jqxGrid('setcellvalue', 0, "f_percentage", 100);
					}
					calcFermentables();
					calcSVG();
					calcABV();
					calcIBUs();
				});
			},
			ready: function() {
				calcFermentables();
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
				{ text: 'Vergistbaar ingredi&euml;nt', editable: false, datafield: 'f_name',
				  cellsrenderer:  function (row, columnfield, value, defaulthtml, columnproperties) {
					var rowData = $("#fermentableGrid").jqxGrid('getrowdata', row);
					return "<span style='margin: 3px; margin-top: 6px; float: "+
						columnproperties.cellsalign+"'>" +rowData.f_supplier+" / "+rowData.f_name+" ("+rowData.f_color+" EBC)</span>";
				  }
			       	},
				{ text: 'Type', editable: false, align: 'center', cellsalign: 'center', width: 100, datafield: 'f_type' },
				{ text: 'Moment', width: 110, align: 'center', cellsalign: 'center', datafield: 'f_added', columntype: 'dropdownlist',
				  createeditor: function (row, column, editor) {
					var srcAdded = [ "Mash", "Boil", "Fermentation", "Lagering", "Bottle" ];
					editor.jqxDropDownList({ autoDropDownHeight: true, source: srcAdded });
				  }
				},
				{ text: 'Opbrengst', editable: false, datafield: 'f_yield', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
				{ text: 'Gewicht Kg', datafield: 'f_amount', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f3',
				  columntype: 'numberinput',
				  validation: function (cell, value) {
					// Maximum weight is the batch_size, just a simple check.
					var maxmout = parseFloat($("#batch_size").jqxNumberInput('decimal'));
				  	if (value < 0 || value > maxmout) {
						return { result: false, message: "Gewicht moet 0-"+maxmout+" zijn" };
				  	}
				  	return true;
				  },
				  initeditor: function (row, cellvalue, editor) {
					editor.jqxNumberInput({ inputMode: 'simple', min: 0, decimalDigits: 3, spinButtons: false });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					if (to_100) {
						return oldvalue;	// When using percentages, don't allow edited results.
					}
				  }
				},
				{ text: 'Percentage', datafield: 'f_percentage', width: 110, align: 'right', cellsalign: 'right', cellsformat: 'p1',
				  columntype: 'numberinput',
				  validation: function (cell, value) {
					if (value < 0 || value > 100) {
						return { result: false, message: "Percentage moet 0-100 zijn" };
					}
					return true;
				  },
				  initeditor: function (row, cellvalue, editor) {
					editor.jqxNumberInput({ decimalDigits: 1, min: 0, max: 100, spinButtons: false });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					oldvalue = Math.round(oldvalue * 10) / 10.0;
					var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
					if ((oldvalue != newvalue) && (rowscount > 1)) {
						var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', row);
						if (rowdata.f_adjust_to_total_100) {
							return oldvalue;
						}
						var diff = newvalue - oldvalue;
						var tw = 0;	// total weight
						for (i = 0; i < rowscount; i++) {
							var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
							tw += rowdata.f_amount;
						}
						if (to_100) {
							// Adjust this row and the 100% row.
							var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', row);
							rowdata.f_amount += tw * diff / 100;
							for (i = 0; i < rowscount; i++) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								if (rowdata.f_adjust_to_total_100) {
									rowdata.f_percentage -= diff;
									rowdata.f_amount -= tw * diff / 100;
								}
							}
						} else {
							// Adjust all the rows.
							var nw = tw * diff / 100;
							for (i = 0; i < rowscount; i++) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								if (i == row) {
									rowdata.f_amount += nw;
								} else {
									rowdata.f_amount -= nw / (rowscount - 1);
									rowdata.f_percentage = Math.round((rowdata.f_amount / tw) * 1000) / 10.0;
								}
							}
						}
					}
				  }
				},
				{ text: '100%', align: 'center', datafield: 'f_adjust_to_total_100', columntype: 'checkbox', width: 80,
			          cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					if (to_100) {
						var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
						for (i = 0; i < rowscount; i++) {
							if (i != row) {
								var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
								rowdata.f_adjust_to_total_100 = false;
							}
						}
					}
				  }
			  	}
			]
		});
		$("#fermentableGrid").on('cellendedit', function (event) {
			var args = event.args;
			console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
			// Make sure the grid itself is updated.
			$("#fermentableGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
			if ((args.datafield == 'f_amount') && (! to_100)) {
				// If one of the amounts is changed, recalculate the percentages.
				console.log("adjust percentages");
				var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
	                        if (rowscount > 1) {
					var tw = 0;
					for (i = 0; i < rowscount; i++) {
						var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
						tw += rowdata.f_amount;
					};
					for (i = 0; i < rowscount; i++) {
						var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
						var percentage = Math.round(rowdata.f_amount / tw * 1000) / 10.0;
						$("#fermentableGrid").jqxGrid('setcellvalue', i, "f_percentage", percentage);
					};
				} else {
					$("#fermentableGrid").jqxGrid('setcellvalue', 0, "f_percentage", 100);
				}
			};
			$('#fermentableGrid').jqxGrid('sortby', 'f_amount', 'desc');	// TODO: not reliable
			calcFermentables();
			calcSVG();
			calcABV();
			calcIBUs();	// Depends on gravity, so recalculate.
		});
	};

	// Inline hops editor
        var editHop = function (data) {
                var hopSource = {
                        localdata: data.hops,
                        datatype: "local",
			cache: false,
                        datafields: [
                                { name: 'h_name', type: 'string' },
                                { name: 'h_origin', type: 'string' },
                                { name: 'h_amount', type: 'float' },
                                { name: 'h_cost', type: 'float' },
				{ name: 'h_type', type: 'string' },
				{ name: 'h_form', type: 'string' },
				{ name: 'h_useat', type: 'string' },
                                { name: 'h_time', type: 'float' },
				{ name: 'h_alpha', type: 'float' },
				{ name: 'h_beta', type: 'float' },
				{ name: 'h_hsi', type: 'float' },
				{ name: 'h_humulene', type: 'float' },
				{ name: 'h_carophyllene', type: 'float' },
				{ name: 'h_cohumulone', type: 'float' },
				{ name: 'h_myrcene', type: 'float' },
				{ name: 'h_total_oil', type: 'float' },
				{ name: 'h_weight', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var hopAdapter = new $.jqx.dataAdapter(hopSource, {
			beforeLoadComplete: function (records) {
				var data = new Array();
				for (var i = 0; i < records.length; i++) {
					var row = records[i];
					row.h_weight = row.h_amount * 1000;
					data.push(row);
				}
			        return data;
			},
			loadError: function(jqXHR, status, error) {
				$('#err').text(status + ' ' + error);
			},
		});
                $("#hopGrid").jqxGrid({
                        width: 1050,
                        height: 400,
                        source: hopAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<div style="float: left; margin-left: 165px;" id="haddrowbutton"></div>');
				container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
				container.append('<div style="float: left; margin-left: 10px;" id="hinstockbutton"></div>');
                                container.append('<input style="float: left; margin-left: 280px;" id="hdeleterowbutton" type="button" value="Verwijder hop" />');
                                // add hop from dropdownlist.
                                $("#haddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies hop:",
                                        theme: theme,
                                        source: hoplist,
                                        displayMember: "name",
                                        width: 150,
                                        height: 27,
                                        dropDownWidth: 500,
					dropDownHeight: 500,
					renderer: function (index, label, value) {
						var datarecord = hoplist.records[index];
						return datarecord.origin+ " / " + datarecord.name + " (" + datarecord.alpha + "% &alpha;)";
					}
                                });
                                $("#haddrowbutton").on('select', function (event) {
                                        if (event.args) {
                                                var index = event.args.index;
                                                var datarecord = hoplist.records[index];
                                                var row = {};
                                                row["h_name"] = datarecord.name;
                                                row["h_origin"] = datarecord.origin;
                                                row["h_amount"] = 0;
                                                row["h_cost"] = datarecord.cost;
                                                row["h_type"] = datarecord.type;
						row["h_form"] = datarecord.form;
						row["h_useat"] = datarecord.useat;
						row["h_time"] = 0;
						row["h_alpha"] = datarecord.alpha;
						row["h_beta"] = datarecord.beta;
						row["h_hsi"] = datarecord.hsi;
						row["h_humulene"] = datarecord.humulene;
						row["h_carophyllene"] = datarecord.carophyllene;
						row["h_cohumulone"] = datarecord.cohumulone;
						row["h_myrcene"] = datarecord.myrcene;
						row["h_total_oil"] = datarecord.total_oil;
						row["h_weight"] = 0;
                                                var commit = $("#hopGrid").jqxGrid('addrow', null, row);
                                        }
                                });

				$("#hinstockbutton").jqxCheckBox({ theme: theme, height: 27 });
				$("#hinstockbutton").on('change', function (event) {
					hopinstock = event.args.checked;
					hoplist.dataBind();
				});

                                // delete selected hop.
                                $("#hdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
                                $("#hdeleterowbutton").on('click', function () {
                                        var selectedrowindex = $("#hopGrid").jqxGrid('getselectedrowindex');
                                        var rowscount = $("#hopGrid").jqxGrid('getdatainformation').rowscount;
                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
                                                var id = $("#hopGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#hopGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				$('#jqxTabs').jqxTabs('next');
			},
                        columns: [
                                { text: 'Hop', editable: false, datafield: 'h_name',
				  cellsrenderer: function (row, columnfield, value, defaulthtml, columnproperties) {
					var rowData = $("#hopGrid").jqxGrid('getrowdata', row);
					return "<span style='margin: 3px; margin-top: 6px; float: "+
						columnproperties.cellsalign+"'>" +rowData.h_origin+" / "+rowData.h_name+"</span>";
				  },
			       	},
                                { text: 'Type', editable: false, width: 90, align: 'center', cellsalign: 'center', datafield: 'h_type' },
				{ text: 'Vorm', editable: false, width: 90, align: 'center', cellsalign: 'center', datafield: 'h_form' },
                                { text: 'Alpha', editable: false, datafield: 'h_alpha', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
				{ text: 'Amount', hidden: true, datafield: 'h_amount' },
				{ text: 'Gewicht gr', datafield: 'h_weight', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f1',
				  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<div style='margin: 4px;' class='jqx-right-align'>" + dataAdapter.formatNumber(value, "f1") + " gr</div>";
				  },
				  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
					editor.jqxNumberInput({
						inputMode: 'simple', decimalDigits: 1, min: 0, max: parseFloat(dataRecord.batch_size * 200),
						spinButtons: false
					});
				  },
				  validation: function (cell, value) {
					var maxhops = parseFloat(dataRecord.batch_size) * 200;
					if (value < 0 || value > maxhops ) {
						return { result: false, message: "Gewicht moet tussen 0 en "+maxhops+" gram zijn" };
					}
					return true;
				  }
				},
				{ text: 'Gebruik', width: 110, align: 'center', cellsalign: 'center', datafield: 'h_useat', columntype: 'dropdownlist',
				  createeditor: function (row, column, editor) {
					  var srcUse = [ "Boil", "Dry Hop", "Mash", "First Wort", "Aroma" ];
					  editor.jqxDropDownList({ autoDropDownHeight: true, source: srcUse });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					if ((newvalue == "Mash") || (newvalue == "First Wort")) {
						$("#hopGrid").jqxGrid('setcellvalue', row, "h_time", parseFloat(dataRecord.boil_time));
					} else if (newvalue == "Aroma") {
						$("#hopGrid").jqxGrid('setcellvalue', row, "h_time", 0);
					}
				  }
				},
				{ text: 'Tijd', datafield: 'h_time', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f0',
				  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					if ((rowdata.h_useat == "Boil") || (rowdata.h_useat == "Dry Hop") || (rowdata.h_useat == "Dry hop"))
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+"</div>";
					else
						return "<div style='margin: 4px;' class='jqx-right-align'> </div>";
				  },
				  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
					editor.jqxNumberInput({ decimalDigits: 0, digits: 3, min: 0, max: parseFloat(dataRecord.boil_time) });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					var use = $("#hopGrid").jqxGrid('getcellvalue', row, "h_useat");
					if ((use == "Mash") || (use == "First Wort") || (use == "First wort") || (use == "Aroma"))
						return oldvalue;
				  },
				  validation: function (cell, value) {
					var high = parseFloat(dataRecord.boil_time);
				  	if (value < 0 || value > high ) {
						return { result: false, message: "De tijd moet  0-"+high+" zijn" };
					}
					return true;
				  }
				},
				{ text: 'IBU', editable: false, datafield: 'ibu', width: 80, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var ibu = toIBU(rowdata.h_useat,
							  rowdata.h_form,
							  preboil_sg,
							  parseFloat($("#batch_size").jqxNumberInput('decimal')),
							  parseFloat(rowdata.h_amount),
							  parseFloat(rowdata.h_time),
							  parseFloat(rowdata.h_alpha),
							  $("#ibu_method").val()
							 );
					calcIBUs();
					return "<div style='margin: 4px;' class='jqx-right-align'>" + dataAdapter.formatNumber(ibu, "f1") + "</div>";
				  }
				}
                        ]
                });
		$("#hopGrid").on('cellendedit', function (event) {
			var args = event.args;
			console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
			$("#hopGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
			if (args.datafield == 'h_weight')
				$("#hopGrid").jqxGrid('setcellvalue', args.rowindex, 'h_amount', args.value / 1000);
			//$('#hopGrid').jqxGrid('sortby', 'f_amount', 'desc');
		});
        };

	// Inline miscs editor
        var editMisc = function (data) {
                var miscSource = {
                        localdata: data.miscs,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'm_name', type: 'string' },
                                { name: 'm_amount', type: 'float' },
                                { name: 'm_cost', type: 'float' },
                                { name: 'm_type', type: 'string' },
                                { name: 'm_use_use', type: 'string' },
                                { name: 'm_time', type: 'float' },
                                { name: 'm_amount_is_weight', type: 'bool' },
				{ name: 'm_weight', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var miscAdapter = new $.jqx.dataAdapter(miscSource, {
			beforeLoadComplete: function (records) {
				var data = new Array();
				for (var i = 0; i < records.length; i++) {
					var row = records[i];
					row.m_weight = row.m_amount * 1000;
					data.push(row);
					// Initial set water agent values.
					switch (row.m_name) {
						case 'CaCl2':		$("#wa_cacl2").val(row.m_weight);
									break;
						case 'CaSO4':		$("#wa_caso4").val(row.m_weight);
									break;
						case 'MgSO4':		$("#wa_mgso4").val(row.m_weight);
									break;
						case 'NaCl':		$("#wa_nacl").val(row.m_weight);
									break;
						case 'Melkzuur':	$("#wa_acid_name").val('Melkzuur');
									$("#wa_acid").val(row.m_weight);
									$("#wa_acid_perc").val(80);
									last_acid = 'Melkzuur';
									break;
						case 'Zoutzuur':	$("#wa_acid_name").val('Zoutzuur');
									$("#wa_acid").val(row.m_weight);
									$("#wa_acid_perc").val(80);
									last_acid = 'Zoutzuur';
									break;
						case 'Fosforzuur':	$("#wa_acid_name").val('Fosforzuur');
									$("#wa_acid").val(row.m_weight);
									$("#wa_acid_perc").val(80);
									last_acid = 'Fosforzuur';
									break;
						case 'Zwavelzuur':	$("#wa_acid_name").val('Zwavelzuur');
									$("#wa_acid").val(row.m_weight);
									$("#wa_acid_perc").val(80);
									last_acid = 'Zwavelzuur';
									break;
						case 'NaHCO3':		$("#wa_base_name").val('NaHCO3');
									$("#wa_base").val(row.m_weight);
									last_base = 'NaHCO3';
									break;
						case 'Na2CO3':		$("#wa_base_name").val('Na2CO3');
									$("#wa_base").val(row.m_weight);
									last_base = 'Na2CO3';
									break;
						case 'CaCO3':		$("#wa_base_name").val('CaCO3');
									$("#wa_base").val(row.m_weight);
									last_base = 'CaCO3';
									break;
						case 'Ca(OH)2':		$("#wa_base_name").val('Ca(OH)2');
									$("#wa_base").val(row.m_weight);
									last_base = 'Ca(OH)2';
									break;
					}
				}
				return data;
			},
			loadError: function(jqXHR, status, error) {
				$('#err').text(status + ' ' + error);
			},
		});
                $("#miscGrid").jqxGrid({
                        width: 960,
                        height: 400,
                        source: miscAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<div style="float: left; margin-left: 165px;" id="maddrowbutton"></div>');
				container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
				container.append('<div style="float: left; margin-left: 10px;" id="minstockbutton"></div>');
                                container.append('<input style="float: left; margin-left: 200px;" id="mdeleterowbutton" type="button" value="Verwijder ingredient" />');
                                // add misc from dropdownlist.
                                $("#maddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies ingredient:",
                                        theme: theme,
                                        source: misclist,
                                        displayMember: "name",
                                        width: 150,
                                        height: 27,
                                        dropDownWidth: 500,
					dropDownHeight: 500
                                });
                                $("#maddrowbutton").on('select', function (event) {
                                        if (event.args) {
                                                var index = event.args.index;
                                                var datarecord = misclist.records[index];
                                                var row = {};
                                                row["m_name"] = datarecord.name;
                                                row["m_amount"] = 0;
                                                row["m_cost"] = datarecord.cost;
                                                row["m_type"] = datarecord.type;
                                                row["m_use_use"] = datarecord.use_use;
                                                row["m_time"] = 0;
						row["m_weight"] = 0;
                                                row["m_amount_is_weight"] = datarecord.amount_is_weight;
                                                var commit = $("#miscGrid").jqxGrid('addrow', null, row);
                                        }
                                });
				$("#minstockbutton").jqxCheckBox({ theme: theme, height: 27 });
				$("#minstockbutton").on('change', function (event) {
					miscinstock = event.args.checked;
					misclist.dataBind();
				});
                                // delete selected misc.
                                $("#mdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
                                $("#mdeleterowbutton").on('click', function () {
                                        var selectedrowindex = $("#miscGrid").jqxGrid('getselectedrowindex');
                                        var rowscount = $("#miscGrid").jqxGrid('getdatainformation').rowscount;
					var type = $("#miscGrid").jqxGrid('getcellvalue', selectedrowindex, "m_type");
                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount && type != "Water agent")  {
                                                var id = $("#miscGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#miscGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
                                { text: 'Ingredient', editable: false, datafield: 'm_name' },
                                { text: 'Type', editable: false, width: 120, align: 'center', cellsalign: 'center', datafield: 'm_type' },
                                { text: 'Gebruik', width: 110, align: 'center', cellsalign: 'center', datafield: 'm_use_use', columntype: 'dropdownlist',
				  createeditor: function (row, column, editor) {
					var srcUseUse = [ "Mash", "Boil", "Primary", "Secondary", "Bottling" ];
					editor.jqxDropDownList({ autoDropDownHeight: true, source: srcUseUse });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					var type = $("#miscGrid").jqxGrid('getcellvalue', row, "m_type");
					if (type == "Water agent")
						return oldvalue;
				  }
			       	},
				{ datafield: 'm_amount_is_weight', hidden: true },	// We need to declare this column
				{ datafield: 'm_amount', hidden: true },		// We need to declare this column
                                { text: 'Hoeveelheid', datafield: 'm_weight', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f2',
                                  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var vstr = rowdata.m_amount_is_weight ? "gr":"ml";
					return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value,"f2")+" "+vstr+"</div>";
				  },
                                  validation: function (cell, value) {
					var high = parseFloat(dataRecord.boil_size) * 1000;
                                        if (value < 0 || value > high) {
                                                return { result: false, message: "Hoeveelheid moet tussen 0 en "+high+" zijn" };
                                        }
                                        return true;
                                  },
				  initeditor: function (row, cellvalue, editor) {
					editor.jqxNumberInput({
						inputMode: 'simple', min: 0, max: parseFloat(dataRecord.boil_size) * 1000,
						decimalDigits: 2, spinButtons: false
					});
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					var type = $("#miscGrid").jqxGrid('getcellvalue', row, "m_type");
					if (type == "Water agent")
						return oldvalue;
				  }
                                },
                                { text: 'Tijd', datafield: 'm_time', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f0',
                                  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					if (rowdata.m_use_use == 'Boil') {
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" m</div>";
					} else if (rowdata.m_use_use == 'Secondary') {
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" d</div>";
					} else {
						var tijd = 0;
						return "<div style='margin: 4px;' class='jqx-right-align'> </div>";
					}
				  },
				  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
					editor.jqxNumberInput({ decimalDigits: 0, digits: 3, min: 0, max: parseFloat(dataRecord.boil_time) });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					var use = $("#miscGrid").jqxGrid('getcellvalue', row, "m_use_use");
					if ((use != "Boil") && (use != "Secondary"))
						return oldvalue;
				  },
                                  validation: function (cell, value) {
					var high = parseFloat(dataRecord.boil_time);
                                        if (value < 0 || value > high ) {
                                                return { result: false, message: "De tijd moet 0-"+high+" zijn" };
                                        }
                                        return true;
                                  }
                                }
                        ]
                });
		$("#miscGrid").on('cellendedit', function (event) {
			var args = event.args;
			console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
			$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
			if (args.datafield == 'm_weight') {
				$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, 'm_amount', parseFloat(args.value) / 1000);
			}
		});
        };

	// Inline yeasts editor
        var editYeast = function (data) {
                var yeastSource = {
                        localdata: data.yeasts,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'y_name', type: 'string' },
				{ name: 'y_laboratory', type: 'string' },
				{ name: 'y_product_id', type: 'string' },
                                { name: 'y_amount', type: 'float' },
                                { name: 'y_cost', type: 'float' },
                                { name: 'y_type', type: 'string' },
                                { name: 'y_form', type: 'string' },
                                { name: 'y_time', type: 'float' },
				{ name: 'y_min_temperature', type: 'float' },
				{ name: 'y_max_temperature', type: 'float' },
				{ name: 'y_attenuation', type: 'float' },
                                { name: 'y_amount_is_weight', type: 'bool' },
				{ name: 'y_use', type: 'string' },
				{ name: 'y_weight', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var yeastAdapter = new $.jqx.dataAdapter(yeastSource, {
			 beforeLoadComplete: function (records) {
				var data = new Array();
				for (var i = 0; i < records.length; i++) {
					var row = records[i];
					if (row.y_form == 'Liquid')
						row.y_weight = Math.round(row.y_amount * 17);
					else
						row.y_weight = row.y_amount * 1000;
					data.push(row);
				}
				return data;
			},
			loadError: function(jqXHR, status, error) {
				$('#err').text(status + ' ' + error);
			},
		});
                $("#yeastGrid").jqxGrid({
                        width: 1050,
                        height: 300,
                        source: yeastAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<div style="float: left; margin-left: 165px;" id="yaddrowbutton"></div>');
				container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
				container.append('<div style="float: left; margin-left: 10px;" id="yinstockbutton"></div>');
                                container.append('<input style="float: left; margin-left: 230px;" id="ydeleterowbutton" type="button" value="Verwijder gist" />');
                                // add yeast from dropdownlist.
                                $("#yaddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies gist:",
                                        theme: theme,
                                        source: yeastlist,
                                        displayMember: "name",
                                        width: 150,
                                        height: 27,
                                        dropDownWidth: 500,
					dropDownHeight: 500,
					renderer: function (index, label, value) {
						var datarecord = yeastlist.records[index];
						return datarecord.laboratory+" "+datarecord.product_id+" "+datarecord.name;
					}
                                });
                                $("#yaddrowbutton").on('select', function (event) {
                                        if (event.args) {
                                                var index = event.args.index;
                                                var datarecord = yeastlist.records[index];
                                                var row = {};
                                                row["y_name"] = datarecord.name;
						row["y_laboratory"] = datarecord.laboratory;
						row["y_product_id"] = datarecord.product_id;
						row["y_type"] = datarecord.type;
						row["y_form"] = datarecord.form;
                                                row["y_amount"] = 0;
                                                row["y_cost"] = datarecord.cost;
                                                row["y_use"] = "Primary";
                                                row["y_time"] = 0;
						if (datarecord.form == "Dry") {
							row["y_amount_is_weight"] = 1;
						} else {
							row["y_amount_is_weight"] = 0;
						}
						row["y_min_temperature"] = datarecord.min_temperature;
						row["y_max_temperature"] = datarecord.max_temperature;
						row["y_attenuation"] = datarecord.attenuation;
						row["y_weight"] = 0;
                                                var commit = $("#yeastGrid").jqxGrid('addrow', null, row);
                                        }
                                });
				$("#yinstockbutton").jqxCheckBox({ theme: theme, height: 27 });
				$("#yinstockbutton").on('change', function (event) {
					yeastinstock = event.args.checked;
					yeastlist.dataBind();
				});
                                // delete selected yeast.
                                $("#ydeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
                                $("#ydeleterowbutton").on('click', function () {
                                        var selectedrowindex = $("#yeastGrid").jqxGrid('getselectedrowindex');
                                        var rowscount = $("#yeastGrid").jqxGrid('getdatainformation').rowscount;
                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
                                                var id = $("#yeastGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#yeastGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				calcSVG();
				$('#jqxTabs').jqxTabs('next');
			},
                        columns: [
                                { text: 'Gist', editable: false, datafield: 'y_name' },
				{ text: 'Laboratorium', editable: false, width: 150, datafield: 'y_laboratory' },
				{ text: 'Code', editable: false, width: 90, datafield: 'y_product_id' },
                                { text: 'Soort', editable: false, width: 80, align: 'center', cellsalign: 'center', datafield: 'y_form' },
				{ text: 'Min.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_min_temperature' },
				{ text: 'Max.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_max_temperature' },
				{ text: 'Attn.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_attenuation', cellsformat: 'f1' },
				{ text: 'Voor', width: 100, align: 'center', cellsalign: 'center', datafield: 'y_use', columntype: 'dropdownlist', 
				  createeditor: function (row, column, editor) {
					var srcYUse = [ "Primary", "Secondary", "Bottle" ];
					editor.jqxDropDownList({ autoDropDownHeight: true, source: srcYUse });
				  }
				},
				{ datafield: 'y_amount', width: 90 },
                                { text: 'Hoeveel', datafield: 'y_weight', width: 110, align: 'right', cellsalign: 'right',
				  cellsformat: 'f1', columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					if (rowdata.y_form == 'Liquid') {
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" pk</div>";
					} else if (rowdata.y_form == 'Dry') {
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f1")+" gr</div>";
					} else {
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" ml</div>";
					}
				  },
				  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
					var form = $("#yeastGrid").jqxGrid('getcellvalue', args.rowindex, 'y_form');
					if (form == 'Dry') {
						editor.jqxNumberInput({ decimalDigits: 1, min: 0, spinButtons: false });
					} else {
						editor.jqxNumberInput({ decimalDigits: 0, min: 0, spinButtons: false });
					}
				  },
                                  validation: function (cell, value) {
                                        if (value < 0 || value > 100000000000 ) {
                                                return { result: false, message: "Hoeveelheid moet 0-~ zijn" };
                                        }
                                        return true;
                                  }
                                }
                        ]
                });
		$("#yeastGrid").on('cellendedit', function (event) {
			var args = event.args;
			console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
			$("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
			if (args.datafield == 'y_weight') {
				var form = $("#yeastGrid").jqxGrid('getcellvalue', args.rowindex, 'y_form');
				if (form == 'Liquid')
					$("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, 'y_amount', parseFloat(args.value * 0.0588));
				else
					$("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, 'y_amount', parseFloat(args.value / 1000));
			}
		});
        };

	// inline mash editor
        var editMash = function (data) {
		var generaterow = function () {
			var row = {};
			row["step_name"] = "Stap 1";
			row["step_type"] = "Infusion";
			row["step_infuse_amount"] = 15;
			row["step_temp"] = 62.0;
			row['step_time'] = 20.0;
			row['ramp_time'] = 1.0;
			row['end_temp'] = 62.0;
			return row;
		}
                var mashSource = {
                        localdata: data.mashs,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'step_name', type: 'string' },
                                { name: 'step_type', type: 'string' },
				{ name: 'step_infuse_amount', type: 'float' },
				{ name: 'step_temp', type: 'float' },
				{ name: 'step_time', type: 'float' },
				{ name: 'ramp_time', type: 'float' },
				{ name: 'end_temp', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var mashAdapter = new $.jqx.dataAdapter(mashSource, {
			beforeLoadComplete: function (records) {
				mash_infuse = 0;
				var data = new Array();
				for (var i = 0; i < records.length; i++) {
					var row = records[i];
					if (row.step_type == 'Infusion')
						mash_infuse += parseFloat(row.step_infuse_amount);
				}
			},	
		});
                $("#mashGrid").jqxGrid({
                        width: 960,
                        height: 400,
                        source: mashAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<input style="float: left; margin-left: 165px;" id="saddrowbutton" type="button" value="Nieuwe stap" />');
                                container.append('<input style="float: left; margin-left: 230px;" id="sdeleterowbutton" type="button" value="Verwijder stap" />');
				$("#saddrowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
				$("#saddrowbutton").on('click', function () {
					var datarow = generaterow();
					var commit = $("#mashGrid").jqxGrid('addrow', null, datarow);
				});
                                // delete selected yeast.
                                $("#sdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
                                $("#sdeleterowbutton").on('click', function () {
                                        var selectedrowindex = $("#mashGrid").jqxGrid('getselectedrowindex');
                                        var rowscount = $("#mashGrid").jqxGrid('getdatainformation').rowscount;
                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
                                                var id = $("#mashGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#mashGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				var fg = estimate_fg(psugar, pcara, 0, 0, 0, svg, parseFloat(parseFloat($("#est_og").jqxNumberInput('decimal'))));
				dataRecord.est_fg = fg;
				$('#est_fg').val(fg);
				calcInit();
				$('#jqxLoader').jqxLoader('close');
				$('#jqxTabs').jqxTabs('first');
			},
                        columns: [
				{ text: 'Stap naam', datafield: 'step_name' },
			        { text: 'Stap type', datafield: 'step_type', width: 110, columntype: 'dropdownlist',
				  createeditor: function (row, cellvalue, editor, celltext, cellwidth, cellheight) {
				  	var dataSource = [ "Infusion", "Temperature", "Decoction" ];
					editor.jqxDropDownList({ source: dataSource, dropDownHeight: 105 });
				  }
				},
				{ text: 'Temperatuur', datafield: 'step_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1',
				  validation: function (cell, value) {
					if (value < 35 || value > 80) {
						return { result: false, message: "De temperatuur moet tussen 35 en 80 zijn." };
					}
					return true;
				  }
				},
				{ text: 'Eind', datafield: 'end_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1',
				  validation: function (cell, value) {
					if (value < 35 || value > 80) {
						return { result: false, message: "De temperatuur moet tussen 35 en 80 zijn." };
					}
					return true;
				  }
				},
				{ text: 'Tijd', datafield: 'step_time', width: 70, align: 'right', cellsalign: 'right',
				  validation: function (cell, value) {
					if (value < 1 || value > 360) {
						return { result: false, message: "De tijd moet tussen 1 en 360 zijn." };
					}
					return true;
				  }
				},
				{ text: 'Stap', datafield: 'ramp_time', width: 70, align: 'right', cellsalign: 'right',
				  validation: function (cell, value) {
					if (value < 1 || value > 60) {
						return { result: false, message: "De tijd moet tussen 1 en 60 zijn." };
					}
					return true;
				  }
				},
				{ text: 'Infuse', datafield: 'step_infuse_amount', width: 70, align: 'right', cellsalign: 'right',
				  validation: function (cell, value) {
					if (value < 0 || value > 60) {
						return { result: false, message: "De waarde moet tussen 0 en 60 zijn." };
					}
					return true;
				  }
				}
                        ]
                });
		$("#mashGrid").on('cellendedit', function (event) {
			$('#mashGrid').jqxGrid('sortby', 'step_temp', 'asc');
		});
        };

	// initialize the input fields.
	var srcType = [ "All Grain", "Partial Mash", "Extract" ];
	var srcColor = [ "Morey", "Mosher", "Daniels" ];
	//var srcIBU = [ "Tinseth", "Rager", "Garetz", "Daniels", "Mosher", "Noonan" ];
	var srcIBU = [ "Tinseth", "Rager", "Daniels" ];	// Only these are supported at this time.
	var srcBase = [ "NaHCO3", "Na2CO3", "CaCO3", "Ca(OH)2" ];
	var srcAcid = [ "Melkzuur", "Zoutzuur", "Fosforzuur", "Zwavelzuur" ];
	$("#name").jqxInput({ theme: theme, width: 640, height: 23 });
	$("#notes").jqxInput({ theme: theme, width: 960, height: 200 });
	$("#st_name").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_letter").jqxInput({ theme: theme, width: 100, height: 23 });
	$("#st_guide").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_category").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_category_number").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_type").jqxInput({ theme: theme, width: 90, height: 23 });
	$("#type").jqxDropDownList({ theme: theme, source: srcType, width: 125, height: 23, dropDownHeight: 95 });
	$("#batch_size").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: 'L', symbolPosition: 'right' });
	$("#boil_size").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 2, readOnly: true, symbol: 'L', symbolPosition: 'right' });
	$("#boil_time").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, max: 360, decimalDigits: 0, spinButtons: true });
	$("#efficiency").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 40, max: 100, decimalDigits: 0, spinButtons: true, symbol: '%', symbolPosition: 'right'  });
	$("#est_og").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 1.000, max: 1.200, decimalDigits: 3, spinButtons: true, spinButtonsStep: 0.001 });
	$("#est_og2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_og_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_og_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });

	$("#est_fg").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_fg_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_fg_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });

	$("#est_abv").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_abv_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_abv_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });

	$("#est_color").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' EBC', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
	$("#est_color2").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' EBC', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_color_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_color_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#color_method").jqxDropDownList({ theme: theme, source: srcColor, width: 125, height: 23, dropDownHeight: 95 });

	$("#est_ibu").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' IBU', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
	$("#est_ibu2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_ibu_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_ibu_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#ibu_method").jqxDropDownList({ theme: theme, source: srcIBU, width: 125, height: 23, dropDownHeight: 95, dropDownVerticalAlignment: 'top' });

	$("#est_carb").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_carb_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_carb_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });

	$("#mash_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#mash_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, max: 8, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
	$("#sparge_temp").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 70, max: 98, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5 });
	// Several gauges
	$("#hop_flavour").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
	$("#hop_aroma").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
	$("#perc_malts").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
	$("#perc_sugars").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
	$("#perc_cara").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });

	// Water treatment
	$("#w1_name").jqxDropDownList({
		placeHolder: "Kies hoofd water:",
		theme: theme,
		source: waterlist,
		displayMember: "name",
		width: 250,
		height: 27,
		dropDownWidth: 400,
		dropDownHeight: 400
	});
	$("#w1_name").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = waterlist.records[index];
			dataRecord.w1_name = datarecord.name;
			$("#w1_calcium").val(datarecord.calcium);
			dataRecord.w1_calcium = datarecord.calcium;
			$("#w1_sulfate").val(datarecord.sulfate);
			dataRecord.w1_sulfate = datarecord.sulfate;
			$("#w1_chloride").val(datarecord.chloride);
			dataRecord.w1_chloride = datarecord.chloride;
			$("#w1_sodium").val(datarecord.sodium);
			dataRecord.w1_sodium = datarecord.sodium;
			$("#w1_magnesium").val(datarecord.magnesium);
			dataRecord.w1_magnesium = datarecord.magnesium;
			$("#w1_total_alkalinity").val(datarecord.total_alkalinity);
			dataRecord.w1_total_alkalinity = datarecord.total_alkalinity;
			$("#w1_ph").val(datarecord.ph);
			dataRecord.w1_ph = datarecord.ph;
			$("#w1_cost").val(datarecord.cost);
			dataRecord.w1_cost = datarecord.cost;
			calcWater();
		}
	});
	$("#w1_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w1_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#w2_name").jqxDropDownList({
		placeHolder: "Kies meng water:",
		theme: theme,
		source: waterlist,
		displayMember: "name",
		width: 250,
		height: 27,
		dropDownWidth: 400,
		dropDownHeight: 400
	});
	$("#w2_name").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = waterlist.records[index];
			dataRecord.w2_name = datarecord.name;
			$("#w2_calcium").val(datarecord.calcium);
			dataRecord.w2_calcium = datarecord.calcium;
			$("#w2_sulfate").val(datarecord.sulfate);
			dataRecord.w2_sulfate = datarecord.sulfate;
			$("#w2_chloride").val(datarecord.chloride);
			dataRecord.w2_chloride = datarecord.chloride;
			$("#w2_sodium").val(datarecord.sodium);
			dataRecord.w2_sodium = datarecord.sodium;
			$("#w2_magnesium").val(datarecord.magnesium);
			dataRecord.w2_magnesium = datarecord.magnesium;
			$("#w2_total_alkalinity").val(datarecord.total_alkalinity);
			dataRecord.w2_total_alkalinity = datarecord.total_alkalinity;
			$("#w2_ph").val(datarecord.ph);
			dataRecord.w2_ph = datarecord.ph;
			$("#w2_cost").val(datarecord.cost);
			dataRecord.w2_cost = datarecord.cost;
			$("#w2_amount").jqxNumberInput({ max: 100000, readOnly: false }); // Set high max to enable the spinbuttons.
			calcWater();
		}
	});
	$("#w2_amount").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 94, height: 23, min: 0, max: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5, readOnly: true });
	$("#w2_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#w2_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#wg_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wg_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#wb_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#wb_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#pr_name").jqxDropDownList({
		placeHolder: "Kies doel profiel:",
		theme: theme,
		source: waterprofiles,
		displayMember: "name",
		width: 250,
		height: 27,
		dropDownWidth: 400,
		dropDownHeight: 300
	});
	$("#pr_name").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = waterprofiles.records[index];
			$("#pr_calcium").val(datarecord.calcium);
			$("#pr_sulfate").val(datarecord.sulfate);
			$("#pr_chloride").val(datarecord.chloride);
			$("#pr_sodium").val(datarecord.sodium);
			$("#pr_magnesium").val(datarecord.magnesium);
			$("#pr_total_alkalinity").val(datarecord.total_alkalinity);
		}
        });
	$("#pr_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#pr_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#pr_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#pr_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#pr_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
	$("#pr_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#tgt_bu").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 2, readOnly: true });
	$("#tgt_cl_so4").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });

	$("#wa_cacl2").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
	$("#wa_caso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
	$("#wa_mgso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
	$("#wa_nacl").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, max: 1000, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });

	$("#calc_acid").jqxCheckBox({ theme: theme, width: 120, height: 23 });
	$("#wa_base_name").jqxDropDownList({ theme: theme, source: srcBase, width: 125, height: 23, dropDownHeight: 128 });
	$("#wa_base").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' gr', symbolPosition: 'right' });
	$("#wa_acid_name").jqxDropDownList({ theme: theme, source: srcAcid, width: 125, height: 23, dropDownHeight: 128 })
	$("#wa_acid").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' ml', symbolPosition: 'right' });
	$("#wa_acid_perc").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 80, height: 23, min: 0, max: 100, decimalDigits: 0, spinButtons: true, symbol: '%', symbolPosition: 'right' });

	$("#sparge_volume").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
	$("#sparge_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
	$("#sparge_acid_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 5, readOnly: true });

	// Tabs inside the popup window.
	$('#jqxTabs').jqxTabs({
		theme: theme,
		width: 1280,
		height: 630,
		autoHeight: false,
		position: 'top'
	});

	$("#Print").jqxButton({ template: "info", width: '80px', theme: theme });
	$("#Print").click(function () {
		// Open print in a new tab.
		var url="rec_print.php?record=" + my_record;
		window.open(url);
	});

	$("#Delete").jqxButton({ template: "danger", width: '80px', theme: theme });
	$("#Delete").click(function () {
		// Open a popup to confirm this action.
		$('#eventWindow').jqxWindow('open');
		$("#delOk").click(function () {
			var data = "delete=true&" + $.param({ record: my_record });
			$.ajax({
				dataType: 'json',
				url: url,
				cache: false,
				data: data,
				type: "POST",
				success: function (data, status, xhr) {
					// delete command is executed.
					window.location.href = my_return;
				},
				error: function (jqXHR, textStatus, errorThrown) {
				}
			});
		});
	});

	$("#Cancel").jqxButton({ template: "primary", width: '80px', theme: theme });
	$("#Cancel").click(function () {
		window.location.href = my_return;
	});

	$("#Save").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#Save").click(function () {
		var fermentablerow = $('#fermentableGrid').jqxGrid('getrows');
		var hoprow = $('#hopGrid').jqxGrid('getrows');
		var miscrow = $('#miscGrid').jqxGrid('getrows');
		var yeastrow = $('#yeastGrid').jqxGrid('getrows');
		var mashrow = $('#mashGrid').jqxGrid('getrows');
		var row = {
			record: my_record,
			name: $("#name").val(),
			notes: $("#notes").val(),
			st_name: $('#st_name').val(),
			st_letter: $('#st_letter').val(),
			st_guide: $('#st_guide').val(),
			st_type: $('#st_type').val(),
			st_category: $('#st_category').val(),
			st_category_number: parseFloat($("#st_category_number").jqxNumberInput('decimal')),
			st_og_min: parseFloat($("#st_og_min").jqxNumberInput('decimal')),
			st_og_max: parseFloat($("#st_og_max").jqxNumberInput('decimal')),
			st_fg_min: parseFloat($("#st_fg_min").jqxNumberInput('decimal')),
			st_fg_max: parseFloat($("#st_fg_max").jqxNumberInput('decimal')),
			st_ibu_min: parseFloat($("#st_ibu_min").jqxNumberInput('decimal')),
			st_ibu_max: parseFloat($("#st_ibu_max").jqxNumberInput('decimal')),
			st_color_min: parseFloat($("#st_color_min").jqxNumberInput('decimal')),
			st_color_max: parseFloat($("#st_color_max").jqxNumberInput('decimal')),
			st_carb_min: parseFloat($("#st_carb_min").jqxNumberInput('decimal')),
			st_carb_max: parseFloat($("#st_carb_max").jqxNumberInput('decimal')),
			st_abv_min: parseFloat($("#st_abv_min").jqxNumberInput('decimal')),
			st_abv_max: parseFloat($("#st_abv_max").jqxNumberInput('decimal')),
			type: $("#type").val(),
			batch_size: parseFloat($("#batch_size").jqxNumberInput('decimal')),
			boil_size: parseFloat($("#boil_size").jqxNumberInput('decimal')),
			boil_time: parseFloat($("#boil_time").jqxNumberInput('decimal')),
			efficiency: parseFloat($("#efficiency").jqxNumberInput('decimal')),
			est_og: parseFloat($("#est_og").jqxNumberInput('decimal')),
			est_fg: parseFloat($("#est_fg").jqxNumberInput('decimal')),
			est_abv: parseFloat($("#est_abv").jqxNumberInput('decimal')),
			est_color: parseFloat($("#est_color").jqxNumberInput('decimal')),
			color_method: $("#color_method").val(),
			est_ibu: parseFloat($("#est_ibu").jqxNumberInput('decimal')),
			ibu_method: $("#ibu_method").val(),
			est_carb: parseFloat($("#est_carb").jqxNumberInput('decimal')),
			mash_name: $("#mash_name").val(),
			mash_ph: parseFloat($("#mash_ph").jqxNumberInput('decimal')),
			sparge_temp: parseFloat($("#sparge_temp").jqxNumberInput('decimal')),
			sparge_ph: parseFloat($("#sparge_ph").jqxNumberInput('decimal')),
			sparge_volume: parseFloat($("#sparge_volume").jqxNumberInput('decimal')),
		//	sparge_acid_type: $("#sparge_acid_type").val(),
		//	sparge_acid_perc: parseFloat($("#sparge_acid_perc").jqxNumberInput('decimal')),
		//	sparge_acid_amount: parseFloat($("#sparge_acid_amount").jqxNumberInput('decimal')),
			calc_acid: $("#calc_acid").val(),
			w1_name: $("#w1_name").val(),
			w1_amount: parseFloat($("#w1_amount").jqxNumberInput('decimal')),
			w1_calcium: parseFloat($("#w1_calcium").jqxNumberInput('decimal')),
			w1_sulfate: parseFloat($("#w1_sulfate").jqxNumberInput('decimal')),
			w1_chloride: parseFloat($("#w1_chloride").jqxNumberInput('decimal')),
			w1_sodium: parseFloat($("#w1_sodium").jqxNumberInput('decimal')),
			w1_magnesium: parseFloat($("#w1_magnesium").jqxNumberInput('decimal')),
			w1_total_alkalinity: parseFloat($("#w1_total_alkalinity").jqxNumberInput('decimal')),
			w1_ph: parseFloat($("#w1_ph").jqxNumberInput('decimal')),
			w1_cost: dataRecord.w1_cost,
			w2_name: $("#w2_name").val(),
			w2_amount: parseFloat($("#w2_amount").jqxNumberInput('decimal')),
			w2_calcium: parseFloat($("#w2_calcium").jqxNumberInput('decimal')),
			w2_sulfate: parseFloat($("#w2_sulfate").jqxNumberInput('decimal')),
			w2_chloride: parseFloat($("#w2_chloride").jqxNumberInput('decimal')),
			w2_sodium: parseFloat($("#w2_sodium").jqxNumberInput('decimal')),
			w2_magnesium: parseFloat($("#w2_magnesium").jqxNumberInput('decimal')),
			w2_total_alkalinity: parseFloat($("#w2_total_alkalinity").jqxNumberInput('decimal')),
			w2_ph: parseFloat($("#w2_ph").jqxNumberInput('decimal')),
			w2_cost: dataRecord.w2_cost,
			fermentables: fermentablerow,
			hops: hoprow,
			miscs: miscrow,
			yeasts: yeastrow,
			mashs: mashrow
		};
		var data = "update=true&" + $.param(row);
		$.ajax({
			dataType: 'json',
			url: url,
			cache: false,
			data: data,
			type: "POST",
			success: function (data, status, xhr) {
				// update command is executed.
				window.location.href = my_return;
			},
			error: function(jqXHR, textStatus, errorThrown) {
			}
		});
	});
	createDelElements();
});

mercurial