www/js/global.js

Fri, 01 May 2020 11:56:24 +0200

author
Michiel Broek <mbroek@mbse.eu>
date
Fri, 01 May 2020 11:56:24 +0200
changeset 662
4bb005694ce7
parent 656
a4a9f6ded485
child 663
7de681f68506
permissions
-rw-r--r--

Version 0.3.32 Inventory water and profile water edit screens updated and added ion balance fields. Moved acid specs to global.js. In prod_edit and rec_edit changed the water calculations, eliminated the double percentage calculation. The pH values in the water tab have now 2 decimal digits. The mash pH field is only shown in auto calculate mode. The calculated acid addition results are now a bit better and compare with several famous spreadsheets.

/*****************************************************************************
 * Copyright (C) 2014-2020
 *
 * Michiel Broek <mbroek at mbse dot eu>
 *
 * This file is part of BrewCloud
 *
 * 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.
 *****************************************************************************/


// dropdownlists arrays

var StageData = [
 { id: 0, en: 'Plan', nl: 'Plan' },
 { id: 1, en: 'Wait', nl: 'Wacht' },
 { id: 2, en: 'Brew', nl: 'Brouwen' },
 { id: 3, en: 'Primary', nl: 'Hoofdgisting' },
 { id: 4, en: 'Secondary', nl: 'Nagisting' },
 { id: 5, en: 'Tertiary', nl: 'Lagering' },
 { id: 6, en: 'Package', nl: 'Afvullen' },
 { id: 7, en: 'Carbonation', nl: 'Hergisten' },
 { id: 8, en: 'Mature', nl: 'Rijpen' },
 { id: 9, en: 'Taste', nl: 'Proeven' },
 { id: 10, en: 'Ready', nl: 'Gereed' },
 { id: 11, en: 'Closed', nl: 'Afgesloten' }
],
StageSource = {
 localdata: StageData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
StageAdapter = new $.jqx.dataAdapter(StageSource),

SplitData = [
 { id: 0, en: 'Not divided', nl: 'Niet gesplitst', ok: 100 },
 { id: 1, en: 'After mash', nl: 'Na maischen', ok: 2 },
 { id: 2, en: 'After boil', nl: 'Na koken', ok: 2 },
 { id: 3, en: 'After cooling', nl: 'Na koelen', ok: 2 },
 { id: 4, en: 'After primary', nl: 'Na hoofdgisting', ok: 3 },
 { id: 5, en: 'After secondary', nl: 'Na nagisting', ok: 4 },
 { id: 6, en: 'After tertiary', nl: 'Na lageren', ok: 5 }
],
SplitSource = {
 localdata: SplitData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }, { name: 'ok' }]
},
SplitAdapter = new $.jqx.dataAdapter(SplitSource),

MaterialData = [
 { id: 0, en: 'Stainless Steel', nl: 'RVS', sh: 0.11 },
 { id: 1, en: 'Aluminium', nl: 'Aluminium', sh: 0.22 },
 { id: 2, en: 'Plastics', nl: 'Kunststof', sh: 0.46 },
 { id: 3, en: 'Copper', nl: 'Koper', sh: 0.092 }
],
MaterialSource = {
 localdata: MaterialData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }, { name: 'sh' }]
},
MaterialAdapter = new $.jqx.dataAdapter(MaterialSource),

FermentableTypeData = [
 { id: 0, en: 'Grain', nl: 'Mout' },
 { id: 1, en: 'Sugar', nl: 'Suiker' },
 { id: 2, en: 'Extract', nl: 'Vloeibaar extract' },
 { id: 3, en: 'Dry extract', nl: 'Droog extract' },
 { id: 4, en: 'Adjunct', nl: 'Ongemout graan' }
],
FermentableTypeSource = {
 localdata: FermentableTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
FermentableTypeAdapter = new $.jqx.dataAdapter(FermentableTypeSource),

GrainTypeData = [
 { id: 0, en: 'Base', nl: 'Basismout' },
 { id: 1, en: 'Roast', nl: 'Geroosterde mout' },
 { id: 2, en: 'Crystal', nl: 'Cara- of crystalmout' },
 { id: 3, en: 'Kilned', nl: 'Geƫeste mout'},
 { id: 4, en: 'Sour malt', nl: 'Zuurmout' },
 { id: 5, en: 'Special', nl: 'Speciale mout' },
 { id: 6, en: 'No malt', nl: 'Geen mout' }
],
GrainTypeSource = {
 localdata: GrainTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
GrainTypeAdapter = new $.jqx.dataAdapter(GrainTypeSource),

AddedData = [
 { id: 0, en: 'Mash', nl: 'Maischen' },
 { id: 1, en: 'Boil', nl: 'Koken' },
 { id: 2, en: 'Fermentation', nl: 'Vergisten' },
 { id: 3, en: 'Lagering', nl: 'Nagisten/lageren' },
 { id: 4, en: 'Bottle', nl: 'Bottelen' },
 { id: 5, en: 'Kegs', nl: 'Fust' }
],
AddedSource = {
 localdata: AddedData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
AddedAdapter = new $.jqx.dataAdapter(AddedSource),

HopTypeData = [
 { id: 0, en: 'Bittering', nl: 'Bitterhop' },
 { id: 1, en: 'Aroma', nl: 'Aromahop' },
 { id: 2, en: 'Both', nl: 'Beide' }
],
HopTypeSource = {
 localdata: HopTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
HopTypeAdapter = new $.jqx.dataAdapter(HopTypeSource),

HopFormData = [
 { id: 0, en: 'Pellet', nl: 'Pellets' },
 { id: 1, en: 'Plug', nl: 'Plugs' },
 { id: 2, en: 'Leaf', nl: 'Bloemen' },
 { id: 3, en: 'Leaf wet', nl: 'Hop nat' },
 { id: 4, en: 'Cryo', nl: 'Cryo' }
],
HopFormSource = {
 localdata: HopFormData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
HopFormAdapter = new $.jqx.dataAdapter(HopFormSource),

HopUseData = [
 { id: 0, en: 'Mash', nl: 'Maischhop' },
 { id: 1, en: 'First wort', nl: 'First wort' },
 { id: 2, en: 'Boil', nl: 'Koken' },
 { id: 3, en: 'Aroma', nl: 'Vlamuit' },
 { id: 4, en: 'Whirlpool', nl: 'Whirlpool' },
 { id: 5, en: 'Dry hop', nl: 'Koudhop' }
],
HopUseSource = {
 localdata: HopUseData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
HopUseAdapter = new $.jqx.dataAdapter(HopUseSource),

YeastTypeData = [
 { id: 0, en: 'Lager', nl: 'Ondergist' },
 { id: 1, en: 'Ale', nl: 'Bovengist' },
 { id: 2, en: 'Wheat', nl: 'Tarwegist' },
 { id: 3, en: 'Wine', nl: 'Wijngist' },
 { id: 4, en: 'Champagne', nl: 'Champagnegist' },
 { id: 5, en: 'Brett', nl: 'Brett' },
 { id: 6, en: 'Kveik', nl: 'Kveik' },
 { id: 7, en: 'Hybrid', nl: 'Hybride' }
// { id: 8, en: 'Mixed', nl: 'Mixed' },
// { id: 9, en: 'Spontaneous', nl: 'Spontaan' },
// { id: 10, en: 'Other', nl: 'Overig' }
],
YeastTypeSource = {
 localdata: YeastTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
YeastTypeAdapter = new $.jqx.dataAdapter(YeastTypeSource),

YeastFormData = [
 { id: 0, en: 'Liquid', nl: 'Vloeibaar', cells: 100000000000 },
 { id: 1, en: 'Dry', nl: 'Droog', cells: 15000000000 },
 { id: 2, en: 'Slant', nl: 'Schuine buis', cells: 1700000000 },
 { id: 3, en: 'Culture', nl: 'Slurry', cells: 1700000000 },
 { id: 4, en: 'Frozen', nl: 'Ingevroren', cells: 1700000000 },
 { id: 5, en: 'Bottle', nl: 'Depot', cells: 1700000000 },
 { id: 6, en: 'Dried', nl: 'Gedroogd', cells: 9000000000 }  /* 9..18 billion MTF */
],
YeastFormSource = {
 localdata: YeastFormData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }, { name: 'cells' }]
},
YeastFormAdapter = new $.jqx.dataAdapter(YeastFormSource),

YeastUseData = [
 { id: 0, en: 'Primary', nl: 'Hoofdgisting' },
 { id: 1, en: 'Secondary', nl: 'Nagisting' },
 { id: 2, en: 'Tertiary', nl: 'Lagering' },
 { id: 3, en: 'Bottle', nl: 'Bottelen' }
],
YeastUseSource = {
 localdata: YeastUseData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }, { name: 'cells' }]
},
YeastUseAdapter = new $.jqx.dataAdapter(YeastUseSource),

FlocculationData = [
 { id: 0, en: 'Low', nl: 'Laag' },
 { id: 1, en: 'Medium', nl: 'Medium' },
 { id: 2, en: 'High', nl: 'Hoog' },
 { id: 3, en: 'Very high', nl: 'Zeer hoog' }
],
FlocculationSource = {
 localdata: FlocculationData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
FlocculationAdapter = new $.jqx.dataAdapter(FlocculationSource),

ZymocideData = [
 { id: 0, en: 'None', nl: 'Niet' },
 { id: 1, en: 'K1', nl: 'K1' },
 { id: 2, en: 'K2', nl: 'K2' },
 { id: 3, en: 'K28', nl: 'K28' },
 { id: 4, en: 'Klus', nl: 'Klus' }
],
ZymocideSource = {
 localdata: ZymocideData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
ZymocideAdapter = new $.jqx.dataAdapter(ZymocideSource),

StarterTypeData = [
 { id: 0, en: 'Stirred', nl: 'Geroerd' },
 { id: 1, en: 'Shaken', nl: 'Geschud' },
 { id: 2, en: 'Simple', nl: 'Simpel' }
],
StarterTypeSource = {
 localdata: StarterTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
StarterTypeAdapter = new $.jqx.dataAdapter(StarterTypeSource),

MiscTypeData = [
 { id: 0, en: 'Spice', nl: 'Specerij' },
 { id: 1, en: 'Herb', nl: 'Kruid' },
 { id: 2, en: 'Flavor', nl: 'Smaakstof' },
 { id: 3, en: 'Fining', nl: 'Klaringsmiddel' },
 { id: 4, en: 'Water agent', nl: 'Brouwzout' },
 { id: 5, en: 'Yeast nutrient', nl: 'Gistvoeding' },
 { id: 6, en: 'Other', nl: 'Anders' }
],
MiscTypeSource = {
 localdata: MiscTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
MiscTypeAdapter = new $.jqx.dataAdapter(MiscTypeSource),

MiscUseData = [
 { id: 0, en: 'Starter', nl: 'Starter' },
 { id: 1, en: 'Mash', nl: 'Maischen' },
 { id: 2, en: 'Boil', nl: 'Koken' },
 { id: 3, en: 'Primary', nl: 'Hoofdvergisting' },
 { id: 4, en: 'Secondary', nl: 'Nagisting/lagering' },
 { id: 5, en: 'Bottling', nl: 'Bottelen' }
],
MiscUseSource = {
 localdata: MiscUseData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
MiscUseAdapter = new $.jqx.dataAdapter(MiscUseSource),

StyleTypeData = [
 { id: 0, en: 'Lager', nl: 'Ondergistend bier' },
 { id: 1, en: 'Ale', nl: 'Bovengistend bier' },
 { id: 2, en: 'Mead', nl: 'Mede' },
 { id: 3, en: 'Wheat', nl: 'Tarwebier' },
 { id: 4, en: 'Mixed', nl: 'Gemengd' },
 { id: 5, en: 'Cider', nl: 'Cider' }
],
StyleTypeSource = {
 localdata: StyleTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
StyleTypeAdapter = new $.jqx.dataAdapter(StyleTypeSource),

MashStepTypeData = [
 { id: 0, en: 'Infusion', nl: 'Infusie' },
 { id: 1, en: 'Temperature', nl: 'Verwarming' },
 { id: 2, en: 'Decoction', nl: 'Decoctie' }
],
MashStepTypeSource = {
 localdata: MashStepTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
MashStepTypeAdapter = new $.jqx.dataAdapter(MashStepTypeSource),

RecipeTypeData = [
 { id: 0, en: 'Extract', nl: 'Extract' },
 { id: 1, en: 'Partial Mash', nl: 'Deelmaisch' },
 { id: 2, en: 'All Grain', nl: 'Mout' }
],
RecipeTypeSource = {
 localdata: RecipeTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
RecipeTypeAdapter = new $.jqx.dataAdapter(RecipeTypeSource),

IBUmethodData = [
 { id: 0, en: 'Tinseth', nl: 'Tinseth' },
 { id: 1, en: 'Rager', nl: 'Rager' },
 { id: 2, en: 'Daniels', nl: 'Daniels' }
],
IBUmethodSource = {
 localdata: IBUmethodData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
IBUmethodAdapter = new $.jqx.dataAdapter(IBUmethodSource),

ColorMethodData = [
 { id: 0, en: 'Morey', nl: 'Morey' },
 { id: 1, en: 'Mosher', nl: 'Mosher' },
 { id: 2, en: 'Daniels', nl: 'Daniels' },
 { id: 3, en: 'Halberstadt', nl: 'Halberstadt' },
 { id: 4, en: 'Naudts', nl: 'Naudts' }
],
ColorMethodSource = {
 localdata: ColorMethodData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
ColorMethodAdapter = new $.jqx.dataAdapter(ColorMethodSource),

CoolingTypeData = [
 { id: 0, en: '-', nl: '-' },
 { id: 1, en: 'Emersion chiller', nl: 'Dompelkoeler' },
 { id: 2, en: 'Counterflow chiller', nl: 'Tegenstroomkoeler' },
 { id: 3, en: 'Au bain marie', nl: 'Au bain marie' },
 { id: 4, en: 'Natural', nl: 'Laten afkoelen' }
],
CoolingTypeSource = {
 localdata: CoolingTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
CoolingTypeAdapter = new $.jqx.dataAdapter(CoolingTypeSource),

AerationTypeData = [
 { id: 0, en: 'None', nl: 'Geen' },
 { id: 1, en: 'Air', nl: 'Lucht' },
 { id: 2, en: 'Oxygen', nl: 'Zuurstof' }
],
AerationTypeSource = {
 localdata: AerationTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
AerationTypeAdapter = new $.jqx.dataAdapter(AerationTypeSource),

AcidTypeData = [
 /* Note: AcidSG is for 100% use. AcidPrc is the default setting. */
 { id: 0, en: 'Lactic', nl: 'Melkzuur', pK1: 3.86, pK2: 20, pK3: 20, MolWt: 90.08, AcidSG: 1238, AcidPrc: 80 },
 { id: 1, en: 'Hydrochloric', nl: 'Zoutzuur', pK1: -7, pK2: 20, pK3: 20, MolWt: 36.46, AcidSG: 1497, AcidPrc: 28 },
 { id: 2, en: 'Phosphoric', nl: 'Fosforzuur', pK1: 2.12, pK2: 7.20, pK3: 12.44, MolWt: 98.00, AcidSG: 1765, AcidPrc: 25 },
 { id: 3, en: 'Sulfuric', nl: 'Zwavelzuur', pK1: -1, pK2: 1.92, pK3: 20, MolWt: 98.07, AcidSG: 1884, AcidPrc: 93 }
],
AcidTypeSource = {
 localdata: AcidTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
AcidTypeAdapter = new $.jqx.dataAdapter(AcidTypeSource),

BaseTypeData = [
 { id: 0, en: 'Sodiumbicarbonate', nl: 'NaHCO3' },
 { id: 1, en: 'Sodiumcarbonate', nl: 'Na2CO3' },
 { id: 2, en: 'Calciumcarbonate', nl: 'CaCO3' },
 { id: 3, en: 'Calciumhydroxide', nl: 'Ca(OH)2' }
],
BaseTypeSource = {
 localdata: BaseTypeData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
BaseTypeAdapter = new $.jqx.dataAdapter(BaseTypeSource),

SpargeSourceData = [
 { id: 0, en: 'Source 1', nl: 'Bron 1' },
 { id: 1, en: 'Source 2', nl: 'Bron 2' },
 { id: 2, en: 'Mixed', nl: 'Gemengd' }
],
SpargeSourceSource = {
 localdata: SpargeSourceData,
 datatype: 'array',
 datafields: [{ name: 'id' }, { name: 'en' }, { name: 'nl' }]
},
SpargeSourceAdapter = new $.jqx.dataAdapter(SpargeSourceSource),


// options for editors

Show1wat = { inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true },
Show2wat = { inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 2, readOnly: true },
Show3wat = { inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 3, readOnly: true },
Smal0dec = { inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true },
Smal1dec = { inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true },
Smal2dec = { inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 2, readOnly: true },
Show0dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 0 },
Show1dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 1 },
Show2dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 2 },
Show3dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 3 },
Show4dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 4 },
Show5dec = { inputMode: 'simple', theme: theme, width: 90, height: 23, readOnly: true, decimalDigits: 5 },
  SGopts = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0.990, max: 1.199, decimalDigits: 3, spinButtons: true },
Spin1dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 1, spinButtons: true },
Spin2dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 2, spinButtons: true },
Spin3dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 3, spinButtons: true },
Spin4dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 4, spinButtons: true },
  SpinpH = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 1, max: 14, decimalDigits: 1, spinButtons: true },
 Spin2pH = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 1, max: 14, decimalDigits: 2, spinButtons: true },
  YeastT = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, max: 45, decimalDigits: 1, spinButtons: true },
  PosInt = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, decimalDigits: 0, spinButtons: true },
Perc1dec = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, max: 100, decimalDigits: 1, spinButtons: true },
   Perc0 = { inputMode: 'simple', theme: theme, width: 110, height: 23, min: 0, max: 100, decimalDigits: 0, spinButtons: true },
Dateopts = {
 theme: theme, width: 150, height: 23, allowNullDate: true, todayString: 'Vandaag', clearString: 'Wissen', showFooter: true,
 formatString: 'yyyy-MM-dd', enableBrowserBoundsDetection: true
},
DateTimeopts = {
 theme: theme, width: 230, height: 23, allowNullDate: true, todayString: 'Vandaag', clearString: 'Wissen', showFooter: true,
 formatString: 'yyyy-MM-dd HH:mm:ss', enableBrowserBoundsDetection: true, showTimeButton: true
},


sugardensity = 1.611, //kg/l in solution

// Styles dropdown list
stylesSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'category', type: 'string' },
  { name: 'category_number', type: 'number' },
  { name: 'style_letter', type: 'string' },
  { name: 'style_guide', type: 'string' },
  { name: 'type', type: 'int' },
  { name: 'og_min', type: 'float' },
  { name: 'og_max', type: 'float' },
  { name: 'fg_min', type: 'float' },
  { name: 'fg_max', type: 'float' },
  { name: 'ibu_min', type: 'float' },
  { name: 'ibu_max', type: 'float' },
  { name: 'color_min', type: 'float' },
  { name: 'color_max', type: 'float' },
  { name: 'carb_min', type: 'float' },
  { name: 'carb_max', type: 'float' },
  { name: 'abv_min', type: 'float' },
  { name: 'abv_max', type: 'float' },
  { name: 'notes', type: 'string' },
  { name: 'profile', type: 'string' },
  { name: 'ingredients', type: 'string' },
  { name: 'examples', type: 'string' }
 ],
 url: 'includes/db_profile_styles.php'
},
styleslist = new $.jqx.dataAdapter(stylesSource),

// Equipemnt dropdown list
equipmentSource = {
 datatype: 'json',
 datafields: [
  { name: 'name', type: 'string' },
  { name: 'boil_size', type: 'float' },
  { name: 'batch_size', type: 'float' },
  { name: 'tun_volume', type: 'float' },
  { name: 'tun_weight', type: 'float' },
  { name: 'tun_specific_heat', type: 'float' },
  { name: 'tun_material', type: 'int' },
  { name: 'tun_height', type: 'float' },
  { name: 'top_up_water', type: 'float' },
  { name: 'trub_chiller_loss', type: 'float' },
  { name: 'evap_rate', type: 'float' },
  { name: 'boil_time', type: 'float' },
  { name: 'calc_boil_volume', type: 'int' },
  { name: 'top_up_kettle', type: 'float' },
  { name: 'hop_utilization', type: 'float' },
  { name: 'notes', type: 'string' },
  { name: 'lauter_volume', type: 'float' },
  { name: 'lauter_height', type: 'float' },
  { name: 'lauter_deadspace', type: 'float' },
  { name: 'kettle_volume', type: 'float' },
  { name: 'kettle_height', type: 'float' },
  { name: 'mash_volume', type: 'float' },
  { name: 'mash_max', type: 'float' },
  { name: 'efficiency', type: 'float' }
 ],
 url: 'includes/db_inventory_equipments.php'
},
equipmentlist = new $.jqx.dataAdapter(equipmentSource),

// dropdownlist datasource from inventory_fermentables
fermentableInvSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'type', type: 'int' },
  { name: 'yield', type: 'float' },
  { name: 'color', type: 'float' },
  { name: 'add_after_boil', type: 'int' },
  { name: 'origin', type: 'string' },
  { name: 'supplier', type: 'string' },
  { name: 'coarse_fine_diff', type: 'float' },
  { name: 'moisture', type: 'float' },
  { name: 'diastatic_power', type: 'float' },
  { name: 'protein', type: 'float' },
  { name: 'dissolved_protein', type: 'float' },
  { name: 'max_in_batch', type: 'float' },
  { name: 'recommend_mash', type: 'int' },
  { name: 'graintype', type: 'int' },
  { name: 'di_ph', type: 'float' },
  { name: 'acid_to_ph_57', type: 'float' },
  { name: 'inventory', type: 'float' },
  { name: 'cost', type: 'float' }
 ],
 url: 'getfermentablesources.php'
},
fermentableinstock = false,
fermentablelist = new $.jqx.dataAdapter(fermentableInvSource, {
 beforeLoadComplete: function(records) {
  var row, i, data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
   if (row.inventory || ! fermentableinstock)
    data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),
fermentablesugars = new $.jqx.dataAdapter(fermentableInvSource, {
 beforeLoadComplete: function(records) {
  var row, i, data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
   if (row.type == 1 || row.type == 3) // Sugars or dry extract
    data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),

// dropdownlist datasource from inventory_hops
hopInvSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'origin', type: 'string' },
  { name: 'type', type: 'int' },
  { name: 'alpha', type: 'float' },
  { name: 'beta', type: 'float' },
  { name: 'humulene', type: 'float' },
  { name: 'caryophyllene', type: 'float' },
  { name: 'cohumulone', type: 'float' },
  { name: 'myrcene', type: 'float' },
  { name: 'hsi', type: 'float' },
  { name: 'useat', type: 'int' },
  { name: 'form', type: 'int' },
  { name: 'total_oil', type: 'float' },
  { name: 'inventory', type: 'float' },
  { name: 'cost', type: 'float' }
 ],
 url: 'gethopsources.php'
},
hopinstock = false,
hoplist = new $.jqx.dataAdapter(hopInvSource, {
 beforeLoadComplete: function(records) {
  var row, i, data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
   if (row.inventory || ! hopinstock)
    data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),

// dropdownlist datasource from inventory_miscs
miscInvSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'type', type: 'int' },
  { name: 'use_use', type: 'int' },
  { name: 'amount_is_weight', type: 'int' },
  { name: 'time', type: 'float' },
  { name: 'inventory', type: 'float' },
  { name: 'cost', type: 'float' }
 ],
 url: 'getmiscsources.php'
},
miscinstock = false,
misclist = new $.jqx.dataAdapter(miscInvSource, {
 beforeLoadComplete: function(records) {
  var row, i, data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
    if (row.inventory || ! miscinstock)
     data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),

// dropdownlist datasource from inventory_yeasts
yeastInvSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'type', type: 'int' },
  { name: 'form', type: 'int' },
  { name: 'laboratory', type: 'string' },
  { name: 'product_id', type: 'string' },
  { name: 'min_temperature', type: 'float' },
  { name: 'max_temperature', type: 'float' },
  { name: 'flocculation', type: 'int' },
  { name: 'attenuation', type: 'float' },
  { name: 'cells', type: 'float' },
  { name: 'inventory', type: 'float' },
  { name: 'cost', type: 'float' },
  { name: 'tolerance', type: 'float' },
  { name: 'sta1', type: 'int' },
  { name: 'bacteria', type: 'int' },
  { name: 'harvest_top', type: 'int' },
  { name: 'harvest_time', type: 'int' },
  { name: 'pitch_temperature', type: 'float' },
  { name: 'pofpos', type: 'int' },
  { name: 'zymocide', type: 'int' }
 ],
 url: 'getyeastsources.php'
},
yeastinstock = false,
yeastlist = new $.jqx.dataAdapter(yeastInvSource, {
 beforeLoadComplete: function(records) {
  var row, i, data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
   if (row.inventory || ! yeastinstock)
    data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),
yeastlablist = new $.jqx.dataAdapter(yeastInvSource, { autoBind: false, async: false, uniqueDataFields: ['laboratory'] }),

// dropdownlist datasource from inventory_waters
waterInvSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'unlimited_stock', type: 'int' },
  { name: 'calcium', type: 'float' },
  { name: 'sulfate', type: 'float' },
  { name: 'chloride', type: 'float' },
  { name: 'sodium', type: 'float' },
  { name: 'magnesium', type: 'float' },
  { name: 'ph', type: 'float' },
  { name: 'total_alkalinity', type: 'float' },
  { name: 'inventory', type: 'float' },
  { name: 'cost', type: 'float' },
 ],
 url: 'getwatersources.php'
},
waterinstock = false,
waterlist = new $.jqx.dataAdapter(waterInvSource, {
 beforeLoadComplete: function(records) {
  var data, i, row;
  data = new Array();
  for (i = 0; i < records.length; i++) {
   row = records[i];
   if (row.inventory || row.unlimited_stock || ! waterinstock)
    data.push(row);
  }
  return data;
 },
 loadError: function(jqXHR, status, error) {
  console.log(status + ' ' + error);
 },
}),

// dropdownlist datasource from profile_water
waterProfileSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'calcium', type: 'float' },
  { name: 'bicarbonate', type: 'float' },
  { name: 'sulfate', type: 'float' },
  { name: 'chloride', type: 'float' },
  { name: 'sodium', type: 'float' },
  { name: 'magnesium', type: 'float' },
  { name: 'ph', type: 'float' },
  { name: 'total_alkalinity', type: 'float' },
 ],
 url: 'includes/db_profile_water.php'
},
waterprofiles = new $.jqx.dataAdapter(waterProfileSource),

// dropdownlist datasource from profile_mash
mashProfileSource = {
 datatype: 'json',
 datafields: [
  { name: 'record', type: 'number' },
  { name: 'name', type: 'string' },
  { name: 'steps', type: 'array' }
 ],
 url: 'includes/db_profile_mash.php'
},
mashlist = new $.jqx.dataAdapter(mashProfileSource);



$(document).ready(function() {

 $('#jqxMenu').jqxMenu({
  width: 1280,
  height: '30px',
  autoOpen: false,
  clickToOpen: true,
  theme: theme
 });
 $('#jqxWidget').css('visibility', 'visible');

});



function Round(n, d) {
 for (var i = 0, m = 1; i < d; i++, m *= 10);
 return Math.round(n * m) / m;
}



function ebc_to_srm(ebc) {
 var srm = -1.32303E-12 * Math.pow(ebc, 4) - 0.00000000291515 * Math.pow(ebc, 3) + 0.00000818515 * Math.pow(ebc, 2) + 0.372038 * ebc + 0.596351;
 if (srm < 0)
  srm = 0;
 return srm;
}



function srm_to_ebc(srm) {
 var ebc = Math.round(0.000000000176506 * Math.pow(srm, 4) + 0.000000154529 * Math.pow(srm, 3) - 0.000159428 * Math.pow(srm, 2) + 2.68837 * srm - 1.6004);
 if (ebc < 0)
  ebc = 0;
 return ebc;
}



/* Return incremented color by the boil and yeast.
 * https://www.hobbybrouwen.nl/forum/index.php/topic,19020.msg281132.html#msg281132 */
function get_kt(ebc) {

 var kt = 1;
 if (ebc < 3)
  kt = 3.5;
 else if (ebc < 6)
  kt = 3;
 else if (ebc < 8)
  kt = 2.75;
 else if (ebc < 10)
  kt = 2.5;
 else if (ebc < 20)
  kt = 1.8;
 else if (ebc < 30)
  kt = 1.6;
 else if (ebc < 60)
  kt = 1.3;
 else if (ebc < 100)
  kt = 1.2;
 else if (ebc < 300)
  kt = 1.1;
 return kt;
}



function abvol(og, fg) {
 if (((og - fg) < 0) || (fg < 0.9))
  return 0;
 var factor = og * 3157 * Math.pow(10, -5) + 9.716 * Math.pow(10, -2);
 return (og * 1000 - fg * 1000) * factor;
}



/* Kleurwerking naar SRM. Niet voor Halberstadt, Naudts */
function kw_to_srm(colormethod, c) {
 if (colormethod == 0)
  return 1.4922 * Math.pow(c, 0.6859); //Morey
 if (colormethod == 1)
  return 0.3 * c + 4.7; //Mosher
 if (colormethod == 2)
  return 0.2 * c + 8.4; //Daniels
 return 0; //Halberstadt,Naudts
}



function kw_to_ebc(colormethod, c) {
 return srm_to_ebc(kw_to_srm(colormethod, c));
}



/*
 * Berekeningen uit https://www.hobbybrouwen.nl/forum/index.php/topic,6079.msg69464.html#msg69464
 */
function toIBU(Use, Form, SG, Volume, Amount, Boiltime, Alpha, Method, Whirlpool9, Whirlpool7, Whirlpool6) {

 var gravity, liters, alpha, mass, time, fmoment = 1.0, pfactor = 1.0, ibu = 0, boilfactor, sgfactor,
     AddedAlphaAcids, Bigness_factor, BoilTime_factor, utiisation;

 gravity = parseFloat(SG);
 liters = parseFloat(Volume);
 alpha = parseFloat(Alpha) / 100;
 mass = parseFloat(Amount) * 1000;
 time = parseFloat(Boiltime);

 if ((Use == 3) || (Use == 4) || (Use == 5)) { // Aroma, Whirlpool or Dry hop.
  fmoment = 0.0;
 } else if (Use == 0) { // Mash
  fmoment += my_factor_mashhop / 100; // Brouwhulp
 } else if (Use == 1) { // First wort
  fmoment += my_factor_fwh / 100; // Brouwhulp, Louis, Ozzie
 }

 if (Form == 0) { // Pellet
  pfactor += my_factor_pellet / 100;
 } else if (Form == 1) { // Plug
  pfactor += my_factor_plug / 100;
 } else if (Form == 3) { // Wet leaf
  pfactor += my_factor_wethop / 100; // From https://github.com/chrisgilmerproj/brewday/blob/master/brew/constants.py
 } else if (Form == 4) { // Cryo hop
  pfactor += my_factor_cryohop / 100;
 }

 // Ideas from Zymurgy March-April 2018. These are not exact formulas!
 whirlibus = 0;
 if (Use == 3 || Use == 4) { // Flameout or any whirlpool
  if (Whirlpool9) {
   // 20 mg/l/50 min
   whirlibus += (alpha * mass * 20) / liters * (Whirlpool9 / 50);
   //console.log('Whirlpool9:' + alpha * mass * 20 + ' liter:' + liters + ' time:' + Whirlpool9 + ' ibu' + (alpha * mass * 20) / liters * (Whirlpool9 / 50));
  } else {
   if (Use == 3) // Flameout hops are 2 minutes in this range.
    whirlibus += (alpha * mass * 20) / liters * (2 / 50);
  }
  if (Whirlpool7) {
   // 6 mg/l/50 min
   whirlibus += (alpha * mass * 6) / liters * (Whirlpool7 / 50);
   //console.log('Whirlpool7:' + alpha * mass * 6 + ' liter:' + liters + ' time:' + Whirlpool7 + ' ibu' + (alpha * mass * 6) / liters * (Whirlpool7 / 50));
  } else {
   if (Use == 3) // Flameout hops are 4 minutes in this range.
    whirlibus += (alpha * mass * 6) / liters * (4 / 50);
  }
  if (Whirlpool6) {
   // 2 mg/l/50 min
   whirlibus += (alpha * mass * 2) / liters * (Whirlpool6 / 50);
   //console.log('Whirlpool6:' + alpha * mass * 2 + ' liter:' + liters + ' time:' + Whirlpool6 + ' ibu' + (alpha * mass * 2) / liters * (Whirlpool6 / 50));
  }
 }

 if (Method == 0) { // Tinseth
  /* http://realbeer.com/hops/research.html */
  AddedAlphaAcids = (alpha * mass * 1000) / liters;
  Bigness_factor = 1.65 * Math.pow(0.000125, gravity - 1);
  BoilTime_factor = ((1 - Math.exp(-0.04 * time)) / 4.15);
  utiisation = Bigness_factor * BoilTime_factor;
  ibu = Round(utiisation * AddedAlphaAcids * fmoment * pfactor + whirlibus, 1);
 }
 if (Method == 2) { // Daniels
  if (Form == 2) // Leaf
   boilfactor = -(0.0041 * time * time) + (0.6162 * time) + 1.5779;
  else
   boilfactor = -(0.0051 * time * time) + (0.7835 * time) + 1.9348;
  if (gravity < 1050)
   sgfactor = 0;
  else
   sgfactor = (gravity - 1050) / 200;
  ibu = Round(fmoment * ((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor))) + whirlibus, 1);
 }
 if (Method == 1) { // Rager
  boilfactor = fmoment * 18.11 + 13.86 * Math.tanh((time * 31.32) / 18.27);
  if (gravity < 1050)
   sgfactor = 0;
  else
   sgfactor = (gravity - 1050) / 200;
  ibu = Round((mass * (alpha * 100) * boilfactor * 0.1) / (liters * (1 + sgfactor)) + whirlibus, 1);
 }

// console.log('toIBU(' + Use + ',' + Form + ',' + SG + ',' + Volume + ',' + Amount + ',' + Boiltime + ',' +
//             Alpha + ',' + Method + ',' + Whirlpool9 + ',' + Whirlpool7 + ',' + Whirlpool6 + '):' + ibu + ' fm:' + fmoment + ' pf:' + pfactor);
 return ibu;
}



function ebc_to_color(ebc) {
 return srm_to_color(ebc_to_srm(ebc));
}



function srm_to_color(srm) {
 var i, R, G, B, color, result;

 i = Math.round(srm * 10);
 if (i < 0)
  i = 0;
 if (i > 299)
  i = 299;

 /* Table copied from Brouwhulp/BrewBuddy */

 R = [
 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, //0
 250, 250, 250, 250, 250, 249, 248, 247, 246, 245, 244, 243, 242, 241, 240, 239, 238, 237, 236, 235, //2
 234, 233, 232, 231, 230, 229, 228, 227, 226, 225, 224, 223, 222, 221, 220, 219, 218, 217, 216, 215, //4
 214, 213, 212, 211, 210, 209, 208, 207, 206, 205, 204, 203, 202, 201, 200, 200, 199, 199, 198, 198, //6
 197, 197, 196, 196, 195, 195, 194, 194, 193, 193, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, //8
 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, //10
 192, 192, 192, 192, 192, 192, 192, 192, 191, 190, 189, 188, 187, 186, 185, 184, 183, 182, 181, 180, //12
 179, 178, 177, 175, 174, 172, 171, 169, 168, 167, 195, 164, 162, 161, 159, 158, 157, 155, 154, 152, //14
 151, 149, 148, 147, 145, 144, 142, 141, 139, 138, 137, 135, 134, 132, 131, 129, 128, 127, 125, 124, //16
 122, 121, 119, 118, 117, 115, 114, 112, 111, 109, 108, 107, 105, 104, 102, 101, 99, 98, 97, 95, //18
 94, 92, 91, 89, 88, 87, 85, 84, 82, 81, 79, 78, 77, 75, 74, 72, 71, 69, 68, 67, //20
 65, 64, 62, 61, 59, 58, 57, 55, 54, 52, 51, 49, 48, 47, 45, 44, 43, 41, 39, 38, //22
 37, 37, 36, 36, 35, 35, 34, 34, 33, 33, 32, 32, 31, 31, 30, 30, 29, 29, 28, 28, //24
 27, 27, 26, 26, 25, 25, 24, 24, 23, 23, 22, 22, 21, 21, 20, 20, 19, 19, 18, 18, //26
 17, 17, 16, 16, 15, 15, 14, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 8, 8];

 G = [
 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250, 250,
 250, 250, 250, 250, 250, 250, 249, 248, 247, 246, 245, 244, 242, 240, 238, 236, 234, 232, 230, 228,
 226, 224, 222, 220, 218, 216, 214, 212, 210, 208, 206, 204, 202, 200, 198, 196, 194, 192, 190, 188,
 186, 184, 182, 180, 178, 176, 174, 172, 170, 168, 166, 164, 162, 160, 158, 156, 154, 152, 150, 148,
 146, 144, 142, 141, 140, 139, 139, 138, 137, 136, 136, 135, 134, 133, 133, 132, 131, 130, 130, 129,
 128, 127, 127, 126, 125, 124, 124, 123, 122, 121, 121, 120, 119, 118, 118, 117, 116, 115, 115, 114,
 113, 112, 112, 111, 110, 109, 109, 108, 107, 106, 106, 105, 104, 103, 103, 102, 101, 100, 100, 99,
 98, 97, 97, 96, 95, 94, 94, 93, 92, 91, 91, 90, 89, 88, 88, 87, 86, 85, 85, 84,
 83, 82, 82, 81, 80, 79, 78, 77, 76, 75, 75, 74, 73, 72, 72, 71, 70, 69, 69, 68,
 67, 66, 66, 65, 64, 63, 63, 62, 61, 60, 60, 59, 58, 57, 57, 56, 55, 54, 54, 53,
 52, 51, 51, 50, 49, 48, 48, 47, 46, 45, 45, 44, 43, 42, 42, 41, 40, 39, 39, 38,
 37, 36, 36, 35, 34, 33, 33, 32, 31, 30, 30, 29, 28, 27, 27, 26, 25, 24, 24, 23,
 22, 22, 22, 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 16, 16,
 16, 15, 15, 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9,
 9, 9, 8, 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3];

 B = [
 210, 204, 199, 193, 188, 182, 177, 171, 166, 160, 155, 149, 144, 138, 133, 127, 122, 116, 111, 105,
 100, 94, 89, 83, 78, 72, 67, 61, 56, 50, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47,
 47, 48, 48, 48, 48, 49, 49, 49, 49, 50, 50, 50, 50, 51, 51, 51, 51, 52, 52, 52,
 52, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56,
 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
 56, 56, 56, 55, 55, 55, 55, 54, 54, 54, 54, 53, 53, 53, 53, 52, 52, 52, 52, 51,
 51, 51, 51, 50, 50, 50, 50, 49, 49, 48, 47, 47, 46, 45, 45, 44, 43, 43, 42, 41,
 41, 40, 39, 39, 38, 37, 37, 36, 35, 34, 33, 32, 31, 29, 28, 27, 26, 25, 24, 23,
 21, 20, 19, 18, 17, 16, 15, 13, 12, 11, 10, 9, 8, 9, 9, 10, 10, 11, 11, 12,
 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22,
 21, 21, 21, 20, 20, 20, 19, 19, 19, 18, 18, 18, 17, 17, 17, 17, 16, 16, 15, 15,
 15, 14, 14, 14, 13, 13, 13, 12, 12, 12, 11, 11, 11, 10, 10, 10, 9, 9, 9, 8,
 8, 8, 7, 7, 7, 6, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2];

 color = R[i] * 65536 + G[i] * 256 + B[i];
 result = color.toString(16).toUpperCase();
 if (result.length < 6)
  result = '0' + result;
 return '#' + result;
}



function sg_to_plato(sg) {
// return ((135.997 * sg - 630.272) * sg + 1111.14) * sg - 616.868;
 return -668.962 + (1262.45 * sg) - (776.43 * sg * sg) + (182.94 * sg * sg * sg);
}



function plato_to_sg(plato) {
// return 1 + (plato / (258.6 - ((plato / 258.2) * 227.1)));
 return 1.00001 + (0.0038661 * plato) + (1.3488e-5 * plato * plato) + (4.3074e-8 * plato * plato * plato);
}



function calc_svg(og, fg) {
 var oe = sg_to_plato(og);
 var ae = sg_to_plato(fg);
 return (oe - ae) / oe * 100;
}



function brix_to_sg(brix) {
 if (my_brix_correction > 0)
  return plato_to_sg(brix / my_brix_correction);
 else
  return plato_to_sg(brix);
}



function sg_to_brix(sg) {
 return sg_to_plato(sg) * my_brix_correction;
}



function brix_to_fg(OBrix, FBrix) {
 // Brouwhulp, werkt zonder brix_correctie, waarom?
 var FGbh = Round(1.0031 - 0.002318474 * OBrix - 0.000007775 * (OBrix * OBrix) - 0.000000034 * Math.pow(OBrix, 3) +
            0.00574 * (FBrix) + 0.00003344 * (FBrix * FBrix) + 0.000000086 * Math.pow(FBrix, 3), 4);

 // from http://seanterrill.com   FGoc = old cubix, FGnc = new cubic, FGnl = new linear
 var OBc = OBrix / my_brix_correction;
 var FBc = FBrix / my_brix_correction;

 // Old Cubic, almost the same a BrouwHulp, different offset and with brix_correction.
 var FGoc = Round(1.001843 - 0.002318474 * OBc - 0.000007775 * (OBc * OBc) - 0.000000034 * Math.pow(OBc, 3) +
            0.00574 * (FBc) + 0.00003344 * (FBc * FBc) + 0.000000086 * Math.pow(FBc, 3), 4);

 // New cubic. This looks the best to use.
 var FGnc = Round(1 - 0.0044993 * (OBc) + 0.0117741 * (FBc) +
            0.000275806 * (OBc * OBc) - 0.00127169 * (FBc * FBc) -
            0.00000727999 * Math.pow(OBc, 3) + 0.0000632929 * Math.pow(FBc, 3), 4);

 // New linear, results are pretty much too high and way off for heavy beers.
 var FGnl = Round(1 - 0.000856829 * OBc + 0.00349412 * FBc, 4);

 console.log('brix_to_fg(' + Round(OBrix, 2) + ', ' + FBrix + ') FGbh:' + FGbh + ' FGoc:' + FGoc + ' FGnc:' + FGnc + ' FGnl:' + FGnl);
 return FGnc;
}



function estimate_sg(sugars, batch_size) {

 var plato, sg, i;

 plato = 100 * sugars / batch_size;
 sg = plato_to_sg(plato);
 for (i = 0; i < 20; i++) {
  if (sg > 0)
   plato = 100 * sugars / (batch_size * sg);
  sg = plato_to_sg(plato);
 }
 return Round(sg, 4);
}



function estimate_fg(percSugar, percCara, WGratio, TotTme, Temp, attenuation, og) {

 var BD, AttBeer, fg;

 if (percSugar > 40)
  percSugar = 0;
 if (percCara > 50)
  percCara = 0;
 if ((WGratio > 0) && (TotTme > 0)) {
  BD = WGratio;
  if (BD < 2)
   BD = 2;
  if (BD > 5.5)
   BD = 5.5;
  if (Temp < 60)
   Temp = 60;
  if (Temp > 72)
   Temp = 72;
 } else {
  BD = 3.5;
  Temp = 67;
  TotTme = 75;
 }
 if (attenuation < 30)
  attenuation = 77;

 // 0.00825 Attenuation factor yeast
 // 0.00817 Attenuation factor water/grain ration
 // -0.00684 Attenuation factor mash temperature
 // 0.00026 Attenuation factor total mash time  (at some places this is 0.0026 this is wrong!)
 // -0.00356 Attenuation factor percentage crystal malt
 // 0.00553 Attenuation factor percentage simple sugars
 // 0.547 Attenuation factor constant
 AttBeer = 0.00825 * attenuation + 0.00817 * BD - 0.00684 * Temp + 0.00026 * TotTme - 0.00356 * percCara + 0.00553 * percSugar + 0.547;
 fg = Round(1 + (1 - AttBeer) * (og - 1), 4);

 //console.log('estimate_fg(' + percSugar + ',' + percCara + ',' + BD + ',' + TotTme + ',' +
 //            Temp + ',' + attenuation + ',' + og + ') AttBeer:' + AttBeer + ' fg:' + fg);
 return fg;
}



function CalcFrac(TpH, pK1, pK2, pK3) {

 var r1d, r2d, r3d, dd, f2d, f3d, f4d;

 r1d = Math.pow(10, TpH - pK1);
 r2d = Math.pow(10, TpH - pK2);
 r3d = Math.pow(10, TpH - pK3);
 dd = 1 / (1 + r1d + r1d * r2d + r1d * r2d * r3d);
 f2d = r1d * dd;
 f3d = r1d * r2d * dd;
 f4d = r1d * r2d * r3d * dd;
 return f2d + 2 * f3d + 3 * f4d;
}



function lintner_to_kolbach(lintner) {
 return (3.5 * lintner) - 16;
}


function kolbach_to_lintner(kolbach) {
 return (kolbach + 16) / 3.5;
}


function kettle_cm(vol, kettle_vol, kettle_height) {
 if ((vol > 0) && (kettle_vol > 0) && (vol <= kettle_vol))
  return Round(100 * ((1 - vol / kettle_vol) * kettle_height), 1);
 return 0;
}


function kettle_vol(cm, kettle_vol, kettle_height) {
 if ((cm >= 0) && (kettle_vol > 0) && (cm <= (kettle_height * 100)))
  return Round(((kettle_height - (cm / 100)) / kettle_height) * kettle_vol, 1);
 return 0;
}

mercurial