1 /***************************************************************************** |
|
2 * Copyright (C) 2018-2022 |
|
3 * |
|
4 * Michiel Broek <mbroek at mbse dot eu> |
|
5 * |
|
6 * This file is part of BMS |
|
7 * |
|
8 * This is free software; you can redistribute it and/or modify it |
|
9 * under the terms of the GNU General Public License as published by the |
|
10 * Free Software Foundation; either version 2, or (at your option) any |
|
11 * later version. |
|
12 * |
|
13 * BrewCloud is distributed in the hope that it will be useful, but |
|
14 * WITHOUT ANY WARRANTY; without even the implied warranty of |
|
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
16 * General Public License for more details. |
|
17 * |
|
18 * You should have received a copy of the GNU General Public License |
|
19 * along with ThermFerm; see the file COPYING. If not, write to the Free |
|
20 * Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
|
21 *****************************************************************************/ |
|
22 |
|
23 var psugar = 0, // Percentage real sugars |
|
24 pcara = 0, // Percentage cara/crystal malts |
|
25 svg = 77, // Default attenuation |
|
26 mashkg = 0, // Malt in mash weight |
|
27 mash_infuse = 0, |
|
28 dataRecord = {}, // Main recipe record |
|
29 hop_flavour = 0, |
|
30 hop_aroma = 0, |
|
31 preboil_sg = 0, |
|
32 last_base = '', |
|
33 last_acid = '', |
|
34 Ka1 = 0.0000004445, |
|
35 Ka2 = 0.0000000000468, |
|
36 error_count = 0, |
|
37 MMCa = 40.048, |
|
38 MMMg = 24.305, |
|
39 MMNa = 22.98976928, |
|
40 MMCl = 35.453, |
|
41 MMSO4 = 96.0626, |
|
42 MMCO3 = 60.01684, |
|
43 MMHCO3 = 61.01684, |
|
44 MMCaSO4 = 172.171, |
|
45 MMCaCl2 = 147.015, |
|
46 MMCaCO3 = 100.087, |
|
47 MMMgCl2 = 95.211, /* Since 27-06-2021 */ |
|
48 MMMgSO4 = 246.475, |
|
49 MMNaHCO3 = 84.007, |
|
50 MMNa2CO3 = 105.996, |
|
51 MMNaCl = 58.443, |
|
52 MMCaOH2 = 74.06268, |
|
53 SpecificHeatWater = 1.0, |
|
54 SpecificHeatMalt = 0.399, //cal/g.°C |
|
55 SlakingHeat = 10.318, //cal/g.°C |
|
56 eq_tun_weight = 2.0, // 2 Kg pot |
|
57 eq_tun_specific_heat = 0.110, // Stainless Steel |
|
58 data_loaded = 0; |
|
59 |
|
60 function hopFlavourContribution(bt, vol, use, amount) { |
|
61 var result; |
|
62 |
|
63 if (use == 4 || use == 5) // Whirlpool or Dry-hop |
|
64 return 0; |
|
65 if (use == 1) { // First wort |
|
66 result = 0.15; // assume 15% flavourcontribution for fwh |
|
67 } else if (bt > 50) { |
|
68 result = 0.10; // assume 10% flavourcontribution as a minimum |
|
69 } else { |
|
70 result = 15.25 / (6 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 21) / 6, 2)); |
|
71 if (result < 0.10) |
|
72 result = 0.10; // assume 10% flavourcontribution as a minimum |
|
73 } |
|
74 return (result * amount * 1000) / vol; |
|
75 } |
|
76 |
|
77 |
|
78 function hopAromaContribution(bt, vol, use, amount) { |
|
79 var result = 0; |
|
80 |
|
81 if (use == 5) { // Dry hop |
|
82 result = 1.33; |
|
83 } else if (use == 4) { // Whirlpool |
|
84 if (bt > 30) |
|
85 bt = 30; // Max 30 minutes |
|
86 result = 0.62 * bt / 30; |
|
87 } else if (bt > 20) { |
|
88 result = 0; |
|
89 } else if (bt > 7.5) { |
|
90 result = 10.03 / (4 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 7.5) / 4, 2)); |
|
91 } else if (use == 2) { // Boil |
|
92 result = 1; |
|
93 } else if (use == 3) { // Aroma |
|
94 result = 1.2; |
|
95 } |
|
96 return (result * amount * 1000) / vol; |
|
97 } |
|
98 |
|
99 |
|
100 function calcFermentables() { |
|
101 console.log('calcFermentables()'); |
|
102 var i, row, rows, org, s = 0, d, x, |
|
103 sug, alc, cw, color, scolor, fig, |
|
104 sugarsf = 0, // fermentable sugars mash + boil |
|
105 sugarsm = 0; // fermentable sugars in mash |
|
106 vol = 0, // Volume sugars after boil |
|
107 addedS = 0, // Added sugars after boil |
|
108 addedmass = 0, // Added mass after boil |
|
109 mvol = 0, // mash volume |
|
110 colort = 0, // Colors srm * vol totals |
|
111 colorh = 0, // Colors ebc * vol * kt |
|
112 colorn = 0, // Colors ebc * pt * pct |
|
113 my_100 = false, |
|
114 mashtime = 0, // Total mash time |
|
115 mashtemp = 0, // Average mash temperature |
|
116 bv = 0.925, // Bierverlies rendement |
|
117 sr = 0.95, // Mash en spoel rendement |
|
118 lintner = 0; // Total recipe lintner |
|
119 /* Init global variables */ |
|
120 psugar = 0; |
|
121 pcara = 0; |
|
122 mashkg = 0; |
|
123 |
|
124 if ((rows = $('#mashGrid').jqxGrid('getrows'))) { |
|
125 for (i = 0; i < rows.length; i++) { |
|
126 row = rows[i]; |
|
127 if (row.step_type == 0) // Infusion |
|
128 mvol += parseFloat(row.step_infuse_amount); |
|
129 if (row.step_temp <= 75) { // Ignore mashout |
|
130 mashtime += row.step_time; |
|
131 mashtemp += row.step_time * row.step_temp; |
|
132 } |
|
133 } |
|
134 mashtemp = mashtemp / mashtime; |
|
135 } |
|
136 |
|
137 if (!(rows = $('#fermentableGrid').jqxGrid('getrows'))) { |
|
138 return; // grid not yet loaded. |
|
139 } |
|
140 |
|
141 for (i = 0; i < rows.length; i++) { |
|
142 row = rows[i]; |
|
143 if (row.f_adjust_to_total_100) |
|
144 my_100 = true; |
|
145 if (row.f_type == 1 && row.f_added < 4) // Sugar |
|
146 psugar += row.f_percentage; |
|
147 if (row.f_graintype == 2 && row.f_added < 4) // Crystal |
|
148 pcara += row.f_percentage; |
|
149 d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100); |
|
150 if (row.f_added == 0) { // Mash |
|
151 if (mvol > 0) { // Only if mash already known |
|
152 mvol += row.f_amount * row.f_moisture / 100; |
|
153 s += d; |
|
154 } |
|
155 d = parseFloat(dataRecord.efficiency) / 100 * d; |
|
156 sugarsm += d; |
|
157 mashkg += row.f_amount; |
|
158 } |
|
159 if (row.f_added == 0 || row.f_added == 1) // Mash or Boil |
|
160 sugarsf += d; |
|
161 if (row.f_added == 2 || row.f_added == 3) { // Fermentation or lagering |
|
162 x = (row.f_yield / 100) * (1 - row.f_moisture / 100); |
|
163 addedS += row.f_amount * x; |
|
164 addedmass += row.f_amount; |
|
165 vol += (x * sugardensity + (1 - x) * 1) * row.f_amount; |
|
166 } |
|
167 if (row.f_added == 0 && (row.f_type == 0 || row.f_type == 4) && row.f_color < 50) { // Mash and Grain/Adjunct and Color < 50 |
|
168 lintner += row.f_diastatic_power * row.f_amount; |
|
169 } |
|
170 if (row.f_added < 4) { |
|
171 colort += row.f_amount * ebc_to_srm(row.f_color); |
|
172 colorh += row.f_amount * row.f_color * get_kt(row.f_color); |
|
173 colorn += (row.f_percentage / 100) * row.f_color; // For 8.6 Pt wort. |
|
174 } |
|
175 } |
|
176 $('#ferm_lintner').val(Math.round(parseFloat(lintner / mashkg))); |
|
177 to_100 = my_100; |
|
178 |
|
179 // Estimate total recipe OG. |
|
180 dataRecord.est_og = estimate_sg(sugarsf + addedS, parseFloat(dataRecord.batch_size)); |
|
181 $('#est_og').val(dataRecord.est_og); |
|
182 $('#est_og2').val(dataRecord.est_og); |
|
183 org = dataRecord.est_og; |
|
184 |
|
185 // Estimate SG in kettle before boil |
|
186 preboil_sg = estimate_sg(sugarsm, parseFloat(dataRecord.boil_size)); |
|
187 |
|
188 // Color of the wort |
|
189 if (dataRecord.color_method == 4) { |
|
190 color = Math.round(((sg_to_plato(dataRecord.est_og) / 8.6) * colorn) + (dataRecord.boil_time / 60)); |
|
191 } else if (dataRecord.color_method == 3) { // Hans Halberstadt |
|
192 color = Math.round((4.46 * bv * sr) / parseFloat(dataRecord.batch_size) * colorh); |
|
193 } else { |
|
194 cw = colort / parseFloat(dataRecord.batch_size) * 8.34436; |
|
195 color = kw_to_ebc(dataRecord.color_method, cw); |
|
196 } |
|
197 dataRecord.est_color = color; |
|
198 $('#est_color').val(color); |
|
199 $('#est_color2').val(color); |
|
200 scolor = ebc_to_color(color); |
|
201 document.getElementById('bcolor').style.background = scolor; |
|
202 document.getElementById('bcolor2').style.background = scolor; |
|
203 |
|
204 // Progress bars |
|
205 pmalts = mashkg / (dataRecord.boil_size / 3) * 100; |
|
206 $('#perc_malts').jqxProgressBar('val', pmalts); |
|
207 $('#perc_sugars').jqxProgressBar('val', psugar); |
|
208 $('#perc_cara').jqxProgressBar('val', pcara); |
|
209 |
|
210 // Calculate estimated svg. |
|
211 svg = 0; // default. |
|
212 rows = $('#yeastGrid').jqxGrid('getrows'); |
|
213 for (i = 0; i < rows.length; i++) { |
|
214 row = rows[i]; |
|
215 if (row.y_use == 0) { // Primary |
|
216 if (parseFloat(row.y_attenuation) > svg) |
|
217 svg = parseFloat(row.y_attenuation); // Take the highest if multiple yeasts. |
|
218 } |
|
219 // TODO: brett in secondary ?? |
|
220 } |
|
221 if (svg == 0) |
|
222 svg = 77; |
|
223 |
|
224 if ((mashkg > 0) && (mash_infuse > 0) && (mashtime > 0) && (mashtemp > 0)) { |
|
225 dataRecord.est_fg = estimate_fg(psugar, pcara, mash_infuse / mashkg, mashtime, mashtemp, svg, dataRecord.est_og); |
|
226 } else { |
|
227 dataRecord.est_fg = estimate_fg(psugar, pcara, 0, 0, 0, svg, dataRecord.est_og); |
|
228 } |
|
229 $('#est_fg').val(dataRecord.est_fg); |
|
230 $('#est_fg2').val(dataRecord.est_fg); |
|
231 fig = dataRecord.est_fg; |
|
232 |
|
233 dataRecord.est_abv = abvol(dataRecord.est_og, dataRecord.est_fg); |
|
234 $('#est_abv').val(dataRecord.est_abv); |
|
235 $('#est_abv2').val(dataRecord.est_abv); |
|
236 |
|
237 // Calculate the calories in kcal/l (from brouwhulp) |
|
238 alc = 1881.22 * fig * (org - fig) / (1.775 - org); |
|
239 sug = 3550 * fig * (0.1808 * org + 0.8192 * fig - 1.0004); |
|
240 $('#kcal').val(Math.round((alc + sug) / (12 * 0.0295735296))); |
|
241 } |
|
242 |
|
243 |
|
244 function infusionVol(step_infused, step_mashkg, infuse_temp, step_temp, last_temp) { |
|
245 var a = last_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); |
|
246 var b = step_temp * (eq_tun_weight * eq_tun_specific_heat + step_infused * SpecificHeatWater + step_mashkg * SpecificHeatMalt); |
|
247 var vol = Round(((b - a) / ((infuse_temp - step_temp) * SpecificHeatWater)), 2); |
|
248 console.log('infusionVol(' + step_infused + ', ' + step_mashkg + ', ' + infuse_temp + ', ' + step_temp + ', ' + last_temp + '): ' + vol); |
|
249 return vol; |
|
250 } |
|
251 |
|
252 |
|
253 function decoctionVol(step_volume, step_temp, prev_temp) { |
|
254 var a = (eq_tun_weight * eq_tun_specific_heat + step_volume * SpecificHeatWater) * (step_temp - prev_temp); |
|
255 var b = SpecificHeatWater * (99 - step_temp); |
|
256 var vol = 0; |
|
257 if (b > 0) |
|
258 vol = Round(a / b, 6); |
|
259 console.log('decoctionVol(' + step_volume + ', ' + step_temp + ', ' + prev_temp + '): ' + vol); |
|
260 return vol; |
|
261 } |
|
262 |
|
263 |
|
264 function calcMash() { |
|
265 var infused = 0, vol, i, j, n, a, b, row, rows, temp; |
|
266 var lasttemp = 18.0; |
|
267 var graintemp = 18.0; |
|
268 var tuntemp = 18.0; |
|
269 |
|
270 if ((rows = $('#mashGrid').jqxGrid('getrows')) && (mashkg > 0)) { |
|
271 console.log('calcMash()'); |
|
272 for (i = 0; i < rows.length; i++) { |
|
273 row = $('#mashGrid').jqxGrid('getrowdata', i); |
|
274 if (row.step_type == 0) { // Infusion |
|
275 if (i == 0) { |
|
276 // First mash step, temperature from the mashtun and malt. |
|
277 n = 20; // tun is preheated. |
|
278 tuntemp = row.step_temp; |
|
279 for (j = 0; j < n; j++) { |
|
280 a = mashkg * graintemp * SpecificHeatMalt + eq_tun_weight * tuntemp * eq_tun_specific_heat; |
|
281 b = row.step_temp * (eq_tun_weight * eq_tun_specific_heat + row.step_infuse_amount * SpecificHeatWater + mashkg * SpecificHeatMalt) - SlakingHeat * mashkg; |
|
282 if (row.step_infuse_amount > 0) { |
|
283 temp = (b - a) / (row.step_infuse_amount * SpecificHeatWater); |
|
284 } else { |
|
285 temp = 99; |
|
286 } |
|
287 tuntemp += (temp - tuntemp) / 2; |
|
288 row.step_infuse_temp = Round(temp, 6); |
|
289 } |
|
290 console.log('init infuse temp: ' + row.step_infuse_temp); |
|
291 } else { |
|
292 // Calculate amount of infusion water. |
|
293 row.step_infuse_amount = infusionVol(infused, mashkg, row.step_infuse_temp, row.step_temp, lasttemp); |
|
294 //console.log('vol: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp); |
|
295 } |
|
296 infused += row.step_infuse_amount; |
|
297 } else if (row.step_type == 1) { // Temperature |
|
298 if (i > 0) |
|
299 row.step_infuse_amount = 0; |
|
300 row.step_infuse_temp = 0; |
|
301 } else if (row.step_type == 2) { // Decoction |
|
302 row.step_infuse_amount = decoctionVol(infused, row.step_temp, lasttemp); |
|
303 row.step_infuse_temp = 99; |
|
304 } |
|
305 row.step_volume = infused; |
|
306 //console.log(i + ' type: ' + row.step_type + ' volume: ' + row.step_infuse_amount + ' temp: ' + row.step_infuse_temp); |
|
307 lasttemp = row.step_temp; |
|
308 mashtime += row.step_time + row.ramp_time; |
|
309 row.step_wg_ratio = Round(infused / mashkg, 6); |
|
310 $('#mashGrid').jqxGrid('updaterow', i, row); |
|
311 } |
|
312 } |
|
313 } |
|
314 |
|
315 |
|
316 function GetBUGU() { |
|
317 var gu = (dataRecord.est_og - 1) * 1000; |
|
318 if (gu > 0) |
|
319 return dataRecord.est_ibu / gu; |
|
320 else |
|
321 return 0.5; |
|
322 } |
|
323 |
|
324 |
|
325 function GetOptSO4Clratio() { |
|
326 var BUGU = GetBUGU(); |
|
327 return (-1.2 * BUGU + 1.4); |
|
328 } |
|
329 |
|
330 |
|
331 |
|
332 function setRangeIndicator(ion, rangeCode) { |
|
333 if ((rangeCode == 'laag') || (rangeCode == 'hoog')) |
|
334 $('#wr_' + ion).html('<img src="images/dialog-error.png"><span style="vertical-align: top; font-size: 10px; font-style: italic;">' + rangeCode + '</span>'); |
|
335 else |
|
336 $('#wr_' + ion).html('<img src="images/dialog-ok-apply.png">'); |
|
337 } |
|
338 |
|
339 |
|
340 function mix(v1, v2, c1, c2) { |
|
341 if ((v1 + v2) > 0) { |
|
342 return ((v1 * c1) + (v2 * c2)) / (v1 + v2); |
|
343 } |
|
344 return 0; |
|
345 } |
|
346 |
|
347 |
|
348 // mg/l as CaCO3 |
|
349 function ResidualAlkalinity(total_alkalinity, calcium, magnesium) { |
|
350 return total_alkalinity - (calcium / 1.4 + magnesium / 1.7); |
|
351 } |
|
352 |
|
353 |
|
354 function PartCO3(pH) { |
|
355 var H = Math.pow(10, -pH); |
|
356 return 100 * Ka1 * Ka2 / (H * H + H * Ka1 + Ka1 * Ka2); |
|
357 } |
|
358 |
|
359 |
|
360 function PartHCO3(pH) { |
|
361 var H = Math.pow(10, -pH); |
|
362 return 100 * Ka1 * H / (H * H + H * Ka1 + Ka1 * Ka2); |
|
363 } |
|
364 |
|
365 |
|
366 function Charge(pH) { |
|
367 return (-2 * PartCO3(pH) - PartHCO3(pH)); |
|
368 } |
|
369 |
|
370 |
|
371 //Z alkalinity is the amount of acid (in mEq/l) needed to bring water to the target pH (Z pH) |
|
372 function ZAlkalinity(pHZ) { |
|
373 var C43 = Charge(4.3), |
|
374 Cw = Charge(parseFloat($('#wg_ph').jqxNumberInput('decimal'))), |
|
375 Cz = Charge(pHZ), |
|
376 DeltaCNaught = -C43 + Cw, |
|
377 CT = parseFloat($('#wg_total_alkalinity').jqxNumberInput('decimal')) / 50 / DeltaCNaught, |
|
378 DeltaCZ = -Cz + Cw; |
|
379 return CT * DeltaCZ; |
|
380 } |
|
381 |
|
382 |
|
383 //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) |
|
384 function ZRA(pHZ) { |
|
385 var Calc = parseFloat($('#wg_calcium').jqxNumberInput('decimal')) / (MMCa / 2), |
|
386 Magn = parseFloat($('#wg_magnesium').jqxNumberInput('decimal')) / (MMMg / 2), |
|
387 Z = ZAlkalinity(pHZ); |
|
388 return Z - (Calc / 3.5 + Magn / 7); |
|
389 } |
|
390 |
|
391 |
|
392 function BufferCapacity(di_ph, acid_to_ph_57, ebc, graintype) { |
|
393 C1 = 0; |
|
394 if ((di_ph != 5.7) && ((acid_to_ph_57 < - 0.1) || (acid_to_ph_57 > 0.1))) { |
|
395 C1 = acid_to_ph_57 / (di_ph - 5.7); |
|
396 } else { |
|
397 // If the acid_to_ph_5.7 is unknown from the maltster, guess the required acid. |
|
398 switch (graintype) { |
|
399 case 0: // Base, Special, Kilned |
|
400 case 3: |
|
401 case 5: C1 = 0.014 * ebc - 34.192; |
|
402 break; |
|
403 case 2: C1 = -0.0597 * ebc - 32.457; // Crystal |
|
404 break; |
|
405 case 1: C1 = 0.0107 * ebc - 54.768; // Roast |
|
406 break; |
|
407 case 4: C1 = -149; // Sour malt |
|
408 break; |
|
409 } |
|
410 } |
|
411 return C1; |
|
412 } |
|
413 |
|
414 |
|
415 function ProtonDeficit(pHZ) { |
|
416 var C1, i, rows, row, Result = ZRA(pHZ) * parseFloat($('#wg_amount').jqxNumberInput('decimal')); |
|
417 // proton deficit for the grist |
|
418 if ((rows = $('#fermentableGrid').jqxGrid('getrows'))) { |
|
419 for (i = 0; i < rows.length; i++) { |
|
420 row = rows[i]; |
|
421 if (row.f_added == 0 && row.f_graintype != 6) { // Added == Mash && graintype != No Malt |
|
422 C1 = BufferCapacity(row.f_di_ph, row.f_acid_to_ph_57, row.f_color, row.f_graintype); |
|
423 x = C1 * (pHZ - row.f_di_ph); // AcidRequired(ZpH) |
|
424 Result += x * row.f_amount; |
|
425 } |
|
426 } |
|
427 } else { |
|
428 error_count++; |
|
429 if (error_count < 5) |
|
430 console.log('ProtonDeficit(' + pHZ + ') invalid grist, return ' + Result); |
|
431 } |
|
432 return Result; |
|
433 } |
|
434 |
|
435 |
|
436 function MashpH() { |
|
437 var n = 0, |
|
438 pH = 5.4, |
|
439 deltapH = 0.001, |
|
440 deltapd = 0.1, |
|
441 pd = ProtonDeficit(pH); |
|
442 while (((pd < -deltapd) || (pd > deltapd)) && (n < 2000)) { |
|
443 n++; |
|
444 if (pd < -deltapd) |
|
445 pH -= deltapH; |
|
446 else if (pd > deltapd) |
|
447 pH += deltapH; |
|
448 pd = ProtonDeficit(pH); |
|
449 } |
|
450 pH = Round(pH, 6); |
|
451 //console.log('MashpH() n: ' + n + ' pH: ' + pH); |
|
452 return pH; |
|
453 } |
|
454 |
|
455 |
|
456 |
|
457 |
|
458 $(document).ready(function() { |
|
459 |
|
460 var to_100 = false, // Fermentables adjust to 100% |
|
461 |
|
462 fermentableRow = 0, |
|
463 fermentableData = {}, |
|
464 hopRow = 0, |
|
465 hopData = {}, |
|
466 miscRow = 0, |
|
467 miscData = {}, |
|
468 yeastRow = 0, |
|
469 yeastData = {}, |
|
470 mashRow = 0, |
|
471 mashData = {}, |
|
472 |
|
473 url = 'includes/db_recipes.php', |
|
474 // prepare the data |
|
475 source = { |
|
476 datatype: 'json', |
|
477 cache: false, |
|
478 datafields: [ |
|
479 { name: 'record', type: 'number' }, |
|
480 { name: 'uuid', type: 'string' }, |
|
481 { name: 'locked', type: 'int' }, |
|
482 { name: 'st_name', type: 'string' }, |
|
483 { name: 'st_letter', type: 'string' }, |
|
484 { name: 'st_guide', type: 'string' }, |
|
485 { name: 'st_type', type: 'int' }, |
|
486 { name: 'st_category', type: 'string' }, |
|
487 { name: 'st_category_number', type: 'int' }, |
|
488 { name: 'st_og_min', type: 'float' }, |
|
489 { name: 'st_og_max', type: 'float' }, |
|
490 { name: 'st_fg_min', type: 'float' }, |
|
491 { name: 'st_fg_max', type: 'float' }, |
|
492 { name: 'st_ibu_min', type: 'float' }, |
|
493 { name: 'st_ibu_max', type: 'float' }, |
|
494 { name: 'st_color_min', type: 'float' }, |
|
495 { name: 'st_color_max', type: 'float' }, |
|
496 { name: 'st_carb_min', type: 'float' }, |
|
497 { name: 'st_carb_max', type: 'float' }, |
|
498 { name: 'st_abv_min', type: 'float' }, |
|
499 { name: 'st_abv_max', type: 'float' }, |
|
500 { name: 'name', type: 'string' }, |
|
501 { name: 'notes', type: 'string' }, |
|
502 { name: 'type', type: 'int' }, |
|
503 { name: 'batch_size', type: 'float' }, |
|
504 { name: 'boil_size', type: 'float' }, |
|
505 { name: 'boil_time', type: 'float' }, |
|
506 { name: 'efficiency', type: 'float' }, |
|
507 { name: 'est_og', type: 'float' }, |
|
508 { name: 'est_fg', type: 'float' }, |
|
509 { name: 'est_abv', type: 'float' }, |
|
510 { name: 'est_color', type: 'float' }, |
|
511 { name: 'color_method', type: 'int' }, |
|
512 { name: 'est_ibu', type: 'float' }, |
|
513 { name: 'ibu_method', type: 'int' }, |
|
514 { name: 'est_carb', type: 'float' }, |
|
515 { name: 'sparge_temp', type: 'float' }, |
|
516 { name: 'sparge_ph', type: 'float' }, |
|
517 { name: 'sparge_volume', type: 'float' }, |
|
518 { name: 'sparge_source', type: 'int' }, |
|
519 { name: 'sparge_acid_type', type: 'int' }, |
|
520 { name: 'sparge_acid_perc', type: 'float' }, |
|
521 { name: 'sparge_acid_amount', type: 'float' }, |
|
522 { name: 'mash_ph', type: 'float' }, |
|
523 { name: 'mash_name', type: 'string' }, |
|
524 { name: 'calc_acid', type: 'int' }, |
|
525 { name: 'w1_name', type: 'string' }, |
|
526 { name: 'w1_amount', type: 'float' }, |
|
527 { name: 'w1_calcium', type: 'float' }, |
|
528 { name: 'w1_sulfate', type: 'float' }, |
|
529 { name: 'w1_chloride', type: 'float' }, |
|
530 { name: 'w1_sodium', type: 'float' }, |
|
531 { name: 'w1_magnesium', type: 'float' }, |
|
532 { name: 'w1_total_alkalinity', type: 'float' }, |
|
533 { name: 'w1_ph', type: 'float' }, |
|
534 { name: 'w1_cost', type: 'float' }, |
|
535 { name: 'w2_name', type: 'string' }, |
|
536 { name: 'w2_amount', type: 'float' }, |
|
537 { name: 'w2_calcium', type: 'float' }, |
|
538 { name: 'w2_sulfate', type: 'float' }, |
|
539 { name: 'w2_chloride', type: 'float' }, |
|
540 { name: 'w2_sodium', type: 'float' }, |
|
541 { name: 'w2_magnesium', type: 'float' }, |
|
542 { name: 'w2_total_alkalinity', type: 'float' }, |
|
543 { name: 'w2_ph', type: 'float' }, |
|
544 { name: 'w2_cost', type: 'float' }, |
|
545 { name: 'wg_amount', type: 'float' }, |
|
546 { name: 'wg_calcium', type: 'float' }, |
|
547 { name: 'wg_sulfate', type: 'float' }, |
|
548 { name: 'wg_chloride', type: 'float' }, |
|
549 { name: 'wg_sodium', type: 'float' }, |
|
550 { name: 'wg_magnesium', type: 'float' }, |
|
551 { name: 'wg_total_alkalinity', type: 'float' }, |
|
552 { name: 'wg_ph', type: 'float' }, |
|
553 { name: 'wb_calcium', type: 'float' }, |
|
554 { name: 'wb_sulfate', type: 'float' }, |
|
555 { name: 'wb_chloride', type: 'float' }, |
|
556 { name: 'wb_sodium', type: 'float' }, |
|
557 { name: 'wb_magnesium', type: 'float' }, |
|
558 { name: 'wb_total_alkalinity', type: 'float' }, |
|
559 { name: 'wb_ph', type: 'float' }, |
|
560 { name: 'wa_acid_name', type: 'int' }, |
|
561 { name: 'wa_acid_perc', type: 'int' }, |
|
562 { name: 'wa_base_name', type: 'int' }, |
|
563 { name: 'fermentables', type: 'string' }, |
|
564 { name: 'hops', type: 'string' }, |
|
565 { name: 'miscs', type: 'string' }, |
|
566 { name: 'yeasts', type: 'string' }, |
|
567 { name: 'mashs', type: 'string' } |
|
568 ], |
|
569 id: 'record', |
|
570 url: url + '?record=' + my_record |
|
571 }, |
|
572 // Load data and select one record. |
|
573 dataAdapter = new $.jqx.dataAdapter(source, { |
|
574 loadComplete: function() { |
|
575 var records = dataAdapter.records; |
|
576 dataRecord = records[0]; |
|
577 // Hidden record uuid |
|
578 $('#name').val(dataRecord.name); |
|
579 $('#notes').val(dataRecord.notes); |
|
580 // Hidden record locked |
|
581 $('#st_name').val(dataRecord.st_name); |
|
582 $('#st_letter').val(dataRecord.st_letter); |
|
583 $('#st_guide').val(dataRecord.st_guide); |
|
584 $('#st_category').val(dataRecord.st_category); |
|
585 $('#st_category_number').val(dataRecord.st_category_number); |
|
586 $('#st_type').val(StyleTypeData[dataRecord.st_type].nl); |
|
587 $('#type').val(RecipeTypeData[dataRecord.type].nl); |
|
588 $('#batch_size').val(dataRecord.batch_size); |
|
589 $('#boil_size').val(dataRecord.boil_size); |
|
590 $('#boil_time').val(dataRecord.boil_time); |
|
591 $('#efficiency').val(dataRecord.efficiency); |
|
592 $('#est_og').val(dataRecord.est_og); |
|
593 $('#est_og2').val(dataRecord.est_og); |
|
594 $('#st_og_min').val(dataRecord.st_og_min); |
|
595 $('#st_og_max').val(dataRecord.st_og_max); |
|
596 $('#est_fg').val(dataRecord.est_fg); |
|
597 $('#est_fg2').val(dataRecord.est_fg); |
|
598 $('#st_fg_min').val(dataRecord.st_fg_min); |
|
599 $('#st_fg_max').val(dataRecord.st_fg_max); |
|
600 $('#est_fg').val(dataRecord.est_fg); |
|
601 $('#est_fg2').val(dataRecord.est_fg); |
|
602 $('#st_fg_min').val(dataRecord.st_fg_min); |
|
603 $('#st_fg_max').val(dataRecord.st_fg_max); |
|
604 $('#est_color').val(dataRecord.est_color); |
|
605 $('#est_color2').val(dataRecord.est_color); |
|
606 $('#est_abv').val(dataRecord.est_abv); |
|
607 $('#est_abv2').val(dataRecord.est_abv); |
|
608 $('#st_abv_min').val(dataRecord.st_abv_min); |
|
609 $('#st_abv_max').val(dataRecord.st_abv_max); |
|
610 $('#st_color_min').val(dataRecord.st_color_min); |
|
611 $('#st_color_max').val(dataRecord.st_color_max); |
|
612 $('#color_method').val(ColorMethodData[dataRecord.color_method].nl); |
|
613 $('#est_ibu').val(dataRecord.est_ibu); |
|
614 $('#est_ibu2').val(dataRecord.est_ibu); |
|
615 $('#st_ibu_min').val(dataRecord.st_ibu_min); |
|
616 $('#st_ibu_max').val(dataRecord.st_ibu_max); |
|
617 $('#ibu_method').val(IBUmethodData[dataRecord.ibu_method].nl); |
|
618 $('#est_carb').val(dataRecord.est_carb); |
|
619 $('#st_carb_min').val(dataRecord.st_carb_min); |
|
620 $('#st_carb_max').val(dataRecord.st_carb_max); |
|
621 $('#mash_name').val(dataRecord.mash_name); |
|
622 $('#mash_ph').val(dataRecord.mash_ph); |
|
623 // Hidden record sparge_temp |
|
624 $('#sparge_ph').val(dataRecord.sparge_ph); |
|
625 $('#sw_amount').val(dataRecord.sparge_volume); |
|
626 // Hidden record sparge_source |
|
627 $('#sparge_acid_type').val(AcidTypeData[dataRecord.sparge_acid_type].nl); |
|
628 $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc); |
|
629 $('#sparge_acid_amount').val(dataRecord.sparge_acid_amount * 1000); |
|
630 $('#calc_acid').val(dataRecord.calc_acid); |
|
631 $('#w1_name').val(dataRecord.w1_name); |
|
632 $('#w1_amount').val(dataRecord.w1_amount); |
|
633 $('#w1_calcium').val(dataRecord.w1_calcium); |
|
634 $('#w1_sulfate').val(dataRecord.w1_sulfate); |
|
635 $('#w1_chloride').val(dataRecord.w1_chloride); |
|
636 $('#w1_sodium').val(dataRecord.w1_sodium); |
|
637 $('#w1_magnesium').val(dataRecord.w1_magnesium); |
|
638 $('#w1_total_alkalinity').val(dataRecord.w1_total_alkalinity); |
|
639 $('#w1_ph').val(dataRecord.w1_ph); |
|
640 $('#w1_bicarbonate').val(Bicarbonate(dataRecord.w1_total_alkalinity, dataRecord.w1_ph)); |
|
641 $('#w1_cost').val(dataRecord.w1_cost); |
|
642 $('#w2_name').val(dataRecord.w2_name); |
|
643 $('#w2_amount').val(dataRecord.w2_amount); |
|
644 $('#w2_calcium').val(dataRecord.w2_calcium); |
|
645 $('#w2_sulfate').val(dataRecord.w2_sulfate); |
|
646 $('#w2_chloride').val(dataRecord.w2_chloride); |
|
647 $('#w2_sodium').val(dataRecord.w2_sodium); |
|
648 $('#w2_magnesium').val(dataRecord.w2_magnesium); |
|
649 $('#w2_total_alkalinity').val(dataRecord.w2_total_alkalinity); |
|
650 $('#w2_ph').val(dataRecord.w2_ph); |
|
651 $('#w2_bicarbonate').val(Bicarbonate(dataRecord.w2_total_alkalinity, dataRecord.w2_ph)); |
|
652 $('#w2_cost').val(dataRecord.w2_cost); |
|
653 $('#wg_amount').val(dataRecord.wg_amount); |
|
654 $('#wg_calcium').val(dataRecord.wg_calcium); |
|
655 $('#wg_sulfate').val(dataRecord.wg_sulfate); |
|
656 $('#wg_chloride').val(dataRecord.wg_chloride); |
|
657 $('#wg_sodium').val(dataRecord.wg_sodium); |
|
658 $('#wg_magnesium').val(dataRecord.wg_magnesium); |
|
659 $('#wg_total_alkalinity').val(dataRecord.wg_total_alkalinity); |
|
660 $('#wg_ph').val(dataRecord.wg_ph); |
|
661 $('#wb_calcium').val(dataRecord.wb_calcium); |
|
662 $('#wb_sulfate').val(dataRecord.wb_sulfate); |
|
663 $('#wb_chloride').val(dataRecord.wb_chloride); |
|
664 $('#wb_sodium').val(dataRecord.wb_sodium); |
|
665 $('#wb_magnesium').val(dataRecord.wb_magnesium); |
|
666 $('#wb_total_alkalinity').val(dataRecord.wb_total_alkalinity); |
|
667 $('#wb_ph').val(dataRecord.wb_ph); |
|
668 $('#wa_acid_name').val(AcidTypeData[dataRecord.wa_acid_name].nl); |
|
669 $('#wa_acid_perc').val(dataRecord.wa_acid_perc); |
|
670 editFermentable(dataRecord); |
|
671 editHop(dataRecord); |
|
672 editMisc(dataRecord); |
|
673 editYeast(dataRecord); |
|
674 editMash(dataRecord); |
|
675 $('#jqxTabs').jqxTabs('next'); |
|
676 data_loaded = 1; |
|
677 }, |
|
678 loadError: function(jqXHR, status, error) {}, |
|
679 beforeLoadComplete: function(records) { $('#jqxLoader').jqxLoader('open'); } |
|
680 }), |
|
681 |
|
682 // Inline fermentables editor |
|
683 editFermentable = function(data) { |
|
684 var fermentableSource = { |
|
685 localdata: data.fermentables, |
|
686 datafields: [ |
|
687 { name: 'f_name', type: 'string' }, |
|
688 { name: 'f_origin', type: 'string' }, |
|
689 { name: 'f_supplier', type: 'string' }, |
|
690 { name: 'f_amount', type: 'float' }, |
|
691 { name: 'f_cost', type: 'float' }, |
|
692 { name: 'f_type', type: 'int' }, |
|
693 { name: 'f_yield', type: 'float' }, |
|
694 { name: 'f_color', type: 'float' }, |
|
695 { name: 'f_coarse_fine_diff', type: 'float' }, |
|
696 { name: 'f_moisture', type: 'float' }, |
|
697 { name: 'f_diastatic_power', type: 'float' }, |
|
698 { name: 'f_protein', type: 'float' }, |
|
699 { name: 'f_max_in_batch', type: 'float' }, |
|
700 { name: 'f_graintype', type: 'int' }, |
|
701 { name: 'f_added', type: 'int' }, |
|
702 { name: 'f_dissolved_protein', type: 'float' }, |
|
703 { name: 'f_recommend_mash', type: 'int' }, |
|
704 { name: 'f_add_after_boil', type: 'int' }, |
|
705 { name: 'f_adjust_to_total_100', type: 'int' }, |
|
706 { name: 'f_percentage', type: 'float' }, |
|
707 { name: 'f_di_ph', type: 'float' }, |
|
708 { name: 'f_acid_to_ph_57', type: 'float' }, |
|
709 { name: 'f_inventory', type: 'float' }, |
|
710 { name: 'f_avail', type: 'int' } |
|
711 ], |
|
712 }, |
|
713 fermentableAdapter = new $.jqx.dataAdapter(fermentableSource); |
|
714 |
|
715 $('#fermentableGrid').jqxGrid({ |
|
716 width: 1240, |
|
717 height: 470, |
|
718 source: fermentableAdapter, |
|
719 theme: theme, |
|
720 editable: false, |
|
721 ready: function() { |
|
722 calcFermentables(); |
|
723 $('#jqxTabs').jqxTabs('next'); |
|
724 }, |
|
725 columns: [ |
|
726 { text: 'Vergistbaar ingrediënt', datafield: 'f_name' }, |
|
727 { text: 'Leverancier', datafield: 'f_supplier', width: 180 }, |
|
728 { text: 'Kleur', datafield: 'f_color', width: 90, align: 'right', |
|
729 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
730 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f0') + ' EBC</span>'; |
|
731 } |
|
732 }, |
|
733 { text: 'Type', width: 100, datafield: 'f_type', |
|
734 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
735 return '<span style="margin: 3px; margin-top: 6px; float: left;">' + FermentableTypeData[value].nl + '</span>'; |
|
736 } |
|
737 }, |
|
738 { text: 'Moment', width: 110, datafield: 'f_added', |
|
739 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
740 return '<span style="margin: 3px; margin-top: 6px; float: left;">' + AddedData[value].nl + '</span>'; |
|
741 } |
|
742 }, |
|
743 { text: 'Maxinbatch', datafield: 'f_max_in_batch', hidden: true }, |
|
744 { text: 'Opbrengst', editable: false, datafield: 'f_yield', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'p1' }, |
|
745 { text: 'Gewicht Kg', datafield: 'f_amount', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f3' }, |
|
746 { text: 'Procent', datafield: 'f_percentage', width: 90, align: 'right', |
|
747 cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { |
|
748 var color = '#ffffff'; |
|
749 if (value > rowdata.f_max_in_batch) |
|
750 color = '#ff4040'; |
|
751 return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + |
|
752 color + ';">' + fermentableAdapter.formatNumber(value, 'p1') + '</span>'; |
|
753 } |
|
754 }, |
|
755 { text: '100%', align: 'center', datafield: 'f_adjust_to_total_100', columntype: 'checkbox', width: 70 } |
|
756 ] |
|
757 }); |
|
758 }; |
|
759 |
|
760 // Inline hops editor |
|
761 var editHop = function(data) { |
|
762 var hopSource = { |
|
763 localdata: data.hops, |
|
764 datafields: [ |
|
765 { name: 'h_name', type: 'string' }, |
|
766 { name: 'h_origin', type: 'string' }, |
|
767 { name: 'h_amount', type: 'float' }, |
|
768 { name: 'h_cost', type: 'float' }, |
|
769 { name: 'h_type', type: 'int' }, |
|
770 { name: 'h_form', type: 'int' }, |
|
771 { name: 'h_useat', type: 'int' }, |
|
772 { name: 'h_time', type: 'float' }, |
|
773 { name: 'h_alpha', type: 'float' }, |
|
774 { name: 'h_beta', type: 'float' }, |
|
775 { name: 'h_hsi', type: 'float' }, |
|
776 { name: 'h_humulene', type: 'float' }, |
|
777 { name: 'h_caryophyllene', type: 'float' }, |
|
778 { name: 'h_cohumulone', type: 'float' }, |
|
779 { name: 'h_myrcene', type: 'float' }, |
|
780 { name: 'h_total_oil', type: 'float' }, |
|
781 { name: 'h_inventory', type: 'float' }, |
|
782 { name: 'h_avail', type: 'int' } |
|
783 ], |
|
784 }, |
|
785 hopAdapter = new $.jqx.dataAdapter(hopSource); |
|
786 |
|
787 $('#hopGrid').jqxGrid({ |
|
788 width: 1240, |
|
789 height: 560, |
|
790 source: hopAdapter, |
|
791 theme: theme, |
|
792 editable: false, |
|
793 ready: function() { $('#jqxTabs').jqxTabs('next'); }, |
|
794 columns: [ |
|
795 { text: 'Hop', datafield: 'h_name' }, |
|
796 { text: 'Origin', width: 180, datafield: 'h_origin' }, |
|
797 { text: 'Type', width: 90, datafield: 'h_type', |
|
798 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
799 return '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopTypeData[value].nl + '</span>'; |
|
800 } |
|
801 }, |
|
802 { text: 'Vorm', width: 110, datafield: 'h_form', |
|
803 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
804 return '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopFormData[value].nl + '</span>'; |
|
805 } |
|
806 }, |
|
807 { text: 'Alpha', datafield: 'h_alpha', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'p1' }, |
|
808 { text: 'Gebruik', width: 110, datafield: 'h_useat', |
|
809 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
810 return '<span style="margin: 4px; margin-top: 6px; float: left;">' + HopUseData[value].nl + '</span>'; |
|
811 } |
|
812 }, |
|
813 { text: 'Tijdsduur', datafield: 'h_time', width: 90, align: 'right', |
|
814 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
815 var duration = ''; |
|
816 if ((rowdata.h_useat == 2) || (rowdata.h_useat == 4)) // Boil, Whirlpool |
|
817 duration = dataAdapter.formatNumber(value, 'f0') + ' min.'; |
|
818 else if (rowdata.h_useat == 5) // Dry hop |
|
819 duration = dataAdapter.formatNumber(value / 1440, 'f0') + ' dagen'; |
|
820 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + duration + '</span>'; |
|
821 } |
|
822 }, |
|
823 { text: 'IBU', datafield: 'ibu', width: 80, align: 'right', |
|
824 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
825 var ibu = toIBU(rowdata.h_useat, rowdata.h_form, preboil_sg, parseFloat(dataRecord.batch_size), |
|
826 parseFloat(rowdata.h_amount), parseFloat(rowdata.h_time), |
|
827 parseFloat(rowdata.h_alpha), dataRecord.ibu_method, 0, parseFloat(rowdata.h_time), 0); |
|
828 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(ibu, 'f1') + '</span>'; |
|
829 } |
|
830 }, |
|
831 { text: 'Gewicht', datafield: 'h_amount', width: 110, align: 'right', |
|
832 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
833 var amount = dataAdapter.formatNumber(value, 'f1') + ' kg'; |
|
834 if (value < 1) |
|
835 amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr'; |
|
836 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + amount + '</span>'; |
|
837 } |
|
838 } |
|
839 ] |
|
840 }); |
|
841 }; |
|
842 |
|
843 // Inline miscs editor |
|
844 var editMisc = function(data) { |
|
845 var miscSource = { |
|
846 localdata: data.miscs, |
|
847 datafields: [ |
|
848 { name: 'm_name', type: 'string' }, |
|
849 { name: 'm_amount', type: 'float' }, |
|
850 { name: 'm_cost', type: 'float' }, |
|
851 { name: 'm_type', type: 'int' }, |
|
852 { name: 'm_use_use', type: 'int' }, |
|
853 { name: 'm_time', type: 'float' }, |
|
854 { name: 'm_amount_is_weight', type: 'int' }, |
|
855 { name: 'm_inventory', type: 'float' }, |
|
856 { name: 'm_avail', type: 'int' } |
|
857 ], |
|
858 }, |
|
859 miscAdapter = new $.jqx.dataAdapter(miscSource, { |
|
860 beforeLoadComplete: function(records) { |
|
861 var i, row, data = new Array(); |
|
862 for (i = 0; i < records.length; i++) { |
|
863 row = records[i]; |
|
864 data.push(row); |
|
865 // Initial set water agent values. |
|
866 if (row.m_use_use == 1) { // Mash |
|
867 switch (row.m_name) { |
|
868 case 'CaCl2': |
|
869 $('#wa_cacl2').val(row.m_amount * 1000); |
|
870 break; |
|
871 case 'CaSO4': |
|
872 $('#wa_caso4').val(row.m_amount * 1000); |
|
873 break; |
|
874 case 'MgSO4': |
|
875 $('#wa_mgso4').val(row.m_amount * 1000); |
|
876 break; |
|
877 case 'NaCl': |
|
878 $('#wa_nacl').val(row.m_amount * 1000); |
|
879 break; |
|
880 case 'MgCl2': |
|
881 $('#wa_mgcl2').val(row.m_amount * 1000); |
|
882 break; |
|
883 case 'NaHCO3': |
|
884 $('#wa_nahco3').val(row.m_amount * 1000); |
|
885 break; |
|
886 case 'CaCO3': |
|
887 $('#wa_caco3').val(row.m_amount * 1000); |
|
888 break; |
|
889 case 'Melkzuur': |
|
890 $('#wa_acid_name').val(AcidTypeData[0].nl); |
|
891 $('#wa_acid').val(row.m_amount * 1000); |
|
892 $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); |
|
893 last_acid = 'Melkzuur'; |
|
894 break; |
|
895 case 'Zoutzuur': |
|
896 $('#wa_acid_name').val(AcidTypeData[1].nl); |
|
897 $('#wa_acid').val(row.m_amount * 1000); |
|
898 $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc); |
|
899 last_acid = 'Zoutzuur'; |
|
900 break; |
|
901 case 'Fosforzuur': |
|
902 $('#wa_acid_name').val(AcidTypeData[2].nl); |
|
903 $('#wa_acid').val(row.m_amount * 1000); |
|
904 $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc); |
|
905 last_acid = 'Fosforzuur'; |
|
906 break; |
|
907 case 'Zwavelzuur': |
|
908 $('#wa_acid_name').val(AcidTypeData[3].nl); |
|
909 $('#wa_acid').val(row.m_amount * 1000); |
|
910 $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc); |
|
911 last_acid = 'Zwavelzuur'; |
|
912 break; |
|
913 } |
|
914 } |
|
915 if (row.m_use_use == 6) { // Sparge |
|
916 switch (row.m_name) { |
|
917 case 'CaCl2': |
|
918 $('#ss_cacl2').val(row.m_amount * 1000); |
|
919 break; |
|
920 case 'CaSO4': |
|
921 $('#ss_caso4').val(row.m_amount * 1000); |
|
922 break; |
|
923 case 'MgSO4': |
|
924 $('#ss_mgso4').val(row.m_amount * 1000); |
|
925 break; |
|
926 case 'NaCl': |
|
927 $('#ss_nacl').val(row.m_amount * 1000); |
|
928 break; |
|
929 case 'MgCl2': |
|
930 $('#ss_mgcl2').val(row.m_amount * 1000); |
|
931 break; |
|
932 case 'Melkzuur': |
|
933 // $('#wa_acid_name').val(0); |
|
934 // $('#wa_acid').val(row.m_amount * 1000); |
|
935 // $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); // TODO: this ignores changed percentages. |
|
936 // last_acid = 'Melkzuur'; |
|
937 break; |
|
938 case 'Zoutzuur': |
|
939 // $('#wa_acid_name').val(1); |
|
940 // $('#wa_acid').val(row.m_amount * 1000); |
|
941 // $('#wa_acid_perc').val(AcidTypeData[1].AcidPrc); |
|
942 // last_acid = 'Zoutzuur'; |
|
943 break; |
|
944 case 'Fosforzuur': |
|
945 // $('#wa_acid_name').val(2); |
|
946 // $('#wa_acid').val(row.m_amount * 1000); |
|
947 // $('#wa_acid_perc').val(AcidTypeData[2].AcidPrc); |
|
948 // last_acid = 'Fosforzuur'; |
|
949 break; |
|
950 case 'Zwavelzuur': |
|
951 // $('#wa_acid_name').val(3); |
|
952 // $('#wa_acid').val(row.m_amount * 1000); |
|
953 // $('#wa_acid_perc').val(AcidTypeData[3].AcidPrc); |
|
954 // last_acid = 'Zwavelzuur'; |
|
955 break; |
|
956 } |
|
957 } |
|
958 } |
|
959 return data; |
|
960 }, |
|
961 loadError: function(jqXHR, status, error) {} |
|
962 }); |
|
963 $('#miscGrid').jqxGrid({ |
|
964 width: 1240, |
|
965 height: 575, |
|
966 source: miscAdapter, |
|
967 theme: theme, |
|
968 editable: false, |
|
969 ready: function() { |
|
970 $('#jqxTabs').jqxTabs('next'); |
|
971 }, |
|
972 columns: [ |
|
973 { text: 'Ingredient', datafield: 'm_name' }, |
|
974 { text: 'Type', width: 140, datafield: 'm_type', |
|
975 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
976 return '<span style="margin: 3px; margin-top: 6px; float: left;">' + MiscTypeData[value].nl + '</span>'; |
|
977 } |
|
978 }, |
|
979 { text: 'Gebruik', width: 140, datafield: 'm_use_use', |
|
980 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
981 return '<span style="margin: 3px; margin-top: 6px; float: left;">' + MiscUseData[value].nl + '</span>'; |
|
982 } |
|
983 }, |
|
984 { text: 'Tijd', datafield: 'm_time', width: 140, align: 'right', |
|
985 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
986 var duration = ''; |
|
987 if (rowdata.m_use_use == 2) // Boil |
|
988 duration = dataAdapter.formatNumber(value, 'f0') + ' minuten'; |
|
989 else if ((rowdata.m_use_use == 3) || (rowdata.m_use_use == 4)) // Primary or Secondary |
|
990 duration = dataAdapter.formatNumber(value / 1440, 'f0') + ' dagen'; |
|
991 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + duration + '</span>'; |
|
992 } |
|
993 }, |
|
994 { text: 'Hoeveel', datafield: 'm_amount', width: 110, align: 'right', |
|
995 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
996 var vstr = rowdata.m_amount_is_weight ? 'gr' : 'ml'; |
|
997 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + |
|
998 dataAdapter.formatNumber(value * 1000, 'f2') + ' ' + vstr + '</span>'; |
|
999 } |
|
1000 } |
|
1001 ] |
|
1002 }); |
|
1003 }; |
|
1004 |
|
1005 // Inline yeasts editor |
|
1006 var editYeast = function(data) { |
|
1007 var yeastSource = { |
|
1008 localdata: data.yeasts, |
|
1009 datafields: [ |
|
1010 { name: 'y_name', type: 'string' }, |
|
1011 { name: 'y_laboratory', type: 'string' }, |
|
1012 { name: 'y_product_id', type: 'string' }, |
|
1013 { name: 'y_amount', type: 'float' }, |
|
1014 { name: 'y_cost', type: 'float' }, |
|
1015 { name: 'y_type', type: 'int' }, |
|
1016 { name: 'y_form', type: 'int' }, |
|
1017 { name: 'y_flocculation', type: 'int' }, |
|
1018 { name: 'y_min_temperature', type: 'float' }, |
|
1019 { name: 'y_max_temperature', type: 'float' }, |
|
1020 { name: 'y_attenuation', type: 'float' }, |
|
1021 { name: 'y_use', type: 'int' }, |
|
1022 { name: 'y_cells', type: 'float' }, |
|
1023 { name: 'y_tolerance', type: 'float' }, |
|
1024 { name: 'y_inventory', type: 'float' }, |
|
1025 { name: 'y_sta1', type: 'int' }, |
|
1026 { name: 'y_bacteria', type: 'int' }, |
|
1027 { name: 'y_harvest_top', type: 'int' }, |
|
1028 { name: 'y_harvest_time', type: 'int' }, |
|
1029 { name: 'y_pitch_temperature', type: 'float' }, |
|
1030 { name: 'y_pofpos', type: 'int' }, |
|
1031 { name: 'y_zymocide', type: 'int' }, |
|
1032 { name: 'y_gr_hl_lo', type: 'int' }, |
|
1033 { name: 'y_sg_lo', type: 'float' }, |
|
1034 { name: 'y_gr_hl_hi', type: 'int' }, |
|
1035 { name: 'y_sg_hi', type: 'float' }, |
|
1036 { name: 'y_avail', type: 'int' } |
|
1037 ], |
|
1038 }, |
|
1039 yeastAdapter = new $.jqx.dataAdapter(yeastSource); |
|
1040 |
|
1041 $('#yeastGrid').jqxGrid({ |
|
1042 width: 1240, |
|
1043 height: 350, |
|
1044 source: yeastAdapter, |
|
1045 theme: theme, |
|
1046 editable: false, |
|
1047 ready: function() { |
|
1048 calcFermentables(); |
|
1049 $('#jqxTabs').jqxTabs('next'); |
|
1050 }, |
|
1051 columns: [ |
|
1052 { text: 'Gist', datafield: 'y_name' }, |
|
1053 { text: 'Laboratorium', width: 150, datafield: 'y_laboratory' }, |
|
1054 { text: 'Code', width: 90, datafield: 'y_product_id' }, |
|
1055 { text: 'Soort', width: 100, datafield: 'y_form', |
|
1056 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
1057 return '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastFormData[value].nl + '</span>'; |
|
1058 } |
|
1059 }, |
|
1060 { text: 'Min. °C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_min_temperature' }, |
|
1061 { text: 'Max. °C', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_max_temperature' }, |
|
1062 { text: 'Tol. %', width: 60, align: 'right', cellsalign: 'right', datafield: 'y_tolerance', |
|
1063 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
1064 var amount = '', color = '#ffffff'; |
|
1065 if (value > 0) { |
|
1066 amount = dataAdapter.formatNumber(value, 'f1'); |
|
1067 if (dataRecord.est_abv > value) |
|
1068 color = '#ff4040'; |
|
1069 } |
|
1070 return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + amount + '</span>'; |
|
1071 } |
|
1072 }, |
|
1073 { text: 'Attn. %', width: 70, align: 'right', cellsalign: 'right', datafield: 'y_attenuation', cellsformat: 'f1' }, |
|
1074 { text: 'Voor', width: 120, datafield: 'y_use', |
|
1075 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
1076 return '<span style="margin: 4px; margin-top: 6px; float: left;">' + YeastUseData[value].nl + '</span>'; |
|
1077 } |
|
1078 }, |
|
1079 { text: 'Hoeveel', datafield: 'y_amount', width: 90, align: 'right', |
|
1080 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
1081 var amount = dataAdapter.formatNumber(value * 1000, 'f0') + ' ml'; |
|
1082 if (rowdata.y_form == 0) // Liquid |
|
1083 amount = dataAdapter.formatNumber(value, 'f0') + ' pk'; |
|
1084 else if (rowdata.y_form == 1) // Dry |
|
1085 amount = dataAdapter.formatNumber(value * 1000, 'f1') + ' gr'; |
|
1086 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + amount + '</span>'; |
|
1087 } |
|
1088 } |
|
1089 ] |
|
1090 }); |
|
1091 }; |
|
1092 |
|
1093 // inline mash editor |
|
1094 var editMash = function(data) { |
|
1095 var mashSource = { |
|
1096 localdata: data.mashs, |
|
1097 datafields: [ |
|
1098 { name: 'step_name', type: 'string' }, |
|
1099 { name: 'step_type', type: 'int' }, |
|
1100 { name: 'step_volume', type: 'float' }, |
|
1101 { name: 'step_infuse_amount', type: 'float' }, |
|
1102 { name: 'step_infuse_temp', type: 'float' }, |
|
1103 { name: 'step_temp', type: 'float' }, |
|
1104 { name: 'step_time', type: 'float' }, |
|
1105 { name: 'step_wg_ratio', type: 'float' }, |
|
1106 { name: 'ramp_time', type: 'float' }, |
|
1107 { name: 'end_temp', type: 'float' } |
|
1108 ], |
|
1109 }, |
|
1110 mashAdapter = new $.jqx.dataAdapter(mashSource, { |
|
1111 beforeLoadComplete: function(records) { |
|
1112 mash_infuse = 0; |
|
1113 var row, i, data = new Array(); |
|
1114 for (i = 0; i < records.length; i++) { |
|
1115 row = records[i]; |
|
1116 if (row.step_type == 0) // Infusion |
|
1117 mash_infuse += parseFloat(row.step_infuse_amount); |
|
1118 row.step_wg_ratio = 0; // Init this field. |
|
1119 data.push(row); |
|
1120 } |
|
1121 }, |
|
1122 }); |
|
1123 $('#mashGrid').jqxGrid({ |
|
1124 width: 1240, |
|
1125 height: 400, |
|
1126 source: mashAdapter, |
|
1127 theme: theme, |
|
1128 editable: false, |
|
1129 ready: function() { |
|
1130 calcFermentables(); |
|
1131 calcInit(); |
|
1132 calcMash(); |
|
1133 $('#jqxLoader').jqxLoader('close'); |
|
1134 $('#jqxTabs').jqxTabs('first'); |
|
1135 }, |
|
1136 columns: [ |
|
1137 { text: 'Stap naam', datafield: 'step_name' }, |
|
1138 { text: 'Stap type', datafield: 'step_type', width: 175, |
|
1139 cellsrenderer: function(index, datafield, value, defaultvalue, column, rowdata) { |
|
1140 return '<div style="margin: 4px;">' + MashStepTypeData[value].nl + '</div>'; |
|
1141 } |
|
1142 }, |
|
1143 { text: 'Start °C', datafield: 'step_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' }, |
|
1144 { text: 'Eind °C', datafield: 'end_temp', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'f1' }, |
|
1145 { text: 'Rust min.', datafield: 'step_time', width: 90, align: 'right', cellsalign: 'right' }, |
|
1146 { text: 'Stap min.', datafield: 'ramp_time', width: 90, align: 'right', cellsalign: 'right' }, |
|
1147 { text: 'Inf/dec L.', datafield: 'step_infuse_amount', width: 90, align: 'right', |
|
1148 cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { |
|
1149 if (rowdata.step_type == 1) |
|
1150 return '<span></span>'; |
|
1151 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f1') + '</span>'; |
|
1152 } |
|
1153 }, |
|
1154 { text: 'Inf/dec °C', datafield: 'step_infuse_temp', width: 90, align: 'right', |
|
1155 cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { |
|
1156 if (rowdata.step_type == 1) |
|
1157 return '<span></span>'; |
|
1158 return '<span style="margin: 4px; margin-top: 6px; float: right;">' + dataAdapter.formatNumber(value, 'f2') + '</span>'; |
|
1159 } |
|
1160 }, |
|
1161 { text: 'L/Kg.', datafield: 'step_wg_ratio', width: 90, align: 'right', |
|
1162 cellsrenderer: function(row, columnfield, value, defaulthtml, columnproperties, rowdata) { |
|
1163 var color = '#ffffff'; |
|
1164 if (value < 2.0 || value > 6.0) |
|
1165 color = '#ff4040'; |
|
1166 return '<span style="margin: 4px; margin-top: 6px; float: right; color: ' + color + ';">' + dataAdapter.formatNumber(value, 'f2') + '</span>'; |
|
1167 } |
|
1168 } |
|
1169 ] |
|
1170 }); |
|
1171 }; |
|
1172 |
|
1173 |
|
1174 /* |
|
1175 * Remove the top menu so that we MUST use the buttons to leave the editor. |
|
1176 */ |
|
1177 $('#jqxMenu').jqxMenu('destroy'); |
|
1178 |
|
1179 console.log('record:' + my_record + ' return:' + my_return + ' theme:' + theme); |
|
1180 $('#jqxLoader').jqxLoader({ |
|
1181 width: 250, |
|
1182 height: 150, |
|
1183 isModal: true, |
|
1184 text: 'Laden recept ...', |
|
1185 theme: theme |
|
1186 }); |
|
1187 |
|
1188 function setWaterAgent(name, amount) { |
|
1189 |
|
1190 var record, records, miscs, i, id, row, found = false, rows = $('#miscGrid').jqxGrid('getrows'); |
|
1191 if (amount == 0) { |
|
1192 for (i = 0; i < rows.length; i++) { |
|
1193 row = rows[i]; |
|
1194 if (row.m_name == name) { |
|
1195 id = $('#miscGrid').jqxGrid('getrowid', i); |
|
1196 $('#miscGrid').jqxGrid('deleterow', id); |
|
1197 } |
|
1198 } |
|
1199 } else { |
|
1200 for (i = 0; i < rows.length; i++) { |
|
1201 row = rows[i]; |
|
1202 if (row.m_name == name) { |
|
1203 found = true; |
|
1204 $('#miscGrid').jqxGrid('setcellvalue', i, 'm_amount', amount / 1000); |
|
1205 break; |
|
1206 } |
|
1207 } |
|
1208 if (! found) { |
|
1209 miscs = new $.jqx.dataAdapter(miscInvSource, { |
|
1210 loadComplete: function() { |
|
1211 records = miscs.records; |
|
1212 for (i = 0; i < records.length; i++) { |
|
1213 record = records[i]; |
|
1214 if (record.name == name) { |
|
1215 row = {}; |
|
1216 row['m_name'] = record.name; |
|
1217 row['m_amount'] = amount / 1000; |
|
1218 row['m_cost'] = record.cost; |
|
1219 row['m_type'] = record.type; |
|
1220 row['m_use_use'] = record.use_use; |
|
1221 row['m_time'] = 0; |
|
1222 row['m_amount_is_weight'] = record.amount_is_weight; |
|
1223 row['m_inventory'] = record.inventory; |
|
1224 row['m_avail'] = 1; |
|
1225 $('#miscGrid').jqxGrid('addrow', null, row); |
|
1226 } |
|
1227 } |
|
1228 } |
|
1229 }); |
|
1230 miscs.dataBind(); |
|
1231 return; |
|
1232 } |
|
1233 } |
|
1234 } |
|
1235 |
|
1236 |
|
1237 // Procedure TFrmWaterAdjustment.CalcWater2; |
|
1238 function calcWater() { |
|
1239 |
|
1240 if (! data_loaded) { |
|
1241 console.log('calcWater(): failsave'); |
|
1242 return; |
|
1243 } |
|
1244 |
|
1245 var liters = 0, |
|
1246 calcium = 0, |
|
1247 magnesium = 0, |
|
1248 sodium = 0, |
|
1249 total_alkalinity = 0, |
|
1250 bicarbonate = 0, |
|
1251 chloride = 0, |
|
1252 sulfate = 0, |
|
1253 ph = 0, |
|
1254 RA = 0, |
|
1255 frac = 0, |
|
1256 TpH = 0, |
|
1257 protonDeficit = 0, |
|
1258 AT, BT, |
|
1259 r1d, r2d, f1d, f2d, f3d, |
|
1260 deltapH, deltapd, pd, n, |
|
1261 Res; |
|
1262 |
|
1263 if (dataRecord.w1_name == '') { |
|
1264 return; |
|
1265 } |
|
1266 |
|
1267 $('#w1_hardness').val(Hardness(dataRecord.w1_calcium, dataRecord.w1_magnesium)); |
|
1268 $('#w1_ra').val(ResidualAlkalinity(dataRecord.w1_total_alkalinity, dataRecord.w1_calcium, dataRecord.w1_magnesium)); |
|
1269 |
|
1270 // If there is a dillute water source, mix the waters. |
|
1271 if (dataRecord.w2_name != '') { |
|
1272 liters = dataRecord.w1_amount + dataRecord.w2_amount; |
|
1273 calcium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_calcium, dataRecord.w2_calcium); |
|
1274 magnesium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_magnesium, dataRecord.w2_magnesium); |
|
1275 sodium = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sodium, dataRecord.w2_sodium); |
|
1276 chloride = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_chloride, dataRecord.w2_chloride); |
|
1277 sulfate = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_sulfate, dataRecord.w2_sulfate); |
|
1278 total_alkalinity = mix(dataRecord.w1_amount, dataRecord.w2_amount, dataRecord.w1_total_alkalinity, dataRecord.w2_total_alkalinity); |
|
1279 ph = -Math.log10(((Math.pow(10, -dataRecord.w1_ph) * dataRecord.w1_amount) + (Math.pow(10, -dataRecord.w2_ph) * dataRecord.w2_amount)) / liters); |
|
1280 $('#w2_hardness').val(Hardness(dataRecord.w2_calcium, dataRecord.w2_magnesium)); |
|
1281 $('#w2_ra').val(ResidualAlkalinity(dataRecord.w2_total_alkalinity, dataRecord.w2_calcium, dataRecord.w2_magnesium)); |
|
1282 } else { |
|
1283 liters = dataRecord.w1_amount; |
|
1284 calcium = dataRecord.w1_calcium; |
|
1285 magnesium = dataRecord.w1_magnesium; |
|
1286 sodium = dataRecord.w1_sodium; |
|
1287 chloride = dataRecord.w1_chloride; |
|
1288 sulfate = dataRecord.w1_sulfate; |
|
1289 total_alkalinity = dataRecord.w1_total_alkalinity; |
|
1290 ph = dataRecord.w1_ph; |
|
1291 } |
|
1292 var bicarbonate = Bicarbonate(total_alkalinity, ph); |
|
1293 |
|
1294 /* Save mixed water ions for later */ |
|
1295 var wg_calcium = calcium; |
|
1296 var wg_sodium = sodium; |
|
1297 var wg_total_alkalinity = total_alkalinity; |
|
1298 var wg_chloride = chloride; |
|
1299 var wg_sulfate = sulfate; |
|
1300 var wg_bicarbonate = bicarbonate; |
|
1301 |
|
1302 dataRecord.wg_amount = liters; |
|
1303 dataRecord.wg_ph = ph; |
|
1304 |
|
1305 $('#wg_amount').val(liters); |
|
1306 $('#wg_calcium').val(Round(calcium, 1)); |
|
1307 $('#wg_magnesium').val(Round(magnesium, 1)); |
|
1308 $('#wg_sodium').val(Round(sodium, 1)); |
|
1309 $('#wg_bicarbonate').val(Round(bicarbonate, 1)); |
|
1310 $('#wg_total_alkalinity').val(Round(total_alkalinity, 1)); |
|
1311 $('#wg_chloride').val(Round(chloride, 1)); |
|
1312 $('#wg_sulfate').val(Round(sulfate, 1)); |
|
1313 $('#wg_ph').val(Round(ph, 2)); |
|
1314 $('#wg_hardness').val(Round(Hardness(calcium, magnesium), 1)); |
|
1315 $('#wg_ra').val(Round(ResidualAlkalinity(total_alkalinity, calcium, magnesium), 1)); |
|
1316 |
|
1317 var mash_ph = Round(MashpH(), 3); |
|
1318 console.log('Distilled water mash pH: ' + mash_ph); |
|
1319 |
|
1320 /* Calculate Salt additions */ |
|
1321 if (liters > 0) { |
|
1322 calcium += (parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + |
|
1323 parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000 + |
|
1324 parseFloat($('#wa_caco3').jqxNumberInput('decimal')) * MMCa / MMCaCO3 * 1000) / liters; |
|
1325 magnesium += (parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000 + |
|
1326 parseFloat($('#wa_mgcl2').jqxNumberInput('decimal')) * MMMg / MMMgCl2 * 1000) / liters; |
|
1327 sodium += (parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000 + |
|
1328 parseFloat($('#wa_nahco3').jqxNumberInput('decimal')) * MMNa / MMNaHCO3 * 1000) / liters; |
|
1329 sulfate += (parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 + |
|
1330 parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / liters; |
|
1331 chloride += (2 * parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 + |
|
1332 parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000 + |
|
1333 parseFloat($('#wa_mgcl2').jqxNumberInput('decimal')) * MMCl / MMMgCl2 * 1000) / liters; |
|
1334 bicarbonate += (parseFloat($('#wa_nahco3').jqxNumberInput('decimal')) * MMHCO3 / MMNaHCO3 * 1000 + |
|
1335 parseFloat($('#wa_caco3').jqxNumberInput('decimal')) / 3 * MMHCO3 / MMCaCO3 * 1000) / liters; |
|
1336 } |
|
1337 |
|
1338 if (dataRecord.wa_acid_name < 0 || dataRecord,wa_acid_name >= AcidTypeData.length) { |
|
1339 $('#wa_acid_name').val(0); |
|
1340 dataRecord.wa_acid_name = 0; |
|
1341 dataRecord.wa_acid_perc = AcidTypeData[0].AcidPrc; |
|
1342 $('#wa_acid_perc').val(AcidTypeData[0].AcidPrc); |
|
1343 } |
|
1344 if (last_acid == '') |
|
1345 last_acid = AcidTypeData[dataRecord.wa_acid_name].nl; |
|
1346 |
|
1347 if (parseFloat(dataRecord.wa_acid_perc) == 0) { |
|
1348 dataRecord.wa_acid_perc = AcidTypeData[AT].AcidPrc; |
|
1349 $('#wa_acid_perc').val(AcidTypeData[AT].AcidPrc); |
|
1350 } |
|
1351 |
|
1352 AT = dataRecord.wa_acid_name; |
|
1353 |
|
1354 /* Note that the next calculations do not correct the pH change by the added salts. |
|
1355 This pH change is at most 0.1 pH and is a minor difference in Acid amount. */ |
|
1356 |
|
1357 if (dataRecord.calc_acid) { |
|
1358 $('.c_mashph').show(); |
|
1359 /* Auto calculate pH */ |
|
1360 TpH = parseFloat(dataRecord.mash_ph); |
|
1361 protonDeficit = ProtonDeficit(TpH); |
|
1362 console.log('calc_acid tgt: ' + TpH + ' protonDeficit: ' + protonDeficit); |
|
1363 if (protonDeficit > 0) { // Add acid |
|
1364 frac = CalcFrac(TpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); |
|
1365 Acid = protonDeficit / frac; |
|
1366 Acid *= AcidTypeData[AT].MolWt; // mg |
|
1367 Acidmg = Acid; |
|
1368 var RealSG = Round(((AcidTypeData[AT].AcidSG - 1000) * (parseFloat(dataRecord.wa_acid_perc) / 100)) + 1000, 2); |
|
1369 Acid /= RealSG; |
|
1370 Acid /= AcidTypeData[AT].AcidPrc / 100; |
|
1371 Acid = Round(Acid, 2); |
|
1372 console.log('Mash auto Acid final ml: ' + Acid); |
|
1373 $('#wa_acid').val(Acid); |
|
1374 setWaterAgent(AcidTypeData[AT].nl, Acid); |
|
1375 |
|
1376 bicarbonate = bicarbonate - protonDeficit * frac / liters; |
|
1377 total_alkalinity = bicarbonate * 50 / 61; |
|
1378 } |
|
1379 ph = TpH; |
|
1380 dataRecord.wb_ph = ph; |
|
1381 $('#wb_ph').val(Round(ph, 2)); |
|
1382 $('#est_mash_ph').val(Round(ph, 2)); |
|
1383 } else { // Manual |
|
1384 /* Manual calculate pH */ |
|
1385 $('.c_mashph').hide(); |
|
1386 console.log('calc_acid no'); |
|
1387 pHa = Round(ph, 3); // Adjusted water pH |
|
1388 // Then calculate the new pH with added acids and malts |
|
1389 console.log('Mash pH: ' + pHa); |
|
1390 Acid = AcidTypeData[AT].AcidSG * (parseFloat(dataRecord.wa_acid_perc) / 100); // ml |
|
1391 Acid *= parseFloat($('#wa_acid').jqxNumberInput('decimal')); |
|
1392 Acid /= AcidTypeData[AT].MolWt; // mg |
|
1393 Acidmg = Acid; |
|
1394 |
|
1395 //find the pH where the protondeficit = protondeficit by the acid |
|
1396 frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); |
|
1397 protonDeficit = Round(Acid * frac, 3); |
|
1398 //console.log('protonDeficit Acid: ' + protonDeficit + ' frac: ' + frac + ' pH: ' + pHa); |
|
1399 |
|
1400 deltapH = 0.001; |
|
1401 deltapd = 0.1; |
|
1402 pd = Round(ProtonDeficit(pHa), 6); |
|
1403 n = 0; |
|
1404 while (((pd < (protonDeficit - deltapd)) || (pd > (protonDeficit + deltapd))) && (n < 4000)) { |
|
1405 n++; |
|
1406 if (pd < (protonDeficit - deltapd)) |
|
1407 pHa -= deltapH; |
|
1408 else if (pd > (protonDeficit + deltapd)) |
|
1409 pHa += deltapH; |
|
1410 frac = CalcFrac(pHa, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); |
|
1411 protonDeficit = Acid * frac; |
|
1412 pd = ProtonDeficit(pHa); |
|
1413 } |
|
1414 //console.log('n: ' + n + ' pd: ' + pd + ' protonDeficit: ' + protonDeficit + ' frac: ' + frac + ' pHa: ' + pHa); |
|
1415 bicarbonate = wg_bicarbonate - protonDeficit * frac / liters; |
|
1416 total_alkalinity = RA * 50 / 61; |
|
1417 ph = pHa; |
|
1418 $('#wb_ph').val(Round(ph, 2)); |
|
1419 $('#est_mash_ph').val(Round(ph, 2)); |
|
1420 } |
|
1421 |
|
1422 if ((AT == 3) && (liters > 0)) { // Sulfuctic / Zwavelzuur |
|
1423 RA = parseFloat($('#wa_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 + |
|
1424 parseFloat($('#wa_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 + |
|
1425 Acidmg / 1000 * MMSO4 / (MMSO4 + 2); |
|
1426 RA = 1000 * RA / liters; |
|
1427 sulfate = wg_sulfate + RA; // Not add to sulfate?? |
|
1428 } else if ((AT == 1) && (liters > 0)) { // Hydrochloric, Zoutzuur |
|
1429 RA = parseFloat($('#wa_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 + |
|
1430 parseFloat($('#wa_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl + |
|
1431 Acidmg / 1000 * MMCl / (MMCl + 1); |
|
1432 RA = 1000 * RA / liters; |
|
1433 chloride = wg_chloride + RA; |
|
1434 } |
|
1435 |
|
1436 var BUGU = GetBUGU(); |
|
1437 $('#tgt_bu').val(Round(BUGU, 2)); |
|
1438 // From brouwhulp. |
|
1439 if (BUGU < 0.32) |
|
1440 $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig en zoet</span>"); |
|
1441 else if (BUGU < 0.43) |
|
1442 $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig, zoet</span>"); |
|
1443 else if (BUGU < 0.52) |
|
1444 $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Evenwichtig</span>"); |
|
1445 else if (BUGU < 0.63) |
|
1446 $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Licht hoppig, bitter</span>"); |
|
1447 else |
|
1448 $('#wr_bu').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Extra hoppig, zeer bitter</span>"); |
|
1449 |
|
1450 // Sulfate to Chloride ratio (Palmer). |
|
1451 var OptSO4Clratio = GetOptSO4Clratio(); |
|
1452 $('#tgt_so4_cl').val(Round(OptSO4Clratio, 1)); |
|
1453 if (OptSO4Clratio < 0.4) |
|
1454 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Te moutig</span>"); |
|
1455 else if (OptSO4Clratio < 0.6) |
|
1456 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer moutig</span>"); |
|
1457 else if (OptSO4Clratio < 0.8) |
|
1458 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Moutig</span>"); |
|
1459 else if (OptSO4Clratio < 1.5) |
|
1460 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Gebalanceerd</span>"); |
|
1461 else if (OptSO4Clratio < 2.0) |
|
1462 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Licht bitter</span>"); |
|
1463 else if (OptSO4Clratio < 4.0) |
|
1464 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Bitter</span>"); |
|
1465 else if (OptSO4Clratio < 9.0) |
|
1466 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Zeer bitter</span>"); |
|
1467 else |
|
1468 $('#wrt_so4_cl').html("<span style='vertical-align: top; font-size: 14px; font-style: italic;'>Te bitter</span>"); |
|
1469 if (chloride > 0) |
|
1470 RA = sulfate / chloride; |
|
1471 else |
|
1472 RA = 10; |
|
1473 $('#got_so4_cl').val(Round(RA, 1)); |
|
1474 Res = 'normaal'; |
|
1475 if (RA < (0.8 * OptSO4Clratio)) |
|
1476 Res = 'laag'; |
|
1477 else if (RA > (1.2 * OptSO4Clratio)) |
|
1478 Res = 'hoog'; |
|
1479 setRangeIndicator('so4_cl', Res); |
|
1480 |
|
1481 $('#wb_calcium').val(Round(calcium, 1)); |
|
1482 $('#wb_magnesium').val(Round(magnesium, 1)); |
|
1483 $('#wb_sodium').val(Round(sodium, 1)); |
|
1484 $('#wb_sulfate').val(Round(sulfate, 1)); |
|
1485 $('#wb_chloride').val(Round(chloride, 1)); |
|
1486 $('#wb_bicarbonate').val(Round(bicarbonate, 1)); |
|
1487 $('#wb_total_alkalinity').val(Round(total_alkalinity, 1)); |
|
1488 $('#wb_hardness').val(Hardness(calcium, magnesium)); |
|
1489 $('#wb_ra').val(ResidualAlkalinity(total_alkalinity, calcium, magnesium)); |
|
1490 |
|
1491 if (calcium < 40) { |
|
1492 setRangeIndicator('calcium', 'laag'); |
|
1493 } else if (calcium > 150) { |
|
1494 setRangeIndicator('calcium', 'hoog'); |
|
1495 } else { |
|
1496 setRangeIndicator('calcium', 'normaal'); |
|
1497 } |
|
1498 if (magnesium < 5) { |
|
1499 setRangeIndicator('magnesium', 'laag'); |
|
1500 } else if (magnesium > 40) { |
|
1501 setRangeIndicator('magnesium', 'hoog'); |
|
1502 } else { |
|
1503 setRangeIndicator('magnesium', 'normaal'); |
|
1504 } |
|
1505 if (sodium <= 150) { |
|
1506 setRangeIndicator('sodium', 'normaal'); |
|
1507 } else { |
|
1508 setRangeIndicator('sodium', 'hoog'); |
|
1509 } |
|
1510 // Both chloride and sulfate should be above 50 according to |
|
1511 // John Palmer. So the Cl/SO4 ratio calculation will work. |
|
1512 if (chloride <= 50) { |
|
1513 setRangeIndicator('chloride', 'laag'); |
|
1514 } else if (chloride <= 150) { |
|
1515 setRangeIndicator('chloride', 'normaal'); |
|
1516 } else { |
|
1517 setRangeIndicator('chloride', 'hoog'); |
|
1518 } |
|
1519 if (sulfate <= 50) { |
|
1520 setRangeIndicator('sulfate', 'laag'); |
|
1521 } else if (sulfate <= 400) { |
|
1522 setRangeIndicator('sulfate', 'normaal'); |
|
1523 } else { |
|
1524 setRangeIndicator('sulfate', 'hoog'); |
|
1525 } |
|
1526 // (cloride + sulfate) > 500 is too high |
|
1527 if ((chloride + sulfate) > 500) { |
|
1528 setRangeIndicator('chloride', 'hoog'); |
|
1529 setRangeIndicator('sulfate', 'hoog'); |
|
1530 } |
|
1531 if (ph < 5.2) { |
|
1532 setRangeIndicator('ph', 'laag'); |
|
1533 } else if (ph > 5.6) { |
|
1534 setRangeIndicator('ph', 'hoog'); |
|
1535 } else { |
|
1536 setRangeIndicator('ph', 'normaal'); |
|
1537 } |
|
1538 if (bicarbonate > 250) { |
|
1539 setRangeIndicator('bicarbonate', 'hoog'); |
|
1540 } else { |
|
1541 setRangeIndicator('bicarbonate', 'normaal'); |
|
1542 } |
|
1543 calcSparge(); |
|
1544 } |
|
1545 |
|
1546 |
|
1547 function calcSparge() { |
|
1548 |
|
1549 /* Based on the work of ajDeLange. */ |
|
1550 var ws_calcium, ws_magnesium, ws_total_alkalinity, ws_sodium, ws_chloride; |
|
1551 var ws_sulfate, ws_ph, ws_hardness, ws_ra; |
|
1552 var TargetpH = dataRecord.sparge_ph; |
|
1553 var Source_pH = 7.0; |
|
1554 |
|
1555 // Select watersource or fallback to the first source. |
|
1556 if ((dataRecord.sparge_source == 1) && (dataRecord.w2_ph > 0.0)) { // Source 2 |
|
1557 ws_calcium = dataRecord.w2_calcium; |
|
1558 ws_magnesium = dataRecord.w2_magnesium; |
|
1559 ws_total_alkalinity = dataRecord.w2_total_alkalinity; |
|
1560 ws_sodium = dataRecord.w2_sodium; |
|
1561 ws_chloride = dataRecord.w2_chloride; |
|
1562 ws_sulfate = dataRecord.w2_sulfate; |
|
1563 Source_pH = dataRecord.w2_ph; |
|
1564 $('#w2_button').jqxRadioButton({ checked: true }); |
|
1565 } else if ((dataRecord.sparge_source == 2) && (dataRecord.w2_ph > 0.0)) { // Mixed |
|
1566 ws_calcium = dataRecord.wg_calcium; |
|
1567 ws_magnesium = dataRecord.wg_magnesium; |
|
1568 ws_total_alkalinity = dataRecord.wg_total_alkalinity; |
|
1569 ws_sodium = dataRecord.wg_sodium; |
|
1570 ws_chloride = dataRecord.wg_chloride; |
|
1571 ws_sulfate = dataRecord.wg_sulfate; |
|
1572 Source_pH = dataRecord.wg_ph; |
|
1573 $('#wg_button').jqxRadioButton({ checked: true }); |
|
1574 } else { |
|
1575 ws_calcium = dataRecord.w1_calcium; |
|
1576 ws_magnesium = dataRecord.w1_magnesium; |
|
1577 ws_total_alkalinity = dataRecord.w1_total_alkalinity; |
|
1578 ws_sodium = dataRecord.w1_sodium; |
|
1579 ws_chloride = dataRecord.w1_chloride; |
|
1580 ws_sulfate = dataRecord.w1_sulfate; |
|
1581 Source_pH = dataRecord.w1_ph; |
|
1582 $('#w1_button').jqxRadioButton({ checked: true }); |
|
1583 } |
|
1584 |
|
1585 if (dataRecord.sparge_volume > 0) { |
|
1586 ws_calcium += (parseFloat($('#ss_cacl2').jqxNumberInput('decimal')) * MMCa / MMCaCl2 * 1000 + |
|
1587 parseFloat($('#ss_caso4').jqxNumberInput('decimal')) * MMCa / MMCaSO4 * 1000) / dataRecord.sparge_volume; |
|
1588 ws_magnesium += (parseFloat($('#ss_mgso4').jqxNumberInput('decimal')) * MMMg / MMMgSO4 * 1000 + |
|
1589 parseFloat($('#ss_mgcl2').jqxNumberInput('decimal')) * MMMg / MMMgCl2 * 1000) / dataRecord.sparge_volume; |
|
1590 ws_sodium += (parseFloat($('#ss_nacl').jqxNumberInput('decimal')) * MMNa / MMNaCl * 1000) / dataRecord.sparge_volume; |
|
1591 ws_sulfate += (parseFloat($('#ss_caso4').jqxNumberInput('decimal')) * MMSO4 / MMCaSO4 * 1000 + |
|
1592 parseFloat($('#ss_mgso4').jqxNumberInput('decimal')) * MMSO4 / MMMgSO4 * 1000) / dataRecord.sparge_volume; |
|
1593 ws_chloride += (2 * parseFloat($('#ss_cacl2').jqxNumberInput('decimal')) * MMCl / MMCaCl2 * 1000 + |
|
1594 parseFloat($('#ss_nacl').jqxNumberInput('decimal')) * MMCl / MMNaCl * 1000 + |
|
1595 parseFloat($('#ss_mgcl2').jqxNumberInput('decimal')) * MMCl / MMMgCl2 * 1000) / dataRecord.sparge_volume; |
|
1596 } |
|
1597 |
|
1598 /* Show the spargewate with salt additions */ |
|
1599 $('#sw_calcium').val(Round(ws_calcium, 1)); |
|
1600 $('#sw_magnesium').val(Round(ws_magnesium, 1)); |
|
1601 $('#sw_sodium').val(Round(ws_sodium, 1)); |
|
1602 $('#sw_sulfate').val(Round(ws_sulfate, 1)); |
|
1603 $('#sw_chloride').val(Round(ws_chloride, 1)); |
|
1604 $('#sw_bicarbonate').val(Round(Bicarbonate(ws_total_alkalinity, Source_pH), 1)); |
|
1605 $('#sw_total_alkalinity').val(Round(ws_total_alkalinity, 1)); |
|
1606 $('#sw_ph').val(dataRecord.sparge_ph); |
|
1607 $('#sw_hardness').val(Hardness(ws_calcium, ws_magnesium)); |
|
1608 $('#sw_ra').val(ResidualAlkalinity(ws_total_alkalinity, ws_calcium, ws_magnesium)); |
|
1609 |
|
1610 AT = dataRecord.sparge_acid_type; |
|
1611 if (AT < 0 || AT >= AcidTypeData.length) { |
|
1612 AT = 0; |
|
1613 dataRecord.sparge_acid_type = 0; |
|
1614 $('#sparge_acid_type').val(AcidTypeData[0].nl); |
|
1615 dataRecord.sparge_acid_perc = AcidTypeData[0].AcidPrc; |
|
1616 $('#sparge_acid_perc').val(dataRecord.sparge_acid_perc); |
|
1617 } |
|
1618 |
|
1619 /* |
|
1620 * Auto calculate the required acid |
|
1621 */ |
|
1622 if (dataRecord.calc_acid) { |
|
1623 // Step 1: Compute the mole fractions of carbonic (f1o), bicarbonate (f2o) and carbonate(f3o) at the water pH |
|
1624 var r1 = Math.pow(10, Source_pH - 6.35); |
|
1625 var r2 = Math.pow(10, Source_pH - 10.33); |
|
1626 var d = 1 + r1 + r1 * r2; |
|
1627 var f1 = 1 / d; |
|
1628 var f3 = r1 * r2 / d; |
|
1629 |
|
1630 //Step 2. Compute the mole fractions at pH = 4.3 (the pH which defines alkalinity) |
|
1631 var r143 = Math.pow(10, 4.3 - 6.35); |
|
1632 var r243 = Math.pow(10, 4.3 - 10.33); |
|
1633 var d43 = 1 + r143 + r143 * r243; |
|
1634 var f143 = 1 / d43; |
|
1635 var f343 = r143 * r243 / d43; |
|
1636 |
|
1637 //Step 4. Solve |
|
1638 var Ct = ws_total_alkalinity / 50 / ((f143 - f1) + (f3 - f343)); |
|
1639 |
|
1640 //Step 5. Compute mole fractions at desired pH |
|
1641 var r1g = Math.pow(10, TargetpH - 6.35); |
|
1642 var r2g = Math.pow(10, TargetpH - 10.33); |
|
1643 var dg = 1 + r1g + r1g * r2g; |
|
1644 var f1g = 1 / dg; |
|
1645 var f3g = r1g * r2g / dg; |
|
1646 |
|
1647 //Step 6. Use these to compute the milliequivalents acid required per liter (mEq/L) |
|
1648 var Acid = Ct * ((f1g - f1) + (f3 - f3g)) + Math.pow(10, -TargetpH) - Math.pow(10, -Source_pH); //mEq/l |
|
1649 Acid += 0.01; // Add acid that would be required for distilled water. |
|
1650 |
|
1651 //Step 8. Get the acid data. |
|
1652 var fract = CalcFrac(TargetpH, AcidTypeData[AT].pK1, AcidTypeData[AT].pK2, AcidTypeData[AT].pK3); |
|
1653 |
|
1654 //Step 9. Now divide the mEq required by the "fraction". This is the required number of moles of acid. |
|
1655 Acid /= fract; |
|
1656 |
|
1657 //Step 10. Multiply by molecular weight of the acid |
|
1658 Acid *= AcidTypeData[AT].MolWt; //mg |
|
1659 |
|
1660 //Step 11. Divide by Specific Gravity and Percentage to get the final ml. |
|
1661 var RealSG = Round(((AcidTypeData[AT].AcidSG - 1000) * (dataRecord.sparge_acid_perc / 100)) + 1000, 2); |
|
1662 Acid = Acid / RealSG; //ml |
|
1663 Acid *= dataRecord.sparge_volume; //ml acid total at 100% |
|
1664 Acid /= AcidTypeData[AT].AcidPrc / 100; //ml acid at supplied strength |
|
1665 Acid = Round(Acid, 2); |
|
1666 dataRecord.sparge_acid_amount = Acid / 1000; |
|
1667 $('#sparge_acid_amount').val(Acid); |
|
1668 } |
|
1669 |
|
1670 // Finally calculate the estimate preboil pH |
|
1671 var ph = -Math.log10(((Math.pow(10, -dataRecord.wb_ph) * dataRecord.wg_amount) + (Math.pow(10, -dataRecord.sparge_ph) * dataRecord.sparge_volume)) / |
|
1672 (dataRecord.wg_amount + dataRecord.sparge_volume)); |
|
1673 $('#preboil_ph').val(ph); |
|
1674 } |
|
1675 |
|
1676 function calcInit() { |
|
1677 console.log('calc.init()'); |
|
1678 |
|
1679 calcWater(); |
|
1680 $('#w1_name').jqxDropDownList('selectItem', dataRecord.w1_name); |
|
1681 $('#w2_name').jqxDropDownList('selectItem', dataRecord.w2_name); |
|
1682 // Fix tap water if zero using mash infuse amount. |
|
1683 if (parseFloat($('#w1_amount').jqxNumberInput('decimal')) == 0 && mash_infuse > 0) { |
|
1684 $('#w1_amount').val(mash_infuse); |
|
1685 dataRecord.w1_amount = mash_infuse; |
|
1686 $('#wg_amount').val(mash_infuse); |
|
1687 $('#w2_amount').val(0); |
|
1688 dataRecord.w2_amount = 0; |
|
1689 } |
|
1690 calcWater(); |
|
1691 |
|
1692 }; |
|
1693 |
|
1694 function saveRecord(goback) { |
|
1695 window.location.href = my_return; |
|
1696 }; |
|
1697 |
|
1698 dataAdapter.dataBind(); |
|
1699 |
|
1700 // initialize the input fields. |
|
1701 // Tab 1, Algemeen |
|
1702 $('#name').jqxTooltip({ content: 'De naam voor dit recept.' }); |
|
1703 $('#name').jqxInput({ theme: theme, width: 640, height: 23 }); |
|
1704 $('#notes').jqxTooltip({ content: 'De uitgebreide opmerkingen over dit recept.' }); |
|
1705 $('#notes').jqxInput({ theme: theme, width: 960, height: 200 }); |
|
1706 $('#type').jqxTooltip({ content: 'Het brouw type van dit recept.' }); |
|
1707 $('#type').jqxInput({ theme: theme, width: 180, height: 23 }); |
|
1708 $('#efficiency').jqxTooltip({ content: 'Het rendement van maischen en koken.' }); |
|
1709 $('#efficiency').jqxNumberInput(Show1dec); |
|
1710 $('#batch_size').jqxTooltip({ content: 'Het volume van het gekoelde wort na het koken.' }); |
|
1711 $('#batch_size').jqxNumberInput(Show1dec); |
|
1712 $('#batch_size').jqxNumberInput({ min: 4 }); |
|
1713 $('#boil_size').jqxTooltip({ content: 'Het volume van het wort voor het koken.' }); |
|
1714 $('#boil_size').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 90, height: 23, decimalDigits: 2, readOnly: true }); |
|
1715 $('#boil_time').jqxTooltip({ content: 'De kooktijd in minuten.' }); |
|
1716 $('#boil_time').jqxNumberInput(Show0dec); |
|
1717 $('#boil_time').jqxNumberInput({ min: 4, max: 360 }); |
|
1718 |
|
1719 $('#st_name').jqxTooltip({ content: 'De bierstijl naam voor dit recept.'}); |
|
1720 $('#st_name').jqxInput({ theme: theme, width: 250, height: 23 }); |
|
1721 $('#st_letter').jqxTooltip({ content: 'De bierstijl letter voor dit recept.'}); |
|
1722 $('#st_letter').jqxInput({ theme: theme, width: 90, height: 23 }); |
|
1723 $('#st_guide').jqxTooltip({ content: 'De bierstijl gids voor dit recept.'}); |
|
1724 $('#st_guide').jqxInput({ theme: theme, width: 250, height: 23 }); |
|
1725 $('#st_category').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'}); |
|
1726 $('#st_category').jqxInput({ theme: theme, width: 250, height: 23 }); |
|
1727 $('#st_category_number').jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'}); |
|
1728 $('#st_category_number').jqxNumberInput(Smal0dec); |
|
1729 $('#st_type').jqxTooltip({ content: 'Het bierstijl type.'}); |
|
1730 $('#st_type').jqxInput({ theme: theme, width: 250, height: 23 }); |
|
1731 |
|
1732 $('#est_og').jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' }); |
|
1733 $('#est_og').jqxNumberInput(Show3dec); |
|
1734 $('#st_og_min').jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'}); |
|
1735 $('#st_og_min').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true }); |
|
1736 $('#st_og_max').jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'}); |
|
1737 $('#st_og_max').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true }); |
|
1738 |
|
1739 $('#est_fg').jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' }); |
|
1740 $('#est_fg').jqxNumberInput(Show3dec); |
|
1741 $('#st_fg_min').jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'}); |
|
1742 $('#st_fg_min').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true }); |
|
1743 $('#st_fg_max').jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'}); |
|
1744 $('#st_fg_max').jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true }); |
|
1745 |
|
1746 $('#est_abv').jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' }); |
|
1747 $('#est_abv').jqxNumberInput(Smal1dec); |
|
1748 $('#st_abv_min').jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'}); |
|
1749 $('#st_abv_min').jqxNumberInput(Smal1dec); |
|
1750 $('#st_abv_max').jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'}); |
|
1751 $('#st_abv_max').jqxNumberInput(Smal1dec); |
|
1752 |
|
1753 $('#est_color').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' }); |
|
1754 $('#est_color').jqxNumberInput(Show0dec); |
|
1755 $('#st_color_min').jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'}); |
|
1756 $('#st_color_min').jqxNumberInput(Smal0dec); |
|
1757 $('#st_color_max').jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'}); |
|
1758 $('#st_color_max').jqxNumberInput(Smal0dec); |
|
1759 $('#color_method').jqxInput({ theme: theme, width: 180, height: 23 }); |
|
1760 $('#est_ibu').jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' }); |
|
1761 $('#est_ibu').jqxNumberInput(Show0dec); |
|
1762 $('#st_ibu_min').jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'}); |
|
1763 $('#st_ibu_min').jqxNumberInput(Smal0dec); |
|
1764 $('#st_ibu_max').jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'}); |
|
1765 $('#st_ibu_max').jqxNumberInput(Smal0dec); |
|
1766 |
|
1767 $('#ibu_method').jqxInput({ theme: theme, width: 180, height: 23 }); |
|
1768 $('#kcal').jqxTooltip({ content: 'Energie-inhoud in kcal/liter.' }); |
|
1769 $('#kcal').jqxNumberInput(Smal0dec); |
|
1770 $('#est_carb').jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' }); |
|
1771 $('#est_carb').jqxNumberInput(Smal1dec); |
|
1772 $('#st_carb_min').jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'}); |
|
1773 $('#st_carb_min').jqxNumberInput(Smal1dec); |
|
1774 $('#st_carb_max').jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'}); |
|
1775 $('#st_carb_max').jqxNumberInput(Smal1dec); |
|
1776 |
|
1777 // Tab 2, Vergistbaar |
|
1778 $('#est_color2').jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' }); |
|
1779 $('#est_color2').jqxNumberInput(Show0dec); |
|
1780 $('#est_og2').jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' }); |
|
1781 $('#est_og2').jqxNumberInput(Show3dec); |
|
1782 $('#perc_malts').jqxProgressBar({ |
|
1783 width: 300, |
|
1784 height: 23, |
|
1785 theme: theme, |
|
1786 showText: true, |
|
1787 max: 120, |
|
1788 animationDuration: 0, |
|
1789 colorRanges: [ |
|
1790 { stop: 90, color: '#008C00' }, |
|
1791 { stop: 100, color: '#EB7331' }, |
|
1792 { stop: 120, color: '#FF0000' } |
|
1793 ], |
|
1794 renderText: function(text) { |
|
1795 return (Math.round(parseInt(text) * 1.2)) + '%'; |
|
1796 } |
|
1797 }); |
|
1798 $('#perc_sugars').jqxProgressBar({ |
|
1799 width: 300, |
|
1800 height: 23, |
|
1801 theme: theme, |
|
1802 showText: true, |
|
1803 max: 50, |
|
1804 animationDuration: 0, |
|
1805 colorRanges: [ |
|
1806 { stop: 20, color: '#008C00' }, |
|
1807 { stop: 50, color: '#FF0000' } |
|
1808 ], |
|
1809 renderText: function(text) { |
|
1810 return (Math.round(parseInt(text) * 5) / 10) + '%'; |
|
1811 } |
|
1812 }); |
|
1813 $('#perc_cara').jqxProgressBar({ |
|
1814 width: 300, |
|
1815 height: 23, |
|
1816 theme: theme, |
|
1817 showText: true, |
|
1818 max: 50, |
|
1819 animationDuration: 0, |
|
1820 colorRanges: [ |
|
1821 { stop: 25, color: '#008C00' }, |
|
1822 { stop: 50, color: '#FF0000' } |
|
1823 ], |
|
1824 renderText: function(text) { |
|
1825 return (Math.round(parseInt(text) * 5) / 10) + '%'; |
|
1826 } |
|
1827 }); |
|
1828 $('#ferm_lintner').jqxProgressBar({ |
|
1829 width: 300, |
|
1830 height: 23, |
|
1831 theme: theme, |
|
1832 showText: true, |
|
1833 max: 200, |
|
1834 animationDuration: 0, |
|
1835 colorRanges: [ |
|
1836 { stop: 30, color: '#FF0000' }, |
|
1837 { stop: 40, color: '#EB7331' }, |
|
1838 { stop: 200, color: '#008C00' } |
|
1839 ], |
|
1840 renderText: function(text) { |
|
1841 return (parseInt(text) * 2) + ' lintner'; |
|
1842 } |
|
1843 }); |
|
1844 |
|
1845 // Tab 3, Hoppen |
|
1846 $('#est_ibu2').jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' }); |
|
1847 $('#est_ibu2').jqxNumberInput(Smal0dec); |
|
1848 $('#hop_flavour').jqxProgressBar({ |
|
1849 width: 300, |
|
1850 height: 23, |
|
1851 theme: theme, |
|
1852 showText: true, |
|
1853 animationDuration: 0, |
|
1854 colorRanges: [ |
|
1855 { stop: 20, color: '#004D00' }, |
|
1856 { stop: 40, color: '#008C00' }, |
|
1857 { stop: 60, color: '#00BF00' }, |
|
1858 { stop: 80, color: '#00FF00' }, |
|
1859 { stop: 100, color: '#80FF80' } |
|
1860 ], |
|
1861 renderText: function(text) { |
|
1862 var val = parseInt(text); |
|
1863 if (val < 20) |
|
1864 return 'Weinig'; |
|
1865 else if (val < 40) |
|
1866 return 'Matig'; |
|
1867 else if (val < 60) |
|
1868 return 'Redelijk'; |
|
1869 else if (val < 80) |
|
1870 return 'Veel'; |
|
1871 else |
|
1872 return 'Zeer veel'; |
|
1873 } |
|
1874 }); |
|
1875 $('#hop_aroma').jqxProgressBar({ |
|
1876 width: 300, |
|
1877 height: 23, |
|
1878 theme: theme, |
|
1879 showText: true, |
|
1880 animationDuration: 0, |
|
1881 colorRanges: [ |
|
1882 { stop: 20, color: '#004D00' }, |
|
1883 { stop: 40, color: '#008C00' }, |
|
1884 { stop: 60, color: '#00BF00' }, |
|
1885 { stop: 80, color: '#00FF00' }, |
|
1886 { stop: 100, color: '#80FF80' } |
|
1887 ], |
|
1888 renderText: function(text) { |
|
1889 var val = parseInt(text); |
|
1890 if (val < 20) |
|
1891 return 'Weinig'; |
|
1892 else if (val < 40) |
|
1893 return 'Matig'; |
|
1894 else if (val < 60) |
|
1895 return 'Redelijk'; |
|
1896 else if (val < 80) |
|
1897 return 'Veel'; |
|
1898 else |
|
1899 return 'Zeer veel'; |
|
1900 } |
|
1901 }); |
|
1902 |
|
1903 // Tab 4, Diversen |
|
1904 |
|
1905 // Tab 5, Gist |
|
1906 $('#est_fg2').jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' }); |
|
1907 $('#est_fg2').jqxNumberInput(Show3dec); |
|
1908 $('#est_abv2').jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' }); |
|
1909 $('#est_abv2').jqxNumberInput(Smal1dec); |
|
1910 |
|
1911 // Tab 6, Maischen |
|
1912 $('#mash_name').jqxInput({ theme: theme, width: 320, height: 23 }); |
|
1913 |
|
1914 // Tab 7, Water |
|
1915 $('#tgt_bu').jqxNumberInput(Show2wat); |
|
1916 $('#tgt_so4_cl,#got_so4_cl').jqxNumberInput(Show1wat); |
|
1917 $('#preboil_ph').jqxNumberInput(Show2wat); |
|
1918 |
|
1919 // Water source 1 |
|
1920 $('#w1_name').jqxInput({ theme: theme, width: 200, height: 23 }); |
|
1921 $('#w1_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true }); |
|
1922 $('#w1_amount').jqxNumberInput(Show1wat); |
|
1923 $('#w1_calcium').jqxNumberInput(Show1wat); |
|
1924 $('#w1_magnesium').jqxNumberInput(Show1wat); |
|
1925 $('#w1_sodium').jqxNumberInput(Show1wat); |
|
1926 $('#w1_bicarbonate').jqxNumberInput(Show1wat); |
|
1927 $('#w1_total_alkalinity').jqxNumberInput(Show1wat); |
|
1928 $('#w1_chloride').jqxNumberInput(Show1wat); |
|
1929 $('#w1_sulfate').jqxNumberInput(Show1wat); |
|
1930 $('#w1_ph').jqxNumberInput(Show2wat); |
|
1931 $('#w1_hardness').jqxNumberInput(Show1wat); |
|
1932 $('#w1_ra').jqxNumberInput(Show1wat); |
|
1933 // Water source 2 |
|
1934 $('#w2_name').jqxInput({ theme: theme, width: 200, height: 23 }); |
|
1935 $('#w2_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true }); |
|
1936 $('#w2_amount').jqxNumberInput(Show1wat); |
|
1937 $('#w2_calcium').jqxNumberInput(Show1wat); |
|
1938 $('#w2_magnesium').jqxNumberInput(Show1wat); |
|
1939 $('#w2_sodium').jqxNumberInput(Show1wat); |
|
1940 $('#w2_bicarbonate').jqxNumberInput(Show1wat); |
|
1941 $('#w2_total_alkalinity').jqxNumberInput(Show1wat); |
|
1942 $('#w2_chloride').jqxNumberInput(Show1wat); |
|
1943 $('#w2_sulfate').jqxNumberInput(Show1wat); |
|
1944 $('#w2_ph').jqxNumberInput(Show2wat); |
|
1945 $('#w2_hardness').jqxNumberInput(Show1wat); |
|
1946 $('#w2_ra').jqxNumberInput(Show1wat); |
|
1947 // Water mixed |
|
1948 $('#wg_button').jqxRadioButton({ theme: theme, width: 50, height: 23, disabled: true }); |
|
1949 $('#wg_amount').jqxNumberInput(Show1wat); |
|
1950 $('#wg_calcium').jqxNumberInput(Show1wat); |
|
1951 $('#wg_magnesium').jqxNumberInput(Show1wat); |
|
1952 $('#wg_sodium').jqxNumberInput(Show1wat); |
|
1953 $('#wg_bicarbonate').jqxNumberInput(Show1wat); |
|
1954 $('#wg_total_alkalinity').jqxNumberInput(Show1wat); |
|
1955 $('#wg_chloride').jqxNumberInput(Show1wat); |
|
1956 $('#wg_sulfate').jqxNumberInput(Show1wat); |
|
1957 $('#wg_ph').jqxNumberInput(Show2wat); |
|
1958 $('#wg_hardness').jqxNumberInput(Show1wat); |
|
1959 $('#wg_ra').jqxNumberInput(Show1wat); |
|
1960 // Water treated |
|
1961 $('#wb_calcium').jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'}); |
|
1962 $('#wb_calcium').jqxNumberInput(Show1wat); |
|
1963 $('#wb_magnesium').jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is tussen 5 en 40.'}); |
|
1964 $('#wb_magnesium').jqxNumberInput(Show1wat); |
|
1965 $('#wb_sodium').jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'}); |
|
1966 $('#wb_sodium').jqxNumberInput(Show1wat); |
|
1967 $('#wb_chloride').jqxTooltip({ content: 'De ideale hoeveelheid Chloride is tussen 50 en 150. Samen met Sulfaat minder dan 500.'}); |
|
1968 $('#wb_chloride').jqxNumberInput(Show1wat); |
|
1969 $('#wb_sulfate').jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is tussen 50 en 400. Samen met Sulfaat minder dan 500.'}); |
|
1970 $('#wb_sulfate').jqxNumberInput(Show1wat); |
|
1971 $('#wb_bicarbonate').jqxTooltip({ content: '0 tot 50 lichte bieren, 50 tot 150 amber bieren, 150 tot 250 donkere bieren.'}); |
|
1972 $('#wb_bicarbonate').jqxNumberInput(Show1wat); |
|
1973 $('#wb_total_alkalinity').jqxNumberInput(Show1wat); |
|
1974 $('#wb_ph').jqxNumberInput(Show2wat); |
|
1975 $('#wb_hardness').jqxNumberInput(Show1wat); |
|
1976 $('#wb_ra').jqxNumberInput(Show1wat); |
|
1977 // Sparge water |
|
1978 $('#sw_amount').jqxNumberInput(Show1wat); |
|
1979 $('#sw_calcium').jqxNumberInput(Show1wat); |
|
1980 $('#sw_magnesium').jqxNumberInput(Show1wat); |
|
1981 $('#sw_sodium').jqxNumberInput(Show1wat); |
|
1982 $('#sw_bicarbonate').jqxNumberInput(Show1wat); |
|
1983 $('#sw_total_alkalinity').jqxNumberInput(Show1wat); |
|
1984 $('#sw_chloride').jqxNumberInput(Show1wat); |
|
1985 $('#sw_sulfate').jqxNumberInput(Show1wat); |
|
1986 $('#sw_ph').jqxNumberInput(Show2wat); |
|
1987 $('#sw_hardness').jqxNumberInput(Show1wat); |
|
1988 $('#sw_ra').jqxNumberInput(Show1wat); |
|
1989 |
|
1990 // Water agents |
|
1991 $('#wa_cacl2').jqxTooltip({ content: 'Voor het maken van een ander waterprofiel. Voegt calcium en chloride toe. Voor het verbeteren van zoetere bieren.'}); |
|
1992 $('#wa_cacl2').jqxNumberInput(Show2wat); |
|
1993 $('#ss_cacl2').jqxNumberInput(Show2wat); |
|
1994 |
|
1995 $('#wa_caso4').jqxTooltip({ |
|
1996 content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.' |
|
1997 }); |
|
1998 $('#wa_caso4').jqxNumberInput(Show2wat); |
|
1999 $('#ss_caso4').jqxNumberInput(Show2wat); |
|
2000 |
|
2001 $('#wa_mgso4').jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'}); |
|
2002 $('#wa_mgso4').jqxNumberInput(Show2wat); |
|
2003 $('#ss_mgso4').jqxNumberInput(Show2wat); |
|
2004 |
|
2005 $('#wa_nacl').jqxTooltip({ |
|
2006 content: 'Keukenzout. Voor het maken van een ander waterprofiel. Voegt natrium en chloride toe. ' + |
|
2007 'Voor het accentueren van zoetheid. Bij hoge dosering wordt het bier ziltig.' |
|
2008 }); |
|
2009 $('#wa_nacl').jqxNumberInput(Show2wat); |
|
2010 $('#ss_nacl').jqxNumberInput(Show2wat); |
|
2011 |
|
2012 $('#wa_mgcl2').jqxTooltip({ content: 'Magnesiumchloride'}); |
|
2013 $('#wa_mgcl2').jqxNumberInput(Show2wat); |
|
2014 $('#ss_mgcl2').jqxNumberInput(Show2wat); |
|
2015 |
|
2016 $('#wa_nahco3').jqxTooltip({ content: 'Baksoda'}); |
|
2017 $('#wa_caco3').jqxTooltip({ content: 'Kalk'}); |
|
2018 $('#wa_nahco3,#wa_caco3').jqxNumberInput(Show2wat); |
|
2019 |
|
2020 $('#mash_ph').jqxTooltip({ content: 'Maisch pH tussen 5.2 en 5.6. Gebruik 5.2 voor lichte en 5.5 voor donkere bieren.'}); |
|
2021 $('#mash_ph').jqxNumberInput(Show2dec); |
|
2022 |
|
2023 $('#calc_acid').jqxCheckBox({ theme: theme, width: 120, height: 23, disabled: true }); |
|
2024 |
|
2025 $('#wa_acid_name').jqxInput({ theme: theme, width: 130, height: 23 }); |
|
2026 $('#wa_acid').jqxNumberInput(Show2dec); |
|
2027 $('#wa_acid').jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' }); |
|
2028 $('#wa_acid_perc').jqxNumberInput(Show0dec); |
|
2029 $('#wa_acid_perc').jqxNumberInput({ symbol: '%', symbolPosition: 'right' }); |
|
2030 |
|
2031 // Sparge water |
|
2032 $('#sparge_ph').jqxNumberInput(Show2dec); |
|
2033 $('#sparge_acid_amount').jqxNumberInput(Show2dec); |
|
2034 $('#sparge_acid_amount').jqxNumberInput({ symbol: ' ml', symbolPosition: 'right' }); |
|
2035 $('#sparge_acid_type').jqxInput({ theme: theme, width: 130, height: 23 }); |
|
2036 $('#sparge_acid_perc').jqxNumberInput(Show0dec); |
|
2037 $('#sparge_acid_perc').jqxNumberInput({ symbol: '%', symbolPosition: 'right' }); |
|
2038 |
|
2039 // Tabs inside the popup window. |
|
2040 $('#jqxTabs').jqxTabs({ |
|
2041 theme: theme, |
|
2042 width: 1280, |
|
2043 height: 660, |
|
2044 autoHeight: false, |
|
2045 position: 'top' |
|
2046 }); |
|
2047 |
|
2048 // Button below |
|
2049 $('#Terug').jqxButton({ template: 'primary', width: '80px', theme: theme }); |
|
2050 $('#Terug').bind('click', function() { |
|
2051 window.location.href = my_return; |
|
2052 }); |
|
2053 |
|
2054 }); |
|
2055 |
|