www/js/rec_view.js

Thu, 12 Oct 2023 14:19:46 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Thu, 12 Oct 2023 14:19:46 +0200
changeset 849
16079aef4c4c
parent 825
8b87ad5bd3c3
permissions
-rw-r--r--

Version 0.3.44. Moved iSpindel Plato calculation from the php script to bmsd. This uses calibration data in the mon_ispindels table. Setup of this data will be done by the bmsapp applications. Default settings are stored in MySQL. From now on you don't need to store calibration data in the iSpindel.

/*****************************************************************************
 * Copyright (C) 2018-2022
 *
 * 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,
 error_count = 0,
 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,
 MMMgCl2 = 95.211,      /* Since 27-06-2021 */
 MMMgSO4 = 246.475,
 MMNaHCO3 = 84.007,
 MMNa2CO3 = 105.996,
 MMNaCl = 58.443,
 MMCaOH2 = 74.06268,
 SpecificHeatWater = 1.0,
 SpecificHeatMalt = 0.399, //cal/g.°C
 SlakingHeat = 10.318, //cal/g.°C
 eq_tun_weight = 2.0, // 2 Kg pot
 eq_tun_specific_heat = 0.110, // Stainless Steel
 data_loaded = 0;

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

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


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

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


function 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)));
 to_100 = my_100;

 // 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 infusionVol(step_infused, step_mashkg, infuse_temp, step_temp, last_temp) {
 var a = last_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
 var b = step_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt);
 var vol = Round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)), 2);
 console.log('infusionVol(' + step_infused + ', ' + step_mashkg + ', ' + infuse_temp + ', ' + step_temp + ', ' + last_temp + '): ' + vol);
 return vol;
}


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


function calcMash() {
 var infused = 0, vol, i, j, n, a, b, row, rows, temp;
 var lasttemp = 18.0;
 var graintemp = 18.0;
 var tuntemp = 18.0;

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


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


function GetOptSO4Clratio() {
 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 BufferCapacity(di_ph, acid_to_ph_57, ebc, graintype) {
 C1 = 0;
 if ((di_ph != 5.7) && ((acid_to_ph_57 < - 0.1) || (acid_to_ph_57 > 0.1))) {
  C1 = acid_to_ph_57 / (di_ph - 5.7);
 } else {
  // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid.
  switch (graintype) {
   case 0:                                 // Base, Special, Kilned
   case 3:
   case 5: C1 = 0.014 * ebc - 34.192;
           break;
   case 2: C1 = -0.0597 * ebc - 32.457;    // Crystal
           break;
   case 1: C1 = 0.0107 * ebc - 54.768;     // Roast
           break;
   case 4: C1 = -149;                      // Sour malt
           break;
  }
 }
 return C1;
}


function ProtonDeficit(pHZ) {
 var C1, i, rows, row, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal'));
 // proton deficit for the grist
 if ((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
    C1 = BufferCapacity(row.f_di_ph, row.f_acid_to_ph_57, row.f_color, row.f_graintype);
    x = C1 * (pHZ - row.f_di_ph);   // AcidRequired(ZpH)
    Result += x * row.f_amount;
   }
  }
 } else {
  error_count++;
  if (error_count < 5)
   console.log('ProtonDeficit(' + pHZ + ') invalid grist, return ' + Result);
 }
 return Result;
}


function MashpH() {
 var n = 0,
 pH = 5.4,
 deltapH = 0.001,
 deltapd = 0.1,
 pd = ProtonDeficit(pH);
 while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) {
  n++;
  if (pd < -deltapd)
   pH -= deltapH;
  else if (pd > deltapd)
   pH += deltapH;
  pd = ProtonDeficit(pH);
 }
 pH = Round(pH, 6);
 //console.log('MashpH() n: ' + n + ' pH: ' + pH);
 return pH;
}




$(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: 'int' },
   { name: 'st_og_min', type: 'float' },
   { name: 'st_og_max', type: 'float' },
   { name: 'st_fg_min', type: 'float' },
   { name: 'st_fg_max', type: 'float' },
   { name: 'st_ibu_min', type: 'float' },
   { name: 'st_ibu_max', type: 'float' },
   { name: 'st_color_min', type: 'float' },
   { name: 'st_color_max', type: 'float' },
   { name: 'st_carb_min', type: 'float' },
   { name: 'st_carb_max', type: 'float' },
   { name: 'st_abv_min', type: 'float' },
   { name: 'st_abv_max', type: 'float' },
   { name: '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: 'string' },
   { name: 'hops', type: 'string' },
   { name: 'miscs', type: 'string' },
   { name: 'yeasts', type: 'string' },
   { name: 'mashs', type: 'string' }
  ],
  id: 'record',
  url: url + '?record=' + my_record
 },
 // Load data and select one record.
 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);
   // Hidden record 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(RecipeTypeData[dataRecord.type].nl);
   $('#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(ColorMethodData[dataRecord.color_method].nl);
   $('#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(IBUmethodData[dataRecord.ibu_method].nl);
   $('#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);
   // Hidden record sparge_temp
   $('#sparge_ph').val(dataRecord.sparge_ph);
   $('#sw_amount').val(dataRecord.sparge_volume);
   // Hidden record sparge_source
   $('#sparge_acid_type').val(AcidTypeData[dataRecord.sparge_acid_type].nl);
   $('#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_bicarbonate').val(Bicarbonate(dataRecord.w1_total_alkalinity, 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_bicarbonate').val(Bicarbonate(dataRecord.w2_total_alkalinity, 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(AcidTypeData[dataRecord.wa_acid_name].nl);
   $('#wa_acid_perc').val(dataRecord.wa_acid_perc);
   editFermentable(dataRecord);
   editHop(dataRecord);
   editMisc(dataRecord);
   editYeast(dataRecord);
   editMash(dataRecord);
   $('#jqxTabs').jqxTabs('next');
   data_loaded = 1;
  },
  loadError: function(jqXHR, status, error) {},
  beforeLoadComplete: function(records) { $('#jqxLoader').jqxLoader('open'); }
 }),

 // Inline fermentables editor
 editFermentable = function(data) {
  var fermentableSource = {
   localdata: data.fermentables,
   datafields: [
    { name: 'f_name', type: 'string' },
    { name: 'f_origin', type: 'string' },
    { name: 'f_supplier', type: 'string' },
    { name: 'f_amount', type: 'float' },
    { name: 'f_cost', type: 'float' },
    { name: 'f_type', type: 'int' },
    { name: 'f_yield', type: 'float' },
    { name: 'f_color', type: 'float' },
    { name: 'f_coarse_fine_diff', type: 'float' },
    { name: 'f_moisture', type: 'float' },
    { name: 'f_diastatic_power', type: 'float' },
    { name: 'f_protein', type: 'float' },
    { name: 'f_max_in_batch', type: 'float' },
    { name: 'f_graintype', type: 'int' },
    { name: 'f_added', type: 'int' },
    { name: 'f_dissolved_protein', type: 'float' },
    { name: 'f_recommend_mash', type: 'int' },
    { name: 'f_add_after_boil', type: 'int' },
    { name: 'f_adjust_to_total_100', type: 'int' },
    { name: 'f_percentage', type: 'float' },
    { name: 'f_di_ph', type: 'float' },
    { name: 'f_acid_to_ph_57', type: 'float' },
    { name: 'f_inventory', type: 'float' },
    { name: 'f_avail', type: 'int' }
   ],
  },
  fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);

  $('#fermentableGrid').jqxGrid({
   width: 1240,
   height: 470,
   source: fermentableAdapter,
   theme: theme,
   editable: false,
   ready: function() {
    calcFermentables();
    $('#jqxTabs').jqxTabs('next');
   },
   columns: [
    { text: 'Vergistbaar ingredi&euml;nt', datafield: 'f_name' },
    { text: 'Leverancier', datafield: 'f_supplier', width: 180 },
    { text: 'Kleur', datafield: 'f_color', width: 90, align: 'right',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f0') + ' 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: '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 }
   ]
  });
 };

 // Inline hops editor
 var editHop = function(data) {
  var hopSource = {
   localdata: data.hops,
   datafields: [
    { name: 'h_name', type: 'string' },
    { name: 'h_origin', type: 'string' },
    { name: 'h_amount', type: 'float' },
    { name: 'h_cost', type: 'float' },
    { name: 'h_type', type: 'int' },
    { name: 'h_form', type: 'int' },
    { name: 'h_useat', type: 'int' },
    { name: 'h_time', type: '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' }
   ],
  },
  hopAdapter = new $.jqx.dataAdapter(hopSource);

  $('#hopGrid').jqxGrid({
   width: 1240,
   height: 560,
   source: hopAdapter,
   theme: theme,
   editable: false,
   ready: function() { $('#jqxTabs').jqxTabs('next'); },
   columns: [
    { text: 'Hop', datafield: 'h_name' },
    { text: 'Origin', width: 180, datafield: 'h_origin' },
    { 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: 110, 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(dataRecord.batch_size),
                      parseFloat(rowdata.h_amount), parseFloat(rowdata.h_time),
                      parseFloat(rowdata.h_alpha), dataRecord.ibu_method, 0, parseFloat(rowdata.h_time), 0);
      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>';
     }
    }
   ]
  });
 };

 // Inline miscs editor
 var editMisc = function(data) {
  var miscSource = {
   localdata: data.miscs,
   datafields: [
    { name: 'm_name', type: 'string' },
    { name: 'm_amount', type: 'float' },
    { name: 'm_cost', type: 'float' },
    { name: 'm_type', type: 'int' },
    { name: 'm_use_use', type: 'int' },
    { name: 'm_time', type: 'float' },
    { name: 'm_amount_is_weight', type: 'int' },
    { name: 'm_inventory', type: 'float' },
    { name: 'm_avail', type: 'int' }
   ],
  },
  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.
     if (row.m_use_use == 1) {	// Mash
      switch (row.m_name) {
       case 'CaCl2':
        $('#wa_cacl2').val(row.m_amount * 1000);
        break;
       case 'CaSO4':
        $('#wa_caso4').val(row.m_amount * 1000);
        break;
       case 'MgSO4':
        $('#wa_mgso4').val(row.m_amount * 1000);
        break;
       case 'NaCl':
        $('#wa_nacl').val(row.m_amount * 1000);
        break;
       case 'MgCl2':
        $('#wa_mgcl2').val(row.m_amount * 1000);
        break;
       case 'NaHCO3':
        $('#wa_nahco3').val(row.m_amount * 1000);
        break;
       case 'CaCO3':
        $('#wa_caco3').val(row.m_amount * 1000);
        break;
       case 'Melkzuur':
        $('#wa_acid_name').val(AcidTypeData[0].nl);
        $('#wa_acid').val(row.m_amount * 1000);
        $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc);
        last_acid = 'Melkzuur';
        break;
       case 'Zoutzuur':
        $('#wa_acid_name').val(AcidTypeData[1].nl);
        $('#wa_acid').val(row.m_amount * 1000);
        $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc);
        last_acid = 'Zoutzuur';
        break;
       case 'Fosforzuur':
        $('#wa_acid_name').val(AcidTypeData[2].nl);
        $('#wa_acid').val(row.m_amount * 1000);
        $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc);
        last_acid = 'Fosforzuur';
        break;
       case 'Zwavelzuur':
        $('#wa_acid_name').val(AcidTypeData[3].nl);
        $('#wa_acid').val(row.m_amount * 1000);
        $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc);
        last_acid = 'Zwavelzuur';
        break;
      }
     }
     if (row.m_use_use == 6) {	// Sparge
      switch (row.m_name) {
       case 'CaCl2':
        $('#ss_cacl2').val(row.m_amount * 1000);
        break;
       case 'CaSO4':
        $('#ss_caso4').val(row.m_amount * 1000);
        break;
       case 'MgSO4':
        $('#ss_mgso4').val(row.m_amount * 1000);
        break;
       case 'NaCl':
        $('#ss_nacl').val(row.m_amount * 1000);
        break;
       case 'MgCl2':
        $('#ss_mgcl2').val(row.m_amount * 1000);
        break;
       case 'Melkzuur':
//       $('#wa_acid_name').val(0);
//       $('#wa_acid').val(row.m_amount * 1000);
//       $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); // TODO: this ignores changed percentages.
//       last_acid = 'Melkzuur';
        break;
       case 'Zoutzuur':
 //      $('#wa_acid_name').val(1);
//       $('#wa_acid').val(row.m_amount * 1000);
//       $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc);
//       last_acid = 'Zoutzuur';
        break;
       case 'Fosforzuur':
//       $('#wa_acid_name').val(2);
//       $('#wa_acid').val(row.m_amount * 1000);
//       $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc);
//       last_acid = 'Fosforzuur';
        break;
       case 'Zwavelzuur':
//       $('#wa_acid_name').val(3);
//       $('#wa_acid').val(row.m_amount * 1000);
//       $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc);
//       last_acid = 'Zwavelzuur';
        break;
      }
     }
    }
    return data;
   },
   loadError: function(jqXHR, status, error) {}
  });
  $('#miscGrid').jqxGrid({
   width: 1240,
   height: 575,
   source: miscAdapter,
   theme: theme,
   editable: false,
   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: 140, align: 'right',
     cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) {
      var duration = '';
      if (rowdata.m_use_use == 2)     // Boil
       duration = dataAdapter.formatNumber(value, 'f0') + ' minuten';
      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>';
     }
    }
   ]
  });
 };

 // Inline yeasts editor
 var editYeast = function(data) {
  var yeastSource = {
   localdata: data.yeasts,
   datafields: [
    { name: 'y_name', type: 'string' },
    { name: 'y_laboratory', type: 'string' },
    { name: 'y_product_id', type: 'string' },
    { name: 'y_amount', type: 'float' },
    { name: 'y_cost', type: 'float' },
    { name: 'y_type', type: 'int' },
    { name: 'y_form', type: 'int' },
    { name: 'y_flocculation', type: 'int' },
    { name: 'y_min_temperature', type: 'float' },
    { name: 'y_max_temperature', type: 'float' },
    { name: 'y_attenuation', type: 'float' },
    { name: 'y_use', type: 'int' },
    { name: 'y_cells', type: 'float' },
    { name: 'y_tolerance', type: 'float' },
    { name: 'y_inventory', type: 'float' },
    { name: 'y_sta1', type: 'int' },
    { name: 'y_bacteria', type: 'int' },
    { name: 'y_harvest_top', type: 'int' },
    { name: 'y_harvest_time', type: 'int' },
    { name: 'y_pitch_temperature', type: 'float' },
    { name: 'y_pofpos', type: 'int' },
    { name: 'y_zymocide', type: 'int' },
    { name: 'y_gr_hl_lo', type: 'int' },
    { name: 'y_sg_lo', type: 'float' },
    { name: 'y_gr_hl_hi', type: 'int' },
    { name: 'y_sg_hi', type: 'float' },
    { name: 'y_avail', type: 'int' }
   ],
  },
  yeastAdapter = new $.jqx.dataAdapter(yeastSource);

  $('#yeastGrid').jqxGrid({
   width: 1240,
   height: 350,
   source: yeastAdapter,
   theme: theme,
   editable: false,
   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>';
     }
    }
   ]
  });
 };

 // inline mash editor
 var editMash = function(data) {
  var mashSource = {
   localdata: data.mashs,
   datafields: [
    { name: 'step_name', type: 'string' },
    { name: 'step_type', type: 'int' },
    { name: 'step_volume', type: 'float' },
    { name: 'step_infuse_amount', type: 'float' },
    { name: 'step_infuse_temp', type: 'float' },
    { name: 'step_temp', type: 'float' },
    { name: 'step_time', type: 'float' },
    { name: 'step_wg_ratio', type: 'float' },
    { name: 'ramp_time', type: 'float' },
    { name: 'end_temp', type: 'float' }
   ],
  },
  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_wg_ratio = 0; // Init this field.
     data.push(row);
    }
   },
  });
  $('#mashGrid').jqxGrid({
   width: 1240,
   height: 400,
   source: mashAdapter,
   theme: theme,
   editable: false,
   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: 'Inf/dec L.', datafield: 'step_infuse_amount', width: 90, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       if (rowdata.step_type == 1)
        return '<span></span>';
       return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f1') + '</span>';
      }
    },
    { text: 'Inf/dec &deg;C', datafield: 'step_infuse_temp', width: 90, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       if (rowdata.step_type == 1)
        return '<span></span>';
       return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
      }
    },
    { text: 'L/Kg.', datafield: 'step_wg_ratio', width: 90, align: 'right',
      cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) {
       var color = '#ffffff';
       if (value < 2.0 || value > 6.0)
        color = '#ff4040';
       return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f2') + '</span>';
      }
    }
   ]
  });
 };


 /*
  * 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() {

  if (! data_loaded) {
   console.log('calcWater(): failsave');
   return;
  }

  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,
  r1d, r2d, f1d, f2d, f3d,
  deltapH, deltapd, pd, n,
  Res;

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

  $('#w1_hardness').val(Hardness(dataRecord.w1_calcium, dataRecord.w1_magnesium));
  $('#w1_ra').val(ResidualAlkalinity(dataRecord.w1_total_alkalinity, dataRecord.w1_calcium, dataRecord.w1_magnesium));

  // 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);
   $('#w2_hardness').val(Hardness(dataRecord.w2_calcium, dataRecord.w2_magnesium));
   $('#w2_ra').val(ResidualAlkalinity(dataRecord.w2_total_alkalinity, dataRecord.w2_calcium, dataRecord.w2_magnesium));
  } else {
   liters = dataRecord.w1_amount;
   calcium = dataRecord.w1_calcium;
   magnesium = dataRecord.w1_magnesium;
   sodium = dataRecord.w1_sodium;
   chloride = dataRecord.w1_chloride;
   sulfate = dataRecord.w1_sulfate;
   total_alkalinity = dataRecord.w1_total_alkalinity;
   ph = dataRecord.w1_ph;
  }
  var bicarbonate = Bicarbonate(total_alkalinity, ph);

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

  dataRecord.wg_amount = liters;
  dataRecord.wg_ph = ph;

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

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

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

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

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

  AT = dataRecord.wa_acid_name;

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

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

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

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

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

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

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

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

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

  if (calcium < 40) {
   setRangeIndicator('calcium', 'laag');
  } else if (calcium > 150) {
   setRangeIndicator('calcium', 'hoog');
  } else {
   setRangeIndicator('calcium', 'normaal');
  }
  if (magnesium < 5) {
   setRangeIndicator('magnesium', 'laag');
  } else if (magnesium > 40) {
   setRangeIndicator('magnesium', 'hoog');
  } else {
   setRangeIndicator('magnesium', 'normaal');
  }
  if (sodium <= 150) {
   setRangeIndicator('sodium', 'normaal');
  } else {
   setRangeIndicator('sodium', 'hoog');
  }
  // Both chloride and sulfate should be above 50 according to
  // John Palmer. So the Cl/SO4 ratio calculation will work.
  if (chloride <= 50) {
   setRangeIndicator('chloride', 'laag');
  } else if (chloride <= 150) {
   setRangeIndicator('chloride', 'normaal');
  } else {
   setRangeIndicator('chloride', 'hoog');
  }
  if (sulfate <= 50) {
   setRangeIndicator('sulfate', 'laag');
  } else if (sulfate <= 400) {
   setRangeIndicator('sulfate', 'normaal');
  } else {
   setRangeIndicator('sulfate', 'hoog');
  }
  // (cloride + sulfate) > 500 is too high
  if ((chloride + sulfate) > 500) {
   setRangeIndicator('chloride', 'hoog');
   setRangeIndicator('sulfate', 'hoog');
  }
  if (ph < 5.2) {
   setRangeIndicator('ph', 'laag');
  } else if (ph > 5.6) {
   setRangeIndicator('ph', 'hoog');
  } else {
   setRangeIndicator('ph', 'normaal');
  }
  if (bicarbonate > 250) {
   setRangeIndicator('bicarbonate', 'hoog');
  } else {
   setRangeIndicator('bicarbonate', 'normaal');
  }
  calcSparge();
 }


 function calcSparge() {

  /* Based on the work of ajDeLange. */
  var ws_calcium, ws_magnesium, ws_total_alkalinity, ws_sodium, ws_chloride;
  var ws_sulfate, ws_ph, ws_hardness, ws_ra;
  var TargetpH = dataRecord.sparge_ph;
  var Source_pH = 7.0;

  // Select watersource or fallback to the first source.
  if ((dataRecord.sparge_source == 1) && (dataRecord.w2_ph > 0.0)) { // Source 2
    ws_calcium = dataRecord.w2_calcium;
    ws_magnesium = dataRecord.w2_magnesium;
    ws_total_alkalinity = dataRecord.w2_total_alkalinity;
    ws_sodium = dataRecord.w2_sodium;
    ws_chloride = dataRecord.w2_chloride;
    ws_sulfate = dataRecord.w2_sulfate;
    Source_pH = dataRecord.w2_ph;
    $('#w2_button').jqxRadioButton({ checked: true });
  } else if ((dataRecord.sparge_source == 2) && (dataRecord.w2_ph > 0.0)) { // Mixed
    ws_calcium = dataRecord.wg_calcium;
    ws_magnesium = dataRecord.wg_magnesium;
    ws_total_alkalinity = dataRecord.wg_total_alkalinity;
    ws_sodium = dataRecord.wg_sodium;
    ws_chloride = dataRecord.wg_chloride;
    ws_sulfate = dataRecord.wg_sulfate;
    Source_pH = dataRecord.wg_ph;
    $('#wg_button').jqxRadioButton({ checked: true });
  } else {
    ws_calcium = dataRecord.w1_calcium;
    ws_magnesium = dataRecord.w1_magnesium;
    ws_total_alkalinity = dataRecord.w1_total_alkalinity;
    ws_sodium = dataRecord.w1_sodium;
    ws_chloride = dataRecord.w1_chloride;
    ws_sulfate = dataRecord.w1_sulfate;
    Source_pH = dataRecord.w1_ph;
    $('#w1_button').jqxRadioButton({ checked: true });
  }

  if (dataRecord.sparge_volume > 0) {
   ws_calcium += (parseFloat($('#ss_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 +
    parseFloat($('#ss_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000) / dataRecord.sparge_volume;
   ws_magnesium += (parseFloat($('#ss_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000 +
    parseFloat($('#ss_mgcl2').jqxNumberInput('decimal')) * MMMg / MMMgCl2 * 1000) / dataRecord.sparge_volume;
   ws_sodium += (parseFloat($('#ss_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000) / dataRecord.sparge_volume;
   ws_sulfate += (parseFloat($('#ss_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 +
    parseFloat($('#ss_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / dataRecord.sparge_volume;
   ws_chloride += (2 * parseFloat($('#ss_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 +
    parseFloat($('#ss_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000 +
    parseFloat($('#ss_mgcl2').jqxNumberInput('decimal')) * MMCl / MMMgCl2 * 1000) / dataRecord.sparge_volume;
  }

  /* Show the spargewate with salt additions */
  $('#sw_calcium').val(Round(ws_calcium, 1));
  $('#sw_magnesium').val(Round(ws_magnesium, 1));
  $('#sw_sodium').val(Round(ws_sodium, 1));
  $('#sw_sulfate').val(Round(ws_sulfate, 1));
  $('#sw_chloride').val(Round(ws_chloride, 1));
  $('#sw_bicarbonate').val(Round(Bicarbonate(ws_total_alkalinity, Source_pH), 1));
  $('#sw_total_alkalinity').val(Round(ws_total_alkalinity, 1));
  $('#sw_ph').val(dataRecord.sparge_ph);
  $('#sw_hardness').val(Hardness(ws_calcium, ws_magnesium));
  $('#sw_ra').val(ResidualAlkalinity(ws_total_alkalinity, ws_calcium, ws_magnesium));

  AT = dataRecord.sparge_acid_type;
  if (AT < 0 || AT >= AcidTypeData.length) {
   AT = 0;
   dataRecord.sparge_acid_type = 0;
   $('#sparge_acid_type').val(AcidTypeData[0].nl);
   dataRecord.sparge_acid_perc = AcidTypeData[0].AcidPrc;
   $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc);
  }

  /*
   * Auto calculate the required acid
   */
  if (dataRecord.calc_acid) {
   // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH
   var r1 = Math.pow(10, Source_pH - 6.35);
   var r2 = Math.pow(10, Source_pH - 10.33);
   var d = 1 + r1 + r1 * r2;
   var f1 = 1 / d;
   var f3 = r1 * r2 / d;

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

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

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

   //Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L)
   var Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH);  //mEq/l
   Acid += 0.01;   // Add acid that would be required for distilled water.

   //Step 8. Get the acid data.
   var fract = CalcFrac(TargetpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3);

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

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

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

  // Finally calculate the estimate preboil pH
  var ph = -Math.log10(((Math.pow(10, -dataRecord.wb_ph) * dataRecord.wg_amount) + (Math.pow(10, -dataRecord.sparge_ph) * dataRecord.sparge_volume)) /
	  (dataRecord.wg_amount + dataRecord.sparge_volume));
  $('#preboil_ph').val(ph);
 }

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

  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();

 };

 function saveRecord(goback) {
  window.location.href = my_return;
 };

 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 });
 $('#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').jqxInput({ theme: theme, width: 180, height: 23 });
 $('#efficiency').jqxTooltip({ content: 'Het rendement van maischen en koken.' });
 $('#efficiency').jqxNumberInput(Show1dec);
 $('#batch_size').jqxTooltip({ content: 'Het volume van het gekoelde wort na het koken.' });
 $('#batch_size').jqxNumberInput(Show1dec);
 $('#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(Show0dec);
 $('#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(Show3dec);
 $('#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').jqxInput({ theme: theme, width: 180, height: 23 }); 
 $('#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').jqxInput({ theme: theme, width: 180, height: 23 });
 $('#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';
  }
 });

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

 // Tab 4, Diversen

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

 // Tab 6, Maischen
 $('#mash_name').jqxInput({ theme: theme, width: 320, height: 23 });

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

 // Water source 1
 $('#w1_name').jqxInput({ theme: theme, width: 200, height: 23 });
 $('#w1_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true });
 $('#w1_amount').jqxNumberInput(Show1wat);
 $('#w1_calcium').jqxNumberInput(Show1wat);
 $('#w1_magnesium').jqxNumberInput(Show1wat);
 $('#w1_sodium').jqxNumberInput(Show1wat);
 $('#w1_bicarbonate').jqxNumberInput(Show1wat);
 $('#w1_total_alkalinity').jqxNumberInput(Show1wat);
 $('#w1_chloride').jqxNumberInput(Show1wat);
 $('#w1_sulfate').jqxNumberInput(Show1wat);
 $('#w1_ph').jqxNumberInput(Show2wat);
 $('#w1_hardness').jqxNumberInput(Show1wat);
 $('#w1_ra').jqxNumberInput(Show1wat);
 // Water source 2
 $('#w2_name').jqxInput({ theme: theme, width: 200, height: 23 });
 $('#w2_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true });
 $('#w2_amount').jqxNumberInput(Show1wat);
 $('#w2_calcium').jqxNumberInput(Show1wat);
 $('#w2_magnesium').jqxNumberInput(Show1wat);
 $('#w2_sodium').jqxNumberInput(Show1wat);
 $('#w2_bicarbonate').jqxNumberInput(Show1wat);
 $('#w2_total_alkalinity').jqxNumberInput(Show1wat);
 $('#w2_chloride').jqxNumberInput(Show1wat);
 $('#w2_sulfate').jqxNumberInput(Show1wat);
 $('#w2_ph').jqxNumberInput(Show2wat);
 $('#w2_hardness').jqxNumberInput(Show1wat);
 $('#w2_ra').jqxNumberInput(Show1wat);
 // Water mixed
 $('#wg_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true });
 $('#wg_amount').jqxNumberInput(Show1wat);
 $('#wg_calcium').jqxNumberInput(Show1wat);
 $('#wg_magnesium').jqxNumberInput(Show1wat);
 $('#wg_sodium').jqxNumberInput(Show1wat);
 $('#wg_bicarbonate').jqxNumberInput(Show1wat);
 $('#wg_total_alkalinity').jqxNumberInput(Show1wat);
 $('#wg_chloride').jqxNumberInput(Show1wat);
 $('#wg_sulfate').jqxNumberInput(Show1wat);
 $('#wg_ph').jqxNumberInput(Show2wat);
 $('#wg_hardness').jqxNumberInput(Show1wat);
 $('#wg_ra').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 tussen 5 en 40.'});
 $('#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 150. Samen met Sulfaat minder dan 500.'});
 $('#wb_chloride').jqxNumberInput(Show1wat);
 $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 400. Samen met Sulfaat minder dan 500.'});
 $('#wb_sulfate').jqxNumberInput(Show1wat);
 $('#wb_bicarbonate').jqxTooltip({ content: '0 tot 50 lichte bieren, 50 tot 150 amber bieren, 150 tot 250 donkere bieren.'});
 $('#wb_bicarbonate').jqxNumberInput(Show1wat);
 $('#wb_total_alkalinity').jqxNumberInput(Show1wat);
 $('#wb_ph').jqxNumberInput(Show2wat);
 $('#wb_hardness').jqxNumberInput(Show1wat);
 $('#wb_ra').jqxNumberInput(Show1wat);
 // Sparge water
 $('#sw_amount').jqxNumberInput(Show1wat);
 $('#sw_calcium').jqxNumberInput(Show1wat);
 $('#sw_magnesium').jqxNumberInput(Show1wat);
 $('#sw_sodium').jqxNumberInput(Show1wat);
 $('#sw_bicarbonate').jqxNumberInput(Show1wat);
 $('#sw_total_alkalinity').jqxNumberInput(Show1wat);
 $('#sw_chloride').jqxNumberInput(Show1wat);
 $('#sw_sulfate').jqxNumberInput(Show1wat);
 $('#sw_ph').jqxNumberInput(Show2wat);
 $('#sw_hardness').jqxNumberInput(Show1wat);
 $('#sw_ra').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(Show2wat);
 $('#ss_cacl2').jqxNumberInput(Show2wat);

 $('#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(Show2wat);
 $('#ss_caso4').jqxNumberInput(Show2wat);

 $('#wa_mgso4').jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'});
 $('#wa_mgso4').jqxNumberInput(Show2wat);
 $('#ss_mgso4').jqxNumberInput(Show2wat);

 $('#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(Show2wat);
 $('#ss_nacl').jqxNumberInput(Show2wat);

 $('#wa_mgcl2').jqxTooltip({ content: 'Magnesiumchloride'});
 $('#wa_mgcl2').jqxNumberInput(Show2wat);
 $('#ss_mgcl2').jqxNumberInput(Show2wat);

 $('#wa_nahco3').jqxTooltip({ content: 'Baksoda'});
 $('#wa_caco3').jqxTooltip({ content: 'Kalk'});
 $('#wa_nahco3,#wa_caco3').jqxNumberInput(Show2wat);

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

 $('#calc_acid').jqxCheckBox({ theme: theme, width: 120, height: 23, disabled: true });

 $('#wa_acid_name').jqxInput({ theme: theme, width: 130, height: 23 });
 $('#wa_acid').jqxNumberInput(Show2dec);
 $('#wa_acid').jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' });
 $('#wa_acid_perc').jqxNumberInput(Show0dec);
 $('#wa_acid_perc').jqxNumberInput({ symbol: '%', symbolPosition: 'right' });

 // Sparge water
 $('#sparge_ph').jqxNumberInput(Show2dec);
 $('#sparge_acid_amount').jqxNumberInput(Show2dec);
 $('#sparge_acid_amount').jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' });
 $('#sparge_acid_type').jqxInput({ theme: theme, width: 130, height: 23 });
 $('#sparge_acid_perc').jqxNumberInput(Show0dec);
 $('#sparge_acid_perc').jqxNumberInput({ symbol: '%', symbolPosition: 'right' });

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

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

});

mercurial