www/js/rec_edit.js

Fri, 13 Sep 2019 20:48:35 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 13 Sep 2019 20:48:35 +0200
changeset 489
18e5cfe5d126
parent 474
7b7df475e67d
child 524
55a246085522
permissions
-rw-r--r--

Load new url in database write callback

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

 var psugar = 0,     // Percentage real sugars
 pcara = 0,          // Percentage cara/crystal malts
 svg = 77,           // Default attenuation
 mashkg = 0,         // Malt in mash weight
 mash_infuse = 0,
 dataRecord = {},    // Main recipe record
 hop_flavour = 0,
 hop_aroma = 0,
 preboil_sg = 0,
 last_base = '',
 last_acid = '',
 Ka1 = 0.0000004445,
 Ka2 = 0.0000000000468,
 MMCa = 40.048,
 MMMg = 24.305,
 MMNa = 22.98976928,
 MMCl = 35.453,
 MMSO4 = 96.0626,
 MMCO3 = 60.01684,
 MMHCO3 = 61.01684,
 MMCaSO4 = 172.171,
 MMCaCl2 = 147.015,
 MMCaCO3 = 100.087,
 MMMgSO4 = 246.475,
 MMNaHCO3 = 84.007,
 MMNa2CO3 = 105.996,
 MMNaCl = 58.443,
 MMCaOH2 = 74.06268;


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


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

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


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

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


function setReadonly(ro) {
 var rw = ! ro, w100 = 110, w80 = 80;
 if (ro) { // jqxNumberInput width -20 for no spinbuttons
  w100 = 90;
  w80 = 60;
 }
 $('#batch_size').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#boil_size').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#boil_time').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#efficiency').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#est_og').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 // id="st_fg_min" margin-left 15/35 maken
 $('#type').jqxDropDownList({ disabled: ro });
 $('#styleSelect').jqxDropDownList({ disabled: ro });
 $('#color_method').jqxDropDownList({ disabled: ro });
 $('#ibu_method').jqxDropDownList({ disabled: ro });
 $('#Delete').jqxButton({ disabled: ro });
 $('#fermentableGrid').jqxGrid({ editable: rw });
 $('#faddrowbutton').jqxDropDownList({ disabled: ro });
 $('#finstockbutton').jqxCheckBox({ disabled: ro });
 $('#fdeleterowbutton').jqxButton({ disabled: ro });
 $('#hopGrid').jqxGrid({ editable: rw });
 $('#haddrowbutton').jqxDropDownList({ disabled: ro });
 $('#hinstockbutton').jqxCheckBox({ disabled: ro });
 $('#hdeleterowbutton').jqxButton({ disabled: ro });
 $('#miscGrid').jqxGrid({ editable: rw });
 $('#maddrowbutton').jqxDropDownList({ disabled: ro });
 $('#minstockbutton').jqxCheckBox({ disabled: ro });
 $('#mdeleterowbutton').jqxButton({ disabled: ro });
 $('#yeastGrid').jqxGrid({ editable: rw });
 $('#yaddrowbutton').jqxDropDownList({ disabled: ro });
 $('#yinstockbutton').jqxCheckBox({ disabled: ro });
 $('#ydeleterowbutton').jqxButton({ disabled: ro });
 $('#mashGrid').jqxGrid({ editable: rw });
 $('#saddrowbutton').jqxButton({ disabled: ro });
 $('#sdeleterowbutton').jqxButton({ disabled: ro });
 $('#w1_name').jqxDropDownList({ disabled: ro });
 $('#w2_name').jqxDropDownList({ disabled: ro });
 $('#pr_name').jqxDropDownList({ disabled: ro });
 $('#wa_cacl2').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#wa_caso4').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#wa_mgso4').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#wa_nacl').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#mash_ph').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#calc_acid').jqxCheckBox({ disabled: ro });
 $('#wa_base_name').jqxDropDownList({ disabled: ro });
 $('#wa_base').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#wa_acid_name').jqxDropDownList({ disabled: ro });
 $('#wa_acid').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#wa_acid_perc').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w80 });
 $('#sparge_temp').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#sparge_volume').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#sparge_ph').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
 $('#sparge_source').jqxDropDownList({ disabled: ro });
 $('#sparge_acid_type').jqxDropDownList({ disabled: ro });
 $('#sparge_acid_perc').jqxNumberInput({ spinButtons: rw, readOnly: ro, width: w100 });
}


function calcPercentages() {

 console.log('calcPercentages()');
 var tw, rowdata, percentage, i, rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
 if (rowscount > 1) {
  tw = 0;
  for (i = 0; i < rowscount; i++) {
   rowdata = $('#fermentableGrid').jqxGrid('getrowdata', 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);
 }
}


function calcFermentables() {
 console.log('calcFermentables()');
 var i, row, rows, org, s = 0, d, x,
 sug, alc, cw, color, scolor, fig,
 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
 /* Init global variables */
 psugar = 0;
 pcara = 0;
 mashkg = 0;

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

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

 for (i = 0; i < rows.length; i++) {
  row = rows[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 += 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 == 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;
  }
  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.
  }
 }
 $('#ferm_lintner').val(Math.round(parseFloat(lintner / mashkg)));
 //console.log('lintner:' + lintner + ' kg:' + mashkg);
 to_100 = my_100;
 if (to_100) {
  $('#wf_amount').jqxNumberInput({ width: 90, readOnly: true, spinButtons: false });
 } else {
  $('#wf_amount').jqxNumberInput({ width: 110, readOnly: false, spinButtons: true });
 }

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

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

 // Color of the wort
 if (dataRecord.color_method == 4) {
  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.boil_size / 3) * 100;
 $('#perc_malts').jqxProgressBar('val', pmalts);
 $('#perc_sugars').jqxProgressBar('val', psugar);
 $('#perc_cara').jqxProgressBar('val', pcara);

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

 if ((mashkg > 0) && (mash_infuse > 0) && (mashtime > 0) && (mashtemp > 0)) {
  dataRecord.est_fg = estimate_fg(psugar, pcara, mash_infuse / mashkg, mashtime, mashtemp, svg, dataRecord.est_og);
 } else {
  dataRecord.est_fg = estimate_fg(psugar, pcara, 0, 0, 0, svg, dataRecord.est_og);
 }
 $('#est_fg').val(dataRecord.est_fg);
 $('#est_fg2').val(dataRecord.est_fg);
 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 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 infused = 0, i, row, rows;
 if (!(rows = $('#mashGrid').jqxGrid('getrows')))
  return;
 if (mashkg == 0)
  return;

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


function calcIBUs() {
 var total_ibus = 0,
 rows = {},
 i, row;

 hop_aroma = hop_flavour = 0;
 if (!(rows = $('#hopGrid').jqxGrid('getrows'))) {
  return;
 }
 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);
  hop_flavour += hopFlavourContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size), row.h_useat, parseFloat(row.h_amount));
  hop_aroma += hopAromaContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size), row.h_useat, parseFloat(row.h_amount));
 }
 total_ibus = Round(total_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);
 dataRecord.est_ibu = total_ibus;
 $('#est_ibu').val(total_ibus);
 $('#est_ibu2').val(total_ibus);
 $('#hop_flavour').jqxProgressBar('val', hop_flavour);
 $('#hop_aroma').jqxProgressBar('val', hop_aroma);
}


function adjustHops(factor) {

 //console.log('adjustHops(' + factor + ')');
 var i, row, amount, rowscount = $('#hopGrid').jqxGrid('getdatainformation').rowscount;
 if (rowscount == 0)
  return;

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


function adjustMiscs(factor) {

 //console.log('adjustMiscs(' + factor + ')');
 var i, row, amount, rowscount = $('#miscGrid').jqxGrid('getdatainformation').rowscount;
 if (rowscount == 0)
  return;

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


function adjustYeasts(factor) {

 //console.log('adjustYeasts(' + factor + ')');
 var i, row, amount, rowscount = $('#yeastGrid').jqxGrid('getdatainformation').rowscount;
 if (rowscount == 0)
  return;

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


function adjustWaters(factor) {

 //console.log('adjustWaters(' + factor + ')');
 var i, row, amount, rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
 if (rowscount == 0)
  return;
 mash_infuse = 0;
 for (i = 0; i < rowscount; i++) {
  row = $('#mashGrid').jqxGrid('getrowdata', i);
  if (row.step_type == 0) { // Infusion
   amount = Round(row.step_infuse_amount * factor, 1);
   $('#mashGrid').jqxGrid('setcellvalue', i, 'step_infuse_amount', amount);
   mash_infuse += amount;
  }
 }
 if (dataRecord.w2_amount == 0) {
  dataRecord.w1_amount = mash_infuse;
  $('#w1_amount').val(mash_infuse);
 } else {
  dataRecord.w1_amount = (dataRecord.w1_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
  dataRecord.w2_amount = (dataRecord.w2_amount / (dataRecord.w1_amount + dataRecord.w2_amount)) * mash_infuse;
  $('#w1_amount').val(dataRecord.w1_amount);
  $('#w2_amount').val(dataRecord.w2_amount);
 }
 $('#wg_amount').val(mash_infuse);
}


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


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



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


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


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


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 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 ProtonDeficit(pHZ) {
 var ebc, C1, i, rows, row, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal'));
 // proton deficit for the grist
 rows = $('#fermentableGrid').jqxGrid('getrows');
 for (i = 0; i < rows.length; i++) {
  row = rows[i];
  if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt
   // Check if acid is required
   C1 = 0;
   if ((row.f_di_ph != 5.7) && ((row.f_acid_to_ph_57 < - 0.1) || (row.f_acid_to_ph_57 > 0.1))) {
    C1 = row.f_acid_to_ph_57 / (row.f_di_ph - 5.7);
   } else {
    // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
    ebc = row.f_color;
    switch (row.f_graintype) {
     case 0:                              // Base, Special, Kilned
     case 3:
     case 5: C1 = 0.014 * ebc - 34.192;
             break;
     case 2: C1 = -0.0597 * ebc - 32.457; // Crystal
             break;
     case 1: C1 = 0.0107 * ebc - 54.768;  // Roast
             break;
     case 4: C1 = -149;                   // Sour malt
             break;
    }
   }
   x = C1 * (pHZ - row.f_di_ph);   // AcidRequired(ZpH)
   Result += x * row.f_amount;
  }
 }
 return Result;
}


function MashpH() {
 var n = 0,
 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);
 }
 //console.log('MashpH() n: ' + n + ' pH: ' + pH);
 return pH;
}


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







$(document).ready(function() {

 var to_100 = false, // Fermentables adjust to 100%

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

 url = 'includes/db_recipes.php',
 // prepare the data
 source = {
  datatype: 'json',
  cache: false,
  datafields: [
   { name: 'record', type: 'number' },
   { name: 'uuid', type: 'string' },
   { name: 'locked', type: 'int' },
   { name: 'st_name', type: 'string' },
   { name: 'st_letter', type: 'string' },
   { name: 'st_guide', type: 'string' },
   { name: 'st_type', type: 'int' },
   { name: 'st_category', type: 'string' },
   { name: 'st_category_number', type: 'float' },
   { name: 'st_og_min', type: 'float' },
   { name: 'st_og_max', type: 'float' },
   { name: 'st_fg_min', type: 'float' },
   { name: 'st_fg_max', type: 'float' },
   { name: 'st_ibu_min', type: 'float' },
   { name: 'st_ibu_max', type: 'float' },
   { name: 'st_color_min', type: 'float' },
   { name: 'st_color_max', type: 'float' },
   { name: 'st_carb_min', type: 'float' },
   { name: 'st_carb_max', type: 'float' },
   { name: 'st_abv_min', type: 'float' },
   { name: 'st_abv_max', type: 'float' },
   { name: 'name', type: 'string' },
   { name: 'notes', type: 'string' },
   { name: 'type', type: 'int' },
   { name: 'batch_size', type: 'float' },
   { name: 'boil_size', type: 'float' },
   { name: 'boil_time', type: 'float' },
   { name: 'efficiency', type: 'float' },
   { name: 'est_og', type: 'float' },
   { name: 'est_fg', type: 'float' },
   { name: 'est_abv', type: 'float' },
   { name: 'est_color', type: 'float' },
   { name: 'color_method', type: 'int' },
   { name: 'est_ibu', type: 'float' },
   { name: 'ibu_method', type: 'int' },
   { name: 'est_carb', type: 'float' },
   { name: 'sparge_temp', type: 'float' },
   { name: 'sparge_ph', type: 'float' },
   { name: 'sparge_volume', type: 'float' },
   { name: 'sparge_source', type: 'int' },
   { name: 'sparge_acid_type', type: 'int' },
   { name: 'sparge_acid_perc', type: 'float' },
   { name: 'sparge_acid_amount', type: 'float' },
   { name: 'mash_ph', type: 'float' },
   { name: 'mash_name', type: 'string' },
   { name: 'calc_acid', type: 'int' },
   { name: 'w1_name', type: 'string' },
   { name: 'w1_amount', type: 'float' },
   { name: 'w1_calcium', type: 'float' },
   { name: 'w1_sulfate', type: 'float' },
   { name: 'w1_chloride', type: 'float' },
   { name: 'w1_sodium', type: 'float' },
   { name: 'w1_magnesium', type: 'float' },
   { name: 'w1_total_alkalinity', type: 'float' },
   { name: 'w1_ph', type: 'float' },
   { name: 'w1_cost', type: 'float' },
   { name: 'w2_name', type: 'string' },
   { name: 'w2_amount', type: 'float' },
   { name: 'w2_calcium', type: 'float' },
   { name: 'w2_sulfate', type: 'float' },
   { name: 'w2_chloride', type: 'float' },
   { name: 'w2_sodium', type: 'float' },
   { name: 'w2_magnesium', type: 'float' },
   { name: 'w2_total_alkalinity', type: 'float' },
   { name: 'w2_ph', type: 'float' },
   { name: 'w2_cost', type: 'float' },
   { name: 'wg_amount', type: 'float' },
   { name: 'wg_calcium', type: 'float' },
   { name: 'wg_sulfate', type: 'float' },
   { name: 'wg_chloride', type: 'float' },
   { name: 'wg_sodium', type: 'float' },
   { name: 'wg_magnesium', type: 'float' },
   { name: 'wg_total_alkalinity', type: 'float' },
   { name: 'wg_ph', type: 'float' },
   { name: 'wb_calcium', type: 'float' },
   { name: 'wb_sulfate', type: 'float' },
   { name: 'wb_chloride', type: 'float' },
   { name: 'wb_sodium', type: 'float' },
   { name: 'wb_magnesium', type: 'float' },
   { name: 'wb_total_alkalinity', type: 'float' },
   { name: 'wb_ph', type: 'float' },
   { name: 'wa_acid_name', type: 'int' },
   { name: 'wa_acid_perc', type: 'int' },
   { name: 'wa_base_name', type: 'int' },
   { name: 'fermentables', type: 'array' },
   { name: 'hops', type: 'array' },
   { name: 'miscs', type: 'array' },
   { name: 'yeasts', type: 'array' },
   { name: 'mashs', type: 'array' }
  ],
  id: 'record',
  url: url + '?record=' + my_record
 },
 // Load data and select one record.
 dataAdapter = new $.jqx.dataAdapter(source, {
  loadComplete: function() {
   var records = dataAdapter.records;
   dataRecord = records[0];
   // Hidden record uuid
   $('#name').val(dataRecord.name);
   $('#notes').val(dataRecord.notes);
   $('#locked').val(dataRecord.locked);
   $('#st_name').val(dataRecord.st_name);
   $('#st_letter').val(dataRecord.st_letter);
   $('#st_guide').val(dataRecord.st_guide);
   $('#st_category').val(dataRecord.st_category);
   $('#st_category_number').val(dataRecord.st_category_number);
   $('#st_type').val(StyleTypeData[dataRecord.st_type].nl);
   $('#type').val(dataRecord.type);
   $('#batch_size').val(dataRecord.batch_size);
   $('#boil_size').val(dataRecord.boil_size);
   $('#boil_time').val(dataRecord.boil_time);
   $('#efficiency').val(dataRecord.efficiency);
   $('#est_og').val(dataRecord.est_og);
   $('#est_og2').val(dataRecord.est_og);
   $('#st_og_min').val(dataRecord.st_og_min);
   $('#st_og_max').val(dataRecord.st_og_max);
   $('#est_fg').val(dataRecord.est_fg);
   $('#est_fg2').val(dataRecord.est_fg);
   $('#st_fg_min').val(dataRecord.st_fg_min);
   $('#st_fg_max').val(dataRecord.st_fg_max);
   $('#est_fg').val(dataRecord.est_fg);
   $('#est_fg2').val(dataRecord.est_fg);
   $('#st_fg_min').val(dataRecord.st_fg_min);
   $('#st_fg_max').val(dataRecord.st_fg_max);
   $('#est_color').val(dataRecord.est_color);
   $('#est_color2').val(dataRecord.est_color);
   $('#est_abv').val(dataRecord.est_abv);
   $('#est_abv2').val(dataRecord.est_abv);
   $('#st_abv_min').val(dataRecord.st_abv_min);
   $('#st_abv_max').val(dataRecord.st_abv_max);
   $('#st_color_min').val(dataRecord.st_color_min);
   $('#st_color_max').val(dataRecord.st_color_max);
   $('#color_method').val(dataRecord.color_method);
   $('#est_ibu').val(dataRecord.est_ibu);
   $('#est_ibu2').val(dataRecord.est_ibu);
   $('#st_ibu_min').val(dataRecord.st_ibu_min);
   $('#st_ibu_max').val(dataRecord.st_ibu_max);
   $('#ibu_method').val(dataRecord.ibu_method);
   $('#est_carb').val(dataRecord.est_carb);
   $('#st_carb_min').val(dataRecord.st_carb_min);
   $('#st_carb_max').val(dataRecord.st_carb_max);
   $('#mash_name').val(dataRecord.mash_name);
   $('#mash_ph').val(dataRecord.mash_ph);
   $('#sparge_temp').val(dataRecord.sparge_temp);
   $('#sparge_ph').val(dataRecord.sparge_ph);
   $('#sparge_volume').val(dataRecord.sparge_volume);
   $('#sparge_source').val(dataRecord.sparge_source);
   $('#sparge_acid_type').val(dataRecord.sparge_acid_type);
   $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc);
   $('#sparge_acid_amount').val(dataRecord.sparge_acid_amount * 1000);
   $('#calc_acid').val(dataRecord.calc_acid);
   $('#w1_name').val(dataRecord.w1_name);
   $('#w1_amount').val(dataRecord.w1_amount);
   $('#w1_calcium').val(dataRecord.w1_calcium);
   $('#w1_sulfate').val(dataRecord.w1_sulfate);
   $('#w1_chloride').val(dataRecord.w1_chloride);
   $('#w1_sodium').val(dataRecord.w1_sodium);
   $('#w1_magnesium').val(dataRecord.w1_magnesium);
   $('#w1_total_alkalinity').val(dataRecord.w1_total_alkalinity);
   $('#w1_ph').val(dataRecord.w1_ph);
   $('#w1_cost').val(dataRecord.w1_cost);
   $('#w2_name').val(dataRecord.w2_name);
   $('#w2_amount').val(dataRecord.w2_amount);
   $('#w2_calcium').val(dataRecord.w2_calcium);
   $('#w2_sulfate').val(dataRecord.w2_sulfate);
   $('#w2_chloride').val(dataRecord.w2_chloride);
   $('#w2_sodium').val(dataRecord.w2_sodium);
   $('#w2_magnesium').val(dataRecord.w2_magnesium);
   $('#w2_total_alkalinity').val(dataRecord.w2_total_alkalinity);
   $('#w2_ph').val(dataRecord.w2_ph);
   $('#w2_cost').val(dataRecord.w2_cost);
   $('#wg_amount').val(dataRecord.wg_amount);
   $('#wg_calcium').val(dataRecord.wg_calcium);
   $('#wg_sulfate').val(dataRecord.wg_sulfate);
   $('#wg_chloride').val(dataRecord.wg_chloride);
   $('#wg_sodium').val(dataRecord.wg_sodium);
   $('#wg_magnesium').val(dataRecord.wg_magnesium);
   $('#wg_total_alkalinity').val(dataRecord.wg_total_alkalinity);
   $('#wg_ph').val(dataRecord.wg_ph);
   $('#wb_calcium').val(dataRecord.wb_calcium);
   $('#wb_sulfate').val(dataRecord.wb_sulfate);
   $('#wb_chloride').val(dataRecord.wb_chloride);
   $('#wb_sodium').val(dataRecord.wb_sodium);
   $('#wb_magnesium').val(dataRecord.wb_magnesium);
   $('#wb_total_alkalinity').val(dataRecord.wb_total_alkalinity);
   $('#wb_ph').val(dataRecord.wb_ph);
   $('#wa_acid_name').val(dataRecord.wa_acid_name);
   $('#wa_acid_perc').val(dataRecord.wa_acid_perc);
   $('#wa_base_name').val(dataRecord.wa_base_name);
   editFermentable(dataRecord);
   editHop(dataRecord);
   editMisc(dataRecord);
   editYeast(dataRecord);
   editMash(dataRecord);
   $('#jqxTabs').jqxTabs('next');
  },
  loadError: function(jqXHR, status, error) {},
  beforeLoadComplete: function(records) { $('#jqxLoader').jqxLoader('open'); }
 }),

 // Inline fermentables editor
 editFermentable = function(data) {
  var fermentableSource = {
   localdata: data.fermentables,
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'f_name', type: 'string' },
    { name: 'f_origin', type: 'string' },
    { name: 'f_supplier', type: 'string' },
    { name: 'f_amount', type: 'float' },
    { name: 'f_cost', type: 'float' },
    { name: 'f_type', type: 'int' },
    { name: 'f_yield', type: 'float' },
    { name: 'f_color', type: 'float' },
    { name: 'f_coarse_fine_diff', type: 'float' },
    { name: 'f_moisture', type: 'float' },
    { name: 'f_diastatic_power', type: 'float' },
    { name: 'f_protein', type: 'float' },
    { name: 'f_max_in_batch', type: 'float' },
    { name: 'f_graintype', type: 'int' },
    { name: 'f_added', type: 'int' },
    { name: 'f_dissolved_protein', type: 'float' },
    { name: 'f_recommend_mash', type: 'int' },
    { name: 'f_add_after_boil', type: 'int' },
    { name: 'f_adjust_to_total_100', type: 'int' },
    { name: 'f_percentage', type: 'float' },
    { name: 'f_di_ph', type: 'float' },
    { name: 'f_acid_to_ph_57', type: 'float' },
    { name: 'f_inventory', type: 'float' },
    { name: 'f_avail', type: 'int' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    //console.log("fermentable addrow "+rowid);
    commit(true);
   },
   deleterow: function(rowid, commit) {
    //console.log("fermentable deleterow "+rowid);
    commit(true);
   },
   updaterow: function(rowid, rowdata, commit) {
    //console.log("fermentable updaterow "+rowid);
    commit(true);
   }
  },
  fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);

  $('#fermentableGrid').jqxGrid({
   width: 1240,
   height: 470,
   source: fermentableAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    //var me = this;
    var container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>');
    toolbar.append(container);
    container.append('<div style="float: left; margin-left: 165px;" id="faddrowbutton"></div>');
    container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
    container.append('<div style="float: left; margin-left: 10px;" id="finstockbutton"></div>');
    container.append('<input style="float: left; margin-left: 400px;" id="fdeleterowbutton" type="button" value="Verwijder mout" />');
    // add fermentable from dropdownlist.
    $('#faddrowbutton').jqxDropDownList({
     placeHolder: 'Kies mout:',
     theme: theme,
     template: 'primary',
     source: fermentablelist,
     displayMember: 'name',
     width: 150,
     height: 27,
     dropDownWidth: 500,
     dropDownHeight: 500,
     renderer: function(index, label, value) {
      var datarecord = fermentablelist.records[index];
      return datarecord.supplier + ' / ' + datarecord.name + ' (' + datarecord.color + ' EBC)';
     }
    });
    $('#faddrowbutton').on('select', function(event) {
     if (event.args) {
      var 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['[_cost'] = datarecord.cost;
      row['f_type'] = datarecord.type;
      row['f_yield'] = datarecord.yield;
      row['f_color'] = datarecord.color;
      row['f_coarse_fine_diff'] = datarecord.coarse_fine_diff;
      row['f_moisture'] = datarecord.moisture;
      row['f_diastatic_power'] = datarecord.diastatic_power;
      row['f_protein'] = datarecord.protein;
      row['f_max_in_batch'] = datarecord.max_in_batch;
      row['f_graintype'] = datarecord.graintype;
      if (datarecord.add_after_boil) {
       row['f_added'] = 2;     // Fermentation
      } else if ((datarecord.type == 1) || (datarecord.type == 4)) {  // Sugar or Adjunct
       row['f_added'] = 1;     // Boil
      } else {
       row['f_added'] = 0;     // Mash
      }
      row['f_dissolved_protein'] = datarecord.dissolved_protein;
      row['f_recommend_mash'] = datarecord.recommend_mash;
      row['f_add_after_boil'] = datarecord.add_after_boil;
      if (rowscount == 0) {
       // The first fermentable
       row['f_adjust_to_total_100'] = 1;
       row['f_percentage'] = 100;
      } else {
       row['f_adjust_to_total_100'] = 0;
       row['f_percentage'] = 0;
      }
      row['f_di_ph'] = datarecord.di_ph;
      row['f_acid_to_ph_57'] = datarecord.acid_to_ph_57;
      row['f_inventory'] = datarecord.inventory;
      $('#fermentableGrid').jqxGrid('addrow', null, row);
     }
    });
    $('#finstockbutton').jqxCheckBox({ theme: theme, height: 27 });
    $('#finstockbutton').on('change', function(event) {
     fermentableinstock = event.args.checked;
     fermentablelist.dataBind();
    });

    // delete selected fermentable.
    $('#fdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150 });
    $('#fdeleterowbutton').on('click', function() {
     var id, percent, amount, i, rowdata, rowscount, selectedrowindex = $('#fermentableGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      id = $('#fermentableGrid').jqxGrid('getrowid', selectedrowindex);
      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() {
    calcFermentables();
    $('#jqxTabs').jqxTabs('next');
   },
   columns: [
    { text: 'Vergistbaar ingredi&euml;nt', datafield: 'f_name',
     cellsrenderer: function(index, datafield, value, defaulvalue, column, rowdata) {
      return '<span style="margin: 3px; margin-top: 6px; float: left;">' +
      rowdata.f_supplier + ' / ' + rowdata.f_name + ' (' + rowdata.f_color + ' EBC)</span>';
     }
    },
    { text: 'Type', width: 100, datafield: 'f_type',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<span style="margin: 3px; margin-top: 6px; float: left;">' + FermentableTypeData[value].nl + '</span>';
     }
    },
    { text: 'Moment', width: 110, datafield: 'f_added',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<span style="margin: 3px; margin-top: 6px; float: left;">' + AddedData[value].nl + '</span>';
     }
    },
    { text: 'Maxinbatch', datafield: 'f_max_in_batch', hidden: true },
    { text: 'Opbrengst', editable: false, datafield: 'f_yield', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
    { text: 'Gewicht Kg', datafield: 'f_amount', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f3' },
    { text: 'Voorr. Kg', datafield: 'f_inventory', width: 120, align: 'right',
     cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
      var color = '#ffffff';
      if (value < rowdata.f_amount)
       color = '#ff4040';
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' +
             color + ';">' + fermentableAdapter.formatNumber(value, 'f3') + '</span>';
     }
    },
    { text: 'Procent', datafield: 'f_percentage', width: 90, align: 'right',
     cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
      var color = '#ffffff';
      if (value > rowdata.f_max_in_batch)
       color = '#ff4040';
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' +
             color + ';">' + fermentableAdapter.formatNumber(value, 'p1') + '</span>';
     }
    },
    { text: '100%', align: 'center', datafield: 'f_adjust_to_total_100', columntype: 'checkbox', width: 70 },
    { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center', cellsrenderer: function() {
     return 'Wijzig';
     }, buttonclick: function(row) {
      fermentableRow = row;
      fermentableData = $('#fermentableGrid').jqxGrid('getrowdata', fermentableRow);
      $('#wf_name').val(fermentableData.f_name);
      $('#wf_amount').val(fermentableData.f_amount);
      $('#wf_percentage').val(fermentableData.f_percentage);
      $('#wf_max_in_batch').val(fermentableData.f_max_in_batch);
      $('#wf_adjust_to_total_100').val(fermentableData.f_adjust_to_total_100);
      $('#wf_added').val(fermentableData.f_added);
      // show the popup window.
      $('#popupFermentable').jqxWindow('open');
     }
    }
   ]
  });
 };

 // Inline hops editor
 var editHop = function(data) {
  var hopSource = {
   localdata: data.hops,
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'h_name', type: 'string' },
    { name: 'h_origin', type: 'string' },
    { name: 'h_amount', type: 'float' },
    { name: 'h_cost', type: 'float' },
    { name: 'h_type', type: 'int' },
    { name: 'h_form', type: 'int' },
    { name: 'h_useat', type: 'int' },
    { name: 'h_time', type: 'float' },
    { name: 'h_alpha', type: 'float' },
    { name: 'h_beta', type: 'float' },
    { name: 'h_hsi', type: 'float' },
    { name: 'h_humulene', type: 'float' },
    { name: 'h_caryophyllene', type: 'float' },
    { name: 'h_cohumulone', type: 'float' },
    { name: 'h_myrcene', type: 'float' },
    { name: 'h_total_oil', type: 'float' },
    { name: 'h_inventory', type: 'float' },
    { name: 'h_avail', type: 'int' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    //console.log("hop addrow "+rowid);
    commit(true);
   },
   deleterow: function(rowid, commit) {
    //console.log("hop deleterow "+rowid);
    commit(true);
   },
   updaterow: function(rowid, rowdata, commit) {
    //console.log("hop updaterow "+rowid);
    commit(true);
   }
  },
  hopAdapter = new $.jqx.dataAdapter(hopSource);

  $('#hopGrid').jqxGrid({
   width: 1240,
   height: 560,
   source: hopAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    //var me = this;
    var container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>');
    toolbar.append(container);
    container.append('<div style="float: left; margin-left: 165px;" id="haddrowbutton"></div>');
    container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
    container.append('<div style="float: left; margin-left: 10px;" id="hinstockbutton"></div>');
    container.append('<input style="float: left; margin-left: 400px;" id="hdeleterowbutton" type="button" value="Verwijder hop" />');
    // add hop from dropdownlist.
    $('#haddrowbutton').jqxDropDownList({
     placeHolder: 'Kies hop:',
     theme: theme,
     template: 'primary',
     source: hoplist,
     displayMember: 'name',
     width: 150,
     height: 27,
     dropDownWidth: 500,
     dropDownHeight: 500,
     renderer: function(index, label, value) {
      var datarecord = hoplist.records[index];
      return datarecord.origin + ' / ' + datarecord.name + ' (' + datarecord.alpha + '% &alpha;)';
     }
    });
    $('#haddrowbutton').on('select', function(event) {
     if (event.args) {
      var 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;
      $('#hopGrid').jqxGrid('addrow', null, row);
     }
     $('#haddrowbutton').jqxDropDownList('clearSelection');
    });

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

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

 // Inline miscs editor
 var editMisc = function(data) {
  var miscSource = {
   localdata: data.miscs,
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'm_name', type: 'string' },
    { name: 'm_amount', type: 'float' },
    { name: 'm_cost', type: 'float' },
    { name: 'm_type', type: 'int' },
    { name: 'm_use_use', type: 'int' },
    { name: 'm_time', type: 'float' },
    { name: 'm_amount_is_weight', type: 'int' },
    { name: 'm_inventory', type: 'float' },
    { name: 'm_avail', type: 'int' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    //console.log("misc addrow "+rowid);
    commit(true);
   },
   deleterow: function(rowid, commit) {
    //console.log("misc deleterow "+rowid);
    commit(true);
   },
   updaterow: function(rowid, rowdata, commit) {
    //console.log("misc updaterow "+rowid);
    commit(true);
   }
  },
  miscAdapter = new $.jqx.dataAdapter(miscSource, {
   beforeLoadComplete: function(records) {
    var i, row, 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 'Melkzuur':
       $('#wa_acid_name').val(0);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(80);
       last_acid = 'Melkzuur';
       break;
      case 'Zoutzuur':
       $('#wa_acid_name').val(1);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(80);
       last_acid = 'Zoutzuur';
       break;
      case 'Fosforzuur':
       $('#wa_acid_name').val(2);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(80);
       last_acid = 'Fosforzuur';
       break;
      case 'Zwavelzuur':
       $('#wa_acid_name').val(3);
       $('#wa_acid').val(row.m_amount * 1000);
       $('#wa_acid_perc').val(80);
       last_acid = 'Zwavelzuur';
       break;
      case 'NaHCO3':
       $('#wa_base_name').val(0);
       $('#wa_base').val(row.m_amount * 1000);
       last_base = 'NaHCO3';
       break;
      case 'Na2CO3':
       $('#wa_base_name').val(1);
       $('#wa_base').val(row.m_amount * 1000);
       last_base = 'Na2CO3';
       break;
      case 'CaCO3':
       $('#wa_base_name').val(2);
       $('#wa_base').val(row.m_amount * 1000);
       last_base = 'CaCO3';
       break;
      case 'Ca(OH)2':
       $('#wa_base_name').val(3);
       $('#wa_base').val(row.m_amount * 1000);
       last_base = 'Ca(OH)2';
       break;
     }
    }
    return data;
   },
   loadError: function(jqXHR, status, error) {}
  });
  $('#miscGrid').jqxGrid({
   width: 1240,
   height: 575,
   source: miscAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    //var me = this;
    var container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>');
    toolbar.append(container);
    container.append('<div style="float: left; margin-left: 165px;" id="maddrowbutton"></div>');
    container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
    container.append('<div style="float: left; margin-left: 10px;" id="minstockbutton"></div>');
    container.append('<input style="float: left; margin-left: 400px;" id="mdeleterowbutton" type="button" value="Verwijder ingredi&euml;nt" />');
    // add misc from dropdownlist.
    $('#maddrowbutton').jqxDropDownList({
     placeHolder: 'Kies ingredi&euml;nt:',
     theme: theme,
     template: 'primary',
     source: misclist,
     displayMember: 'name',
     width: 150,
     height: 27,
     dropDownWidth: 500,
     dropDownHeight: 500
    });
    $('#maddrowbutton').on('select', function(event) {
     if (event.args) {
      var 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'] = 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 });
    $('#minstockbutton').on('change', function(event) {
     miscinstock = event.args.checked;
     misclist.dataBind();
    });
    // delete selected misc.
    $('#mdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150 });
    $('#mdeleterowbutton').on('click', function() {
     var rowscount, type, id, selectedrowindex = $('#miscGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#miscGrid').jqxGrid('getdatainformation').rowscount;
     type = $('#miscGrid').jqxGrid('getcellvalue', selectedrowindex, 'm_type');
     if (selectedrowindex >= 0 && selectedrowindex < rowscount && type != 4) {       // Water agent
      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) {
      var amount, color = '#ffffff', vstr = rowdata.m_amount_is_weight ? 'gr' : 'ml';
      if (value < rowdata.m_amount)
       color = '#ff4040';
      amount = dataAdapter.formatNumber(value * 1000, 'f2') + ' ' + vstr;
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
     },
    },
    { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      miscRow = row;
      miscData = $('#miscGrid').jqxGrid('getrowdata', miscRow);
      if (miscData.m_type == 4) {
       alert('Brouwzouten wijzigen in de water tab.');
      } else {
       if (miscData.m_amount_is_weight)
        $('#wm_pmpt_amount').html('Gewicht gram:');
       else
        $('#wm_pmpt_amount').html('Volume ml:');
       $('#wm_name').val(miscData.m_name);
       $('#wm_amount').val(miscData.m_amount * 1000);
       if ((miscData.m_use_use == 3) || (miscData.m_use_use == 4))     // Primary or Secondary
        $('#wm_time').val(miscData.m_time / 1440);
       else
        $('#wm_time').val(miscData.m_time);
       $('#wm_use_use').val(miscData.m_use_use);
       // show the popup window.
       $('#popupMisc').jqxWindow('open');
      }
     }
    }
   ]
  });
 };

 // Inline yeasts editor
 var editYeast = function(data) {
  var yeastSource = {
   localdata: data.yeasts,
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'y_name', type: 'string' },
    { name: 'y_laboratory', type: 'string' },
    { name: 'y_product_id', type: 'string' },
    { name: 'y_amount', type: 'float' },
    { name: 'y_cost', type: 'float' },
    { name: 'y_type', type: 'int' },
    { name: 'y_form', type: 'int' },
    { name: 'y_flocculation', type: 'int' },
    { name: 'y_min_temperature', type: 'float' },
    { name: 'y_max_temperature', type: 'float' },
    { name: 'y_attenuation', type: 'float' },
    { name: 'y_use', type: 'int' },
    { name: 'y_cells', type: 'float' },
    { name: 'y_tolerance', type: 'float' },
    { name: 'y_inventory', type: 'float' },
    { name: 'y_avail', type: 'int' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    //console.log("yeast addrow "+rowid);
    commit(true);
   },
   deleterow: function(rowid, commit) {
    //console.log("yeast deleterow "+rowid);
    commit(true);
   },
   updaterow: function(rowid, rowdata, commit) {
    //console.log("yeast updaterow "+rowid);
    commit(true);
   }
  },
  yeastAdapter = new $.jqx.dataAdapter(yeastSource);

  $('#yeastGrid').jqxGrid({
   width: 1240,
   height: 350,
   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,
     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'] = 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;
      $('#yeastGrid').jqxGrid('addrow', null, row);
     }
     $('#yaddrowbutton').jqxDropDownList('clearSelection');
    });
    $('#yinstockbutton').jqxCheckBox({ theme: theme, height: 27, disabled: (dataRecord.stage > 3) });
    $('#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 > 3) });
    $('#ydeleterowbutton').on('click', function() {
     var id, rowscount, selectedrowindex = $('#yeastGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#yeastGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      id = $('#yeastGrid').jqxGrid('getrowid', selectedrowindex);
      $('#yeastGrid').jqxGrid('deleterow', id);
     }
    });
   },
   ready: function() {
    calcFermentables();
    $('#jqxTabs').jqxTabs('next');
   },
   columns: [
    { text: 'Gist', datafield: 'y_name' },
    { text: 'Laboratorium', width: 150, datafield: 'y_laboratory' },
    { text: 'Code', width: 90, datafield: 'y_product_id' },
    { text: 'Soort', width: 100, datafield: 'y_form',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastFormData[value].nl + '</span>';
     }
    },
    { text: 'Min. &deg;C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_min_temperature' },
    { text: 'Max. &deg;C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_max_temperature' },
    { text: 'Tol. %', width: 60, align: 'right', cellsalign: 'right', datafield: 'y_tolerance',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      var amount = '', color = '#ffffff';
      if (value > 0) {
       amount = dataAdapter.formatNumber(value, 'f1');
       if (dataRecord.est_abv > value)
        color = '#ff4040';
      }
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
     }
    },
    { text: 'Attn. %', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_attenuation', cellsformat: 'f1' },
    { text: 'Voor', width: 120, datafield: 'y_use',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastUseData[value].nl + '</span>';
     }
    },
    { text: 'Hoeveel', datafield: 'y_amount', width: 90, align: 'right',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      var amount = dataAdapter.formatNumber(value * 1000, 'f0') + ' ml';
      if (rowdata.y_form == 0)        // Liquid
       amount = dataAdapter.formatNumber(value, 'f0') + ' pk';
      else if (rowdata.y_form == 1)   // Dry
       amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr';
      return '<span style="margin: 4px; margin-top: 6px; float: right;">' + amount + '</span>';
     }
    },
    { text: 'Voorraad', datafield: 'y_inventory', width: 90, align: 'right',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      var color, amount;
      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)   // Dry
       amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr';
      return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>';
     }
    },
    { text: '', datafield: 'Edit', columntype: 'button', width: 90, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      yeastRow = row;
      yeastData = $('#yeastGrid').jqxGrid('getrowdata', yeastRow);
      if (yeastData.y_form == 0) {
       $('#wy_pmpt_amount').html('Pak(ken):');
       $('#wy_amount').val(yeastData.y_amount);
       $('#wy_amount').jqxNumberInput({ decimalDigits: 0 });
      } else if (yeastData.y_form == 1) {
       $('#wy_pmpt_amount').html('Gewicht gram:');
       $('#wy_amount').val(yeastData.y_amount * 1000);
       $('#wy_amount').jqxNumberInput({ decimalDigits: 1 });
      } else {
       $('#wy_pmpt_amount').html('Volume ml:');
       $('#wy_amount').val(yeastData.y_amount * 1000);
       $('#wy_amount').jqxNumberInput({ decimalDigits: 0 });
      }
      $('#wy_name').val(yeastData.y_name);
      $('#wy_laboratory').val(yeastData.y_laboratory);
      $('#wy_product_id').val(yeastData.y_product_id);
      $('#wy_use').val(yeastData.y_use);
      // show the popup window.
      $('#popupYeast').jqxWindow('open');
     }
    }
   ]
  });
 };

 // inline mash editor
 var editMash = function(data) {
  var generaterow = function() {
   var row = {};
   row['step_name'] = 'Stap 1';
   row['step_type'] = 0;
   row['step_infuse_amount'] = 15;
   row['step_temp'] = 62.0;
   row['step_time'] = 20.0;
   row['step_thickness'] = 0;
   row['ramp_time'] = 1.0;
   row['end_temp'] = 62.0;
   return row;
  };
  var mashSource = {
   localdata: data.mashs,
   datatype: 'local',
   cache: false,
   async: false,
   datafields: [
    { name: 'step_name', type: 'string' },
    { name: 'step_type', type: 'int' },
    { name: 'step_infuse_amount', type: 'float' },
    { name: 'step_temp', type: 'float' },
    { name: 'step_time', type: 'float' },
    { name: 'step_thickness', type: 'float' },
    { name: 'ramp_time', type: 'float' },
    { name: 'end_temp', type: 'float' }
   ],
   addrow: function(rowid, rowdata, position, commit) {
    commit(true);
   },
   deleterow: function(rowid, commit) {
    commit(true);
   }
  },
  mashAdapter = new $.jqx.dataAdapter(mashSource, {
   beforeLoadComplete: function(records) {
    mash_infuse = 0;
    var row, i, 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_thickness = 0; // Init this field.
     data.push(row);
    }
   },
  });
  $('#mashGrid').jqxGrid({
   width: 1240,
   height: 400,
   source: mashAdapter,
   theme: theme,
   selectionmode: 'singlerow',
   showtoolbar: true,
   rendertoolbar: function(toolbar) {
    //var me = this;
    var container = $('<div style="overflow: hidden; position: relative; margin: 5px;"></div>');
    toolbar.append(container);
    container.append('<input style="float: left; margin-left: 165px;" id="saddrowbutton" type="button" value="Nieuwe stap" />');
    container.append('<input style="float: left; margin-left: 565px;" id="sdeleterowbutton" type="button" value="Verwijder stap" />');
    $('#saddrowbutton').jqxButton({ template: 'primary', theme: theme, height: 27, width: 150 });
    $('#saddrowbutton').on('click', function() {
     var datarow = generaterow();
     $('#mashGrid').jqxGrid('addrow', null, datarow);
    });
    // delete selected yeast.
    $('#sdeleterowbutton').jqxButton({ template: 'danger', theme: theme, height: 27, width: 150 });
    $('#sdeleterowbutton').on('click', function() {
     var id, rowscount, selectedrowindex = $('#mashGrid').jqxGrid('getselectedrowindex');
     rowscount = $('#mashGrid').jqxGrid('getdatainformation').rowscount;
     if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
      id = $('#mashGrid').jqxGrid('getrowid', selectedrowindex);
      $('#mashGrid').jqxGrid('deleterow', id);
     }
    });
   },
   ready: function() {
    calcFermentables();
    calcInit();
    calcMash();
    $('#jqxLoader').jqxLoader('close');
    $('#jqxTabs').jqxTabs('first');
   },
   columns: [
    { text: 'Stap naam', datafield: 'step_name' },
    { text: 'Stap type', datafield: 'step_type', width: 175,
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<div style="margin: 4px;">' + MashStepTypeData[value].nl + '</div>';
     }
    },
    { text: 'Start &deg;C', datafield: 'step_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
    { text: 'Eind &deg;C', datafield: 'end_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
    { text: 'Rust min.', datafield: 'step_time', width: 90, align: 'right', cellsalign: 'right' },
    { text: 'Stap min.', datafield: 'ramp_time', width: 90, align: 'right', cellsalign: 'right' },
    { text: 'Infuse L.', datafield: 'step_infuse_amount', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' },
    { text: 'L/Kg.', datafield: 'step_thickness', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f2' },
    { text: '', datafield: 'Edit', columntype: 'button', width: 100, align: 'center',
     cellsrenderer: function() {
      return 'Wijzig';
     }, buttonclick: function(row) {
      mashRow = row;
      mashData = $('#mashGrid').jqxGrid('getrowdata', mashRow);
      $('#wstep_name').val(mashData.step_name);
      $('#wstep_type').val(mashData.step_type);
      $('#wstep_infuse_amount').val(mashData.step_infuse_amount);
      $('#wstep_temp').val(mashData.step_temp);
      $('#wend_temp').val(mashData.end_temp);
      $('#wstep_time').val(mashData.step_time);
      $('#wramp_time').val(mashData.ramp_time);
      if (mashData.step_type == 0) {
       $('#wstep_infuse_amount').show();
       $('#wstep_pmpt').show();
      } else {
       $('#wstep_infuse_amount').hide();
       $('#wstep_pmpt').hide();
      }
      // show the popup window.
      $('#popupMash').jqxWindow('open');
     }
    }
   ]
  });
 };




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

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


 function setWaterAgent(name, amount) {

  var record, records, miscs, i, id, row, found = false, 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() {
      records = miscs.records;
      for (i = 0; i < records.length; i++) {
       record = records[i];
       if (record.name == name) {
        row = {};
        row['m_name'] = record.name;
        row['m_amount'] = amount / 1000;
        row['m_cost'] = record.cost;
        row['m_type'] = record.type;
        row['m_use_use'] = record.use_use;
        row['m_time'] = 0;
        row['m_amount_is_weight'] = record.amount_is_weight;
        row['m_inventory'] = record.inventory;
        row['m_avail'] = 1;
        $('#miscGrid').jqxGrid('addrow', null, row);
       }
      }
     }
    });
    miscs.dataBind();
    return;
   }
  }
 }


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

  console.log('calcWater()');
  var liters = 0,
  calcium = 0,
  magnesium = 0,
  sodium = 0,
  total_alkalinity = 0,
  bicarbonate = 0,
  chloride = 0,
  sulfate = 0,
  ph = 0,
  RA = 0,
  frac = 0,
  TpH = 0,
  protonDeficit = 0,
  AT, BT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc,
  r1d, r2d, f1d, f2d, f3d,
  deltapH, deltapd, pd, n,
  piCLSO4_low, piCLSO4_high, Res,
  wg_calcium, wg_sodium, wg_total_alkalinity, wg_chloride, wg_sulfate, wg_bicarbonate;

  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;
  }
  $('#wg_amount').val(liters);
  wg_calcium = calcium;
  $('#wg_calcium').val(Math.round(calcium * 10) / 10);
  //wg_magnesium = magnesium;
  $('#wg_magnesium').val(Math.round(magnesium * 10) / 10);
  wg_sodium = sodium;
  $('#wg_sodium').val(Math.round(sodium * 10) / 10);
  wg_total_alkalinity = total_alkalinity;
  $('#wg_total_alkalinity').val(Math.round(total_alkalinity * 10) / 10);
  wg_chloride = chloride;
  $('#wg_chloride').val(Math.round(chloride * 10) / 10);
  wg_sulfate = sulfate;
  $('#wg_sulfate').val(Math.round(sulfate * 10) / 10);
  // Note: brouwhulp has the malts included here in the result.
  //wg_ph = ph;
  $('#wg_ph').val(Round(ph, 1));
  $('#wb_ph').val(Round(MashpH(), 1));
  bicarbonate = total_alkalinity * 1.22;
  wg_bicarbonate = bicarbonate;

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

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

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

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

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

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

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

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

  result = GetAcidSpecs(AT);
  pK1 = result.pK1;
  pK2 = result.pK2;
  pK3 = result.pK3;
  MolWt = result.MolWt;
  AcidSG = result.AcidSG;
  AcidPrc = result.AcidPrc;

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

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

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

   TpH = parseFloat(dataRecord.mash_ph);
   pHa = MashpH(); // This one is in demi water, should be in adjusted water???
   // Then calculate the new pH with added acids
   if (parseFloat($('#wa_acid').jqxNumberInput('decimal')) > 0) {
    console.log('TpH: ' + TpH + ' water: ' + pHa);
    Acid = parseFloat($('#wa_acid').jqxNumberInput('decimal'));
    if (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) == 0)
     $('#wa_acid_perc').val(AcidPrc);
    Acid = Acid / AcidPrc * (parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')) / 100); // ml
    Acid *= AcidSG; // ml
    Acid /= MolWt;  // mg
    Acidmg = Acid;

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

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

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

  // 2:1 Sulfate to Chroride IPA's, Pale Ales.
  // 1:1 Sulfate to Chloride Balanced
  // 1:2 Sulfate to Chloride Malty
  // Note, values below are the other way, cl to so4!
  // So: 0.5 is IPA's, Pale Ales.
  //       1 Balanced
  //       2 Malty.
  $('#tgt_bu').val(Math.round(GetBUGU() * 100) / 100);
  // From brouwhulp.
  if (GetBUGU() < 0.32)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig en zoet</span>");
  else if (GetBUGU() < 0.43)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig, zoet</span>");
  else if (GetBUGU() < 0.52)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Evenwichtig</span>");
  else if (GetBUGU() < 0.63)
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Licht hoppig, bitter</span>");
  else
   $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Extra hoppig, zeer bitter</span>");
  $('#tgt_cl_so4').val(Math.round(GetOptClSO4ratio() * 10) / 10);
  if (sulfate > 0)
   RA = chloride / sulfate;
  else
   RA = 10;
  $('#got_cl_so4').val(Math.round(RA * 10) / 10);
  piCLSO4_low = 0.8 * GetOptClSO4ratio();
  piCLSO4_high = 1.2 * GetOptClSO4ratio();
  Res = 'normaal';
  if (RA < piCLSO4_low)
   Res = 'laag';
  else if (RA > piCLSO4_high)
   Res = 'hoog';
  setRangeIndicator('cl_so4', 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_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 >= 0 && magnesium <= 30) {
   setRangeIndicator('magnesium', 'normaal');
  } else {
   setRangeIndicator('magnesium', 'hoog');
  }
  if (sodium <= 150) {
   setRangeIndicator('sodium', 'normaal');
  } else {
   setRangeIndicator('sodium', 'hoog');
  }
  // Both chloride and sulfate should be above 50 according to
  // John Palmer. So the Cl/SO4 ratio calculation will work.
  if (chloride <= 50) {
   setRangeIndicator('chloride', 'laag');
  } else if (chloride <= 100) {
   setRangeIndicator('chloride', 'normaal');
  } else {
   setRangeIndicator('chloride', 'hoog');
  }
  if (sulfate <= 50) {
   setRangeIndicator('sulfate', 'laag');
  } else if (sulfate <= 350) {
   setRangeIndicator('sulfate', 'normaal');
  } else {
   setRangeIndicator('sulfate', 'hoog');
  }
  if (ph < 5.2) {
   setRangeIndicator('ph', 'laag');
  } else if (ph > 5.6) {
   setRangeIndicator('ph', 'hoog');
  } else {
   setRangeIndicator('ph', 'normaal');
  }
  calcSparge();
 }


 function calcSparge() {
  var TargetpH, Source_pH, Source_alkalinity, r1, r2, d, f1, f3,
  r143, r243, d43, f143, f343, alkalinity, Ct, r1g, r2g, dg, f1g, f3g,
  Acid, AT, result, pK1, pK2, pK3, MolWt, AcidSG, AcidPrc, fract;

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

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

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

  //Step 3. Convert the sample alkalinity to milliequivalents/L
  alkalinity = Source_alkalinity / 50;
  //Step 4. Solve
  Ct = (alkalinity - 1000 * (Math.pow(10, -4.3) - Math.pow(10, -Source_pH))) / ((f143 - f1) + (f3 - f343));

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

  //Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
  Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH);  //mEq/l
  Acid += 0.01;   // Add acid that would be required for distilled water.
  if (dataRecord.sparge_acid_type < 0 || dataRecord.sparge_acid_type > 3) {
   dataRecord.sparge_acid_type = 0;
   $('#sparge_acid_type').val(0);
  }

  //Step 8. Get the acid data.
  AT = dataRecord.sparge_acid_type;
  result = GetAcidSpecs(AT);
  pK1 = result.pK1;
  pK2 = result.pK2;
  pK3 = result.pK3;
  MolWt = result.MolWt;
  AcidSG = result.AcidSG;
  AcidPrc = result.AcidPrc;
  fract = CalcFrac(TargetpH, pK1, pK2, pK3);

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

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

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

  Acid *= dataRecord.sparge_volume; //ml lactic acid total
  Acid = Round(Acid, 2);
  dataRecord.sparge_acid_amount = Acid / 1000;
  $('#sparge_acid_amount').val(Acid);
 }

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

  console.log('calcFermentablesFromOG(' + OG + ')');
  var 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 = $('#fermentableGrid').jqxGrid('getdatainformation').rowscount;

  for (i = 0; i < rowscount; i++) {
   row = $('#fermentableGrid').jqxGrid('getrowdata', 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 = Math.round((sug / tot) * 1000) / 1000;

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

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

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

   if (newval > mash_infuse) {
    $('#w2_amount').val(dataRecord.w2_amount);
    return;
   }
   dataRecord.w1_amount = parseFloat($('#wg_amount').jqxNumberInput('decimal')) - newval;
   $('#w1_amount').val(dataRecord.w1_amount);
   dataRecord.w2_amount = newval;
   console.log('new: ' + event.args.value + ' w1: ' + dataRecord.w1_amount + '  w2: ' + dataRecord.w2_amount);
   calcWater();
  });
  $('#wa_cacl2').on('change', function(event) {
   if (event.args) {
    setWaterAgent('CaCl2', 0); // This can prevent double entries.
    setWaterAgent('CaCl2', event.args.value);
    calcWater();
   }
  });
  $('#wa_caso4').on('change', function(event) {
   if (event.args) {
    setWaterAgent('CaSO4', 0);
    setWaterAgent('CaSO4', event.args.value);
    calcWater();
   }
  });
  $('#wa_mgso4').on('change', function(event) {
   if (event.args) {
    setWaterAgent('MgSO4', 0);
    setWaterAgent('MgSO4', event.args.value);
    calcWater();
   }
  });
  $('#wa_nacl').on('change', function(event) {
   if (event.args) {
    setWaterAgent('NaCl', 0);
    setWaterAgent('NaCl', event.args.value);
    calcWater();
   }
  });
  $('#wa_base_name').on('change', function(event) {
   if (event.args) {
    var index = event.args.index;
    setWaterAgent(last_base, 0);
    last_base = BaseTypeData[index].nl;
    setWaterAgent(last_base, parseFloat($('#wa_base').jqxNumberInput('decimal')));
    dataRecord.wa_base_name = index;
    calcWater();
   }
  });
  $('#wa_base').on('change', function(event) {
   var name = BaseTypeData[$('#wa_base_name').val()].nl;
   setWaterAgent(name, parseFloat(event.args.value));
   calcWater();
  });
  $('#wa_acid_name').on('change', function(event) {
   if (event.args) {
    var index = event.args.index;
    setWaterAgent(last_acid, 0);
    last_acid = AcidTypeData[index].nl;
    setWaterAgent(last_acid, parseFloat($('#wa_acid').jqxNumberInput('decimal')));
    dataRecord.wa_acid_name = index;
    calcWater();
   }
  });
  $('#wa_acid').on('change', function(event) {
   var name = AcidTypeData[$('#wa_acid_name').val()].nl;
   setWaterAgent(name, parseFloat(event.args.value));
   calcWater();
  });
  $('#wa_acid_perc').on('change', function(event) { calcWater(); });

  $('#color_method').on('change', function(event) {
   dataRecord.color_method = event.args.index;
   calcFermentables();
  });
  $('#ibu_method').on('change', function(event) {
   dataRecord.ibu_method = event.args.index;
   calcFermentables();
   calcIBUs();
  });
  $('#batch_size').on('change', function(event) {
   console.log('batch_size change:' + event.args.value + ' old:' + dataRecord.batch_size);
   var factor, new_boil = parseFloat(event.args.value) + dataRecord.boil_size - dataRecord.batch_size;
   factor = parseFloat(event.args.value) / dataRecord.batch_size;
   dataRecord.boil_size = new_boil;
   $('#boil_size').val(Round(new_boil, 2));
   dataRecord.sparge_volume *= factor;
   $('#sparge_volume').val(dataRecord.sparge_volume);
   dataRecord.batch_size = parseFloat(event.args.value);
   calcFermentablesFromOG(parseFloat($('#est_og').jqxNumberInput('decimal')));     // Keep the OG
   adjustWaters(factor);
   calcFermentables();
   adjustHops(factor);
   adjustMiscs(factor);
   adjustYeasts(factor);
   calcIBUs();
   calcWater();
   calcSparge();
   calcMash();
  });
  $('#boil_time').on('change', function(event) {
   console.log('boil_time change:' + parseFloat(event.args.value) + ' old:' + dataRecord.boil_time);
   var new_boil, new_evap, old_evap = parseFloat(dataRecord.boil_size) - parseFloat(dataRecord.batch_size);
   new_evap = old_evap * (parseFloat(event.args.value) / dataRecord.boil_time);
   new_boil = parseFloat(dataRecord.batch_size) + new_evap;
   dataRecord.boil_time = parseFloat(event.args.value);
   dataRecord.boil_size = new_boil;
   $('#boil_size').val(Round(new_boil, 2));
   calcFermentables();
   // TODO: adjust the hops, miscs, yeast, water.
   calcIBUs();
  });
  $('#efficiency').on('change', function(event) {
   var estog = parseFloat($('#est_og').jqxNumberInput('decimal'));
   dataRecord.efficiency = parseFloat(event.args.value);
   console.log('efficiency change:' + dataRecord.efficiency);
   calcFermentablesFromOG(estog);     // Keep the OG
   calcFermentables();
   calcIBUs();
  });
  $('#est_og').on('change', function(event) {
   dataRecord.est_og = parseFloat(event.args.value);
   console.log('est_og change:' + dataRecord.est_og);
   calcFermentablesFromOG(dataRecord.est_og); // Adjust fermentables amounts
   calcFermentables(); // Update the recipe details
   calcIBUs();
   calcMash();
  });
  $('#mash_ph').on('change', function(event) {
   dataRecord.mash_ph = parseFloat(event.args.value);
   calcWater();
  });

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

 $('#styleSelect').jqxDropDownList({
  placeHolder: 'Kies bierstijl:',
  theme: theme,
  source: styleslist,
  displayMember: 'name',
  width: 180,
  height: 23,
  dropDownVerticalAlignment: 'top',
  dropDownWidth: 500,
  dropDownHeight: 350,
  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_max').val(datarecord.carb_max);
   $('#st_abv_min').val(datarecord.abv_min);
   $('#st_abv_max').val(datarecord.abv_max);
  }
 });

 function saveRecord(goback) {
  var row = {
   record: my_record,
   uuid: dataRecord.uuid,
   name: $('#name').val(),
   locked: dataRecord.locked,
   notes: $('#notes').val(),
   st_name: $('#st_name').val(),
   st_letter: $('#st_letter').val(),
   st_guide: $('#st_guide').val(),
   st_type: dataRecord.st_type,
   st_category: $('#st_category').val(),
   st_category_number: parseFloat($('#st_category_number').jqxNumberInput('decimal')),
   st_og_min: parseFloat($('#st_og_min').jqxNumberInput('decimal')),
   st_og_max: parseFloat($('#st_og_max').jqxNumberInput('decimal')),
   st_fg_min: parseFloat($('#st_fg_min').jqxNumberInput('decimal')),
   st_fg_max: parseFloat($('#st_fg_max').jqxNumberInput('decimal')),
   st_ibu_min: parseFloat($('#st_ibu_min').jqxNumberInput('decimal')),
   st_ibu_max: parseFloat($('#st_ibu_max').jqxNumberInput('decimal')),
   st_color_min: parseFloat($('#st_color_min').jqxNumberInput('decimal')),
   st_color_max: parseFloat($('#st_color_max').jqxNumberInput('decimal')),
   st_carb_min: parseFloat($('#st_carb_min').jqxNumberInput('decimal')),
   st_carb_max: parseFloat($('#st_carb_max').jqxNumberInput('decimal')),
   st_abv_min: parseFloat($('#st_abv_min').jqxNumberInput('decimal')),
   st_abv_max: parseFloat($('#st_abv_max').jqxNumberInput('decimal')),
   type: $('#type').val(),
   batch_size: parseFloat($('#batch_size').jqxNumberInput('decimal')),
   boil_size: parseFloat($('#boil_size').jqxNumberInput('decimal')),
   boil_time: parseFloat($('#boil_time').jqxNumberInput('decimal')),
   efficiency: parseFloat($('#efficiency').jqxNumberInput('decimal')),
   est_og: parseFloat($('#est_og').jqxNumberInput('decimal')),
   est_fg: parseFloat($('#est_fg').jqxNumberInput('decimal')),
   est_abv: parseFloat($('#est_abv').jqxNumberInput('decimal')),
   est_color: parseFloat($('#est_color').jqxNumberInput('decimal')),
   color_method: $('#color_method').val(),
   est_ibu: parseFloat($('#est_ibu').jqxNumberInput('decimal')),
   ibu_method: $('#ibu_method').val(),
   est_carb: parseFloat($('#est_carb').jqxNumberInput('decimal')),
   mash_name: $('#mash_name').val(),
   mash_ph: parseFloat($('#mash_ph').jqxNumberInput('decimal')),
   sparge_temp: parseFloat($('#sparge_temp').jqxNumberInput('decimal')),
   sparge_ph: parseFloat($('#sparge_ph').jqxNumberInput('decimal')),
   sparge_volume: parseFloat($('#sparge_volume').jqxNumberInput('decimal')),
   sparge_source: $('#sparge_source').val(),
   sparge_acid_type: $('#sparge_acid_type').val(),
   sparge_acid_perc: parseFloat($('#sparge_acid_perc').jqxNumberInput('decimal')),
   sparge_acid_amount: dataRecord.sparge_acid_amount,
   calc_acid: dataRecord.calc_acid,
   w1_name: $('#w1_name').val(),
   w1_amount: parseFloat($('#w1_amount').jqxNumberInput('decimal')),
   w1_calcium: parseFloat($('#w1_calcium').jqxNumberInput('decimal')),
   w1_sulfate: parseFloat($('#w1_sulfate').jqxNumberInput('decimal')),
   w1_chloride: parseFloat($('#w1_chloride').jqxNumberInput('decimal')),
   w1_sodium: parseFloat($('#w1_sodium').jqxNumberInput('decimal')),
   w1_magnesium: parseFloat($('#w1_magnesium').jqxNumberInput('decimal')),
   w1_total_alkalinity: parseFloat($('#w1_total_alkalinity').jqxNumberInput('decimal')),
   w1_ph: parseFloat($('#w1_ph').jqxNumberInput('decimal')),
   w1_cost: dataRecord.w1_cost,
   w2_name: $('#w2_name').val(),
   w2_amount: parseFloat($('#w2_amount').jqxNumberInput('decimal')),
   w2_calcium: parseFloat($('#w2_calcium').jqxNumberInput('decimal')),
   w2_sulfate: parseFloat($('#w2_sulfate').jqxNumberInput('decimal')),
   w2_chloride: parseFloat($('#w2_chloride').jqxNumberInput('decimal')),
   w2_sodium: parseFloat($('#w2_sodium').jqxNumberInput('decimal')),
   w2_magnesium: parseFloat($('#w2_magnesium').jqxNumberInput('decimal')),
   w2_total_alkalinity: parseFloat($('#w2_total_alkalinity').jqxNumberInput('decimal')),
   w2_ph: parseFloat($('#w2_ph').jqxNumberInput('decimal')),
   w2_cost: dataRecord.w2_cost,
   wg_amount: parseFloat($('#wg_amount').jqxNumberInput('decimal')),
   wg_calcium: parseFloat($('#wg_calcium').jqxNumberInput('decimal')),
   wg_sulfate: parseFloat($('#wg_sulfate').jqxNumberInput('decimal')),
   wg_chloride: parseFloat($('#wg_chloride').jqxNumberInput('decimal')),
   wg_sodium: parseFloat($('#wg_sodium').jqxNumberInput('decimal')),
   wg_magnesium: parseFloat($('#wg_magnesium').jqxNumberInput('decimal')),
   wg_total_alkalinity: parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')),
   wg_ph: parseFloat($('#wg_ph').jqxNumberInput('decimal')),
   wb_calcium: parseFloat($('#wb_calcium').jqxNumberInput('decimal')),
   wb_sulfate: parseFloat($('#wb_sulfate').jqxNumberInput('decimal')),
   wb_chloride: parseFloat($('#wb_chloride').jqxNumberInput('decimal')),
   wb_sodium: parseFloat($('#wb_sodium').jqxNumberInput('decimal')),
   wb_magnesium: parseFloat($('#wb_magnesium').jqxNumberInput('decimal')),
   wb_total_alkalinity: parseFloat($('#wb_total_alkalinity').jqxNumberInput('decimal')),
   wb_ph: parseFloat($('#wb_ph').jqxNumberInput('decimal')),
   wa_acid_name: $('#wa_acid_name').val(),
   wa_acid_perc: parseFloat($('#wa_acid_perc').jqxNumberInput('decimal')),
   wa_base_name: $('#wa_base_name').val(),
   fermentables: $('#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,
   async: false,
   data: data,
   type: 'POST',
   success: function(data, status, xhr) {
    console.log('saveRecord(' + goback + ') success');
    if (goback)
     window.location.href = my_return;
    else
     window.location.href = 'rec_export.php?record=' + my_record + '&return=' + my_return + '&name=' + dataRecord.name;
   },
   error: function(jqXHR, textStatus, errorThrown) {
    console.log('saveRecord() ' + textStatus);
   }
  });
 };

 dataAdapter.dataBind();

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

 $('#st_name').jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
 $('#st_name').jqxInput({ theme: theme, width: 250, height: 23 });
 $('#st_letter').jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
 $('#st_letter').jqxInput({ theme: theme, width: 90, height: 23 });
 $('#st_guide').jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
 $('#st_guide').jqxInput({ theme: theme, width: 250, height: 23 });
 $('#st_category').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
 $('#st_category').jqxInput({ theme: theme, width: 250, height: 23 });
 $('#st_category_number').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
 $('#st_category_number').jqxNumberInput(Smal0dec);
 $('#st_type').jqxTooltip({ content: 'Het bierstijl type.'});
 $('#st_type').jqxInput({ theme: theme, width: 250, height: 23 });

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

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

 $('#est_abv').jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
 $('#est_abv').jqxNumberInput(Smal1dec);
 $('#st_abv_min').jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
 $('#st_abv_min').jqxNumberInput(Smal1dec);
 $('#st_abv_max').jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
 $('#st_abv_max').jqxNumberInput(Smal1dec);

 $('#est_color').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
 $('#est_color').jqxNumberInput(Show0dec);
 $('#st_color_min').jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
 $('#st_color_min').jqxNumberInput(Smal0dec);
 $('#st_color_max').jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
 $('#st_color_max').jqxNumberInput(Smal0dec);
 $('#color_method').jqxDropDownList({
  theme: theme,
  source: ColorMethodAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true
 });
 $('#est_ibu').jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
 $('#est_ibu').jqxNumberInput(Show0dec);
 $('#st_ibu_min').jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
 $('#st_ibu_min').jqxNumberInput(Smal0dec);
 $('#st_ibu_max').jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
 $('#st_ibu_max').jqxNumberInput(Smal0dec);
 $('#ibu_method').jqxDropDownList({
  theme: theme,
  source: IBUmethodAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true,
  dropDownVerticalAlignment: 'top'
 });
 $('#kcal').jqxTooltip({ content: 'Energie-inhoud in kcal/liter.' });
 $('#kcal').jqxNumberInput(Smal0dec);
 $('#est_carb').jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' });
 $('#est_carb').jqxNumberInput(Smal1dec);
 $('#st_carb_min').jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
 $('#st_carb_min').jqxNumberInput(Smal1dec);
 $('#st_carb_max').jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});
 $('#st_carb_max').jqxNumberInput(Smal1dec);

 // Tab 2, Vergistbaar
 $('#est_color2').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
 $('#est_color2').jqxNumberInput(Show0dec);
 $('#est_og2').jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
 $('#est_og2').jqxNumberInput(Show3dec);
 $('#perc_malts').jqxProgressBar({
  width: 300,
  height: 23,
  theme: theme,
  showText: true,
  max: 120,
  animationDuration: 0,
  colorRanges: [
   { stop: 90, color: '#008C00' },
   { stop: 100, color: '#EB7331' },
   { stop: 120, color: '#FF0000' }
  ],
  renderText: function(text) {
   return (Math.round(parseInt(text) * 1.2)) + '%';
  }
 });
 $('#perc_sugars').jqxProgressBar({
  width: 300,
  height: 23,
  theme: theme,
  showText: true,
  max: 50,
  animationDuration: 0,
  colorRanges: [
   { stop: 20, color: '#008C00' },
   { stop: 50, color: '#FF0000' }
  ],
  renderText: function(text) {
   return (Math.round(parseInt(text) * 5) / 10) + '%';
  }
 });
 $('#perc_cara').jqxProgressBar({
  width: 300,
  height: 23,
  theme: theme,
  showText: true,
  max: 50,
  animationDuration: 0,
  colorRanges: [
   { stop: 25, color: '#008C00' },
   { stop: 50, color: '#FF0000' }
  ],
  renderText: function(text) {
   return (Math.round(parseInt(text) * 5) / 10) + '%';
  }
 });
 $('#ferm_lintner').jqxProgressBar({
  width: 300,
  height: 23,
  theme: theme,
  showText: true,
  max: 200,
  animationDuration: 0,
  colorRanges: [
   { stop: 30, color: '#FF0000' },
   { stop: 40, color: '#EB7331' },
   { stop: 200, color: '#008C00' }
  ],
  renderText: function(text) {
   return (parseInt(text) * 2) + ' lintner';
  }
 });
 $('#popupFermentable').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#FermentableReady'),
  modalOpacity: 0.40
 });
 $('#FermentableReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#FermentableReady').click(function() {
  var 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();
   calcMash();
  }
 });
 $('#wf_percentage').jqxNumberInput(Perc1dec);
 $('#wf_percentage').on('change', function(event) {
  var newperc, nw, damount, namount, rowscount, rowdata, diff, tw, i, newvalue,
  oldvalue = Round(fermentableData.f_percentage, 1);
  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 += Round(rowdata.f_amount, 3);
    }
    tw = Round(tw, 3);
    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();
     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 = Round((rowdata.f_amount - (nw / (rowscount - 1))), 3);
        newperc = Round((namount / tw) * 100, 1);
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_amount', namount);
        $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', newperc);
       }
      } else {
       $('#fermentableGrid').jqxGrid('setcellvalue', i, 'f_percentage', 0);
      }
     }
     calcFermentables();
     calcMash();
    }
   }
  }
 });
 $('#wf_max_in_batch').jqxNumberInput(Show1dec);
 $('#wf_adjust_to_total_100').jqxCheckBox({ theme: theme, width: 120, height: 23 });
 $('#wf_adjust_to_total_100').on('checked', function(event) {
  if (fermentableData.f_adjust_to_total_100 == 0) {
   if (to_100) {
    // Reset other flag first.
    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 3, Hoppen
 $('#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';
  }
 });
 $('#popupHop').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#HopReady'),
  modalOpacity: 0.40
 });
 $('#HopReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#HopReady').click(function() {
  var row, rowID = $('#hopGrid').jqxGrid('getrowid', hopRow);
  console.log('HopReady row:' + hopRow + ' ID:' + rowID);
  row = {
   h_name: $('#wh_name').val(),
   h_origin: hopData.h_origin,
   h_amount: parseFloat($('#wh_amount').jqxNumberInput('decimal')) / 1000,
   h_cost: hopData.h_cost,
   h_type: hopData.h_type,
   h_form: hopData.h_form,
   h_useat: $('#wh_useat').val(),
   h_time: hopData.h_time,
   h_alpha: hopData.h_alpha,
   h_beta: hopData.h_beta,
   h_hsi: hopData.h_hsi,
   h_humulene: hopData.h_humulene,
   h_caryophyllene: hopData.h_caryophyllene,
   h_cohumulone: hopData.h_cohumulone,
   h_myrcene: hopData.h_myrcene,
   h_total_oil: hopData.h_total_oil,
   h_inventory: hopData.h_inventory,
   h_avail: hopData.h_avail
  };
  $('#hopGrid').jqxGrid('updaterow', rowID, row);
  calcIBUs();
 });
 $('#wh_name').jqxInput({ theme: theme, width: 320, height: 23 });
 $('#wh_instock').jqxCheckBox({ theme: theme, height: 23 });
 $('#wh_instock').on('change', function(event) {
  hopinstock = event.args.checked;
  hoplist.dataBind();
 });
 $('#wh_select').jqxDropDownList({
  placeHolder: 'Kies hop:',
  theme: theme,
  source: hoplist,
  displayMember: 'name',
  width: 150,
  height: 23,
  dropDownWidth: 500,
  dropDownHeight: 500,
  renderer: function(index, label, value) {
   var datarecord = hoplist.records[index];
   return datarecord.origin + ' / ' + datarecord.name + ' (' + datarecord.alpha + ' % &alpha;)';
  }
 });
 $('#wh_select').on('select', function(event) {
  if (event.args) {
   var 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) {
  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());
  hopData.h_amount = amount;
  console.log('amount changed: ' + event.args.value + ' time:' + hopData.h_time + ' alpha:' + hopData.h_alpha + ' IBU:' + ibu);
  $('#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 == 4) { // Whirlpool
   if (newtime > 120) {
    newtime = 120;
    $('#wh_time').val(newtime);
   }
   hopData.h_time = newtime;
  } else if (hopData.h_useat == 5) { // Dry hop
   if (newtime > 21) {
    newtime = 21;
    $('#wh_time').val(newtime);
   }
   hopData.h_time = newtime * 1440;
  }
  ibu = toIBU(hopData.h_useat, hopData.h_form, preboil_sg, parseFloat($('#batch_size').jqxNumberInput('decimal')),
   parseFloat(hopData.h_amount), parseFloat(hopData.h_time), parseFloat(hopData.h_alpha), $('#ibu_method').val());
  $('#wh_ibu').val(ibu);
 });
 $('#wh_useat').jqxDropDownList({
  theme: theme,
  source: HopUseAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true,
  dropDownVerticalAlignment: 'top'
 });
 $('#wh_useat').on('select', function(event) {
  if (event.args) {
   var index = event.args.index;
   hopData.h_useat = index;
   if ((index == 0) || (index == 1)) { // Mashhop or First wort hop
    hopData.h_time = parseFloat(dataRecord.boil_time);
    $('#wh_time').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
    $('#wh_time').val(hopData.h_time);
   } else if (index == 3) { // Aroma
    hopData.h_time = 0;
    $('#wh_time').jqxNumberInput({ spinButtons: false, readOnly: true, width: 90 });
    $('#wh_time').val(0);
   } else { // Boil, Whirlpool or Dry hop
    $('#wh_time').jqxNumberInput({ spinButtons: true, readOnly: false, width: 110 });
   }
   if (index == 5) // Dry hop
    $('#wh_pmpt_time').html('Tijd in dagen');
   else
    $('#wh_pmpt_time').html('Tijd in minuten');
  }
 });

 // Tab 4, Diversen
 $('#popupMisc').jqxWindow({
  width: 800,
  height: 275,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#MiscReady'),
  modalOpacity: 0.40
 });
 $('#MiscReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#MiscReady').click(function() {
  var 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);
 });
 $('#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(Spin1dec);
 $('#wm_amount').on('change', function(event) {
  console.log('amount changed: ' + event.args.value);
  miscData.m_amount = parseFloat(event.args.value) / 1000;
 });
 $('#wm_time').jqxNumberInput(PosInt);
 $('#wm_time').on('change', function(event) {
  console.log('time changed: ' + event.args.value);
  var newtime = parseFloat(event.args.value);

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

 // Tab 5, Gist
 $('#est_fg2').jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' });
 $('#est_fg2').jqxNumberInput(Show3dec);
 $('#est_abv2').jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
 $('#est_abv2').jqxNumberInput(Smal1dec);
 $('#popupYeast').jqxWindow({
  width: 800,
  height: 300,
  position: { x: 230, y: 100 },
  resizable: false,
  theme: theme,
  isModal: true,
  autoOpen: false,
  cancelButton: $('#YeastReady'),
  modalOpacity: 0.40
 });
 $('#YeastReady').jqxButton({ template: 'success', width: '90px', theme: theme });
 $('#YeastReady').click(function() {
  var 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_avail: yeastData.y_avail
  };
  $('#yeastGrid').jqxGrid('updaterow', rowID, row);
  calcFermentables();
 });
 $('#wy_name').jqxInput({ theme: theme, width: 320, height: 23 });
 $('#wy_laboratory').jqxInput({ theme: theme, width: 320, height: 23 });
 $('#wy_product_id').jqxInput({ theme: theme, width: 320, height: 23 });
 $('#wy_instock').jqxCheckBox({ theme: theme, height: 23 });
 $('#wy_instock').on('change', function(event) {
  yeastinstock = event.args.checked;
  yeastlist.dataBind();
 });
 $('#wy_select').jqxDropDownList({
  placeHolder: 'Kies gist:',
  theme: theme,
  source: yeastlist,
  displayMember: 'name',
  width: 150,
  height: 23,
  dropDownWidth: 500,
  dropDownHeight: 500,
  renderer: function(index, label, value) {
   var datarecord = yeastlist.records[index];
   return datarecord.laboratory + ' ' + datarecord.product_id + ' ' + datarecord.name;
  }
 });
 $('#wy_select').on('select', function(event) {
  if (event.args) {
   var 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;
   if (yeastData.y_form == 0) {
    $('#wy_pmpt_amount').html('Pak(ken):');
   } else if (yeastData.y_form == 1) {
    $('#wy_pmpt_amount').html('Gewicht gram:');
   } else {
    $('#wy_pmpt_amount').html('Volume ml:');
   }
   calcFermentables();
  }
 });
 $('#wy_amount').jqxNumberInput(Spin1dec);
 $('#wy_amount').on('change', function(event) {
  console.log('amount changed: ' + event.args.value);
  var amount;
  if (yeastData.y_form == 0) // Liquid
   amount = parseFloat(event.args.value);
  else
   amount = parseFloat(event.args.value) / 1000;
  yeastData.y_amount = amount;
  calcFermentables();
 });
 $('#wy_use').jqxDropDownList({
  theme: theme,
  source: YeastUseAdapter,
  valueMember: 'id',
  displayMember: 'nl',
  width: 180,
  height: 23,
  autoDropDownHeight: true,
  dropDownVerticalAlignment: 'top'
 });
 $('#wy_use').on('select', function(event) {
  if (event.args) {
   var index = event.args.index;
   yeastData.y_use = index;
   calcFermentabes();
  }
 });

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

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

 // Water source 1
 $('#w1_name').jqxDropDownList({
  placeHolder: 'Kies hoofd water:',
  theme: theme,
  source: waterlist,
  displayMember: 'name',
  width: 250,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 400
 });
 $('#w1_name').on('select', function(event) {
  if (event.args) {
   var 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);
   dataRecord.w1_total_alkalinity = datarecord.total_alkalinity;
   $('#w1_ph').val(datarecord.ph);
   dataRecord.w1_ph = datarecord.ph;
   $('#w1_cost').val(datarecord.cost);
   dataRecord.w1_cost = datarecord.cost;
   calcWater();
  }
 });
 $('#w1_amount').jqxNumberInput(Show1wat);
 $('#w1_calcium').jqxNumberInput(Show1wat);
 $('#w1_magnesium').jqxNumberInput(Show1wat);
 $('#w1_sodium').jqxNumberInput(Show1wat);
 $('#w1_total_alkalinity').jqxNumberInput(Show1wat);
 $('#w1_chloride').jqxNumberInput(Show1wat);
 $('#w1_sulfate').jqxNumberInput(Show1wat);
 $('#w1_ph').jqxNumberInput(Show1wat);
 // Water source 2
 $('#w2_name').jqxDropDownList({
  placeHolder: 'Kies meng water:',
  theme: theme,
  source: waterlist,
  displayMember: 'name',
  width: 250,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 400
 });
 $('#w2_name').on('select', function(event) {
  if (event.args) {
   var datarecord, index = event.args.index;
   datarecord = waterlist.records[index];
   dataRecord.w2_name = datarecord.name;
   $('#w2_calcium').val(datarecord.calcium);
   dataRecord.w2_calcium = datarecord.calcium;
   $('#w2_sulfate').val(datarecord.sulfate);
   dataRecord.w2_sulfate = datarecord.sulfate;
   $('#w2_chloride').val(datarecord.chloride);
   dataRecord.w2_chloride = datarecord.chloride;
   $('#w2_sodium').val(datarecord.sodium);
   dataRecord.w2_sodium = datarecord.sodium;
   $('#w2_magnesium').val(datarecord.magnesium);
   dataRecord.w2_magnesium = datarecord.magnesium;
   $('#w2_total_alkalinity').val(datarecord.total_alkalinity);
   dataRecord.w2_total_alkalinity = datarecord.total_alkalinity;
   $('#w2_ph').val(datarecord.ph);
   dataRecord.w2_ph = datarecord.ph;
   $('#w2_cost').val(datarecord.cost);
   dataRecord.w2_cost = datarecord.cost;
   $('#w2_amount').jqxNumberInput({ max: 100000, readOnly: false }); // Set high max to enable the spinbuttons.
   calcWater();
  }
 });
 $('#w2_amount').jqxTooltip({ content: 'De verdeling van het hoofd en meng water. Het totale maisch water volume blijft gelijk.'});
 $('#w2_amount').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 94, height: 23, min: 0, decimalDigits: 1, spinButtons: true, readOnly: true });
 $('#w2_calcium').jqxNumberInput(Show1wat);
 $('#w2_magnesium').jqxNumberInput(Show1wat);
 $('#w2_sodium').jqxNumberInput(Show1wat);
 $('#w2_total_alkalinity').jqxNumberInput(Show1wat);
 $('#w2_chloride').jqxNumberInput(Show1wat);
 $('#w2_sulfate').jqxNumberInput(Show1wat);
 $('#w2_ph').jqxNumberInput(Show1wat);
 // Water mixed
 $('#wg_amount').jqxNumberInput(Show1wat);
 $('#wg_calcium').jqxNumberInput(Show1wat);
 $('#wg_magnesium').jqxNumberInput(Show1wat);
 $('#wg_sodium').jqxNumberInput(Show1wat);
 $('#wg_total_alkalinity').jqxNumberInput(Show1wat);
 $('#wg_chloride').jqxNumberInput(Show1wat);
 $('#wg_sulfate').jqxNumberInput(Show1wat);
 $('#wg_ph').jqxNumberInput(Show1wat);
 // Water treated
 $('#wb_calcium').jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'});
 $('#wb_calcium').jqxNumberInput(Show1wat);
 $('#wb_magnesium').jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is lager dan 30.'});
 $('#wb_magnesium').jqxNumberInput(Show1wat);
 $('#wb_sodium').jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'});
 $('#wb_sodium').jqxNumberInput(Show1wat);
 $('#wb_chloride').jqxTooltip({ content: 'De ideale hoeveelheid Chloride is tussen 50 en 100.'});
 $('#wb_chloride').jqxNumberInput(Show1wat);
 $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 350.'});
 $('#wb_sulfate').jqxNumberInput(Show1wat);
 $('#wb_total_alkalinity').jqxNumberInput(Show1wat);
 $('#wb_ph').jqxNumberInput(Show1wat);
 // Water target profile
 $('#pr_name').jqxDropDownList({
  placeHolder: 'Kies doel profiel:',
  theme: theme,
  source: waterprofiles,
  displayMember: 'name',
  width: 250,
  height: 27,
  dropDownWidth: 400,
  dropDownHeight: 300
 });
 $('#pr_name').on('select', function(event) {
  if (event.args) {
   var 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_calcium').jqxNumberInput(Show1wat);
 $('#pr_magnesium').jqxNumberInput(Show1wat);
 $('#pr_sodium').jqxNumberInput(Show1wat);
 $('#pr_total_alkalinity').jqxNumberInput(Show1wat);
 $('#pr_chloride').jqxNumberInput(Show1wat);
 $('#pr_sulfate').jqxNumberInput(Show1wat);

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

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

 // Buttons below
 $('#Export').jqxButton({ template: 'info', width: '80px', theme: theme });
 $('#Export').bind('click', function() {
  saveRecord(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({ record: my_record });
   $.ajax({
    dataType: 'json',
    url: url,
    cache: false,
    data: data,
    type: 'POST',
    success: function(data, status, xhr) {
     // delete command is executed.
     window.location.href = my_return;
    },
    error: function(jqXHR, textStatus, errorThrown) {
    }
   });
  });
 });

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

 $('#Save').jqxButton({ template: 'success', width: '80px', theme: theme });
 $('#Save').bind('click', function() {
  saveRecord(1);
 });
 createDelElements();
});

mercurial