www/js/rec_edit.js

Tue, 04 Jun 2019 19:50:06 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Tue, 04 Jun 2019 19:50:06 +0200
changeset 396
804800d8e885
parent 392
544d7d0183b2
child 406
0ad967f2d6ee
permissions
-rw-r--r--

Added console logging in the grid write callback functions. Removed grid sorting, it is done on the server side. Alert popup for block row edit functions. Better grid live updates. In recipe and product print show the whirlpool time. The checklist now shows misc ingredients added in the mash. Show hops added in the whirlpool. Most ingredient names are now quoted.

/*****************************************************************************
 * 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     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;

	var	fermentableRow = 0;
	var	fermentableData = {};
	var	hopRow = 0;
	var	hopData = {};
	var	miscRow = 0;
	var	miscData = {};
	var	yeastRow = 0;
	var	yeastData = {};
	var	mashRow = 0;
	var	mashData = {};

	/*
	 * Remove the top menu so that we MUST use the buttons to leave the editor.
	 */
	$('#jqxMenu').jqxMenu('destroy');

	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 = 110;
		var w80 = 80;
		if (ro) { // jqxNumberInput width -20 for no spinbuttons
			w100 = 90;
			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, width: w100 });
		// id="st_fg_min" margin-left 15/35 maken
		$("#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;		// fermentable sugars mash + boil
		sugarsm = 0;		// fermentable sugars in mash
		psugar = 0;
		pcara = 0;
		mashkg = 0;
		var vol = 0;		// Volume sugars after boil
		var addedS = 0;		// Added sugars after boil
		var addedmass = 0;	// Added mass after boil
		var mvol = 0;		// mash volume
		var colort = 0;		// Colors srm * vol totals
		var colorh = 0;         // Colors ebc * vol * kt
		var colorn = 0;		// Colors ebc * pt * pct
		var my_100 = false;
		var mashtime = 0;       // Total mash time
		var mashtemp = 0;       // Average mash temperature
		var bv = 0.925;         // Bierverlies rendement
                var sr = 0.95;          // Mash en spoel rendement
		var lintner = 0;	// Total recipe lintner

		if ((rows = $('#mashGrid').jqxGrid('getrows'))) {
			for (var i = 0; i < rows.length; i++) {
				var row = rows[i];
				if (row.step_type == 0) // Infusion
					mvol += parseFloat(row.step_infuse_amount);
				if (row.step_temp <= 75) { // Ignore mashout
					mashtime += row.step_time;
					mashtemp += row.step_time * row.step_temp;
				}
			}
			mashtemp = mashtemp / mashtime;
		}

		if (!(rows = $('#fermentableGrid').jqxGrid('getrows'))) {
			return; // grid not yet loaded.
		}

		var s = 0;
		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 == 1)		// Sugar
				psugar += row.f_percentage;
			if (row.f_graintype == 2)	// Crystal
				pcara += row.f_percentage;
			var d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
			if (row.f_added == 0) {		// Mash
				if (mvol > 0) { 	// Only if mash already known
					mvol += row.f_amount * row.f_moisture / 100;
					s += d;
				}
				d = parseFloat(dataRecord.efficiency) / 100 * d;
				sugarsm += d;
				mashkg += row.f_amount;
			}
			if (row.f_added == 0 || row.f_added == 1)	// Mash or Boil
				sugarsf += d;
			if (row.f_added == 2 || row.f_added == 3) {
				var x = (row.f_yield / 100) * (1 - row.f_moisture / 100);
				addedS += row.f_amount * x;
				addedmass += row.f_amount;
				vol += (x * sugardensity + (1 - x) * 1) * row.f_amount;
			}
			if (row.f_added == 0 && (row.f_type == 0 || row.f_type == 4) && row.f_color < 50) { // Mash and Grain/Adjunct and Color < 50
				lintner += row.f_diastatic_power * row.f_amount;
			}
			colort += row.f_amount * ebc_to_srm(row.f_color);
			colorh += row.f_amount * row.f_color * get_kt(row.f_color);
			colorn += (row.f_percentage / 100) * row.f_color;	// For 8.6 Pt wort.
		}
		$("#ferm_lintner").val(Math.round(parseFloat(lintner / mashkg)));
		console.log("lintner:"+lintner+" kg:"+mashkg);
		to_100 = my_100;
		if (to_100) {
			$("#wf_amount").jqxNumberInput({ width: 90, readOnly: true, spinButtons: false  });
		} else {
			$("#wf_amount").jqxNumberInput({ width: 110, readOnly: false, spinButtons: true });
		}

		// Estimate total recipe OG.
		dataRecord.est_og = estimate_sg(sugarsf + addedS, parseFloat(dataRecord.batch_size));
		$('#est_og').val(dataRecord.est_og);
		$('#est_og2').val(dataRecord.est_og);

		// Estimate SG in kettle before boil
		preboil_sg = estimate_sg(sugarsm, parseFloat(dataRecord.boil_size));

		// Color of the wort
		if (dataRecord.color_method == 4) {
			var color = Math.round(((sg_to_plato(dataRecord.est_og) / 8.6) * colorn) + (dataRecord.boil_time / 60));
		} else if (dataRecord.color_method == 3) {     // Hans Halberstadt
                        var color = Math.round((4.46 * bv * sr) /  parseFloat(dataRecord.batch_size) * colorh);
                } else {
			var cw = colort / parseFloat(dataRecord.batch_size) * 8.34436;
			var color = kw_to_ebc(dataRecord.color_method, cw);
		}
		dataRecord.est_color = color;
		$('#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;

		// Progress bars
		pmalts = mashkg / (dataRecord.boil_size / 3) * 100;
		$("#perc_malts").jqxProgressBar('val', pmalts);
		$("#perc_sugars").jqxProgressBar('val', psugar);
		$("#perc_cara").jqxProgressBar('val', pcara);

		// Calculate estimated svg.
		svg = 0; // default.
		var rows = $('#yeastGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.y_use == 0) {   // Primary
				if (parseFloat(row.y_attenuation) > svg)
					svg = parseFloat(row.y_attenuation);    // Take the highest if multiple yeasts.
			}
			// TODO: brett in secondary ??
		}
		if (svg == 0)
			svg = 77;

		if ((mashkg > 0) && (mash_infuse > 0) && (mashtime > 0) && (mashtemp > 0)) {
			dataRecord.est_fg = estimate_fg(psugar, pcara, mash_infuse / mashkg, mashtime, mashtemp, svg, dataRecord.est_og);
		} else {
			dataRecord.est_fg = estimate_fg(psugar, pcara, 0, 0, 0, svg, dataRecord.est_og);
		}
		$('#est_fg').val(dataRecord.est_fg);
		$('#est_fg2').val(dataRecord.est_fg);

		dataRecord.est_abv = abvol(dataRecord.est_og, dataRecord.est_fg);
		$("#est_abv").val(dataRecord.est_abv);
		$("#est_abv2").val(dataRecord.est_abv);
	};

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

		if (use == 1) {	// 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
		}
		return (result * amount * 1000) / vol;
	}

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

		if (use == 5) {         // Dry hop
                        result = 1.33;
                } else 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 == 2) {	// Boil
			result = 1;
		} else if (use == 3) {	// Aroma / vlamuit
			result = 1.2;
		} else if (use == 4) { // Whirlpool
			result = 1.2;
		}
		return (result * amount * 1000) / vol;
	}

	function calcMash() {

		if (!(rows = $('#mashGrid').jqxGrid('getrows')))
                        return;
		if (mashkg == 0)
			return;

		var infused = 0;
		for (var i = 0; i < rows.length; i++) {
			var row = $("#mashGrid").jqxGrid('getrowdata', i);
			if (row.step_type == 0) // Infusion
				infused += row.step_infuse_amount;
                        $("#mashGrid").jqxGrid('setcellvalue', i, "step_thickness", infused / mashkg);
		}
	}

	function calcIBUs() {
		var total_ibus = 0;
		hop_aroma = hop_flavour = 0;
		var rows = {};
		if (!(rows = $('#hopGrid').jqxGrid('getrows'))) {
                        return;
                }
		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 * 10) / 10;
		hop_flavour = Math.round(hop_flavour * 1000 / 5) / 10;
                hop_aroma = Math.round(hop_aroma * 1000 / 6) / 10;
		if (hop_flavour > 100)
                        hop_flavour = 100;
                if (hop_aroma > 100)
                        hop_aroma = 100;
		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);
		$("#hop_aroma").jqxProgressBar('val', hop_aroma);
	};

	function adjustHops(factor) {

		console.log("adjustHops("+factor+")");

		var rowscount = $("#hopGrid").jqxGrid('getdatainformation').rowscount;
		if (rowscount == 0)
			return;

		for (var i = 0; i < rowscount; i++) {
			var row = $("#hopGrid").jqxGrid('getrowdata', i);
			var amount = row.h_amount * factor;
			$("#hopGrid").jqxGrid('setcellvalue', i, "h_amount", amount);
		}
	};

	function adjustMiscs(factor) {

		console.log("adjustMiscs("+factor+")");

		var rowscount = $("#miscGrid").jqxGrid('getdatainformation').rowscount;
		if (rowscount == 0)
			return;

		for (var i = 0; i < rowscount; i++) {
			var row = $("#miscGrid").jqxGrid('getrowdata', i);
			var amount = row.m_amount * factor;
			$("#miscGrid").jqxGrid('setcellvalue', i, "m_amount", amount);
			switch (row.m_name) {
				case 'CaCl2':           $("#wa_cacl2").val(row.m_amount * 1000);
							break;
				case 'CaSO4':           $("#wa_caso4").val(row.m_amount * 1000);
							break;
				case 'MgSO4':           $("#wa_mgso4").val(row.m_amount * 1000);
							break;
				case 'NaCl':            $("#wa_nacl").val(row.m_amount * 1000);
							break;
				case 'Melkzuur':
				case 'Zoutzuur':
				case 'Fosforzuur':
				case 'Zwavelzuur':      $("#wa_acid").val(row.m_amount * 1000);
							break;
				case 'NaHCO3':
				case 'Na2CO3':
				case 'CaCO3':
				case 'Ca(OH)2':         $("#wa_base").val(row.m_amount * 1000);
							break;
			}
		}
	};

	function adjustYeasts(factor) {

		console.log("adjustYeasts("+factor+")");

		var rowscount = $("#yeastGrid").jqxGrid('getdatainformation').rowscount;
		if (rowscount == 0)
			return;

		for (var i = 0; i < rowscount; i++) {
			var row = $("#yeastGrid").jqxGrid('getrowdata', i);
			if (row.y_form == 1) { // Only adjust dry yeast
				var amount = row.y_amount * factor;
				$("#yeastGrid").jqxGrid('setcellvalue', i, "y_amount", amount);
			}
		}
	};

	function adjustWaters(factor) {

		console.log("adjustWaters("+factor+")");

		var rowscount = $("#mashGrid").jqxGrid('getdatainformation').rowscount;
		if (rowscount == 0)
			return;

		mash_infuse = 0;
		for (var i = 0; i < rowscount; i++) {
			var row = $("#mashGrid").jqxGrid('getrowdata', i);
			if (row.step_type == 0) { // Infusion
				var amount = Math.round(row.step_infuse_amount * factor * 10) / 10;
				$("#mashGrid").jqxGrid('setcellvalue', i, "step_infuse_amount", amount);
				mash_infuse += amount;
			}
		}
		if (dataRecord.w2_amount == 0) {
			dataRecord.w1_amount = mash_infuse;
			$("#w1_amount").val(mash_infuse);
		} else {
			dataRecord.w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
			dataRecord.w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
			$("#w1_amount").val(dataRecord.w1_amount);
			$("#w2_amount").val(dataRecord.w2_amount);
		}
		$('#wg_amount').val(mash_infuse);
	};

	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_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_amount_is_weight"] = record.amount_is_weight;
								row["m_inventory"] = record.inventory;
								row["m_avail"] = 1;
								var commit = $("#miscGrid").jqxGrid('addrow', null, row);
							}
						}
					}
				});
				miscs.dataBind();
				return;
			}
		}
	}

	function setRangeIndicator(ion, rangeCode) {
		if ((rangeCode == "laag") || (rangeCode == "hoog"))
			$("#wr_"+ion).html("<img src='images/dialog-error.png'><span style='vertical-align: top; font-size: 10px; font-style: italic;'>"+rangeCode + "</span>");
		else
			$("#wr_"+ion).html("<img src='images/dialog-ok-apply.png'>");
	}

	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 == 0 && row.f_graintype != 6) {	// Added == Mash && 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 0:					// Base, Special, Kilned
						case 3:
						case 5:	C1 = 0.014 * ebc - 34.192;
							break;
						case 2:	C1 = -0.0597 * ebc - 32.457;	// Crystal
							break;
						case 1:	C1 = 0.0107 * ebc - 54.768;	// Roast
							break;
						case 4:	C1 = -149;			// Sour malt
							break;
					}
				}
				x = C1 * (pHZ - row.f_di_ph);	// AcidRequired(ZpH)
				Result += x * row.f_amount;
			}
		}
		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 0:	return {	// Melkzuur
					pK1: 3.86,
					pK2: 20,
					pK3: 20,
					MolWt: 90.08,
					AcidSG: 1214,
					AcidPrc: 0.88
				};
			case 1:	return {	// Zoutzuur
					pK1: -7,
					pK2: 20,
					pK3: 20,
					MolWt: 36.46,
					AcidSG: 1142,
					AcidPrc: 0.28
				};
			case 2:	return {	// Fosforzuur
					pK1: 2.12,
					pK2: 7.20,
					pK3: 12.44,
					MolWt: 98.00,
					AcidSG: 1170,
					AcidPrc: 0.25
				};
			case 3:	return {	// Zwavelzuur
					pK1: -1,
					pK2: 1.92,
					pK3: 20,
					MolWt: 98.07,
					AcidSG: 1700,
					AcidPrc: 0.93
				};
		}
		console.log("Bummer, AT is " + AT);
	}

	// 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() < 0 || $("#wa_acid_name").val() > 3) {
			console.log("fix wa_acid_name");
			$("#wa_acid_name").val(0);
			dataRecord.wa_acid_name = 0;
		}
		if (last_acid == '')
			last_acid = AcidTypeData[$("#wa_acid_name").val()].nl;

		if ($("#wa_base_name").val() < 0 || $("#wa_base_name").val() > 3) {
			console.log("fix wa_base_name");
			$("#wa_base_name").val(0);
			dataRecord.wa_base_name = 0;
		}
		if (last_base == '')
			last_base = BaseTypeData[$("#wa_base_name").val()].nl;

		var AT = dataRecord.wa_acid_name;
		var BT = dataRecord.wa_base_name;

		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(AcidTypeData[AT].nl, 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 0:	RA = -protonDeficit / (f1d - f3d); //Sodiumbicarbonate, mmol totaal
						RA = RA * MMNaHCO3/1000; //gram
						$("#wa_base").val(Math.round(RA * 100) / 100);
						setWaterAgent('NaHCO3', 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 1:	RA = -protonDeficit / (2 * f1d + f2d); // Sodiumcarbonate, mmol totaal
						RA = RA * MMNa2CO3/1000; //gram
						$("#wa_base").val(Math.round(RA * 100) / 100);
						setWaterAgent('Na2CO3', 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 2:	RA = -protonDeficit * (f1d - f3d); // Calciumcarbonate, 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('CaCO3', 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 3:	RA = -protonDeficit / 19.3; // Calciumhydroxide
						$("#wa_base").val(Math.round(RA * 100) / 100);
						setWaterAgent('Ca(OH)2', 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 0:	// Sodiumbicarbonate, 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 1:	// Sodiumcarbonate
							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 2:	// Calciumcarbonate: 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;
				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 == 3) && (liters > 0)) {	// Sulfuctic / Zwavelzuur
			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 == 1) && (liters > 0)) {	// Hydrochloric, Zoutzuur
			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
		// Note, values below are the other way, cl to so4!
		// So: 0.5 is IPA's, Pale Ales.
		//       1 Balanced
		//       2 Malty.
		$('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
		// From brouwhulp.
		if (GetBUGU() < 0.32)
			$('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig en zoet</span>");
		else if (GetBUGU() < 0.43)
			$('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig, zoet</span>");
		else if (GetBUGU() < 0.52)
			$('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Evenwichtig</span>");
		else if (GetBUGU() < 0.63)
			$('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Licht hoppig, bitter</span>");
		else
			$('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Extra hoppig, zeer bitter</span>");
		$('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);
		if (sulfate > 0)
			RA = chloride / sulfate;
		else
			RA = 10;
		$('#got_cl_so4').val(Math.round(RA * 10) / 10);
		var piCLSO4_low = 0.8 * GetOptClSO4ratio();
		var piCLSO4_high = 1.2 * GetOptClSO4ratio();
		var Res = 'normaal';
		if (RA < piCLSO4_low)
			Res = 'laag';
		else if (RA > piCLSO4_high)
			Res = 'hoog';
		setRangeIndicator('cl_so4', Res);

                $('#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", "laag");
		} else if (calcium > 150) {
			setRangeIndicator("calcium", "hoog");
		} else {
			setRangeIndicator("calcium", "normaal");
		}
                if (magnesium >= 0 && magnesium <= 30) {
			setRangeIndicator("magnesium", "normaal");
		} else {
			setRangeIndicator("magnesium", "hoog");
		}
                if (sodium <= 150) {
			setRangeIndicator("sodium", "normaal");
		} else {
			setRangeIndicator("sodium", "hoog");
		}
		// Both chloride and sulfate should be above 50 according to
		// John Palmer. So the Cl/SO4 ratio calculation will work.
		if (chloride <= 50) {
			setRangeIndicator("chloride", "laag");
		} else if (chloride <= 100) {
			setRangeIndicator("chloride", "normaal");
		} else {
			setRangeIndicator("chloride", "hoog");
		}
		if (sulfate <= 50) {
			setRangeIndicator("sulfate", "laag");
                } else if (sulfate <= 350) {
			setRangeIndicator("sulfate", "normaal");
		} else {
			setRangeIndicator("sulfate", "hoog");
		}
		if (ph < 5.2) {
			setRangeIndicator("ph", "laag");
		} else if (ph > 5.6) {
			setRangeIndicator("ph", "hoog");
		} else {
			setRangeIndicator("ph", "normaal");
		}
		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 == 1) {	// Source 2
			if (dataRecord.w2_ph > 0.0) {
				Source_pH = dataRecord.w2_ph;
				Source_alkalinity = dataRecord.w2_total_alkalinity;
			} else {
				dataRecord.sparge_source = 0;	// Source 1
				$("#sparge_source").val(0);
			}
		} else if (dataRecord.sparge_source == 2) {	// Mixed
			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 = 0;
				$("#sparge_source").val(0);
			}
		}

		//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.373);
		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.373);
		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
		var Ct = (alkalinity - 1000 * (Math.pow(10, -4.3) - Math.pow(10, -Source_pH))) / ((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.373);
		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 = Ct * ((f1g-f1)+(f3-f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH);  //mEq/l
                Acid += 0.01;   // Add acid that would be required for distilled water.
		if (dataRecord.sparge_acid_type < 0 || dataRecord.sparge_acid_type > 3) {
			dataRecord.sparge_acid_type = 0;
			$("#sparge_acid_type").val(0);
		}

		//Step 8. Get the acid data.
		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
		var 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);
	}

	/*
	 * Change OG of recipe but keep the water volumes.
	 */
	function calcFermentablesFromOG(OG) {

		console.log("calcFermentablesFromOG("+OG+")");
		var efficiency = parseFloat($("#efficiency").jqxNumberInput('decimal'));
		var sug = sg_to_plato(OG) * parseFloat($("#batch_size").jqxNumberInput('decimal')) * OG / 100;	//total amount of sugars in kg
		var tot = 0;
		var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;

		for (var i = 0; i < rowscount; i++) {
			var row = $("#fermentableGrid").jqxGrid('getrowdata', i);
			var d = row.f_percentage / 100 * (row.f_yield / 100) * (1 - row.f_moisture / 100);
			if (row.f_added == 0)	// Mash
				d = efficiency / 100 * d;
			tot += d;
		}
		var	totmass = 0;
		if (tot)
			totmass = sug / tot;

		if (totmass) {
			for (i = 0; i < rowscount; i++) {
				var row = $("#fermentableGrid").jqxGrid('getrowdata', i);
				var amount = row.f_percentage / 100 * totmass;
				$("#fermentableGrid").jqxGrid('setcellvalue', i, "f_amount", amount);
			}
		}
	};

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

		$("#calc_acid").on('checked', function (event) {
			dataRecord.calc_acid = 1;
			calcWater();
		});
		$("#calc_acid").on('unchecked', function (event) {
			dataRecord.calc_acid = 0;
			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($("#w1_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) {
			if (event.args) {
				setWaterAgent('CaCl2', 0);	// This can prevent double entries.
				setWaterAgent('CaCl2', event.args.value);
				calcWater();
			}
		});
		$('#wa_caso4').on('change', function (event) {
			if (event.args) {
				setWaterAgent('CaSO4', 0);
				setWaterAgent('CaSO4', event.args.value);
				calcWater();
			}
		});
		$('#wa_mgso4').on('change', function (event) {
			if (event.args) {
				setWaterAgent('MgSO4', 0);
				setWaterAgent('MgSO4', event.args.value);
				calcWater();
			}
		});
		$('#wa_nacl').on('change', function (event) {
			if (event.args) {
				setWaterAgent('NaCl', 0);
				setWaterAgent('NaCl', event.args.value);
				calcWater();
			}
		});
		$('#wa_base_name').on('change', function (event) {
			if (event.args) {
				var index = event.args.index;
				console.log("wa_base_name "+index);
				setWaterAgent(last_base, 0);
				last_base = BaseTypeData[index].nl;
				setWaterAgent(last_base, parseFloat($("#wa_base").jqxNumberInput('decimal')));
				dataRecord.wa_base_name = index;
				calcWater();
			}
		});
		$('#wa_base').on('change', function (event) {
			var name = BaseTypeData[$("#wa_base_name").val()].nl;
			setWaterAgent(name, parseFloat(event.args.value));
			calcWater();
		});
		$('#wa_acid_name').on('change', function (event) {
			if (event.args) {
				var index = event.args.index;
				console.log("wa_acid_name "+index);
				setWaterAgent(last_acid, 0);
				last_acid = AcidTypeData[index].nl;
				setWaterAgent(last_acid, parseFloat($("#wa_acid").jqxNumberInput('decimal')));
				dataRecord.wa_acid_name = index;
				calcWater();
			}
		});
		$('#wa_acid').on('change', function (event) {
			var name = AcidTypeData[$("#wa_acid_name").val()].nl;
			setWaterAgent(name, parseFloat(event.args.value));
			calcWater();
		});
		$('#wa_acid_perc').on('change', function (event) { calcWater(); });

		$('#color_method').on('change', function (event) {
			dataRecord.color_method = event.args.index;
			calcFermentables();
		});
		$('#ibu_method').on('change', function (event) {
			dataRecord.ibu_method = event.args.index;
			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;
			var factor = parseFloat(event.args.value) / dataRecord.batch_size;
			dataRecord.boil_size = new_boil;
			$("#boil_size").val(Math.round(new_boil * 100) / 100);
			dataRecord.sparge_volume *= factor;
			$("#sparge_volume").val(dataRecord.sparge_volume);
			dataRecord.batch_size = parseFloat(event.args.value);
			calcFermentablesFromOG(parseFloat($("#est_og").jqxNumberInput('decimal')));     // Keep the OG
			adjustWaters(factor);
			calcFermentables();
			adjustHops(factor);
			adjustMiscs(factor);
			adjustYeasts(factor);
			calcIBUs();
			calcWater();
			calcSparge();
			calcMash();
		});
		$('#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();
			// TODO: adjust the hops, miscs, yeast, water.
			calcIBUs();
		});
		$('#efficiency').on('change', function (event) {
			var estog = parseFloat($("#est_og").jqxNumberInput('decimal'));
			dataRecord.efficiency = parseFloat(event.args.value);
			console.log("efficiency change:"+dataRecord.efficiency);
			calcFermentablesFromOG(estog);     // Keep the OG
			calcFermentables();
			calcIBUs();
		});
		$('#est_og').on('change', function (event) {
			dataRecord.est_og = parseFloat(event.args.value);
			console.log("est_og change:"+dataRecord.est_og);
			calcFermentablesFromOG(dataRecord.est_og);      // Adjust fermentables amounts
			calcFermentables();                             // Update the recipe details
			calcIBUs();                                     // and the IBU's.
			calcMash();
		});
		$('#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) {
			if (event.args) {
				var index = event.args.index;
				dataRecord.sparge_source= index;
				calcSparge();
			}
		});
		$('#sparge_acid_type').on('change', function (event) {
			if (event.args) {
				var index = event.args.index;
				dataRecord.sparge_acid_type = index;
				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) {
			dataRecord.locked = 1;
			setReadonly(true);
		});
		$('#locked').on('unchecked', function (event) {
			dataRecord.locked = 0;
			setReadonly(false);
		});
//		setReadonly(false);
	};

	$("#styleSelect").jqxDropDownList({
		placeHolder: "Kies bierstijl:",
		theme: theme,
		source: styleslist,
		displayMember: "name",
		width: 180,
		height: 23,
		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(StyleTypeData[datarecord.type].nl);
			$("#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);
		}
	});

	function saveRecord() {
		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: dataRecord.locked,
			notes: $("#notes").val(),
			st_name: $('#st_name').val(),
			st_letter: $('#st_letter').val(),
			st_guide: $('#st_guide').val(),
			st_type: dataRecord.st_type,
			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: dataRecord.calc_acid,
			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,
			wg_amount: parseFloat($("#wg_amount").jqxNumberInput('decimal')),
			wg_calcium: parseFloat($("#wg_calcium").jqxNumberInput('decimal')),
			wg_sulfate: parseFloat($("#wg_sulfate").jqxNumberInput('decimal')),
			wg_chloride: parseFloat($("#wg_chloride").jqxNumberInput('decimal')),
			wg_sodium: parseFloat($("#wg_sodium").jqxNumberInput('decimal')),
			wg_magnesium: parseFloat($("#wg_magnesium").jqxNumberInput('decimal')),
			wg_total_alkalinity: parseFloat($("#wg_total_alkalinity").jqxNumberInput('decimal')),
			wg_ph: parseFloat($("#wg_ph").jqxNumberInput('decimal')),
			wb_calcium: parseFloat($("#wb_calcium").jqxNumberInput('decimal')),
			wb_sulfate: parseFloat($("#wb_sulfate").jqxNumberInput('decimal')),
			wb_chloride: parseFloat($("#wb_chloride").jqxNumberInput('decimal')),
			wb_sodium: parseFloat($("#wb_sodium").jqxNumberInput('decimal')),
			wb_magnesium: parseFloat($("#wb_magnesium").jqxNumberInput('decimal')),
			wb_total_alkalinity: parseFloat($("#wb_total_alkalinity").jqxNumberInput('decimal')),
			wb_ph: parseFloat($("#wb_ph").jqxNumberInput('decimal')),
			wa_acid_name: $("#wa_acid_name").val(),
			wa_acid_perc: parseFloat($("#wa_acid_perc").jqxNumberInput('decimal')),
			wa_base_name: $("#wa_base_name").val(),
			fermentables: fermentablerow,
			hops: hoprow,
			miscs: miscrow,
			yeasts: yeastrow,
			mashs: mashrow
		};
		var data = "update=true&" + $.param(row);
		$.ajax({
			dataType: 'json',
			url: url,
			cache: false,
			async: false,
			data: data,
			type: "POST",
			success: function (data, status, xhr) {
				console.log("saveRecord() success");
			},
			error: function(jqXHR, textStatus, errorThrown) {
				console.log("saveRecord() error");
			}
		});
	};

	var dataRecord = {};
	var url = "includes/db_recipes.php";

	// prepare the data
	var source = {
		datatype: "json",
		cache: false,
		datafields: [
			{ name: 'record', type: 'number' },
			{ name: 'uuid', type: 'string' },
			{ name: 'locked', type: 'int' },
			{ name: 'st_name', type: 'string' },
			{ name: 'st_letter', type: 'string' },
			{ name: 'st_guide', type: 'string' },
			{ name: 'st_type', type: 'int' },
			{ 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: 'int' },
			{ 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: 'int' },
			{ name: 'est_ibu', type: 'float' },
			{ name: 'ibu_method', type: 'int' },
			{ name: 'est_carb', type: 'float' },
			{ name: 'sparge_temp', type: 'float' },
			{ name: 'sparge_ph', type: 'float' },
			{ name: 'sparge_volume', type: 'float' },
			{ name: 'sparge_source', type: 'int' },
			{ name: 'sparge_acid_type', type: 'int' },
			{ 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: 'int' },
			{ 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: 'wg_amount', type: 'float' },
			{ name: 'wg_calcium', type: 'float' },
			{ name: 'wg_sulfate', type: 'float' },
			{ name: 'wg_chloride', type: 'float' },
			{ name: 'wg_sodium', type: 'float' },
			{ name: 'wg_magnesium', type: 'float' },
			{ name: 'wg_total_alkalinity', type: 'float' },
			{ name: 'wg_ph', type: 'float' },
			{ name: 'wb_calcium', type: 'float' },
			{ name: 'wb_sulfate', type: 'float' },
			{ name: 'wb_chloride', type: 'float' },
			{ name: 'wb_sodium', type: 'float' },
			{ name: 'wb_magnesium', type: 'float' },
			{ name: 'wb_total_alkalinity', type: 'float' },
			{ name: 'wb_ph', type: 'float' },
			{ name: 'wa_acid_name', type: 'int' },
			{ name: 'wa_acid_perc', type: 'int' },
			{ name: 'wa_base_name', type: 'int' },
			{ name: 'fermentables', type: 'array' },
			{ name: 'hops', type: 'array' },
			{ name: 'miscs', type: 'array' },
			{ name: 'yeasts', type: 'array' },
			{ name: 'mashs', type: 'array' }
		],
		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(StyleTypeData[dataRecord.st_type].nl);
			$("#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);
			$("#est_fg2").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);
			$("#est_abv2").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);
			$("#wg_amount").val(dataRecord.wg_amount);
			$("#wg_calcium").val(dataRecord.wg_calcium);
			$("#wg_sulfate").val(dataRecord.wg_sulfate);
			$("#wg_chloride").val(dataRecord.wg_chloride);
			$("#wg_sodium").val(dataRecord.wg_sodium);
			$("#wg_magnesium").val(dataRecord.wg_magnesium);
			$("#wg_total_alkalinity").val(dataRecord.wg_total_alkalinity);
			$("#wg_ph").val(dataRecord.wg_ph);
			$("#wb_calcium").val(dataRecord.wb_calcium);
			$("#wb_sulfate").val(dataRecord.wb_sulfate);
			$("#wb_chloride").val(dataRecord.wb_chloride);
			$("#wb_sodium").val(dataRecord.wb_sodium);
			$("#wb_magnesium").val(dataRecord.wb_magnesium);
			$("#wb_total_alkalinity").val(dataRecord.wb_total_alkalinity);
			$("#wb_ph").val(dataRecord.wb_ph);
			$("#wa_acid_name").val(dataRecord.wa_acid_name);
			$("#wa_acid_perc").val(dataRecord.wa_acid_perc);
			$("#wa_base_name").val(dataRecord.wa_base_name);
			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: 'int' },
				{ 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: 'int' },
				{ name: 'f_added', type: 'int' },
				{ name: 'f_dissolved_protein', type: 'float' },
				{ name: 'f_recommend_mash', type: 'int' },
				{ name: 'f_add_after_boil', type: 'int' },
				{ name: 'f_adjust_to_total_100', type: 'int' },
				{ name: 'f_percentage', type: 'float' },
				{ name: 'f_di_ph', type: 'float' },
				{ name: 'f_acid_to_ph_57', type: 'float' },
				{ name: 'f_inventory', type: 'float' },
				{ name: 'f_avail', type: 'int' }
			],
			addrow: function (rowid, rowdata, position, commit) {
				console.log("fermentable addrow "+rowid);
				commit(true);
			},
			deleterow: function (rowid, commit) {
				console.log("fermentable deleterow "+rowid);
				commit(true);
			},
			updaterow: function (rowid, rowdata, commit) {
				console.log("fermentable updaterow "+rowid);
				commit(true);
			}
		};
		var fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);
		$("#fermentableGrid").jqxGrid({
			width: 1240,
			height: 470,
			source: fermentableAdapter,
			theme: theme,
			selectionmode: 'singlerow',
			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,
					template: "primary",
					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"] = 2;	// Fermentation
						} else if ((datarecord.type == 1) || (datarecord.type == 4)) {	// Sugar or Adjunct
							row["f_added"] = 1;	// Boil
						} else {
							row["f_added"] = 0;	// Mash
						}
						row["f_dissolved_protein"] = datarecord.dissolved_protein;
						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;
						row["f_inventory"] = datarecord.inventory;
						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({ template: "danger", 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();
					calcIBUs();
				});
			},
			ready: function() {
				calcFermentables();
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
				{ text: 'Vergistbaar ingredi&euml;nt', datafield: 'f_name',
				  cellsrenderer:  function (index, datafield, value, defaulvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" +
						rowdata.f_supplier+" / "+rowdata.f_name+" ("+rowdata.f_color+" EBC)</span>";
				  }
			       	},
				{ text: 'Type', width: 100, datafield: 'f_type',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" + FermentableTypeData[value].nl + "</span>";
				  }
				},
				{ text: 'Moment', width: 110, datafield: 'f_added',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" + AddedData[value].nl + "</span>";
				  }
				},
				{ text:'Maxinbatch', datafield: 'f_max_in_batch', hidden: true },
				{ 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' },
				{ text: 'Voorr. Kg', datafield: 'f_inventory', width: 120, align: 'right',
				  cellsrenderer:  function (row, columnfield, value, defaulthtml, columnproperties, rowdata) {
					var color = '#ffffff';
					if (value < rowdata.f_amount)
						color = '#ff4040';
					return  '<span style="margin: 4px; margin-top: 6px; float: right; color: ' +
						color + ';">' +fermentableAdapter.formatNumber(value, "f3") + '</span>';
				  }
				},
				{ text: 'Procent', datafield: 'f_percentage', width: 90, align: 'right',
				  cellsrenderer:  function (row, columnfield, value, defaulthtml, columnproperties, rowdata) {
					var color = '#ffffff';
					if (value > rowdata.f_max_in_batch)
						color = '#ff4040';
					return  '<span style="margin: 4px; margin-top: 6px; float: right; color: ' +
						color + ';">' +fermentableAdapter.formatNumber(value, "p1") + '</span>';
				  }
				},
				{ text: '100%', align: 'center', datafield: 'f_adjust_to_total_100', columntype: 'checkbox', width: 70 },
				{ text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', cellsrenderer: function () {
					return "Wijzig";
				        }, buttonclick: function (row) {
						fermentableRow = row;
						fermentableData = $("#fermentableGrid").jqxGrid('getrowdata', fermentableRow);
						$("#wf_name").val(fermentableData.f_name);
						$("#wf_amount").val(fermentableData.f_amount);
						$("#wf_percentage").val(fermentableData.f_percentage);
						$("#wf_max_in_batch").val(fermentableData.f_max_in_batch);
						$("#wf_adjust_to_total_100").val(fermentableData.f_adjust_to_total_100);
						$("#wf_added").val(fermentableData.f_added);
						// show the popup window.
						$("#popupFermentable").jqxWindow('open');
					}
				}
			]
		});
	};

	// 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: 'int' },
				{ name: 'h_form', type: 'int' },
				{ name: 'h_useat', type: 'int' },
                                { 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_caryophyllene', type: 'float' },
				{ name: 'h_cohumulone', type: 'float' },
				{ name: 'h_myrcene', type: 'float' },
				{ name: 'h_total_oil', type: 'float' },
				{ name: 'h_inventory', type: 'float' },
				{ name: 'h_avail', type: 'int' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
				console.log("hop addrow "+rowid);
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
				console.log("hop deleterow "+rowid);
                                commit(true);
                        },
			updaterow: function (rowid, rowdata, commit) {
				console.log("hop updaterow "+rowid);
				commit(true);
			}
                };
                var hopAdapter = new $.jqx.dataAdapter(hopSource);
                $("#hopGrid").jqxGrid({
                        width: 1240,
                        height: 560,
                        source: hopAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        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: 400px;" id="hdeleterowbutton" type="button" value="Verwijder hop" />');
                                // add hop from dropdownlist.
                                $("#haddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies hop:",
                                        theme: theme,
					template: "primary",
                                        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"] = 2;	// Boil
						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_caryophyllene"] = datarecord.caryophyllene;
						row["h_cohumulone"] = datarecord.cohumulone;
						row["h_myrcene"] = datarecord.myrcene;
						row["h_total_oil"] = datarecord.total_oil;
						row["h_inventory"] = datarecord.inventory;
                                                var commit = $("#hopGrid").jqxGrid('addrow', null, row);
                                        }
					$("#haddrowbutton").jqxDropDownList('clearSelection');
                                });

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

                                // delete selected hop.
                                $("#hdeleterowbutton").jqxButton({ template: "danger", 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);
                                        }
					calcIBUs();
                                });
                        },
			ready: function() {
				calcIBUs();
				$('#jqxTabs').jqxTabs('next');
			},
                        columns: [
                                { text: 'Hop', datafield: 'h_name',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" +rowdata.h_origin+" / "+rowdata.h_name+"</span>";
				  },
			       	},
                                { text: 'Type', width: 90, datafield: 'h_type',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return  '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopTypeData[value].nl + '</span>';
				  }
			       	},
				{ text: 'Vorm', width: 90, datafield: 'h_form',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return  '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopFormData[value].nl + '</span>';
				  }
			       	},
                                { text: 'Alpha', datafield: 'h_alpha', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
				{ text: 'Gebruik', width: 110, datafield: 'h_useat',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return  '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopUseData[value].nl + '</span>';
				  }
				},
				{ text: 'Tijdsduur', datafield: 'h_time', width: 90, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var duration = '';
					if ((rowdata.h_useat == 2) || (rowdata.h_useat == 4))   // Boil, Whirlpool
						duration = dataAdapter.formatNumber(value, "f0")+" min.";
					else if (rowdata.h_useat == 5)       // Dry hop
						duration = dataAdapter.formatNumber(value/1440, "f0")+" dagen";
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' + duration + '</span>';
				  }
				},
				{ text: 'IBU', 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());
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(ibu, "f1") + '</span>';
				  }
				},
				{ text: 'Gewicht', datafield: 'h_amount', width: 110, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var amount = dataAdapter.formatNumber(value, "f1") + ' kg';
					if (value < 1)
						amount = dataAdapter.formatNumber(value * 1000, "f1") + ' gr';
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' + amount + '</span>';
				  }
				},
				{ text: 'Voorraad', datafield: 'h_inventory', width: 110, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var color = '#ffffff';
					if (value < rowdata.h_amount)
						color = '#ff4040';
					var amount = dataAdapter.formatNumber(value, "f1") + ' kg';
					if (value < 1)
						amount = dataAdapter.formatNumber(value * 1000, "f1") + ' gr';
					return  '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
				  }
				},
				{ text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', cellsrenderer: function () {
					return "Wijzig";
					}, buttonclick: function (row) {
						hopRow = row;
						hopData = $("#hopGrid").jqxGrid('getrowdata', hopRow);
						$("#wh_name").val(hopData.h_name);
						$("#wh_amount").val(hopData.h_amount * 1000);
						var ibu = toIBU(hopData.h_useat, hopData.h_form, preboil_sg,
							parseFloat($("#batch_size").jqxNumberInput('decimal')),
							parseFloat(hopData.h_amount), parseFloat(hopData.h_time),
							parseFloat(hopData.h_alpha), $("#ibu_method").val()
						);
						$("#wh_ibu").val(ibu);
						if (hopData.h_useat == 5)	// Dry hop
							$("#wh_time").val(hopData.h_time / 1440);
						else
							$("#wh_time").val(hopData.h_time);
						$("#wh_useat").val(hopData.h_useat);
						// show the popup window.
						$("#popupHop").jqxWindow('open');
					}
				}
                        ]
                });
        };

	// 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: 'int' },
                                { name: 'm_use_use', type: 'int' },
                                { name: 'm_time', type: 'float' },
                                { name: 'm_amount_is_weight', type: 'int' },
				{ name: 'm_inventory', type: 'float' },
				{ name: 'm_avail', type: 'int' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
				console.log("misc addrow "+rowid);
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
				console.log("misc deleterow "+rowid);
                                commit(true);
                        },
			updaterow: function (rowid, rowdata, commit) {
				console.log("misc updaterow "+rowid);
				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];
					data.push(row);
					// Initial set water agent values.
					switch (row.m_name) {
						case 'CaCl2':		$("#wa_cacl2").val(row.m_amount * 1000);
									break;
						case 'CaSO4':		$("#wa_caso4").val(row.m_amount * 1000);
									break;
						case 'MgSO4':		$("#wa_mgso4").val(row.m_amount * 1000);
									break;
						case 'NaCl':		$("#wa_nacl").val(row.m_amount * 1000);
									break;
						case 'Melkzuur':	$("#wa_acid_name").val(0);
									$("#wa_acid").val(row.m_amount * 1000);
									$("#wa_acid_perc").val(80);
									last_acid = 'Melkzuur';
									break;
						case 'Zoutzuur':	$("#wa_acid_name").val(1);
									$("#wa_acid").val(row.m_amount * 1000);
									$("#wa_acid_perc").val(80);
									last_acid = 'Zoutzuur';
									break;
						case 'Fosforzuur':	$("#wa_acid_name").val(2);
									$("#wa_acid").val(row.m_amount * 1000);
									$("#wa_acid_perc").val(80);
									last_acid = 'Fosforzuur';
									break;
						case 'Zwavelzuur':	$("#wa_acid_name").val(3);
									$("#wa_acid").val(row.m_amount * 1000);
									$("#wa_acid_perc").val(80);
									last_acid = 'Zwavelzuur';
									break;
						case 'NaHCO3':		$("#wa_base_name").val(0);
									$("#wa_base").val(row.m_amount * 1000);
									last_base = 'NaHCO3';
									break;
						case 'Na2CO3':		$("#wa_base_name").val(1);
									$("#wa_base").val(row.m_amount * 1000);
									last_base = 'Na2CO3';
									break;
						case 'CaCO3':		$("#wa_base_name").val(2);
									$("#wa_base").val(row.m_amount * 1000);
									last_base = 'CaCO3';
									break;
						case 'Ca(OH)2':		$("#wa_base_name").val(3);
									$("#wa_base").val(row.m_amount * 1000);
									last_base = 'Ca(OH)2';
									break;
					}
				}
				return data;
			},
			loadError: function(jqXHR, status, error) {
				$('#err').text(status + ' ' + error);
			},
		});
                $("#miscGrid").jqxGrid({
                        width: 1240,
                        height: 575,
                        source: miscAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        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: 400px;" id="mdeleterowbutton" type="button" value="Verwijder ingredi&euml;nt" />');
                                // add misc from dropdownlist.
                                $("#maddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies ingredi&euml;nt:",
                                        theme: theme,
					template: "primary",
                                        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_amount_is_weight"] = datarecord.amount_is_weight;
						row["m_inventory"] = datarecord.inventory;
                                                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({ template: "danger", 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 != 4) {	// Water agent
                                                var id = $("#miscGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#miscGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
                                { text: 'Ingredient', datafield: 'm_name' },
                                { text: 'Type', width: 140, datafield: 'm_type',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" + MiscTypeData[value].nl + "</span>";
				  }
			       	},
                                { text: 'Gebruik', width: 140, datafield: 'm_use_use',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<span style='margin: 3px; margin-top: 6px; float: left;'>" + MiscUseData[value].nl + "</span>";
				  }
			       	},
                                { text: 'Tijd', datafield: 'm_time', width: 90, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var duration = '';
					if (rowdata.m_use_use == 2)     // Boil
						duration = dataAdapter.formatNumber(value, "f0")+" min.";
					else if ((rowdata.m_use_use == 3) || (rowdata.m_use_use == 4))  // Primary or Secondary
						duration = dataAdapter.formatNumber(value/1440, "f0")+" dagen";
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' + duration + '</span>';
				  },
                                },
				{ text: 'Hoeveel', datafield: 'm_amount', width: 110, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var vstr = rowdata.m_amount_is_weight ? "gr":"ml";
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' +
						dataAdapter.formatNumber(value * 1000,"f2")+" "+vstr + '</span>';
				  },
				},
				{ text: 'Voorraad', datafield: 'm_inventory', width: 110, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var vstr = rowdata.m_amount_is_weight ? "gr":"ml";
					var color = '#ffffff';
					if (value < rowdata.m_amount)
						color = '#ff4040';
					var amount = dataAdapter.formatNumber(value * 1000,"f2")+" "+vstr;
					return  '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
				  },
				},
				{ text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', cellsrenderer: function () {
					return "Wijzig";
					}, buttonclick: function (row) {
						miscRow = row;
						miscData = $("#miscGrid").jqxGrid('getrowdata', miscRow);
						if (miscData.m_type == 4) {
							alert("Brouwzouten wijzigen in de water tab.");
						} else {
							if (miscData.m_amount_is_weight)
								$("#wm_pmpt_amount").html("Gewicht gram:");
							else
								$("#wm_pmpt_amount").html("Volume ml:");
							$("#wm_name").val(miscData.m_name);
							$("#wm_amount").val(miscData.m_amount * 1000);
							if ((miscData.m_use_use == 3) || (miscData.m_use_use == 4))	// Primary or Secondary
								$("#wm_time").val(miscData.m_time / 1440);
							else
								$("#wm_time").val(miscData.m_time);
							$("#wm_use_use").val(miscData.m_use_use);
							// show the popup window.
							$("#popupMisc").jqxWindow('open');
						}
					}
				}
                        ]
                });
        };

	// 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: 'int' },
                                { name: 'y_form', type: 'int' },
				{ name: 'y_flocculation', type: 'int' },
				{ name: 'y_min_temperature', type: 'float' },
				{ name: 'y_max_temperature', type: 'float' },
				{ name: 'y_attenuation', type: 'float' },
				{ name: 'y_use', type: 'int' },
				{ name: 'y_cells', type: 'float' },
				{ name: 'y_tolerance', type: 'float' },
				{ name: 'y_inventory', type: 'float' },
				{ name: 'y_avail', type: 'int' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
				console.log("yeast addrow "+rowid);
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
				console.log("yeast deleterow "+rowid);
                                commit(true);
                        },
			updaterow: function (rowid, rowdata, commit) {
				console.log("yeast updaterow "+rowid);
				commit(true);
			}
                };
                var yeastAdapter = new $.jqx.dataAdapter(yeastSource);
                $("#yeastGrid").jqxGrid({
                        width: 1240,
                        height: 350,
                        source: yeastAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        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: 400px;" id="ydeleterowbutton" type="button" value="Verwijder gist" />');
                                // add yeast from dropdownlist.
                                $("#yaddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies gist:",
                                        theme: theme,
					template: "primary",
                                        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"] = 0;
						row["y_min_temperature"] = datarecord.min_temperature;
						row["y_max_temperature"] = datarecord.max_temperature;
						row["y_attenuation"] = datarecord.attenuation;
						row["y_flocculation"] = datarecord.flocculation;
						row["y_cells"] = datarecord.cells;
						row["y_tolerance"] = datarecord.tolerance;
						row["y_inventory"] = datarecord.inventory;
                                                var commit = $("#yeastGrid").jqxGrid('addrow', null, row);
                                        }
					$("#yaddrowbutton").jqxDropDownList('clearSelection');
                                });
				$("#yinstockbutton").jqxCheckBox({ theme: theme, height: 27 });
				$("#yinstockbutton").on('change', function (event) {
					yeastinstock = event.args.checked;
					yeastlist.dataBind();
				});
                                // delete selected yeast.
                                $("#ydeleterowbutton").jqxButton({ template: "danger", 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() {
				calcFermentables();
				$('#jqxTabs').jqxTabs('next');
			},
                        columns: [
                                { text: 'Gist', datafield: 'y_name' },
				{ text: 'Laboratorium', width: 150, datafield: 'y_laboratory' },
				{ text: 'Code', width: 90, datafield: 'y_product_id' },
                                { text: 'Soort', width: 100, datafield: 'y_form',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return  '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastFormData[value].nl + '</span>';
				  }
				},
				{ text: 'Min. &deg;C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_min_temperature' },
				{ text: 'Max. &deg;C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_max_temperature' },
				{ text: 'Tol. %', width: 60, align: 'right', cellsalign: 'right', datafield: 'y_tolerance',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var color = '#ffffff';
					var amount = "";
					if (value > 0) {
						amount = dataAdapter.formatNumber(value, "f1");
						if (dataRecord.est_abv > value)
							color = '#ff4040';
					}
					return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
				  }
				},
				{ text: 'Attn. %', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_attenuation', cellsformat: 'f1' },
				{ text: 'Voor', width: 120, datafield: 'y_use',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return  '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastUseData[value].nl + '</span>';
				  }
				},
				{ text: 'Hoeveel', datafield: 'y_amount', width: 90, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var amount = dataAdapter.formatNumber(value*1000, "f0")+" ml";
					if (rowdata.y_form == 0)        // Liquid
						amount = dataAdapter.formatNumber(value, "f0")+" pk";
					else if (rowdata.y_form == 1)   // Dry
						amount = dataAdapter.formatNumber(value*1000, "f1")+" gr";
					return  '<span style="margin: 4px; margin-top: 6px; float: right;">' + amount + '</span>';
                                  }
                                },
				{ text: 'Voorraad', datafield: 'y_inventory', width: 90, align: 'right',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var color = '#ffffff';
					if (value < rowdata.y_amount)
						color = '#ff4040';
					var amount = dataAdapter.formatNumber(value*1000, "f0")+" ml";
					if (rowdata.y_form == 0)        // Liquid
						amount = dataAdapter.formatNumber(value, "f0")+" pk";
					else if (rowdata.y_form == 1)   // Dry
						amount = dataAdapter.formatNumber(value*1000, "f1")+" gr";
					return  '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
				  }
				},
				{ text: '', datafield: 'Edit', columntype: 'button', width: 90, align: 'center', cellsrenderer: function () {
					return "Wijzig";
					}, buttonclick: function (row) {
						yeastRow = row;
						yeastData = $("#yeastGrid").jqxGrid('getrowdata', yeastRow);
						if (yeastData.y_form == 0) {
							$("#wy_pmpt_amount").html("Pak(ken):");
							$("#wy_amount").val(yeastData.y_amount);
							$("#wy_amount").jqxNumberInput({ decimalDigits: 0 });
						} else if (yeastData.y_form == 1) {
							$("#wy_pmpt_amount").html("Gewicht gram:");
							$("#wy_amount").val(yeastData.y_amount * 1000);
							$("#wy_amount").jqxNumberInput({ decimalDigits: 1 });
						} else {
							$("#wy_pmpt_amount").html("Volume ml:");
							$("#wy_amount").val(yeastData.y_amount * 1000);
							$("#wy_amount").jqxNumberInput({ decimalDigits: 0 });
						}
						$("#wy_name").val(yeastData.y_name);
						$("#wy_laboratory").val(yeastData.y_laboratory);
						$("#wy_product_id").val(yeastData.y_product_id);
						$("#wy_use").val(yeastData.y_use);
						// show the popup window.
						$("#popupYeast").jqxWindow('open');
					}
				}
                        ]
                });
        };

	// inline mash editor
        var editMash = function (data) {
		var generaterow = function () {
			var row = {};
			row["step_name"] = "Stap 1";
			row["step_type"] = 0;
			row["step_infuse_amount"] = 15;
			row["step_temp"] = 62.0;
			row['step_time'] = 20.0;
			row['step_thickness'] = 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: 'int' },
				{ name: 'step_infuse_amount', type: 'float' },
				{ name: 'step_temp', type: 'float' },
				{ name: 'step_time', type: 'float' },
				{ name: 'step_thickness', 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 == 0)	// Infusion
						mash_infuse += parseFloat(row.step_infuse_amount);
					row.step_thickness = 0;	// Init this field.
					data.push(row);
				}
			},	
		});
                $("#mashGrid").jqxGrid({
                        width: 1240,
                        height: 400,
                        source: mashAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        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: 565px;" id="sdeleterowbutton" type="button" value="Verwijder stap" />');
				$("#saddrowbutton").jqxButton({ template: "primary", 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({ template: "danger", 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() {
				calcFermentables();
				calcInit();
				calcMash();
				$('#jqxLoader').jqxLoader('close');
				$('#jqxTabs').jqxTabs('first');
			},
                        columns: [
				{ text: 'Stap naam', datafield: 'step_name' },
			        { text: 'Stap type', datafield: 'step_type', width: 175,
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					return "<div style='margin: 4px;'>" + MashStepTypeData[value].nl + "</div>";
				  }
				},
				{ text: 'Start &deg;C', datafield: 'step_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Eind &deg;C', datafield: 'end_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Rust min.', datafield: 'step_time', width: 90, align: 'right', cellsalign: 'right' },
				{ text: 'Stap min.', datafield: 'ramp_time', width: 90, align: 'right', cellsalign: 'right' },
				{ text: 'Infuse L.', datafield: 'step_infuse_amount', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'L/Kg.', datafield: 'step_thickness', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f2' },
				{ text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', cellsrenderer: function () {
					return "Wijzig";
					}, buttonclick: function (row) {
						mashRow = row;
						mashData = $("#mashGrid").jqxGrid('getrowdata', mashRow);
						$("#wstep_name").val(mashData.step_name);
						$("#wstep_type").val(mashData.step_type);
						$("#wstep_infuse_amount").val(mashData.step_infuse_amount);
						$("#wstep_temp").val(mashData.step_temp);
						$("#wend_temp").val(mashData.end_temp);
						$("#wstep_time").val(mashData.step_time);
						$("#wramp_time").val(mashData.ramp_time);
						if (mashData.step_type == 0) {
							$("#wstep_infuse_amount").show();
							$("#wstep_pmpt").show();
						} else {
							$("#wstep_infuse_amount").hide();
							$("#wstep_pmpt").hide();
						}
						// show the popup window.
						$("#popupMash").jqxWindow('open');
					}
				}
                        ]
                });
        };

	// initialize the input fields.
	// Tab 1, Algemeen
	$("#name").jqxTooltip({ content: 'De naam voor dit recept.' });
	$("#name").jqxInput({ theme: theme, width: 640, height: 23 });
	$("#locked").jqxCheckBox({ theme: theme, width: 120, height: 23 });
	$("#notes").jqxTooltip({ content: 'De uitgebreide opmerkingen over dit recept.' });
	$("#notes").jqxInput({ theme: theme, width: 960, height: 200 });
	$("#type").jqxTooltip({ content: 'Het brouw type van dit recept.' });
	$("#type").jqxDropDownList({
		theme: theme,
		source: RecipeTypeAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true
	});
	$("#efficiency").jqxTooltip({ content: 'Het rendement van maischen en koken.' });
	$("#efficiency").jqxNumberInput( Perc1dec );
	$("#batch_size").jqxTooltip({ content: 'Het volume van het gekoelde wort na het koken.' });
	$("#batch_size").jqxNumberInput( Spin1dec );
	$("#batch_size").jqxNumberInput({ min: 4 });
	$("#boil_size").jqxTooltip({ content: 'Het volume van het wort voor het koken.' });
	$("#boil_size").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 90, height: 23, decimalDigits: 2, readOnly: true });
	$("#boil_time").jqxTooltip({ content: 'De kooktijd in minuten.' });
	$("#boil_time").jqxNumberInput( PosInt );
	$("#boil_time").jqxNumberInput({ min: 4, max: 360 });

	$("#st_name").jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
	$("#st_name").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_letter").jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
	$("#st_letter").jqxInput({ theme: theme, width: 100, height: 23 });
	$("#st_guide").jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
	$("#st_guide").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_category").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
	$("#st_category").jqxInput({ theme: theme, width: 250, height: 23 });
	$("#st_category_number").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
	$("#st_category_number").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_type").jqxTooltip({ content: 'Het bierstijl type.'});
	$("#st_type").jqxInput({ theme: theme, width: 250, height: 23 });

	$("#est_og").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
	$("#est_og").jqxNumberInput( SGopts );
	$("#st_og_min").jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'});
	$("#st_og_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_og_max").jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'});
	$("#st_og_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });

	$("#est_fg").jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' });
	$("#est_fg").jqxNumberInput( Show3dec );
	$("#st_fg_min").jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'});
	$("#st_fg_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
	$("#st_fg_max").jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'});
	$("#st_fg_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });

	$("#est_abv").jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
	$("#est_abv").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_abv_min").jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
	$("#st_abv_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_abv_max").jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
	$("#st_abv_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });

	$("#est_color").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
	$("#est_color").jqxNumberInput( Show0dec );
	$("#st_color_min").jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
	$("#st_color_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_color_max").jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
	$("#st_color_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#color_method").jqxDropDownList({
		theme: theme,
		source: ColorMethodAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true
	});

	$("#est_ibu").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
	$("#est_ibu").jqxNumberInput( Show0dec );
	$("#st_ibu_min").jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
	$("#st_ibu_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#st_ibu_max").jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
	$("#st_ibu_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#ibu_method").jqxDropDownList({
		theme: theme,
		source: IBUmethodAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true,
		dropDownVerticalAlignment: 'top'
	});

	$("#est_carb").jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' });
	$("#est_carb").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_carb_min").jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
	$("#st_carb_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#st_carb_max").jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});
	$("#st_carb_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });

	// Tab 2, Vergistbaar
	$("#est_color2").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
	$("#est_color2").jqxNumberInput( Show0dec );
	$("#est_og2").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
	$("#est_og2").jqxNumberInput( Show3dec );
	$("#perc_malts").jqxProgressBar({
		width: 300,
		height: 23,
		theme: theme,
		showText: true,
		max: 120,
		animationDuration: 0,
		colorRanges: [
			{ stop:  90, color: '#008C00' },
			{ stop: 100, color: '#EB7331' },
			{ stop: 120, color: '#FF0000' }
		],
		renderText: function (text) {
			return (Math.round(parseInt(text) * 1.2)) + '%';
		}
	});
	$("#perc_sugars").jqxProgressBar({
		width: 300,
		height: 23,
		theme: theme,
		showText: true,
		max: 50,
		animationDuration: 0,
		colorRanges: [
			{ stop: 20, color: '#008C00' },
			{ stop: 50, color: '#FF0000' }
		],
		renderText: function (text) {
			return (Math.round(parseInt(text) * 5) / 10) + '%';
		}
	});
	$("#perc_cara").jqxProgressBar({
		width: 300,
		height: 23,
		theme: theme,
		showText: true,
		max: 50,
		animationDuration: 0,
		colorRanges: [
			{ stop: 25, color: '#008C00' },
			{ stop: 50, color: '#FF0000' }
		],
		renderText: function (text) {
			return (Math.round(parseInt(text) * 5) / 10) + '%';
		}
	});
        $("#ferm_lintner").jqxProgressBar({
                width: 300,
                height: 23,
                theme: theme,
                showText: true,
                max: 200,
                animationDuration: 0,
                colorRanges: [
                        { stop:  30, color: '#FF0000' },
                        { stop:  40, color: '#EB7331' },
                        { stop: 200, color: '#008C00' }
                ],
                renderText: function (text) {
                        return (parseInt(text) * 2) + ' lintner';
                }
        });
	$("#popupFermentable").jqxWindow({
		width: 800,
		height: 300,
		position: { x: 230, y: 100 },
		resizable: false,
		theme: theme,
		isModal: true,
		autoOpen: false,
		cancelButton: $("#FermentableReady"),
		modalOpacity: 0.40
	});
	$("#FermentableReady").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#FermentableReady").click(function () {
		var rowID = $("#fermentableGrid").jqxGrid('getrowid', fermentableRow);
		console.log("FermentableReady row:"+fermentableRow+" ID:"+rowID);
		var row = {
			f_name: fermentableData.f_name,
			f_origin: fermentableData.f_origin,
			f_supplier: fermentableData.f_supplier,
			f_amount: fermentableData.f_amount,
			f_cost: fermentableData.f_cost,
			f_type: fermentableData.f_type,
			f_yield: fermentableData.f_yield,
			f_color: fermentableData.f_color,
			f_coarse_fine_diff: fermentableData.f_coarse_fine_diff,
			f_moisture: fermentableData.f_moisture,
			f_diastatic_power: fermentableData.f_diastatic_power,
			f_protein: fermentableData.f_protein,
			f_max_in_batch: fermentableData.f_max_in_batch,
			f_graintype: fermentableData.f_graintype,
			f_added: fermentableData.f_added,
			f_dissolved_protein: fermentableData.f_dissolved_protein,
			f_recommend_mash: fermentableData.f_recommend_mash,
			f_add_after_boil: fermentableData.f_add_after_boil,
			f_adjust_to_total_100: fermentableData.f_adjust_to_total_100,
			f_percentage: fermentableData.f_percentage,
			f_di_ph: fermentableData.f_di_ph,
			f_acid_to_ph_57: fermentableData.f_acid_to_ph_57,
			f_inventory: fermentableData.f_inventory,
			f_avail: fermentableData.f_avail
		};
		$("#fermentableGrid").jqxGrid('updaterow', rowID, row);
		// Recalc percentages
		calcFermentables();
		calcIBUs();
		calcMash();
		// Waters: yes there is impact.
	});
	$("#wf_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wf_instock").jqxCheckBox({ theme: theme, height: 23 });
	$("#wf_instock").on('change', function (event) {
		fermentableinstock = event.args.checked;
		fermentablelist.dataBind();
	});
	$("#wf_select").jqxDropDownList({
		placeHolder: "Kies mout:",
		theme: theme,
		source: fermentablelist,
		displayMember: "name",
		width: 150,
		height: 23,
		dropDownWidth: 500,
		dropDownHeight: 500,
		renderer: function (index, label, value) {
			var datarecord = fermentablelist.records[index];
			return datarecord.supplier+ " / " + datarecord.name + " (" + datarecord.color + " EBC)";
		}
	});
	$("#wf_select").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = fermentablelist.records[index];
			$("#wf_name").val(datarecord.name);
			fermentableData.f_name = datarecord.name;
			fermentableData.f_origin = datarecord.origin;
			fermentableData.f_supplier = datarecord.supplier;
			fermentableData.f_type = datarecord.type;
			fermentableData.f_cost = datarecord.cost;
			fermentableData.f_yield = datarecord.yield;
			fermentableData.f_color = datarecord.color;
			fermentableData.f_coarse_fine_diff = datarecord.coarse_fine_diff;
			fermentableData.f_moisture = datarecord.moisture;
			fermentableData.f_diastatic_power = datarecord.diastatic_power;
			fermentableData.f_protein = datarecord.protein;
			fermentableData.f_max_in_batch = datarecord.max_in_batch;
			fermentableData.f_graintype = datarecord.graintype;
			fermentableData.f_dissolved_protein = datarecord.dissolved_protein;
			fermentableData.f_recommend_mash = datarecord.recommend_mash;
			fermentableData.f_add_after_boil = datarecord.add_after_boil;
			fermentableData.f_di_ph = datarecord.di_ph;
			fermentableData.f_acid_to_ph_57 = datarecord.acid_to_ph_57;
			fermentableData.f_inventory = datarecord.inventory;
		}
	});
	$("#wf_amount").jqxNumberInput( Spin3dec );
	$('#wf_amount').on('change', function (event) {
		console.log("amount changed: "+event.args.value);
		$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_amount', event.args.value);
		fermentableData.f_amount = event.args.value;
		if (! to_100) {
			// Recalculate 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);
		//			if (i == fermentableRow) // Will crash the script.
		//				$("#wf_percentage").val(percentage);
				};
			} else {
				$("#fermentableGrid").jqxGrid('setcellvalue', 0, "f_percentage", 100);
			}
			calcFermentables();
			calcMash();
		};
	});
	$("#wf_percentage").jqxNumberInput( Perc1dec );
	$("#wf_percentage").on('change', function (event) {
		var oldvalue = Math.round(fermentableData.f_percentage * 10) / 10.0;
		var newvalue = event.args.value;
		console.log("percentage changed: "+newvalue+" old: "+oldvalue);
		fermentableData.f_percent = newvalue;
		var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
		if ((oldvalue != newvalue) && (rowscount > 1)) {
			var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', fermentableRow);
			if (rowdata.f_adjust_to_total_100) {
				$("#wf_percentage").val(oldvalue);
			} else {
				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 damount = tw * diff / 100;
					var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', fermentableRow);
					var namount = rowdata.f_amount + damount;
					$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_amount', namount);
					$("#wf_amount").val(namount);
					$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_percentage', rowdata.f_percentage + diff);
					for (i = 0; i < rowscount; i++) {
						var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
						if (rowdata.f_adjust_to_total_100) {
							namount = rowdata.f_amount - damount;
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_percentage', rowdata.f_percentage - diff);
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_amount', namount);
						}
					}
					calcFermentables();
					cacMash();
				} else {
					// Adjust all the rows.
					var nw = tw * diff / 100;
					for (i = 0; i < rowscount; i++) {
						var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
						if (i == fermentableRow) {
							var namount = rowdata.f_amount + nw;
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_amount', namount);
		//					$("#wf_amount").val(namount); // Will crash the script.
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_percentage', newvalue);
						} else {
							var namount = rowdata.f_amount - (nw / (rowscount - 1));
							var newperc = Math.round((namount / tw) * 1000) / 10.0;
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_amount', namount);
							$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_percentage', newperc);
						}
					}
					calcFermentables();
					calcMash();
				}
			}
		}

	});
	$("#wf_max_in_batch").jqxNumberInput( Show1dec );
	$("#wf_adjust_to_total_100").jqxCheckBox({ theme: theme, width: 120, height: 23 });
	$("#wf_adjust_to_total_100").on('checked', function (event) {
		if (fermentableData.f_adjust_to_total_100 == 0) {
			if (to_100) {
				// Reset other flag first.
				var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
				for (var i = 0; i < rowscount; i++) {
					if (i != fermentableRow) {
						$("#fermentableGrid").jqxGrid('setcellvalue', i, 'f_adjust_to_total_100', 0);
					}
				}
			}
			$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_adjust_to_total_100', 1);
			calcFermentables();
		}
	});
	$("#wf_adjust_to_total_100").on('unchecked', function (event) {
		if (fermentableData.f_adjust_to_total_100 != 0) {
			$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_adjust_to_total_100', 0);
			calcFermentables();
		}
	});
	$("#wf_added").jqxDropDownList({
		theme: theme,
		source: AddedAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true,
		dropDownVerticalAlignment: 'top'
	});
	$("#wf_added").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			$("#fermentableGrid").jqxGrid('setcellvalue', fermentableRow, 'f_added', index);
			calcFermentables();
			calcIBUs();
			calcMash();
		}
	});

	// Tab 3, Hoppen
	$("#est_ibu2").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
	$("#est_ibu2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
	$("#hop_flavour").jqxProgressBar({
		width: 300,
		height: 23,
		theme: theme,
		showText: true,
		animationDuration: 0,
		colorRanges: [
			{ stop:  20, color: '#004D00' },
			{ stop:  40, color: '#008C00' },
			{ stop:  60, color: '#00BF00' },
			{ stop:  80, color: '#00FF00' },
			{ stop: 100, color: '#80FF80' }
		],
                renderText: function (text) {
                        var val = parseInt(text);
                        if (val < 20)
                                return 'Weinig';
                        else if (val < 40)
                                return 'Matig';
                        else if (val < 60)
                                return 'Redelijk';
                        else if (val < 80)
                                return 'Veel';
                        else
                                return 'Zeer veel';
                }
	});
	$("#hop_aroma").jqxProgressBar({
		width: 300,
		height: 23,
		theme: theme,
		showText: true,
		animationDuration: 0,
		colorRanges: [
			{ stop:  20, color: '#004D00' },
			{ stop:  40, color: '#008C00' },
			{ stop:  60, color: '#00BF00' },
			{ stop:  80, color: '#00FF00' },
			{ stop: 100, color: '#80FF80' }
		],
                renderText: function (text) {
                        var val = parseInt(text);
                        if (val < 20)
                                return 'Weinig';
                        else if (val < 40)
                                return 'Matig';
                        else if (val < 60)
                                return 'Redelijk';
                        else if (val < 80)
                                return 'Veel';
                        else
                                return 'Zeer veel';
                }
	});
	$("#popupHop").jqxWindow({
		width: 800,
		height: 300,
		position: { x: 230, y: 100 },
		resizable: false,
		theme: theme,
		isModal: true,
		autoOpen: false,
		cancelButton: $("#HopReady"),
		modalOpacity: 0.40
	});
	$("#HopReady").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#HopReady").click(function () {
		var rowID = $("#hopGrid").jqxGrid('getrowid', hopRow);
		console.log("HopReady row:"+hopRow+" ID:"+rowID);
		var row = {
			h_name: $("#wh_name").val(),
			h_origin: hopData.h_origin,
			h_amount: parseFloat($("#wh_amount").jqxNumberInput('decimal')) / 1000,
			h_cost: hopData.h_cost,
			h_type: hopData.h_type,
			h_form: hopData.h_form,
			h_useat: $("#wh_useat").val(),
			h_time: hopData.h_time,
			h_alpha: hopData.h_alpha,
			h_beta: hopData.h_beta,
			h_hsi: hopData.h_hsi,
			h_humulene: hopData.h_humulene,
			h_caryophyllene: hopData.h_caryophyllene,
			h_cohumulone: hopData.h_cohumulone,
			h_myrcene: hopData.h_myrcene,
			h_total_oil: hopData.h_total_oil,
			h_inventory: hopData.h_inventory,
			h_avail: hopData.h_avail
		};
		$("#hopGrid").jqxGrid('updaterow', rowID, row);
		calcIBUs();
	});
	$("#wh_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wh_instock").jqxCheckBox({ theme: theme, height: 23 });
	$("#wh_instock").on('change', function (event) {
		hopinstock = event.args.checked;
		hoplist.dataBind();
	});
	$("#wh_select").jqxDropDownList({
		placeHolder: "Kies hop:",
		theme: theme,
		source: hoplist,
		displayMember: "name",
		width: 150,
		height: 23,
		dropDownWidth: 500,
		dropDownHeight: 500,
		renderer: function (index, label, value) {
			var datarecord = hoplist.records[index];
			return datarecord.origin+ " / " + datarecord.name + " (" + datarecord.alpha + " % &alpha;)";
		}
	});
	$("#wh_select").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = hoplist.records[index];
			$("#wh_name").val(datarecord.name);
			hopData.h_name = datarecord.name;
			hopData.h_origin = datarecord.origin;
			hopData.h_cost = datarecord.cost;
			hopData.h_type = datarecord.type;
			hopData.h_form = datarecord.form;
			hopData.h_alpha = datarecord.alpha;
			hopData.h_beta = datarecord.beta;
			hopData.h_hsi = datarecord.hsi;
			hopData.h_humulene = datarecord.humulene;
			hopData.h_caryophyllene = datarecord.caryophyllene;
			hopData.h_cohumulone = datarecord.cohumulone;
			hopData.h_myrcene = datarecord.myrcene;
			hopData.h_total_oil = datarecord.total_oil;
			hopData.h_inventory = datarecord.inventory;
		}
	});
	$("#wh_amount").jqxNumberInput( Spin1dec );
	$('#wh_amount').on('change', function (event) {
		console.log("amount changed: "+event.args.value+" time:"+hopData.h_time+" alpha:"+hopData.h_alpha);
		var amount = parseFloat(event.args.value) / 1000;

		var ibu = toIBU(hopData.h_useat, hopData.h_form, preboil_sg,
			parseFloat($("#batch_size").jqxNumberInput('decimal')),
			amount, parseFloat(hopData.h_time),
			parseFloat(hopData.h_alpha), $("#ibu_method").val()
		);
		hopData.h_amount = amount;
		$("#wh_ibu").val(ibu);
	});
	$("#wh_ibu").jqxNumberInput( Show1dec );
	$("#wh_time").jqxNumberInput( PosInt );
	$("#wh_time").on('change', function (event) {
		var newtime = parseFloat(event.args.value);
		// Check limits and correct
		if (hopData.h_useat == 2) {	// Boil
			if (newtime > parseFloat($("#boil_time").jqxNumberInput('decimal'))) {
				newtime = parseFloat($("#boil_time").jqxNumberInput('decimal'));
				$("#wh_time").val(newtime);
			}
			hopData.h_time = newtime;
		} else if (hopData.h_useat == 4) {	// Whirlpool
			if (newtime > 120) {
				newtime = 120;
				$("#wh_time").val(newtime);
			}
			hopData.h_time = newtime;
		} else if (hopData.h_useat == 5) {	// Dry hop
			if (newtime > 21) {
				newtime = 21;
				$("#wh_time").val(newtime);
			}
			hopData.h_time = newtime * 1440;
		}
		var ibu = toIBU(hopData.h_useat, hopData.h_form, preboil_sg, parseFloat($("#batch_size").jqxNumberInput('decimal')),
			parseFloat(hopData.h_amount), parseFloat(hopData.h_time), parseFloat(hopData.h_alpha), $("#ibu_method").val());
		$("#wh_ibu").val(ibu);
	});
	$("#wh_useat").jqxDropDownList({
		theme: theme,
		source: HopUseAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true,
		dropDownVerticalAlignment: 'top'
	});
	$("#wh_useat").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			hopData.h_useat = index;
			if ((index == 0) || (index == 1)) {	// Mashhop or First wort hop
				hopData.h_time = parseFloat(dataRecord.boil_time);
				$("#wh_time").jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
				$("#wh_time").val(hopData.h_time);
			} else if (index == 3) {	// Aroma
				hopData.h_time = 0;
				$("#wh_time").jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
				$("#wh_time").val(0);
			} else {	// Boil, Whirlpool or Dry hop
				$("#wh_time").jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
			}
			if (index == 5)	// Dry hop
				$("#wh_pmpt_time").html("Tijd in dagen");
			else
				$("#wh_pmpt_time").html("Tijd in minuten");
		}
	});

	// Tab 4, Diversen
	$("#popupMisc").jqxWindow({
		width: 800,
		height: 275,
		position: { x: 230, y: 100 },
		resizable: false,
		theme: theme,
		isModal: true,
		autoOpen: false,
		cancelButton: $("#MiscReady"),
		modalOpacity: 0.40
	});
	$("#MiscReady").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#MiscReady").click(function () {
		var rowID = $("#miscGrid").jqxGrid('getrowid', miscRow);
		console.log("MiscReady row:"+miscRow+" ID:"+rowID);
		var row = {
			m_name: miscData.m_name,
			m_amount: miscData.m_amount,
			m_cost: miscData.m_cost,
			m_type: miscData.m_type,
			m_use_use: miscData.m_use_use,
			m_time: miscData.m_time,
			m_amount_is_weight: miscData.m_amount_is_weight,
			m_inventory: miscData.m_inventory,
			m_avail:  miscData.m_avail
		};
		$("#miscGrid").jqxGrid('updaterow', rowID, row);
        });
	$("#wm_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wm_instock").jqxCheckBox({ theme: theme, height: 23 });
	$("#wm_instock").on('change', function (event) {
		miscinstock = event.args.checked;
		misclist.dataBind();
	});
	$("#wm_select").jqxDropDownList({
		placeHolder: "Kies ingredi&euml;nt:",
		theme: theme,
		source: misclist,
		displayMember: "name",
		width: 150,
		height: 23,
		dropDownWidth: 500,
		dropDownHeight: 500
	});
	$("#wm_select").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = misclist.records[index];
			$("#wm_name").val(datarecord.name);
			miscData.m_name = datarecord.name;
			miscData.m_cost = datarecord.cost;
			miscData.m_type = datarecord.type;
			miscData.m_use_use = datarecord.use_use;
			miscData.m_amount_is_weight = datarecord.amount_is_weight;
			miscData.m_inventory = datarecord.inventory;
		}
	});
	$("#wm_amount").jqxNumberInput( Spin1dec );
	$('#wm_amount').on('change', function (event) {
		console.log("amount changed: "+event.args.value);
		miscData.m_amount = parseFloat(event.args.value) / 1000;
	});
	$("#wm_time").jqxNumberInput( PosInt );
	$("#wm_time").on('change', function (event) {
		console.log("time changed: "+event.args.value);
		var newtime = parseFloat(event.args.value);

		if (miscData.m_use_use == 2) {	// Boil
			if (newtime > parseFloat($("#boil_time").jqxNumberInput('decimal'))) {
				newtime = parseFloat($("#boil_time").jqxNumberInput('decimal'));
				$("#wm_time").val(newtime);
			}
		        miscData.m_time = newtime;
		} else if ((miscData.m_use_use == 3) || (miscData.m_use_use == 4)) {	// Primary or Secondary
			if (newtime > 21) {
				newtime = 21;
				$("#wm_time").val(newtime);
			}
			miscData.m_time = newtime * 1440;
		}
	});
	$("#wm_use_use").jqxDropDownList({
		theme: theme,
		source: MiscUseAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true,
		dropDownVerticalAlignment: 'top'
	});
	$("#wm_use_use").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			miscData.m_use_use = index;
			if ((index == 2) || (index == 3) || (index == 4)) {	// Boil, Primary or Secondary
				$("#wm_time").jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
			} else {
				miscData.m_time = 0;
				$("#wm_time").jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
				$("#wm_time").val(0);
			}
		}
	});

	// Tab 5, Gist
	$("#est_fg2").jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' });
	$("#est_fg2").jqxNumberInput( Show3dec );
	$("#est_abv2").jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
	$("#est_abv2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
	$("#popupYeast").jqxWindow({
		width: 800,
		height: 300,
		position: { x: 230, y: 100 },
		resizable: false,
		theme: theme,
		isModal: true,
		autoOpen: false,
		cancelButton: $("#YeastReady"),
		modalOpacity: 0.40
	});
	$("#YeastReady").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#YeastReady").click(function () {
		var rowID = $("#yeastGrid").jqxGrid('getrowid', yeastRow);
		console.log("YeastReady row:"+yeastRow+" ID:"+rowID);
		var row = {
			y_name: yeastData.y_name,
			y_laboratory: yeastData.y_laboratory,
			y_product_id: yeastData.y_product_id,
			y_amount: yeastData.y_amount,
			y_cost: yeastData.y_cost,
			y_type: yeastData.y_type,
			y_form: yeastData.y_form,
			y_flocculation: yeastData.y_flocculation,
			y_min_temperature: yeastData.y_min_temperature,
			y_max_temperature: yeastData.y_max_temperature,
			y_attenuation: yeastData.y_attenuation,
			y_use: yeastData.y_use,
			y_cells: yeastData.y_cells,
			y_tolerance: yeastData.y_tolerance,
			y_inventory: yeastData.y_inventory,
			y_avail: yeastData.y_avail
		};
		$("#yeastGrid").jqxGrid('updaterow', rowID, row);
		calcFermentables();
	});
	$("#wy_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wy_laboratory").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wy_product_id").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wy_instock").jqxCheckBox({ theme: theme, height: 23 });
	$("#wy_instock").on('change', function (event) {
		yeastinstock = event.args.checked;
		yeastlist.dataBind();
	});
	$("#wy_select").jqxDropDownList({
		placeHolder: "Kies gist:",
		theme: theme,
		source: yeastlist,
		displayMember: "name",
		width: 150,
		height: 23,
		dropDownWidth: 500,
		dropDownHeight: 500,
		renderer: function (index, label, value) {
			var datarecord = yeastlist.records[index];
			return datarecord.laboratory+" "+datarecord.product_id+" "+datarecord.name;
		}
	});
	$("#wy_select").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = yeastlist.records[index];
			$("#wy_name").val(datarecord.name);
			$("#wy_laboratory").val(datarecord.laboratory);
			$("#wy_product_id").val(datarecord.product_id);
			yeastData.y_name = datarecord.name;
			yeastData.y_cost = datarecord.cost;
			yeastData.y_type = datarecord.type;
			yeastData.y_form = datarecord.form;
			yeastData.y_laboratory = datarecord.laboratory;
			yeastData.y_product_id = datarecord.product_id;
			yeastData.y_min_temperature = datarecord.min_temperature;
			yeastData.y_max_temperature = datarecord.max_temperature;
			yeastData.y_flocculation = datarecord.flocculation;
			yeastData.y_attenuation = datarecord.attenuation;
			yeastData.y_cells = datarecord.cells;
			yeastData.y_inventory = datarecord.inventory;
			if (yeastData.y_form == 0) {
				$("#wy_pmpt_amount").html("Pak(ken):");
			} else if (yeastData.y_form == 1) {
				$("#wy_pmpt_amount").html("Gewicht gram:");
			} else {
				$("#wy_pmpt_amount").html("Volume ml:");
			}
			calcFermentables();
		}
	});
	$("#wy_amount").jqxNumberInput( Spin1dec );
	$('#wy_amount').on('change', function (event) {
		console.log("amount changed: "+event.args.value);
		if (yeastData.y_form == 0)	// Liquid
			var amount = parseFloat(event.args.value);
		else
			var amount = parseFloat(event.args.value) / 1000;
		yeastData.y_amount = amount;
		calcFermentables();
	});
	$("#wy_use").jqxDropDownList({
		theme: theme,
		source: YeastUseAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true,
		dropDownVerticalAlignment: 'top'
	});
	$("#wy_use").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			yeastData.y_use = index;
			calcFermentabes();
		}
	});

	// Tab 6, Maischen
	$("#mash_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#mash_select").jqxDropDownList({
		placeHolder: "Kies schema:",
		theme: theme,
		source: mashlist,
		displayMember: "name",
		width: 250,
		height: 23,
		dropDownWidth: 500,
		dropDownHeight: 500,
		dropDownHorizontalAlignment: 'right'
	});
	$("#mash_select").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			// First delete all current steps
			var rowIDs = new Array();
			var rows = $("#mashGrid").jqxGrid('getdisplayrows');
			for (var i = 0; i < rows.length; i++) {
				var row = rows[i];
				rowIDs.push(row.uid);
			}
			$("#mashGrid").jqxGrid('deleterow', rowIDs);
			// Then add the new steps
			var datarecord = mashlist.records[index];
			$("#mash_name").val(datarecord.name);
			for (var i = 0; i < datarecord.steps.length; i++) {
				var data = datarecord.steps[i];
				var row = {};
				row["step_name"] = data.step_name;
				row["step_type"] = data.step_type;
				// For now, but this must be smarter.
				if (mash_infuse == 0 && dataRecord.w1_amount > 0)
					mash_infuse = dataRecord.w1_amount;
				if (i == 0)
					row["step_infuse_amount"] = mash_infuse;
				else
					row["step_infuse_amount"] = 0;
				row["step_temp"] = data.step_temp;
				if (mashkg > 0)
					row["step_thickness"] = parseFloat(mash_infuse / mashkg);
				else
					row["step_thickness"] = 0;
				row["end_temp"] = data.end_temp;
				row["step_time"] = data.step_time;
				row["ramp_time"] = data.ramp_time;
				var commit = $("#mashGrid").jqxGrid('addrow', null, row);
			}
		}
	});
	$("#popupMash").jqxWindow({
		width: 800,
		height: 350,
		position: { x: 230, y: 100 },
		resizable: false,
		theme: theme,
		isModal: true,
		autoOpen: false,
		cancelButton: $("#MashReady"),
		modalOpacity: 0.40
	});
	$("#MashReady").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#MashReady").click(function () {
		calcMash();
	});
	$("#wstep_name").jqxInput({ theme: theme, width: 320, height: 23 });
	$("#wstep_name").on('change', function (event) {
                var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
                rowdata.step_name = event.args.value;
        });
	$("#wstep_type").jqxDropDownList({
		theme: theme,
		source: MashStepTypeAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 180,
		height: 23,
		autoDropDownHeight: true
	});
	$("#wstep_type").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
			rowdata.step_type = index;
			if (index == 0) {
				$("#wstep_infuse_amount").show();
				$("#wstep_pmpt").show();
			} else {
				rowdata.step_infuse_amount = 0;
				$("#wstep_infuse_amount").hide();
				$("#wstep_pmpt").hide();
			}
			mash_infuse = 0;
			var rows = $('#mashGrid').jqxGrid('getrows');
			for (var i = 0; i < rows.length; i++) {
				var row = rows[i];
				if (row.step_type == 0) // Infusion
					mash_infuse += parseFloat(row.step_infuse_amount);
			}
		}
	});
	$("#wstep_temp").jqxNumberInput( Spin1dec );
	$('#wstep_temp').on('change', function (event) {
		var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
		rowdata.step_temp = parseFloat(event.args.value);
	});
	$("#wend_temp").jqxNumberInput( Spin1dec );
	$('#wend_temp').on('change', function (event) {
		var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
		rowdata.end_temp = parseFloat(event.args.value);
	});
	$("#wstep_time").jqxNumberInput( PosInt );
	$('#wstep_time').on('change', function (event) {
		var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
		rowdata.step_time = parseFloat(event.args.value);
	});
	$("#wramp_time").jqxNumberInput( PosInt );
	$('#wramp_time').on('change', function (event) {
		var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
		rowdata.ramp_time = parseFloat(event.args.value);
	});
	$("#wstep_infuse_amount").jqxNumberInput( Spin1dec );
	$('#wstep_infuse_amount').on('change', function (event) {
		var rowdata = $("#mashGrid").jqxGrid('getrowdata', mashRow);
		rowdata.step_infuse_amount = parseFloat(event.args.value);
		mash_infuse = 0;
		var rows = $('#mashGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.step_type == 0) // Infusion
				mash_infuse += parseFloat(row.step_infuse_amount);
		}
		if (dataRecord.w2_amount == 0) {
			dataRecord.w1_amount = mash_infuse;
			$("#w1_amount").val(mash_infuse);
		} else {
			dataRecord.w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
			dataRecord.w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
			$("#w1_amount").val(dataRecord.w1_amount);
			$("#w2_amount").val(dataRecord.w2_amount);
		}
		$('#wg_amount').val(mash_infuse);
	});

	// Tab 7, Water
	$("#tgt_bu").jqxNumberInput( Show2wat );
	$("#tgt_cl_so4").jqxNumberInput( Show1wat );
	$("#got_cl_so4").jqxNumberInput( Show1wat );

	// Water source 1
	$("#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( Show1wat );
	$("#w1_calcium").jqxNumberInput( Show1wat );
	$("#w1_magnesium").jqxNumberInput( Show1wat );
	$("#w1_sodium").jqxNumberInput( Show1wat );
	$("#w1_total_alkalinity").jqxNumberInput( Show1wat );
	$("#w1_chloride").jqxNumberInput( Show1wat );
	$("#w1_sulfate").jqxNumberInput( Show1wat );
	$("#w1_ph").jqxNumberInput( Show1wat );
	// Water source 2
	$("#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").jqxTooltip({ content: 'De verdeling van het hoofd en meng water. Het totale maisch water volume blijft gelijk.'});
	$("#w2_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 94, height: 23, min: 0, max: 0, decimalDigits: 1, spinButtons: true, readOnly: true });
	$("#w2_calcium").jqxNumberInput( Show1wat );
	$("#w2_magnesium").jqxNumberInput( Show1wat );
	$("#w2_sodium").jqxNumberInput( Show1wat );
	$("#w2_total_alkalinity").jqxNumberInput( Show1wat );
	$("#w2_chloride").jqxNumberInput( Show1wat );
	$("#w2_sulfate").jqxNumberInput( Show1wat );
	$("#w2_ph").jqxNumberInput( Show1wat );
	// Water mixed
	$("#wg_amount").jqxNumberInput( Show1wat );
	$("#wg_calcium").jqxNumberInput( Show1wat );
	$("#wg_magnesium").jqxNumberInput( Show1wat );
	$("#wg_sodium").jqxNumberInput( Show1wat );
	$("#wg_total_alkalinity").jqxNumberInput( Show1wat );
	$("#wg_chloride").jqxNumberInput( Show1wat );
	$("#wg_sulfate").jqxNumberInput( Show1wat );
	$("#wg_ph").jqxNumberInput( Show1wat );
	// Water treated
	$("#wb_calcium").jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'});
	$("#wb_calcium").jqxNumberInput( Show1wat );
	$("#wb_magnesium").jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is lager dan 30.'});
	$("#wb_magnesium").jqxNumberInput( Show1wat );
	$("#wb_sodium").jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'});
	$("#wb_sodium").jqxNumberInput( Show1wat );
	$("#wb_chloride").jqxTooltip({ content: 'De ideale hoeveelheid Chloride is tussen 50 en 100.'});
	$("#wb_chloride").jqxNumberInput( Show1wat );
	$("#wb_sulfate").jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 350.'});
	$("#wb_sulfate").jqxNumberInput( Show1wat );
	$("#wb_total_alkalinity").jqxNumberInput( Show1wat );
	$("#wb_ph").jqxNumberInput( Show1wat );
	// Water target profile
	$("#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( Show1wat );
	$("#pr_magnesium").jqxNumberInput( Show1wat );
	$("#pr_sodium").jqxNumberInput( Show1wat );
	$("#pr_total_alkalinity").jqxNumberInput( Show1wat );
	$("#pr_chloride").jqxNumberInput( Show1wat );
	$("#pr_sulfate").jqxNumberInput( Show1wat );

	// Water agents
	$("#wa_cacl2").jqxTooltip({ content: 'Voor het maken van een ander waterprofiel. Voegt calcium en chloride toe. Voor het verbeteren van zoetere bieren.'});
	$("#wa_cacl2").jqxNumberInput( Spin1dec );
	$("#wa_caso4").jqxTooltip({ content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.'});
	$("#wa_caso4").jqxNumberInput( Spin1dec );
	$("#wa_mgso4").jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'});
	$("#wa_mgso4").jqxNumberInput( Spin1dec );
	$("#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.'});
	$("#wa_nacl").jqxNumberInput( Spin1dec );
	$("#mash_ph").jqxTooltip({ content: 'Maisch pH tussen 5.2 en 5.6. Gebruik 5.2 voor lichte en 5.5 voor donkere bieren.'});
	$("#mash_ph").jqxNumberInput( SpinpH );
	$("#calc_acid").jqxCheckBox({ theme: theme, width: 120, height: 23 });
	$("#wa_base_name").jqxDropDownList({
		theme: theme,
		source: BaseTypeAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 170,
		height: 23,
		autoDropDownHeight: true
	});
	$("#wa_base").jqxNumberInput( Spin2dec );
	$("#wa_base").jqxNumberInput({ symbol: ' gr', symbolPosition: 'right' });
	$("#wa_acid_name").jqxDropDownList({
		theme: theme,
		source: AcidTypeAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 170,
		height: 23,
		autoDropDownHeight: true
	});
	$("#wa_acid").jqxNumberInput( Spin2dec );
	$("#wa_acid").jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' });
	$("#wa_acid_perc").jqxNumberInput( Perc0 );
	$("#wa_acid_perc").jqxNumberInput({ width: 70, symbol: '%', symbolPosition: 'right' });
	// Sparge water
	$("#sparge_temp").jqxNumberInput( Spin1dec );
	$("#sparge_volume").jqxNumberInput( Spin1dec );
	$("#sparge_ph").jqxNumberInput( SpinpH );
	$("#sparge_source").jqxDropDownList({
		theme: theme,
		source: SpargeSourceAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 110,
		height: 23,
		autoDropDownHeight: true
	});
	$("#sparge_acid_amount").jqxNumberInput( Spin2dec );
	$("#sparge_acid_amount").jqxNumberInput({ spinButtons: false, readOnly: true, symbol: ' ml', symbolPosition: 'right' });
	$("#sparge_acid_type").jqxDropDownList({
		theme: theme,
		source: AcidTypeAdapter,
		valueMember: 'id',
		displayMember: 'nl',
		width: 110,
		height: 23,
		autoDropDownHeight: true
	});
	$("#sparge_acid_perc").jqxNumberInput( Perc0 );
	$("#sparge_acid_perc").jqxNumberInput({ symbol: '%', symbolPosition: 'right' });

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

	// Buttons below
	$("#Export").jqxButton({ template: "info", width: '80px', theme: theme });
	$("#Export").bind('click', function () {
		saveRecord();
		var url="rec_export.php?record=" + my_record + "&return=" + my_return + "&name=" + dataRecord.name;
		window.location.href = url;
	});

	$("#Delete").jqxButton({ template: "danger", width: '80px', theme: theme });
	$("#Delete").bind('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").bind('click', function () {
		window.location.href = my_return;
	});

	$("#Save").jqxButton({ template: "success", width: '80px', theme: theme });
	$("#Save").bind('click', function () {
		saveRecord();
		window.location.href = my_return;
	});
	createDelElements();
});

mercurial