www/js/prod_edit.js

Wed, 30 Jun 2021 23:09:31 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Wed, 30 Jun 2021 23:09:31 +0200
changeset 764
e95744b6b31d
parent 760
fce78f57a44d
child 769
5a2ec06e00d4
permissions
-rw-r--r--

Added HCO3 to the water summary. Changed some water chemicals limits to new insights.

/*****************************************************************************
 * Copyright (C) 2018-2021
 *
 * 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 createPopupElements() {

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

 $('#volumeWindow').jqxWindow({
  theme: theme,
  position: { x: 380, y: 210 },
  width: 500,
  height: 200,
  resizable: false,
  isModal: true,
  modalOpacity: 0.4,
  autoOpen: false,
  cancelButton: $('#volumeReady')
 });

 $('#pitchrateWindow').jqxWindow({
  theme: theme,
  position: { x: 330, y: 210 },
  width: 600,
  height: 200,
  resizable: false,
  isModal: true,
  modalOpacity: 0.4,
  autoOpen: false,
  cancelButton: $('#pitchrateReady')
 });

 $('#popupFermentable').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#FermentableReady'),
  modalOpacity: 0.40
 });

 $('#popupHop').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#HopReady'),
  modalOpacity: 0.40
 });

 $('#popupMisc').jqxWindow({
  width: 800,
  height: 275,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#MiscReady'),
  modalOpacity: 0.40
 });

 $('#popupYeast').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#YeastReady'),
  modalOpacity: 0.40
 });

 $('#popupMash').jqxWindow({
  width: 800,
  height: 400,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#MashReady'),
  modalOpacity: 0.40
 });
}



function drop_endis(flag, item, index) {
 (flag) ? $(item).jqxDropDownList('disableAt', index) : $(item).jqxDropDownList('enableAt', index);
}



function block_fermentable(stage, added) {
 if (stage > 5 && added < 4) // After fermentation and added before packaging
  return true;
 if (stage > 3 && added < 3) // After primary and added before sec/tert
  return true;
 if (stage > 2 && added < 2) // After boil and added during mash or boil
  return true;
 return false;
}



function minimum_fermentable(stage, added) {
 if (stage > 5 && added < 4)
  return 4;
 if (stage > 3 && added < 3)
  return 3;
 if (stage > 2 && added < 2)
  return 2;
 return added;
}



function minimum_hop(stage, useat)
{
 if (stage > 2 && useat < 5)
  return 5;
 return useat;
}



function block_hop(stage, useat)
{
 if (stage > 2 && useat < 5)
  return true;
 return false;
}



function block_misc(stage, use_use) {
 if (stage > 5 && use_use < 5)
  return true;
 if (stage > 3 && use_use < 4)
  return true;
 if (stage > 2 && use_use < 3)
  return true;
 if (stage > 1 && use_use < 1)
  return true;
 return false;
}



function minimum_misc(stage, use_use) {
 if (stage > 5 && use_use < 5)
  return 5;
 if (stage > 3 && use_use < 4)
  return 4;
 if (stage > 2 && use_use < 3)
  return 3;
 if (stage > 1 && use_use < 1)
  return 1;
 return use_use;
}



function block_yeast(stage, use) {
 if (stage > 3 && use < 1)
  return true;
 if (stage > 4 && use < 2)
  return true;
 if (stage > 5 && use < 3)
  return true;
 if (stage > 6 && use < 4)
  return true;
 return false;
}



function minimum_yeast(stage, use) {
 if (stage > 3 && use < 1)
  return 1;
 if (stage > 4 && use < 2)
  return 2;
 if (stage > 5 && use < 3)
  return 3;
 if (stage > 6 && use < 4)
  return 4;
 return use;
}



$(document).ready(function() {

 var i,
 to_100 = false,      // Fermentables adjust to 100%
 preboil_sg = 0,
 aboil_sg = 0,
 est_mash_sg = 0,
 psugar = 0,          // Percentage real sugars
 pcara = 0,           // Percentage cara/crystal malts
 svg = 77,            // Default attenuation
 mashkg = 0,          // Malt in mash weight
 initcells = 0,       // Initial yeast cell count

 ok_fermentables = 1, // Fermentables are in stock
 ok_hops = 1,         // Hops are in stock
 ok_miscs = 1,        // Miscs are in stock
 ok_yeasts = 1,       // Yeasts are in stock
 ok_waters = 1,       // Waters are in stock

 data_loaded = 0;
 error_count = 0;
 k_cm = 0;
 k_vol = 0;
 k_what = 0;

 hop_flavour = 0,
 hop_aroma = 0,
 mash_infuse = 0,
 last_acid = '',

 MMCa = 40.048,
 MMMg = 24.305,
 MMNa = 22.98976928,
 MMCl = 35.453,
 MMSO4 = 96.0626,
 MMHCO3 = 61.01684,
 MMCaSO4 = 172.171,
 MMCaCl2 = 147.015,
 MMCaCO3 = 100.087,
 MMMgCl2 = 95.211,	/* Since 27-06-2021 */
 MMMgSO4 = 246.475,
 MMNaHCO3 = 84.007,
 MMNa2CO3 = 105.996,
 MMNaCl = 58.443,
 MMCaOH2 = 74.06268,

 fermentableRow = 0,
 fermentableData = {},
 fermentableInit = 1,
 hopRow = 0,
 hopData = {},
 miscRow = 0,
 miscData = {},
 yeastRow = 0,
 yeastData = {},
 mashRow = 0,
 mashData = {},
 Ka1 = 0.0000004445,
 Ka2 = 0.0000000000468,
 dataRecord = {},
 url = 'includes/db_product.php',
 MaltVolume = 0.87,    // l/kg 0.688 volgens internetbronnen, gemeten 0.874 l/kg, na enige tijd maischen 0,715 l/kg (A3 Otten).
 SpecificHeatWater = 1.0,
 SpecificHeatMalt = 0.399, //cal/g.°C
 SlakingHeat = 10.318, //cal/g.°C

 // Prepare the data
 source = {
  datatype: 'json',
  cache: false,
  async: true,
  datafields: [
   // From prod_main
   { name: 'record', type: 'number' },
   { name: 'uuid', type: 'string' },
   { name: 'name', type: 'string' },
   { name: 'code', type: 'string' },
   { name: 'birth', type: 'string' },
   { name: 'stage', type: 'int' },
   { name: 'notes', type: 'string' },
   { name: 'log_brew', type: 'int' },
   { name: 'log_fermentation', type: 'int' },
   { name: 'log_ispindel', type: 'int' },
   { name: 'log_co2pressure', type: 'int' },
   { name: 'inventory_reduced', type: 'int' },
   { name: 'locked', type: 'int' },
   { name: 'eq_name', type: 'string' },
   { name: 'eq_boil_size', type: 'float' },
   { name: 'eq_batch_size', type: 'float' },
   { name: 'eq_tun_volume', type: 'float' },
   { name: 'eq_tun_weight', type: 'float' },
   { name: 'eq_tun_specific_heat', type: 'float' },
   { name: 'eq_tun_material', type: 'int' },
   { name: 'eq_tun_height', type: 'float' },
   { name: 'eq_top_up_water', type: 'float' },
   { name: 'eq_trub_chiller_loss', type: 'float' },
   { name: 'eq_evap_rate', type: 'float' },
   { name: 'eq_boil_time', type: 'float' },
   { name: 'eq_calc_boil_volume', type: 'int' },
   { name: 'eq_top_up_kettle', type: 'float' },
   { name: 'eq_hop_utilization', type: 'float' },
   { name: 'eq_notes', type: 'string' },
   { name: 'eq_lauter_volume', type: 'float' },
   { name: 'eq_lauter_height', type: 'float' },
   { name: 'eq_lauter_deadspace', type: 'float' },
   { name: 'eq_kettle_volume', type: 'float' },
   { name: 'eq_kettle_height', type: 'float' },
   { name: 'eq_mash_volume', type: 'float' },
   { name: 'eq_mash_max', type: 'float' },
   { name: 'eq_efficiency', type: 'float' },
   { name: 'brew_date_start', type: 'string' },
   { name: 'brew_mash_ph', type: 'float' },
   { name: 'brew_mash_sg', type: 'float' },
   { name: 'brew_mash_efficiency', type: 'float' },
   { name: 'brew_sparge_est', type: 'float' },
   { name: 'brew_sparge_ph', type: 'float' },
   { name: 'brew_preboil_volume', type: 'float' },
   { name: 'brew_preboil_sg', type: 'float' },
   { name: 'brew_preboil_ph', type: 'float' },
   { name: 'brew_preboil_efficiency', type: 'float' },
   { name: 'brew_aboil_volume', type: 'float' },
   { name: 'brew_aboil_sg', type: 'float' },
   { name: 'brew_aboil_ph', type: 'float' },
   { name: 'brew_aboil_efficiency', type: 'float' },
   { name: 'brew_cooling_method', type: 'int' },
   { name: 'brew_cooling_time', type: 'float' },
   { name: 'brew_cooling_to', type: 'float' },
   { name: 'brew_whirlpool9', type: 'float' },
   { name: 'brew_whirlpool7', type: 'float' },
   { name: 'brew_whirlpool6', type: 'float' },
   { name: 'brew_whirlpool2', type: 'float' },
   { name: 'brew_fermenter_volume', type: 'float' },
   { name: 'brew_fermenter_extrawater', type: 'float' },
   { name: 'brew_fermenter_tcloss', type: 'float' },
   { name: 'brew_aeration_time', type: 'float' },
   { name: 'brew_aeration_speed', type: 'float' },
   { name: 'brew_aeration_type', type: 'int' },
   { name: 'brew_fermenter_sg', type: 'float' },
   { name: 'brew_fermenter_ibu', type: 'float' },
   { name: 'brew_fermenter_color', type: 'float' },
   { name: 'brew_date_end', type: 'string' },
   { name: 'og', type: 'float' },
   { name: 'fg', type: 'float' },
   { name: 'primary_start_temp', type: 'float' },
   { name: 'primary_max_temp', type: 'float' },
   { name: 'primary_end_temp', type: 'float' },
   { name: 'primary_end_sg', type: 'float' },
   { name: 'primary_end_date', type: 'string' },
   { name: 'secondary_temp', type: 'float' },
   { name: 'secondary_end_sg', type: 'float' },
   { name: 'secondary_end_date', type: 'string' },
   { name: 'tertiary_temp', type: 'float' },
   { name: 'package_date', type: 'string' },
   { name: 'package_volume', type: 'float' },
   { name: 'package_infuse_amount', type: 'float' },
   { name: 'package_infuse_abv', type: 'float' },
   { name: 'package_infuse_notes', type: 'string' },
   { name: 'package_abv', type: 'float' },
   { name: 'package_ph', type: 'float' },
   { name: 'bottle_amount', type: 'float' },
   { name: 'bottle_carbonation', type: 'float' },
   { name: 'bottle_priming_water', type: 'float' },
   { name: 'bottle_priming_amount', type: 'float' },
   { name: 'bottle_carbonation_temp', type: 'float' },
   { name: 'keg_amount', type: 'float' },
   { name: 'keg_carbonation', type: 'float' },
   { name: 'keg_priming_water', type: 'float' },
   { name: 'keg_priming_amount', type: 'float' },
   { name: 'keg_carbonation_temp', type: 'float' },
   { name: 'keg_forced_carb', type: 'int' },
   { name: 'keg_pressure', type: 'float' },
   { name: 'taste_notes', type: 'string' },
   { name: 'taste_rate', type: 'float' },
   { name: 'taste_date', type: 'string' },
   { name: 'taste_color', type: 'string' },
   { name: 'taste_transparency', type: 'string' },
   { name: 'taste_head', type: 'string' },
   { name: 'taste_aroma', type: 'string' },
   { name: 'taste_taste', type: 'string' },
   { name: 'taste_mouthfeel', type: 'string' },
   { name: 'taste_aftertaste', type: 'string' },
   { name: 'st_name', type: 'string' },
   { name: 'st_letter', type: 'string' },
   { name: 'st_guide', type: 'string' },
   { name: 'st_category', type: 'string' },
   { name: 'st_category_number', type: 'int' },
   { name: 'st_type', type: 'int' },
   { 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: '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_og3', 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: 'starter_enable', type: 'int' },
   { name: 'starter_type', type: 'int' },
   { name: 'starter_sg', type: 'float' },
   { name: 'starter_viability', type: 'int' },
   { name: 'yeast_prod_date', type: 'string' },
   { name: 'yeast_pitchrate', type: 'float' },
   { name: 'prop1_type', type: 'int' },
   { name: 'prop1_volume', type: 'float' },
   { name: 'prop2_type', type: 'int' },
   { name: 'prop2_volume', type: 'float' },
   { name: 'prop3_type', type: 'int' },
   { name: 'prop3_volume', type: 'float' },
   { name: 'prop4_type', type: 'int' },
   { name: 'prop4_volume', type: 'float' },
   { name: 'divide_type', type: 'int' },
   { name: 'divide_size', type: 'float' },
   { name: 'divide_factor', type: 'float' },
   { name: 'divide_parts', type: 'int' },
   { name: 'divide_part', 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.
 dataAdapter = new $.jqx.dataAdapter(source, {
  loadComplete: function() {
   dataRecord = dataAdapter.records[0];
   // Hidden record uuid
   $('#name').val(dataRecord.name);
   $('#code').val(dataRecord.code);
   $('#birth').val(dataRecord.birth);
   $('#stage').val(StageData[dataRecord.stage].nl);
   $('#notes').val(dataRecord.notes);
   $('#locked').val(dataRecord.locked);
   $('#eq_name').val(dataRecord.eq_name);
   $('#eq_notes').val(dataRecord.eq_notes);
   $('#eq_boil_size').val(dataRecord.eq_boil_size);
   $('#eq_batch_size').val(dataRecord.eq_batch_size);
   $('#eq_tun_volume').val(dataRecord.eq_tun_volume);
   $('#eq_top_up_water').val(dataRecord.eq_top_up_water);
   $('#eq_trub_chiller_loss').val(dataRecord.eq_trub_chiller_loss);
   $('#eq_evap_rate').val(dataRecord.eq_evap_rate);
   $('#eq_boil_time').val(dataRecord.eq_boil_time);
   $('#eq_top_up_kettle').val(dataRecord.eq_top_up_kettle);
   $('#eq_hop_utilization').val(dataRecord.eq_hop_utilization);
   $('#eq_lauter_volume').val(dataRecord.eq_lauter_volume);
   $('#eq_lauter_deadspace').val(dataRecord.eq_lauter_deadspace);
   $('#eq_kettle_volume').val(dataRecord.eq_kettle_volume);
   $('#eq_mash_volume').val(dataRecord.eq_mash_volume);
   $('#eq_mash_max').val(dataRecord.eq_mash_max);
   $('#eq_efficiency').val(dataRecord.eq_efficiency);
   // Brewdate
   $('#brew_date_start').val(dataRecord.brew_date_start);
   $('#brew_mash_ph').val(dataRecord.brew_mash_ph);
   $('#brew_mash_sg').val(dataRecord.brew_mash_sg);
   $('#brew_mash_efficiency').val(dataRecord.brew_mash_efficiency);
   // Header Spoelen en filteren
   $('#brew_sparge_temperature').val(dataRecord.sparge_temp);
   $('#brew_sparge_volume').val(dataRecord.sparge_volume);
   $('#brew_sparge_est').val(dataRecord.brew_sparge_est);
   $('#brew_sparge_ph').val(dataRecord.brew_sparge_ph);
   // Header Beluchten
   $('#brew_aeration_type').val(dataRecord.brew_aeration_type);
   $('#brew_aeration_time').val(dataRecord.brew_aeration_time);
   $('#brew_aeration_speed').val(dataRecord.brew_aeration_speed);

   $('#brew_preboil_ph').val(dataRecord.brew_preboil_ph);
   $('#brew_preboil_sg').val(dataRecord.brew_preboil_sg);
   $('#brew_preboil_volume').val(dataRecord.brew_preboil_volume);
   $('#brew_preboil_efficiency').val(dataRecord.brew_preboil_efficiency);
   // Header Koelen en whirlpoolen
   $('#brew_whirlpool9').val(dataRecord.brew_whirlpool9);
   $('#brew_whirlpool7').val(dataRecord.brew_whirlpool7);
   $('#brew_whirlpool6').val(dataRecord.brew_whirlpool6);
   $('#brew_whirlpool2').val(dataRecord.brew_whirlpool2);
   // Header Naar gistvat
   $('#brew_fermenter_volume').val(dataRecord.brew_fermenter_volume);
   $('#brew_fermenter_sg').val(dataRecord.brew_fermenter_sg);
   $('#brew_fermenter_sg2').val(dataRecord.brew_fermenter_sg);
   $('#brew_fermenter_ibu').val(dataRecord.brew_fermenter_ibu);
   $('#brew_fermenter_color').val(dataRecord.brew_fermenter_color);
   $('#brew_fermenter_extrawater').val(dataRecord.brew_fermenter_extrawater);
   $('#brew_fermenter_tcloss').val(dataRecord.brew_fermenter_tcloss);

   $('#brew_aboil_ph').val(dataRecord.brew_aboil_ph);
   $('#brew_aboil_sg').val(dataRecord.brew_aboil_sg);
   $('#brew_aboil_volume').val(dataRecord.brew_aboil_volume);
   $('#brew_aboil_efficiency').val(dataRecord.brew_aboil_efficiency);
   // Header Koelen en whirlpoolen
   $('#brew_cooling_to').val(dataRecord.brew_cooling_to);
   $('#brew_cooling_method').val(dataRecord.brew_cooling_method);
   $('#brew_cooling_time').val(dataRecord.brew_cooling_time);
   // Niks
   $('#brew_date_end').val(dataRecord.brew_date_end);
   $('#og').val(dataRecord.og);
   $('#fg').val(dataRecord.fg);
   $('#primary_start_temp').val(dataRecord.primary_start_temp);
   $('#primary_max_temp').val(dataRecord.primary_max_temp);
   $('#primary_end_temp').val(dataRecord.primary_end_temp);
   $('#primary_end_sg').val(dataRecord.primary_end_sg);
   $('#primary_end_date').val(dataRecord.primary_end_date);
   $('#secondary_temp').val(dataRecord.secondary_temp);
   $('#secondary_end_sg').val(dataRecord.secondary_end_sg);
   $('#secondary_end_date').val(dataRecord.secondary_end_date);
   $('#tertiary_temp').val(dataRecord.tertiary_temp);
   $('#package_date').val(dataRecord.package_date);
   $('#package_volume').val(dataRecord.package_volume);
   $('#package_infuse_amount').val(dataRecord.package_infuse_amount);
   $('#package_infuse_abv').val(dataRecord.package_infuse_abv);
   $('#package_infuse_notes').val(dataRecord.package_infuse_notes);
   $('#package_abv').val(dataRecord.package_abv);
   $('#package_ph').val(dataRecord.package_ph);
   $('#bottle_amount').val(dataRecord.bottle_amount);
   $('#bottle_carbonation').val(dataRecord.bottle_carbonation);
   $('#bottle_priming_water').val(dataRecord.bottle_priming_water);
   $('#bottle_priming_amount').val(dataRecord.bottle_priming_amount);
   $('#bottle_carbonation_temp').val(dataRecord.bottle_carbonation_temp);
   $('#keg_amount').val(dataRecord.keg_amount);
   $('#keg_carbonation').val(dataRecord.keg_carbonation);
   $('#keg_priming_water').val(dataRecord.keg_priming_water);
   $('#keg_priming_amount').val(dataRecord.keg_priming_amount);
   $('#keg_carbonation_temp').val(dataRecord.keg_carbonation_temp);
   $('#keg_forced_carb').val(dataRecord.keg_forced_carb);
   $('#keg_pressure').val(dataRecord.keg_pressure);
   $('#taste_notes').val(dataRecord.taste_notes);
   $('#taste_rate').val(dataRecord.taste_rate);
   $('#taste_date').val(dataRecord.taste_date);
   $('#taste_color').val(dataRecord.taste_color);
   $('#taste_transparency').val(dataRecord.taste_transparency);
   $('#taste_head').val(dataRecord.taste_head);
   $('#taste_aroma').val(dataRecord.taste_aroma);
   $('#taste_taste').val(dataRecord.taste_taste);
   $('#taste_mouthfeel').val(dataRecord.taste_mouthfeel);
   $('#taste_aftertaste').val(dataRecord.taste_aftertaste);

   // Recipe
   $('#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);
   $('#st_og_min').val(dataRecord.st_og_min);
   $('#st_og_max').val(dataRecord.st_og_max);
   $('#st_fg_min').val(dataRecord.st_fg_min);
   $('#st_fg_max').val(dataRecord.st_fg_max);
   $('#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);
   $('#st_ibu_min').val(dataRecord.st_ibu_min);
   $('#st_ibu_max').val(dataRecord.st_ibu_max);
   $('#st_carb_min').val(dataRecord.st_carb_min);
   $('#st_carb_min2').val(dataRecord.st_carb_min);
   $('#st_carb_max').val(dataRecord.st_carb_max);
   $('#st_carb_max2').val(dataRecord.st_carb_max);
   $('#type').val(dataRecord.type);
   $('#batch_size').val(dataRecord.batch_size);
   $('#est_a_vol').val(dataRecord.batch_size * 1.04);
   $('#boil_size').val(dataRecord.boil_size);
   $('#est_pre_vol').val(dataRecord.boil_size * 1.04);
   $('#boil_time').val(dataRecord.boil_time);
   $('#efficiency').val(dataRecord.efficiency);
   $('#est_og').val(dataRecord.est_og);
   $('#est_og2').val(dataRecord.est_og);
   $('#est_og3').val(dataRecord.est_og3);
   $('#est_fg').val(dataRecord.est_fg);
   $('#est_fg2').val(dataRecord.est_fg);
   $('#est_fg3').val(dataRecord.est_fg);
   $('#est_color').val(dataRecord.est_color);
   $('#est_color2').val(dataRecord.est_color);
   $('#est_abv').val(dataRecord.est_abv);
   $('#color_method').val(dataRecord.color_method);
   $('#est_ibu').val(dataRecord.est_ibu);
   $('#est_ibu2').val(dataRecord.est_ibu);
   $('#ibu_method').val(dataRecord.ibu_method);
   $('#est_carb').val(dataRecord.est_carb);
   $('#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_bicarbonate').val(dataRecord.w1_total_alkalinity * 1.22);
   $('#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_bicarbonate').val(dataRecord.w2_total_alkalinity * 1.22);
   $('#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);
   $('#starter_type').val(dataRecord.starter_type);
   $('#starter_sg').val(dataRecord.starter_sg);
   $('#starter_viability').val(dataRecord.starter_viability);
   $('#yeast_prod_date').val(dataRecord.yeast_prod_date);
   $('#yeast_pitchrate').val(dataRecord.yeast_pitchrate);
   $('#prop1_type').val(dataRecord.prop1_type);
   $('#prop1_volume').val(dataRecord.prop1_volume);
   $('#prop2_type').val(dataRecord.prop2_type);
   $('#prop2_volume').val(dataRecord.prop2_volume);
   $('#prop3_type').val(dataRecord.prop3_type);
   $('#prop3_volume').val(dataRecord.prop3_volume);
   $('#prop4_type').val(dataRecord.prop4_type);
   $('#prop4_volume').val(dataRecord.prop4_volume);
   $('#divide_type').val(SplitData[dataRecord.divide_type].nl);
   if (dataRecord.divide_type > 0)
    $('#divide_batch').val((dataRecord.divide_part + 1) + ' van ' + (dataRecord.divide_parts + 1));
   else
    $('#divide_batch').val('n.v.t.');
   // hidden divide_size
   // hidden divide_factor
   // hidden divide_parts
   // hidden divide_part
   editFermentable(dataRecord);
   editHop(dataRecord);
   editMisc(dataRecord);
   editYeast(dataRecord);
   editMash(dataRecord);
   calcStage();
   $('#jqxTabs').jqxTabs('select', 2);
   data_loaded = 1;
  },
  loadError: function(jqXHR, status, error) {
   console.log('main data load error: ' + status + ' ' + error);
  }
 });

 // Inline fermentables editor
 var editFermentable = function(data) {
  var fermentableSource = {
   localdata: data.fermentables,
   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) { commit(true); },
   deleterow: function(rowid, commit) { commit(true); },
   updaterow: function(rowid, rowdata, commit) { commit(true); }
  },
  fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);

  $('#fermentableGrid').jqxGrid({
   width: 1240,
   height: 470,
   source: fermentableAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    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',
     disabled: (dataRecord.stage > 6),
     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) (' + Round(datarecord.inventory, 3) + ' kg)';
     }
    });
    $('#faddrowbutton').on('select', function(event) {
     if (event.args) {
      var index, datarecord, row = {}, rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
      index = event.args.index;
      datarecord = fermentablelist.records[index];
      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
      }
      /* If stage > added moment, adjust the moment */
      row['f_added'] = minimum_fermentable(dataRecord.stage, row['f_added']);
      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;
      $('#fermentableGrid').jqxGrid('addrow', null, row);
     }
    });

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

    // delete selected fermentable.
    $('#fdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 6) });
    $('#fdeleterowbutton').on('click', function() {
     var rowscount, id, percent, amount, i, rowdata,
     selectedrowindex = $('#fermentableGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      id = $('#fermentableGrid').jqxGrid('getrowid', selectedrowindex);
      added = $('#fermentableGrid').jqxGrid('getcellvalue', selectedrowindex, 'f_added');
      if (block_fermentable(dataRecord.stage, added)) {
       percent = mount = 0;
       alert('Ingredieënt is al verwerkt.');
      } else {
       percent = $('#fermentableGrid').jqxGrid('getcellvalue', id, 'f_percentage');
       amount = $('#fermentableGrid').jqxGrid('getcellvalue', id, 'f_amount');
       $('#fermentableGrid').jqxGrid('deleterow', id);
      }
     }
     rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
     if (rowscount > 1) {
      if (to_100) {
       for (i = 0; i < rowscount; i++) {
        rowdata = $('#fermentableGrid').jqxGrid('getrowdata', i);
        if (rowdata.f_adjust_to_total_100) {
         rowdata.f_percentage += percent;
         rowdata.f_amount += amount;
        }
       }
      } else {
       calcPercentages();
      }
     } else {
      $('#fermentableGrid').jqxGrid('setcellvalue', 0, 'f_percentage', 100);
     }
     calcFermentables();
     calcIBUs();
    });
   },
   ready: function() { $('#jqxTabs').jqxTabs('next'); },
   columns: [
    { text: 'Vergistbaar ingredi&euml;nt', datafield: 'f_name',
     cellsrenderer: function(index, datafield, value, defaultvalue, 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', 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: 'Voorraad Kg', datafield: 'f_inventory', width: 120, align: 'right',
     cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
      var color = (value < rowdata.f_amount) ? '#ff4040':'#ffffff';
      if (block_fermentable(dataRecord.inventory_reduced, rowdata.f_added) == false) {
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + fermentableAdapter.formatNumber(value, 'f3') + '</span>';
      } else {
       return '<span></span>';
      }
     }
    },
    { text: 'Procent', datafield: 'f_percentage', width: 90, align: 'right',
     cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
      if (rowdata.f_added >= 4)
       return '<span></span>';
      var color = (value > rowdata.f_max_in_batch) ? '#ff4040':'#ffffff';
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + fermentableAdapter.formatNumber(value, 'p1') + '</span>';
     }
    },
    { text: '100%', datafield: 'f_adjust_to_total_100', width: 70, align: 'center', cellsalign: 'center',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      if (value == 0)
       return '<span></span>';
      return '<span><img style="float:left; margin-left:25px; margin-top:4px;" src="images/dialog-ok-apply.png"></span>';
     }
    },
    { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      fermentableRow = row;
      fermentableData = $('#fermentableGrid').jqxGrid('getrowdata', fermentableRow);
      if (fermentableData.f_added >= 4) {
       alert('Wijzig dit in de Verpakken tab');
      } else if (block_fermentable(dataRecord.stage, fermentableData.f_added)) {
       alert('Ingredieënt is al verwerkt.');
      } else {
       $('#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);
       drop_endis(dataRecord.stage >= 3, '#wf_added', 0);
       drop_endis(dataRecord.stage >= 3, '#wf_added', 1);
       drop_endis(dataRecord.stage >= 4, '#wf_added', 2);
       $('#wf_added').jqxDropDownList('disableAt', 4);
       $('#wf_added').jqxDropDownList('disableAt', 5);
       // show the popup window.
       $('#popupFermentable').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 // Inline hops editor
 var editHop = function(data) {
  var hopSource = {
   localdata: data.hops,
   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: 'int' },
    { 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) { commit(true); },
   deleterow: function(rowid, commit) { commit(true); },
   updaterow: function(rowid, rowdata, commit) { commit(true); }
  },
  hopAdapter = new $.jqx.dataAdapter(hopSource);

  $('#hopGrid').jqxGrid({
   width: 1240,
   height: 560,
   source: hopAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    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,
     disabled: (dataRecord.stage > 5),
     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 + ' / ' + HopFormData[datarecord.form].nl + ' (' + datarecord.alpha + '% &alpha;) (' +
       Round(datarecord.inventory * 1000.0, 1) + ' gr)';
     }
    });
    $('#haddrowbutton').on('select', function(event) {
     if (event.args) {
      var datarecord, row = {}, index = event.args.index;
      datarecord = hoplist.records[index];
      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;
      /* If stage > useat moment, adjust the moment */
      row['h_useat'] = minimum_hop(dataRecord.stage, row['h_useat']);
      $('#hopGrid').jqxGrid('addrow', null, row);
     }
     $('#haddrowbutton').jqxDropDownList('clearSelection');
    });

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

    // delete selected hop.
    $('#hdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 5) });
    $('#hdeleterowbutton').on('click', function() {
     var rowscount, id, selectedrowindex = $('#hopGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#hopGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      useat = $('#hopGrid').jqxGrid('getcellvalue', selectedrowindex, 'h_useat');
      if (block_hop(dataRecord.stage, useat)) {
       alert('Ingredieënt is al verwerkt.');
      } else {
       id = $('#hopGrid').jqxGrid('getrowid', selectedrowindex);
       $('#hopGrid').jqxGrid('deleterow', id);
      }
     }
     calcIBUs();
    });
   },
   ready: function() { $('#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(),
                dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6);
      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) {
      if (block_hop(dataRecord.inventory_reduced, rowdata.h_useat) == false) {
       var amount = dataAdapter.formatNumber(value, 'f1') + ' kg',
       color = (value < rowdata.h_amount) ? '#ff4040':'#ffffff';
       if (value < 1)
        amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr';
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
      } else {
       return '<span></span>';
      }
     }
    },
    { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      hopRow = row;
      hopData = $('#hopGrid').jqxGrid('getrowdata', hopRow);
      if (block_hop(dataRecord.stage, hopData.h_useat)) {
       alert('Ingredieënt is al verwerkt.');
      } else {
       console.log('edit button row ' + row);
       $('#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(),
                 dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6);
       $('#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);
       for (i = 0; i < 5; i++) {
	drop_endis(dataRecord.stage > 2, '#wh_useat', i);
       }
       // show the popup window.
       $('#popupHop').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 // Inline miscs editor
 var editMisc = function(data) {
  var miscSource = {
   localdata: data.miscs,
   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) { commit(true); },
   deleterow: function(rowid, commit) { commit(true); },
   updaterow: function(rowid, rowdata, commit) { commit(true); }
  },
  miscAdapter = new $.jqx.dataAdapter(miscSource, {
   beforeLoadComplete: function(records) {
    var row, i, data = new Array();
    for (i = 0; i < records.length; i++) {
     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 'MgCl2':
       $('#wa_mgcl2').val(row.m_amount * 1000);
       break;
      case 'NaHCO3':
       $('#wa_nahco3').val(row.m_amount * 1000);
       break;
      case 'CaCO3':
       $('#wa_caco3').val(row.m_amount * 1000);
       break;
      case 'Melkzuur':
       $('#wa_acid_name').val(0);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc);	// TODO: this ignores changed percentages.
       last_acid = 'Melkzuur';
       break;
      case 'Zoutzuur':
       $('#wa_acid_name').val(1);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc);
       last_acid = 'Zoutzuur';
       break;
      case 'Fosforzuur':
       $('#wa_acid_name').val(2);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc);
       last_acid = 'Fosforzuur';
       break;
      case 'Zwavelzuur':
       $('#wa_acid_name').val(3);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc);
       last_acid = 'Zwavelzuur';
       break;
     }
    }
    return data;
   },
   loadError: function(jqXHR, status, error) { console.log('miscs load error ' + status + ' ' + error); },
  });
  $('#miscGrid').jqxGrid({
   width: 1240,
   height: 575,
   source: miscAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    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 ingredient" />');
    // add misc from dropdownlist.
    $('#maddrowbutton').jqxDropDownList({
     placeHolder: 'Kies ingredi&euml;nt:',
     theme: theme,
     template: 'primary',
     source: misclist,
     disabled: (dataRecord.stage > 6),
     displayMember: 'name',
     width: 150,
     height: 27,
     dropDownWidth: 500,
     dropDownHeight: 500
    });
    $('#maddrowbutton').on('select', function(event) {
     if (event.args) {
      var datarecord, row = {}, index = event.args.index;
      datarecord = misclist.records[index];
      row['m_name'] = datarecord.name;
      row['m_amount'] = 0;
      row['m_cost'] = datarecord.cost;
      row['m_type'] = datarecord.type;
      row['m_use_use'] = minimum_misc(dataRecord.stage, datarecord.use_use);
      row['m_time'] = 0;
      row['m_amount_is_weight'] = datarecord.amount_is_weight;
      row['m_inventory'] = datarecord.inventory;
      $('#miscGrid').jqxGrid('addrow', null, row);
     }
    });
    $('#minstockbutton').jqxCheckBox({ theme: theme, height: 27, disabled: (dataRecord.stage > 6) });
    $('#minstockbutton').on('change', function(event) {
     miscinstock = event.args.checked;
     misclist.dataBind();
    });
    // delete selected misc.
    $('#mdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 6) });
    $('#mdeleterowbutton').on('click', function() {
     var rowscount, type, id, selectedrowindex = $('#miscGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#miscGrid').jqxGrid('getdatainformation').rowscount;
     type = $('#miscGrid').jqxGrid('getcellvalue', selectedrowindex, 'm_type');
     use_use = $('#miscGrid').jqxGrid('getcellvalue', selectedrowindex, 'm_use_use');
     if (selectedrowindex >= 0 && selectedrowindex < rowscount && type == 4) {
      alert('Brouwzouten verwijderen in de water tab.');
     } else if (block_misc(dataRecord.stage, use_use)) {
      alert('Ingredieënt is al verwerkt.');
     } else {
      id = $('#miscGrid').jqxGrid('getrowid', selectedrowindex);
      $('#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) {
      if (block_misc(dataRecord.inventory_reduced, rowdata.m_use_use) == false) {
       var vstr = rowdata.m_amount_is_weight ? 'gr' : 'ml',
       color = (value < rowdata.m_amount) ? '#ff4040':'#ffffff',
       amount = dataAdapter.formatNumber(value * 1000, 'f2') + ' ' + vstr;
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
      } else {
       return '<span></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 (block_misc(dataRecord.stage, miscData.m_use_use)) {
       alert('Ingredieënt is al verwerkt.');
      } else if (miscData.m_type == 4) {
       alert('Brouwzouten wijzigen in de water tab.');
      } else {
       console.log('edit button row ' + row);
       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);
       drop_endis(dataRecord.stage >= 2, '#wm_use_use', 0);
       drop_endis(dataRecord.stage >= 3, '#wm_use_use', 1);
       drop_endis(dataRecord.stage >= 3, '#wm_use_use', 2);
       drop_endis(dataRecord.stage >= 4, '#wm_use_use', 3);
       drop_endis(dataRecord.stage >= 5, '#wm_use_use', 4);
       // show the popup window.
       $('#popupMisc').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 // Inline yeasts editor
 var editYeast = function(data) {
  var yeastSource = {
   localdata: data.yeasts,
   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_sta1', type: 'int' },
    { name: 'y_bacteria', type: 'int' },
    { name: 'y_harvest_top', type: 'int' },
    { name: 'y_harvest_time', type: 'int' },
    { name: 'y_pitch_temperature', type: 'float' },
    { name: 'y_pofpos', type: 'int' },
    { name: 'y_zymocide', type: 'int' },
    { name: 'y_gr_hl_lo', type: 'int' },
    { name: 'y_sg_lo', type: 'float' },
    { name: 'y_gr_hl_hi', type: 'int' },
    { name: 'y_sg_hi', type: 'float' },
    { name: 'y_avail', type: 'int' }
   ],
   addrow: function(rowid, rowdata, position, commit) { commit(true); },
   deleterow: function(rowid, commit) { commit(true); },
   updaterow: function(rowid, rowdata, commit) { commit(true); }
  },
  yeastAdapter = new $.jqx.dataAdapter(yeastSource);

  $('#yeastGrid').jqxGrid({
   width: 1240,
   height: 325,
   source: yeastAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    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,
     source: yeastlist,
     disabled: (dataRecord.stage > 6),
     template: 'primary',
     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 datarecord, row = {}, index = event.args.index;
      datarecord = yeastlist.records[index];
      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'] = minimum_yeast(dataRecord.stage, 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;
      row['y_sta1'] = datarecord.sta1;
      row['y_bacteria'] = datarecord.bacteria;
      row['y_harvest_top'] = datarecord.harvest_top;
      row['y_harvest_time'] = datarecord.harvest_time;
      row['y_pitch_temperature'] = datarecord.pitch_temperature;
      row['y_pofpos'] = datarecord.pofpos;
      row['y_zymocide'] = datarecord.zymocide;
      row['y_gr_hl_lo'] = datarecord.gr_hl_lo;
      row['y_sg_lo'] = datarecord.sg_lo;
      row['y_gr_hl_hi'] = datarecord.gr_hl_hi;
      row['y_sg_hi'] = datarecord.sg_hi;
      $('#yeastGrid').jqxGrid('addrow', null, row);
     }
     calcViability();
     calcYeast();
     $('#yaddrowbutton').jqxDropDownList('clearSelection');
    });
    $('#yinstockbutton').jqxCheckBox({ theme: theme, height: 27, disabled: (dataRecord.stage > 6) });
    $('#yinstockbutton').on('change', function(event) {
     yeastinstock = event.args.checked;
     yeastlist.dataBind();
    });
    // delete selected yeast.
    $('#ydeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 6) });
    $('#ydeleterowbutton').on('click', function() {
     var id, rowscount, selectedrowindex = $('#yeastGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#yeastGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      use = $('#yeastGrid').jqxGrid('getcellvalue', selectedrowindex, 'y_use');
      if (block_yeast(dataRecord.stage, use)) {
       alert('Ingredieënt is al verwerkt.');
      } else {
       id = $('#yeastGrid').jqxGrid('getrowid', selectedrowindex);
       $('#yeastGrid').jqxGrid('deleterow', id);
       calcViability();
       calcYeast();
      }
     }
    });
   },
   ready: function() { $('#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 amount = '', color = (dataRecord.est_abv > value) ? '#ff4040':'#ffffff';
      if (value > 0) {
       amount = dataAdapter.formatNumber(value, 'f1');
      }
      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 || rowdata.y_form == 6) // 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, amount;
      if (block_yeast(dataRecord.inventory_reduced, rowdata.y_use) == false) {
       color = '#ffffff';
       if (value < rowdata.y_amount)
        color = '#ff4040';
       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 || rowdata.y_form == 6)   // Dry
        amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr';
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
      } else {
       return '<span></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 (block_yeast(dataRecord.stage, yeastData.y_use)) {
       alert('Ingredieënt is al verwerkt.');
      } else {
       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 || yeastData.y_form == 6) {
        $('#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);
       drop_endis(dataRecord.stage > 3, '#wy_use', 0);
       drop_endis(dataRecord.stage > 4, '#wy_use', 1);
       drop_endis(dataRecord.stage > 5, '#wy_use', 2);
       // show the popup window.
       $('#popupYeast').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 // inline mash editor
 var editMash = function(data) {
  var mashSource = {
   localdata: data.mashs,
   datafields: [
    { name: 'step_name', type: 'string' },
    { name: 'step_type', type: 'int' },
    { name: 'step_volume', type: 'float' },
    { name: 'step_infuse_amount', type: 'float' },
    { name: 'step_infuse_temp', type: 'float' },
    { name: 'step_temp', type: 'float' },
    { name: 'step_time', type: 'float' },
    { name: 'step_wg_ratio', type: 'float' },
    { name: 'ramp_time', type: 'float' },
    { name: 'end_temp', type: 'float' },
    { name: 'step_ph', type: 'float' },
    { name: 'step_sg', type: 'float' }
   ],
   addrow: function(rowid, rowdata, position, commit) { commit(true); },
   deleterow: function(rowid, commit) { commit(true); }
  },
  mashAdapter = new $.jqx.dataAdapter(mashSource, {
   beforeLoadComplete: function(records) {
    mash_infuse = 0;
    var i, row, data = new Array();
    for (i = 0; i < records.length; i++) {
     row = records[i];
     if (row.step_type == 0) // Infusion
      mash_infuse += parseFloat(row.step_infuse_amount);
     row.step_wg_ratio = 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 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, disabled: (dataRecord.stage > 3) });
    $('#saddrowbutton').on('click', function() {
     var row = {}, rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
     row['step_name'] = 'Stap ' + (rowscount + 1);
     if (rowscount > 0) {
      row['step_type'] = 1;
      row['step_infuse_amount'] = 0.0;
      row['step_volume'] = mash_infuse;
     } else {
      row['step_type'] = 0;
      row['step_infuse_amount'] = 15.0;
      row['step_volume'] = 15;
     }
     row['step_infuse_temp'] = 0;
     row['step_temp'] = 62.0;
     row['step_time'] = 20.0;
     row['step_wg_ratio'] = 0.0;
     row['ramp_time'] = 1.0;
     row['end_temp'] = 62.0;
     row['step_ph'] = 0.0;
     row['step_sg'] = 0.0;
     $('#mashGrid').jqxGrid('addrow', null, row);
     calcMash();
    });
    // delete selected step.
    $('#sdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150, disabled: (dataRecord.stage > 3) });
    $('#sdeleterowbutton').on('click', function() {
     var rowscount, id, selectedrowindex = $('#mashGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      id = $('#mashGrid').jqxGrid('getrowid', selectedrowindex);
      $('#mashGrid').jqxGrid('deleterow', id);
      calcMash();
     }
    });
   },
   ready: function() {
    /* Calculate the whole recipe */
    console.log('ready mashs, start calculations');
    calcFermentables();
    whirlpoolHops();
    calcIBUs();
    calcMiscs();
    calcViability();
    showStarter();
    calcYeast();
    calcInit();
    calcMash();
    kookTijd();
    console.log('calculations ready');
    $('#jqxLoader').jqxLoader('close');
    $('#jqxTabs').jqxTabs('first');
   },
   columns: [
    { text: 'Stap naam', datafield: 'step_name' },
    { text: 'Stap type', datafield: 'step_type', width: 150,
      cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
       return '<span style="margin: 4px; margin-top: 6px; float: left;">' + MashStepTypeData[value].nl + '</span>';
      }
    },
    { text: 'Start &deg;C', datafield: 'step_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
    { text: 'Eind &deg;C', datafield: 'end_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
    { text: 'Rust min.', datafield: 'step_time', width: 80, align: 'right', cellsalign: 'right' },
    { text: 'Stap min.', datafield: 'ramp_time', width: 80, align: 'right', cellsalign: 'right' },
    { text: 'Inf/dec L.', datafield: 'step_infuse_amount', width: 80, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       if (rowdata.step_type == 1)
        return '<span></span>';
       var color = '#ffffff';
       var mvol = mashkg * MaltVolume;
       if ((rowdata.step_wg_ratio * mashkg + mvol) > dataRecord.eq_tun_volume)
        color = '#ff4040';
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f1') + '</span>';
      }
    },
    { text: 'Inf/dec &deg;C', datafield: 'step_infuse_temp', width: 90, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       if (rowdata.step_type == 1)
        return '<span></span>';
       return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
      }
    },
    { text: 'L/Kg.', datafield: 'step_wg_ratio', width: 80, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       var color = '#ffffff';
       if (value < 2.0 || value > 6.0)
        color = '#ff4040';
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
      }
    },
    { text: 'pH', datafield: 'step_ph', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f2' },
    { text: 'SG', datafield: 'step_sg', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f3' },
    { text: '', columntype: 'button', width: 15, align: 'center',
     cellsrenderer: function(row) {
      if (row < 2)
       return ' ';
      return '▴';
     }, buttonclick: function(row) {
      if (row >= 2) {
       swapMash(row, row-1);
      }
     }
    },
    { text: '', columntype: 'button', width: 15, align: 'center',
     cellsrenderer: function(row) {
      rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
      if (row < 1 || row > (rowscount -2))
       return ' ';
      return '▾';
     }, buttonclick: function(row) {
      rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
      if (row >= 1 && row <= (rowscount -2)) {
       swapMash(row, row+1);
      }
     }
    },
    { text: '', datafield: 'Edit', columntype: 'button', width: 80, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      if (dataRecord.stage > 3) {
       alert('Het maischen is al gedaan.');
      } else {
       mashRow = row;
       mashData = $('#mashGrid').jqxGrid('getrowdata', mashRow);
       if (mashRow == 0)
        $("#wstep_type").jqxDropDownList('disableAt', 2);
       else
        $("#wstep_type").jqxDropDownList('enableAt', 2);
       $('#wstep_name').val(mashData.step_name);
       $('#wstep_type').val(mashData.step_type);
       $('#wstep_infuse_amount').val(mashData.step_infuse_amount);
       $('#wstep_infuse_temp').val(mashData.step_infuse_temp);
       $('#wstep_temp').val(mashData.step_temp);
       $('#wend_temp').val(mashData.end_temp);
       $('#wstep_time').val(mashData.step_time);
       $('#wramp_time').val(mashData.ramp_time);
       $('#wstep_infuse_amount').hide(); // Hide all untile we need it.
       $('#wstep_infuse_temp').hide();
       $('#wstep_pmpt_amount').hide();
       $('#wstep_pmpt_temp').hide();
       if (mashData.step_type == 0) {
        if (mashRow == 0) {
         $('#wstep_infuse_amount').show();
         $('#wstep_pmpt_amount').show();
        } else {
         $('#wstep_infuse_temp').show();
         $('#wstep_pmpt_temp').show();
        }
       }
       $('#wstep_ph').val(mashData.step_ph);
       $('#wstep_sg').val(mashData.step_sg);
       // show the popup window.
       $('#popupMash').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 /*
  * 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 product ...', theme: theme });
 $('#jqxLoader').jqxLoader('open');

 /* Moved to before all functions */
 dataAdapter.dataBind();

 /*
  * Generic functions
  */
 function kookTijd() {
  if (dataRecord.boil_time) {
   $('#brew_pmpt_koken').html('Koken ' + dataRecord.boil_time + ' minuten');
  } else {
   $('#brew_pmpt_koken').html('Koken "no-boil"');
  }
 }

 function swapMash(r1, r2) {

  console.log('swap mash rows ' + r1 + ' ' + r2);
  var row1 = $('#mashGrid').jqxGrid('getrowdata', r1);
  var row2 = $('#mashGrid').jqxGrid('getrowdata', r2);
  var obj1 = { step_name: row1.step_name, step_type: row1.step_type, step_volume: row1.step_volume, step_infuse_amount: row1.step_infuse_amount,
               step_infuse_temp: row1.step_infuse_temp, step_temp: row1.step_temp, step_time: row1.step_time,
               ramp_time: row1.ramp_time, end_temp: row1.end_temp, step_wg_ratio: row1.step_wg_ratio, step_ph: row1.step_ph, step_sg: row1.step_sg };
  var obj2 = { step_name: row2.step_name, step_type: row2.step_type, step_volume: row2.step_volume, step_infuse_amount: row2.step_infuse_amount,
               step_infuse_temp: row2.step_infuse_temp, step_temp: row2.step_temp, step_time: row2.step_time,
               ramp_time: row2.ramp_time, end_temp: row2.end_temp, step_wg_ratio: row2.step_wg_ratio, step_ph: row2.step_ph, step_sg: row2.step_sg };
  $("#mashGrid").jqxGrid('updaterow', r1, obj2);
  $("#mashGrid").jqxGrid('updaterow', r2, obj1);
 }

 function infusionVol(step_infused, step_mashkg, infuse_temp, step_temp, last_temp) {
  var a = last_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
  var b = step_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
  //console.log('b - a: ' + (b - a) + ' t: ' + ((infuse_temp - step_temp) * SpecificHeatWater));
  //console.log('res: ' + ((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)));
  var vol = Round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)), 2);
  console.log('infusionVol(' + step_infused + ', ' + step_mashkg + ', ' + infuse_temp + ', ' + step_temp + ', ' + last_temp + '): ' + vol);
  return vol;
 }

 function decoctionVol(step_volume, step_temp, prev_temp) {
  var a = (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp);
  var b = SpecificHeatWater * (99 - step_temp);
  var vol = 0;
  if (b > 0)
   vol = Round(a / b, 6);
  console.log('decoctionVol(' + step_volume + ', ' + step_temp + ', ' + prev_temp + '): ' + vol);
  return vol;
 }

 function calcViability() {
  var vpm = 1.00;
  var max = 100;
  var rowscount = dataRecord.yeasts.length;
  if (rowscount) {
   for (i = 0; i < rowscount; i++) {
    row = dataRecord.yeasts[i];
    if (row.y_use == 0) {
     if (row.y_form == 0) { // Liquid
      vpm = 0.80;
      max = 97;
      if (row.y_laboratory == 'White Labs') { // PurePitch
       vpm = 0.95;
       max = 100;
      }
     } else if (row.y_form == 1) { // dry yeast
      vpm = 0.998;
      max = 100;
     } else if (row.y_form == 6) { // Dried kveik
      vpm = 0.92;
      max = 100;
     } else { // Slant, Culture, Frozen, Bottle
      vpm = 0.99;
      max = 97;
     }
    }
   }
  }
  var base = max;
  var days = 0;

  if (parseFloat($('#yeast_prod_date').val()) > 2000) {
   console.log('calculate viability');
   var d = new Date();
   var date2 = $('#yeast_prod_date').val();
   date2 = date2.split('-');
   // Now we convert the array to a Date object
   var date1 = new Date(d.getFullYear(), d.getMonth(), d.getDate());
   date2 = new Date(date2[0], date2[1] - 1, date2[2]);
   var diff = parseInt(date1.getTime()) - parseInt(date2.getTime());
   days = Math.floor(diff/1000/60/60/24);

   var degrade = 1 - ((1 - vpm) / 30.41);  // viability degradation per day.
   for (i = 0; i < days; i++) {
    base = base * degrade;
   }
   if (base > max) {
    base = max;
   }
   base = Math.round(base);
  }
  console.log('age:' + days + ' max:' + max + ' vpm:' + vpm + ' base:' + base);

  if (dataRecord.starter_viability != base) {
   dataRecord.starter_viability = base;
   $('#starter_viability').val(dataRecord.starter_viability);
  }
 }

 function calcSupplies() {
  if (dataRecord.inventory_reduced > 6) {
   $('#ok_pmpt').hide();
   return;
  }
  if (ok_fermentables && ok_hops && ok_miscs && ok_yeasts && ok_waters)
   $('#ok_supplies').html("<img src='images/dialog-ok-apply.png'>");
  else
   $('#ok_supplies').html("<img src='images/dialog-error.png'>");
 }

 function calcPercentages() {

  console.log('calcPercentages()');
  var tw = 0, rowdata, percentage, rowscount = dataRecord.fermentables.length;
  if (rowscount > 1) {
   for (i = 0; i < rowscount; i++) {
    rowdata = dataRecord.fermentables[i];
    if (rowdata.f_added < 4)
     tw += Round(rowdata.f_amount, 3);
   }
   tw = Round(tw, 3);

   for (i = 0; i < rowscount; i++) {
    rowdata = $('#fermentableGrid').jqxGrid('getrowdata', i);
    if (rowdata.f_added < 4) {
     percentage = Round(rowdata.f_amount / tw * 100, 1);
     $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', percentage);
    } else {
     $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', 0);
    }
   }
  } else {
   $('#fermentableGrid').jqxGrid('setcellvalue', 0, 'f_percentage', 100);
  }
 }

 /*
  * All calculations that depend on changes in the fermentables,
  * volumes and equipments.
  */
 function calcFermentables() {

  var sugarsf = 0,  // fermentable sugars mash + boil
  sugarsm = 0,      // fermentable sugars in mash
  vol = 0,          // Volume sugars after boil
  addedS = 0,       // Added sugars after boil
  addedmass = 0,    // Added mass after boil
  mvol = 0,         // mash volume
  colort = 0,       // Colors srm * vol totals
  colorh = 0,       // Colors ebc * vol * kt
  colorn = 0,       // Colors ebc * pt * pct
  my_100 = false,
  mashtime = 0,     // Total mash time
  mashtemp = 0,     // Average mash temperature
  bv = 0.925,       // Bierverlies rendement
  sr = 0.95,        // Mash en spoel rendement
  lintner = 0,      // Total recipe lintner
  i, row, rows, org, timem, aboil_volume, spoelw, ogx, topw, s = 0, d, v, x,
  sug, alc, pt, cw, color, scolor, fig;

  /* Init global variables */
  psugar = 0;
  pcara = 0;
  mashkg = 0;
  ok_fermentables = 1;    // All is in stock.
  ok_yeasts = 1;

  if (dataRecord.mashs.length) {
   for (i = 0; i < dataRecord.mashs.length; i++) {
    row = dataRecord.mashs[i];
    if (parseFloat(row.step_type) == 0) // Infusion
     mvol += parseFloat(row.step_infuse_amount);
    if (row.step_temp <= 75) { // Ignore mashout
     timem = row.step_time + row.ramp_time;
     mashtime += timem;
     mashtemp += timem * row.step_temp;
    }
   }
   if (mashtime > 5)
    mashtime -= 5; //Correct last ramp > 75
   mashtemp = Round(mashtemp / mashtime, 2);
  } else {
   console.log("calcFermentables() no mash steps");
  }

  if (! dataRecord.fermentables.length) {
   console.log("calcFermentables() no fermentables");
   return; // grid not yet loaded.
  }

  for (i = 0; i < dataRecord.fermentables.length; i++) {
   row = dataRecord.fermentables[i];
   if (row.f_adjust_to_total_100)
    my_100 = true;
   if (row.f_type == 1 && row.f_added < 4) // Sugar
    psugar += row.f_percentage;
   if (row.f_graintype == 2 && row.f_added < 4)    // Crystal
    pcara += row.f_percentage;
   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 += parseFloat(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) {     // Fermentation or lagering
    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 < 4) {
    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.
   }
   if (fermentableInit) {
    if (row.f_added == 4) {
     $('#bottle_priming_total').val(row.f_amount * 1000);    // Prevent clearing
     $('#bottle_priming_sugar').jqxDropDownList('selectItem', row.f_name);
    }
    if (row.f_added == 5) {
     $('#keg_priming_total').val(row.f_amount * 1000);
     $('#keg_priming_sugar').jqxDropDownList('selectItem', row.f_name);
    }
   }
   // Check supplies.
   if ((((dataRecord.inventory_reduced <= 2) && (row.f_added <= 1)) ||  // Mash or boil
        ((dataRecord.inventory_reduced <= 3) && (row.f_added == 2)) ||  // Primary
        ((dataRecord.inventory_reduced <= 5) && (row.f_added == 3)) ||  // Secondary or Tertiary
        ((dataRecord.inventory_reduced <= 6) && (row.f_added == 4)) ||  // Bottle
        ((dataRecord.inventory_reduced <= 6) && (row.f_added == 5))) && row.f_inventory < row.f_amount) {
    ok_fermentables = 0;
   }
   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;
   }
  }
  fermentableInit = 0;
  $('#ferm_lintner').val(Math.round(parseFloat(lintner / mashkg)));
  $('#mash_kg').val(mashkg);
  console.log('calcFermentables() supplies:' + ok_fermentables + ' moutsuiker:' + Round(sugarsm, 3) + '/' + Round(sugarsf, 3));
  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 });
  }

  if (mvol > 0) {
   v = s / sugardensity + mvol;
   s = 1000 * s / (v * 10); //deg. Plato
   est_mash_sg = Round(plato_to_sg(s), 5);
   $('#est_mash_sg').val(est_mash_sg);
  }

  // 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);
  org = dataRecord.est_og;

  // Estimate SG in kettle after boil
  aboil_sg = estimate_sg(sugarsf, parseFloat(dataRecord.batch_size));
  $('#est_og3').val(aboil_sg);

  // Estimate SG in kettle before boil
  preboil_sg = estimate_sg(sugarsm, parseFloat(dataRecord.boil_size));
  $('#est_pre_sg').val(preboil_sg);

  // Recalculate volumes.
  aboil_volume = parseFloat(dataRecord.batch_size);
  if (dataRecord.brew_aboil_volume > 0)
   aboil_volume = dataRecord.brew_aboil_volume / 1.04;     // volume @ 20 degrees
  if (dataRecord.brew_fermenter_tcloss == 0) {
   dataRecord.brew_fermenter_tcloss = dataRecord.eq_trub_chiller_loss;
   $('#brew_fermenter_tcloss').val(dataRecord.brew_fermenter_tcloss);
  }
  dataRecord.brew_fermenter_volume = aboil_volume - dataRecord.brew_fermenter_tcloss + dataRecord.brew_fermenter_extrawater;
  $('#brew_fermenter_volume').val(dataRecord.brew_fermenter_volume);
  // Estimated needed sparge water corrected for the temperature.
  spoelw = (dataRecord.boil_size - mash_infuse + (mashkg * my_grain_absorbtion) + dataRecord.eq_lauter_deadspace) * 1.03;
  $('#brew_sparge_est').val(spoelw);
  // Calculate SG in fermenter
  ogx = dataRecord.brew_aboil_sg;
  if (ogx < 1.002)
   ogx = aboil_sg;
  topw = dataRecord.brew_fermenter_extrawater;

  if (dataRecord.brew_fermenter_volume > 0) {
   sug = sg_to_plato(ogx) * dataRecord.brew_fermenter_volume * ogx / 100;  //kg of sugar in
   sug += addedS; //kg

   if ((dataRecord.brew_fermenter_volume * ogx + addedmass) > 0) {
    pt = 100 * sug / (dataRecord.brew_fermenter_volume * ogx + addedmass + topw);
    dataRecord.og = dataRecord.brew_fermenter_sg = Round(plato_to_sg(pt), 4);
    $('#brew_fermenter_sg').val(dataRecord.brew_fermenter_sg);
    // color
    if (dataRecord.color_method == 4) {
     dataRecord.brew_fermenter_color = Math.round(((pt / 8.6) * colorn) + (dataRecord.boil_time / 60));
    } else if (dataRecord.color_method == 3) {
     dataRecord.brew_fermenter_color = Math.round((4.46 * bv * sr) / (aboil_volume + topw) * colorh);
    } else {
     cw = colort / (aboil_volume + topw) * 8.34436;
     dataRecord.brew_fermenter_color = kw_to_ebc(dataRecord.color_method, cw);
    }
    $('#brew_fermenter_color').val(dataRecord.brew_fermenter_color);
    scolor = ebc_to_color(dataRecord.brew_fermenter_color);
    $('#bcolorf').show();
    document.getElementById('bcolorf').style.background = scolor;
   }
  } else {
   // Negative volume
   dataRecord.brew_fermenter_sg = dataRecord.brew_fermenter_color = 0;
   $('#brew_fermenter_sg').val(0);
   $('#brew_fermenter_color').val(0);
   $('#bcolorf').hide();
  }

  // Color of the wort
  if (dataRecord.color_method == 4) {
   color = Math.round(((sg_to_plato(dataRecord.est_og) / 8.6) * colorn) + (dataRecord.boil_time / 60));
  } else if (dataRecord.color_method == 3) {      // Hans Halberstadt
   color = Math.round((4.46 * bv * sr) / parseFloat(dataRecord.batch_size) * colorh);
  } else {
   cw = colort / parseFloat(dataRecord.batch_size) * 8.34436;
   color = kw_to_ebc(dataRecord.color_method, cw);
  }
  dataRecord.est_color = color;
  $('#est_color').val(color);
  $('#est_color2').val(color);
  scolor = ebc_to_color(color);
  document.getElementById('bcolor').style.background = scolor;
  document.getElementById('bcolor2').style.background = scolor;

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

  // Calculate estimated svg.
  svg = 0; // default.
  initcells = 0;
  for (i = 0; i < dataRecord.yeasts.length; i++) {
   row = dataRecord.yeasts[i];
   if (row.y_use == 0) {   // Primary
    if (parseFloat(row.y_attenuation) > svg)
     svg = parseFloat(row.y_attenuation);    // Take the highest if multiple yeasts.
    if (row.y_form == 0)
     initcells += (parseFloat(row.y_cells) / 1000000000) * parseFloat(row.y_amount) * (dataRecord.starter_viability / 100);
    else
     initcells += (parseFloat(row.y_cells) / 1000000) * parseFloat(row.y_amount) * (dataRecord.starter_viability / 100);
   }
   // TODO: brett in secondary ??
   if ((((dataRecord.inventory_reduced <= 3) && (row.y_use == 0)) ||  // Primary
        ((dataRecord.inventory_reduced <= 4) && (row.y_use == 1)) ||  // Secondary
        ((dataRecord.inventory_reduced <= 5) && (row.y_use == 2)) ||  // Tertiary
        ((dataRecord.inventory_reduced <= 6) && (row.y_use == 3))) && // Bottle
        (row.y_inventory < row.y_amount)) {
    ok_yeasts = 0;
   }
  }
  calcSupplies();
  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);
  $('#est_fg3').val(dataRecord.est_fg);
  fig = 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);

  // Calculate the final svg if available use the real value.
  if ((dataRecord.stage >= 6) && (dataRecord.fg > 0.990) && (dataRecord.fg < dataRecord.brew_fermenter_sg)) {
   svg = calc_svg(dataRecord.brew_fermenter_sg, dataRecord.fg);
   org = dataRecord.brew_fermenter_sg;
   fig = dataRecord.fg;
  }

  $('#yeast_cells').val(initcells);
  $('#need_cells').val(getNeededYeastCells());

  // Calculate the calories in kcal/l (from brouwhulp)
  alc = 1881.22 * fig * (org - fig) / (1.775 - org);
  sug = 3550 * fig * (0.1808 * org + 0.8192 * fig - 1.0004);
  $('#kcal').val(Math.round((alc + sug) / (12 * 0.0295735296)));
 };

 function calcMash() {

  var h, m, infused = 0, mashtime = 0, mashvol = 0, vol, i, j, n, a, b, row, temp;
  var lasttemp = 18.0;
  var graintemp = 18.0;
  var tuntemp = 18.0;

  if (dataRecord.mashs.length && (mashkg > 0)) {
   console.log('calcMash()');
   for (i = 0; i < dataRecord.mashs.length; i++) {
    row = dataRecord.mashs[i];
    if (row.step_type == 0) { // Infusion
     if (i == 0) {
      // First mash step, temperature from the mashtun and malt.
      n = 20; // tun is preheated.
      tuntemp = row.step_temp;
      for (j = 0; j < n; j++) {
       a = mashkg * graintemp * SpecificHeatMalt + dataRecord.eq_tun_weight * tuntemp * dataRecord.eq_tun_specific_heat;
       b = row.step_temp * (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + row.step_infuse_amount * SpecificHeatWater + mashkg * SpecificHeatMalt) - SlakingHeat * mashkg;
       if (row.step_infuse_amount > 0) {
        temp = (b - a) / (row.step_infuse_amount * SpecificHeatWater);
       } else {
        temp = 99;
       }
       tuntemp += (temp - tuntemp) / 2;
       row.step_infuse_temp = Round(temp, 6);
      }
      //console.log('init infuse temp: ' + row.step_infuse_temp);
     } else {
      // Calculate amount of infusion water.
      row.step_infuse_amount = infusionVol(infused, mashkg, row.step_infuse_temp, row.step_temp, lasttemp);
      console.log('vol: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp);
     }
     infused += row.step_infuse_amount;
    } else if (row.step_type == 1) { // Temperature
     if (i > 0)
      row.step_infuse_amount = 0;
     row.step_infuse_temp = 0;
    } else if (row.step_type == 2) { // Decoction
     row.step_infuse_amount = decoctionVol(infused, row.step_temp, lasttemp);
     row.step_infuse_temp = 99;
    }
    row.step_volume = infused;
    //console.log(i + ' type: ' + row.step_type + ' volume: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp);
    lasttemp = row.step_temp;
    mashtime += row.step_time + row.ramp_time;
    row.step_wg_ratio = Round(infused / mashkg, 6);
    $('#mashGrid').jqxGrid('updaterow', i, row);
   }
  }
  mashvol = mashkg * MaltVolume + infused;
  $('#est_mashvol').val(mashvol);
  h = Math.floor(mashtime / 60);
  m = Math.floor(mashtime - (h * 60));
  if (h < 10)
    h = '0' + h;
  if (m < 10)
    m = '0' + m;
  $('#est_mashtime').val(h + ':' + m);
 }

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

  console.log('calcFermentablesFromOG(' + OG + ')');
  var amount, row, d, i, sug, tot = 0, totmass = 0, rowscount, efficiency = parseFloat($('#efficiency').jqxNumberInput('decimal'));
  sug = sg_to_plato(OG) * parseFloat($('#batch_size').jqxNumberInput('decimal')) * OG / 100;      //total amount of sugars in kg
  rowscount = dataRecord.fermentables.length;

  for (i = 0; i < rowscount; i++) {
   row = dataRecord.fermentables[i];
   if (row.f_added < 4) {
    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;
   }
  }
  if (tot)
   totmass = Round(sug / tot, 3);

  if (totmass) {
   for (i = 0; i < rowscount; i++) {
    row = dataRecord.fermentables[i];
    if (row.f_added < 4) {
     amount = Math.round(row.f_percentage * 10 * totmass) / 1000;
     dataRecord.fermentables[i].f_amount = amount;
     $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', amount);
    }
   }
  }
 };

 function getNeededYeastCells() {

  var plato, volume, sg = dataRecord.brew_fermenter_sg;
  if (sg <= 1.0001 && dataRecord.fg > 1.000)
   sg = dataRecord.fg;
  else if (sg <= 1.0001)
   sg = dataRecord.est_og;
  plato = sg_to_plato(sg);

  volume = dataRecord.brew_fermenter_volume;
  if (volume <= 0)
   volume = dataRecord.batch_size - dataRecord.eq_trub_chiller_loss;

  return dataRecord.yeast_pitchrate * volume * plato;
 }

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

  if (use == 4 || use == 5) // Whirlpool or Dry-hop
   return 0;
  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 (use == 4) { // Whirlpool
   if (bt > 30)
    bt = 30; // Max 30 minutes
   result = 0.62 * bt / 30;
  } 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
   result = 1.2;
  }
  return (result * amount * 1000) / vol;
 }

 function calcIBUs() {
  var total_ibus = 0, ferm_ibus = 0, rows = {}, i, row;
  hop_aroma = hop_flavour = 0;
  if (!(rows = $('#hopGrid').jqxGrid('getrows'))) {
   return;
  }
  ok_hops = 1;
  for (i = 0; i < rows.length; i++) {
   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,
    dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6);
   ferm_ibus += toIBU(row.h_useat, row.h_form, preboil_sg,
    parseFloat(dataRecord.brew_fermenter_volume) + parseFloat(dataRecord.brew_fermenter_tcloss),
    parseFloat(row.h_amount), parseFloat(row.h_time), parseFloat(row.h_alpha), dataRecord.ibu_method,
    dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6);
   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));
   if ((((dataRecord.inventory_reduced <= 2) && (row.h_useat <= 4)) ||  // Mash, FW, Boil, Aroma, Whirlpool
        ((dataRecord.inventory_reduced <= 6) && (row.h_useat == 5))) && // Dry-hop
        (row.h_inventory < row.h_amount))
    ok_hops = 0;
  }
  total_ibus = Round(total_ibus, 1);
  ferm_ibus = Round(ferm_ibus, 1);
  hop_flavour = Round(hop_flavour * 100 / 5, 1);
  hop_aroma = Round(hop_aroma * 100 / 6, 1);
  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 +
               ' fermenter:' + ferm_ibus + ' supplies:' + ok_hops);
  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);
  $('#brew_fermenter_ibu').val(ferm_ibus);
  calcStage();
  calcSupplies();
 };

 /*
  * http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
  *
  * stype: 0=stirred, 1=shaken, 2=simple
  * totcells: initial cells
  * egrams: gram extract
  */
 function getGrowthRate(stype, totcells, egrams) {

  /* Cells per grams extract (B/g) */
  var cpe = totcells / egrams;

  if (cpe > 3.5)
   return 0;       // no growth
  if (stype == 2)
   return 0.4;     // simple starter
  if (stype == 1)
   return 0.62;    // shaken starter
  if (cpe <= 1.4)  // stirred starter
   return 1.4;
  return 2.33 - (.67 * cpe);
 };

 function calcStep(svol, stype, start) {

  var gperpoint = 2.72715,  //number of grams of extract per point of starter gravity per liter
  prate = start / svol * 1000,
  irate = Round(prate, 1),
  egrams = (dataRecord.starter_sg - 1) * svol * gperpoint,
  grate = getGrowthRate(stype, start, egrams),
  ncells = Round(egrams * grate, 1),
  totcells = parseFloat(ncells) + start;

  return {
   svol: svol,
   irate: irate,
   prate: Round(prate, 1),
   ncells: ncells,
   totcells: totcells,
   growf: Round(ncells / start, 2)
  };
 }

 function killstep2() {

  dataRecord.prop2_volume = 0;
  $('#prop2_volume').val(0);
  $('#prop2_tcells').val(0);
  $('#prop2_type,#prop2_volume,#prop2_irate,#prop2_ncells,#prop2_tcells,#prop2_growf').hide();
  $('#r2_pmpt').show();
 }

 function killstep3() {

  dataRecord.prop3_volume = 0;
  $('#prop3_volume').val(0);
  $('#prop3_tcells').val(0);
  $('#prop3_type,#prop3_volume,#prop3_irate,#prop3_ncells,#prop3_tcells,#prop3_growf').hide();
  $('#r3_pmpt').show();
 }

 function killstep4() {

  dataRecord.prop4_volume = 0;
  $('#prop4_volume').val(0);
  $('#prop4_tcells').val(0);
  $('#prop4_type,#prop4_volume,#prop4_irate,#prop4_ncells,#prop4_tcells,#prop4_growf').hide();
  $('#r4_pmpt').show();
 }

 /*
  * Calculate all starter steps.
  * stype: final starter type: 0 = stirred, 1 = shaked, 2 = simple.
  * start: initial cells in billions
  * needed: needed cells in billions
  *
  * result: all values updated.
  */
 function calcSteps(stype, start, needed) {

  var uvols = [20, 40, 60, 80, 100, 150, 200, 250, 375, 500, 625, 750, 875, 1000, 1250, 1500, 2000, 2500, 3000, 4000, 5000],
      mvols = uvols.length, svol = 0, lasti = 0, result = {}, i;

  /*
   * If no values are set, auto calculate the starter.
   */
  if ((parseFloat($('#prop1_volume').jqxNumberInput('decimal')) + parseFloat($('#prop2_volume').jqxNumberInput('decimal')) +
       parseFloat($('#prop3_volume').jqxNumberInput('decimal')) + parseFloat($('#prop4_volume').jqxNumberInput('decimal'))) == 0) {
   // clear by default
   for (i = 1; i < 5; i++) {
    $('#prop' + i + '_type,#prop' + i + '_volume,#prop' + i + '_irate,#prop' + i + '_ncells,#prop' + i + '_tcells,#prop' + i + '_growf').hide();
    $('#r' + i + '_pmpt').show();
    $('#prop' + i + '_type').val(stype);
    $('#prop' + i + '_volume').val(0);
   }
   if (start > needed) {
    return; // no starter needed
   }
   $('#prop1_type,#prop1_volume,#prop1_irate,#prop1_ncells,#prop1_tcells,#prop1_growf').show();
   $('#r1_pmpt').hide();
   for (i = lasti; i <= mvols; i++) {
    lasti = i;
    svol = uvols[lasti];
    result = calcStep(svol, stype, start);
    if (result.irate < 25) {
     // inocculation rate too low, backup one step and break out.
     lasti = i - 1;
     svol = uvols[lasti];
     result = calcStep(svol, stype, start);
     break;
    }
    if (result.totcells > needed || i == mvols) { // hit the target or loops done
     break;
    }
   }
   $('#prop1_volume').val(result.svol / 1000); // to liters
   $('#prop1_irate').val(result.prate);
   $('#prop1_ncells').val(result.ncells);
   $('#prop1_tcells').val(result.totcells);
   $('#prop1_growf').val(result.growf);
   if (result.totcells > needed)
    return; // hit the target

   // second stage
   $('#r2_pmpt').hide();
   $('#prop2_type').val(stype);
   $('#prop2_type,#prop2_volume,#prop2_irate,#prop2_ncells,#prop2_tcells,#prop2_growf').show();
   for (i = lasti; i <= mvols; i++) {
    lasti = i;
    svol = uvols[lasti];
    result = calcStep(svol, stype, $('#prop1_tcells').val());
    if (result.irate < 25) {
     lasti = i - 1;
     svol = uvols[lasti];
     result = calcStep(svol, stype, $('#prop1_tcells').val());
     break;
    }
    if (result.totcells > needed || i == mvols) { // hit the target or loops done
     break;
    }
   }
   $('#prop2_volume').val(result.svol / 1000); // to liters
   $('#prop2_irate').val(result.prate);
   $('#prop2_ncells').val(result.ncells);
   $('#prop2_tcells').val(result.totcells);
   $('#prop2_growf').val(result.growf);
   if (result.totcells > needed)
    return; // hit the target

   // third stage
   $('#r3_pmpt').hide();
   $('#prop3_type').val(stype);
   $('#prop3_type,#prop3_volume,#prop3_irate,#prop3_ncells,#prop3_tcells,#prop3_growf').show();
   for (i = lasti; i <= mvols; i++) {
    lasti = i;
    svol = uvols[lasti];
    result = calcStep(svol, stype, $('#prop2_tcells').val());
    if (result.irate < 25) {
     lasti = i - 1;
     svol = uvols[lasti];
     result = calcStep(svol, stype, $('#prop2_tcells').val());
     break;
    }
    if (result.totcells > needed || i == mvols) { // hit the target or loops done
     break;
    }
   }
   $('#prop3_volume').val(result.svol / 1000); // to liters
   $('#prop3_irate').val(result.prate);
   $('#prop3_ncells').val(result.ncells);
   $('#prop3_tcells').val(result.totcells);
   $('#prop3_growf').val(result.growf);
   if (result.totcells > needed)
    return; // hit the target

   // fourth stage
   $('#r4_pmpt').hide();
   $('#prop4_type').val(stype);
   $('#prop4_type,#prop4_volume,#prop4_irate,#prop4_ncells,#prop4_tcells,#prop4_growf').show();
   for (i = lasti; i <= mvols; i++) {
    lasti = i;
    svol = uvols[lasti];
    result = calcStep(svol, stype, $('#prop3_tcells').val());
    if (result.totcells > needed || i == mvols) { // hit the target or loops done
     $('#prop4_volume').val(result.svol / 1000); // to liters
     $('#prop4_irate').val(result.prate);
     $('#prop4_ncells').val(result.ncells);
     $('#prop4_tcells').val(result.totcells);
     $('#prop4_growf').val(result.growf);
     return;
    }
   }
  } else {
   // recalculate
   if (dataRecord.prop1_volume > 0) {
    $('#r1_pmpt').hide();
    $('#prop1_type,#prop1_volume,#prop1_irate,#prop1_ncells,#prop1_tcells,#prop1_growf').show();
    result = calcStep($('#prop1_volume').val() * 1000, dataRecord.prop1_type, start);
    $('#prop1_irate').val(result.prate);
    $('#prop1_ncells').val(result.ncells);
    $('#prop1_tcells').val(result.totcells);
    $('#prop1_growf').val(result.growf);
    if (result.totcells > needed) {
     killstep2();
     killstep3();
     killstep4();
    } else if (dataRecord.prop2_volume == 0) {
     dataRecord.prop2_volume = dataRecord.prop1_volume; /* Extra step needed, start with the same size */
     dataRecord.prop2_type = dataRecord.prop1_type;
     $('#prop2_volume').val(dataRecord.prop2_volume);
     $('#prop2_type').val(dataRecord.prop2_type);
    }
   }
   if (dataRecord.prop2_volume > 0) {
    $('#r2_pmpt').hide();
    $('#prop2_type,#prop2_volume,#prop2_irate,#prop2_ncells,#prop2_tcells,#prop2_growf').show();
    result = calcStep($('#prop2_volume').val() * 1000, dataRecord.prop2_type, $('#prop1_tcells').val());
    $('#prop2_irate').val(result.prate);
    $('#prop2_ncells').val(result.ncells);
    $('#prop2_tcells').val(result.totcells);
    $('#prop2_growf').val(result.growf);
    if (result.totcells > needed) {
     killstep3();
     killstep4();
    } else if (dataRecord.prop3_volume == 0) {
     dataRecord.prop3_volume = dataRecord.prop2_volume; /* Extra step needed, start with the same size */
     dataRecord.prop3_type = dataRecord.prop2_type;
     $('#prop3_volume').val(dataRecord.prop3_volume);
     $('#prop3_type').val(dataRecord.prop3_type);
    }
   }
   if (dataRecord.prop3_volume > 0) {
    $('#r3_pmpt').hide();
    $('#prop3_type,#prop3_volume,#prop3_irate,#prop3_ncells,#prop3_tcells,#prop3_growf').show();
    result = calcStep($('#prop3_volume').val() * 1000, dataRecord.prop3_type, $('#prop2_tcells').val());
    $('#prop3_irate').val(result.prate);
    $('#prop3_ncells').val(result.ncells);
    $('#prop3_tcells').val(result.totcells);
    $('#prop3_growf').val(result.growf);
    if (result.totcells > needed) {
     killstep4();
    } else if (dataRecord.prop4_volume == 0) {
     dataRecord.prop4_volume = dataRecord.prop3_volume; /* Extra step needed, start with the same size */
     dataRecord.prop4_type = dataRecord.prop3_type;
     $('#prop4_volume').val(dataRecord.prop4_volume);
     $('#prop4_type').val(dataRecord.prop4_type);
    }
   }
   if (dataRecord.prop4_volume > 0) {
    $('#r4_pmpt').hide();
    $('#prop4_type,#prop4_volume,#prop4_irate,#prop4_ncells,#prop4_tcells,#prop4_growf').show();
    result = calcStep($('#prop4_volume').val() * 1000, dataRecord.prop4_type, $('#prop3_tcells').val());
    $('#prop4_irate').val(result.prate);
    $('#prop4_ncells').val(result.ncells);
    $('#prop4_tcells').val(result.totcells);
    $('#prop4_growf').val(result.growf);
   }
  }
 }

 function calcYeast() {

  // Calculate needed cells.
  var plato, volume, rows, rowscount, row, i, needed, use_cells, sg = dataRecord.brew_fermenter_sg;

  if (sg <= 1.0001 && dataRecord.fg > 1.000)
   sg = dataRecord.fg;
  else if (sg <= 1.0001)
   sg = dataRecord.est_og;
  plato = sg_to_plato(sg);

  volume = dataRecord.brew_fermenter_volume;
  if (volume > 0) {
   if (dataRecord.brew_fermenter_extrawater > 0)
    volume += dataRecord.brew_fermenter_extrawater;
  } else {
   volume = dataRecord.batch_size - dataRecord.eq_trub_chiller_loss;
  }

  // Also in calcFermentables()
  $('#yeast_cells').val(initcells);

  if (!(rows = $('#yeastGrid').jqxGrid('getrows'))) {
   return; // grid not yet loaded.
  }
  rowscount = dataRecord.yeasts.length;
  if (rowscount == 0)
   return; // no yeast in recipe

  $('.primary_dry').hide();
  $('.primary_liquid').hide();

  var maybe_starter = 0;
  var pitchrate = 0.75;    // Yeast pitch rate default
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.yeasts[i];
   if (row.y_use == 0) { // primary
    if (row.y_form == 1) {
     // Dry yeast
     $('.primary_dry').show();
     console.log('dry yeast: ' + row.y_gr_hl_lo + '@' + row.y_sg_lo + '  ' + row.y_gr_hl_hi + '@' + row.y_sg_hi);
     // Build the formule with the yeast parameters.
     // Based on https://www.lallemandbrewing.com/en/canada/brewers-corner/brewing-tools/pitching-rate-calculator/
     var og = row.y_sg_lo;
     var f1 = row.y_gr_hl_lo / 100;
     var f2 = Round(f1 / 5, 6);
     // After a lot of try and error, study, the best thing to increase the pitch amount is actually
     // use this simple formula by Lallemand. This is about the same as sugar weight increment in the wort.
     var multiplier = (sg <= og) ? f1 : (f1 + f2 * (sg - og) / 0.008);
     console.log('sg: ' + sg + ' og: ' + og + ' f1: ' + f1 + ' f2: ' + f2 + ' multiplier: ' + multiplier);
     // dataRecord.starter_viability
     var yeast_grams = Round(volume * multiplier * (100 / dataRecord.starter_viability), 2);
     $('#yeast_grams').val(yeast_grams);
     var yeast_gr_hl = Round(yeast_grams / (volume * 0.01), 2);
     $('#yeast_gr_hl').val(yeast_gr_hl);
     //console.log('need ' + yeast_grams + ' grams, gr/hl: ' + yeast_gr_hl);

    } else {
     // Liquid yeast
     $('.primary_liquid').show();
     // pitchrate see https://www.brewersfriend.com/yeast-pitch-rate-and-starter-calculator/
     // and http://braukaiser.com/blog/blog/2012/11/03/estimating-yeast-growth/
     if (row.y_type == 0) { // lager yeast
      pitchrate = 1.5;
      if (dataRecord.est_og > 1.060)
       pitchrate = 2.0;
     } else if (row.y_type == 6) { // Kveik
      pitchrate = 0.075;
     } else {
      pitchrate = 0.75;
      if (dataRecord.est_og > 1.060)
       pitchrate = 1.0;
     }
     if (dataRecord.yeast_pitchrate < 0.01) {
      dataRecord.yeast_pitchrate = pitchrate;
      $('#yeast_pitchrate').val(pitchrate);
     }
     maybe_starter = 1;
    }
   }
  }

  needed = Round(dataRecord.yeast_pitchrate * volume * plato, 1);
  $('#need_cells').val(needed);
  use_cells = initcells;
  if (needed <= initcells)
   maybe_starter = 0;
  //console.log('calcYeast() pitchrate:' + dataRecord.yeast_pitchrate + ' start:' + initcells + ' needed:' + needed + ' volume:' + volume + ' maybe_starter:' + maybe_starter);

  if (maybe_starter != dataRecord.starter_enable) {
   dataRecord.starter_enable = maybe_starter;
   showStarter();
  }

  if (dataRecord.starter_enable) {
   calcSteps(dataRecord.starter_type, initcells, needed);

   for (i = 1; i < 5; i++) {
    $('#r' + i + '_irate').html('');
    $('#r' + i + '_growf').html('');
    $('#r' + i + '_tcells').html('');
    if (parseFloat($('#prop' + i + '_volume').val()) > 0) {
     if ((parseFloat($('#prop' + i + '_irate').val()) < 25) || (parseFloat($('#prop' + i + '_irate').val()) > 100)) {
      $('#r' + i + '_irate').html("<img src='images/dialog-error.png'>");
     } else {
      $('#r' + i + '_irate').html("<img src='images/dialog-ok-apply.png'>");
     }
     if (parseFloat($('#prop' + i + '_growf').val()) < 1)
      $('#r' + i + '_growf').html("<img src='images/dialog-error.png'>");
     if (($('#prop' + i + '_type').val() > 0) && (parseFloat($('#prop' + i + '_growf').val()) > 3))
      $('#r' + i + '_growf').html("<img src='images/dialog-error.png'>");
     if (parseFloat($('#prop' + i + '_tcells').val()) > needed)
      $('#r' + i + '_tcells').html("<img src='images/dialog-ok-apply.png'>");
     use_cells = parseFloat($('#prop' + i + '_tcells').val());
    } else {
     $('#r' + i + '_irate').html('');
    }
   }
  }
  $('#plato_cells').val(parseFloat(use_cells / (volume * plato)));
 };

 function adjustHops(factor) {

  //console.log('adjustHops(' + factor + ')');
  var row, i, amount, rowscount = dataRecord.hops.length;
  if (rowscount == 0)
   return;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.hops[i];
   amount = row.h_amount * factor;
   dataRecord.hops[i].h_amount = amount;
   $('#hopGrid').jqxGrid('setcellvalue', i, 'h_amount', amount);
  }
 };

 function whirlpoolHops() {
  var row, i, time, rowscount = dataRecord.hops.length;
  if (rowscount == 0)
   return;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.hops[i];
   if (row.h_useat == 4) {
    time = parseFloat(dataRecord.brew_whirlpool9) + parseFloat(dataRecord.brew_whirlpool7) + parseFloat(dataRecord.brew_whirlpool6);
    dataRecord.hops[i].h_time = time;
    $('#hopGrid').jqxGrid('setcellvalue', i, 'h_time', time);
   }
  }
 };

 function calcMiscs() {

  ok_miscs = 1;
  var row, i, rowscount = dataRecord.miscs.length;
  if (rowscount == 0)
   return;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.miscs[i];
   if ((((dataRecord.inventory_reduced <= 2) && (row.m_use_use <= 2)) ||  // Starter, Mash, Boil
        ((dataRecord.inventory_reduced <= 3) && (row.m_use_use == 3)) ||  // Primary
        ((dataRecord.inventory_reduced <= 5) && (row.m_use_use == 4)) ||  // Secondary, Teriary
        ((dataRecord.inventory_reduced <= 6) && (row.m_use_use == 5))) && // Bottle
        (row.m_inventory < row.m_amount)) {
    ok_miscs = 0;
   }
  }
  calcSupplies();
 };

 function adjustMiscs(factor) {

  //console.log('adjustMiscs(' + factor + ')');
  var row, i, amount, rowscount = dataRecord.miscs.length;
  if (rowscount == 0)
   return;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.miscs[i];
   amount = row.m_amount * factor;
   dataRecord.miscs[i].m_amount = amount;
   $('#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 'MgCl2':
     $('#wa_mgcl2').val(row.m_amount * 1000);
     break;
    case 'NaHCO3':
     $('#wa_nahco3').val(row.m_amount * 1000);
     break;
    case 'CaCO3':
     $('#wa_caco3').val(row.m_amount * 1000);
     break;
    case 'Melkzuur':
    case 'Zoutzuur':
    case 'Fosforzuur':
    case 'Zwavelzuur':
     $('#wa_acid').val(row.m_amount * 1000);
     break;
   }
  }
 };

 function adjustYeasts(factor) {

  //console.log('adjustYeasts(' + factor + ')');
  var row, i, amount, rowscount = dataRecord.yeasts.length;
  if (rowscount == 0)
   return;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.yeasts[i];
   if (! dataRecord.starter_enable) { // Only adjust without a starter
    amount = row.y_amount * factor;
    dataRecord.yeasts[i].y_amount = amount;
    $('#yeastGrid').jqxGrid('setcellvalue', i, 'y_amount', amount);
   }
  }
  calcYeast();
 };

 function adjustWaters(factor) {

  //console.log('adjustWaters(' + factor + ')');
  var amount, row, i, rowscount = dataRecord.mashs.length;
  if (rowscount == 0)
   return;
  mash_infuse = 0;
  for (i = 0; i < rowscount; i++) {
   row = dataRecord.mashs[i];
   if (row.step_type == 0) { // Infusion
    amount = Round(parseFloat(row.step_infuse_amount) * factor, 1);
    dataRecord.mashs[i].step_infuse_amount = amount;
    $('#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 {
   var w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
   var w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
   dataRecord.w1_amount = Round(w1_amount, 3);
   dataRecord.w2_amount = Round(w2_amount, 3);
   $('#w1_amount').val(dataRecord.w1_amount);
   $('#w2_amount').val(dataRecord.w2_amount);
  }
  $('#wg_amount').val(mash_infuse);
 };

 function calcMashEfficiency() {
  var c, m;
  if (parseFloat($('#brew_mash_sg').jqxNumberInput('decimal')) < 1.002)
   return;
  c = sg_to_plato(est_mash_sg);
  m = sg_to_plato(parseFloat($('#brew_mash_sg').jqxNumberInput('decimal')));
  if (c > 0.5)
   $('#brew_mash_efficiency').val(100 * m / c);
  else
   $('#brew_mash_efficiency').val(0);
 };

 function calcEfficiencyBeforeBoil() {
  var m = 0, i, row, tot, result = 0;
  if (!($('#fermentableGrid').jqxGrid('getrows')))
   return; // no grid loaded yet
  if (dataRecord.fermentables.length == 0)
   return; // no fermentables
  for (i = 0; i < dataRecord.fermentables.length; i++) {
   row = dataRecord.fermentables[i];
   if (row.f_added == 0) { // Mash
    m += row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
   }
  }
  tot = sg_to_plato(dataRecord.brew_preboil_sg) * (dataRecord.brew_preboil_volume / 1.04) * dataRecord.brew_preboil_sg * 10 / 1000;
  if (m > 0)
   result = Round((tot / m * 100), 1);
  if (result < 0)
   result = 0;
  $('#brew_preboil_efficiency').val(result);
 }

 function calcEfficiencyAfterBoil() {
  var m = 0, // Sugars added at mash
  b = 0,     // Sugars added at boil
  i, row, tot, result = 0;
  if (!($('#fermentableGrid').jqxGrid('getrows'))) {
   return; // grid not yet loaded.
  }
  for (i = 0; i < dataRecord.fermentables.length; i++) {
   row = dataRecord.fermentables[i];
   if (row.f_added == 0) { // Mash
    m += row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
   } else if (row.f_added == 1) { // Boil
    b += row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
   }
  }
  tot = sg_to_plato(dataRecord.brew_aboil_sg) * (dataRecord.brew_aboil_volume / 1.04) * dataRecord.brew_aboil_sg * 10 / 1000;
  tot -= b;       // total sugars in wort  minus added sugars.
  if (m > 0)
   result = Round((tot / m * 100), 1);
  if (result < 0)
   result = 0;
  dataRecord.brew_aboil_efficiency = result;
  $('#brew_aboil_efficiency').val(result);
 }

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

 function GetOptSO4Clratio() {
  if (parseFloat($('#pr_sulfate').jqxNumberInput('decimal')) > 0 && parseFloat($('#pr_chloride').jqxNumberInput('decimal'))) {
   return (parseFloat($('#pr_sulfate').jqxNumberInput('decimal')) / parseFloat($('#pr_chloride').jqxNumberInput('decimal')));
  } else {
   var BUGU = GetBUGU();
   return (1.0 / (-1.2 * BUGU + 1.4));
  }
 }

 function setWaterAgent(name, amount) {
  var row, i, id, found = false, miscs, rows = $('#miscGrid').jqxGrid('getrows');
  if (amount == 0) {
   for (i = 0; i < rows.length; i++) {
    row = rows[i];
    if (row.m_name == name) {
     id = $('#miscGrid').jqxGrid('getrowid', i);
     $('#miscGrid').jqxGrid('deleterow', id);
    }
   }
  } else {
   for (i = 0; i < rows.length; i++) {
    row = rows[i];
    if (row.m_name == name) {
     found = true;
     $('#miscGrid').jqxGrid('setcellvalue', i, 'm_amount', amount / 1000);
     break;
    }
   }
   if (! found) {
    miscs = new $.jqx.dataAdapter(miscInvSource, {
     loadComplete: function() {
      var record, i, row = {}, records = miscs.records;
      for (i = 0; i < records.length; i++) {
       record = records[i];
       if (record.name == name) {
        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;
        $('#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;
 }

 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),
  Cw = Charge(parseFloat($('#wg_ph').jqxNumberInput('decimal'))),
  Cz = Charge(pHZ),
  DeltaCNaught = -C43 + Cw,
  CT = parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) / 50 / DeltaCNaught,
  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 Magn, Z, Calc = parseFloat($('#wg_calcium').jqxNumberInput('decimal')) / (MMCa / 2);
  Magn = parseFloat($('#wg_magnesium').jqxNumberInput('decimal')) / (MMMg / 2);
  Z = ZAlkalinity(pHZ);
  return Z - (Calc / 3.5 + Magn / 7);
 }

 function BufferCapacity(di_ph, acid_to_ph_57, ebc, graintype) {
  C1 = 0;
  if ((di_ph != 5.7) && ((acid_to_ph_57 < - 0.1) || (acid_to_ph_57 > 0.1))) {
      C1 = acid_to_ph_57 / (di_ph - 5.7);
  } else {
      // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
      switch (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;
      }
  }
  return C1;
 }

 function ProtonDeficit(pHZ) {

  var i, C1, x, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal'));
  // proton deficit for the grist
  if (( $('#fermentableGrid').jqxGrid('getrows'))) {
   for (i = 0; i < dataRecord.fermentables.length; i++) {
    row = dataRecord.fermentables[i];
    if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt
     C1 = BufferCapacity(row.f_di_ph, row.f_acid_to_ph_57, row.f_color, row.f_graintype);
     x = C1 * (pHZ - row.f_di_ph);   // AcidRequired(ZpH)
     Result += x * row.f_amount;
    }
   }
  } else {
   error_count++;
   if (error_count < 5)
    console.log('ProtonDeficit(' + pHZ + ') invalid grist, return ' + Result);
  }
  return Result;
 }

 function MashpH() {

  var n = 0, pH = 5.4, deltapH = 0.001, deltapd = 0.1, 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);
  }
  pH = Round(pH, 6);
  //console.log('MashpH() n: ' + n + ' pH: ' + pH);
  return pH;
 }

 function calcWater() {

  /* Can be called during loading and building the screens */
  if (! data_loaded) {
   console.log('calcWater() failsave');
   return;
  }

  var liters = 0,
  calcium = 0,
  magnesium = 0,
  sodium = 0,
  total_alkalinity = 0,
  chloride = 0,
  sulfate = 0,
  ph = 0,
  RA = 0,
  frac = 0,
  TpH = 0,
  protonDeficit = 0,
  AT, /*BT,*/
  r1d, r2d, f1d, f2d, f3d,
  deltapH, deltapd, pd, n,
  Res;

  if (dataRecord.w1_name == '') {
   return;
  }

  // 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;
  }
  var bicarbonate = total_alkalinity * 1.22;

  /* Save mixed water ions for later */
  var wg_calcium = calcium;
  var wg_sodium = sodium;
  var wg_total_alkalinity = total_alkalinity;
  var wg_chloride = chloride;
  var wg_sulfate = sulfate;
  var wg_bicarbonate = bicarbonate;

  $('#wg_amount').val(liters);
  $('#wg_calcium').val(Round(calcium, 1));
  $('#wg_magnesium').val(Round(magnesium, 1));
  $('#wg_sodium').val(Round(sodium, 1));
  $('#wg_bicarbonate').val(Round(bicarbonate, 1));
  $('#wg_total_alkalinity').val(Round(total_alkalinity, 1));
  $('#wg_chloride').val(Round(chloride, 1));
  $('#wg_sulfate').val(Round(sulfate, 1));
  $('#wg_ph').val(Round(ph, 2));

  var mash_ph = Round(MashpH(), 3);
  console.log('Distilled water mash pH: ' + mash_ph);

  /* Calculate Salt additions */
  if (liters > 0) {
   calcium += (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 +
    parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 +
    parseFloat($('#wa_caco3').jqxNumberInput('decimal')) * MMCa / MMCaCO3 * 1000) / liters;
   magnesium += (parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000 +
    parseFloat($('#wa_mgcl2').jqxNumberInput('decimal')) * MMMg / MMMgCl2 * 1000) / liters;
   sodium += (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 +
    parseFloat($('#wa_nahco3').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters;
   sulfate += (parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 +
    parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / liters;
   chloride += (2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 +
    parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000 +
    parseFloat($('#wa_mgcl2').jqxNumberInput('decimal')) * MMCl / MMMgCl2 * 1000) / liters;
   bicarbonate += (parseFloat($('#wa_nahco3').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000 +
    parseFloat($('#wa_caco3').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3 * 1000) / liters;
  }

  if (dataRecord.wa_acid_name < 0 || dataRecord,wa_acid_name >= AcidTypeData.length) {
   $('#wa_acid_name').val(0);
   dataRecord.wa_acid_name = 0;
   dataRecord.wa_acid_perc = AcidTypeData[0].AcidPrc;
   $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc);
  }
  if (last_acid == '')
   last_acid = AcidTypeData[dataRecord.wa_acid_name].nl;

  if (parseFloat(dataRecord.wa_acid_perc) == 0) {
   dataRecord.wa_acid_perc = AcidTypeData[AT].AcidPrc;
   $('#wa_acid_perc').val(AcidTypeData[AT].AcidPrc);
  }

  AT = dataRecord.wa_acid_name;

  /* Note that the next calculations do not correct the pH change by the added salts.
     This pH change is at most 0.1 pH and is a minor difference in Acid amount. */

  if (dataRecord.calc_acid) {
   /* Auto calculate pH */
   $('.c_mashph').show();
   TpH = parseFloat(dataRecord.mash_ph);
   protonDeficit = ProtonDeficit(TpH);
   //console.log('calc_acid tgt: ' + TpH + ' protonDeficit: ' + protonDeficit);
   if (protonDeficit > 0) { // Add acid
    frac = CalcFrac(TpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3);
    Acid = protonDeficit / frac;
    Acid *= AcidTypeData[AT].MolWt; // mg
    Acidmg = Acid;
    Acid = Acid / AcidTypeData[AT].AcidSG; // ml
    Acid = Round(Acid / (parseFloat(dataRecord.wa_acid_perc) / 100), 2); // ml
    console.log('Mash auto Acid final ml: ' + Acid);
    $('#wa_acid').val(Acid);
    setWaterAgent(AcidTypeData[AT].nl, Acid);

    bicarbonate = bicarbonate - protonDeficit * frac / liters;
    total_alkalinity = bicarbonate * 50 / 61;
   }
   ph = TpH;
   $('#wb_ph').val(Round(ph, 2));
   $('#est_mash_ph').val(Round(ph, 2));
  } else {
   /* Manual calculate pH */
   $('.c_mashph').hide();
   console.log('calc_acid no');
   pHa = Round(ph, 3); // Adjusted water pH
   // Then calculate the new pH with added acids and malts
   console.log('Mash pH: ' + pHa);
   Acid  = AcidTypeData[AT].AcidSG * (parseFloat(dataRecord.wa_acid_perc) / 100); // ml
   Acid *= parseFloat($('#wa_acid').jqxNumberInput('decimal'));
   Acid /= AcidTypeData[AT].MolWt;  // mg
   Acidmg = Acid;

   //find the pH where the protondeficit = protondeficit by the acid
   frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3);
   protonDeficit = Round(Acid * frac, 3);
   //console.log('protonDeficit Acid: ' + protonDeficit + ' frac: ' + frac + ' pH: ' + pHa);

   deltapH = 0.001;
   deltapd = 0.1;
   pd = Round(ProtonDeficit(pHa), 6);
   n = 0;
   while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) {
     n++;
     if (pd < (protonDeficit - deltapd))
      pHa -= deltapH;
     else if (pd > (protonDeficit + deltapd))
      pHa += deltapH;
     frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].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(Round(ph, 2));
   $('#est_mash_ph').val(Round(ph, 2));
  }

  if ((AT == 3) && (liters > 0)) {        // Sulfuric / 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;
  }

  var BUGU = GetBUGU();
  $('#tgt_bu').val(Round(BUGU, 2));
  // From brouwhulp.
  if (BUGU < 0.32)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig en zoet</span>");
  else if (BUGU < 0.43)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig, zoet</span>");
  else if (BUGU < 0.52)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Evenwichtig</span>");
  else if (BUGU < 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>");

  // Sulfate to Chloride ratio (Palmer).
  var OptSO4Clratio = GetOptSO4Clratio();
  $('#tgt_so4_cl').val(Round(OptSO4Clratio, 1));
  if (OptSO4Clratio < 0.4)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Te moutig</span>");
  else if (OptSO4Clratio < 0.6)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig</span>");
  else if (OptSO4Clratio < 0.8)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig</span>");
  else if (OptSO4Clratio < 1.5)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Gebalanceerd</span>");
  else if (OptSO4Clratio < 2.0)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Licht bitter</span>");
  else if (OptSO4Clratio < 4.0)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Bitter</span>");
  else if (OptSO4Clratio < 9.0)
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer bitter</span>");
  else
   $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Te bitter</span>");
  if (chloride > 0)
   RA = sulfate / chloride;
  else
   RA = 10;
  $('#got_so4_cl').val(Round(RA, 1));
  Res = 'normaal';
  if (RA < (0.8 * OptSO4Clratio))
   Res = 'laag';
  else if (RA > (1.2 * OptSO4Clratio))
   Res = 'hoog';
  setRangeIndicator('so4_cl', Res);

  $('#wb_calcium').val(Round(calcium, 1));
  $('#wb_magnesium').val(Round(magnesium, 1));
  $('#wb_sodium').val(Round(sodium, 1));
  $('#wb_sulfate').val(Round(sulfate, 1));
  $('#wb_chloride').val(Round(chloride, 1));
  $('#wb_bicarbonate').val(Round(bicarbonate, 1));
  $('#wb_total_alkalinity').val(Round(total_alkalinity, 1));

  if (calcium < 40) {
   setRangeIndicator('calcium', 'laag');
  } else if (calcium > 150) {
   setRangeIndicator('calcium', 'hoog');
  } else {
   setRangeIndicator('calcium', 'normaal');
  }
  if (magnesium < 5) {
   setRangeIndicator('magnesium', 'laag');
  } else if (magnesium > 40) {
   setRangeIndicator('magnesium', 'hoog');
  } else {
   setRangeIndicator('magnesium', 'normaal');
  }
  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 <= 150) {
   setRangeIndicator('chloride', 'normaal');
  } else {
   setRangeIndicator('chloride', 'hoog');
  }
  if (sulfate <= 50) {
   setRangeIndicator('sulfate', 'laag');
  } else if (sulfate <= 400) {
   setRangeIndicator('sulfate', 'normaal');
  } else {
   setRangeIndicator('sulfate', 'hoog');
  }
  // (cloride + sulfate) > 500 is too high
  if ((chloride + sulfate) > 500) {
   setRangeIndicator('chloride', 'hoog');
   setRangeIndicator('sulfate', 'hoog');
  }
  if (ph < 5.2) {
   setRangeIndicator('ph', 'laag');
  } else if (ph > 5.6) {
   setRangeIndicator('ph', 'hoog');
  } else {
   setRangeIndicator('ph', 'normaal');
  }
  if (bicarbonate > 250) {
   setRangeIndicator('bicarbonate', 'hoog');
  } else {
   setRangeIndicator('bicarbonate', 'normaal');
  }
  calcSparge();
  calcMiscs();
  calcSupplies();
 }

 function calcSparge() {

  /* Based on the work of ajDeLange. */
  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(dataRecord.wg_ph);
    Source_alkalinity = parseFloat(dataRecord.wg_total_alkalinity);
   } else {
    dataRecord.sparge_source = 0;
    $('#sparge_source').val(0);
   }
  }

  // Step 1: Compute the mole fractions of carbonic (f1) and carbonate(f3) at the source water pH
  var r1 = Math.pow(10, Source_pH - 6.35);
  var r2 = Math.pow(10, Source_pH - 10.33);
  var d = 1 + r1 + r1 * r2;
  var f1 = 1 / 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.35);
  var r243 = Math.pow(10, 4.3 - 10.33);
  var d43 = 1 + r143 + r143 * r243;
  var f143 = 1 / d43;
  var f343 = r143 * r243 / d43;

  //Step 4. Solve
  var Ct = Source_alkalinity / 50 / ((f143 - f1) + (f3 - f343));

  //Step 5. Compute mole fractions at desired pH
  var r1g = Math.pow(10, TargetpH - 6.35);
  var r2g = Math.pow(10, TargetpH - 10.33);
  var dg = 1 + r1g + r1g * r2g;
  var f1g = 1 / 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.

  //Step 8. Get the acid data.
  var AT = dataRecord.sparge_acid_type;
  if (AT < 0 || AT >= AcidTypeData.length) {
   AT = 0;
   dataRecord.sparge_acid_type = 0;
   $('#sparge_acid_type').val(0);
   dataRecord.sparge_acid_perc = AcidTypeData[0].AcidPrc;
   $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc);
  }
  var fract = CalcFrac(TargetpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].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 *= AcidTypeData[AT].MolWt; //mg

  //Step 11. Divide by Specific Gravity and Percentage to get the final ml.
  Acid = Acid / AcidTypeData[AT].AcidSG / (dataRecord.sparge_acid_perc / 100); //ml
  Acid *= dataRecord.sparge_volume; //ml acid total
  Acid = Round(Acid, 2);
  dataRecord.sparge_acid_amount = Acid / 1000;
  $('#sparge_acid_amount').val(Acid);
 }

 function calcFermentation() {
  var primary_svg, secondary_svg, final_svg, ABV;
  if (dataRecord.brew_fermenter_sg < 1.020)
   return;
  if ((dataRecord.primary_end_sg > 0.990) && (dataRecord.primary_end_sg < dataRecord.brew_fermenter_sg)) {
   primary_svg = Round(calc_svg(dataRecord.brew_fermenter_sg, dataRecord.primary_end_sg), 1);
   $('#primary_svg').val(primary_svg);
   if ((dataRecord.secondary_end_sg > 0.990) && (dataRecord.secondary_end_sg < dataRecord.brew_fermenter_sg)) {
    secondary_svg = Round(calc_svg(dataRecord.brew_fermenter_sg, dataRecord.secondary_end_sg), 1);
    $('#secondary_svg').val(secondary_svg);
    if ((dataRecord.fg > 0.990) && (dataRecord.fg < dataRecord.brew_fermenter_sg)) {
     final_svg = Round(calc_svg(dataRecord.brew_fermenter_sg, dataRecord.fg), 1);
     $('#final_svg').val(final_svg);
     ABV = Round(abvol(dataRecord.brew_fermenter_sg, dataRecord.fg), 2);
     $('#final_abv').val(ABV);
    }
   }
  }
 }

 function ResCO2(T) {
  var F = T * 1.8 + 32;
  return Round(3.0378 - 0.050062 * F + 0.00026555 * F * F, 6);
 }

 function CarbCO2toS(CO2, T, SFactor) {
  //var sugar = SFactor * (CO2 - ResCO2(CO2, T)) / 0.286;
  var sugar = Round(SFactor * (CO2 - ResCO2(T)) * 4.014094, 6);
  if (sugar < 0)
   sugar = 0;
  return Round(sugar, 3);
 }

 function GetPressure(CO2, T1, T2) {
  var P, V = CO2 - ResCO2(T1);
  V = CO2; // TODO: temp only total pressure, testing
  if (V < 0)
   return 0;
  P = -1.09145427669121 + 0.00800006989646477 * T2 + 0.000260276315484684 * T2 * T2 + 0.0215142075945119 * T2 * V +
      0.674996600795854 * V + -0.00471757220150754 * V * V;
  if (P < 0)
   P = 0;
  P = Round(P * 1.01325, 2); // atm to bar
  console.log("GetPressure(" + CO2 + ", " + T1 + ", " + T2 + ")  V:" + V + "  Bar: " + P + " ignored ResCO2: " + ResCO2(T1));
  return P;
 }

 function CarbCO2ToPressure(CO2, T) {
  return (CO2 - (-0.000005594056 * Math.pow(T, 4) + 0.000144357886 * Math.pow(T, 3) +
          0.000362999168 * T * T - 0.064872987645 * T + 1.641145175049)) /
         (0.00000498031 * Math.pow(T, 4) - 0.00024358267 * Math.pow(T, 3) + 0.00385867329 * T * T - 0.05671206825 * T + 1.53801423376);
 }

 function calcCarbonation() {

  var TSec, ABV, bvol, balc, babv, mvol, malc, tvol, talc, i, row, SFactor, pvol, pabv, Pressure, kabv;

  TSec = dataRecord.secondary_temp;
  if (TSec < 1)
   TSec = dataRecord.primary_end_temp;
  if (TSec < 1)
   TSec = 18;

  if (dataRecord.fg == 0.000)
   ABV = abvol(dataRecord.brew_fermenter_sg, parseFloat($('#est_fg').jqxNumberInput('decimal')));
  else
   ABV = abvol(dataRecord.brew_fermenter_sg, dataRecord.fg);

  /* Calculate new volume and alcohol. */
  bvol = dataRecord.package_volume - (ABV * dataRecord.package_volume) / 100;
  balc = dataRecord.package_volume - bvol;
  mvol = dataRecord.package_infuse_amount - (dataRecord.package_infuse_abv * dataRecord.package_infuse_amount) / 100;
  malc = dataRecord.package_infuse_amount - mvol;
  talc = balc + malc;
  tvol = bvol + mvol;
  ABV = Round(talc / (tvol + talc) * 100, 2);
  dataRecord.package_abv = ABV;
  $('#package_abv').val(ABV);

  //console.log("calcCarbonation() TSec:"+TSec+"  ABV:"+ABV);
  if (!(rows = $('#fermentableGrid').jqxGrid('getrows'))) {
   return;
  }

  // Bottles
  dataRecord.bottle_priming_amount = 0;
  dataRecord.bottle_priming_total = 0;
  for (i = 0; i < rows.length; i++) {
   row = rows[i];
   if (row.f_added == 4) {
    SFactor = 1 / ((row.f_yield / 100) * (1 - row.f_moisture / 100));
    dataRecord.bottle_priming_amount = CarbCO2toS(dataRecord.bottle_carbonation, TSec, SFactor);
    dataRecord.bottle_priming_total = Round(dataRecord.bottle_amount * dataRecord.bottle_priming_amount, 2);
    $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', dataRecord.bottle_priming_total / 1000);
   }
  }
  $('#bottle_priming_amount').val(Round(dataRecord.bottle_priming_amount, 1));
  $('#bottle_priming_total').val(dataRecord.bottle_priming_total);
  pabv = ABV + dataRecord.bottle_priming_amount * 0.47 / 7.907;
  pvol = dataRecord.bottle_amount - (pabv * dataRecord.bottle_amount) / 100;
  talc = dataRecord.bottle_amount - pvol;
  tvol = pvol + dataRecord.bottle_priming_water;
  babv = Round(talc / (tvol + talc) * 100, 2);
  //console.log("bottle pabv:"+pabv+" pvol:"+pvol+" wvol:"+dataRecord.bottle_priming_water+" tvol:"+tvol+" talc:"+talc+" abv:"+babv);
  $('#bottle_abv').val(babv);
  $('#bottle_pressure').val(GetPressure(dataRecord.bottle_carbonation, TSec, dataRecord.bottle_carbonation_temp));

  // Kegs
  Pressure = CarbCO2ToPressure(dataRecord.keg_carbonation, dataRecord.keg_carbonation_temp);
  if (Pressure < 0)
   Pressure = 0;
  dataRecord.keg_pressure = Pressure;
  $('#keg_pressure').val(Round(Pressure, 1));

  dataRecord.keg_priming_amount = 0;
  dataRecord.keg_priming_total = 0;
  if (!dataRecord.keg_forced_carb) {
   for (i = 0; i < rows.length; i++) {
    row = rows[i];
    if (row.f_added == 5) {
     SFactor = 1 / ((row.f_yield / 100) * (1 - row.f_moisture / 100));
     dataRecord.keg_priming_amount = CarbCO2toS(dataRecord.keg_carbonation, TSec, SFactor);
     dataRecord.keg_priming_total = Round(dataRecord.keg_amount * dataRecord.keg_priming_amount, 2);
     $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', dataRecord.keg_priming_total / 1000);
    }
   }
   $('#keg_priming_amount').val(Round(dataRecord.keg_priming_amount, 1));
   $('#keg_priming_total').val(dataRecord.keg_priming_total);
   pabv = ABV + dataRecord.keg_priming_amount * 0.47 / 7.907;
   pvol = dataRecord.keg_amount - (pabv * dataRecord.keg_amount) / 100;
   talc = dataRecord.keg_amount - pvol;
   tvol = pvol + dataRecord.keg_priming_water;
   kabv = Round(talc / (tvol + talc) * 100, 2);
   //console.log("kegs  pabv:"+pabv+" pvol:"+pvol+" wvol:"+dataRecord.keg_priming_water+" tvol:"+tvol+" talc:"+talc+" abv:"+kabv);
   $('#keg_abv').val(kabv);
  } else {
   $('#keg_priming_amount').val(0);
   $('#keg_priming_total').val(0);
   $('#keg_abv').val(ABV);
  }
 }

 function en_stage_equipment(state) {
  $('#equipmentSelect').jqxDropDownList({ disabled: (state) ? true:false });
  $('#Delete').jqxButton({ disabled: (state) ? true:false });
 }

 function en_stage_brewday(state) {
  $('#jqxTabs').jqxTabs((state) ? 'disableAt':'enableAt', 8);	// Brewday tab
 }

 function en_stage_afterbrew(state) {

  var onval = { disabled: (state) ? true:false };
  var spinstate = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 };
  var spinsmall = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 70:90 };

  $('#jqxTabs').jqxTabs((state) ? 'enableAt':'disableAt', 9);	// Fermentation tab
  $('#name').jqxInput(onval);
  $('#code').jqxInput(onval);
  $('#batch_size').jqxNumberInput(spinstate);
  $('#boil_size').jqxNumberInput(spinstate);
  $('#boil_time').jqxNumberInput(spinstate);
  $('#efficiency').jqxNumberInput(spinstate);
  $('#est_og').jqxNumberInput(spinstate);
  $('#type').jqxDropDownList(onval);
  $('#styleSelect').jqxDropDownList(onval);
  $('#color_method').jqxDropDownList(onval);
  $('#ibu_method').jqxDropDownList(onval);
  $('#mash_select').jqxDropDownList(onval);
  $('#w1_name').jqxDropDownList(onval);
  $('#w2_name').jqxDropDownList(onval);
  $('#w2_amount').jqxNumberInput(onval);
  $('#pr_name').jqxDropDownList(onval);
  $('#wa_cacl2').jqxNumberInput(spinstate);
  $('#wa_caso4').jqxNumberInput(spinstate);
  $('#wa_mgso4').jqxNumberInput(spinstate);
  $('#wa_nacl').jqxNumberInput(spinstate);
  $('#wa_mgcl2').jqxNumberInput(spinstate);
  $('#wa_nahco3').jqxNumberInput(spinstate);
  $('#wa_caco3').jqxNumberInput(spinstate);
  $('#mash_ph').jqxNumberInput(spinstate);
  $('#calc_acid').jqxCheckBox(onval);
  $('#wa_acid_name').jqxDropDownList(onval);
  $('#wa_acid').jqxNumberInput(spinstate);
  $('#wa_acid_perc').jqxNumberInput(spinsmall);
  $('#sparge_temp').jqxNumberInput(spinstate);
  $('#sparge_volume').jqxNumberInput(spinstate);
  $('#sparge_ph').jqxNumberInput(spinstate);
  $('#sparge_source').jqxDropDownList(onval);
  $('#sparge_acid_type').jqxDropDownList(onval);
  $('#sparge_acid_perc').jqxNumberInput(spinstate);
  $('#starter_type').jqxDropDownList(onval);
  $('#starter_try').jqxButton(onval);
  $('#starter_sg').jqxNumberInput(spinstate);
  $('#yeast_prod_date').jqxDateTimeInput(onval);
  $('#yeast_pitchrate').jqxNumberInput(spinstate);
  $('#but_pickpitchrate').jqxButton(onval);
 }

 function en_stage_afterprimary(istate) {

  var state = istate;
  var onval = { disabled: (state) ? true:false };
  var spinstate = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 };

  $('#brew_date_start').jqxDateTimeInput(onval);
  $('#brew_date_end').jqxDateTimeInput(onval);
  $('#brew_mash_ph').jqxNumberInput(spinstate);
  $('#brew_mash_sg').jqxNumberInput(spinstate);
  $('#brew_whirlpool9').jqxNumberInput(spinstate);
  $('#brew_cooling_to').jqxNumberInput(spinstate);
  $('#brew_whirlpool7').jqxNumberInput(spinstate);
  $('#brew_cooling_method').jqxDropDownList(onval);
  $('#brew_whirlpool6').jqxNumberInput(spinstate);
  $('#brew_cooling_time').jqxNumberInput(spinstate);
  $('#brew_sparge_ph').jqxNumberInput(spinstate);
  $('#brew_whirlpool2').jqxNumberInput(spinstate);
  $('#brew_aeration_type').jqxDropDownList(onval);
  $('#brew_fermenter_tcloss').jqxNumberInput(spinstate);
  $('#brew_aeration_time').jqxNumberInput(spinstate);
  $('#brew_fermenter_extrawater').jqxNumberInput(spinstate);
  $('#brew_aeration_speed').jqxNumberInput(spinstate);

  if (! state) {
   state = (dataRecord.boil_time == 0);
  }
  onval = { disabled: (state) ? true:false };
  spinstate = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 };
  $('#brew_preboil_ph').jqxNumberInput(spinstate);
  $('#brew_aboil_ph').jqxNumberInput(spinstate);
  $('#brew_preboil_sg').jqxNumberInput(spinstate);
  $('#brew_aboil_sg').jqxNumberInput(spinstate);
  $('#brew_preboil_volume').jqxNumberInput(spinstate);
  $('#brew_aboil_volume').jqxNumberInput(spinstate);
  $('#but_pre_boil').jqxButton(onval);
  $('#but_after_boil').jqxButton(onval);
 }

 function en_stage_tertiary(state) {
  $('#package_date').jqxDateTimeInput({ disabled: (state) ? false:true });
 }

 function en_stage_aftertertiary(state) {

  var spinstate = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 };

  $('#primary_start_temp').jqxNumberInput(spinstate);
  $('#primary_max_temp').jqxNumberInput(spinstate);
  $('#primary_end_temp').jqxNumberInput(spinstate);
  $('#primary_end_sg').jqxNumberInput(spinstate);
  $('#primary_end_brix').jqxNumberInput(spinstate);
  $('#primary_end_date').jqxDateTimeInput({ disabled: (state) ? true:false });
 }

 function en_stage_afterpackaging(state) {

  var onval = { disabled: (state) ? true:false };
  var spinstate = { spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 };

  $('#secondary_temp').jqxNumberInput(spinstate);
  $('#secondary_end_sg').jqxNumberInput(spinstate);
  $('#secondary_end_date').jqxDateTimeInput(onval);
  $('#secondary_end_brix').jqxNumberInput(spinstate);
  $('#tertiary_temp').jqxNumberInput(spinstate);
  $('#fg').jqxNumberInput(spinstate);
  $('#final_brix').jqxNumberInput(spinstate);
  $('#package_date').jqxDateTimeInput(onval);
  $('#package_volume').jqxNumberInput(spinstate);
  $('#package_infuse_amount').jqxNumberInput(spinstate);
  $('#package_infuse_abv').jqxNumberInput(spinstate);
  $('#package_infuse_notes').jqxInput(onval);
  $('#package_ph').jqxNumberInput(spinstate);
  $('#bottle_amount').jqxNumberInput(spinstate);
  $('#bottle_priming_water').jqxNumberInput(spinstate);
  $('#keg_priming_water').jqxNumberInput(spinstate);
  $('#keg_amount').jqxNumberInput(spinstate);
  $('#bottle_carbonation').jqxNumberInput(spinstate);
  $('#keg_carbonation').jqxNumberInput(spinstate);
  $('#bottle_priming_sugar').jqxDropDownList(onval);
  $('#keg_priming_sugar').jqxDropDownList(onval);
  $('#keg_forced_carb').jqxCheckBox(onval);
  $('#bottle_carbonation_temp').jqxNumberInput(spinstate);
  $('#keg_carbonation_temp').jqxNumberInput(spinstate);
 }

 function en_stage_b4taste(state) {
  $('#jqxTabs').jqxTabs((state) ? 'disableAt':'enableAt', 11); // Tasting tab
 }

 function en_stage_locked(state) {

  var onval = { disabled: (state) ? true:false };

  $('#taste_date').jqxDateTimeInput(onval);
  $('#taste_rate').jqxNumberInput({ spinButtons: (state) ? false:true, readOnly: (state) ? true:false, width: (state) ? 90:110 });
  $('#taste_color').jqxInput(onval);
  $('#taste_transparency').jqxInput(onval);
  $('#taste_head').jqxInput(onval);
  $('#taste_aroma').jqxInput(onval);
  $('#taste_taste').jqxInput(onval);
  $('#taste_aftertaste').jqxInput(onval);
  $('#taste_mouthfeel').jqxInput(onval);
  $('#taste_notes').jqxInput(onval);
  $('#notes').jqxInput(onval);
 }

 function calcStage() {

  var newstage = dataRecord.stage, d, date1, date2, date1_unixtime, date2_unixtime, timeDifference, timeDifferenceInDays;

  if (newstage == 0 && dataRecord.est_og > 1.005 && dataRecord.est_color > 3 && dataRecord.est_ibu > 3)
   newstage = 1;
  if (newstage == 1 && parseFloat($('#brew_date_start').val()) > 2000)
   newstage = 2;   // Brewday
  if (newstage == 2 && ($('#brew_date_start').val() == ''))
   newstage = 1;   // No brewday
  if (newstage == 2 && parseFloat($('#brew_date_end').val()) > 2000)
   newstage = 3;   // Primary
  if (newstage == 3 && parseFloat($('#primary_end_date').val()) > 2000)
   newstage = 4;   // Secondary
  if (newstage == 4 && parseFloat($('#secondary_end_date').val()) > 2000)
   newstage = 5;   // Tertiary
  if (newstage == 5 && parseFloat($('#package_date').val()) > 2000)
   newstage = 6;   // Package
  if (newstage >= 6 && newstage < 9) {
   d = new Date();
   date2 = $('#package_date').val();
   date2 = date2.split('-');
   // Now we convert the array to a Date object
   date1 = new Date(d.getFullYear(), d.getMonth(), d.getDate());
   date2 = new Date(date2[0], date2[1] - 1, date2[2]);
   // We use the getTime() method and get the unixtime
   date1_unixtime = parseInt(date1.getTime() / 1000);
   date2_unixtime = parseInt(date2.getTime() / 1000);
   // This is the calculated difference in seconds
   timeDifference = date1_unixtime - date2_unixtime;
   timeDifferenceInDays = timeDifference / 60 / 60 / 24;
   if (timeDifferenceInDays > 0) {                 // At least one day
    if (timeDifferenceInDays >= 42)         // 6 weeks
     newstage = 9;                   // Ready to taste
    else if (timeDifferenceInDays >= 14)    // 14 days
     newstage = 8;                   // Mature
    else
     newstage = 7;                   // Carbonation
   }
  }
  if (newstage == 9 && parseFloat($('#taste_date').val()) > 2000)
   newstage = 10;  // Ready

  if (newstage != dataRecord.stage) {
   console.log('calcStage() old: ' + dataRecord.stage + ' new: ' + newstage);
   dataRecord.stage = newstage;
  }

  /*
   * Set stage and enable or disable parts of the screens.
   */
  $('#stage').val(StageData[dataRecord.stage].nl);
  if (dataRecord.stage >= 10) {
   $('#locked').jqxCheckBox({ disabled: false });
  }

  /*
   * Enable or disable parts of the screens.
   */
  en_stage_equipment(dataRecord.stage > 1);		// When the brew is in progress or done
  en_stage_brewday(dataRecord.stage < 1);		// Planning, no ingredients
  en_stage_afterbrew(dataRecord.stage > 2);		// After the brew
  en_stage_afterprimary(dataRecord.stage > 3);		// Primary fermentation done
  en_stage_tertiary(dataRecord.stage == 5);		// Tertiary, allow packaging
  en_stage_aftertertiary(dataRecord.stage >= 5);	// After all fermentation steps
  en_stage_afterpackaging(dataRecord.stage >= 6);	// After packaging
  en_stage_b4taste(dataRecord.stage < 9);		// Taste when at least Mature.
  en_stage_locked(dataRecord.stage == 11);		// Locked.
 }

 function showStarter() {

  if (dataRecord.starter_enable) {
   $('#propagator').show();
   $('#starter_type').jqxDropDownList({ disabled: false });
   $('#starter_try').jqxButton({ disabled: false });
   $('#starter_sg').jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
  } else {
   $('#propagator').hide();
   $('#starter_type').jqxDropDownList({ disabled: true });
   $('#starter_try').jqxButton({ disabled: true });
   $('#starter_sg').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
  }
 }

 function calcInit() {

  calcMashEfficiency();
  calcEfficiencyBeforeBoil();
  calcEfficiencyAfterBoil();

  $('#starter_try').click(function() {
   $('#prop1_volume').val(0);
   $('#prop2_volume').val(0);
   $('#prop3_volume').val(0);
   $('#prop4_volume').val(0);
   dataRecord.prop1_volume = dataRecord.prop2_volume = dataRecord.prop3_volume = dataRecord.prop4_volume = 0;
   calcYeast();
  });
  $('#starter_type').on('change', function(event) {
   if (event.args) {
    dataRecord.starter_type = event.args.index;
    calcYeast();
   }
  });
  $('#starter_sg').on('change', function(event) {
   if (event.args) {
    dataRecord.starter_sg = event.args.value;
    calcYeast();
   }
  });
  $('#prop1_type').on('change', function(event) {
   if (event.args) {
    dataRecord.prop1_type = event.args.index;
    calcYeast();
   }
  });
  $('#prop1_volume').on('change', function(event) {
   if (event.args) {
    dataRecord.prop1_volume = event.args.value;
    calcYeast();
   }
  });
  $('#prop2_type').on('change', function(event) {
   if (event.args) {
    dataRecord.prop2_type = event.args.index;
    calcYeast();
   }
  });
  $('#prop2_volume').on('change', function(event) {
   if (event.args) {
    dataRecord.prop2_volume = event.args.value;
    calcYeast();
   }
  });
  $('#prop3_type').on('change', function(event) {
   if (event.args) {
    dataRecord.prop3_type = event.args.index;
    calcYeast();
   }
  });
  $('#prop3_volume').on('change', function(event) {
   if (event.args) {
    dataRecord.prop3_volume = event.args.value;
    calcYeast();
   }
  });
  $('#prop4_type').on('change', function(event) {
   if (event.args) {
    dataRecord.prop4_type = event.args.index;
    calcYeast();
   }
  });
  $('#prop4_volume').on('change', function(event) {
   if (event.args) {
    dataRecord.prop4_volume = event.args.value;
    calcYeast();
   }
  });

  $('#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 prevents 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_mgcl2').on('change', function(event) {
   if (event.args) {
    setWaterAgent('MgCl2', 0);
    setWaterAgent('MgCl2', event.args.value);
    calcWater();
   }
  });
  $('#wa_nahco3').on('change', function(event) {
   if (event.args) {
    setWaterAgent('NaHCO3', 0);
    setWaterAgent('NaHCO3', event.args.value);
    calcWater();
   }
  });
  $('#wa_caco3').on('change', function(event) {
   if (event.args) {
    setWaterAgent('CaCO3', 0);
    setWaterAgent('CaCO3', event.args.value);
    calcWater();
   }
  });
  $('#wa_acid_name').on('select', function(event) {
   if (event.args) {
    var index = event.args.index;
    console.log('wa_acid_name ' + index + ' last_acid: ' + last_acid);
    setWaterAgent(last_acid, 0);
    last_acid = AcidTypeData[index].nl;
    dataRecord.wa_acid_name = index;
    dataRecord.wa_acid_perc = AcidTypeData[index].AcidPrc;
    $('#wa_acid_perc').val(dataRecord.wa_acid_perc);
    calcWater();
    setWaterAgent(last_acid, parseFloat($('#wa_acid').jqxNumberInput('decimal')));
   }
  });
  $('#wa_acid').on('change', function(event) {
   var name = AcidTypeData[dataRecord.wa_acid_name].nl;
   setWaterAgent(name, parseFloat(event.args.value));
   calcWater();
  });
  $('#wa_acid_perc').on('change', function(event) {
   dataRecord.wa_acid_perc = parseFloat(event.args.value);
   calcWater();
  });

  $('#color_method').on('select', function(event) {
   dataRecord.color_method = event.args.index;
   calcFermentables();
  });
  $('#ibu_method').on('select', 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);
   $('#est_a_vol').val(event.args.value * 1.04);
   var evap = parseFloat($('#eq_evap_rate').jqxNumberInput('decimal')) * dataRecord.boil_time / 60;
   dataRecord.boil_size = parseFloat(event.args.value) + evap;
   var factor = parseFloat(event.args.value) / dataRecord.batch_size;
   $('#boil_size').val(Round(dataRecord.boil_size, 2));
   $('#est_pre_vol').val(Round(dataRecord.boil_size * 1.04, 2));
   dataRecord.sparge_volume *= factor;
   $('#sparge_volume').val(dataRecord.sparge_volume);
   $('#brew_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) {
   var new_time, old_time, new_evap;
   old_time = parseFloat(dataRecord.boil_time);
   new_time = parseFloat(event.args.value);
   console.log('boil_time change:' + new_time + ' old:' + old_time);
   new_evap = parseFloat($('#eq_evap_rate').jqxNumberInput('decimal')) * new_time / 60;
   dataRecord.boil_size = parseFloat(dataRecord.batch_size) + new_evap;
   dataRecord.boil_time = new_time;
   $('#est_pre_vol').val(Round(dataRecord.boil_size * 1.04, 2));
   $('#boil_size').val(Round(dataRecord.boil_size, 2));
   calcFermentables();
   calcIBUs();
   calcYeast();
   calcStage();
   kookTijd();
  });
  $('#efficiency').on('change', function(event) {
   var estog = parseFloat($('#est_og').jqxNumberInput('decimal'));
   dataRecord.efficiency = parseFloat(event.args.value);
   console.log('efficiency change:' + dataRecord.efficiency + ' est_og:' + estog);
   calcFermentablesFromOG(estog);     // Keep the OG
   calcFermentables();
   calcIBUs();
   calcYeast();
  });
  $('#est_og').on('change', function(event) {
   dataRecord.est_og = parseFloat(event.args.value);
   console.log('est_og change:' + dataRecord.est_og);
   $('#est_og2').val(dataRecord.est_og);
   calcFermentablesFromOG(dataRecord.est_og);      // Adjust fermentables amounts
   calcFermentables();                             // Update the recipe details
   calcIBUs();                                     // and the IBU's.
   calcMash();
   calcYeast();
  });
  $('#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);
   $('#brew_sparge_volume').val(dataRecord.sparge_volume);
   calcSparge();
  });
  $('#sparge_temp').on('change', function(event) {
   dataRecord.sparge_temp = parseFloat(event.args.value);
   $('#brew_sparge_temperature').val(dataRecord.sparge_temp);
  });
  $('#sparge_source').on('select', function(event) {
   if (event.args) {
    dataRecord.sparge_source = event.args.index;
    calcSparge();
   }
  });
  $('#sparge_acid_type').on('select', function(event) {
   if (event.args) {
    dataRecord.sparge_acid_type = event.args.index;
    console.log('new sparge_acid_type: ' + dataRecord.sparge_acid_type);
    dataRecord.sparge_acid_perc = AcidTypeData[event.args.index].AcidPrc;
    $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc);
    calcSparge();
   }
  });
  $('#sparge_acid_perc').on('change', function(event) {
   dataRecord.sparge_acid_perc = parseFloat(event.args.value);
   calcSparge();
  });

  calcFermentation();
  calcCarbonation();
  $('#package_volume').on('change', function(event) {
   var diff, tnew, told = dataRecord.package_volume + dataRecord.package_infuse_amount;
   dataRecord.package_volume = parseFloat(event.args.value);
   if (dataRecord.package_volume > dataRecord.brew_fermenter_volume) {
    dataRecord.package_volume = dataRecord.brew_fermenter_volume;
    $('#package_volume').val(dataRecord.package_volume);
   }
   tnew = dataRecord.package_volume + dataRecord.package_infuse_amount;
   diff = Round(tnew - told, 3);
   if (told > 0) {
    dataRecord.bottle_amount = Round(dataRecord.bottle_amount + ((dataRecord.bottle_amount / told) * diff), 3);
    dataRecord.keg_amount = Round(dataRecord.keg_amount + ((dataRecord.keg_amount / told) * diff), 3);
   } else {
    dataRecord.bottle_amount = tnew;
    dataRecord.keg_amount = 0;
   }
   $('#bottle_amount').val(parseFloat(dataRecord.bottle_amount));
   $('#keg_amount').val(parseFloat(dataRecord.keg_amount));
   calcCarbonation();
  });
  $('#package_infuse_amount').on('change', function(event) {
   var diff, tnew, told = dataRecord.package_volume + dataRecord.package_infuse_amount;
   dataRecord.package_infuse_amount = parseFloat(event.args.value);
   tnew = dataRecord.package_volume + dataRecord.package_infuse_amount;
   diff = Round(tnew - told, 3);
   if (told > 0) {
    dataRecord.bottle_amount = Round(dataRecord.bottle_amount + ((dataRecord.bottle_amount / told) * diff), 3);
    dataRecord.keg_amount = Round(dataRecord.keg_amount + ((dataRecord.keg_amount / told) * diff), 3);
   } else {
    dataRecord.bottle_amount = tnew;
    dataRecord.keg_amount = 0;
   }
   $('#bottle_amount').val(parseFloat(dataRecord.bottle_amount));
   $('#keg_amount').val(parseFloat(dataRecord.keg_amount));
   calcCarbonation();
  });
  $('#package_infuse_abv').on('change', function(event) {
   dataRecord.package_infuse_abv = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#bottle_amount').on('change', function(event) {
   var vtot, vnew = parseFloat(event.args.value);
   vtot = Round(dataRecord.package_volume + dataRecord.package_infuse_amount - dataRecord.keg_amount, 3);
   if (vnew > vtot)
    vnew = vtot;
   dataRecord.bottle_amount = Round(vnew, 3);
   $('#bottle_amount').val(parseFloat(dataRecord.bottle_amount));
   console.log('vtot:' + vtot + ' vnew:' + vnew + ' bottle:' + dataRecord.bottle_amount + ' keg:' + dataRecord.keg_amount);
   calcCarbonation();
  });
  $('#bottle_priming_water').on('change', function(event) {
   dataRecord.bottle_priming_water = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#keg_amount').on('change', function(event) {
   var vtot, vnew = parseFloat(event.args.value);
   vtot = Round(dataRecord.package_volume + dataRecord.package_infuse_amount - dataRecord.bottle_amount, 3);
   if (vnew > vtot)
    vnew = vtot;
   dataRecord.keg_amount = Round(vnew, 3);
   $('#keg_amount').val(parseFloat(dataRecord.keg_amount));
   console.log('vtot:' + vtot + ' vnew:' + vnew + ' bottle:' + dataRecord.bottle_amount + ' keg:' + dataRecord.keg_amount);
   calcCarbonation();
  });
  $('#keg_priming_water').on('change', function(event) {
   dataRecord.keg_priming_water = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#bottle_carbonation').on('change', function(event) {
   dataRecord.bottle_carbonation = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#bottle_carbonation_temp').on('change', function(event) {
   dataRecord.bottle_carbonation_temp = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#keg_carbonation').on('change', function(event) {
   dataRecord.keg_carbonation = parseFloat(event.args.value);
   calcCarbonation();
  });
  $('#keg_forced_carb').on('checked', function(event) {
   dataRecord.keg_forced_carb = 1;
   calcCarbonation();
  });
  $('#keg_forced_carb').on('unchecked', function(event) {
   dataRecord.keg_forced_carb = 0;
   calcCarbonation();
  });
  $('#keg_carbonation_temp').on('change', function(event) {
   dataRecord.keg_carbonation_temp = parseFloat(event.args.value);
   calcCarbonation();
  });

  $('#brew_fermenter_extrawater').on('change', function(event) {
   dataRecord.brew_fermenter_extrawater = parseFloat(event.args.value);
   calcFermentables();
   calcIBUs();
   calcYeast();
  });
  $('#brew_fermenter_tcloss').on('change', function(event) {
   dataRecord.brew_fermenter_tcloss = parseFloat(event.args.value);
   calcFermentables();
   calcIBUs();
   calcYeast();
  });
  $('#primary_end_sg').on('change', function(event) {
   dataRecord.primary_end_sg = parseFloat(event.args.value);
   calcFermentation();
  });
  $('#primary_end_brix').on('change', function(event) {
   var OBrix, FBrix, FG;
   if (dataRecord.brew_fermenter_sg >= 1.020) {
    OBrix = sg_to_plato(dataRecord.brew_fermenter_sg);
    FBrix = parseFloat(event.args.value);
    FG = brix_to_fg(OBrix, FBrix);
    if (FBrix > 0.05) {
     $('#primary_end_sg').val(FG);
     dataRecord.primary_end_sg = FG;
    }
    calcFermentation();
   }
  });
  $('#secondary_end_sg').on('change', function(event) {
   dataRecord.secondary_end_sg = parseFloat(event.args.value);
   calcFermentation();
  });
  $('#secondary_end_brix').on('change', function(event) {
   var OBrix, FBrix, FG;
   if (dataRecord.brew_fermenter_sg >= 1.020) {
    OBrix = sg_to_plato(dataRecord.brew_fermenter_sg);
    FBrix = parseFloat(event.args.value);
    FG = brix_to_fg(OBrix, FBrix);
    if (FBrix > 0.05) {
     $('#secondary_end_sg').val(FG);
     dataRecord.secondary_end_sg = FG;
    }
    calcFermentation();
   }
  });
  $('#final_brix').on('change', function(event) {
   var OBrix, FBrix, FG;
   if (dataRecord.brew_fermenter_sg >= 1.020) {
    OBrix = sg_to_plato(dataRecord.brew_fermenter_sg);
    FBrix = parseFloat(event.args.value);
    FG = brix_to_fg(OBrix, FBrix);
    if (FBrix > 0.05) {
     $('#fg').val(FG);
     dataRecord.fg = FG;
    }
    calcFermentation();
   }
  });
  $('#fg').on('change', function(event) {
   dataRecord.fg = parseFloat(event.args.value);
   calcFermentation();
  });
  $('#brew_whirlpool9').on('valueChanged', function(event) {
   dataRecord.brew_whirlpool9 = event.args.value;
   whirlpoolHops();
   calcIBUs();
  });
  $('#brew_whirlpool7').on('valueChanged', function(event) {
   dataRecord.brew_whirlpool7 = event.args.value;
   whirlpoolHops();
   calcIBUs();
  });
  $('#brew_whirlpool6').on('valueChanged', function(event) {
   dataRecord.brew_whirlpool6 = event.args.value;
   whirlpoolHops();
   calcIBUs();
  });
  $('#BLog').jqxButton({ disabled: (dataRecord.log_brew) ? false : true});
  $('#FLog').jqxButton({ disabled: (dataRecord.log_fermentation) ? false : true});
  $('#ILog').jqxButton({ disabled: (dataRecord.log_ispindel) ? false : true});
  $('#CLog').jqxButton({ disabled: (dataRecord.log_co2pressure) ? false : true});
 }

 $('#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 datarecord, index = event.args.index;
   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_min2').val(datarecord.carb_min);
   $('#st_carb_max').val(datarecord.carb_max);
   $('#st_carb_max2').val(datarecord.carb_max);
   $('#st_abv_min').val(datarecord.abv_min);
   $('#st_abv_max').val(datarecord.abv_max);
  }
 });

 // Equipemnt dropdown list
 $('#equipmentSelect').jqxDropDownList({
  placeHolder: 'Kies apparatuur:',
  theme: theme,
  source: equipmentlist,
  displayMember: 'name',
  width: 170,
  height: 23,
  dropDownWidth: 300,
  renderer: function(index, label, value) {
   var datarecord = equipmentlist.records[index];
   return datarecord.batch_size + ' liter ' + datarecord.name;
  }
 });
 $('#equipmentSelect').on('select', function(event) {
  if (event.args) {
   var datarecord, factor, index = event.args.index;
   datarecord = equipmentlist.records[index];
   factor = datarecord.batch_size / dataRecord.batch_size;
   $('#eq_name').val(datarecord.name);
   $('#eq_boil_size').val(datarecord.boil_size);
   dataRecord.boil_size = datarecord.boil_size;
   $('#boil_size').val(datarecord.boil_size);
   $('#eq_batch_size').val(datarecord.batch_size);
   dataRecord.batch_size = datarecord.batch_size;
   $('#batch_size').val(datarecord.batch_size);
   $('#est_a_vol').val(datarecord.batch_size * 1.04);
   $('#eq_tun_volume').val(datarecord.tun_volume);
   dataRecord.eq_tun_weight = datarecord.tun_weight;
   dataRecord.eq_tun_specific_heat = datarecord.tun_specific_heat;
   dataRecord.eq_tun_material = datarecord.tun_material;
   dataRecord.eq_tun_height = datarecord.tun_height / 100.0;
   $('#eq_top_up_water').val(datarecord.top_up_water);
   dataRecord.eq_trub_chiller_loss = datarecord.trub_chiller_loss;
   $('#eq_trub_chiller_loss').val(datarecord.trub_chiller_loss);
   $('#eq_evap_rate').val(datarecord.evap_rate);
   $('#eq_boil_time').val(datarecord.boil_time);
   dataRecord.eq_calc_boil_volume = datarecord.calc_boil_volume;
   $('#eq_top_up_kettle').val(datarecord.top_up_kettle);
   $('#eq_hop_utilization').val(datarecord.hop_utilization);
   $('#eq_notes').val(datarecord.notes);
   $('#eq_lauter_volume').val(datarecord.lauter_volume);
   dataRecord.eq_lauter_height = datarecord.lauter_height / 100.0;
   $('#eq_lauter_deadspace').val(datarecord.lauter_deadspace);
   $('#eq_kettle_volume').val(datarecord.kettle_volume);
   dataRecord.eq_kettle_height = datarecord.kettle_height / 100.0;
   $('#eq_mash_volume').val(datarecord.mash_volume);
   $('#eq_mash_max').val(datarecord.mash_max);
   dataRecord.eq_mash_max = datarecord.mash_max;
   $('#mash_max').val(datarecord.mash_max);
   $('#eq_efficiency').val(datarecord.efficiency);
   dataRecord.efficiency = datarecord.efficiency;
   $('#efficiency').val(datarecord.efficiency);

   dataRecord.sparge_volume = Math.round(datarecord.boil_size * 5) / 10;
   $('#sparge_volume').val(dataRecord.sparge_volume);
   $('#brew_sparge_volume').val(dataRecord.sparge_volume);
   $('#est_pre_vol').val(datarecord.boil_size * 1.04);
   calcFermentablesFromOG(parseFloat($('#est_og').jqxNumberInput('decimal')));     // Keep the OG
   adjustWaters(factor);
   calcFermentables();
   adjustHops(factor);
   adjustMiscs(factor);
   adjustYeasts(factor);
   calcIBUs();
   calcWater();
   calcSparge();
  }
 });

 function saveRecord(goback) {
  var row = {
   record: my_record,
   uuid: dataRecord.uuid,
   name: $('#name').val(),
   code: $('#code').val(),
   birth: $('#birth').val(),
   stage: dataRecord.stage,
   notes: $('#notes').val(),
   log_brew: dataRecord.log_brew,
   log_fermentation: dataRecord.log_fermentation,
   log_ispindel: dataRecord.log_ispindel,
   log_co2pressure: dataRecord.log_co2pressure,
   inventory_reduced: dataRecord.inventory_reduced,
   locked: dataRecord.locked,
   eq_name: $('#eq_name').val(),
   eq_boil_size: parseFloat($('#eq_boil_size').jqxNumberInput('decimal')),
   eq_batch_size: parseFloat($('#eq_batch_size').jqxNumberInput('decimal')),
   eq_tun_volume: parseFloat($('#eq_tun_volume').jqxNumberInput('decimal')),
   eq_tun_weight: dataRecord.eq_tun_weight,
   eq_tun_specific_heat: dataRecord.eq_tun_specific_heat,
   eq_tun_material: dataRecord.eq_tun_material,
   eq_tun_height: dataRecord.eq_tun_height,
   eq_top_up_water: parseFloat($('#eq_top_up_water').jqxNumberInput('decimal')),
   eq_trub_chiller_loss: parseFloat($('#eq_trub_chiller_loss').jqxNumberInput('decimal')),
   eq_evap_rate: parseFloat($('#eq_evap_rate').jqxNumberInput('decimal')),
   eq_boil_time: parseFloat($('#eq_boil_time').jqxNumberInput('decimal')),
   eq_calc_boil_volume: dataRecord.eq_calc_boil_volume,
   eq_top_up_kettle: parseFloat($('#eq_top_up_kettle').jqxNumberInput('decimal')),
   eq_hop_utilization: parseFloat($('#eq_hop_utilization').jqxNumberInput('decimal')),
   eq_notes: $('#eq_notes').val(),
   eq_lauter_volume: parseFloat($('#eq_lauter_volume').jqxNumberInput('decimal')),
   eq_lauter_height: dataRecord.eq_lauter_height,
   eq_lauter_deadspace: parseFloat($('#eq_lauter_deadspace').jqxNumberInput('decimal')),
   eq_kettle_volume: parseFloat($('#eq_kettle_volume').jqxNumberInput('decimal')),
   eq_kettle_height: dataRecord.eq_kettle_height,
   eq_mash_volume: parseFloat($('#eq_mash_volume').jqxNumberInput('decimal')),
   eq_mash_max: parseFloat($('#eq_mash_max').jqxNumberInput('decimal')),
   eq_efficiency: parseFloat($('#eq_efficiency').jqxNumberInput('decimal')),
   brew_date_start: $('#brew_date_start').val(),
   brew_mash_ph: parseFloat($('#brew_mash_ph').jqxNumberInput('decimal')),
   brew_mash_sg: parseFloat($('#brew_mash_sg').jqxNumberInput('decimal')),
   brew_mash_efficiency: parseFloat($('#brew_mash_efficiency').jqxNumberInput('decimal')),
   brew_sparge_est: parseFloat($('#brew_sparge_est').jqxNumberInput('decimal')),
   brew_sparge_ph: parseFloat($('#brew_sparge_ph').jqxNumberInput('decimal')),
   brew_preboil_volume: parseFloat($('#brew_preboil_volume').jqxNumberInput('decimal')),
   brew_preboil_sg: parseFloat($('#brew_preboil_sg').jqxNumberInput('decimal')),
   brew_preboil_ph: parseFloat($('#brew_preboil_ph').jqxNumberInput('decimal')),
   brew_preboil_efficiency: parseFloat($('#brew_preboil_efficiency').jqxNumberInput('decimal')),
   brew_aboil_volume: parseFloat($('#brew_aboil_volume').jqxNumberInput('decimal')),
   brew_aboil_sg: parseFloat($('#brew_aboil_sg').jqxNumberInput('decimal')),
   brew_aboil_ph: parseFloat($('#brew_aboil_ph').jqxNumberInput('decimal')),
   brew_aboil_efficiency: parseFloat($('#brew_aboil_efficiency').jqxNumberInput('decimal')),
   brew_cooling_method: $('#brew_cooling_method').val(),
   brew_cooling_time: parseFloat($('#brew_cooling_time').jqxNumberInput('decimal')),
   brew_cooling_to: parseFloat($('#brew_cooling_to').jqxNumberInput('decimal')),
   brew_whirlpool9: parseFloat($('#brew_whirlpool9').jqxNumberInput('decimal')),
   brew_whirlpool7: parseFloat($('#brew_whirlpool7').jqxNumberInput('decimal')),
   brew_whirlpool6: parseFloat($('#brew_whirlpool6').jqxNumberInput('decimal')),
   brew_whirlpool2: parseFloat($('#brew_whirlpool2').jqxNumberInput('decimal')),
   brew_fermenter_volume: parseFloat($('#brew_fermenter_volume').jqxNumberInput('decimal')),
   brew_fermenter_extrawater: parseFloat($('#brew_fermenter_extrawater').jqxNumberInput('decimal')),
   brew_fermenter_tcloss: parseFloat($('#brew_fermenter_tcloss').jqxNumberInput('decimal')),
   brew_aeration_time: parseFloat($('#brew_aeration_time').jqxNumberInput('decimal')),
   brew_aeration_speed: parseFloat($('#brew_aeration_speed').jqxNumberInput('decimal')),
   brew_aeration_type: $('#brew_aeration_type').val(),
   brew_fermenter_sg: parseFloat($('#brew_fermenter_sg').jqxNumberInput('decimal')),
   brew_fermenter_ibu: parseFloat($('#brew_fermenter_ibu').jqxNumberInput('decimal')),
   brew_fermenter_color: parseFloat($('#brew_fermenter_color').jqxNumberInput('decimal')),
   brew_date_end: $('#brew_date_end').val(),
   og: dataRecord.og,
   fg: parseFloat($('#fg').jqxNumberInput('decimal')),
   primary_start_temp: parseFloat($('#primary_start_temp').jqxNumberInput('decimal')),
   primary_max_temp: parseFloat($('#primary_max_temp').jqxNumberInput('decimal')),
   primary_end_temp: parseFloat($('#primary_end_temp').jqxNumberInput('decimal')),
   primary_end_sg: parseFloat($('#primary_end_sg').jqxNumberInput('decimal')),
   primary_end_date: $('#primary_end_date').val(),
   secondary_temp: parseFloat($('#secondary_temp').jqxNumberInput('decimal')),
   secondary_end_sg: parseFloat($('#secondary_end_sg').jqxNumberInput('decimal')),
   secondary_end_date: $('#secondary_end_date').val(),
   tertiary_temp: parseFloat($('#tertiary_temp').jqxNumberInput('decimal')),
   package_date: $('#package_date').val(),
   package_volume: parseFloat($('#package_volume').jqxNumberInput('decimal')),
   package_infuse_amount: parseFloat($('#package_infuse_amount').jqxNumberInput('decimal')),
   package_infuse_abv: parseFloat($('#package_infuse_abv').jqxNumberInput('decimal')),
   package_infuse_notes: $('#package_infuse_notes').val(),
   package_abv: parseFloat($('#package_abv').jqxNumberInput('decimal')),
   package_ph: parseFloat($('#package_ph').jqxNumberInput('decimal')),
   bottle_amount: parseFloat($('#bottle_amount').jqxNumberInput('decimal')),
   bottle_carbonation: parseFloat($('#bottle_carbonation').jqxNumberInput('decimal')),
   bottle_priming_water: parseFloat($('#bottle_priming_water').jqxNumberInput('decimal')),
   bottle_priming_amount: parseFloat($('#bottle_priming_amount').jqxNumberInput('decimal')),
   bottle_carbonation_temp: parseFloat($('#bottle_carbonation_temp').jqxNumberInput('decimal')),
   keg_amount: parseFloat($('#keg_amount').jqxNumberInput('decimal')),
   keg_carbonation: parseFloat($('#keg_carbonation').jqxNumberInput('decimal')),
   keg_priming_water: parseFloat($('#keg_priming_water').jqxNumberInput('decimal')),
   keg_priming_amount: parseFloat($('#keg_priming_amount').jqxNumberInput('decimal')),
   keg_carbonation_temp: parseFloat($('#keg_carbonation_temp').jqxNumberInput('decimal')),
   keg_forced_carb: dataRecord.keg_forced_carb,
   keg_pressure: parseFloat($('#keg_pressure').jqxNumberInput('decimal')),
   taste_notes: $('#taste_notes').val(),
   taste_rate: parseFloat($('#taste_rate').jqxNumberInput('decimal')),
   taste_date: $('#taste_date').val(),
   taste_color: $('#taste_color').val(),
   taste_transparency: $('#taste_transparency').val(),
   taste_head: $('#taste_head').val(),
   taste_aroma: $('#taste_aroma').val(),
   taste_taste: $('#taste_taste').val(),
   taste_mouthfeel: $('#taste_mouthfeel').val(),
   taste_aftertaste: $('#taste_aftertaste').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: $('#st_category_number').val(),
   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_og3: parseFloat($('#est_og3').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: parseInt($('#wa_acid_name').val()),
   wa_acid_perc: parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')),
   wa_base_name: 0,
   starter_enable: dataRecord.starter_enable,
   starter_type: $('#starter_type').val(),
   starter_sg: parseFloat($('#starter_sg').jqxNumberInput('decimal')),
   starter_viability: parseFloat($('#starter_viability').jqxNumberInput('decimal')),
   yeast_prod_date: $('#yeast_prod_date').val(),
   yeast_pitchrate: parseFloat($('#yeast_pitchrate').jqxNumberInput('decimal')),
   prop1_type: $('#prop1_type').val(),
   prop1_volume: parseFloat($('#prop1_volume').jqxNumberInput('decimal')),
   prop2_type: $('#prop2_type').val(),
   prop2_volume: parseFloat($('#prop2_volume').jqxNumberInput('decimal')),
   prop3_type: $('#prop3_type').val(),
   prop3_volume: parseFloat($('#prop3_volume').jqxNumberInput('decimal')),
   prop4_type: $('#prop4_type').val(),
   prop4_volume: parseFloat($('#prop4_volume').jqxNumberInput('decimal')),
   divide_type: dataRecord.divide_type,
   divide_size: dataRecord.divide_size,
   divide_factor: dataRecord.divide_factor,
   divide_parts: dataRecord.divide_parts,
   divide_part: dataRecord.divide_part,
   fermentables: $('#fermentableGrid').jqxGrid('getrows'),
   hops: $('#hopGrid').jqxGrid('getrows'),
   miscs: $('#miscGrid').jqxGrid('getrows'),
   yeasts: $('#yeastGrid').jqxGrid('getrows'),
   mashs: $('#mashGrid').jqxGrid('getrows')
  },
  data = 'update=true&' + $.param(row);
  $.ajax({
   dataType: 'json',
   url: url,
   cache: false,
   data: data,
   async: false,
   type: 'POST',
   success: function(data) {
    if (data.error) {
     console.log('saveRecord(' + goback + ') error ' + data.msg);
     alert('SQL fout: ' + data.msg);
    } else {
     console.log('saveRecord(' + goback + ') success');
    }
    if (goback)
     window.location.href = my_return;
    else
     window.location.href =
        'prod_export.php?record=' + my_record + '&return=' + my_return + '&select=' + my_select + '&code=' + dataRecord.code + '&name=' + dataRecord.name +
        '&stage=' + dataRecord.stage + '&split=' + dataRecord.divide_type;
   },
   error: function(jqXHR, textStatus, errorThrown) {
    console.log('saveRecord() ' + textStatus);
   }
  });
 };

 // initialize the input fields.
 // Tab 1, Algemeen
 $('#name').jqxTooltip({ content: 'De naam voor dit product.' });
 $('#code').jqxTooltip({ content: 'Product code nummer.' });
 $('#birth').jqxTooltip({ content: 'De ontwerp datum van dit product.' });
 $('#stage').jqxTooltip({ content: 'De productie fase van dit product.' });
 $('#divide_batch').jqxTooltip({ content: 'Het aantal extra gesplitste batches.' });
 $('#divide_type').jqxTooltip({ content: 'Het splitsing moment in het productie proces.' });
 $('#notes').jqxTooltip({ content: 'De uitgebreide opmerkingen over dit product.' });
 $('#type').jqxTooltip({ content: 'Het brouw type van dit recept.' });
 $('#efficiency').jqxTooltip({ content: 'Het rendement van maischen en koken.' });
 $('#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.' });
 $('#st_guide').jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
 $('#st_name').jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
 $('#st_letter').jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
 $('#st_letter').jqxInput({ theme: theme, width: 90, height: 23 });
 $('#st_type').jqxTooltip({ content: 'Het bierstijl type.'});
 $('#st_category').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
 $('#st_category_number').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
 $('#est_og').jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
 $('#st_og_min').jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'});
 $('#st_og_max').jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'});
 $('#est_fg').jqxTooltip({ content: 'Het verwachte eind SG. Dit wordt automatisch berekend.' });
 $('#st_fg_min').jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'});
 $('#st_fg_max').jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'});
 $('#est_abv').jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
 $('#st_abv_min').jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
 $('#st_abv_max').jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
 $('#est_color').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
 $('#st_color_min').jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
 $('#st_color_max').jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
 $('#est_ibu').jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
 $('#st_ibu_min').jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
 $('#st_ibu_max').jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
 $('#kcal').jqxTooltip({ content: 'Energie-inhoud in kcal/liter.' });
 $('#est_carb').jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' });
 $('#st_carb_min').jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
 $('#st_carb_max').jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});

 $('#name').jqxInput({ theme: theme, width: 640, height: 23 });
 $('#code, #stage').jqxInput({ theme: theme, width: 100, height: 23 });
 $('#locked').jqxCheckBox({ theme: theme, width: 120, height: 23, disabled: true });
 $('#locked').on('checked', function(event) {
  if (dataRecord.stage >= 10) {
   dataRecord.locked = 1;
   dataRecord.stage = 11;
   calcStage();
  }
 });
 $('#locked').on('unchecked', function(event) {
  if (dataRecord.stage >= 10) {
   dataRecord.locked = 0;
   dataRecord.stage = 10;
   calcStage();
  }
 });
 $('#birth,#divide_batch,#divide_type').jqxInput({ theme: theme, width: 120, height: 23 });
 $('#notes').jqxInput({ theme: theme, width: 960, height: 100 });
 $('#type').jqxDropDownList({
  theme: theme,
  source: RecipeTypeAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });
 $('#efficiency').jqxNumberInput(Perc1dec);
 $('#batch_size').jqxNumberInput(Spin1dec);
 $('#batch_size').jqxNumberInput({ min: 4 });
 $('#boil_time').jqxNumberInput(PosInt);
 $('#boil_time').jqxNumberInput({ min: 0, max: 360 });
 $('#boil_size').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 90, height: 23, decimalDigits: 2, readOnly: true });
 $('#st_guide,#st_name,#st_type,#st_category').jqxInput({ theme: theme, width: 250, height: 23 });
 $('#est_og').jqxNumberInput(SGopts);
 $('#est_fg').jqxNumberInput(Show3dec);
 $('#st_og_min,#st_og_max,#st_fg_min,#st_fg_max').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
 $('#est_ibu,#est_color').jqxNumberInput(Show0dec);
 $('#color_method').jqxDropDownList({
  theme: theme,
  source: ColorMethodAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });
 $('#st_color_min,#st_color_max,#st_category_number,#st_ibu_min,#st_ibu_max,#kcal').jqxNumberInput(Smal0dec);
 $('#ibu_method').jqxDropDownList({
  theme: theme,
  source: IBUmethodAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true,
  dropDownVerticalAlignment: 'top'
 });
 $('#est_abv,#st_abv_min,#st_abv_max,#est_carb,#st_carb_min,#st_carb_max').jqxNumberInput(Smal1dec);

 // Tab 2, Equipment
 $('#eq_name').jqxTooltip({ content: 'De naam van deze brouw apparatuur.' });
 $('#eq_boil_size').jqxTooltip({ content: 'Normaal kook volume in liters' });
 $('#eq_batch_size').jqxTooltip({ content: 'Berekende batch grootte in liters aan het eind van de kook.' });
 $('#eq_tun_volume').jqxTooltip({ content: 'Maisch ketel volume.' });
 $('#eq_top_up_water').jqxTooltip({ content: 'Extra water in het gistvat.' });
 $('#eq_trub_chiller_loss').jqxTooltip({ content: 'Standaard verlies bij het overbrengen naar het gistvat.' });
 $('#eq_evap_rate').jqxTooltip({ content: 'Verdamping in liters per uur.' });
 $('#eq_boil_time').jqxTooltip({ content: 'Normale kooktijd in minuten, 0 voor no-boil recepten.' });
 $('#eq_top_up_kettle').jqxTooltip({ content: 'Extra water toevoegen tijdens de kook.' });
 $('#eq_hop_utilization').jqxTooltip({ content: '100% voor kleine installaties, hoger voor grote brouwerijen.' });
 $('#eq_notes').jqxTooltip({ content: 'Opmerkingen over deze apparatuur.' });
 $('#eq_lauter_volume').jqxTooltip({ content: 'Filterkuip volume.' });
 $('#eq_lauter_deadspace').jqxTooltip({ content: 'Filterkuip verlies in liters.' });
 $('#eq_kettle_volume').jqxTooltip({ content: 'Kook ketel volume in liters.' });
 $('#eq_mash_volume').jqxTooltip({ content: 'Maisch water voor de eerste stap.' });
 $('#eq_mash_max').jqxTooltip({ content: 'De maximale moutstort in Kg.' });
 $('#eq_efficiency').jqxTooltip({ content: 'Gemiddeld brouwzaal rendement.' });

 $('#eq_name').jqxInput({ theme: theme, width: 250, height: 23 });
 $('#eq_evap_rate').jqxNumberInput(Show2dec);
 $('#eq_boil_time,#eq_hop_utilization').jqxNumberInput(Show0dec);
 $('#eq_notes').jqxInput({ theme: theme, width: 960, height: 200 });
 $('#eq_boil_size,#eq_batch_size,#eq_tun_volume,#eq_top_up_water,#eq_trub_chiller_loss,#eq_top_up_kettle').jqxNumberInput(Show1dec);
 $('#eq_lauter_volume,#eq_lauter_deadspace,#eq_kettle_volume,#eq_mash_volume,#eq_mash_max,#eq_efficiency').jqxNumberInput(Show1dec);

 // Tab 3, Fermentables
 $('#est_color2').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
 $('#est_og2').jqxTooltip({ content: 'Het geschatte begin SG van dit product.' });
 $('#mash_kg').jqxTooltip({ content: 'Het gewicht van alle mouten in de maisch.' });

 $('#est_color2').jqxNumberInput(Show0dec);
 $('#est_og2,#mash_kg').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'; }
 });
 $('#FermentableReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#FermentableReady').click(function() {
  var row, rowID = $('#fermentableGrid').jqxGrid('getrowid', fermentableRow);
  console.log('FermentableReady row:' + fermentableRow + ' ID:' + rowID);
  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);
  calcPercentages();
  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 datarecord, index = event.args.index;
   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) {
   calcPercentages();
   calcFermentables();
   calcIBUs();
   calcMash();
  }
 });
 $('#wf_percentage').jqxNumberInput(Perc1dec);
 $('#wf_percentage').on('change', function(event) {
  var newvalue, rowscount, rowdata, diff, tw, damount, namount, nw, newperc,
  oldvalue = Math.round(fermentableData.f_percentage * 10) / 10.0;
  newvalue = event.args.value;
  console.log('percentage changed: ' + newvalue + ' old: ' + oldvalue);
  fermentableData.f_percent = newvalue;
  rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
  if ((oldvalue != newvalue) && (rowscount > 1)) {
   rowdata = $('#fermentableGrid').jqxGrid('getrowdata', fermentableRow);
   if (rowdata.f_adjust_to_total_100) {
    $('#wf_percentage').val(oldvalue);
   } else {
    diff = newvalue - oldvalue;
    tw = 0;     // total weight
    for (i = 0; i < rowscount; i++) {
     rowdata = $('#fermentableGrid').jqxGrid('getrowdata', i);
     if (rowdata.f_added < 4)
      tw += Math.round(rowdata.f_amount * 1000) / 1000;
    }
    tw = Math.round(tw * 1000) / 1000;
    if (to_100) {
     // Adjust this row and the 100% row.
     damount = Math.round(tw * diff * 10) / 1000;
     rowdata = $('#fermentableGrid').jqxGrid('getrowdata', fermentableRow);
     namount = Round(rowdata.f_amount + damount, 3);
     $('#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++) {
      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();
     calcIBUs();
     calcMash();
    } else {
     // Adjust all the rows.
     nw = tw * diff / 100;
     for (i = 0; i < rowscount; i++) {
      rowdata = $('#fermentableGrid').jqxGrid('getrowdata', i);
      if (rowdata.f_added < 4) {
       if (i == fermentableRow) {
        namount = Math.round((rowdata.f_amount + nw) * 1000) / 1000;
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', namount);
  //    $('#wf_amount').val(namount); // Will crash the script.
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', newvalue);
       } else {
        namount = Math.round((rowdata.f_amount - (nw / (rowscount - 1))) * 1000) / 1000;
        newperc = Math.round((namount / tw) * 1000) / 10.0;
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', namount);
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', newperc);
       }
      } else {
       $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', 0);
      }
     }
     calcFermentables();
     calcIBUs();
     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 i, rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
    for (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 4, Hops
 $('#est_ibu2').jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
 $('#est_ibu2').jqxNumberInput(Smal0dec);
 $('#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';
  }
 });
 $('#HopReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#HopReady').click(function() {
  var row, rowID = $('#hopGrid').jqxGrid('getrowid', hopRow);
  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 + ' / ' + HopFormData[datarecord.form].nl + ' (' + datarecord.alpha + ' % &alpha;)';
  }
 });
 $('#wh_select').on('select', function(event) {
  if (event.args) {
   var datarecord, index = event.args.index;
   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 ibu, amount = parseFloat(event.args.value) / 1000;
  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(),
   dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6
  );
  hopData.h_amount = amount;
  $('#wh_ibu').val(ibu);
 });
 $('#wh_ibu').jqxNumberInput(Show1dec);
 $('#wh_time').jqxNumberInput(PosInt);
 $('#wh_time').on('change', function(event) {
  var ibu, 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 == 5) { // Dry hop
   if (newtime > 21) {
    newtime = 21;
    $('#wh_time').val(newtime);
   }
   hopData.h_time = newtime * 1440;
  }
  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(),
    dataRecord.brew_whirlpool9, dataRecord.brew_whirlpool7, dataRecord.brew_whirlpool6);
  $('#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 if (index == 4) { // Whirlpool
    hopData.h_time = (parseFloat(dataRecord.brew_whirlpool9) + parseFloat(dataRecord.brew_whirlpool7) + parseFloat(dataRecord.brew_whirlpool6));
    $('#wh_time').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
    $('#wh_time').val(hopData.h_time);
   } else {        // Boil, 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 5, Miscs
 $('#MiscReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#MiscReady').click(function() {
  var row, rowID = $('#miscGrid').jqxGrid('getrowid', miscRow);
  console.log('MiscReady row:' + miscRow + ' ID:' + rowID);
  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);
  calcMiscs();
 });
 $('#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 datarecord, index = event.args.index;
   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(Spin2dec);
 $('#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) {
  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 {
    $('#wm_time').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
    $('#wm_time').val(0);
    miscData.m_time = 0;
   }
  }
 });

 // Tab 6, Yeasts
 $('#est_fg2').jqxTooltip({ content: 'Het verwachte eind SG. Dit wordt automatisch berekend.' });
 $('#est_abv2').jqxTooltip({ content: 'Verwacht alcohol volume %. Dit wordt automatisch berekend.' });
 $('#yeast_grams').jqxTooltip({ content: 'De gewenste totale hoeveelheid droge gist voor dit bier.' });
 $('#yeast_gr_hl').jqxTooltip({ content: 'De werkelijke hoeveelheid gist per hectoliter.' });
 $('#yeast_cells').jqxTooltip({ content: 'Het aantal miljard beschikbare gistcellen zonder eventuele starter.' });
 $('#need_cells').jqxTooltip({ content: 'Het aantal miljard nodige cellen is afhankelijk van het begin SG, biertype en volume.' });
 $('#plato_cells').jqxTooltip({ content: 'De berekende pitchrate in miljard cellen per ml per graad Plato.' });
 $('#yeast_prod_date').jqxTooltip({ content: 'Bij korrelgisten is meestal "best voor" datum op het zakje gedrukt.<br>Gebruik die datum maar dan twee jaar eerder als productie datum.<br>Bij White Labs is de productie datum vier maanden voor de "Best by" datum die geprint op het buisje.<br>Bij Wyeast is dit de "manufacture date" die op het pak geprint is.<br>Voor schuine buis, slurry, opkweek en gedroogd is dit de datum dat je de gist geoogst hebt.' });
 $('#yeast_pitchrate').jqxTooltip({ content: 'De gewenste pitchrate in miljard cellen per ml per graad Plato voor de vergisting van dit bier.' });

 $('#est_fg2,#plato_cells').jqxNumberInput(Show3dec);
 $('#est_fg2').jqxNumberInput({ width: 70 });
 $('#est_abv2').jqxNumberInput(Show2dec);
 $('#est_abv2').jqxNumberInput({ width: 70, symbol: '%', symbolPosition: 'right' });
 $('#yeast_grams').jqxNumberInput(Show1dec);
 $('#yeast_gr_hl').jqxNumberInput(Show1dec);
 $('#yeast_cells,#need_cells').jqxNumberInput(Show1dec);
 $('#yeast_prod_date').jqxDateTimeInput(Dateopts);
 $('#yeast_prod_date').on('close', function(event) {
  calcViability();
  calcFermentables();
  calcYeast();
 });
 $('#yeast_pitchrate').jqxNumberInput(Spin3dec);
 $('#yeast_pitchrate').on('change', function(event) {
  dataRecord.yeast_pitchrate = parseFloat(event.args.value);
  calcViability();
  calcFermentables();
  calcYeast();
 });
 $('#but_pickpitchrate').jqxButton({ template: 'success', width: '23px', height: 23, theme: theme });
 $('#but_pickpitchrate').bind('click', function() {
  $('#pick_pitchrate').val(0); // Set default pick incase no perfect match.
  $('#pick_pitchrate').val(dataRecord.yeast_pitchrate);
  $('#pitchrateWindow').jqxWindow('open');
 });
 $('#pick_pitchrate').jqxDropDownList({
  theme: theme,
  source: PitchrateAdapter,
  valueMember: 'rate',
  displayMember: 'nl',
  width: 275,
  height: 23,
  autoDropDownHeight: true,
  dropDownVerticalAlignment: 'top'
 });
 $('#pitchrateReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#pitchrateReady').click(function() {
  console.log('pitchrateReady ' + $('#pick_pitchrate').val() );
  dataRecord.yeast_pitchrate = parseFloat($('#pick_pitchrate').val());
  $('#yeast_pitchrate').val(dataRecord.yeast_pitchrate);
  calcViability();
  calcFermentables();
  calcYeast();
 });
 $('#YeastReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#YeastReady').click(function() {
  var row, rowID = $('#yeastGrid').jqxGrid('getrowid', yeastRow);
  console.log('YeastReady row:' + yeastRow + ' ID:' + rowID);
  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_sta1: yeastData.y_sta1,
   y_bacteria: yeastData.y_bacteria,
   y_harvest_top: yeastData.y_harvest_top,
   y_harvest_time: yeastData.y_harvest_time,
   y_pitch_temperature: yeastData.y_pitch_temperature,
   y_pofpos: yeastData.y_pofpos,
   y_zymocide: yeastData.y_zymocide,
   y_gr_hl_lo: yeastData.y_gr_hl_lo,
   y_sg_lo: yeastData.y_sg_lo,
   y_gr_hl_hi: yeastData.y_gr_hl_hi,
   y_sg_hi: yeastData.y_sg_hi,
   y_avail: yeastData.y_avail
  };
  $('#yeastGrid').jqxGrid('updaterow', rowID, row);
  calcViability();
  calcFermentables();
  calcYeast();
 });
 $('#wy_name,#wy_laboratory,#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 datarecord, index = event.args.index;
   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;
   yeastData.y_sta1 = datarecord.sta1;
   yeastData.y_bacteria = datarecord.bacteria;
   yeastData.y_harvest_top = datarecord.harvest_top;
   yeastData.y_harvest_time = datarecord.harvest_time;
   yeastData.y_pitch_temperature = datarecord.pitch_temperature;
   yeastData.y_pofpos = datarecord.pofpos;
   yeastData.y_zymocide = datarecord.zymocide;
   yeastData.y_gr_hl_lo = datarecord.gr_hl_lo;
   yeastData.y_sg_lo = datarecord.sg_lo;
   yeastData.y_gr_hl_hi = datarecord.gr_hl_hi;
   yeastData.y_sg_hi = datarecord.sg_hi;
   if (yeastData.y_form == 0) {
    $('#wy_pmpt_amount').html('Pak(ken):');
   } else if (yeastData.y_form == 1 || yeastData.y_form == 6) {
    $('#wy_pmpt_amount').html('Gewicht gram:');
   } else {
    $('#wy_pmpt_amount').html('Volume ml:');
   }
   calcViability();
   calcFermentables();
   calcYeast();
  }
 });
 $('#wy_amount').jqxNumberInput(Spin1dec);
 $('#wy_amount').on('change', function(event) {
  var amount = parseFloat(event.args.value);
  if (yeastData.y_form != 0) // not Liquid
   amount = parseFloat(event.args.value) / 1000;
  yeastData.y_amount = amount;
  calcFermentables();
  calcYeast();
 });
 $('#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;
   calcViability();
   calcFermentables();
   calcYeast();
  }
 });
 for (i = 1; i < 5; i++) {
  $('#prop' + i + '_volume').jqxTooltip({ content: 'Het volume van deze starter stap.' });
  $('#prop' + i + '_irate').jqxTooltip({ content: 'Voor de beste gistgroei, houd de injectie factor tussen de 25 en 100 miljoen cellen per ml.' });
  $('#prop' + i + '_ncells').jqxTooltip({ content: 'Het aantal miljard nieuwe gistcellen in deze stap.' });
  $('#prop' + i + '_tcells').jqxTooltip({ content: 'Het totaal aantal miljard gistcellen na deze stap.' });
  $('#prop' + i + '_growf').jqxTooltip({ content: 'De groeifactor, minstens 1. Ongeroerde starters komen meestal niet boven de 3.' });

  $('#prop' + i + '_type').jqxDropDownList({
   theme: theme,
   source: StarterTypeAdapter,
   valueMember: 'id',
   displayMember: 'nl',
   width: 120,
   height: 23,
   autoDropDownHeight: true
  });
  $('#prop' + i + '_volume').jqxNumberInput(Spin3dec);
  $('#prop' + i + '_irate,#prop' + i + '_ncells,#prop' + i + '_tcells').jqxNumberInput(Show1dec);
  $('#prop' + i + '_growf').jqxNumberInput(Show2dec);
  $('#prop' + i + '_type,#prop' + i + '_volume,#prop' + i + '_irate,#prop' + i + '_ncells,#prop' + i + '_tcells,#prop' + i + '_growf').hide();
 }
 $('#starter_type').jqxTooltip({ content: 'starter_type' });
 $('#starter_type').jqxDropDownList({
  theme: theme,
  source: StarterTypeAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 120,
  height: 23,
  autoDropDownHeight: true
 });
 $('#starter_sg').jqxTooltip({ content: 'Het ideale starter SG moet tussen de 1.030 en 1.040 zijn. Optimaal is 1.037.' });
 $('#starter_sg').jqxNumberInput(SGopts);
 $('#starter_viability').jqxTooltip({ content: 'De gist conditie.' });
 $('#starter_viability').jqxNumberInput(Show0dec);
 $('#starter_try').jqxButton({ template: 'primary', width: '100px', height: 23, theme: theme });

 // Tab 7, Mashing
 $('#mash_name').jqxTooltip({ content: 'De omschrijving van dit maisch profiel.' });
 $('#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 infused = 0, data, datarecord, rowIDs, rows, i, row, index = event.args.index;
   // First delete all current steps
   rowIDs = new Array();
   rows = $('#mashGrid').jqxGrid('getdisplayrows');
   for (i = 0; i < rows.length; i++) {
    row = rows[i];
    rowIDs.push(row.uid);
   }
   $('#mashGrid').jqxGrid('deleterow', rowIDs);
   // Then add the new steps
   datarecord = mashlist.records[index];
   $('#mash_name').val(datarecord.name);
   for (i = 0; i < datarecord.steps.length; i++) {
    data = datarecord.steps[i];
    row = {};
    row['step_name'] = data.step_name;
    row['step_type'] = parseInt(data.step_type);
    row['step_temp'] = parseFloat(data.step_temp);
    row['end_temp'] = parseFloat(data.end_temp);
    row['step_time'] = parseFloat(data.step_time);
    row['ramp_time'] = parseFloat(data.ramp_time);
    row['step_infuse_temp'] = 0.0;
    row['step_infuse_amount'] = 0.0;
    if (mash_infuse == 0 && dataRecord.wg_amount > 0)
     mash_infuse = dataRecord.wg_amount;
    if (data.step_type == 0) { // Infusion
     if (i == 0) {
      row['step_infuse_amount'] = parseFloat(mash_infuse);
     } else {
      row['step_infuse_temp'] = 99.0;
     }
    }
    row['step_ph'] = 0.0;
    row['step_sg'] = 0.0;
    //console.log(i + ' type: ' + row['step_type'] + ' start infusion: ' + parseFloat(row['step_infuse_amount']) + ' mash_infuse: ' + mash_infuse);
    infused += parseFloat(row['step_infuse_amount']);
    row['step_volume'] = infused;
    if (mashkg > 0)
     row['step_wg_ratio'] = Round(parseFloat(mash_infuse / mashkg), 6);
    else
     row['step_wg_ratio'] = 0.0;
    $('#mashGrid').jqxGrid('addrow', null, row);
   }
   calcMash();
  }
 });
 $('#est_mashvol').jqxTooltip({ content: 'Het totale volume van het maishwater en de mout in de maish pan.' });
 $('#est_mashvol').jqxNumberInput(Show1dec);
 $('#est_mashtime').jqxTooltip({ content: 'De totale tijdsduur van het maischen.' });
 $('#est_mashtime').jqxInput({ theme: theme, width: 70, height: 23 });
 $('#MashReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#MashReady').click(function() {
  calcFermentables();
  calcWater();
  calcSparge();
  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 = $('#wstep_name').val();
 });
 $('#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 rowdata, i, rows, row, index = event.args.index;
   rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
   if (rowdata.step_type != index) {
    rowdata.step_type = index;
    $('#wstep_infuse_amount').hide();
    $('#wstep_infuse_temp').hide();
    $('#wstep_pmpt_amount').hide();
    $('#wstep_pmpt_temp').hide();
    if (index == 0) { // Infusion
     if (mashRow == 0) {
      $('#wstep_infuse_amount').show();
      $('#wstep_pmpt_amount').show();
     } else {
      $('#wstep_infuse_temp').show();
      $('#wstep_pmpt_temp').show();
     }
    }
    if (index == 1) { // Temperature
     if (mashRow > 0)
      rowdata.step_infuse_amount = 0;
     rowdata.step_infuse_temp = 0;
    }
    if (index == 2) { // Decoction
     var rowprev = $('#mashGrid').jqxGrid('getrowdata', mashRow-1);
     rowdata.step_infuse_temp = 99;
     rowdata.step_infuse_amount = decoctionVol(rowdata.step_volume, rowdata.step_temp, rowprev.end_temp);
     console.log('decoction: ' + rowdata.step_infuse_amount + '/' + rowdata.step_infuse_temp);
    }
    $('#mashGrid').jqxGrid('updaterow', mashRow, rowdata);
    mash_infuse = 0;
    rows = $('#mashGrid').jqxGrid('getrows');
    for (i = 0; i < rows.length; i++) {
     row = rows[i];
     if (row.step_type == 0) // Infusion
      mash_infuse += parseFloat(row.step_infuse_amount);
    }
    calcMash();
   }
  }
 });
 $('#wstep_temp').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  if (rowdata.step_type == 2) { // Decoction
   var rowprev = $('#mashGrid').jqxGrid('getrowdata', mashRow-1);
   var a = (dataRecord.eq_tun_weight * dataRecord.eq_tun_specific_heat + rowdata.step_volume * SpecificHeatWater) *
           (parseFloat(event.args.value) - rowprev.end_temp);
   var b = SpecificHeatWater * (99 - parseFloat(event.args.value));
   if (b > 0) {
    rowdata.step_temp = parseFloat(event.args.value);
    rowdata.step_infuse_amount = Round(a / b, 2);
   } else
    rowdata.step_infuse_amount = 0;
   console.log('change temp ' + rowdata.step_temp + ' decoction: ' + rowdata.step_infuse_amount + '/' + rowdata.step_infuse_temp);
  } else {
   rowdata.step_temp = parseFloat(event.args.value);
  }
 });
 $('#wstep_temp,#wend_temp,#wstep_infuse_amount,#wstep_infuse_temp').jqxNumberInput(Spin1dec);
 $('#wend_temp').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.end_temp = parseFloat(event.args.value);
 });
 $('#wstep_time,#wramp_time').jqxNumberInput(PosInt);
 $('#wstep_time').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.step_time = parseFloat(event.args.value);
  calcMash();
 });
 $('#wstep_ph').jqxNumberInput(Spin2pH);
 $('#wstep_ph').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.step_ph = parseFloat(event.args.value);
 });
 $('#wstep_sg').jqxNumberInput(SGopts);
 $('#wstep_sg').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.step_sg = parseFloat(event.args.value);
 });
 $('#wstep_brix').jqxNumberInput(Spin1dec);
 $('#wstep_brix').on('change', function(event) {
  /* Use plato_to_sg at this stage */
  var sg = plato_to_sg(parseFloat(event.args.value));
console.log('brix: ' + parseFloat(event.args.value) + ' sg: ' + sg);
  if (sg > 1.005) {
   var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
   rowdata.step_sg = sg;
   $('#wstep_sg').val(sg);
  }
 });
 $('#wramp_time').on('change', function(event) {
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.ramp_time = parseFloat(event.args.value);
  calcMash();
 });
 $('#wstep_infuse_amount').on('change', function(event) {
  var row, i, rows, rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.step_infuse_amount = parseFloat(event.args.value);
  if (mashRow == 0) {
   rowdata.step_infuse_amount = parseFloat(event.args.value);
   mash_infuse = 0;
   rows = $('#mashGrid').jqxGrid('getrows');
   for (i = 0; i < rows.length; i++) {
    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 {
    var w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
    var w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
    dataRecord.w1_amount = Round(w1_amount, 3);
    dataRecord.w2_amount = Round(w2_amount, 3);
    $('#w1_amount').val(dataRecord.w1_amount);
    $('#w2_amount').val(dataRecord.w2_amount);
   }
   $('#wg_amount').val(mash_infuse);
   console.log('new infuse amount: ' + mash_infuse);
   calcWater();
//   calcMash();
  }
 });
 $('#wstep_infuse_temp').on('change', function(event) {
  var prevdata = $('#mashGrid').jqxGrid('getrowdata', mashRow-1);
  var rowdata = $('#mashGrid').jqxGrid('getrowdata', mashRow);
  rowdata.step_infuse_temp = parseFloat(event.args.value);
  var vol = infusionVol(prevdata.step_volume, mashkg, rowdata.step_infuse_temp, rowdata.step_temp, prevdata.end_temp);
  console.log('new vol: ' + vol);
  rowdata.step_infuse_amount = vol;
  $('#wstep_infuse_amount').val(vol);
 });

 // Tab 8, Water
 $('#tgt_bu').jqxNumberInput(Show2wat);
 $('#tgt_so4_cl,#got_so4_cl').jqxNumberInput(Show1wat);

 // Water source 1
 $('#w1_name').jqxDropDownList({
  placeHolder: 'Kies hoofd water:',
  theme: theme,
  source: waterlist,
  displayMember: 'name',
  width: 200,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 400
 });
 $('#w1_name').on('select', function(event) {
  if (event.args) {
   var datarecord, index = event.args.index;
   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);
   $('#w1_bicarbonate').val(datarecord.total_alkalinity * 1.22);
   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,#w1_calcium,#w1_magnesium,#w1_sodium,#w1_bicarbonate,#w1_total_alkalinity,#w1_chloride,#w1_sulfate').jqxNumberInput(Show1wat);
 $('#w1_ph').jqxNumberInput(Show2wat);
 // Water source 2
 $('#w2_name').jqxDropDownList({
  placeHolder: 'Kies meng water:',
  theme: theme,
  source: water2list,
  displayMember: 'name',
  width: 200,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 400
 });
 $('#w2_name').on('select', function(event) {
  if (event.args) {
   var datarecord, index = event.args.index;
   datarecord = water2list.records[index];
   if (index == 0) {
    dataRecord.w2_amount = 0;
    $('#w2_amount').val(0);
    dataRecord.w1_amount = mash_infuse;
    $('#w1_amount').val(mash_infuse);
   }
   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);
   $('#w2_bicarbonate').val(datarecord.total_alkalinity * 1.22);
   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', spinMode: 'simple', theme: theme, width: 94, height: 23, min: 0, max: 0, decimalDigits: 1,
  spinButtons: true, spinButtonsStep: 0.1, readOnly: true
 });
 $('#w2_calcium,#w2_magnesium,#w2_sodium,#w2_bicarbonate,#w2_total_alkalinity,#w2_chloride,#w2_sulfate').jqxNumberInput(Show1wat);
 $('#w2_ph').jqxNumberInput(Show2wat);
 // Water mixed
 $('#wg_amount,#wg_calcium,#wg_magnesium,#wg_sodium,#wg_bicarbonate,#wg_total_alkalinity,#wg_chloride,#wg_sulfate').jqxNumberInput(Show1wat);
 $('#wg_ph').jqxNumberInput(Show2wat);
 // Water treated
 $('#wb_calcium').jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'});
 $('#wb_magnesium').jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is tussen 5 en 40.'});
 $('#wb_sodium').jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'});
 $('#wb_chloride').jqxTooltip({ content: 'De ideale hoeveelheid Chloride is tussen 50 en 150. Samen met Sulfaat minder dan 500.'});
 $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 400. Samen met Chloride minder dan 500.'});
 $('#wb_bicarbonate').jqxTooltip({ content: '0 tot 50 lichte bieren, 50 tot 150 amber bieren, 150 tot 250 donkere bieren.'});
 $('#wb_calcium,#wb_magnesium,#wb_sodium,#wb_bicarbonate,#wb_total_alkalinity,#wb_chloride,#wb_sulfate').jqxNumberInput(Show1wat);
 $('#wb_ph').jqxNumberInput(Show2wat);
 // Water target profile
 $('#pr_name').jqxDropDownList({
  placeHolder: 'Kies doel profiel:',
  theme: theme,
  source: waterprofiles,
  displayMember: 'name',
  width: 200,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 300
 });
 $('#pr_name').on('select', function(event) {
  if (event.args) {
   var datarecord, index = event.args.index;
   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_bicarbonate').val(datarecord.total_alkalinity * 1.22);
   calcWater();
  }
 });
 $('#pr_calcium,#pr_magnesium,#pr_sodium,#pr_bicarbonate,#pr_total_alkalinity,#pr_chloride,#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_caso4').jqxTooltip({
  content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.'
 });
 $('#wa_mgso4').jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!' });
 $('#wa_nacl').jqxTooltip({
  content: 'Keukenzout. Voor het maken van een ander waterprofiel. Voegt natrium en chloride toe. ' +
  'Voor het accentueren van zoetheid. Bij hoge dosering wordt het bier ziltig.'
 });
 $('#wa_mgcl2').jqxTooltip({ content: 'Magnesiumchloride'});
 $('#wa_nahco3').jqxTooltip({ content: 'Baksoda'});
 $('#wa_caco3').jqxTooltip({ content: 'Kalk'});
 $('#wa_cacl2,#wa_caso4,#wa_mgso4,#wa_nacl,#wa_mgcl2,#wa_nahco3,#wa_caco3').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_acid').jqxNumberInput(Spin2dec);
 $('#wa_acid_name').jqxDropDownList({
  theme: theme,
  source: AcidTypeAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 130,
  height: 23,
  autoDropDownHeight: true
 });
 $('#wa_acid').jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' });
 $('#wa_acid_perc,#sparge_acid_perc').jqxNumberInput(Perc0);
 $('#wa_acid_perc').jqxNumberInput({ width: 70, symbol: '%', symbolPosition: 'right' });
 // Sparge water
 $('#sparge_temp,#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({ symbol: '%', symbolPosition: 'right' });

 // Tab 9, Brewday
 $('#brew_date_start').jqxTooltip({ content: 'Brouw datum en tijd. Voor planning laat de tijd op 00:00:00 staan.' });
 $('#brew_date_end').jqxTooltip({ content: 'End datum en tijd van de brouw. Leeg laten als er nog niet gebrouwen is.' });
 $('#brew_mash_ph').jqxTooltip({ content: 'De gemeten pH tijdens het maischen eventueel na correctie.' });
 $('#est_mash_ph').jqxTooltip({ content: 'De gewenste pH tijdens het maischen.' });
 $('#brew_preboil_ph').jqxTooltip({ content: 'De gemeten pH in de kookketel na het spoelen en voor de kook.' });
 $('#brew_aboil_ph').jqxTooltip({ content: 'De gemeten pH na het koken.' });
 $('#brew_mash_sg').jqxTooltip({ content: 'Het bereikte SG na het maischen.' });
 $('#est_mash_sg').jqxTooltip({ content: 'Het berekende verwachte SG na het maischen.' });
 $('#brew_preboil_sg').jqxTooltip({ content: 'Het gemeten SG in de kookketel na het spoelen en voor het koken.' });
 $('#est_pre_sg').jqxTooltip({ content: 'Het berekende SG in de kookketel na het spoelen en voor het koken.' });
 $('#brew_aboil_sg').jqxTooltip({ content: 'Het gemeten SG in de kookketel na het koken.' });
 $('#est_og3').jqxTooltip({ content: 'Het gewenste SG in de kookketel na het koken zonder eventuele suikers die tijdens de vergisting toegevoegd worden.' });
 $('#brew_mash_efficiency').jqxTooltip({ content: 'Het behaalde maisch rendement.' });
 $('#brew_preboil_volume').jqxTooltip({ content: 'Het gemeten volume van het wort voor het koken.' });
 $('#est_pre_vol').jqxTooltip({ content: 'Het berekende volume van het wort voor het koken.' });
 $('#brew_aboil_volume').jqxTooltip({ content: 'Het gemeten volume van het wort na het koken.' });
 $('#est_a_vol').jqxTooltip({ content: 'Het gewenste volume na het koken.' });
 $('#brew_preboil_efficiency').jqxTooltip({ content: 'Het berekende rendement voor het koken.' });
 $('#brew_aboil_efficiency').jqxTooltip({ content: 'Het bereikte rendement na het koken.' });
 $('#brew_sparge_temperature').jqxTooltip({ content: 'De spoelwater temperatuur, in te stellen in de Water tab.' });
 $('#brew_sparge_volume').jqxTooltip({ content: 'Het spoelwater voorraad volume, in te stellen in de Water tab.' });
 $('#brew_date_start,#brew_date_end').jqxDateTimeInput(DateTimeopts);
 $('#brew_date_start,#brew_date_end').on('close', function(event) { calcStage(); });
 $('#est_mash_ph').jqxNumberInput(Show2wat);
 $('#brew_mash_ph,#brew_preboil_ph,#brew_aboil_ph').jqxNumberInput(Spin2pH);
 $('#brew_mash_sg').on('valueChanged', function() { calcMashEfficiency(); });
 $('#brew_preboil_sg').on('valueChanged', function(event) {
  dataRecord.brew_preboil_sg = event.args.value;
  calcEfficiencyBeforeBoil();
 });
 $('#brew_mash_sg,#brew_preboil_sg,#brew_aboil_sg').jqxNumberInput(SGopts);
 $('#brew_aboil_sg').on('valueChanged', function(event) {
  dataRecord.brew_aboil_sg = event.args.value;
  calcEfficiencyAfterBoil();
  calcFermentables();
  calcIBUs();
 });
 $('#est_mash_sg,#est_pre_sg,#est_og3').jqxNumberInput(Show3wat);
 $('#brew_mash_efficiency').jqxNumberInput(Show1dec);
 $('#brew_preboil_volume').on('valueChanged', function(event) {
  dataRecord.brew_preboil_volume = event.args.value;
  calcEfficiencyBeforeBoil();
 });
 $('#brew_preboil_volume,#brew_aboil_volume').jqxNumberInput(Spin1dec);
 $('#brew_aboil_volume').on('valueChanged', function(event) {
  dataRecord.brew_aboil_volume = event.args.value;
  calcEfficiencyAfterBoil();
  calcFermentables();
  calcIBUs();
 });
 $('#ketel_volume').jqxNumberInput(Show1dec);
 $('#ketel_cm').jqxNumberInput(Spin1dec);
 $('#ketel_cm').on('change', function(event) {
  k_cm = parseFloat(event.args.value);
  k_vol = kettle_vol(k_cm, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  $('#ketel_volume').val(k_vol);
 });
 $('#volumeReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#volumeReady').click(function() {
  /* Is it pre or after boil */
  console.log('volumeReady ' + k_what + ' volume: ' + Round(k_vol, 1));
  if (k_what == 0) {
   dataRecord.brew_preboil_volume = Round(k_vol, 1);
   $('#brew_preboil_volume').val(Round(k_vol, 1));
  } else if (k_what == 1) {
   dataRecord.brew_aboil_volume = Round(k_vol, 1);
   $('#brew_aboil_volume').val(Round(k_vol, 1));
  }
 });
 $('#but_pre_boil').jqxButton({ template: 'success', width: '23px', height: 23, theme: theme });
 $('#but_pre_boil').bind('click', function() {
  /* If we already have measured a value, use that. Else use the estimated value. */
  if (dataRecord.brew_preboil_volume > 0)
   k_cm = kettle_cm(dataRecord.brew_preboil_volume, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  else
   k_cm = kettle_cm(dataRecord.boil_size * 1.04, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  k_vol = kettle_vol(k_cm, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  k_what = 0;
  $('#ketel_cm').val(k_cm);
  $('#ketel_volume').val(k_vol);
  $('#volumeWindow').jqxWindow('open');
 });
 $('#but_after_boil').jqxButton({ template: 'success', width: 23, height: 23, theme: theme });
 $('#but_after_boil').bind('click', function() {
  if (dataRecord.brew_aboil_volume > 0)
   k_cm = kettle_cm(dataRecord.brew_aboil_volume, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  else
   k_cm = kettle_cm(dataRecord.batch_size * 1.04, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  k_vol = kettle_vol(k_cm, dataRecord.eq_kettle_volume, dataRecord.eq_kettle_height);
  k_what = 1;
  $('#ketel_cm').val(k_cm);
  $('#ketel_volume').val(k_vol);
  $('#volumeWindow').jqxWindow('open');
 });
 $('#est_pre_vol,#est_a_vol').jqxNumberInput(Show1wat);
 $('#brew_preboil_efficiency,#brew_aboil_efficiency,#brew_sparge_temperature,#brew_sparge_volume,#brew_sparge_est').jqxNumberInput(Show1dec);
 $('#brew_cooling_to').jqxNumberInput(Spin1dec);
 $('#brew_sparge_ph').jqxNumberInput(Spin2pH);
 $('#brew_cooling_method').jqxDropDownList({
  theme: theme,
  source: CoolingTypeAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });
 $('#brew_cooling_time,#brew_whirlpool9,#brew_whirlpool7,#brew_whirlpool6,#brew_whirlpool2,#brew_aeration_time,#brew_aeration_speed').jqxNumberInput(PosInt);
 $('#brew_cooling_time,#brew_aeration_time,#brew_aeration_speed').jqxNumberInput({ max: 1440 });
 $('#brew_whirlpool9,#brew_whirlpool7,#brew_whirlpool6,#brew_whirlpool2').jqxNumberInput({ max: 120 });
 $('#brew_aeration_type').jqxDropDownList({
  theme: theme,
  source: AerationTypeAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });
 $('#brew_fermenter_volume').jqxNumberInput(Show1dec);
 $('#brew_fermenter_sg').jqxNumberInput(Show3dec);
 $('#brew_fermenter_extrawater,#brew_fermenter_tcloss').jqxNumberInput(Spin1dec);
 $('#brew_fermenter_ibu,#brew_fermenter_color').jqxNumberInput(Show0dec);
 $('#BLog').jqxButton({ template: 'info', width: '150px', theme: theme });
 $('#BLog').click(function() {
  // Open log in a new tab.
  window.open('log_brew.php?code=' + dataRecord.code + '&name=' + dataRecord.name);
 });

 // Tab 10, Fermentation
 // Note, fermentation temps changes must do calcCarbonation()
 $('#brew_fermenter_sg2').jqxTooltip({ content: 'Het behaalde SG in het gistvat, overgenomen van de brouwdag.' });
 $('#primary_start_temp').jqxTooltip({ content: 'De begintemperatuur van de hoofdvergisting.' });
 $('#primary_max_temp').jqxTooltip({ content: 'De hoogst bereikte piek temperatuur tijdens de hoofgvergisting.' });
 $('#primary_end_temp').jqxTooltip({ content: 'De eind temperatuur van de hoofdvergisting.' });
 $('#primary_end_sg').jqxTooltip({ content: 'Het gemeten SG aan het eind van de hoofdvergisting.' });
 $('#primary_end_brix,#secondary_end_brix,#final_brix').jqxTooltip({ content: 'Hulpfinctie: de afgelezen &deg;Brix RI waarde met een refractometer.' });
 $('#primary_svg').jqxTooltip({ content: 'De schijnbare vergisting graad behaald na de hoofdgisting.' });
 $('#primary_end_date').jqxTooltip({ content: 'De eind datum van de hoofdvergisting en eventueel overhevelen.' });
 $('#secondary_end_sg').jqxTooltip({ content: 'Het gemeten SG aan het eind van de navergisting.' });
 $('#secondary_svg').jqxTooltip({ content: 'De schijnbare vergisting graad behaald na de nagisting.' });
 $('#secondary_end_date').jqxTooltip({ content: 'De eind datum van de navergisting en het begin van het lageren.' });
 $('#est_fg3').jqxTooltip({ content: 'Het verwachte eind SG. Dit wordt automatisch berekend.' });

 $('#primary_end_sg,#secondary_end_sg').jqxNumberInput(SGopts);
 $('#primary_end_date,#secondary_end_date').jqxDateTimeInput(Dateopts);
 $('#primary_end_date,#secondary_end_date').on('close', function(event) { calcStage(); });
 $('#primary_start_temp,#primary_max_temp,#primary_end_temp,#secondary_temp,#tertiary_temp').jqxNumberInput(YeastT);
 $('#fg').jqxNumberInput(Spin3dec);
 $('#brew_fermenter_sg2,#est_fg3').jqxNumberInput(Show3dec);
 $('#primary_end_brix,#secondary_end_brix,#final_brix').jqxNumberInput(Spin1dec);
 $('#final_abv').jqxNumberInput(Show2dec);
 $('#primary_svg,#secondary_svg,#final_svg').jqxNumberInput(Show1dec);
 $('#FLog').jqxButton({ template: 'info', width: '150px', theme: theme });
 $('#FLog').click(function() {
  // Open log in a new tab.
  window.open('log_fermentation.php?code=' + dataRecord.code + '&name=' + dataRecord.name);
 });
 $('#ILog').jqxButton({ template: 'info', width: '150px', theme: theme });
 $('#ILog').click(function() {
  // Open log in a new tab.
  window.open('log_ispindel.php?code=' + dataRecord.code + '&name=' + dataRecord.name);
 });

 // Tab 11, Packaging
 // TODO: high gravity packaging, extra water and recalc abv, color and ibu.
 $('#package_date').jqxTooltip({ content: 'De verpakkings datum van dit bier.' });
 $('#package_volume').jqxTooltip({ content: 'Het beschikbare volume om te bottelen of op fust te zetten.' });
 $('#package_infuse_amount').jqxTooltip({ content: 'De hoeveelheid water of drank extra toe te voegen.' });
 $('#package_infuse_abv').jqxTooltip({ content: 'De hoeveelheid alcohol in de drank, of 0.0 als het water is.' });
 $('#package_infuse_notes').jqxTooltip({ content: 'Omschrijving van de extra toevoeging.' });
 $('#package_abv').jqxTooltip({ content: 'De uiteindelijke hoeveelheid alcohol volume %.' });
 $('#package_ph').jqxTooltip({ content: 'De gemeten pH vlak voor het verpakken.' });
 $('#st_carb_min2').jqxTooltip({ content: 'Het minimum aanbevolen koolzuur volume voor deze bierstijl.'});
 $('#st_carb_max2').jqxTooltip({ content: 'Het maximum aamnevolen koolzuur volume voor deze bierstijl.'});
 $('#bottle_amount').jqxTooltip({ content: 'De totale hoeveelheid te bottelen bier.' });
 $('#keg_amount').jqxTooltip({ content: 'De totale hoeveelheid op fust te zetten bier.' });
 $('#bottle_carbonation').jqxTooltip({ content: 'Het gewenste CO2 volume in de flessen.' });
 $('#keg_carbonation').jqxTooltip({ content: 'Het gewenste CO2 volume door de suiker in de fusten.' });
 $('#bottle_priming_water,#keg_priming_water').jqxTooltip({ content: 'De hoeveelheid water om de suiker op te lossen.' });
 $('#bottle_pressure').jqxTooltip({ content: 'De maximaal te verwachten druk tijdens het hergisten.' });
 $('#package_date').jqxDateTimeInput(Dateopts);
 $('#package_date').on('close', function(event) { calcStage(); });
 $('#package_infuse_amount').jqxNumberInput(Spin3dec);
 $('#package_infuse_notes').jqxInput({ theme: theme, width: 640, height: 23 });
 $('#package_abv').jqxNumberInput(Show2dec);
 $('#package_ph').jqxNumberInput(SpinpH);
 $('#st_carb_min2,#st_carb_max2').jqxNumberInput(Smal1dec);
 $('#package_volume,#package_infuse_abv,#bottle_amount,#keg_amount').jqxNumberInput(Spin1dec);
 $('#bottle_carbonation,#keg_carbonation').jqxNumberInput(Spin2dec);
 $('#bottle_carbonation,#keg_carbonation').jqxNumberInput({ max: 5 });
 $('#bottle_priming_sugar').jqxDropDownList({
  placeHolder: 'Kies suiker:',
  theme: theme,
  source: fermentablesugars,
  displayMember: 'name',
  width: 200,
  height: 23,
  dropDownWidth: 300,
  dropDownHeight: 400
 });
 $('#bottle_priming_sugar').on('select', function(event) {
  if (event.args) {
   var rowID, index, editrow = -1, datarecord, rows, i, row = {};
   index = event.args.index;
   datarecord = fermentablesugars.records[index];
   rows = $('#fermentableGrid').jqxGrid('getrows');
   for (i = 0; i < rows.length; i++) {
    if (rows[i].f_added == 4) {
     editrow = i;
    }
   }
   row['f_name'] = datarecord.name;
   row['f_origin'] = datarecord.origin;
   row['f_supplier'] = datarecord.supplier;
   row['f_amount'] = parseFloat($('#bottle_priming_total').jqxNumberInput('decimal')) / 1000;
   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;
   row['f_added'] = 4;
   row['f_dissolved_protein'] = datarecord.dissolved_protein;
   row['f_recommend_mash'] = datarecord.recommend_mash;
   row['f_add_after_boil'] = 1;
   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;
   if (editrow >= 0) {
    rowID = $('#fermentableGrid').jqxGrid('getrowid', editrow);
    $('#fermentableGrid').jqxGrid('updaterow', rowID, row);
   } else {
    $('#fermentableGrid').jqxGrid('addrow', null, row);
   }
   calcCarbonation();
  }
 });
 $('#keg_priming_sugar').jqxDropDownList({
  placeHolder: 'Kies suiker:',
  theme: theme,
  source: fermentablesugars,
  displayMember: 'name',
  width: 200,
  height: 23,
  dropDownWidth: 300,
  dropDownHeight: 400
 });
 $('#keg_priming_sugar').on('select', function(event) {
  if (event.args) {
   var rowID, index, editrow = -1, datarecord, rows, i, row = {};
   index = event.args.index;
   datarecord = fermentablesugars.records[index];
   rows = $('#fermentableGrid').jqxGrid('getrows');
   for (i = 0; i < rows.length; i++) {
    if (rows[i].f_added == 5) {
     editrow = i;
    }
   }
   row['f_name'] = datarecord.name;
   row['f_origin'] = datarecord.origin;
   row['f_supplier'] = datarecord.supplier;
   row['f_amount'] = parseFloat($('#keg_priming_total').jqxNumberInput('decimal')) / 1000;
   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;
   row['f_added'] = 5;
   row['f_dissolved_protein'] = datarecord.dissolved_protein;
   row['f_recommend_mash'] = datarecord.recommend_mash;
   row['f_add_after_boil'] = 1;
   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;
   if (editrow >= 0) {
    rowID = $('#fermentableGrid').jqxGrid('getrowid', editrow);
    $('#fermentableGrid').jqxGrid('updaterow', rowID, row);
   } else {
    $('#fermentableGrid').jqxGrid('addrow', null, row);
   }
   calcCarbonation();
  }
 });
 $('#bottle_priming_water,#keg_priming_water').jqxNumberInput(Spin3dec);
 $('#keg_forced_carb').jqxCheckBox({ theme: theme, width: 120, height: 23 });
 $('#bottle_priming_amount,#keg_priming_amount,#bottle_priming_total,#bottle_pressure,#keg_priming_total,#keg_pressure').jqxNumberInput(Show1dec);
 $('#bottle_abv,#keg_abv').jqxNumberInput(Show2dec);
 $('#bottle_carbonation_temp,#keg_carbonation_temp').jqxNumberInput(YeastT);
 $('#CLog').jqxButton({ template: 'info', width: '150px', theme: theme });
 $('#CLog').click(function() {
  // Open log in a new tab.
  window.open('log_co2pressure.php?code=' + dataRecord.code + '&name=' + dataRecord.name);
 });

 // Tab 12, Tasting
 $('#taste_date').jqxTooltip({ content: 'De proef datum van dit bier.' });
 $('#taste_date').jqxDateTimeInput(Dateopts);
 $('#taste_date').on('close', function(event) { calcStage(); });
 $('#taste_rate').jqxTooltip({ content: 'Het cijfer voor dit bier van 1 tot 10.' });
 $('#taste_rate').jqxNumberInput(Spin1dec);
 $('#taste_rate').jqxNumberInput({ max: 10 });
 $('#taste_color').jqxTooltip({ content: 'De kleur van het bier.' });
 $('#taste_transparency').jqxTooltip({ content: 'De helderheid van het bier.' });
 $('#taste_head').jqxTooltip({ content: 'Het schuim op het bier.' });
 $('#taste_color,#taste_transparency,#taste_head').jqxInput({ theme: theme, width: 320, height: 23 });
 $('#taste_aroma').jqxTooltip({ content: 'Het aroma van het bier.' });
 $('#taste_taste').jqxTooltip({ content: 'De smaak van het bier.' });
 $('#taste_aftertaste').jqxTooltip({ content: 'De nasmaak van het bier.' });
 $('#taste_mouthfeel').jqxTooltip({ content: 'Het mondgevoelvan het bier.' });
 $('#taste_aroma,#taste_taste,#taste_aftertaste,#taste_mouthfeel').jqxInput({ theme: theme, width: 960, height: 23 });
 $('#taste_notes').jqxTooltip({ content: 'Het oordeel en opmerkingen over dit bier.' });
 $('#taste_notes').jqxInput({ theme: theme, width: 960, height: 100 });

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

 $('#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({ uuid: dataRecord.uuid });
   $.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(1);
 });
 createPopupElements();
});

mercurial