www/js/rec_edit.js

Sat, 15 Dec 2018 21:27:01 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Sat, 15 Dec 2018 21:27:01 +0100
changeset 142
793af7691a5e
parent 141
fe9e2a677611
child 143
122f64716161
permissions
-rw-r--r--

During hops load an extra weight in grams field is generated for the grid. Edit the amount is done on the weight field and the amount field is updated too. During save to the database the extra field is removed.

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


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


$(document).ready(function () {

	var	dataRecord = {};
	var	to_100 = false;	// Fermentables adjust to 100%
	var	preboil_sg = 0;
	var	sugarsm = 0;	// Sugars after mash
	var	sugarsf = 0;	// Sugars after boil
	var     psugar = 0;     // Percentage real sugars
	var     pcara = 0;      // Percentage cara/crystal malts
	var	svg = 77;	// Default attenuation
	var	hop_flavour = 0;
	var	hop_aroma = 0;

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

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

		var rows = $('#fermentableGrid').jqxGrid('getrows');
		for (var i = 0; i < rows.length; i++) {
			var row = rows[i];
			if (row.f_adjust_to_total_100)
				my_100 = true;
			if (row.f_type == "Sugar")
				psugar += row.f_percentage;
			if (row.f_graintype == "Crystal")
				pcara += row.f_percentage;
			var d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
			if (row.f_added == "Mash") {
				d = parseFloat($("#efficiency").jqxNumberInput('decimal')) / 100 * d;
				sugarsm += d;
			}
			sugarsf += d;
			colorw += row.f_amount * ebc_to_srm(row.f_color) / parseFloat($("#batch_size").jqxNumberInput('decimal')) * 8.34436;
		}
		if (to_100 != my_100)
			console.log("change to_100 to:"+my_100);
		to_100 = my_100;
		var est_og = estimate_sg(sugarsf, parseFloat($("#batch_size").jqxNumberInput('decimal')));
		$('#est_og').val(est_og);
		$('#est_og2').val(est_og);
		preboil_sg = estimate_sg(sugarsm, parseFloat($("#boil_size").jqxNumberInput('decimal')));
		var color = kw_to_ebc($("#color_method").val(), colorw);
		$('#est_color').val(color);
		$('#est_color2').val(color);
		var scolor = ebc_to_color(color);
		document.getElementById("bcolor").style.background= scolor;
		document.getElementById("bcolor2").style.background= scolor;
	};

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

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

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

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

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

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

	function calcFermentablesFromOG(OG) {

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

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

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

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

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

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

	// Styles dropdown list
	var stylesUrl = "includes/db_profile_styles.php";
	var stylesSource = {
		datatype: "json",
		datafields: [
			{ name: 'record', type: 'number' },
		        { name: 'name', type: 'string' },
			{ name: 'category', type: 'string' },
			{ name: 'category_number', type: 'number' },
			{ name: 'style_letter', type: 'string' },
			{ name: 'style_guide', type: 'string' },
			{ name: 'type', type: 'string' },
			{ name: 'og_min', type: 'float' },
			{ name: 'og_max', type: 'float' },
			{ name: 'fg_min', type: 'float' },
			{ name: 'fg_max', type: 'float' },
			{ name: 'ibu_min', type: 'float' },
			{ name: 'ibu_max', type: 'float' },
			{ name: 'color_min', type: 'float' },
			{ name: 'color_max', type: 'float' },
			{ name: 'carb_min', type: 'float' },
			{ name: 'carb_max', type: 'float' },
			{ name: 'abv_min', type: 'float' },
			{ name: 'abv_max', type: 'float' },
			{ name: 'notes', type: 'string' },
			{ name: 'profile', type: 'string' },
			{ name: 'ingredients', type: 'string' },
			{ name: 'examples', type: 'string' }
		],
		url: stylesUrl,
		async: true
	};
	var styleslist = new $.jqx.dataAdapter(stylesSource);
	$("#styleSelect").jqxDropDownList({
		placeHolder: "Kies bierstijl:",
		theme: theme,
		source: styleslist,
		displayMember: "name",
		width: 150,
		height: 27,
		dropDownVerticalAlignment: 'top',
		dropDownWidth: 500,
		dropDownHeight: 380,
		renderer: function (index, label, value) {
			var datarecord = styleslist.records[index];
			return datarecord.style_guide + " " + datarecord.style_letter+ " " + datarecord.name;
		}
	});
	$("#styleSelect").on('select', function (event) {
		if (event.args) {
			var index = event.args.index;
			var datarecord = styleslist.records[index];
			$("#st_name").val(datarecord.name);
			$("#st_category").val(datarecord.category);
			$("#st_category_number").val(datarecord.category_number);
			$("#st_letter").val(datarecord.style_letter);
			$("#st_guide").val(datarecord.style_guide);
			$("#st_type").val(datarecord.type);
			$("#st_og_min").val(datarecord.og_min);
			$("#st_og_max").val(datarecord.og_max);
			$("#st_fg_min").val(datarecord.fg_min);
			$("#st_fg_max").val(datarecord.fg_max);
			$("#st_ibu_min").val(datarecord.ibu_min);
			$("#st_ibu_max").val(datarecord.ibu_max);
			$("#st_color_min").val(datarecord.color_min);
			$("#st_color_max").val(datarecord.color_max);
			$("#st_carb_min").val(datarecord.carb_min);
			$("#st_carb_max").val(datarecord.carb_max);
			$("#st_abv_min").val(datarecord.abv_min);
			$("#st_abv_max").val(datarecord.abv_max);
		}
	});

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

	$("#st_name").jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
	$("#st_letter").jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
	$("#st_guide").jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
	$("#st_category").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
	$("#st_category_number").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
	$("#st_type").jqxTooltip({ content: 'Het bierstijl type.'});
	$("#st_og_min").jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'});
	$("#st_og_max").jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'});
	$("#st_fg_min").jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'});
	$("#st_fg_max").jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'});
	$("#st_color_min").jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
	$("#st_color_max").jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
	$("#st_ibu_min").jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
	$("#st_ibu_max").jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
	$("#st_abv_min").jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
	$("#st_abv_max").jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
	$("#st_carb_min").jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
	$("#st_carb_max").jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});

	// prepare the data
	var source = {
		datatype: "json",
		cache: false,
		datafields: [
			{ name: 'record', type: 'number' },
			{ name: 'st_name', type: 'string' },
			{ name: 'st_letter', type: 'string' },
			{ name: 'st_guide', type: 'string' },
			{ name: 'st_type', type: 'string' },
			{ name: 'st_category', type: 'string' },
			{ name: 'st_category_number', type: 'float' },
			{ name: 'st_og_min', type: 'float' },
			{ name: 'st_og_max', type: 'float' },
			{ name: 'st_fg_min', type: 'float' },
			{ name: 'st_fg_max', type: 'float' },
			{ name: 'st_ibu_min', type: 'float' },
			{ name: 'st_ibu_max', type: 'float' },
			{ name: 'st_color_min', type: 'float' },
			{ name: 'st_color_max', type: 'float' },
			{ name: 'st_carb_min', type: 'float' },
			{ name: 'st_carb_max', type: 'float' },
			{ name: 'st_abv_min', type: 'float' },
			{ name: 'st_abv_max', type: 'float' },
			{ name: 'name', type: 'string' },
			{ name: 'notes', type: 'string' },
			{ name: 'type', type: 'string' },
			{ name: 'batch_size', type: 'float' },
			{ name: 'boil_size', type: 'float' },
			{ name: 'boil_time', type: 'float' },
			{ name: 'efficiency', type: 'float' },
			{ name: 'est_og', type: 'float' },
			{ name: 'est_fg', type: 'float' },
			{ name: 'est_abv', type: 'float' },
			{ name: 'est_color', type: 'float' },
			{ name: 'color_method', type: 'string' },
			{ name: 'est_ibu', type: 'float' },
			{ name: 'ibu_method', type: 'string' },
			{ name: 'est_carb', type: 'float' },
			{ name: 'mash_sparge_temp', type: 'float' },
			{ name: 'mash_ph', type: 'float' },
			{ name: 'mash_name', type: 'string' },
			{ name: 'fermentables', type: 'array' },
			{ name: 'hops', type: 'string' },
			{ name: 'miscs', type: 'string' },
			{ name: 'yeasts', type: 'string' },
			{ name: 'waters', type: 'array' },
			{ name: 'mashs', type: 'string' }
		],
		id: 'record',
		url: url + '?record=' + my_record
	};
	// Load data and select one record.
	var dataAdapter = new $.jqx.dataAdapter(source, {
		loadComplete: function () {
			var records = dataAdapter.records;
			dataRecord = records[0];
			$("#name").val(dataRecord.name);
			$("#notes").val(dataRecord.notes);
			$("#st_name").val(dataRecord.st_name);
			$("#st_letter").val(dataRecord.st_letter);
			$("#st_guide").val(dataRecord.st_guide);
			$("#st_category").val(dataRecord.st_category);
			$("#st_category_number").val(dataRecord.st_category_number);
			$("#st_type").val(dataRecord.st_type);
			$("#type").val(dataRecord.type);
			$("#batch_size").val(dataRecord.batch_size);
			$("#boil_size").val(dataRecord.boil_size);
			$("#boil_time").val(dataRecord.boil_time);
			$("#efficiency").val(dataRecord.efficiency);
			$("#est_og").val(dataRecord.est_og);
			$("#est_og2").val(dataRecord.est_og);
			$("#st_og_min").val(dataRecord.st_og_min);
			$("#st_og_max").val(dataRecord.st_og_max);
			$("#est_fg").val(dataRecord.est_fg);
			$("#st_fg_min").val(dataRecord.st_fg_min);
			$("#st_fg_max").val(dataRecord.st_fg_max);
			$("#est_color").val(dataRecord.est_color);
			$("#est_color2").val(dataRecord.est_color);
			$("#est_abv").val(dataRecord.est_abv);
			$("#st_abv_min").val(dataRecord.st_abv_min);
			$("#st_abv_max").val(dataRecord.st_abv_max);
			$("#st_color_min").val(dataRecord.st_color_min);
			$("#st_color_max").val(dataRecord.st_color_max);
			$("#color_method").val(dataRecord.color_method);
			$("#est_ibu").val(dataRecord.est_ibu);
			$("#est_ibu2").val(dataRecord.est_ibu);
			$("#st_ibu_min").val(dataRecord.st_ibu_min);
			$("#st_ibu_max").val(dataRecord.st_ibu_max);
			$("#ibu_method").val(dataRecord.ibu_method);
			$("#est_carb").val(dataRecord.est_carb);
			$("#st_carb_min").val(dataRecord.st_carb_min);
			$("#st_carb_max").val(dataRecord.st_carb_max);
			$("#mash_name").val(dataRecord.mash_name);
			$("#mash_ph").val(dataRecord.mash_ph);
			$("#mash_sparge_temp").val(dataRecord.mash_sparge_temp);
			editFermentable(dataRecord);
			editHop(dataRecord);
			editMisc(dataRecord);
			editYeast(dataRecord);
			editWater(dataRecord);
			editMash(dataRecord);
			$('#jqxTabs').jqxTabs('next');
		},
		loadError: function (jqXHR, status, error) {
		},
		beforeLoadComplete: function (records) {
			$('#jqxLoader').jqxLoader('open');
		}
	});
	dataAdapter.dataBind();

	// Inline fermentables editor
	var editFermentable = function (data) {
		var fermentableSource = {
			localdata: data.fermentables,
			datatype: "local",
			datafields: [
				{ name: 'f_name', type: 'string' },
				{ name: 'f_origin', type: 'string' },
				{ name: 'f_supplier', type: 'string' },
				{ name: 'f_amount', type: 'float' },
				{ name: 'f_cost', type: 'float' },
				{ name: 'f_type', type: 'string' },
				{ name: 'f_yield', type: 'float' },
				{ name: 'f_color', type: 'float' },
				{ name: 'f_coarse_fine_diff', type: 'float' },
				{ name: 'f_moisture', type: 'float' },
				{ name: 'f_diastatic_power', type: 'float' },
				{ name: 'f_protein', type: 'float' },
				{ name: 'f_max_in_batch', type: 'float' },
				{ name: 'f_graintype', type: 'string' },
				{ name: 'f_added', type: 'string' },
				{ name: 'f_dissolved_protein', type: 'float' },
				{ name: 'f_recommend_mash', type: 'bool' },
				{ name: 'f_add_after_boil', type: 'bool' },
				{ name: 'f_adjust_to_total_100', type: 'bool' },
				{ name: 'f_percentage', type: 'float' },
				{ name: 'f_di_ph', type: 'float' }
			],
			addrow: function (rowid, rowdata, position, commit) {
				commit(true);
			},
			deleterow: function (rowid, commit) {
				commit(true);
			}
		};
		var fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);
		// dropdownlist datasource from inventory_fermentables
		var fermentableUrl = "getfermentablesources.php";
		var fermentableInvSource = {
			datatype: "json",
			datafields: [
				{ name: 'record', type: 'number' },
				{ name: 'name', type: 'string' },
				{ name: 'type', type: 'string' },
				{ name: 'yield', type: 'float' },
				{ name: 'color', type: 'float' },
				{ name: 'add_after_boil', type: 'bool' },
				{ name: 'origin', type: 'string' },
				{ name: 'supplier', type: 'string' },
				{ name: 'coarse_fine_diff', type: 'float' },
				{ name: 'moisture', type: 'float' },
				{ name: 'diastatic_power', type: 'float' },
				{ name: 'protein', type: 'float' },
				{ name: 'max_in_batch', type: 'float' },
				{ name: 'recommend_mash', type: 'bool' },
				{ name: 'graintype', type: 'string' },
				{ name: 'di_ph', type: 'float' },
				{ name: 'inventory', type: 'float' },
				{ name: 'cost', type: 'float' }
			],
			url: fermentableUrl,
			async: true
		};
		var fermentablelist = new $.jqx.dataAdapter(fermentableInvSource);

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

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

	// Inline hops editor
        var editHop = function (data) {
                var hopSource = {
                        localdata: data.hops,
                        datatype: "local",
			cache: false,
                        datafields: [
                                { name: 'h_name', type: 'string' },
                                { name: 'h_origin', type: 'string' },
                                { name: 'h_amount', type: 'float' },
                                { name: 'h_cost', type: 'float' },
				{ name: 'h_type', type: 'string' },
				{ name: 'h_form', type: 'string' },
				{ name: 'h_useat', type: 'string' },
                                { name: 'h_time', type: 'float' },
				{ name: 'h_alpha', type: 'float' },
				{ name: 'h_beta', type: 'float' },
				{ name: 'h_hsi', type: 'float' },
				{ name: 'h_humulene', type: 'float' },
				{ name: 'h_carophyllene', type: 'float' },
				{ name: 'h_cohumulone', type: 'float' },
				{ name: 'h_myrcene', type: 'float' },
				{ name: 'h_total_oil', type: 'float' },
				{ name: 'h_weight', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var hopAdapter = new $.jqx.dataAdapter(hopSource, {
			beforeLoadComplete: function (records) {
				var data = new Array();
				for (var i = 0; i < records.length; i++) {
					var row = records[i];
					row.h_weight = row.h_amount * 1000;
					data.push(row);
				}
			        return data;
			},
			loadError: function(jqXHR, status, error) {
				$('#err').text(status + ' ' + error);
			},
		});
                // dropdownlist datasource from inventory_hops
                var hopUrl = "gethopsources.php";
                var hopInvSource = {
                        datatype: "json",
                        datafields: [
                                { name: 'record', type: 'number' },
                                { name: 'name', type: 'string' },
				{ name: 'origin', type: 'string' },
                                { name: 'type', type: 'string' },
                                { name: 'alpha', type: 'float' },
                                { name: 'beta', type: 'float' },
				{ name: 'humulene', type: 'float' },
				{ name: 'caryophyllene', type: 'float' },
				{ name: 'cohumulone', type: 'float' },
				{ name: 'myrcene', type: 'float' },
				{ name: 'hsi', type: 'float' },
                                { name: 'useat', type: 'string' },
				{ name: 'form', type: 'string' },
				{ name: 'total_oil', type: 'float' },
				{ name: 'cost', type: 'float' }
                        ],
                        url: hopUrl,
                        async: true
                };
                var hoplist = new $.jqx.dataAdapter(hopInvSource);

                $("#hopGrid").jqxGrid({
                        width: 1150,
                        height: 400,
                        source: hopAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<div style="float: left; margin-left: 165px;" id="haddrowbutton"></div>');
                                container.append('<input style="float: left; margin-left: 230px;" id="hdeleterowbutton" type="button" value="Verwijder hop" />');
                                // add hop from dropdownlist.
                                $("#haddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies hop:",
                                        theme: theme,
                                        source: hoplist,
                                        displayMember: "name",
                                        width: 150,
                                        height: 27,
                                        dropDownWidth: 500,
					dropDownHeight: 500,
					renderer: function (index, label, value) {
						var datarecord = hoplist.records[index];
						return datarecord.origin+ " / " + datarecord.name + " (" + datarecord.alpha + "% &alpha;)";
					}
                                });
                                $("#haddrowbutton").on('select', function (event) {
                                        if (event.args) {
                                                var index = event.args.index;
                                                var datarecord = hoplist.records[index];
                                                var row = {};
                                                row["h_name"] = datarecord.name;
                                                row["h_origin"] = datarecord.origin;
                                                row["h_amount"] = 0;
                                                row["h_cost"] = datarecord.cost;
                                                row["h_type"] = datarecord.type;
						row["h_form"] = datarecord.form;
						row["h_useat"] = datarecord.useat;
						row["h_time"] = 0;
						row["h_alpha"] = datarecord.alpha;
						row["h_beta"] = datarecord.beta;
						row["h_hsi"] = datarecord.hsi;
						row["h_humulene"] = datarecord.humulene;
						row["h_carophyllene"] = datarecord.carophyllene;
						row["h_cohumulone"] = datarecord.cohumulone;
						row["h_myrcene"] = datarecord.myrcene;
						row["h_total_oil"] = datarecord.total_oil;
						row["h_weight"] = 0;
                                                var commit = $("#hopGrid").jqxGrid('addrow', null, row);
                                        }
                                });

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

	// Inline miscs editor
        var editMisc = function (data) {
                var miscSource = {
                        localdata: data.miscs,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'm_name', type: 'string' },
                                { name: 'm_amount', type: 'float' },
                                { name: 'm_cost', type: 'float' },
                                { name: 'm_type', type: 'string' },
                                { name: 'm_use_use', type: 'string' },
                                { name: 'm_time', type: 'float' },
                                { name: 'm_amount_is_weight', type: 'bool' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var miscAdapter = new $.jqx.dataAdapter(miscSource);
                // dropdownlist datasource from inventory_miscs
                var miscUrl = "getmiscsources.php";
                var miscInvSource = {
                        datatype: "json",
                        datafields: [
                                { name: 'record', type: 'number' },
                                { name: 'name', type: 'string' },
                                { name: 'type', type: 'string' },
                                { name: 'use_use', type: 'string' },
                                { name: 'amount_is_weight', type: 'bool' },
                                { name: 'time', type: 'float' },
                                { name: 'cost', type: 'float' }
                        ],
                        url: miscUrl,
                        async: true
                };
                var misclist = new $.jqx.dataAdapter(miscInvSource);

                $("#miscGrid").jqxGrid({
                        width: 960,
                        height: 400,
                        source: miscAdapter,
                        theme: theme,
                        selectionmode: 'singlerow',
                        editmode: 'selectedcell',
                        editable: true,
                        localization: getLocalization(),
                        showtoolbar: true,
                        rendertoolbar: function (toolbar) {
                                var me = this;
                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
                                toolbar.append(container);
                                container.append('<div style="float: left; margin-left: 165px;" id="maddrowbutton"></div>');
                                container.append('<input style="float: left; margin-left: 230px;" id="mdeleterowbutton" type="button" value="Verwijder ingredient" />');
                                // add misc from dropdownlist.
                                $("#maddrowbutton").jqxDropDownList({
                                        placeHolder: "Kies ingredient:",
                                        theme: theme,
                                        source: misclist,
                                        displayMember: "name",
                                        width: 150,
                                        height: 27,
                                        dropDownWidth: 300
                                });
                                $("#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;
                                                var commit = $("#miscGrid").jqxGrid('addrow', null, row);
                                        }
                                });
                                // delete selected misc.
                                $("#mdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
                                $("#mdeleterowbutton").on('click', function () {
                                        var selectedrowindex = $("#miscGrid").jqxGrid('getselectedrowindex');
                                        var rowscount = $("#miscGrid").jqxGrid('getdatainformation').rowscount;
                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
                                                var id = $("#miscGrid").jqxGrid('getrowid', selectedrowindex);
                                                var commit = $("#miscGrid").jqxGrid('deleterow', id);
                                        }
                                });
                        },
			ready: function() {
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
                                { text: 'Ingredient', editable: false, datafield: 'm_name' },
                                { text: 'Type', editable: false, width: 120, align: 'center', cellsalign: 'center', datafield: 'm_type' },
                                { text: 'Gebruik', width: 110, align: 'center', cellsalign: 'center', datafield: 'm_use_use', columntype: 'dropdownlist',
				  createeditor: function (row, column, editor) {
					var srcUseUse = [ "Mash", "Boil", "Primary", "Secondary", "Bottling" ];
					editor.jqxDropDownList({ autoDropDownHeight: true, source: srcUseUse });
				  }
			       	},
				{ datafield: 'm_amount_is_weight', hidden: true },	// We need to declare this column
				{ datafield: 'm_amount', hidden: true },		// We need to declare this column
                                { text: 'Hoeveelheid', datafield: 'grammen', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f2',
                                  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					var gram = rowdata.m_amount * 1000;
					var vstr = rowdata.m_amount_is_weight ? "gr":"ml";
					return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(gram,"f2")+" "+vstr+"</div>";
				  },
                                  validation: function (cell, value) {
					var high = parseFloat(dataRecord.boil_size) * 1000;
                                        if (value < 0 || value > high) {
                                                return { result: false, message: "Hoeveelheid moet tussen 0 en "+high+" zijn" };
                                        }
                                        return true;
                                  },
				  initeditor: function (row, cellvalue, editor) {
					editor.jqxNumberInput({ inputMode: 'simple', min: 0, decimalDigits: 2, spinButtons: false });
				  }
                                },
				{ datafield: 'm_time' },
                                { text: 'Tijd', datafield: 'time', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f0',
                                  columntype: 'numberinput',
				  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
					if (rowdata.m_use_use == 'Boil') {
						var tijd = rowdata.m_time;
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(tijd, "f0")+" m</div>";
					} else if (rowdata.m_use_use == 'Secondary') {
						var tijd = Math.round(rowdata.m_time / 1440);
						return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(tijd, "f0")+" d</div>";
					} else {
						var tijd = 0;
						return "<div style='margin: 4px;' class='jqx-right-align'> </div>";
					}
				  },
				  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
					editor.jqxNumberInput({ decimalDigits: 0, digits: 3, min: 0, max: parseFloat(dataRecord.boil_time) });
				  },
				  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
					var use = $("#miscGrid").jqxGrid('getcellvalue', row, "m_use_use");
					if (use != "Boil")
						return oldvalue;
				  },
                                  validation: function (cell, value) {
					var high = parseFloat(dataRecord.boil_time);
                                        if (value < 0 || value > high ) {
                                                return { result: false, message: "De tijd moet 0-"+high+" zijn" };
                                        }
                                        return true;
                                  }
                                }
                        ]
                });
		$("#miscGrid").on('cellendedit', function (event) {
			var args = event.args;
			console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
			if (args.datafield == 'grammen') {
				$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, 'm_amount', parseFloat(args.value / 1000));
			} else if (args.datafield == 'time') {
				var use_use = $("#miscGrid").jqxGrid('getcellvalue', args.rowindex, 'm_use_use');
				console.log("use: "+use_use);
				if (use_use == 'Secondary')
					$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, 'm_time', parseFloat(args.value * 1440));
				else
					$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, 'm_time', parseFloat(args.value));
			} else {
				$("#miscGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
			}
		});
        };

	// Inline yeasts editor
        var editYeast = function (data) {
                var yeastSource = {
                        localdata: data.yeasts,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'y_name', type: 'string' },
				{ name: 'y_laboratory', type: 'string' },
				{ name: 'y_product_id', type: 'string' },
                                { name: 'y_amount', type: 'float' },
                                { name: 'y_cost', type: 'float' },
                                { name: 'y_type', type: 'string' },
                                { name: 'y_form', type: 'string' },
                                { name: 'y_time', type: 'float' },
				{ name: 'y_min_temperature', type: 'float' },
				{ name: 'y_max_temperature', type: 'float' },
				{ name: 'y_attenuation', type: 'float' },
                                { name: 'y_amount_is_weight', type: 'bool' },
				{ name: 'y_use', type: 'string' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var yeastAdapter = new $.jqx.dataAdapter(yeastSource);
                // dropdownlist datasource from inventory_yeasts
                var yeastUrl = "getyeastsources.php";
                var yeastInvSource = {
                        datatype: "json",
                        datafields: [
                                { name: 'record', type: 'number' },
                                { name: 'name', type: 'string' },
                                { name: 'type', type: 'string' },
                                { name: 'form', type: 'string' },
				{ name: 'laboratory', type: 'string' },
				{ name: 'product_id', type: 'string' },
				{ name: 'min_temperature', type: 'float' },
				{ name: 'max_temperature', type: 'float' },
				{ name: 'attenuation', type: 'float' },
                                { name: 'cost', type: 'float' }
                        ],
                        url: yeastUrl,
                        async: true
                };
                var yeastlist = new $.jqx.dataAdapter(yeastInvSource);

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

	// Inline waters editor
	var editWater = function (data) {
		var waterSource = {
			localdata: data.waters,
			datatype: "local",
			datafields: [
				{ name: 'w_name', type: 'string' },
				{ name: 'w_amount', type: 'float' },
				{ name: 'w_calcium', type: 'float' },
				{ name: 'w_sulfate', type: 'float' },
				{ name: 'w_chloride', type: 'float' },
				{ name: 'w_sodium', type: 'float' },
				{ name: 'w_magnesium', type: 'float' },
				{ name: 'w_ph', type: 'float' },
				{ name: 'w_total_alkalinity', type: 'float' },
				{ name: 'w_cost', type: 'float' },
			],
			addrow: function (rowid, rowdata, position, commit) {
				commit(true);
			},
			deleterow: function (rowid, commit) {
				commit(true);
			}
		};
		var waterAdapter = new $.jqx.dataAdapter(waterSource);
		// dropdownlist datasource from inventory_waters
		var waterUrl = "getwatersources.php";
		var waterInvSource = {
			datatype: "json",
			datafields: [
				{ name: 'record', type: 'number' },
				{ name: 'name', type: 'string' },
				{ name: 'unlimited_stock', type: 'bool' },
				{ name: 'calcium', type: 'float' },
		                { name: 'sulfate', type: 'float' },
				{ name: 'chloride', type: 'float' },
				{ name: 'sodium', type: 'float' },
				{ name: 'magnesium', type: 'float' },
				{ name: 'ph', type: 'float' },
				{ name: 'total_alkalinity', type: 'float' },
				{ name: 'cost', type: 'float' },
			],
			url: waterUrl,
			async: true
		};
		var waterlist = new $.jqx.dataAdapter(waterInvSource);

		$("#waterGrid").jqxGrid({
			width: 960,
			height: 200,
			source: waterAdapter,
			theme: theme,
			selectionmode: 'singlerow',
			editmode: 'selectedcell',
			editable: true,
			localization: getLocalization(),
			showtoolbar: true,
			rendertoolbar: function (toolbar) {
				var me = this;
				var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
				toolbar.append(container);
				container.append('<div style="float: left; margin-left: 165px;" id="waddrowbutton"></div>');
				container.append('<input style="float: left; margin-left: 230px;" id="wdeleterowbutton" type="button" value="Verwijder water" />');
				// add water from dropdownlist.
				$("#waddrowbutton").jqxDropDownList({
					placeHolder: "Kies water:",
					theme: theme,
					source: waterlist,
					displayMember: "name",
					width: 150,
					height: 27,
					dropDownWidth: 300
				});
				$("#waddrowbutton").on('select', function (event) {
					if (event.args) {
						var index = event.args.index;
						var datarecord = waterlist.records[index];
						var row = {};
						row["w_name"] = datarecord.name;
						row["w_amount"] = 0;
						row["w_calcium"] = datarecord.calcium;
						row["w_sulfate"] = datarecord.sulfate;
						row["w_chloride"] = datarecord.chloride;
						row["w_sodium"] = datarecord.sodium;
						row["w_magnesium"] = datarecord.magnesium;
						row["w_ph"] = datarecord.ph;
						row["w_total_alkalinity"] = datarecord.total_alkalinity;
						row["w_cost"] = datarecord.cost;
						var commit = $("#waterGrid").jqxGrid('addrow', null, row);
					}
				});

				// delete selected water.
				// Overgebleven waters in volume verhogen met het verwijderde water.
				$("#wdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
				$("#wdeleterowbutton").on('click', function () {
					var selectedrowindex = $("#waterGrid").jqxGrid('getselectedrowindex');
					var rowscount = $("#waterGrid").jqxGrid('getdatainformation').rowscount;
					if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
						var id = $("#waterGrid").jqxGrid('getrowid', selectedrowindex);
						var commit = $("#waterGrid").jqxGrid('deleterow', id);
					}
				});
			},
			ready: function() {
				$('#jqxTabs').jqxTabs('next');
			},
			columns: [
				{ text: 'Water bron', editable: false, datafield: 'w_name' },
				{ text: 'Volume', datafield: 'w_amount', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1', 
				  columntype: 'numberinput',
				  validation: function (cell, value) {
					if (value < 0 || value > 100000000000 ) {
						return { result: false, message: "Volume moet  0-~ zijn" };
					}
					return true;
				  }
			       	},
				// TODO: validator test max is hoeveelheid maischwater. Dan water verdelen voor totaal.
				{ text: 'Ca', editable: false, datafield: 'w_calcium', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Mg', editable: false, datafield: 'w_magnesium', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Na', editable: false, datafield: 'w_sodium', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Tot Alk', editable: false, datafield: 'w_total_alkalinity', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'CaSO4', editable: false, datafield: 'w_sulfate', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'Cl', editable: false, datafield: 'w_chloride', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
				{ text: 'pH', editable: false, datafield: 'w_ph', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' }
			]
		});
	}; // editWater = function (data) {

	// inline mash editor
        var editMash = function (data) {
		var generaterow = function () {
			var row = {};
			row["step_name"] = "Stap 1";
			row["step_type"] = "Infusion";
			row["step_infuse_amount"] = 15;
			row["step_temp"] = 62.0;
			row['step_time'] = 20.0;
			row['ramp_time'] = 1.0;
			row['end_temp'] = 62.0;
			return row;
		}
                var mashSource = {
                        localdata: data.mashs,
                        datatype: "local",
                        cache: false,
                        datafields: [
                                { name: 'step_name', type: 'string' },
                                { name: 'step_type', type: 'string' },
				{ name: 'step_infuse_amount', type: 'float' },
				{ name: 'step_temp', type: 'float' },
				{ name: 'step_time', type: 'float' },
				{ name: 'ramp_time', type: 'float' },
				{ name: 'end_temp', type: 'float' }
                        ],
                        addrow: function (rowid, rowdata, position, commit) {
                                commit(true);
                        },
                        deleterow: function (rowid, commit) {
                                commit(true);
                        }
                };
                var mashAdapter = new $.jqx.dataAdapter(mashSource);
                // dropdownlist datasource from profile_mash
                var mashUrl = "include/db_profile_mash.php";
                var mashInvSource = {
                        datatype: "json",
                        datafields: [
                                { name: 'record', type: 'number' },
                                { name: 'name', type: 'string' },
                                { name: 'steps', type: 'array' }
                        ],
                        url: mashUrl,
                        async: true
                };
                var mashlist = new $.jqx.dataAdapter(mashInvSource);

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

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

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

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

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

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

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

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

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

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

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

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

	$("#Save").jqxButton({ template: "success", width: '90px', theme: theme });
	$("#Save").click(function () {
		var fermentablerow = $('#fermentableGrid').jqxGrid('getrows');
		var hoprow = $('#hopGrid').jqxGrid('getrows');
		var miscrow = $('#miscGrid').jqxGrid('getrows');
		var yeastrow = $('#yeastGrid').jqxGrid('getrows');
		var waterrow = $('#waterGrid').jqxGrid('getrows');
		var mashrow = $('#mashGrid').jqxGrid('getrows');
		var row = {
			record: my_record,
			name: $("#name").val(),
			notes: $("#notes").val(),
			st_name: $('#st_name').val(),
			st_letter: $('#st_letter').val(),
			st_guide: $('#st_guide').val(),
			st_type: $('#st_type').val(),
			st_category: $('#st_category').val(),
			st_category_number: parseFloat($("#st_category_number").jqxNumberInput('decimal')),
			st_og_min: parseFloat($("#st_og_min").jqxNumberInput('decimal')),
			st_og_max: parseFloat($("#st_og_max").jqxNumberInput('decimal')),
			st_fg_min: parseFloat($("#st_fg_min").jqxNumberInput('decimal')),
			st_fg_max: parseFloat($("#st_fg_max").jqxNumberInput('decimal')),
			st_ibu_min: parseFloat($("#st_ibu_min").jqxNumberInput('decimal')),
			st_ibu_max: parseFloat($("#st_ibu_max").jqxNumberInput('decimal')),
			st_color_min: parseFloat($("#st_color_min").jqxNumberInput('decimal')),
			st_color_max: parseFloat($("#st_color_max").jqxNumberInput('decimal')),
			st_carb_min: parseFloat($("#st_carb_min").jqxNumberInput('decimal')),
			st_carb_max: parseFloat($("#st_carb_max").jqxNumberInput('decimal')),
			st_abv_min: parseFloat($("#st_abv_min").jqxNumberInput('decimal')),
			st_abv_max: parseFloat($("#st_abv_max").jqxNumberInput('decimal')),
			type: $("#type").val(),
			batch_size: parseFloat($("#batch_size").jqxNumberInput('decimal')),
			boil_size: parseFloat($("#boil_size").jqxNumberInput('decimal')),
			boil_time: parseFloat($("#boil_time").jqxNumberInput('decimal')),
			efficiency: parseFloat($("#efficiency").jqxNumberInput('decimal')),
			est_og: parseFloat($("#est_og").jqxNumberInput('decimal')),
			est_fg: parseFloat($("#est_fg").jqxNumberInput('decimal')),
			est_abv: parseFloat($("#est_abv").jqxNumberInput('decimal')),
			est_color: parseFloat($("#est_color").jqxNumberInput('decimal')),
			color_method: $("#color_method").val(),
			est_ibu: parseFloat($("#est_ibu").jqxNumberInput('decimal')),
			ibu_method: $("#ibu_method").val(),
			est_carb: parseFloat($("#est_carb").jqxNumberInput('decimal')),
			mash_name: $("#mash_name").val(),
			mash_ph: parseFloat($("#mash_ph").jqxNumberInput('decimal')),
			mash_sparge_temp: parseFloat($("#mash_sparge_temp").jqxNumberInput('decimal')),
			fermentables: fermentablerow,
			hops: hoprow,
			miscs: miscrow,
			yeasts: yeastrow,
			waters: waterrow,
			mashs: mashrow
		};
		var data = "update=true&" + $.param(row);
		$.ajax({
			dataType: 'json',
			url: url,
			cache: false,
			data: data,
			type: "POST",
			success: function (data, status, xhr) {
				// update command is executed.
				window.location.href = my_return;
			},
			error: function(jqXHR, textStatus, errorThrown) {
			}
		});
	});
	createDelElements();
});

mercurial