www/js/rec_edit.js

Tue, 22 Jan 2019 20:27:45 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 22 Jan 2019 20:27:45 +0100
changeset 195
2ac491548d8d
parent 175
da16371538c3
child 220
14e349ff2a10
permissions
-rw-r--r--

Fermentables database bool to int types. Dropdown lists are using tables with indexes. Dropdown lists display in Dutch. Save and delete in the database now use POST instead of GET. Added the added and dissolved_protein fields. Removed the ibu_gal_per_lb field. Redesigned the web page. Code cleanup.

/*****************************************************************************
 * Copyright (C) 2018-2019
 *
 * 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 setReadonly(ro) {
		var rw = ! ro;
		var w100 = 100;
		var w80 = 80;
		if (ro) { // jqxNumberInput width -20 for no spinbuttons
			w100 = 80;
			w80 = 60;
		}
		$("#batch_size").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#boil_size").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#boil_time").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#efficiency").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#est_og").jqxNumberInput({ spinButtons: rw, readOnly: ro });
		$("#type").jqxDropDownList({ disabled: ro });
		$("#styleSelect").jqxDropDownList({ disabled: ro });
		$("#color_method").jqxDropDownList({ disabled: ro });
		$("#ibu_method").jqxDropDownList({ disabled: ro });
		$("#Delete").jqxButton({ disabled: ro });
		$("#fermentableGrid").jqxGrid({ editable: rw });
		$("#faddrowbutton").jqxDropDownList({ disabled: ro });
		$("#finstockbutton").jqxCheckBox({ disabled: ro });
		$("#fdeleterowbutton").jqxButton({ disabled: ro });
		$("#hopGrid").jqxGrid({ editable: rw });
		$("#haddrowbutton").jqxDropDownList({ disabled: ro });
		$("#hinstockbutton").jqxCheckBox({ disabled: ro });
		$("#hdeleterowbutton").jqxButton({ disabled: ro });
		$("#miscGrid").jqxGrid({ editable: rw });
		$("#maddrowbutton").jqxDropDownList({ disabled: ro });
		$("#minstockbutton").jqxCheckBox({ disabled: ro });
		$("#mdeleterowbutton").jqxButton({ disabled: ro });
		$("#yeastGrid").jqxGrid({ editable: rw });
		$("#yaddrowbutton").jqxDropDownList({ disabled: ro });
		$("#yinstockbutton").jqxCheckBox({ disabled: ro });
		$("#ydeleterowbutton").jqxButton({ disabled: ro });
		$("#mashGrid").jqxGrid({ editable: rw });
		$("#saddrowbutton").jqxButton({ disabled: ro });
		$("#sdeleterowbutton").jqxButton({ disabled: ro });
		$("#w1_name").jqxDropDownList({ disabled: ro });
		$("#w2_name").jqxDropDownList({ disabled: ro });
		$("#pr_name").jqxDropDownList({ disabled: ro });
		$("#wa_cacl2").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#wa_caso4").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#wa_mgso4").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#wa_nacl").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#mash_ph").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#calc_acid").jqxCheckBox({ disabled: ro });
		$("#wa_base_name").jqxDropDownList({ disabled: ro });
		$("#wa_base").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#wa_acid_name").jqxDropDownList({ disabled: ro });
		$("#wa_acid").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#wa_acid_perc").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w80 });
		$("#sparge_temp").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#sparge_volume").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#sparge_ph").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
		$("#sparge_source").jqxDropDownList({ disabled: ro });
		$("#sparge_acid_type").jqxDropDownList({ disabled: ro });
		$("#sparge_acid_perc").jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
	};

	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;
				}
			}
			if (! found) {
				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;
	}

	// mg/l as CaCO3
	function ResidualAlkalinity(total_alkalinity, calcium, magnesium) {
		return total_alkalinity - (calcium / 1.4 + magnesium / 1.7);
	}

	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) {
		var C43 = Charge(4.3);
		var Cw = Charge(parseFloat($("#wg_ph").jqxNumberInput('decimal')));
		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) {

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

	function ProtonDeficit(pHZ) {

		var Result = ZRA(pHZ) * parseFloat($("#wg_amount").jqxNumberInput('decimal'));
		// proton deficit for the grist
		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);
				} else {
					// If the acid_to_ph_5.7 is unknown from the maltster, 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;
					}
				}
				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 TpH = 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(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);
		var pK1 = result.pK1;
		var pK2 = result.pK2;
		var pK3 = result.pK3;
		var MolWt = result.MolWt;
		var AcidSG = result.AcidSG;
		var AcidPrc = result.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;
				Acid *= MolWt; // mg
				Acidmg = Acid;
				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);
				var r1d = Math.pow(10, (TpH - 6.38));
				var r2d = Math.pow(10, (TpH - 10.38));
				var f1d = 1 / (1 + r1d + r1d * r2d);
				var f2d = f1d * r1d;
				var f3d = f2d * r2d;
				switch (BT) {
					case 'NaHCO3':	RA = -protonDeficit / (f1d - f3d); //mmol totaal
							RA = RA * MMNaHCO3/1000; //gram
							$("#wa_base").val(Math.round(RA * 100) / 100);
							setWaterAgent(BT, Math.round(RA * 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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
							}
							break;
					case 'Na2CO3':	RA = -protonDeficit / (2 * f1d + f2d); //mmol totaal
							RA = RA * MMNa2CO3/1000; //gram
							$("#wa_base").val(Math.round(RA * 100) / 100);
							setWaterAgent(BT, Math.round(RA * 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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
							}
							break;
					case 'CaCO3':	RA = -protonDeficit * (f1d - f3d); //mmol totaal
							RA = RA * MMCaCO3/1000; //gram
							//but only 1/3 is effective, so add 3 times as much
							RA = 3 * RA;
							$("#wa_base").val(Math.round(RA * 100) / 100);
							setWaterAgent(BT, Math.round(RA * 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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
							}
							break;
					case 'Ca(OH)2':	RA = -protonDeficit / 19.3; // g
							$("#wa_base").val(Math.round(RA * 100) / 100);
							setWaterAgent(BT, Math.round(RA * 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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
							}
							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) {
				if (liters > 0) {
					switch (BT) {
						case 'NaHCO3':	// 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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
								break;
						case 'Na2CO3':	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;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
								break;
						case 'CaCO3':	// Bicarbonate
								RA = parseFloat($("#wa_base").jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3;
								RA = 1000 * RA / liters;
								bicarbonate = wg_bicarbonate + RA;
								total_alkalinity = bicarbonate * 50 / 61;
								RA = ResidualAlkalinity(wb_total_alkalinity, wb_calcium, wb_magnesium);
								// Ca
								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;
					}
				}
			}

			TpH = parseFloat(dataRecord.mash_ph);
			pHa = MashpH();	// This one is in demi water, should be in adjusted water???
			// 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 *= AcidSG; // ml
				Acid /= MolWt;  // mg
				Acidmg = Acid;

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

				var deltapH = 0.001;
				var deltapd = 0.1;
				var pd = ProtonDeficit(pHa);
				var 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 -= deltapH;
					else if (pd > (protonDeficit+deltapd))
						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);
				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;
		}

		// 2:1 Sulfate to Chroride IPA's, Pale Ales.
		// 1:1 Sulfate to Chloride Balanced
		// 1:2 Sulfate to Chloride Malty
		$('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
		$('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);	// Show real value too
		if (sulfate > 0)
			RA = chloride / sulfate;
		else
			RA = 10;
		var piCLSO4_low = 0.8 * GetOptClSO4ratio();
		var piCLSO4_high = 1.2 * GetOptClSO4ratio();
		console.log("low: "+piCLSO4_low+" val: "+RA+" high: "+piCLSO4_high);

                $('#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");
		}
		calcSparge();
	}

	function calcSparge() {

		// Code from BrewBuddy/Brouwhulp, who got it from http://www.brewery.org/brewery/library/Acidi0,00fWaterAJD0497.html
		var TargetpH = dataRecord.sparge_ph;
		var Source_pH = dataRecord.w1_ph;
		var Source_alkalinity = dataRecord.w1_total_alkalinity;
		// Select watersource or fallback to the first source.
		if (dataRecord.sparge_source == 'Bron 2') {
			if (dataRecord.w2_ph > 0.0) {
				Source_pH = dataRecord.w2_ph;
				Source_alkalinity = dataRecord.w2_total_alkalinity;
			} else {
				dataRecord.sparge_source = 'Bron 1';
				$("#sparge_source").val('Bron 1');
			}
		} else if (dataRecord.sparge_source == 'Gemengd') {
			if (dataRecord.w2_ph > 0.0) {
				Source_pH = parseFloat($("#wg_ph").jqxNumberInput('decimal'));
				Source_alkalinity = parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal'));
			} else {
				dataRecord.sparge_source = 'Bron 1';
				$("#sparge_source").val('Bron 1');
			}
		}

		console.log("calcSparge() target pH: "+TargetpH+" Source: "+Source_pH+" alkalinity: "+Source_alkalinity);

		// Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH
		var r1 = Math.pow(10, Source_pH - 6.38);
		var r2 = Math.pow(10, Source_pH - 10.33);
		var d = 1 + r1 + r1*r2;
		var f1 = 1/d;
		var f2 = r1/d;
		var f3 = r1 * r2 / d;

		//Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity)
		var r143 = Math.pow(10, 4.3 - 6.38);
		var r243 = Math.pow(10, 4.3 - 10.33);
		var d43 = 1 + r143 + r143*r243;
		var f143 = 1/d43;
		var f243 = r143 / d43;
		var f343 = r143 * r243 / d43;

		//Step 3. Convert the sample alkalinity to milliequivalents/L
		var alkalinity = Source_alkalinity / 50;
		//Step 4. Solve
		alkalinity = alkalinity / ((f143-f1)+(f3-f343));

		//Step 5. Compute mole fractions at desired pH
		var r1g = Math.pow(10, TargetpH - 6.38);
		var r2g = Math.pow(10, TargetpH - 10.33);
		var dg = 1 + r1g + r1g*r2g;
		var f1g = 1/dg;
		var f2g = r1g / dg;
		var f3g = r1g * r2g / dg;

		//Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
		var Acid = alkalinity * ((f1g-f1)+(f3-f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH);  //mEq/l

		if ($("#sparge_acid_type").val() == "") {
			$("#sparge_acid_type").val('Melkzuur');
			dataRecord.sparge_acid_type = 'Melkzuur';
		}
		var AT = dataRecord.sparge_acid_type;
		var result = GetAcidSpecs(AT);
		var pK1 = result.pK1;
		var pK2 = result.pK2;
		var pK3 = result.pK3;
		var MolWt = result.MolWt;
		var AcidSG = result.AcidSG;
		var AcidPrc = result.AcidPrc;
		var fract = CalcFrac(TargetpH, pK1, pK2, pK3);

		//Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid.
		Acid /= fract;

		//Step 10. Multiply by molecular weight of the acid
		Acid *= MolWt; //mg

		Acid = Acid / AcidSG; //ml ; 88% lactic solution
		f1 = dataRecord.sparge_acid_perc;
		if (f1 <= 0.1)
			f1 = AcidPrc;
		Acid = Acid * AcidPrc / (f1 / 100);

		Acid *= dataRecord.sparge_volume; //ml lactic acid total
		Acid = Math.round(Acid * 100) / 100;
		dataRecord.sparge_acid_amount = Acid / 1000;
		$("#sparge_acid_amount").val(Acid);
	}

	function calcFermentablesFromOG(OG) {

		console.log("calcFermentablesFromOG("+OG+")");
		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;
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			var 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 (var i = 0; i < rows.length; i++) {
				var 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();
		});

		$('#sparge_ph').on('change', function (event) {
			dataRecord.sparge_ph = parseFloat(event.args.value);
			calcSparge();
		});
		$('#sparge_volume').on('change', function (event) {
			dataRecord.sparge_volume = parseFloat(event.args.value);
			calcSparge();
		});
		$('#sparge_source').on('change', function (event) {
			dataRecord.sparge_source= event.args.item.value;
			calcSparge();
		});
		$('#sparge_acid_type').on('change', function (event) {
			dataRecord.sparge_acid_type = event.args.item.value;
			console.log("new sparge_acid_type: "+dataRecord.sparge_acid_type);
			calcSparge();
		});
		$('#sparge_acid_perc').on('change', function (event) {
			dataRecord.sparge_acid_perc = parseFloat(event.args.value);
			calcSparge();
		});
		$('#locked').on('checked', function (event) {
			setReadonly(true);
		});
		$('#locked').on('unchecked', function (event) {
			setReadonly(false);
		});
//		setReadonly(false);
	};

	$("#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: 'uuid', type: 'string' },
			{ name: 'locked', type: 'bool' },
			{ 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_source', type: 'string' },
			{ 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];
			// Hidden record uuid
			$("#name").val(dataRecord.name);
			$("#notes").val(dataRecord.notes);
			$("#locked").val(dataRecord.locked);
			$("#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_source").val(dataRecord.sparge_source);
			$("#sparge_acid_type").val(dataRecord.sparge_acid_type);
			$("#sparge_acid_perc").val(dataRecord.sparge_acid_perc);
			$("#sparge_acid_amount").val(dataRecord.sparge_acid_amount * 1000);
			$("#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",
			cache: false,
			async: false,
			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,
			async: 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,
			async: 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,
			async: 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,
			async: 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');
		//		setReadonly(dataRecord.locked);
			},
                        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" ];
	var srcSource = [ "Bron 1", "Bron 2", "Gemengd" ];
	$("#name").jqxInput({ theme: theme, width: 640, height: 23 });
	$("#locked").jqxCheckBox({ theme: theme, width: 120, 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 });
	// 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: 100, 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: 100, 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_temp").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 70, max: 98, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5 });
	$("#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, min: 4.5, max: 6.5, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
	$("#sparge_source").jqxDropDownList({ theme: theme, source: srcSource, width: 100, height: 23, dropDownHeight: 95 });
	$("#sparge_acid_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 2, readOnly: true, symbol: ' ml', symbolPosition: 'right' });
	$("#sparge_acid_type").jqxDropDownList({ theme: theme, source: srcAcid, width: 100, height: 23, dropDownHeight: 128 });
	$("#sparge_acid_perc").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, spinButtons: true, decimalDigits: 0, symbol: '%', symbolPosition: 'right' });

	// 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);
	});

	$("#Export").jqxButton({ template: "info", width: '80px', theme: theme });
	$("#Export").click(function () {
		// Open print in a new tab.
		var url="rec_export.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,
			uuid: dataRecord.uuid,
			name: $("#name").val(),
			locked: $("#locked").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_source: $("#sparge_source").val(),
			sparge_acid_type: $("#sparge_acid_type").val(),
			sparge_acid_perc: parseFloat($("#sparge_acid_perc").jqxNumberInput('decimal')),
			sparge_acid_amount: dataRecord.sparge_acid_amount,
			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