Merged prod_main and prod_recipe database tables into products. Added recipe tabs in the products editor.

Mon, 24 Dec 2018 23:10:52 +0100

author
Michiel Broek <mbroek@mbse.eu>
date
Mon, 24 Dec 2018 23:10:52 +0100
changeset 151
2c9cfe2f0860
parent 150
159d7a89fcef
child 152
2e4249add363

Merged prod_main and prod_recipe database tables into products. Added recipe tabs in the products editor.

www/import/from_brouwhulp.php file | annotate | diff | comparison | revisions
www/includes/db_product.php file | annotate | diff | comparison | revisions
www/js/prod_edit.js file | annotate | diff | comparison | revisions
www/js/prod_inprod.js file | annotate | diff | comparison | revisions
www/prod_edit.php file | annotate | diff | comparison | revisions
--- a/www/import/from_brouwhulp.php	Mon Dec 24 15:52:11 2018 +0100
+++ b/www/import/from_brouwhulp.php	Mon Dec 24 23:10:52 2018 +0100
@@ -974,14 +974,10 @@
         $len_mash = 0;
 
         echo "  Start adding brews to the database\n";
-        $psql = "TRUNCATE TABLE prod_main;";
-        if (! $presult = mysqli_query($db, $psql)) {
+        $sql = "TRUNCATE TABLE products;";
+        if (! $presult = mysqli_query($db, $sql)) {
                 printf("Error: %s\n", mysqli_error($db));
 	}
-	$rsql = "TRUNCATE TABLE prod_recipes;";
-	if (! $rresult = mysqli_query($db, $rsql)) {
-		printf("Error: %s\n", mysqli_error($db));
-	}
 
 	date_default_timezone_set('Europe/Amsterdam');
         $recipes = simplexml_load_file($brouwhulp . '/brews.xml');
@@ -999,75 +995,70 @@
 		$stage = "Plan";	// Default value.
                 $uuid = str_replace("\n", "", file_get_contents('/proc/sys/kernel/random/uuid'));
 
-		$psql  = "INSERT INTO prod_main SET puuid='" . $uuid;		// So we can link the records.
-		$rsql  = "INSERT INTO prod_recipes SET uuid='" . $uuid;
-
-		$psql .= "', pname='" . mysqli_real_escape_string($db, $recipe->NAME);
-		$rsql .= "', name='" . mysqli_real_escape_string($db, $recipe->NAME);
+		$sql  = "INSERT INTO products SET uuid='" . $uuid;
+		$sql .= "', name='" . mysqli_real_escape_string($db, $recipe->NAME);
 
                 if ($recipe->NOTES) {
-			$rsql .= "', notes='" . mysqli_real_escape_string($db, $recipe->NOTES);		// Duplicate the notes
-			$psql .= "', pnotes='" . mysqli_real_escape_string($db, $recipe->NOTES);
+			$sql .= "', notes='" . mysqli_real_escape_string($db, $recipe->NOTES);
 		} else {
-			$rsql .= "', notes='";
-			$psql .= "', pnotes='";
+			$sql .= "', notes='";
 		}
                 if ($recipe->TYPE)
-                        $rsql .= "', type='" . mysqli_real_escape_string($db, $recipe->TYPE);
+                        $sql .= "', type='" . mysqli_real_escape_string($db, $recipe->TYPE);
                 else
-                        $rsql .= "', type='";
+                        $sql .= "', type='";
                 if ($recipe->BATCH_SIZE)
                         $batch_size = floatval($recipe->BATCH_SIZE);
-                $rsql .= "', batch_size='" . $batch_size;
+                $sql .= "', batch_size='" . $batch_size;
                 if ($recipe->BOIL_SIZE)
                         $boil_size = floatval($recipe->BOIL_SIZE);
-                $rsql .= "', boil_size='" . $boil_size;
+                $sql .= "', boil_size='" . $boil_size;
                 if ($recipe->BOIL_TIME)
-                        $rsql .= "', boil_time='" . floatval($recipe->BOIL_TIME);
+                        $sql .= "', boil_time='" . floatval($recipe->BOIL_TIME);
                 else
-                        $rsql .= "', boil_time='90";
+                        $sql .= "', boil_time='90";
                 if ($recipe->EFFICIENCY)
                         $efficiency = floatval($recipe->EFFICIENCY);
-                $rsql .= "', efficiency='" . $efficiency;
+                $sql .= "', efficiency='" . $efficiency;
                 /* Don't use $recipe->EST_OG but recalculate it */
                 /* Don't use $recipe->EST_FG but recalculate it */
                 /* Don't use $recipe->EST_COLOR but recalculate it */
                 if ($recipe->COLOR_METHOD)
-                        $rsql .= "', color_method='" . mysqli_real_escape_string($db, $recipe->COLOR_METHOD);
+                        $sql .= "', color_method='" . mysqli_real_escape_string($db, $recipe->COLOR_METHOD);
                 if ($recipe->IBU)
-                        $rsql .= "', est_ibu='" . floatval($recipe->IBU);
+                        $sql .= "', est_ibu='" . floatval($recipe->IBU);
                 if ($recipe->IBU_METHOD)
-                        $rsql .= "', ibu_method='" . mysqli_real_escape_string($db, $recipe->IBU_METHOD);
+                        $sql .= "', ibu_method='" . mysqli_real_escape_string($db, $recipe->IBU_METHOD);
                 if ($recipe->CARBONATION)
-                        $rsql .= "', est_carb='" . floatval($recipe->CARBONATION);
+                        $sql .= "', est_carb='" . floatval($recipe->CARBONATION);
 
                 if ($recipe->STYLE) {
-                        $rsql .= recipe_style($recipe);
+                        $sql .= recipe_style($recipe);
                 }
 
 		if ($recipe->CALC_ACID) {
-			($recipe->CALC_ACID == "TRUE") ? $rsql .= "', calc_acid='1" : $rsql .= "', calc_acid='0";
+			($recipe->CALC_ACID == "TRUE") ? $sql .= "', calc_acid='1" : $sql .= "', calc_acid='0";
 		}
 		if ($recipe->TARGET_PH) {
-			 $rsql .= "', mash_ph='" . floatval($recipe->TARGET_PH);
+			 $sql .= "', mash_ph='" . floatval($recipe->TARGET_PH);
 		}
 		if ($recipe->SPARGE_ACID_TYPE && ($recipe->SPARGE_ACID_TYPE == "Lactic")) {
-			$rsql .= "', sparge_acid_type='Melkzuur";
+			$sql .= "', sparge_acid_type='Melkzuur";
 		} else if ($recipe->SPARGE_ACID_TYPE && ($recipe->SPARGE_ACID_TYPE == "Hydrochloric")) {
-			$rsql .= "', sparge_acid_type='Zoutzuur";
+			$sql .= "', sparge_acid_type='Zoutzuur";
 		} else if ($recipe->SPARGE_ACID_TYPE && ($recipe->SPARGE_ACID_TYPE == "Phosphoric")) {
-			$rsql .= "', sparge_acid_type='Fosforzuur";
+			$sql .= "', sparge_acid_type='Fosforzuur";
 		} else if ($recipe->SPARGE_ACID_TYPE && ($recipe->SPARGE_ACID_TYPE == "Sulfuric")) {
-			$rsql .= "', sparge_acid_type='Zwavelzuur";
+			$sql .= "', sparge_acid_type='Zwavelzuur";
 		}
 		if ($recipe->ACID_SPARGE_PERC) {
-			$rsql .= "', sparge_acid_perc='" . floatval($recipe->ACID_SPARGE_PERC);
+			$sql .= "', sparge_acid_perc='" . floatval($recipe->ACID_SPARGE_PERC);
 		}
 		if ($recipe->LACTIC_SPARGE) {
-			$rsql .= "', sparge_acid_amount='" . floatval($recipe->LACTIC_SPARGE);
+			$sql .= "', sparge_acid_amount='" . floatval($recipe->LACTIC_SPARGE);
 		}
 		if ($recipe->VOLUME_HLT) {
-			$rsql .= "', sparge_volume='" . floatval($recipe->VOLUME_HLT);
+			$sql .= "', sparge_volume='" . floatval($recipe->VOLUME_HLT);
 		}
 
                 /*
@@ -1075,7 +1066,7 @@
                  */
                 if ($recipe->FERMENTABLES) {
                         $fermentables = recipe_fermentables($recipe);
-                        $rsql .= "', json_fermentables='" . $fermentables;
+                        $sql .= "', json_fermentables='" . $fermentables;
                         if (strlen($fermentables) > $len_fermentables)
                                 $len_fermentables = strlen($fermentables);
                 }
@@ -1085,7 +1076,7 @@
                  */
                 if ($recipe->HOPS) {
                         $hops = recipe_hops($recipe);
-                        $rsql .= "', json_hops='" . $hops;
+                        $sql .= "', json_hops='" . $hops;
                         if (strlen($hops) > $len_hops)
                                 $len_hops = strlen($hops);
                 }
@@ -1095,7 +1086,7 @@
                  */
                 if ($recipe->MISCS) {
                         $miscs = recipe_miscs($recipe);
-                        $rsql .= "', json_miscs='" . $miscs;
+                        $sql .= "', json_miscs='" . $miscs;
                         if (strlen($miscs) > $len_miscs)
                                 $len_miscs = strlen($miscs);
                 }
@@ -1105,7 +1096,7 @@
                  */
                 if ($recipe->YEASTS) {
                         $yeasts = recipe_yeasts($recipe);
-                        $rsql .= "', json_yeasts='" . $yeasts;
+                        $sql .= "', json_yeasts='" . $yeasts;
                         if (strlen($yeasts) > $len_yeasts)
                                 $len_yeasts = strlen($yeasts);
                 }
@@ -1114,21 +1105,21 @@
                  * Get the waters
                  */
                 if ($recipe->WATERS) {
-                        $rsql .= recipe_waters($recipe, $db);
+                        $sql .= recipe_waters($recipe, $db);
                 }
 
                 /*
                  * Put the mash in a json array
                  */
                 if ($recipe->MASH) {
-                        $rsql .= "',sparge_temp='" . floatval($recipe->MASH->SPARGE_TEMP);
-                        $rsql .= "',sparge_ph='" . floatval($recipe->MASH->PH);
+                        $sql .= "',sparge_temp='" . floatval($recipe->MASH->SPARGE_TEMP);
+                        $sql .= "',sparge_ph='" . floatval($recipe->MASH->PH);
                         if ($recipe->MASH->NAME)
-                                $rsql .= "',mash_name='" . mysqli_real_escape_string($db, $recipe->MASH->NAME);
+                                $sql .= "',mash_name='" . mysqli_real_escape_string($db, $recipe->MASH->NAME);
 
                         if ($recipe->MASH->MASH_STEPS) {
                                 $steps = recipe_mash_steps($recipe);
-                                $rsql .= "', json_mashs='" . $steps;
+                                $sql .= "', json_mashs='" . $steps;
                                 if (strlen($steps) > $len_mash)
                                         $len_mash = strlen($steps);
                         }
@@ -1139,41 +1130,41 @@
                  *  OG, FG, color, IBU
                  */
                 $og = estimate_sg($f_sugars, $batch_size);
-                $rsql .= "', est_og='" . floatval($og);
+                $sql .= "', est_og='" . floatval($og);
                 $fg = estimate_fg($pSugar, $pCara, 0, 0, 0, $svg, $og);
-                $rsql .= "', est_fg='" . floatval($fg);
+                $sql .= "', est_fg='" . floatval($fg);
                 $abv = abvol($og, $fg);
-                $rsql .= "', est_abv='" . floatval($abv);
+                $sql .= "', est_abv='" . floatval($abv);
                 $color = kw_to_ebc(mysqli_real_escape_string($db, $recipe->COLOR_METHOD), $colorw);
-                $rsql .= "', est_color='" . floatval($color);
-		$psql .= "', code='" . mysqli_real_escape_string($db, $recipe->NR_RECIPE);
+                $sql .= "', est_color='" . floatval($color);
+		$sql .= "', code='" . mysqli_real_escape_string($db, $recipe->NR_RECIPE);
 
 		/*
 		 * Update external logfiles
 		 */
-		$sql  = "UPDATE log_brews SET product_uuid='".$uuid."', product_name='".mysqli_real_escape_string($db, $recipe->NAME);
-		$sql .= "' WHERE product_code='".mysqli_real_escape_string($db, $recipe->NR_RECIPE)."';";
-		if (! $result = mysqli_query($db, $sql)) {
+		$lsql  = "UPDATE log_brews SET product_uuid='".$uuid."', product_name='".mysqli_real_escape_string($db, $recipe->NAME);
+		$lsql .= "' WHERE product_code='".mysqli_real_escape_string($db, $recipe->NR_RECIPE)."';";
+		if (! $result = mysqli_query($db, $lsql)) {
 			printf("Error: %s\n", mysqli_error($db));
 		}
 		$count = mysqli_affected_rows($db);
 		if ($count > 0) {
-			$psql .= "', log_brew='1";
+			$sql .= "', log_brew='1";
 		}
-		$sql  = "UPDATE log_fermentation SET product_uuid='".$uuid."', product_name='".mysqli_real_escape_string($db, $recipe->NAME);
-		$sql .= "' WHERE product_code='".mysqli_real_escape_string($db, $recipe->NR_RECIPE)."';";
-		if (! $result = mysqli_query($db, $sql)) {
+		$lsql  = "UPDATE log_fermentation SET product_uuid='".$uuid."', product_name='".mysqli_real_escape_string($db, $recipe->NAME);
+		$lsql .= "' WHERE product_code='".mysqli_real_escape_string($db, $recipe->NR_RECIPE)."';";
+		if (! $result = mysqli_query($db, $lsql)) {
 			printf("Error: %s\n", mysqli_error($db));
 		}
 		$count = mysqli_affected_rows($db);
 		if ($count > 0) {
-			$psql .= "', log_fermentation='1";
+			$sql .= "', log_fermentation='1";
 		} else {
 			// See if there really are no records
-			$sql  = "SELECT product_code FROM log_fermentation WHERE product_code='".$recipe->NR_RECIPE."';";
+			$lsql  = "SELECT product_code FROM log_fermentation WHERE product_code='".$recipe->NR_RECIPE."';";
 			$count = mysqli_affected_rows($db);
 			if ($count > 0) {
-				$psql .= "', log_fermentation='1";
+				$sql .= "', log_fermentation='1";
 			}
 		}
 
@@ -1199,47 +1190,47 @@
 					printf("Error: %s\n", mysqli_error($db));
 				}
 			}
-			$psql .= "', log_fermentation='1";
+			$sql .= "', log_fermentation='1";
 		}
 
 		if ($recipe->EQUIPMENT) {
-			$psql .= "', eq_name='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->NAME);
-			$psql .= "', eq_notes='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->NOTES);
-			$psql .= "', eq_boil_size='" . floatval($recipe->EQUIPMENT->BOIL_SIZE);
-			$psql .= "', eq_batch_size='" . floatval($recipe->EQUIPMENT->BATCH_SIZE);
-			$psql .= "', eq_tun_volume='" . floatval($recipe->EQUIPMENT->TUN_VOLUME);
-			$psql .= "', eq_tun_weight='" . floatval($recipe->EQUIPMENT->TUN_WEIGHT);
-			$psql .= "', eq_tun_specific_heat='" . floatval($recipe->EQUIPMENT->TUN_SPECIFIC_HEAT);
-			$psql .= "', eq_tun_material='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->TUN_MATERIAL);
-			$psql .= "', eq_tun_height='" . floatval($recipe->EQUIPMENT->TUN_HEIGHT);
+			$sql .= "', eq_name='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->NAME);
+			$sql .= "', eq_notes='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->NOTES);
+			$sql .= "', eq_boil_size='" . floatval($recipe->EQUIPMENT->BOIL_SIZE);
+			$sql .= "', eq_batch_size='" . floatval($recipe->EQUIPMENT->BATCH_SIZE);
+			$sql .= "', eq_tun_volume='" . floatval($recipe->EQUIPMENT->TUN_VOLUME);
+			$sql .= "', eq_tun_weight='" . floatval($recipe->EQUIPMENT->TUN_WEIGHT);
+			$sql .= "', eq_tun_specific_heat='" . floatval($recipe->EQUIPMENT->TUN_SPECIFIC_HEAT);
+			$sql .= "', eq_tun_material='" . mysqli_real_escape_string($db, $recipe->EQUIPMENT->TUN_MATERIAL);
+			$sql .= "', eq_tun_height='" . floatval($recipe->EQUIPMENT->TUN_HEIGHT);
 			if ($recipe->EQUIPMENT->TOP_UP_WATER)
-				$psql .= "', eq_top_up_water='" . floatval($recipe->EQUIPMENT->TOP_UP_WATER);
-			$psql .= "', eq_trub_chiller_loss='" . floatval($recipe->EQUIPMENT->TRUB_CHILLER_LOSS);
+				$sql .= "', eq_top_up_water='" . floatval($recipe->EQUIPMENT->TOP_UP_WATER);
+			$sql .= "', eq_trub_chiller_loss='" . floatval($recipe->EQUIPMENT->TRUB_CHILLER_LOSS);
 			/*
 			 * Brouwhulp uses a percentage for the evaporation rate. This is wrong
 			 * but was made so because the beerxml standard requires this. What we
 			 * do is calculate the actual evaporation and store that.
 			 * This is what we use. Brouwhulp calculates this on the fly.
 			 */
-			$psql .= "', eq_evap_rate='" . (floatval($recipe->EQUIPMENT->EVAP_RATE) * floatval($recipe->EQUIPMENT->BOIL_SIZE)) / 100;
-			$psql .= "', eq_boil_time='" . floatval($recipe->EQUIPMENT->BOIL_TIME);
-			($recipe->EQUIPMENT->CALC_BOIL_VOLUME == 'TRUE') ? $psql .= "', eq_calc_boil_volume='1" : $psql .= "', eq_calc_boil_volume='0";
+			$sql .= "', eq_evap_rate='" . (floatval($recipe->EQUIPMENT->EVAP_RATE) * floatval($recipe->EQUIPMENT->BOIL_SIZE)) / 100;
+			$sql .= "', eq_boil_time='" . floatval($recipe->EQUIPMENT->BOIL_TIME);
+			($recipe->EQUIPMENT->CALC_BOIL_VOLUME == 'TRUE') ? $sql .= "', eq_calc_boil_volume='1" : $sql .= "', eq_calc_boil_volume='0";
 			if ($recipe->EQUIPMENT->TOP_UP_KETTLE)
-				$psql .= "', eq_top_up_kettle='" . floatval($recipe->EQUIPMENT->TOP_UP_KETTLE);
-			$psql .= "', eq_hop_utilization='" . floatval($recipe->EQUIPMENT->HOP_UTILIZATION);
-			$psql .= "', eq_lauter_volume='" . floatval($recipe->EQUIPMENT->LAUTER_VOLUME);
-			$psql .= "', eq_lauter_height='" . floatval($recipe->EQUIPMENT->LAUTER_HEIGHT);
-			$psql .= "', eq_lauter_deadspace='" . floatval($recipe->EQUIPMENT->LAUTER_DEADSPACE);
-			$psql .= "', eq_kettle_volume='" . floatval($recipe->EQUIPMENT->KETTLE_VOLUME);
-			$psql .= "', eq_kettle_height='" . floatval($recipe->EQUIPMENT->KETTLE_HEIGHT);
-			$psql .= "', eq_mash_volume='" . floatval($recipe->EQUIPMENT->MASH_VOLUME);
-			$psql .= "', eq_mash_max='" . round((floatval($recipe->EQUIPMENT->MASH_VOLUME) / 3) * 10) / 10;     // Not in beerxml/brouwhulp. For RIMS systems.
-			$psql .= "', eq_efficiency='" . floatval($recipe->EQUIPMENT->EFFICIENCY);
+				$sql .= "', eq_top_up_kettle='" . floatval($recipe->EQUIPMENT->TOP_UP_KETTLE);
+			$sql .= "', eq_hop_utilization='" . floatval($recipe->EQUIPMENT->HOP_UTILIZATION);
+			$sql .= "', eq_lauter_volume='" . floatval($recipe->EQUIPMENT->LAUTER_VOLUME);
+			$sql .= "', eq_lauter_height='" . floatval($recipe->EQUIPMENT->LAUTER_HEIGHT);
+			$sql .= "', eq_lauter_deadspace='" . floatval($recipe->EQUIPMENT->LAUTER_DEADSPACE);
+			$sql .= "', eq_kettle_volume='" . floatval($recipe->EQUIPMENT->KETTLE_VOLUME);
+			$sql .= "', eq_kettle_height='" . floatval($recipe->EQUIPMENT->KETTLE_HEIGHT);
+			$sql .= "', eq_mash_volume='" . floatval($recipe->EQUIPMENT->MASH_VOLUME);
+			$sql .= "', eq_mash_max='" . round((floatval($recipe->EQUIPMENT->MASH_VOLUME) / 3) * 10) / 10;     // Not in beerxml/brouwhulp. For RIMS systems.
+			$sql .= "', eq_efficiency='" . floatval($recipe->EQUIPMENT->EFFICIENCY);
 		}
 
 		if (($recipe->DATE) && (! $recipe->TIME_STARTED) && (! $recipe->TIME_ENDED)) {
 			/* We have a plan date but haven't brewed yet, use current date */
-			$psql .= "', birth='" . date("Y-m-d");
+			$sql .= "', birth='" . date("Y-m-d");
 			$stageno= 1;
 			$stage = "Wait";
 		}
@@ -1249,74 +1240,74 @@
 			$stageno = 3;
 			$stage = "Primary";	// Need to think about during a brew...
 			$brewdate = substr($recipe->DATE, 6, 4).substr($recipe->DATE,2,4).substr($recipe->DATE,0,2);
-			$psql .= "', birth='" . $brewdate;
+			$sql .= "', birth='" . $brewdate;
 			$date_start = $brewdate.' '.$recipe->TIME_STARTED;
 			$date_end   = $brewdate.' '.$recipe->TIME_ENDED;
-			$psql .= "', brew_date_start='" . $date_start;
+			$sql .= "', brew_date_start='" . $date_start;
 
 			if ($recipe->PH_ADJUSTED)
-				$psql .= "', brew_mash_ph='" . floatval($recipe->PH_ADJUSTED);
+				$sql .= "', brew_mash_ph='" . floatval($recipe->PH_ADJUSTED);
 			if (floatval($recipe->SG_END_MASH) != 1.0)
-				$psql .= "', brew_mash_sg='" . floatval($recipe->SG_END_MASH);
+				$sql .= "', brew_mash_sg='" . floatval($recipe->SG_END_MASH);
 			if ($recipe->MASH->SPARGE_TEMP)
-				$psql .= "', brew_sparge_temperature='" . floatval($recipe->MASH->SPARGE_TEMP);
+				$sql .= "', brew_sparge_temperature='" . floatval($recipe->MASH->SPARGE_TEMP);
 			if ($recipe->MASH->PH)
-				$psql .= "', brew_sparge_ph='" . floatval($recipe->MASH->PH);
+				$sql .= "', brew_sparge_ph='" . floatval($recipe->MASH->PH);
 			if ($recipe->VOLUME_HLT)
-				$psql .= "', brew_sparge_volume='" . floatval($recipe->VOLUME_HLT);
+				$sql .= "', brew_sparge_volume='" . floatval($recipe->VOLUME_HLT);
 			if ($recipe->VOLUME_BEFORE_BOIL)
-				$psql .= "', brew_preboil_volume='" . floatval($recipe->VOLUME_BEFORE_BOIL);
+				$sql .= "', brew_preboil_volume='" . floatval($recipe->VOLUME_BEFORE_BOIL);
 			if (floatval($recipe->OG_BEFORE_BOIL) != 1.0)
-				$psql .= "', brew_preboil_sg='" . floatval($recipe->OG_BEFORE_BOIL);
+				$sql .= "', brew_preboil_sg='" . floatval($recipe->OG_BEFORE_BOIL);
 			if ($recipe->PH_BEFORE_BOIL)
-				$psql .= "', brew_preboil_ph='" . floatval($recipe->PH_BEFORE_BOIL);
+				$sql .= "', brew_preboil_ph='" . floatval($recipe->PH_BEFORE_BOIL);
 			if ($recipe->VOLUME_AFTER_BOIL)
-				$psql .= "', brew_aboil_volume='" . floatval($recipe->VOLUME_AFTER_BOIL);
-			$psql .= "', brew_aboil_sg='" . floatval($recipe->OG);
+				$sql .= "', brew_aboil_volume='" . floatval($recipe->VOLUME_AFTER_BOIL);
+			$sql .= "', brew_aboil_sg='" . floatval($recipe->OG);
 			if ($recipe->PH_AFTER_BOIL)
-				$psql .= "', brew_aboil_ph='" . floatval($recipe->PH_AFTER_BOIL);
+				$sql .= "', brew_aboil_ph='" . floatval($recipe->PH_AFTER_BOIL);
 			if ($recipe->ACTUAL_EFFICIENCY)
-				$psql .= "', brew_aboil_efficiency='" . floatval($recipe->ACTUAL_EFFICIENCY);
-			$psql .= "', brew_whirlpool2='" . floatval($recipe->WHIRLPOOL_TIME);
-			$psql .= "', brew_cooling_method='" . mysqli_real_escape_string($db, $recipe->COOLING_METHOD);
-			$psql .= "', brew_cooling_time='" . floatval($recipe->COOLING_TIME);
-			$psql .= "', brew_cooling_to='" . floatval($recipe->COOLING_TO);
+				$sql .= "', brew_aboil_efficiency='" . floatval($recipe->ACTUAL_EFFICIENCY);
+			$sql .= "', brew_whirlpool2='" . floatval($recipe->WHIRLPOOL_TIME);
+			$sql .= "', brew_cooling_method='" . mysqli_real_escape_string($db, $recipe->COOLING_METHOD);
+			$sql .= "', brew_cooling_time='" . floatval($recipe->COOLING_TIME);
+			$sql .= "', brew_cooling_to='" . floatval($recipe->COOLING_TO);
 			if ($recipe->VOLUME_FERMENTER)
-				$psql .= "', brew_fermenter_volume='" . floatval($recipe->VOLUME_FERMENTER);
+				$sql .= "', brew_fermenter_volume='" . floatval($recipe->VOLUME_FERMENTER);
 			if ($recipe->EQUIPMENT->TOP_UP_WATER_BREWDAY)
-				$psql .= "', brew_fermenter_extrawater='" . floatval($recipe->EQUIPMENT->TOP_UP_WATER_BREWDAY);
-			$psql .= "', brew_fermenter_sg='" . floatval($recipe->OG_FERMENTER);
-			$psql .= "', brew_fermenter_ibu='" . floatval($recipe->IBU);
-			$psql .= "', brew_aeration_type='" . mysqli_real_escape_string($db, $recipe->AERATION_TYPE);
+				$sql .= "', brew_fermenter_extrawater='" . floatval($recipe->EQUIPMENT->TOP_UP_WATER_BREWDAY);
+			$sql .= "', brew_fermenter_sg='" . floatval($recipe->OG_FERMENTER);
+			$sql .= "', brew_fermenter_ibu='" . floatval($recipe->IBU);
+			$sql .= "', brew_aeration_type='" . mysqli_real_escape_string($db, $recipe->AERATION_TYPE);
 			if ($recipe->AERATION_TYPE != "None") {
-				$psql .= "', brew_aeration_speed='" . floatval($recipe->AERATION_SPEED);
-				$psql .= "', brew_aeration_time='" . floatval($recipe->AERATION_TIME);
+				$sql .= "', brew_aeration_speed='" . floatval($recipe->AERATION_SPEED);
+				$sql .= "', brew_aeration_time='" . floatval($recipe->AERATION_TIME);
 			}
-			$psql .= "', brew_date_end='" . $date_end;
+			$sql .= "', brew_date_end='" . $date_end;
 		}
 
 		if ($recipe->PRIMARY_AGE && ($stageno >= 3)) {
 			/* PRIMARY_TEMP is the average of START_TEMP_PRIMARY MAX_TEMP_PRIMARY END_TEMP_PRIMARY */
 			$pdate = new DateTime($brewdate);
 			$pdate->modify("+".floatval($recipe->PRIMARY_AGE)." days");
-			$psql .= "', primary_start_temp='" . floatval($recipe->START_TEMP_PRIMARY);
-			$psql .= "', primary_max_temp='" . floatval($recipe->MAX_TEMP_PRIMARY);
-			$psql .= "', primary_end_temp='" . floatval($recipe->END_TEMP_PRIMARY);
-			$psql .= "', primary_end_sg='" . floatval($recipe->SG_END_PRIMARY);
-			$psql .= "', primary_end_date='" . $pdate->format("Y-m-d");
+			$sql .= "', primary_start_temp='" . floatval($recipe->START_TEMP_PRIMARY);
+			$sql .= "', primary_max_temp='" . floatval($recipe->MAX_TEMP_PRIMARY);
+			$sql .= "', primary_end_temp='" . floatval($recipe->END_TEMP_PRIMARY);
+			$sql .= "', primary_end_sg='" . floatval($recipe->SG_END_PRIMARY);
+			$sql .= "', primary_end_date='" . $pdate->format("Y-m-d");
 			$stageno = 4;
 			$stage = "Secondary";
 
 			if ($recipe->SECONDARY_AGE && ($stageno >= 4)) {
 				$sdate = new DateTime($brewdate);
 				$sdate->modify("+".floatval($recipe->SECONDARY_AGE)." days");
-				$psql .= "', secondary_temp='" . floatval($recipe->SECONDARY_TEMP);
-				$psql .= "', secondary_end_date='" . $sdate->format("Y-m-d");
+				$sql .= "', secondary_temp='" . floatval($recipe->SECONDARY_TEMP);
+				$sql .= "', secondary_end_date='" . $sdate->format("Y-m-d");
 				$stageno = 5;
 				$stage = "Tertiary";
 
 				if ($recipe->TERTIARY_TEMP && ($stageno >= 5)) {
-					$psql .= "', tertiary_temp='" . floatval($recipe->TERTIARY_TEMP);
+					$sql .= "', tertiary_temp='" . floatval($recipe->TERTIARY_TEMP);
 				}
 			}
 		}
@@ -1324,7 +1315,7 @@
 		if ($recipe->DATE_BOTTLING && ($recipe->AMOUNT_BOTTLING || $recipe->AMOUNT_KEGGED) &&
 		    ($recipe->AMOUNT_PRIMING || $recipe->AMOUNT_PRIMING_KEGS) && ($stageno >= 5)) {
 			$bdate = substr($recipe->DATE_BOTTLING, 6, 4).substr($recipe->DATE_BOTTLING,2,4).substr($recipe->DATE_BOTTLING,0,2);
-			$psql .= "', package_date='" . $bdate;
+			$sql .= "', package_date='" . $bdate;
 			$stage = "Package";
 			$stageno = 6;
 			$dStart = new DateTime($bdate);
@@ -1346,61 +1337,54 @@
 			}
 
 			if ($recipe->AMOUNT_BOTTLING && $recipe->AMOUNT_PRIMING) {
-				$psql .= "', bottle_amount='" . floatval($recipe->AMOUNT_BOTTLING);
-				$psql .= "', bottle_carbonation='" . floatval($recipe->CARBONATION);
-				$psql .= "', bottle_priming_sugar='" . mysqli_real_escape_string($db, $recipe->PRIMING_SUGAR_BOTTLES);
-				$psql .= "', bottle_priming_amount='" . floatval($recipe->AMOUNT_PRIMING);
-				$psql .= "', bottle_carbonation_temp='" . floatval($recipe->CARBONATION_TEMP);
+				$sql .= "', bottle_amount='" . floatval($recipe->AMOUNT_BOTTLING);
+				$sql .= "', bottle_carbonation='" . floatval($recipe->CARBONATION);
+				$sql .= "', bottle_priming_sugar='" . mysqli_real_escape_string($db, $recipe->PRIMING_SUGAR_BOTTLES);
+				$sql .= "', bottle_priming_amount='" . floatval($recipe->AMOUNT_PRIMING);
+				$sql .= "', bottle_carbonation_temp='" . floatval($recipe->CARBONATION_TEMP);
 			}
 			if ($recipe->AMOUNT_KEGGED && $recipe->AMOUNT_PRIMING_KEGS) {
-				$psql .= "', keg_amount='" . floatval($recipe->AMOUNT_KEGGED);
-				$psql .= "', keg_carbonation='" . floatval($recipe->CARBONATION); // Lijkt wel hetzelfde als bottles
-				$psql .= "', keg_priming_sugar='" . mysqli_real_escape_string($db, $recipe->PRIMING_SUGAR_KEGS);
-				$psql .= "', keg_priming_amount='" . floatval($recipe->AMOUNT_PRIMING_KEGS);
-				$psql .= "', keg_carbonation_temp='" . floatval($recipe->KEG_CARB_TEMP);
-				($recipe->FORCED_CARB_KEGS == 'TRUE') ? $psql .= "', keg_forced_carb='1" : $psql .= "', keg_forced_carb='0";
-				$psql .= "', keg_pressure='" . floatval($recipe->KEG_PRESSURE);
-				$psql .= "', keg_priming_factor='" . floatval($recipe->KEG_PRIMING_FACTOR);
+				$sql .= "', keg_amount='" . floatval($recipe->AMOUNT_KEGGED);
+				$sql .= "', keg_carbonation='" . floatval($recipe->CARBONATION); // Lijkt wel hetzelfde als bottles
+				$sql .= "', keg_priming_sugar='" . mysqli_real_escape_string($db, $recipe->PRIMING_SUGAR_KEGS);
+				$sql .= "', keg_priming_amount='" . floatval($recipe->AMOUNT_PRIMING_KEGS);
+				$sql .= "', keg_carbonation_temp='" . floatval($recipe->KEG_CARB_TEMP);
+				($recipe->FORCED_CARB_KEGS == 'TRUE') ? $sql .= "', keg_forced_carb='1" : $sql .= "', keg_forced_carb='0";
+				$sql .= "', keg_pressure='" . floatval($recipe->KEG_PRESSURE);
+				$sql .= "', keg_priming_factor='" . floatval($recipe->KEG_PRIMING_FACTOR);
 			}
 		}
 
 		if ($recipe->TASTE_NOTES && $recipe->TASTING_RATE && $recipe->TASTE_DATE && ($stageno >= 9)) {
 			$stage = "Ready";	// Ready if tasted.
 			$stageno = 10;
-			$psql .= "', taste_notes='" . mysqli_real_escape_string($db, $recipe->TASTE_NOTES);
-			$psql .= "', taste_rate='" . floatval($recipe->TASTING_RATE);
+			$sql .= "', taste_notes='" . mysqli_real_escape_string($db, $recipe->TASTE_NOTES);
+			$sql .= "', taste_rate='" . floatval($recipe->TASTING_RATE);
 			$tdate = substr($recipe->TASTE_DATE, 6, 4).substr($recipe->TASTE_DATE,2,4).substr($recipe->TASTE_DATE,0,2);
-			$psql .= "', taste_date='" . $tdate;
-			$psql .= "', taste_color='" . mysqli_real_escape_string($db, $recipe->TASTE_COLOR);
-			$psql .= "', taste_transparency='" . mysqli_real_escape_string($db, $recipe->TASTE_TRANSPARENCY);
-			$psql .= "', taste_head='" . mysqli_real_escape_string($db, $recipe->TASTE_HEAD);
-			$psql .= "', taste_aroma='" . mysqli_real_escape_string($db, $recipe->TASTE_AROMA);
-			$psql .= "', taste_taste='" . mysqli_real_escape_string($db, $recipe->TASTE_TASTE);
-			$psql .= "', taste_mouthfeel='" . mysqli_real_escape_string($db, $recipe->TASTE_MOUTHFEEL);
-			$psql .= "', taste_aftertaste='" . mysqli_real_escape_string($db, $recipe->TASTE_AFTERTASTE);
+			$sql .= "', taste_date='" . $tdate;
+			$sql .= "', taste_color='" . mysqli_real_escape_string($db, $recipe->TASTE_COLOR);
+			$sql .= "', taste_transparency='" . mysqli_real_escape_string($db, $recipe->TASTE_TRANSPARENCY);
+			$sql .= "', taste_head='" . mysqli_real_escape_string($db, $recipe->TASTE_HEAD);
+			$sql .= "', taste_aroma='" . mysqli_real_escape_string($db, $recipe->TASTE_AROMA);
+			$sql .= "', taste_taste='" . mysqli_real_escape_string($db, $recipe->TASTE_TASTE);
+			$sql .= "', taste_mouthfeel='" . mysqli_real_escape_string($db, $recipe->TASTE_MOUTHFEEL);
+			$sql .= "', taste_aftertaste='" . mysqli_real_escape_string($db, $recipe->TASTE_AFTERTASTE);
 		}
 
-		($recipe->INVENTORY_REDUCED == 'TRUE') ? $psql .= "', inventory_reduced='1" : $psql .= "', inventory_reduced='0";
+		($recipe->INVENTORY_REDUCED == 'TRUE') ? $sql .= "', inventory_reduced='1" : $sql .= "', inventory_reduced='0";
 		if (($recipe->LOCKED == 'TRUE') && ($stage == 'Ready')) {
-			$psql .= "', plocked='1";
-			$rsql .= "', locked='1";
+			$sql .= "', locked='1";
 			$stage = "Closed";
 			$stageno = 11;
 		} else {
-			$psql .= "', plocked='0";
-			$rsql .= "', locked='0";
+			$sql .= "', locked='0";
 		}
 
-		$psql .= "', stage='" . $stage;
+		$sql .= "', stage='" . $stage;
 //		echo ' '.$brewdate.' '.$recipe->NR_RECIPE.' '.$stage . PHP_EOL;
 
-		$rsql .= "';";
-		if (! $rresult = mysqli_query($db, $rsql)) {
-			printf("Error: %s\n", mysqli_error($db));
-		}
-
-		$psql .= "';";
-		if (! $presult = mysqli_query($db, $psql)) {
+		$sql .= "';";
+		if (! $rresult = mysqli_query($db, $sql)) {
 			printf("Error: %s\n", mysqli_error($db));
 		}
         }
--- a/www/includes/db_product.php	Mon Dec 24 15:52:11 2018 +0100
+++ b/www/includes/db_product.php	Mon Dec 24 23:10:52 2018 +0100
@@ -12,25 +12,55 @@
 
 $escapers = array("\\", "/", "\"", "\n", "\r", "\t", "\x08", "\x0c");
 $replacements = array("\\\\", "\\/", "\\\"", "\\n", "\\r", "\\t", "\\f", "\\b");
-
+$rescapers = array("'");
+$rreplacements = array("\\'");
+$disallowed = array('visibleindex','uniqueid','boundindex','uid','h_weight','m_weight');
 
 if (isset($_POST['insert']) || isset($_POST['update'])) {
 	if (isset($_POST['insert'])) {
-		$sql  = "INSERT INTO `prod_main` SET ";
+		$sql  = "INSERT INTO `products` SET ";
 	}
 	if (isset($_POST['update'])) {
-		$sql  = "UPDATE `prod_main` SET ";
+		$sql  = "UPDATE `products` SET ";
 	}
+
+	$stage = $_POST['stage'];
+	if ($stage == 'Plan')
+		$stageno = 0;
+	else if ($stage == 'Wait')
+		$stageno = 1;
+	else if ($stage == 'Brew')
+		$stageno = 2;
+	else if ($stage == 'Primary')
+		$stageno = 3;
+	else if ($stage == 'Secondary')
+		$stageno = 4;
+	else if ($stage == 'Tertiary')
+		$stageno = 5;
+	else if ($stage == 'Package')
+		$stageno = 6;
+	else if ($stage == 'Carbonation')
+		$stageno = 7;
+	else if ($stage == 'Mature')
+		$stageno = 8;
+	else if ($stage == 'Taste')
+		$stageno = 9;
+	else if ($stage == 'Ready')
+		$stageno = 10;
+	else if ($stage == 'Closed')
+		$stageno = 11;
+
 	// Basic settings
-	$sql .=    "puuid='" . $_POST['puuid'];
-	$sql .= "', pname='" . mysqli_real_escape_string($connect, $_POST['pname']);
+	$sql .=    "uuid='" . $_POST['uuid'];
+	$sql .= "', name='" . mysqli_real_escape_string($connect, $_POST['name']);
+	$sql .= "', code='" . mysqli_real_escape_string($connect, $_POST['code']);
 	$sql .= "', birth='" . $_POST['birth'];
 	$sql .= "', stage='" . $_POST['stage'];
-	$sql .= "', pnotes='" . mysqli_real_escape_string($connect, $_POST['pnotes']);
+	$sql .= "', notes='" . mysqli_real_escape_string($connect, $_POST['notes']);
 	($_POST['log_brew'] == 'true') ? $sql .= "', log_brew='1" : $sql .= "', log_brew='0";
 	($_POST['log_fermentation'] == 'true') ? $sql .= "', log_fermentation='1" : $sql .= "', log_fermentation='0";
 	($_POST['inventory_reduced'] == 'true') ? $sql .= "', inventory_reduced='1" : $sql .= "', inventory_reduced='0";
-	($_POST['plocked'] == 'true') ? $sql .= "', plocked='1" : $sql .= "', plocked='0";
+	($_POST['locked'] == 'true') ? $sql .= "', locked='1" : $sql .= "', locked='0";
 	// Equipment
 	$sql .= "', eq_name='" . mysqli_real_escape_string($connect, $_POST['eq_name']);
 	$sql .= "', eq_boil_size='" . $_POST['eq_boil_size'];
@@ -57,6 +87,7 @@
 	$sql .= "', eq_mash_max='" . $_POST['eq_mash_max'];
 	$sql .= "', eq_efficiency='" . $_POST['eq_efficiency'];
 
+	if ($stageno >= 2) {
 	// brew_date_start
 	// brew_mash_ph
 	// brew_mash_sg
@@ -86,14 +117,26 @@
 	// brew_fermenter_ibu
 	// brew_date_end
 	// brew_log_available
+	}
+
+	if ($stageno >= 3) {
 	// primary_start_temp
 	// primary_max_temp
 	// primary_end_temp
 	// primary_end_sg
-	// primary_end_date
+		// primary_end_date
+	}
+
+	if ($stageno >= 4) {
 	// secondary_temp
-	// secondary_end_date
-	// tertiary_temp
+		// secondary_end_date
+	}
+
+	if ($stageno >= 5) {
+		// tertiary_temp
+	}
+
+	if ($stageno >= 6) {
 	// package_date
 	// bottle_amount
 	// bottle_carbonation
@@ -107,7 +150,10 @@
 	// keg_carbonation_temp
 	// keg_forced_carb
 	// keg_pressure
-	// keg_priming_factor
+		// keg_priming_factor
+	}
+
+	if ($stageno >= 9) {
 	// taste_notes
 	// taste_rate
 	// taste_date
@@ -117,8 +163,133 @@
 	// taste_aroma
 	// taste_taste
 	// taste_mouthfeel
-	// taste_aftertaste
+		// taste_aftertaste
+	}
+
+	/*
+	 * Recipe part
+	 */
+//	$sql .= "', st_name='" . mysqli_real_escape_string($connect, $_POST['st_name']);
+//	$sql .= "', st_letter='" . mysqli_real_escape_string($connect, $_POST['st_letter']);
+//	$sql .= "', st_guide='" . mysqli_real_escape_string($connect, $_POST['st_guide']);
+//	$sql .= "', st_type='" . mysqli_real_escape_string($connect, $_POST['st_type']);
+//	$sql .= "', st_category='" . mysqli_real_escape_string($connect, $_POST['st_category']);
+//	$sql .= "', st_category_number='" . $_POST['st_category_number'];
+	$sql .= "', st_og_min='" . $_POST['st_og_min'];
+	$sql .= "', st_og_max='" . $_POST['st_og_max'];
+	$sql .= "', st_fg_min='" . $_POST['st_fg_min'];
+	$sql .= "', st_fg_max='" . $_POST['st_fg_max'];
+	$sql .= "', st_ibu_min='" . $_POST['st_ibu_min'];
+	$sql .= "', st_ibu_max='" . $_POST['st_ibu_max'];
+	$sql .= "', st_color_min='" . $_POST['st_color_min'];
+	$sql .= "', st_color_max='" . $_POST['st_color_max'];
+	$sql .= "', st_carb_min='" . $_POST['st_carb_min'];
+	$sql .= "', st_carb_max='" . $_POST['st_carb_max'];
+	$sql .= "', st_abv_min='" . $_POST['st_abv_min'];
+	$sql .= "', st_abv_max='" . $_POST['st_abv_max'];
+	$sql .= "', type='" . $_POST['type'];
+	$sql .= "', batch_size='" . $_POST['batch_size'];
+	$sql .= "', boil_size='" . $_POST['boil_size'];
+	$sql .= "', boil_time='" . $_POST['boil_time'];
+	$sql .= "', efficiency='" . $_POST['efficiency'];
+	$sql .= "', est_og='" . $_POST['est_og'];
+	$sql .= "', est_fg='" . $_POST['est_fg'];
+	$sql .= "', est_abv='" . $_POST['est_abv'];
+	$sql .= "', est_carb='" . $_POST['est_carb'];
+	$sql .= "', est_color='" . $_POST['est_color'];
+	$sql .= "', color_method='" . $_POST['color_method'];
+	$sql .= "', est_ibu='" . $_POST['est_ibu'];
+	$sql .= "', ibu_method='" . $_POST['ibu_method'];
+	$sql .= "', sparge_temp='" . $_POST['sparge_temp'];
+	$sql .= "', sparge_ph='" . $_POST['sparge_ph'];
+	$sql .= "', sparge_volume='" . $_POST['sparge_volume'];
+//	$sql .= "', sparge_acid_type='" . $_POST['sparge_acid_type'];
+//	$sql .= "', sparge_acid_perc='" . $_POST['sparge_acid_perc'];
+//	$sql .= "', sparge_acid_amount='" . $_POST['sparge_acid_amount'];
+	$sql .= "', mash_ph='" . $_POST['mash_ph'];
+	$sql .= "', mash_name='" . $_POST['mash_name'];
+	$sql .= "', calc_acid='" . $_POST['calc_acid'];
+	if (isset($_POST['w1_name'])) {
+		$sql .= "', w1_name='" . mysqli_real_escape_string($connect, $_POST['w1_name']);
+		$sql .= "', w1_amount='" . $_POST['w1_amount'];
+		$sql .= "', w1_calcium='" . $_POST['w1_calcium'];
+		$sql .= "', w1_sulfate='" . $_POST['w1_sulfate'];
+		$sql .= "', w1_chloride='" . $_POST['w1_chloride'];
+		$sql .= "', w1_sodium='" . $_POST['w1_sodium'];
+		$sql .= "', w1_magnesium='" . $_POST['w1_magnesium'];
+		$sql .= "', w1_total_alkalinity='" . $_POST['w1_total_alkalinity'];
+		$sql .= "', w1_ph='" . $_POST['w1_ph'];
+		$sql .= "', w1_cost='" . $_POST['w1_cost'];
+	}
+	if (isset($_POST['w2_name'])) {
+		$sql .= "', w2_name='" . mysqli_real_escape_string($connect, $_POST['w2_name']);
+		$sql .= "', w2_amount='" . $_POST['w2_amount'];
+		$sql .= "', w2_calcium='" . $_POST['w2_calcium'];
+		$sql .= "', w2_sulfate='" . $_POST['w2_sulfate'];
+		$sql .= "', w2_chloride='" . $_POST['w2_chloride'];
+		$sql .= "', w2_sodium='" . $_POST['w2_sodium'];
+		$sql .= "', w2_magnesium='" . $_POST['w2_magnesium'];
+		$sql .= "', w2_total_alkalinity='" . $_POST['w2_total_alkalinity'];
+		$sql .= "', w2_ph='" . $_POST['w2_ph'];
+		$sql .= "', w2_cost='" . $_POST['w2_cost'];
+	}
 	syslog(LOG_NOTICE, $sql);
+
+	if (isset($_POST['fermentables'])) {
+		$array = $_POST['fermentables'];
+		foreach($array as $key => $item){
+			foreach ($disallowed as $disallowed_key) {
+				unset($array[$key]["$disallowed_key"]);
+			}
+		}
+		syslog(LOG_NOTICE, "json_fermentables=: ".str_replace($rescapers,$rreplacements,json_encode($array)));
+		$sql .= "', json_fermentables='" . str_replace($rescapers,$rreplacements,json_encode($array));
+	}
+
+	if (isset($_POST['hops'])) {
+		$array = $_POST['hops'];
+		foreach($array as $key => $item){
+			foreach ($disallowed as $disallowed_key) {
+				unset($array[$key]["$disallowed_key"]);
+			}
+		}
+		syslog(LOG_NOTICE, "json_hops: ".str_replace($rescapers,$rreplacements,json_encode($array)));
+		$sql .= "', json_hops='" . str_replace($rescapers,$rreplacements,json_encode($array));
+	}
+
+	if (isset($_POST['miscs'])) {
+		$array = $_POST['miscs'];
+		foreach($array as $key => $item){ 
+			foreach ($disallowed as $disallowed_key) {
+				unset($array[$key]["$disallowed_key"]);
+			}
+		}
+		syslog(LOG_NOTICE, "json_miscs: ".str_replace($rescapers,$rreplacements,json_encode($array)));
+		$sql .= "', json_miscs='" . str_replace($rescapers,$rreplacements,json_encode($array));
+	}
+
+	if (isset($_POST['yeasts'])) {
+		$array = $_POST['yeasts'];
+		foreach($array as $key => $item){
+			foreach ($disallowed as $disallowed_key) {
+				unset($array[$key]["$disallowed_key"]);
+			}
+		}
+		syslog(LOG_NOTICE, "json_yeasts: ". str_replace($rescapers,$rreplacements,json_encode($array)));
+		$sql .= "', json_yeasts='" . str_replace($rescapers,$rreplacements,json_encode($array));
+	}
+
+	if (isset($_POST['mashs'])) {
+		$array = $_POST['mashs'];
+		foreach($array as $key => $item){
+			foreach ($disallowed as $disallowed_key) {
+				unset($array[$key]["$disallowed_key"]);
+			}
+		}
+		syslog(LOG_NOTICE, "json_mashs: ".str_replace($rescapers,$rreplacements,json_encode($array)));
+		$sql .= "', json_mashs='" . str_replace($rescapers,$rreplacements,json_encode($array));
+	}
+
 	if (isset($_POST['insert'])) {
 		$sql .= "';";
 	}
@@ -144,17 +315,9 @@
 
 } else if (isset($_POST['delete'])) {
 	/*
-	 * DELETE command, first delete the recipe and then the product.
+	 * DELETE command.
 	 */
-	$sql = "DELETE FROM `prod_recipes` WHERE uuid='".$_POST['uuid']."';";
-	$result = mysqli_query($connect, $sql);
-	if (! $result) {
-		syslog(LOG_NOTICE, "db_product: ".$sql." result: ".mysqli_error($connect));
-	} else {
-		syslog(LOG_NOTICE, "db_product: deleted recipe uuid ".$_POST['uuid']);
-	}
-
-	$sql = "DELETE FROM `prod_main` WHERE puuid='".$_POST['uuid']."';";
+	$sql = "DELETE FROM `products` WHERE uuid='".$_POST['uuid']."';";
 	$result = mysqli_query($connect, $sql);
 	if (! $result) {
 		syslog(LOG_NOTICE, "db_product: ".$sql." result: ".mysqli_error($connect));
@@ -168,12 +331,12 @@
 	 * SELECT, produce a list of products that are not yet Closed.
 	 */
 	if (isset($_GET['select']) && ($_GET['select'] == "inprod")) {
-		$query = "SELECT record,pname,code,birth,stage FROM prod_main WHERE stage != 'Closed' ORDER BY birth,code;";
+		$query = "SELECT record,name,code,birth,stage FROM products WHERE stage != 'Closed' ORDER BY birth,code;";
 		$result = mysqli_query($connect, $query) or die("SQL Error 1: " . mysqli_error($connect));
 		while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
 			$brews[] = array(
 				'record' => $row['record'],
-				'pname' => $row['pname'],
+				'name' => $row['name'],
 				'code' => $row['code'],
 				'birth' => $row['birth'],
 				'stage' => $row['stage']
@@ -187,10 +350,9 @@
 	 * Default, select all or a given record.
 	 */
 	if (isset($_GET['record'])) {
-		$query  = "SELECT * FROM prod_main LEFT JOIN prod_recipes ON puuid = uuid WHERE prod_main.record='";
-		$query .= $_GET['record'] . "';";
+		$query  = "SELECT * FROM products WHERE record='" . $_GET['record'] . "';";
 	} else {
-		$query = "SELECT * FROM prod_main LEFT JOIN prod_recipes ON puuid = uuid ORDER BY birth,code;";
+		$query = "SELECT * FROM products ORDER BY birth,code;";
 	}
 	$result = mysqli_query($connect, $query) or die("SQL Error 1: " . mysqli_error($connect));
 	$brews = '[';
@@ -201,16 +363,16 @@
 			$brews .= ',';
 		$comma = TRUE;
 		$brew  = '{"record":' . $row['record'];
-		$brew .= ',"puuid":"' . str_replace($escapers, $replacements, $row['puuid']);
-		$brew .= '","pname":"' . str_replace($escapers, $replacements, $row['pname']);
+		$brew .= ',"uuid":"' . str_replace($escapers, $replacements, $row['uuid']);
+		$brew .= '","name":"' . str_replace($escapers, $replacements, $row['name']);
 		$brew .= '","code":"' . str_replace($escapers, $replacements, $row['code']);
 		$brew .= '","birth":"' . str_replace($escapers, $replacements, $row['birth']);
 		$brew .= '","stage":"' . str_replace($escapers, $replacements, $row['stage']);
-		$brew .= '","pnotes":"' . str_replace($escapers, $replacements, $row['pnotes']);
+		$brew .= '","notes":"' . str_replace($escapers, $replacements, $row['notes']);
 		$brew .= '","log_brew":' . $row['log_brew'];
 		$brew .= ',"log_fermentation":' . $row['log_fermentation'];
 		$brew .= ',"inventory_reduced":' . $row['inventory_reduced'];
-		$brew .= ',"plocked":' . $row['plocked'];
+		$brew .= ',"locked":' . $row['locked'];
 		$brew .= ',"eq_name":"' . str_replace($escapers, $replacements, $row['eq_name']);
 		$brew .= '","eq_notes":"' . str_replace($escapers, $replacements, $row['eq_notes']);
 		$brew .= '","eq_boil_size":' . floatval($row['eq_boil_size']);
@@ -316,8 +478,7 @@
 		$brew .= ',"st_carb_max":' . floatval($row['st_carb_max']);
 		$brew .= ',"st_abv_min":' . floatval($row['st_abv_min']);
 		$brew .= ',"st_abv_max":' . floatval($row['st_abv_max']);
-		$brew .= ',"notes":"' . str_replace($escapers, $replacements, $row['notes']);
-		$brew .= '","type":"' . $row['type'];
+		$brew .= ',"type":"' . $row['type'];
 		$brew .= '","batch_size":' . floatval($row['batch_size']);
 		$brew .= ',"boil_size":' . floatval($row['boil_size']);
 		$brew .= ',"boil_time":' . floatval($row['boil_time']);
@@ -365,6 +526,7 @@
 		$brew .= ',"yeasts":' . $row['json_yeasts'];
 		$brew .= ',"mashs":' . $row['json_mashs'];
 		$brew .= '}';
+//		syslog(LOG_NOTICE, $brew);
 		$brews .= $brew;
 	}
 	$brews .= ']';
--- a/www/js/prod_edit.js	Mon Dec 24 15:52:11 2018 +0100
+++ b/www/js/prod_edit.js	Mon Dec 24 23:10:52 2018 +0100
@@ -53,11 +53,42 @@
 	var     sugarsf = 0;    // Sugars after boil
 	var     psugar = 0;     // Percentage real sugars
 	var     pcara = 0;      // Percentage cara/crystal malts
+	var     svg = 77;               // Default attenuation
+        var     mashkg = 0;             // Malt in mash weight
+        var     hop_flavour = 0;
+        var     hop_aroma = 0;
+        var     mash_infuse = 0;
+        var     last_base = '';
+        var     last_acid = '';
+
+        var     MMCa = 40.048;
+        var     MMMg = 24.305;
+        var     MMNa = 22.98976928;
+        var     MMCl = 35.453;
+        var     MMSO4 = 96.0626;
+        var     MMCO3 = 60.01684;
+        var     MMHCO3 = 61.01684;
+        var     MMCaSO4 = 172.171;
+        var     MMCaCl2 = 147.015;
+        var     MMCaCO3 = 100.087;
+        var     MMMgSO4 = 246.475;
+        var     MMNaHCO3 = 84.007;
+        var     MMNa2CO3 = 105.996;
+        var     MMNaCl = 58.443;
+        var     MMCaOH2 = 74.06268;
+
 	var	old_efficiency;
 	var	old_batch_size;
 	var	old_boil_time;
 
 	console.log("record:" + my_record + "  return:" + my_return + "  theme:" + theme);
+        $("#jqxLoader").jqxLoader({
+                width: 250,
+                height: 150,
+                isModal: true,
+                text: "Laden product ...",
+                theme: theme
+        });
 
 	function calcFermentables() {
 		console.log("calcFermentables()");
@@ -65,29 +96,43 @@
 		sugarsm = 0;
 		psugar = 0;
 		pcara = 0;
+		mashkg = 0;
 		var colorw = 0; // Colors working
+		var my_100 = false;
 
-		for (var i = 0; i < dataRecord.fermentables.length; i++) {
-			var row = dataRecord.fermentables[i];
+                var rows = $('#fermentableGrid').jqxGrid('getrows');
+                for (var i = 0; i < rows.length; i++) {
+                        var row = rows[i];
+                        if (row.f_adjust_to_total_100)
+                                my_100 = true;
 			if (row.f_type == "Sugar")
 				psugar += row.f_percentage;
-			if (row.f_type == "Crystal")
+			if (row.f_graintype == "Crystal")
 				pcara += row.f_percentage;
 			var d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
 			if (row.f_added == "Mash") {
 				d = parseFloat(dataRecord.efficiency) / 100 * d;
 				sugarsm += d;
+				mashkg += row.f_amount;
 			}
 			sugarsf += d;
 			colorw += row.f_amount * ebc_to_srm(row.f_color) / parseFloat(dataRecord.batch_size) * 8.34436;
 		}
-		console.log("sugarsm: " + sugarsm + "  sugarsf: " + sugarsf + "  batch: " + dataRecord.batch_size);
-		console.log("est_og: " + estimate_sg(sugarsf, parseFloat(dataRecord.batch_size)));
+		to_100 = my_100;
+		var est_og = estimate_sg(sugarsf, parseFloat(dataRecord.batch_size));
+                $('#est_og').val(est_og);
+                $('#est_og2').val(est_og);
 		preboil_sg = estimate_sg(sugarsm, parseFloat(dataRecord.boil_size));
-		console.log("preboil_sg: " + preboil_sg);
-		console.log("est_color: " + kw_to_ebc(dataRecord.color_method, colorw));
-		$("#est_og").val(estimate_sg(sugarsf, parseFloat(dataRecord.batch_size)));
-		$('#est_color').val(kw_to_ebc(dataRecord.color_method, colorw));
+		var color = kw_to_ebc(dataRecord.color_method, colorw);
+                $('#est_color').val(color);
+                $('#est_color2').val(color);
+                var scolor = ebc_to_color(color);
+                document.getElementById("bcolor").style.background= scolor;
+                document.getElementById("bcolor2").style.background= scolor;
+                pmalts = mashkg / dataRecord.eq_mash_max * 100;
+                $("#perc_malts").jqxProgressBar('val', pmalts);
+                $("#perc_sugars").jqxProgressBar('val', psugar);
+                $("#perc_cara").jqxProgressBar('val', pcara);
 	};
 
 	function calcFermentablesFromOG(OG) {
@@ -100,7 +145,7 @@
 		for (j = 1; j < 15; j++) {      // Maybe needed if there is equipment, not here.
 			vol = 0;
 			sug2 = 0;
-			efficiency = parseFloat($("#efficiency").jqxNumberInput('decimal'));
+			efficiency = parseFloat(dataRecord.efficiency);
 			var rows = $('#fermentableGrid').jqxGrid('getrows');
 			for (i = 0; i < rows.length; i++) {
 				row = rows[i];
@@ -150,15 +195,64 @@
 		//CalcWaterBalance;
 	};
 
+	function hopFlavourContribution(bt, vol, use, amount) {
+		var result;
+
+		if ((use == "First Wort") || (use == "First wort")) {
+			result = 0.15;          // assume 15% flavourcontribution for fwh
+		} else if (bt > 50) {
+			result = 0.10;          // assume 10% flavourcontribution as a minimum
+		} else {
+			result = 15.25 / (6 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 21) /6, 2));
+			if (result < 0.10)
+				result = 0.10;  // assume 10% flavourcontribution as a minimum
+		}
+		result = (result * amount * 1000) / vol;
+	//      console.log("hopFlavourContribution("+bt+","+vol+","+use+","+amount+"): "+result);
+		return result;
+	}
+
+	function hopAromaContribution(bt, vol, use, amount) {
+		var result = 0;
+
+		if (bt > 20) {
+			result = 0;
+		} else if (bt > 7.5) {
+			result = 10.03 / (4 * Math.sqrt(2 * Math.PI)) * Math.exp(-0.5 * Math.pow((bt - 7.5) /4, 2));
+		} else if (use == "Boil") {
+			result = 1;
+		} else if (use == "Aroma") {
+			result = 1.2;
+		} else if (use == "Whirlpool") {
+			result = 1.2;
+		} else if ((use == "Dry Hop") || (use == "Dry hop")) {
+			result = 1.33;
+		}
+		result = (result * amount * 1000) / vol;
+	//      console.log("hopAromaContribution("+bt+","+vol+","+use+","+amount+"): "+result);
+		return result;
+	}
+
 	function calcIBUs() {
 		var total_ibus = 0;
-		for (var i = 0; i < dataRecord.hops.length; i++) {
-			var row = dataRecord.hops[i];
+		hop_aroma = hop_flavour = 0;
+		var rows = $('#hopGrid').jqxGrid('getrows');
+		for (var i = 0; i < rows.length; i++) {
+			var row = rows[i];
 			total_ibus += toIBU(row.h_useat, row.h_form, preboil_sg, parseFloat(dataRecord.batch_size),
 					parseFloat(row.h_amount), parseFloat(row.h_time), parseFloat(row.h_alpha), dataRecord.ibu_method);
+			hop_flavour += hopFlavourContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size),
+					row.h_useat, parseFloat(row.h_amount));
+			hop_aroma += hopAromaContribution(parseFloat(row.h_time), parseFloat(dataRecord.batch_size),
+					row.h_useat, parseFloat(row.h_amount));
 		}
-		console.log("calcIBUs(): " + total_ibus);
+		total_ibus = Math.round(total_ibus);
+		console.log("calcIBUs(): " + total_ibus + "  flavour: " + hop_flavour + "  aroma: " + hop_aroma);
+		dataRecord.est_ibu = total_ibus;
 		$('#est_ibu').val(total_ibus);
+		$('#est_ibu2').val(total_ibus);
+		$("#hop_flavour").jqxProgressBar('val', hop_flavour * 10);
+		$("#hop_aroma").jqxProgressBar('val', hop_aroma * 10);
 	};
 
 	function calcSGendMash() {
@@ -166,17 +260,18 @@
 		var	mvol = 0;	// Mash volume
 		var	s = 0;
 		var	gs = 0;		// Grain absorbtion
-		for (var i = 0; i < dataRecord.mashs.length; i++) {
-			var row = dataRecord.mashs[i];
+		var rows = $('#mashGrid').jqxGrid('getrows');
+		for (var i = 0; i < rows.length; i++) {
+			var row = rows[i];
 //			console.log("step " + i + " " + row.step_name + " " + row.step_type);
 			if (row.step_type == 'Infusion')
 				mvol += parseFloat(row.step_infuse_amount);
 		}
 		if (mvol > 0) {
 //			console.log("mash volume: " + mvol);
-			for (var i = 0; i < dataRecord.fermentables.length; i++) {
-				var row = dataRecord.fermentables[i];
-
+			var rows = $('#fermentableGrid').jqxGrid('getrows');
+			for (var i = 0; i < rows.length; i++) {
+			        var row = rows[i];
 				if (row.f_added == "Mash") {
 					var d = row.f_amount * (row.f_yield / 100) * (1 - row.f_moisture / 100);
 					mvol += row.f_amount * row.f_moisture / 100;
@@ -204,6 +299,14 @@
 			$("#brew_mash_efficiency").val(0);
 	};
 
+	function calcInit () {
+		console.log("calc.init()");
+
+		calcSGendMash();
+		calcMashEfficiency();
+
+	};
+
 	// Equipemnt dropdown list
 	var equipmentUrl = "includes/db_inventory_equipments.php";
 	var equipmentSource = {
@@ -284,11 +387,11 @@
 	var dataRecord = {};
 	var url = "includes/db_product.php";
 	// tooltips
-	$("#pname").jqxTooltip({ content: 'De naam voor dit product.' });
+	$("#name").jqxTooltip({ content: 'De naam voor dit product.' });
 	$("#code").jqxTooltip({ content: 'Product code nummer.' });
 	$("#birth").jqxTooltip({ content: 'De ontwerp datum van dit product.' });
 	$("#stage").jqxTooltip({ content: 'De productie fase van dit product.' });
-	$("#pnotes").jqxTooltip({ content: 'De uitgebreide opmerkingen over dit product.' });
+	$("#notes").jqxTooltip({ content: 'De uitgebreide opmerkingen over dit product.' });
 	$("#eq_name").jqxTooltip({ content: 'De naam van deze brouw apparatuur.' });
 	$("#eq_notes").jqxTooltip({ content: 'Opmerkingen over deze apparatuur.' });
 	$("#eq_tun_volume").jqxTooltip({ content: 'Maisch ketel volume.' });
@@ -305,6 +408,48 @@
 	$("#eq_hop_utilization").jqxTooltip({ content: '100% voor kleine installaties, hoger voor grote brouwerijen.' });
 	$("#eq_batch_size").jqxTooltip({ content: 'Berekende batch grootte in liters aan het eind van de kook.' });
 	$("#eq_trub_chiller_loss").jqxTooltip({ content: 'Standaard verlies bij het overbrengen naar het gistvat.' });
+	$("#type").jqxTooltip({ content: 'Het brouw type van dit recept.' });
+	$("#batch_size").jqxTooltip({ content: 'Het volume van het gekoelde wort na het koken.' });
+	$("#boil_time").jqxTooltip({ content: 'De kooktijd in minuten.' });
+	$("#boil_size").jqxTooltip({ content: 'Het volume van het wort voor het koken.' });
+	$("#efficiency").jqxTooltip({ content: 'Het rendement van maischen en koken.' });
+	$("#est_og").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
+	$("#est_og2").jqxTooltip({ content: 'Het begin SG wat je wilt bereiken. De moutstort wordt automatisch herberekend.' });
+	$("#est_fg").jqxTooltip({ content: 'Het eind SG. Dit wordt automatisch berekend.' });
+	$("#est_color").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
+	$("#est_color2").jqxTooltip({ content: 'De kleur in EBC. Dit wordt automatisch berekend.' });
+	$("#est_ibu").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
+	$("#est_ibu2").jqxTooltip({ content: 'De bitterheid in IBU. Dit wordt automatisch berekend.' });
+	$("#est_abv").jqxTooltip({ content: 'Alcohol volume %. Dit wordt automatisch berekend.' });
+	$("#est_carb").jqxTooltip({ content: 'Koolzuur volume. Dit wordt automatisch berekend.' });
+//	$("#st_name").jqxTooltip({ content: 'De bierstijl naam voor dit recept.'});
+//	$("#st_letter").jqxTooltip({ content: 'De bierstijl letter voor dit recept.'});
+//	$("#st_guide").jqxTooltip({ content: 'De bierstijl gids voor dit recept.'});
+//	$("#st_category").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie.'});
+//	$("#st_category_number").jqxTooltip({ content: 'De Amerikaanse bierstijl categorie sub nummer.'});
+//	$("#st_type").jqxTooltip({ content: 'Het bierstijl type.'});
+	$("#st_og_min").jqxTooltip({ content: 'Het minimum begin SG voor deze bierstijl.'});
+	$("#st_og_max").jqxTooltip({ content: 'Het maximum begin SG voor deze bierstijl.'});
+	$("#st_fg_min").jqxTooltip({ content: 'Het minimum eind SG voor deze bierstijl.'});
+	$("#st_fg_max").jqxTooltip({ content: 'Het maximum eind SG voor deze bierstijl.'});
+	$("#st_color_min").jqxTooltip({ content: 'De minimum kleur voor deze bierstijl.'});
+	$("#st_color_max").jqxTooltip({ content: 'De maximum kleur voor deze bierstijl.'});
+	$("#st_ibu_min").jqxTooltip({ content: 'De minimum bitterheid voor deze bierstijl.'});
+	$("#st_ibu_max").jqxTooltip({ content: 'De maximum bitterheid voor deze bierstijl.'});
+	$("#st_abv_min").jqxTooltip({ content: 'Het minimum alcohol volume % voor deze bierstijl.'});
+	$("#st_abv_max").jqxTooltip({ content: 'Het maximum alcohol volume % voor deze bierstijl.'});
+	$("#st_carb_min").jqxTooltip({ content: 'Het minimum koolzuur volume voor deze bierstijl.'});
+	$("#st_carb_max").jqxTooltip({ content: 'Het maximum koolzuur volume voor deze bierstijl.'});
+	$("#wa_cacl2").jqxTooltip({ content: 'Voor het maken van een ander waterprofiel. Voegt calcium en chloride toe. Voor het verbeteren van zoetere bieren.'});
+	$("#wa_caso4").jqxTooltip({ content: 'Gips. Voor het maken van een ander waterprofiel. Voegt calcium en sulfaat toe. Voor het verbeteren van bittere bieren.'});
+	$("#wa_mgso4").jqxTooltip({ content: 'Epsom zout. Voor het maken van een ander waterprofiel. Voegt magnesium en sulfaat toe. Gebruik spaarzaam!'});
+	$("#wa_nacl").jqxTooltip({ content: 'Keukenzout. Voor het maken van een ander waterprofiel. Voegt natrium en chloride toe. Voor het accentueren van zoetheid. Bij hoge dosering wordt het bier ziltig.'});
+	$("#w2_amount").jqxTooltip({ content: 'De verdeling van het hoofd en meng water. Het totale maisch water volume blijft gelijk.'});
+	$("#wb_calcium").jqxTooltip({ content: 'De ideale hoeveelheid Calcium is tussen 40 en 150.'});
+	$("#wb_magnesium").jqxTooltip({ content: 'De ideale hoeveelheid Magnesium is lager dan 30.'});
+	$("#wb_sodium").jqxTooltip({ content: 'De ideale hoeveelheid Natrium is lager dan 150.'});
+	$("#wb_chloride").jqxTooltip({ content: 'De ideale hoeveelheid Chloride is lager dan 100.'});
+	$("#wb_sulfate").jqxTooltip({ content: 'De ideale hoeveelheid Sulfaat is lager dan 350.'});
 
 	// Prepare the data
 	var source = {
@@ -313,16 +458,16 @@
 		datafields: [
 			// From prod_main
 			{ name: 'record', type: 'number' },
-			{ name: 'puuid', type: 'string' },
-			{ name: 'pname', type: 'string' },
+			{ name: 'uuid', type: 'string' },
+			{ name: 'name', type: 'string' },
 			{ name: 'code', type: 'string' },
 			{ name: 'birth', type: 'string' },
 			{ name: 'stage', type: 'string' },
-			{ name: 'pnotes', type: 'string' },
+			{ name: 'notes', type: 'string' },
 			{ name: 'log_brew', type: 'bool' },
 			{ name: 'log_fermentation', type: 'bool' },
 			{ name: 'inventory_reduced', type: 'bool' },
-			{ name: 'plocked', type: 'bool' },
+			{ name: 'locked', type: 'bool' },
 			{ name: 'eq_name', type: 'string' },
 			{ name: 'eq_boil_size', type: 'float' },
 			{ name: 'eq_batch_size', type: 'float' },
@@ -408,9 +553,6 @@
 			{ name: 'taste_taste', type: 'string' },
 			{ name: 'taste_mouthfeel', type: 'string' },
 			{ name: 'taste_aftertaste', type: 'string' },
-			// From prod_recipes
-			{ name: 'uuid', type: 'string' },
-			{ name: 'locked', type: 'bool' },
 			{ name: 'st_name', type: 'string' },
 			{ name: 'st_letter', type: 'string' },
 			{ name: 'st_guide', type: 'string' },
@@ -429,8 +571,6 @@
 			{ name: 'st_carb_max', type: 'float' },
 			{ name: 'st_abv_min', type: 'float' },
 			{ name: 'st_abv_max', type: 'float' },
-			{ name: 'name', type: 'string' },
-			{ name: 'notes', type: 'string' },
 			{ name: 'type', type: 'string' },
 			{ name: 'batch_size', type: 'float' },
 			{ name: 'boil_size', type: 'float' },
@@ -444,14 +584,39 @@
 			{ name: 'est_ibu', type: 'float' },
 			{ name: 'ibu_method', type: 'string' },
 			{ name: 'est_carb', type: 'float' },
-			{ name: 'mash_sparge_temp', type: 'float' },
+			{ name: 'sparge_temp', type: 'float' },
+			{ name: 'sparge_ph', type: 'float' },
+			{ name: 'sparge_volume', type: 'float' },
+			{ name: 'sparge_acid_type', type: 'string' },
+			{ name: 'sparge_acid_perc', type: 'float' },
+			{ name: 'sparge_acid_amount', type: 'float' },
 			{ name: 'mash_ph', type: 'float' },
 			{ name: 'mash_name', type: 'string' },
+			{ name: 'calc_acid', type: 'bool' },
+			{ name: 'w1_name', type: 'string' },
+			{ name: 'w1_amount', type: 'float' },
+			{ name: 'w1_calcium', type: 'float' },
+			{ name: 'w1_sulfate', type: 'float' },
+			{ name: 'w1_chloride', type: 'float' },
+			{ name: 'w1_sodium', type: 'float' },
+			{ name: 'w1_magnesium', type: 'float' },
+			{ name: 'w1_total_alkalinity', type: 'float' },
+			{ name: 'w1_ph', type: 'float' },
+			{ name: 'w1_cost', type: 'float' },
+			{ name: 'w2_name', type: 'string' },
+			{ name: 'w2_amount', type: 'float' },
+			{ name: 'w2_calcium', type: 'float' },
+			{ name: 'w2_sulfate', type: 'float' },
+			{ name: 'w2_chloride', type: 'float' },
+			{ name: 'w2_sodium', type: 'float' },
+			{ name: 'w2_magnesium', type: 'float' },
+			{ name: 'w2_total_alkalinity', type: 'float' },
+			{ name: 'w2_ph', type: 'float' },
+			{ name: 'w2_cost', type: 'float' },
 			{ name: 'fermentables', type: 'array' },
 			{ name: 'hops', type: 'string' },
 			{ name: 'miscs', type: 'string' },
 			{ name: 'yeasts', type: 'string' },
-			{ name: 'waters', type: 'array' },
 			{ name: 'mashs', type: 'string' }
 		],
 		id: 'record',
@@ -463,15 +628,15 @@
 			var records = dataAdapter.records;
 			dataRecord = records[0];
 			// Hidden record uuid
-			$("#pname").val(dataRecord.pname);
+			$("#name").val(dataRecord.name);
 			$("#code").val(dataRecord.code);
 			$("#birth").val(dataRecord.birth);
 			$("#stage").val(dataRecord.stage);
-			$("#pnotes").val(dataRecord.pnotes);
+			$("#notes").val(dataRecord.notes);
 			$("#log_brew").val(dataRecord.log_brew);
 			$("#log_fermentation").val(dataRecord.log_fermentation);
 			$("#inventory_reduced").val(dataRecord.inventory_reduced);
-			$("#plocked").val(dataRecord.plocked);
+			$("#locked").val(dataRecord.locked);
 			$("#eq_name").val(dataRecord.eq_name);
 			$("#eq_notes").val(dataRecord.eq_notes);
 			$("#eq_boil_size").val(dataRecord.eq_boil_size);
@@ -533,31 +698,78 @@
 			$("#brew_date_end").val(dataRecord.brew_date_end);
 
 			// Recipe
-			// locked
-			// st_ style settings.
-			// name
-			// notes
-			// type
+			$("#st_name").val(dataRecord.st_name);
+			$("#st_letter").val(dataRecord.st_letter);
+			$("#st_guide").val(dataRecord.st_guide);
+			$("#st_category").val(dataRecord.st_category);
+			$("#st_category_number").val(dataRecord.st_category_number);
+			$("#st_type").val(dataRecord.st_type);
+			$("#st_og_min").val(dataRecord.st_og_min);
+			$("#st_og_max").val(dataRecord.st_og_max);
+			$("#st_fg_min").val(dataRecord.st_fg_min);
+			$("#st_fg_max").val(dataRecord.st_fg_max);
+			$("#st_abv_min").val(dataRecord.st_abv_min);
+			$("#st_abv_max").val(dataRecord.st_abv_max);
+			$("#st_color_min").val(dataRecord.st_color_min);
+			$("#st_color_max").val(dataRecord.st_color_max);
+			$("#st_ibu_min").val(dataRecord.st_ibu_min);
+			$("#st_ibu_max").val(dataRecord.st_ibu_max);
+			$("#st_carb_min").val(dataRecord.st_carb_min);
+			$("#st_carb_max").val(dataRecord.st_carb_max);
+			$("#type").val(dataRecord.type);
+			$("#batch_size").val(dataRecord.batch_size);
 			old_batch_size = dataRecord.batch_size;
-			// boil_size
+			$("#boil_size").val(dataRecord.boil_size);
+			$("#boil_time").val(dataRecord.boil_time);
 			old_boil_time = dataRecord.boil_time;
+			$("#efficiency").val(dataRecord.efficiency);
 			old_efficiency = dataRecord.efficiency;
-			// est_og
-			// est_fg
-			// est_abv
-			// est_color
-			// color_method
-			// est_ibu
-			// ibu_method
-			// mash_sparge_temp
-			// mash_ph
-			// mash_name
-			// fermentables
-			// hops
-			// miscs
-			// yeasts
-			// waters
-			// mashs
+			$("#est_og").val(dataRecord.est_og);
+			$("#est_og2").val(dataRecord.est_og);
+			$("#est_fg").val(dataRecord.est_fg);
+			$("#est_color").val(dataRecord.est_color);
+			$("#est_color2").val(dataRecord.est_color);
+			$("#est_abv").val(dataRecord.est_abv);
+			$("#color_method").val(dataRecord.color_method);
+			$("#est_ibu").val(dataRecord.est_ibu);
+			$("#est_ibu2").val(dataRecord.est_ibu);
+			$("#ibu_method").val(dataRecord.ibu_method);
+			$("#est_carb").val(dataRecord.est_carb);
+			$("#mash_name").val(dataRecord.mash_name);
+			$("#mash_ph").val(dataRecord.mash_ph);
+			$("#tgt_mash_ph").val(dataRecord.mash_ph);
+			$("#sparge_temp").val(dataRecord.sparge_temp);
+			$("#sparge_ph").val(dataRecord.sparge_ph);
+			$("#sparge_volume").val(dataRecord.sparge_volume);
+			$("#sparge_acid_type").val(dataRecord.sparge_acid_type);
+			$("#sparge_acid_perc").val(dataRecord.sparge_acid_perc);
+			$("#sparge_acid_amount").val(dataRecord.sparge_acid_amount);
+			$("#calc_acid").val(dataRecord.calc_acid);
+			$("#w1_name").val(dataRecord.w1_name);
+			$("#w1_amount").val(dataRecord.w1_amount);
+			$("#w1_calcium").val(dataRecord.w1_calcium);
+			$("#w1_sulfate").val(dataRecord.w1_sulfate);
+			$("#w1_chloride").val(dataRecord.w1_chloride);
+			$("#w1_sodium").val(dataRecord.w1_sodium);
+			$("#w1_magnesium").val(dataRecord.w1_magnesium);
+			$("#w1_total_alkalinity").val(dataRecord.w1_total_alkalinity);
+			$("#w1_ph").val(dataRecord.w1_ph);
+			$("#w1_cost").val(dataRecord.w1_cost);
+			$("#w2_name").val(dataRecord.w2_name);
+			$("#w2_amount").val(dataRecord.w2_amount);
+			$("#w2_calcium").val(dataRecord.w2_calcium);
+			$("#w2_sulfate").val(dataRecord.w2_sulfate);
+			$("#w2_chloride").val(dataRecord.w2_chloride);
+			$("#w2_sodium").val(dataRecord.w2_sodium);
+			$("#w2_magnesium").val(dataRecord.w2_magnesium);
+			$("#w2_total_alkalinity").val(dataRecord.w2_total_alkalinity);
+			$("#w2_ph").val(dataRecord.w2_ph);
+			$("#w2_cost").val(dataRecord.w2_cost);
+			editFermentable(dataRecord);
+			editHop(dataRecord);
+                        editMisc(dataRecord);
+                        editYeast(dataRecord);
+                        editMash(dataRecord);
 
 			switch (dataRecord.stage) {
 				case 'Plan':		brewstage = 0;	break;
@@ -585,39 +797,1113 @@
 				$("#birth").jqxDateTimeInput({ disabled: true });
 			}
 			if (brewstage < 3) {
-				$("#brew_log").jqxButton({ disabled: true });
-				$("#ferment_log").jqxButton({ disabled: true });
+		//		$("#brew_log").jqxButton({ disabled: true });
+		//		$("#ferment_log").jqxButton({ disabled: true });
 			} else {
-				if (! dataRecord.log_brew)
-					$("#brew_log").jqxButton({ disabled: true });
-				if (! dataRecord.log_fermentation)
-					$("#ferment_log").jqxButton({ disabled: true });
+		//		if (! dataRecord.log_brew)
+		//			$("#brew_log").jqxButton({ disabled: true });
+		//		if (! dataRecord.log_fermentation)
+		//			$("#ferment_log").jqxButton({ disabled: true });
 			}
 			if (brewstage < 6)
 				$("#inventory_reduced").jqxCheckBox({ disabled : true });
 			else if ($('#inventory_reduced').jqxCheckBox('checked'))
 				$("#inventory_reduced").jqxCheckBox({ disabled : true });
 
-			calcFermentables();
-			calcIBUs();
-			calcSGendMash();
-			calcMashEfficiency();
+			$('#jqxTabs').jqxTabs('select', 2);
 		},
 		loadError: function (jqXHR, status, error) {
-		}
+		},
+                beforeLoadComplete: function (records) {
+                        $('#jqxLoader').jqxLoader('open');
+                }
 	});
 	dataAdapter.dataBind();
 
+        // Inline fermentables editor
+        var editFermentable = function (data) {
+                var fermentableSource = {
+                        localdata: data.fermentables,
+                        datatype: "local",
+                        datafields: [
+                                { name: 'f_name', type: 'string' },
+                                { name: 'f_origin', type: 'string' },
+                                { name: 'f_supplier', type: 'string' },
+                                { name: 'f_amount', type: 'float' },
+                                { name: 'f_cost', type: 'float' },
+                                { name: 'f_type', type: 'string' },
+                                { name: 'f_yield', type: 'float' },
+                                { name: 'f_color', type: 'float' },
+                                { name: 'f_coarse_fine_diff', type: 'float' },
+                                { name: 'f_moisture', type: 'float' },
+                                { name: 'f_diastatic_power', type: 'float' },
+                                { name: 'f_protein', type: 'float' },
+                                { name: 'f_max_in_batch', type: 'float' },
+                                { name: 'f_graintype', type: 'string' },
+                                { name: 'f_added', type: 'string' },
+                                { name: 'f_dissolved_protein', type: 'float' },
+                                { name: 'f_recommend_mash', type: 'bool' },
+                                { name: 'f_add_after_boil', type: 'bool' },
+                                { name: 'f_adjust_to_total_100', type: 'bool' },
+                                { name: 'f_percentage', type: 'float' },
+                                { name: 'f_di_ph', type: 'float' }
+                        ],
+                        addrow: function (rowid, rowdata, position, commit) {
+                                commit(true);
+                        },
+                        deleterow: function (rowid, commit) {
+                                commit(true);
+                        }
+                };
+                var fermentableAdapter = new $.jqx.dataAdapter(fermentableSource);
+                $("#fermentableGrid").jqxGrid({
+                        width: 1150,
+                        height: 400,
+                        source: fermentableAdapter,
+                        theme: theme,
+                        selectionmode: 'singlerow',
+                        editmode: 'selectedcell',
+                        editable: true,
+                        localization: getLocalization(),
+                        showtoolbar: true,
+                        rendertoolbar: function (toolbar) {
+                                var me = this;
+                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
+                                toolbar.append(container);
+                                container.append('<div style="float: left; margin-left: 165px;" id="faddrowbutton"></div>');
+                                container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
+                                container.append('<div style="float: left; margin-left: 10px;" id="finstockbutton"></div>');
+                                container.append('<input style="float: left; margin-left: 400px;" id="fdeleterowbutton" type="button" value="Verwijder mout" />');
+                                // add fermentable from dropdownlist.
+                                $("#faddrowbutton").jqxDropDownList({
+                                        placeHolder: "Kies mout:",
+                                        theme: theme,
+                                        source: fermentablelist,
+                                        displayMember: "name",
+                                        width: 150,
+                                        height: 27,
+                                        dropDownWidth: 500,
+                                        dropDownHeight: 500,
+                                        renderer: function (index, label, value) {
+                                                var datarecord = fermentablelist.records[index];
+                                                return datarecord.supplier+ " / " + datarecord.name + " (" + datarecord.color + " EBC)";
+                                        }
+                                });
+                                $("#faddrowbutton").on('select', function (event) {
+                                        if (event.args) {
+                                                var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                                var index = event.args.index;
+                                                var datarecord = fermentablelist.records[index];
+                                                var row = {};
+                                                row["f_name"] = datarecord.name;
+                                                row["f_origin"] = datarecord.origin;
+                                                row["f_supplier"] = datarecord.supplier;
+                                                row["f_amount"] = 0;
+                                                row["f_cost"] = datarecord.cost;
+                                                row["f_type"] = datarecord.type;
+                                                row["f_yield"] = datarecord.yield;
+                                                row["f_color"] = datarecord.color;
+                                                row["f_coarse_fine_diff"] = datarecord.coarse_fine_diff;
+                                                row["f_moisture"] = datarecord.moisture;
+                                                row["f_diastatic_power"] = datarecord.diastatic_power;
+                                                row["f_protein"] = datarecord.protein;
+                                                row["f_max_in_batch"] = datarecord.max_in_batch;
+                                                row["f_graintype"] = datarecord.graintype;
+                                                if (datarecord.add_after_boil) {
+                                                        row["f_added"] = "Primary";
+                                                } else if ((datarecord.type == "Sugar") || (datarecord.type == "Adjunct")) {
+                                                        row["f_added"] = "Boil";
+                                                } else {
+                                                        row["f_added"] = "Mash";
+                                                }
+                                                row["f_dissolved_protein"] = 0;
+                                                row["f_recommend_mash"] = datarecord.recommend_mash;
+                                                row["f_add_after_boil"] = datarecord.add_after_boil;
+                                                if (rowscount == 0) {
+                                                        // The first fermentable
+                                                        row["f_adjust_to_total_100"] = 1;
+                                                        row["f_percentage"] = 100;
+                                                } else {
+                                                        row["f_adjust_to_total_100"] = 0;
+                                                        row["f_percentage"] = 0;
+                                                }
+                                                row["f_di_ph"] = datarecord.di_ph;
+                                                var commit = $("#fermentableGrid").jqxGrid('addrow', null, row);
+                                        }
+                                });
+
+                                $("#finstockbutton").jqxCheckBox({ theme: theme, height: 27 });
+                                $("#finstockbutton").on('change', function (event) {
+                                        fermentableinstock = event.args.checked;
+                                        fermentablelist.dataBind();
+                                });
+
+                                // delete selected fermentable.
+                                $("#fdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#fdeleterowbutton").on('click', function () {
+                                        var selectedrowindex = $("#fermentableGrid").jqxGrid('getselectedrowindex');
+                                        var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
+                                                var id = $("#fermentableGrid").jqxGrid('getrowid', selectedrowindex);
+                                                var percent = $('#fermentableGrid').jqxGrid('getcellvalue', id, "f_percentage");
+                                                var amount = $('#fermentableGrid').jqxGrid('getcellvalue', id, "f_amount");
+                                                var commit = $("#fermentableGrid").jqxGrid('deleterow', id);
+                                        }
+                                        rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                        if (rowscount > 1) {
+                                                if (to_100) {
+                                                        for (var i = 0; i < rowscount; i++) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                if (rowdata.f_adjust_to_total_100) {
+                                                                        rowdata.f_percentage += percent;
+                                                                        rowdata.f_amount += amount;
+                                                                }
+                                                        }
+                                                } else {
+                                                        var tw = 0;
+                                                        for (i = 0; i < rowscount; i++) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                tw += rowdata.f_amount;
+                                                        };
+                                                        for (i = 0; i < rowscount; i++) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                var percentage = Math.round(rowdata.f_amount / tw * 1000) / 10.0;
+                                                                $("#fermentableGrid").jqxGrid('setcellvalue', i, "f_percentage", percentage);
+                                                        };
+                                                }
+                                        } else {
+                                                $("#fermentableGrid").jqxGrid('setcellvalue', 0, "f_percentage", 100);
+                                        }
+                                        calcFermentables();
+                                        calcSVG();
+                                        calcABV();
+                                        calcIBUs();
+                                });
+                        },
+                        ready: function() {
+                                calcFermentables();
+                                $('#jqxTabs').jqxTabs('next');
+                        },
+                        columns: [
+                                { text: 'Vergistbaar ingredi&euml;nt', editable: false, datafield: 'f_name',
+                                  cellsrenderer:  function (row, columnfield, value, defaulthtml, columnproperties) {
+                                        var rowData = $("#fermentableGrid").jqxGrid('getrowdata', row);
+                                        return "<span style='margin: 3px; margin-top: 6px; float: "+
+                                                columnproperties.cellsalign+"'>" +rowData.f_supplier+" / "+rowData.f_name+" ("+rowData.f_color+" EBC)</span>";
+                                  }
+                                },
+                                { text: 'Type', editable: false, align: 'center', cellsalign: 'center', width: 100, datafield: 'f_type' },
+                                { text: 'Moment', width: 110, align: 'center', cellsalign: 'center', datafield: 'f_added', columntype: 'dropdownlist',
+                                  createeditor: function (row, column, editor) {
+                                        var srcAdded = [ "Mash", "Boil", "Fermentation", "Lagering", "Bottle" ];
+                                        editor.jqxDropDownList({ autoDropDownHeight: true, source: srcAdded });
+                                  }
+                                },
+                                { text: 'Opbrengst', editable: false, datafield: 'f_yield', width: 90, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
+                                { text: 'Gewicht Kg', datafield: 'f_amount', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f3',
+                                  columntype: 'numberinput',
+                                  validation: function (cell, value) {
+                                        // Maximum weight is the batch_size, just a simple check.
+                                        var maxmout = parseFloat($("#batch_size").jqxNumberInput('decimal'));
+                                        if (value < 0 || value > maxmout) {
+                                                return { result: false, message: "Gewicht moet 0-"+maxmout+" zijn" };
+                                        }
+                                        return true;
+                                  },
+                                  initeditor: function (row, cellvalue, editor) {
+                                        editor.jqxNumberInput({ inputMode: 'simple', min: 0, decimalDigits: 3, spinButtons: false });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        if (to_100) {
+                                                return oldvalue;        // When using percentages, don't allow edited results.
+                                        }
+                                  }
+                                },
+                                { text: 'Percentage', datafield: 'f_percentage', width: 110, align: 'right', cellsalign: 'right', cellsformat: 'p1',
+                                  columntype: 'numberinput',
+                                  validation: function (cell, value) {
+                                        if (value < 0 || value > 100) {
+                                                return { result: false, message: "Percentage moet 0-100 zijn" };
+                                        }
+                                        return true;
+                                  },
+                                  initeditor: function (row, cellvalue, editor) {
+                                        editor.jqxNumberInput({ decimalDigits: 1, min: 0, max: 100, spinButtons: false });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        oldvalue = Math.round(oldvalue * 10) / 10.0;
+                                        var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                        if ((oldvalue != newvalue) && (rowscount > 1)) {
+                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', row);
+                                                if (rowdata.f_adjust_to_total_100) {
+                                                        return oldvalue;
+                                                }
+                                                var diff = newvalue - oldvalue;
+                                                var tw = 0;     // total weight
+                                                for (i = 0; i < rowscount; i++) {
+                                                        var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                        tw += rowdata.f_amount;
+                                                }
+                                                if (to_100) {
+                                                        // Adjust this row and the 100% row.
+                                                        var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', row);
+                                                        rowdata.f_amount += tw * diff / 100;
+                                                        for (i = 0; i < rowscount; i++) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                if (rowdata.f_adjust_to_total_100) {
+                                                                        rowdata.f_percentage -= diff;
+                                                                        rowdata.f_amount -= tw * diff / 100;
+                                                                }
+                                                        }
+                                                } else {
+                                                        // Adjust all the rows.
+                                                        var nw = tw * diff / 100;
+                                                        for (i = 0; i < rowscount; i++) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                if (i == row) {
+                                                                        rowdata.f_amount += nw;
+                                                                } else {
+                                                                        rowdata.f_amount -= nw / (rowscount - 1);
+                                                                        rowdata.f_percentage = Math.round((rowdata.f_amount / tw) * 1000) / 10.0;
+                                                                }
+                                                        }
+                                                }
+                                        }
+                                  }
+                                },
+                                { text: '100%', align: 'center', datafield: 'f_adjust_to_total_100', columntype: 'checkbox', width: 80,
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        if (to_100) {
+                                                var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                                for (i = 0; i < rowscount; i++) {
+                                                        if (i != row) {
+                                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                                rowdata.f_adjust_to_total_100 = false;
+                                                        }
+                                                }
+                                        }
+                                  }
+                                }
+                        ]
+                });
+                $("#fermentableGrid").on('cellendedit', function (event) {
+                        var args = event.args;
+                        console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
+                        // Make sure the grid itself is updated.
+                        $("#fermentableGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
+                        if ((args.datafield == 'f_amount') && (! to_100)) {
+                                // If one of the amounts is changed, recalculate the percentages.
+                                console.log("adjust percentages");
+                                var rowscount = $("#fermentableGrid").jqxGrid('getdatainformation').rowscount;
+                                if (rowscount > 1) {
+                                        var tw = 0;
+                                        for (i = 0; i < rowscount; i++) {
+                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                tw += rowdata.f_amount;
+                                        };
+                                        for (i = 0; i < rowscount; i++) {
+                                                var rowdata = $("#fermentableGrid").jqxGrid('getrowdata', i);
+                                                var percentage = Math.round(rowdata.f_amount / tw * 1000) / 10.0;
+                                                $("#fermentableGrid").jqxGrid('setcellvalue', i, "f_percentage", percentage);
+                                        };
+                                } else {
+                                        $("#fermentableGrid").jqxGrid('setcellvalue', 0, "f_percentage", 100);
+                                }
+                        };
+                        $('#fermentableGrid').jqxGrid('sortby', 'f_amount', 'desc');    // TODO: not reliable
+                        calcFermentables();
+                        calcSVG();
+                        calcABV();
+                        calcIBUs();     // Depends on gravity, so recalculate.
+                });
+        };
+
+        // Inline hops editor
+        var editHop = function (data) {
+                var hopSource = {
+                        localdata: data.hops,
+                        datatype: "local",
+                        cache: false,
+                        datafields: [
+                                { name: 'h_name', type: 'string' },
+                                { name: 'h_origin', type: 'string' },
+                                { name: 'h_amount', type: 'float' },
+                                { name: 'h_cost', type: 'float' },
+                                { name: 'h_type', type: 'string' },
+                                { name: 'h_form', type: 'string' },
+                                { name: 'h_useat', type: 'string' },
+                                { name: 'h_time', type: 'float' },
+                                { name: 'h_alpha', type: 'float' },
+                                { name: 'h_beta', type: 'float' },
+                                { name: 'h_hsi', type: 'float' },
+                                { name: 'h_humulene', type: 'float' },
+                                { name: 'h_carophyllene', type: 'float' },
+                                { name: 'h_cohumulone', type: 'float' },
+                                { name: 'h_myrcene', type: 'float' },
+                                { name: 'h_total_oil', type: 'float' },
+                                { name: 'h_weight', type: 'float' }
+                        ],
+                        addrow: function (rowid, rowdata, position, commit) {
+                                commit(true);
+                        },
+                        deleterow: function (rowid, commit) {
+                                commit(true);
+                        }
+                };
+                var hopAdapter = new $.jqx.dataAdapter(hopSource, {
+                        beforeLoadComplete: function (records) {
+                                var data = new Array();
+                                for (var i = 0; i < records.length; i++) {
+                                        var row = records[i];
+                                        row.h_weight = row.h_amount * 1000;
+                                        data.push(row);
+                                }
+                                return data;
+                        },
+                        loadError: function(jqXHR, status, error) {
+                                $('#err').text(status + ' ' + error);
+                        },
+                });
+                $("#hopGrid").jqxGrid({
+                        width: 1050,
+                        height: 400,
+                        source: hopAdapter,
+                        theme: theme,
+                        selectionmode: 'singlerow',
+                        editmode: 'selectedcell',
+                        editable: true,
+                        localization: getLocalization(),
+                        showtoolbar: true,
+                        rendertoolbar: function (toolbar) {
+                                var me = this;
+                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
+                                toolbar.append(container);
+                                container.append('<div style="float: left; margin-left: 165px;" id="haddrowbutton"></div>');
+                                container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
+                                container.append('<div style="float: left; margin-left: 10px;" id="hinstockbutton"></div>');
+                                container.append('<input style="float: left; margin-left: 280px;" id="hdeleterowbutton" type="button" value="Verwijder hop" />');
+                                // add hop from dropdownlist.
+                                $("#haddrowbutton").jqxDropDownList({
+                                        placeHolder: "Kies hop:",
+                                        theme: theme,
+                                        source: hoplist,
+                                        displayMember: "name",
+                                        width: 150,
+                                        height: 27,
+                                        dropDownWidth: 500,
+                                        dropDownHeight: 500,
+                                        renderer: function (index, label, value) {
+                                                var datarecord = hoplist.records[index];
+                                                return datarecord.origin+ " / " + datarecord.name + " (" + datarecord.alpha + "% &alpha;)";
+                                        }
+                                });
+                                $("#haddrowbutton").on('select', function (event) {
+                                        if (event.args) {
+                                                var index = event.args.index;
+                                                var datarecord = hoplist.records[index];
+                                                var row = {};
+                                                row["h_name"] = datarecord.name;
+                                                row["h_origin"] = datarecord.origin;
+                                                row["h_amount"] = 0;
+                                                row["h_cost"] = datarecord.cost;
+                                                row["h_type"] = datarecord.type;
+                                                row["h_form"] = datarecord.form;
+                                                row["h_useat"] = datarecord.useat;
+                                                row["h_time"] = 0;
+                                                row["h_alpha"] = datarecord.alpha;
+                                                row["h_beta"] = datarecord.beta;
+                                                row["h_hsi"] = datarecord.hsi;
+                                                row["h_humulene"] = datarecord.humulene;
+                                                row["h_carophyllene"] = datarecord.carophyllene;
+                                                row["h_cohumulone"] = datarecord.cohumulone;
+                                                row["h_myrcene"] = datarecord.myrcene;
+                                                row["h_total_oil"] = datarecord.total_oil;
+                                                row["h_weight"] = 0;
+                                                var commit = $("#hopGrid").jqxGrid('addrow', null, row);
+                                        }
+                                });
+
+                                $("#hinstockbutton").jqxCheckBox({ theme: theme, height: 27 });
+                                $("#hinstockbutton").on('change', function (event) {
+                                        hopinstock = event.args.checked;
+                                        hoplist.dataBind();
+                                });
+
+                                // delete selected hop.
+                                $("#hdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#hdeleterowbutton").on('click', function () {
+                                        var selectedrowindex = $("#hopGrid").jqxGrid('getselectedrowindex');
+                                        var rowscount = $("#hopGrid").jqxGrid('getdatainformation').rowscount;
+                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
+                                                var id = $("#hopGrid").jqxGrid('getrowid', selectedrowindex);
+                                                var commit = $("#hopGrid").jqxGrid('deleterow', id);
+                                        }
+                                });
+                        },
+                        ready: function() {
+                                $('#jqxTabs').jqxTabs('next');
+                        },
+                        columns: [
+                                { text: 'Hop', editable: false, datafield: 'h_name',
+                                  cellsrenderer: function (row, columnfield, value, defaulthtml, columnproperties) {
+                                        var rowData = $("#hopGrid").jqxGrid('getrowdata', row);
+                                        return "<span style='margin: 3px; margin-top: 6px; float: "+
+                                                columnproperties.cellsalign+"'>" +rowData.h_origin+" / "+rowData.h_name+"</span>";
+                                  },
+                                },
+                                { text: 'Type', editable: false, width: 90, align: 'center', cellsalign: 'center', datafield: 'h_type' },
+                                { text: 'Vorm', editable: false, width: 90, align: 'center', cellsalign: 'center', datafield: 'h_form' },
+                                { text: 'Alpha', editable: false, datafield: 'h_alpha', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'p1' },
+                                { text: 'Amount', hidden: true, datafield: 'h_amount' },
+                                { text: 'Gewicht gr', datafield: 'h_weight', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f1',
+                                  columntype: 'numberinput',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        return "<div style='margin: 4px;' class='jqx-right-align'>" + dataAdapter.formatNumber(value, "f1") + " gr</div>";
+                                  },
+                                  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
+                                        editor.jqxNumberInput({
+                                                inputMode: 'simple', decimalDigits: 1, min: 0, max: parseFloat(dataRecord.batch_size * 200),
+                                                spinButtons: false
+                                        });
+                                  },
+                                  validation: function (cell, value) {
+                                        var maxhops = parseFloat(dataRecord.batch_size) * 200;
+                                        if (value < 0 || value > maxhops ) {
+                                                return { result: false, message: "Gewicht moet tussen 0 en "+maxhops+" gram zijn" };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'Gebruik', width: 110, align: 'center', cellsalign: 'center', datafield: 'h_useat', columntype: 'dropdownlist',
+                                  createeditor: function (row, column, editor) {
+                                          var srcUse = [ "Boil", "Dry Hop", "Mash", "First Wort", "Aroma" ];
+                                          editor.jqxDropDownList({ autoDropDownHeight: true, source: srcUse });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        if ((newvalue == "Mash") || (newvalue == "First Wort")) {
+                                                $("#hopGrid").jqxGrid('setcellvalue', row, "h_time", parseFloat(dataRecord.boil_time));
+                                        } else if (newvalue == "Aroma") {
+                                                $("#hopGrid").jqxGrid('setcellvalue', row, "h_time", 0);
+                                        }
+                                  }
+                                },
+                                { text: 'Tijd', datafield: 'h_time', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f0',
+                                  columntype: 'numberinput',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        if ((rowdata.h_useat == "Boil") || (rowdata.h_useat == "Dry Hop") || (rowdata.h_useat == "Dry hop"))
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+"</div>";
+                                        else
+                                                return "<div style='margin: 4px;' class='jqx-right-align'> </div>";
+                                  },
+                                  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
+                                        editor.jqxNumberInput({ decimalDigits: 0, digits: 3, min: 0, max: parseFloat(dataRecord.boil_time) });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        var use = $("#hopGrid").jqxGrid('getcellvalue', row, "h_useat");
+                                        if ((use == "Mash") || (use == "First Wort") || (use == "First wort") || (use == "Aroma"))
+                                                return oldvalue;
+                                  },
+                                  validation: function (cell, value) {
+                                        var high = parseFloat(dataRecord.boil_time);
+                                        if (value < 0 || value > high ) {
+                                                return { result: false, message: "De tijd moet  0-"+high+" zijn" };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'IBU', editable: false, datafield: 'ibu', width: 80, align: 'right',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        var ibu = toIBU(rowdata.h_useat,
+                                                          rowdata.h_form,
+                                                          preboil_sg,
+                                                          parseFloat($("#batch_size").jqxNumberInput('decimal')),
+                                                          parseFloat(rowdata.h_amount),
+                                                          parseFloat(rowdata.h_time),
+                                                          parseFloat(rowdata.h_alpha),
+                                                          $("#ibu_method").val()
+                                                         );
+                                        calcIBUs();
+                                        return "<div style='margin: 4px;' class='jqx-right-align'>" + dataAdapter.formatNumber(ibu, "f1") + "</div>";
+                                  }
+                                }
+                        ]
+                });
+                $("#hopGrid").on('cellendedit', function (event) {
+                        var args = event.args;
+                        console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
+                        $("#hopGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
+                        if (args.datafield == 'h_weight')
+                                $("#hopGrid").jqxGrid('setcellvalue', args.rowindex, 'h_amount', args.value / 1000);
+                        //$('#hopGrid').jqxGrid('sortby', 'f_amount', 'desc');
+                });
+        };
+
+        // Inline miscs editor
+        var editMisc = function (data) {
+                var miscSource = {
+                        localdata: data.miscs,
+                        datatype: "local",
+                        cache: false,
+                        datafields: [
+                                { name: 'm_name', type: 'string' },
+                                { name: 'm_amount', type: 'float' },
+                                { name: 'm_cost', type: 'float' },
+                                { name: 'm_type', type: 'string' },
+                                { name: 'm_use_use', type: 'string' },
+                                { name: 'm_time', type: 'float' },
+                                { name: 'm_amount_is_weight', type: 'bool' },
+                                { name: 'm_weight', type: 'float' }
+                        ],
+                        addrow: function (rowid, rowdata, position, commit) {
+                                commit(true);
+                        },
+                        deleterow: function (rowid, commit) {
+                                commit(true);
+                        }
+                };
+                var miscAdapter = new $.jqx.dataAdapter(miscSource, {
+                        beforeLoadComplete: function (records) {
+                                var data = new Array();
+                                for (var i = 0; i < records.length; i++) {
+                                        var row = records[i];
+                                        row.m_weight = row.m_amount * 1000;
+                                        data.push(row);
+                                        // Initial set water agent values.
+                                        switch (row.m_name) {
+                                                case 'CaCl2':           $("#wa_cacl2").val(row.m_weight);
+                                                                        break;
+                                                case 'CaSO4':           $("#wa_caso4").val(row.m_weight);
+                                                                        break;
+                                                case 'MgSO4':           $("#wa_mgso4").val(row.m_weight);
+                                                                        break;
+                                                case 'NaCl':            $("#wa_nacl").val(row.m_weight);
+                                                                        break;
+                                                case 'Melkzuur':        $("#wa_acid_name").val('Melkzuur');
+                                                                        $("#wa_acid").val(row.m_weight);
+                                                                        $("#wa_acid_perc").val(80);
+                                                                        last_acid = 'Melkzuur';
+                                                                        break;
+                                                case 'Zoutzuur':        $("#wa_acid_name").val('Zoutzuur');
+                                                                        $("#wa_acid").val(row.m_weight);
+                                                                        $("#wa_acid_perc").val(80);
+                                                                        last_acid = 'Zoutzuur';
+                                                                        break;
+                                                case 'Fosforzuur':      $("#wa_acid_name").val('Fosforzuur');
+                                                                        $("#wa_acid").val(row.m_weight);
+                                                                        $("#wa_acid_perc").val(80);
+                                                                        last_acid = 'Fosforzuur';
+                                                                        break;
+                                                case 'Zwavelzuur':      $("#wa_acid_name").val('Zwavelzuur');
+                                                                        $("#wa_acid").val(row.m_weight);
+                                                                        $("#wa_acid_perc").val(80);
+                                                                        last_acid = 'Zwavelzuur';
+                                                                        break;
+                                                case 'NaHCO3':          $("#wa_base_name").val('NaHCO3');
+                                                                        $("#wa_base").val(row.m_weight);
+                                                                        last_base = 'NaHCO3';
+                                                                        break;
+                                                case 'Na2CO3':          $("#wa_base_name").val('Na2CO3');
+                                                                        $("#wa_base").val(row.m_weight);
+                                                                        last_base = 'Na2CO3';
+                                                                        break;
+                                                case 'CaCO3':           $("#wa_base_name").val('CaCO3');
+                                                                        $("#wa_base").val(row.m_weight);
+                                                                        last_base = 'CaCO3';
+                                                                        break;
+                                                case 'Ca(OH)2':         $("#wa_base_name").val('Ca(OH)2');
+                                                                        $("#wa_base").val(row.m_weight);
+                                                                        last_base = 'Ca(OH)2';
+                                                                        break;
+                                        }
+                                }
+                                return data;
+                        },
+                        loadError: function(jqXHR, status, error) {
+                                $('#err').text(status + ' ' + error);
+                        },
+                });
+                $("#miscGrid").jqxGrid({
+                        width: 960,
+                        height: 400,
+                        source: miscAdapter,
+                        theme: theme,
+                        selectionmode: 'singlerow',
+                        editmode: 'selectedcell',
+                        editable: true,
+                        localization: getLocalization(),
+                        showtoolbar: true,
+                        rendertoolbar: function (toolbar) {
+                                var me = this;
+                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
+                                toolbar.append(container);
+                                container.append('<div style="float: left; margin-left: 165px;" id="maddrowbutton"></div>');
+                                container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
+                                container.append('<div style="float: left; margin-left: 10px;" id="minstockbutton"></div>');
+                                container.append('<input style="float: left; margin-left: 200px;" id="mdeleterowbutton" type="button" value="Verwijder ingredient" />');
+                                // add misc from dropdownlist.
+                                $("#maddrowbutton").jqxDropDownList({
+                                        placeHolder: "Kies ingredient:",
+                                        theme: theme,
+                                        source: misclist,
+                                        displayMember: "name",
+                                        width: 150,
+                                        height: 27,
+                                        dropDownWidth: 500,
+                                        dropDownHeight: 500
+                                });
+                                $("#maddrowbutton").on('select', function (event) {
+                                        if (event.args) {
+                                                var index = event.args.index;
+                                                var datarecord = misclist.records[index];
+                                                var row = {};
+                                                row["m_name"] = datarecord.name;
+                                                row["m_amount"] = 0;
+                                                row["m_cost"] = datarecord.cost;
+                                                row["m_type"] = datarecord.type;
+                                                row["m_use_use"] = datarecord.use_use;
+                                                row["m_time"] = 0;
+                                                row["m_weight"] = 0;
+                                                row["m_amount_is_weight"] = datarecord.amount_is_weight;
+                                                var commit = $("#miscGrid").jqxGrid('addrow', null, row);
+                                        }
+                                });
+                                $("#minstockbutton").jqxCheckBox({ theme: theme, height: 27 });
+                                $("#minstockbutton").on('change', function (event) {
+                                        miscinstock = event.args.checked;
+                                        misclist.dataBind();
+                                });
+                                // delete selected misc.
+                                $("#mdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#mdeleterowbutton").on('click', function () {
+                                        var selectedrowindex = $("#miscGrid").jqxGrid('getselectedrowindex');
+                                        var rowscount = $("#miscGrid").jqxGrid('getdatainformation').rowscount;
+                                        var type = $("#miscGrid").jqxGrid('getcellvalue', selectedrowindex, "m_type");
+                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount && type != "Water agent")  {
+                                                var id = $("#miscGrid").jqxGrid('getrowid', selectedrowindex);
+                                                var commit = $("#miscGrid").jqxGrid('deleterow', id);
+                                        }
+                                });
+                        },
+                        ready: function() {
+                                $('#jqxTabs').jqxTabs('next');
+                        },
+                        columns: [
+                                { text: 'Ingredient', editable: false, datafield: 'm_name' },
+                                { text: 'Type', editable: false, width: 120, align: 'center', cellsalign: 'center', datafield: 'm_type' },
+                                { text: 'Gebruik', width: 110, align: 'center', cellsalign: 'center', datafield: 'm_use_use', columntype: 'dropdownlist',
+                                  createeditor: function (row, column, editor) {
+                                        var srcUseUse = [ "Mash", "Boil", "Primary", "Secondary", "Bottling" ];
+                                        editor.jqxDropDownList({ autoDropDownHeight: true, source: srcUseUse });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        var type = $("#miscGrid").jqxGrid('getcellvalue', row, "m_type");
+                                        if (type == "Water agent")
+                                                return oldvalue;
+                                  }
+                                },
+                                { datafield: 'm_amount_is_weight', hidden: true },      // We need to declare this column
+                                { datafield: 'm_amount', hidden: true },                // We need to declare this column
+                                { text: 'Hoeveelheid', datafield: 'm_weight', width: 120, align: 'right', cellsalign: 'right', cellsformat: 'f2',
+                                  columntype: 'numberinput',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        var vstr = rowdata.m_amount_is_weight ? "gr":"ml";
+                                        return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value,"f2")+" "+vstr+"</div>";
+                                  },
+                                  validation: function (cell, value) {
+                                        var high = parseFloat(dataRecord.boil_size) * 1000;
+                                        if (value < 0 || value > high) {
+                                                return { result: false, message: "Hoeveelheid moet tussen 0 en "+high+" zijn" };
+                                        }
+                                        return true;
+                                  },
+                                  initeditor: function (row, cellvalue, editor) {
+                                        editor.jqxNumberInput({
+                                                inputMode: 'simple', min: 0, max: parseFloat(dataRecord.boil_size) * 1000,
+                                                decimalDigits: 2, spinButtons: false
+                                        });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        var type = $("#miscGrid").jqxGrid('getcellvalue', row, "m_type");
+                                        if (type == "Water agent")
+                                                return oldvalue;
+                                  }
+                                },
+                               { text: 'Tijd', datafield: 'm_time', width: 70, align: 'right', cellsalign: 'right', cellsformat: 'f0',
+                                  columntype: 'numberinput',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        if (rowdata.m_use_use == 'Boil') {
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" m</div>";
+                                        } else if (rowdata.m_use_use == 'Secondary') {
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" d</div>";
+                                        } else {
+                                                var tijd = 0;
+                                                return "<div style='margin: 4px;' class='jqx-right-align'> </div>";
+                                        }
+                                  },
+                                  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
+                                        editor.jqxNumberInput({ decimalDigits: 0, digits: 3, min: 0, max: parseFloat(dataRecord.boil_time) });
+                                  },
+                                  cellvaluechanging: function (row, column, columntype, oldvalue, newvalue) {
+                                        var use = $("#miscGrid").jqxGrid('getcellvalue', row, "m_use_use");
+                                        if ((use != "Boil") && (use != "Secondary"))
+                                                return oldvalue;
+                                  },
+                                  validation: function (cell, value) {
+                                        var high = parseFloat(dataRecord.boil_time);
+                                        if (value < 0 || value > high ) {
+                                                return { result: false, message: "De tijd moet 0-"+high+" zijn" };
+                                        }
+                                        return true;
+                                  }
+                                }
+                        ]
+                });
+                $("#miscGrid").on('cellendedit', function (event) {
+                        var args = event.args;
+                        console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
+                        $("#miscGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
+                        if (args.datafield == 'm_weight') {
+                                $("#miscGrid").jqxGrid('setcellvalue', args.rowindex, 'm_amount', parseFloat(args.value) / 1000);
+                        }
+                });
+        };
+
+        // Inline yeasts editor
+        var editYeast = function (data) {
+                var yeastSource = {
+                        localdata: data.yeasts,
+                        datatype: "local",
+                        cache: false,
+                        datafields: [
+                                { name: 'y_name', type: 'string' },
+                                { name: 'y_laboratory', type: 'string' },
+                                { name: 'y_product_id', type: 'string' },
+                                { name: 'y_amount', type: 'float' },
+                                { name: 'y_cost', type: 'float' },
+                                { name: 'y_type', type: 'string' },
+                                { name: 'y_form', type: 'string' },
+                                { name: 'y_time', type: 'float' },
+                                { name: 'y_min_temperature', type: 'float' },
+                                { name: 'y_max_temperature', type: 'float' },
+                                { name: 'y_attenuation', type: 'float' },
+                                { name: 'y_amount_is_weight', type: 'bool' },
+                                { name: 'y_use', type: 'string' },
+                                { name: 'y_weight', type: 'float' }
+                        ],
+                        addrow: function (rowid, rowdata, position, commit) {
+                                commit(true);
+                        },
+                        deleterow: function (rowid, commit) {
+                                commit(true);
+                        }
+                };
+                var yeastAdapter = new $.jqx.dataAdapter(yeastSource, {
+                         beforeLoadComplete: function (records) {
+                                var data = new Array();
+                                for (var i = 0; i < records.length; i++) {
+                                        var row = records[i];
+                                        if (row.y_form == 'Liquid')
+                                                row.y_weight = Math.round(row.y_amount * 17);
+                                        else
+                                                row.y_weight = row.y_amount * 1000;
+                                        data.push(row);
+                                }
+                                return data;
+                        },
+                        loadError: function(jqXHR, status, error) {
+                                $('#err').text(status + ' ' + error);
+                        },
+                });
+                $("#yeastGrid").jqxGrid({
+                        width: 1050,
+                        height: 300,
+                        source: yeastAdapter,
+                        theme: theme,
+                        selectionmode: 'singlerow',
+                        editmode: 'selectedcell',
+                        editable: true,
+                        localization: getLocalization(),
+                        showtoolbar: true,
+                        rendertoolbar: function (toolbar) {
+                                var me = this;
+                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
+                                toolbar.append(container);
+                                container.append('<div style="float: left; margin-left: 165px;" id="yaddrowbutton"></div>');
+                                container.append('<div style="float: left; margin-left: 10px; margin-top: 5px;">In voorraad:</div>');
+                                container.append('<div style="float: left; margin-left: 10px;" id="yinstockbutton"></div>');
+                                container.append('<input style="float: left; margin-left: 230px;" id="ydeleterowbutton" type="button" value="Verwijder gist" />');
+                                // add yeast from dropdownlist.
+                                $("#yaddrowbutton").jqxDropDownList({
+                                        placeHolder: "Kies gist:",
+                                        theme: theme,
+                                        source: yeastlist,
+                                        displayMember: "name",
+                                        width: 150,
+                                        height: 27,
+                                        dropDownWidth: 500,
+                                        dropDownHeight: 500,
+                                        renderer: function (index, label, value) {
+                                                var datarecord = yeastlist.records[index];
+                                                return datarecord.laboratory+" "+datarecord.product_id+" "+datarecord.name;
+                                        }
+                                });
+                                $("#yaddrowbutton").on('select', function (event) {
+                                        if (event.args) {
+                                                var index = event.args.index;
+                                                var datarecord = yeastlist.records[index];
+                                                var row = {};
+                                                row["y_name"] = datarecord.name;
+                                                row["y_laboratory"] = datarecord.laboratory;
+                                                row["y_product_id"] = datarecord.product_id;
+                                                row["y_type"] = datarecord.type;
+                                                row["y_form"] = datarecord.form;
+                                                row["y_amount"] = 0;
+                                                row["y_cost"] = datarecord.cost;
+                                                row["y_use"] = "Primary";
+                                                row["y_time"] = 0;
+                                                if (datarecord.form == "Dry") {
+                                                        row["y_amount_is_weight"] = 1;
+                                                } else {
+                                                        row["y_amount_is_weight"] = 0;
+                                                }
+                                                row["y_min_temperature"] = datarecord.min_temperature;
+                                                row["y_max_temperature"] = datarecord.max_temperature;
+                                                row["y_attenuation"] = datarecord.attenuation;
+                                                row["y_weight"] = 0;
+                                                var commit = $("#yeastGrid").jqxGrid('addrow', null, row);
+                                        }
+                                });
+                                $("#yinstockbutton").jqxCheckBox({ theme: theme, height: 27 });
+                                $("#yinstockbutton").on('change', function (event) {
+                                        yeastinstock = event.args.checked;
+                                        yeastlist.dataBind();
+                                });
+                                // delete selected yeast.
+                                $("#ydeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#ydeleterowbutton").on('click', function () {
+                                        var selectedrowindex = $("#yeastGrid").jqxGrid('getselectedrowindex');
+                                        var rowscount = $("#yeastGrid").jqxGrid('getdatainformation').rowscount;
+                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
+                                                var id = $("#yeastGrid").jqxGrid('getrowid', selectedrowindex);
+                                                var commit = $("#yeastGrid").jqxGrid('deleterow', id);
+                                        }
+                                });
+                        },
+                        ready: function() {
+//                                calcSVG();
+                                $('#jqxTabs').jqxTabs('next');
+                        },
+                        columns: [
+                                { text: 'Gist', editable: false, datafield: 'y_name' },
+                                { text: 'Laboratorium', editable: false, width: 150, datafield: 'y_laboratory' },
+                                { text: 'Code', editable: false, width: 90, datafield: 'y_product_id' },
+                                { text: 'Soort', editable: false, width: 80, align: 'center', cellsalign: 'center', datafield: 'y_form' },
+                                { text: 'Min.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_min_temperature' },
+                                { text: 'Max.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_max_temperature' },
+                                { text: 'Attn.', editable: false, width: 70, align: 'right', cellsalign: 'right', datafield: 'y_attenuation', cellsformat: 'f1' },
+                                { text: 'Voor', width: 100, align: 'center', cellsalign: 'center', datafield: 'y_use', columntype: 'dropdownlist',
+                                  createeditor: function (row, column, editor) {
+                                        var srcYUse = [ "Primary", "Secondary", "Bottle" ];
+                                        editor.jqxDropDownList({ autoDropDownHeight: true, source: srcYUse });
+                                  }
+                                },
+                                { datafield: 'y_amount', width: 90 },
+                                { text: 'Hoeveel', datafield: 'y_weight', width: 110, align: 'right', cellsalign: 'right',
+                                  cellsformat: 'f1', columntype: 'numberinput',
+                                  cellsrenderer: function (index, datafield, value, defaultvalue, column, rowdata) {
+                                        if (rowdata.y_form == 'Liquid') {
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" pk</div>";
+                                        } else if (rowdata.y_form == 'Dry') {
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f1")+" gr</div>";
+                                        } else {
+                                                return "<div style='margin: 4px;' class='jqx-right-align'>"+dataAdapter.formatNumber(value, "f0")+" ml</div>";
+                                        }
+                                  },
+                                  initeditor: function (row, cellvalue, editor, celltext, pressedChar) {
+                                        var form = $("#yeastGrid").jqxGrid('getcellvalue', args.rowindex, 'y_form');
+                                        if (form == 'Dry') {
+                                                editor.jqxNumberInput({ decimalDigits: 1, min: 0, spinButtons: false });
+                                        } else {
+                                                editor.jqxNumberInput({ decimalDigits: 0, min: 0, spinButtons: false });
+                                        }
+                                  },
+                                  validation: function (cell, value) {
+                                        if (value < 0 || value > 100000000000 ) {
+                                                return { result: false, message: "Hoeveelheid moet 0-~ zijn" };
+                                        }
+                                        return true;
+                                  }
+                                }
+                        ]
+                });
+                $("#yeastGrid").on('cellendedit', function (event) {
+                        var args = event.args;
+                        console.log("Event Type: cellendedit, Column: " + args.datafield + ", Row: " + (args.rowindex) + ", Value: " + args.value);
+                        $("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, args.datafield, args.value);
+                        if (args.datafield == 'y_weight') {
+                                var form = $("#yeastGrid").jqxGrid('getcellvalue', args.rowindex, 'y_form');
+                                if (form == 'Liquid')
+                                        $("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, 'y_amount', parseFloat(args.value * 0.0588));
+                                else
+                                        $("#yeastGrid").jqxGrid('setcellvalue', args.rowindex, 'y_amount', parseFloat(args.value / 1000));
+                        }
+                });
+        };
+
+        // inline mash editor
+        var editMash = function (data) {
+                var generaterow = function () {
+                        var row = {};
+                        row["step_name"] = "Stap 1";
+                        row["step_type"] = "Infusion";
+                        row["step_infuse_amount"] = 15;
+                        row["step_temp"] = 62.0;
+                        row['step_time'] = 20.0;
+                        row['ramp_time'] = 1.0;
+                        row['end_temp'] = 62.0;
+                        return row;
+                }
+                var mashSource = {
+                        localdata: data.mashs,
+                        datatype: "local",
+                        cache: false,
+                        datafields: [
+                                { name: 'step_name', type: 'string' },
+                                { name: 'step_type', type: 'string' },
+                                { name: 'step_infuse_amount', type: 'float' },
+                                { name: 'step_temp', type: 'float' },
+                                { name: 'step_time', type: 'float' },
+                                { name: 'ramp_time', type: 'float' },
+                                { name: 'end_temp', type: 'float' }
+                        ],
+                        addrow: function (rowid, rowdata, position, commit) {
+                                commit(true);
+                        },
+                        deleterow: function (rowid, commit) {
+                                commit(true);
+                        }
+                };
+                var mashAdapter = new $.jqx.dataAdapter(mashSource, {
+                        beforeLoadComplete: function (records) {
+                                mash_infuse = 0;
+                                var data = new Array();
+                                for (var i = 0; i < records.length; i++) {
+                                        var row = records[i];
+                                        if (row.step_type == 'Infusion')
+                                                mash_infuse += parseFloat(row.step_infuse_amount);
+                                }
+                        },
+                });
+                $("#mashGrid").jqxGrid({
+                        width: 960,
+                        height: 400,
+                        source: mashAdapter,
+                        theme: theme,
+                        selectionmode: 'singlerow',
+                        editmode: 'selectedcell',
+                        editable: true,
+                        localization: getLocalization(),
+                        showtoolbar: true,
+                        rendertoolbar: function (toolbar) {
+                                var me = this;
+                                var container = $("<div style='overflow: hidden; position: relative; margin: 5px;'></div>");
+                                toolbar.append(container);
+                                container.append('<input style="float: left; margin-left: 165px;" id="saddrowbutton" type="button" value="Nieuwe stap" />');
+                                container.append('<input style="float: left; margin-left: 230px;" id="sdeleterowbutton" type="button" value="Verwijder stap" />');
+                                $("#saddrowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#saddrowbutton").on('click', function () {
+                                        var datarow = generaterow();
+                                        var commit = $("#mashGrid").jqxGrid('addrow', null, datarow);
+                                });
+                                // delete selected yeast.
+                                $("#sdeleterowbutton").jqxButton({ theme: theme, height: 27, width: 150 });
+                                $("#sdeleterowbutton").on('click', function () {
+                                        var selectedrowindex = $("#mashGrid").jqxGrid('getselectedrowindex');
+                                        var rowscount = $("#mashGrid").jqxGrid('getdatainformation').rowscount;
+                                        if (selectedrowindex >= 0 && selectedrowindex < rowscount) {
+                                                var id = $("#mashGrid").jqxGrid('getrowid', selectedrowindex);
+                                                var commit = $("#mashGrid").jqxGrid('deleterow', id);
+                                        }
+                                });
+                        },
+                        ready: function() {
+                                var fg = estimate_fg(psugar, pcara, 0, 0, 0, svg, parseFloat(parseFloat($("#est_og").jqxNumberInput('decimal'))));
+                                dataRecord.est_fg = fg;
+                                $('#est_fg').val(fg);
+                                calcInit();
+                                $('#jqxLoader').jqxLoader('close');
+                                $('#jqxTabs').jqxTabs('first');
+                        },
+                        columns: [
+                                { text: 'Stap naam', datafield: 'step_name' },
+                                { text: 'Stap type', datafield: 'step_type', width: 110, columntype: 'dropdownlist',
+                                  createeditor: function (row, cellvalue, editor, celltext, cellwidth, cellheight) {
+                                        var dataSource = [ "Infusion", "Temperature", "Decoction" ];
+                                        editor.jqxDropDownList({ source: dataSource, dropDownHeight: 105 });
+                                  }
+                                },
+                                { text: 'Temperatuur', datafield: 'step_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1',
+                                  validation: function (cell, value) {
+                                        if (value < 35 || value > 80) {
+                                                return { result: false, message: "De temperatuur moet tussen 35 en 80 zijn." };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'Eind', datafield: 'end_temp', width: 80, align: 'right', cellsalign: 'right', cellsformat: 'f1',
+                                  validation: function (cell, value) {
+                                        if (value < 35 || value > 80) {
+                                                return { result: false, message: "De temperatuur moet tussen 35 en 80 zijn." };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'Tijd', datafield: 'step_time', width: 70, align: 'right', cellsalign: 'right',
+                                  validation: function (cell, value) {
+                                        if (value < 1 || value > 360) {
+                                                return { result: false, message: "De tijd moet tussen 1 en 360 zijn." };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'Stap', datafield: 'ramp_time', width: 70, align: 'right', cellsalign: 'right',
+                                  validation: function (cell, value) {
+                                        if (value < 1 || value > 60) {
+                                                return { result: false, message: "De tijd moet tussen 1 en 60 zijn." };
+                                        }
+                                        return true;
+                                  }
+                                },
+                                { text: 'Infuse', datafield: 'step_infuse_amount', width: 70, align: 'right', cellsalign: 'right',
+                                  validation: function (cell, value) {
+                                        if (value < 0 || value > 60) {
+                                                return { result: false, message: "De waarde moet tussen 0 en 60 zijn." };
+                                        }
+                                        return true;
+                                  }
+                                }
+                        ]
+                });
+                $("#mashGrid").on('cellendedit', function (event) {
+                        $('#mashGrid').jqxGrid('sortby', 'step_temp', 'asc');
+                });
+        };
+
 	// initialize the input fields.
+        var srcType = [ "All Grain", "Partial Mash", "Extract" ];
+        var srcColor = [ "Morey", "Mosher", "Daniels" ];
+        var srcIBU = [ "Tinseth", "Rager", "Daniels" ]; // Only these are supported at this time.
+        var srcBase = [ "NaHCO3", "Na2CO3", "CaCO3", "Ca(OH)2" ];
+        var srcAcid = [ "Melkzuur", "Zoutzuur", "Fosforzuur", "Zwavelzuur" ];
 	var srcMaterial= [ "RVS", "Aluminium", "Kunststof", "Koper" ];
 	var srcAeration= [ 'None', 'Air', 'Oxygen' ];
 	var srcCooling= [ '-', 'Emersion chiller', 'Counterflow chiller', 'Au bain marie', 'Natural' ];
 	//                '-', 'Dompelkoeler', 'Tegenstroomkoeler', 'Au bain marie', 'Laten afkoelen'
-	$("#pname").jqxInput({ theme: theme, width: 640, height: 23 });
+	$("#name").jqxInput({ theme: theme, width: 640, height: 23 });
 	$("#code").jqxInput({ theme: theme, width: 100, height: 23 });
 	$("#birth").jqxDateTimeInput({ theme: theme, width: 150, height: 23, formatString: 'yyyy-MM-dd' });
 	$("#stage").jqxInput({ theme: theme, width: 100, height: 23 });
-	$("#pnotes").jqxInput({ theme: theme, width: 960, height: 200 });
+	$("#notes").jqxInput({ theme: theme, width: 960, height: 200 });
 	$("#log_brew").jqxCheckBox({ theme: theme, width: 120, height: 23, disabled : true });
 	$("#log_fermentation").jqxCheckBox({ theme: theme, width: 120, height: 23, disabled : true });
 	$("#inventory_reduced").jqxCheckBox({ theme: theme, width: 120, height: 23 });
@@ -627,14 +1913,14 @@
 		// Call the script with the uuid.
 		$("#inventory_reduced").jqxCheckBox({ disabled : true });
 	});
-	$("#plocked").jqxCheckBox({ theme: theme, width: 120, height: 23, disabled : true });
-	$('#plocked').on('checked', function (event) {
+	$("#locked").jqxCheckBox({ theme: theme, width: 120, height: 23, disabled : true });
+	$('#locked').on('checked', function (event) {
 		if (brewstage >= 10) {
 			$("#stage").val('Closed');
 			brewstage = 11;
 		}
 	});
-	$('#plocked').on('unchecked', function (event) {
+	$('#locked').on('unchecked', function (event) {
 		if (brewstage >= 10) {
 			$("#stage").val('Ready');
 			brewstage = 10;
@@ -695,12 +1981,201 @@
 	// Packaging
 	// Tasting
 
-	// Recipe
+	$("#batch_size").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: 'L', symbolPosition: 'right' });
+	$("#boil_size").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 2, readOnly: true, symbol: 'L', symbolPosition: 'right' });
+	$("#boil_time").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, max: 360, decimalDigits: 0, spinButtons: true });
+	$("#efficiency").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 40, max: 100, decimalDigits: 0, spinButtons: true, symbol: '%', symbolPosition: 'right'  });
 	$("#est_og").jqxNumberInput({ inputMode: 'simple', readOnly: true, theme: theme, width: 70, height: 23, decimalDigits: 3 });
-	$("#est_ibu").jqxNumberInput({ inputMode: 'simple', readOnly: true, theme: theme, width: 70, height: 23, decimalDigits: 0 });
-	$("#est_color").jqxNumberInput({ inputMode: 'simple', readOnly: true, theme: theme, width: 70, height: 23, decimalDigits: 0 });
+	$("#est_og2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 3, readOnly: true });
+	$("#st_og_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
+	$("#st_og_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
+
+	$("#est_fg").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 3, readOnly: true });
+	$("#st_fg_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
+	$("#st_fg_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 3, readOnly: true });
+
+	$("#est_abv").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+	$("#st_abv_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+	$("#st_abv_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+
+	$("#est_color").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' EBC', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
+	$("#est_color2").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' EBC', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
+	$("#st_color_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
+	$("#st_color_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
+	$("#color_method").jqxDropDownList({ theme: theme, source: srcColor, width: 125, height: 23, dropDownHeight: 95 });
+
+	$("#est_ibu").jqxNumberInput({ inputMode: 'simple', theme: theme, symbol: ' IBU', symbolPosition: 'right', width: 100, height: 23, decimalDigits: 0, readOnly: true });
+	$("#est_ibu2").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
+	$("#st_ibu_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
+	$("#st_ibu_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 0, readOnly: true });
+	$("#ibu_method").jqxDropDownList({ theme: theme, source: srcIBU, width: 125, height: 23, dropDownHeight: 95, dropDownVerticalAlignment: 'top' });
+
+	$("#est_carb").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+	$("#st_carb_min").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+	$("#st_carb_max").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 50, height: 23, decimalDigits: 1, readOnly: true });
+
+	$("#mash_name").jqxInput({ theme: theme, width: 320, height: 23 });
+	$("#mash_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 4, max: 8, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
+	$("#tgt_mash_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, readOnly: true });
+	$("#sparge_temp").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 70, max: 98, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5 });
+
 	$("#est_mash_sg").jqxNumberInput({ inputMode: 'simple', readOnly: true, theme: theme, width: 70, height: 23, decimalDigits: 3 });
 
+        // Several gauges
+        $("#hop_flavour").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
+        $("#hop_aroma").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
+        $("#perc_malts").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
+        $("#perc_sugars").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
+        $("#perc_cara").jqxProgressBar({ width: 300, height: 23, theme: theme, showText: true });
+
+        // Water treatment
+        $("#w1_name").jqxDropDownList({
+                placeHolder: "Kies hoofd water:",
+                theme: theme,
+                source: waterlist,
+                displayMember: "name",
+                width: 250,
+                height: 27,
+                dropDownWidth: 400,
+                dropDownHeight: 400
+        });
+        $("#w1_name").on('select', function (event) {
+                if (event.args) {
+                        var index = event.args.index;
+                        var datarecord = waterlist.records[index];
+                        dataRecord.w1_name = datarecord.name;
+                        $("#w1_calcium").val(datarecord.calcium);
+                        dataRecord.w1_calcium = datarecord.calcium;
+                        $("#w1_sulfate").val(datarecord.sulfate);
+                        dataRecord.w1_sulfate = datarecord.sulfate;
+                        $("#w1_chloride").val(datarecord.chloride);
+                        dataRecord.w1_chloride = datarecord.chloride;
+                        $("#w1_sodium").val(datarecord.sodium);
+                        dataRecord.w1_sodium = datarecord.sodium;
+                        $("#w1_magnesium").val(datarecord.magnesium);
+                        dataRecord.w1_magnesium = datarecord.magnesium;
+                        $("#w1_total_alkalinity").val(datarecord.total_alkalinity);
+                        dataRecord.w1_total_alkalinity = datarecord.total_alkalinity;
+                        $("#w1_ph").val(datarecord.ph);
+                        dataRecord.w1_ph = datarecord.ph;
+                        $("#w1_cost").val(datarecord.cost);
+                        dataRecord.w1_cost = datarecord.cost;
+                        calcWater();
+                }
+        });
+        $("#w1_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w1_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+
+        $("#w2_name").jqxDropDownList({
+                placeHolder: "Kies meng water:",
+                theme: theme,
+                source: waterlist,
+                displayMember: "name",
+                width: 250,
+                height: 27,
+                dropDownWidth: 400,
+                dropDownHeight: 400
+        });
+        $("#w2_name").on('select', function (event) {
+                if (event.args) {
+                        var index = event.args.index;
+                        var datarecord = waterlist.records[index];
+                        dataRecord.w2_name = datarecord.name;
+                        $("#w2_calcium").val(datarecord.calcium);
+                        dataRecord.w2_calcium = datarecord.calcium;
+                        $("#w2_sulfate").val(datarecord.sulfate);
+                        dataRecord.w2_sulfate = datarecord.sulfate;
+                        $("#w2_chloride").val(datarecord.chloride);
+                        dataRecord.w2_chloride = datarecord.chloride;
+                        $("#w2_sodium").val(datarecord.sodium);
+                        dataRecord.w2_sodium = datarecord.sodium;
+                        $("#w2_magnesium").val(datarecord.magnesium);
+                        dataRecord.w2_magnesium = datarecord.magnesium;
+                        $("#w2_total_alkalinity").val(datarecord.total_alkalinity);
+                        dataRecord.w2_total_alkalinity = datarecord.total_alkalinity;
+                        $("#w2_ph").val(datarecord.ph);
+                        dataRecord.w2_ph = datarecord.ph;
+                        $("#w2_cost").val(datarecord.cost);
+                        dataRecord.w2_cost = datarecord.cost;
+                        $("#w2_amount").jqxNumberInput({ max: 100000, readOnly: false }); // Set high max to enable the spinbuttons.
+                        calcWater();
+                }
+        });
+        $("#w2_amount").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 94, height: 23, min: 0, max: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.5, readOnly: true });
+        $("#w2_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#w2_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+
+        $("#wg_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wg_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wb_ph").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+
+        $("#pr_name").jqxDropDownList({
+                placeHolder: "Kies doel profiel:",
+                theme: theme,
+                source: waterprofiles,
+                displayMember: "name",
+                width: 250,
+                height: 27,
+                dropDownWidth: 400,
+                dropDownHeight: 300
+        });
+        $("#pr_name").on('select', function (event) {
+                if (event.args) {
+                        var index = event.args.index;
+                        var datarecord = waterprofiles.records[index];
+                        $("#pr_calcium").val(datarecord.calcium);
+                        $("#pr_sulfate").val(datarecord.sulfate);
+                        $("#pr_chloride").val(datarecord.chloride);
+                        $("#pr_sodium").val(datarecord.sodium);
+                        $("#pr_magnesium").val(datarecord.magnesium);
+                        $("#pr_total_alkalinity").val(datarecord.total_alkalinity);
+                }
+        });
+        $("#pr_calcium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#pr_magnesium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#pr_sodium").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#pr_total_alkalinity").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#pr_chloride").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#pr_sulfate").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 74, height: 23, decimalDigits: 1, readOnly: true });
+        $("#wa_cacl2").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+        $("#wa_caso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+        $("#wa_mgso4").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+        $("#wa_nacl").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1, symbol: ' gr', symbolPosition: 'right' });
+
+        $("#calc_acid").jqxCheckBox({ theme: theme, width: 120, height: 23 });
+        $("#wa_base_name").jqxDropDownList({ theme: theme, source: srcBase, width: 125, height: 23, dropDownHeight: 128 });
+        $("#wa_base").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' gr', symbolPosition: 'right' });
+        $("#wa_acid_name").jqxDropDownList({ theme: theme, source: srcAcid, width: 125, height: 23, dropDownHeight: 128 })
+        $("#wa_acid").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, min: 0, decimalDigits: 2, spinButtons: true, spinButtonsStep: 0.05, symbol: ' ml', symbolPosition: 'right' });
+        $("#wa_acid_perc").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 80, height: 23, min: 0, max: 100, decimalDigits: 0, spinButtons: true, symbol: '%', symbolPosition: 'right' });
+
+        $("#sparge_volume").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
+        $("#sparge_ph").jqxNumberInput({ inputMode: 'simple', spinMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 1, spinButtons: true, spinButtonsStep: 0.1 });
+        $("#sparge_acid_amount").jqxNumberInput({ inputMode: 'simple', theme: theme, width: 100, height: 23, decimalDigits: 5, readOnly: true });
+
 	$('#jqxTabs').jqxTabs({
 		theme: theme,
 		width: 1280,
@@ -709,18 +2184,13 @@
 		position: 'top'
 	});
 
-	// Buttons sidebar
-	$("#rec_edit").jqxButton({ template: "primary", width: '140px', theme: theme });
-	$("#brew_log").jqxButton({ template: "primary", width: '140px', theme: theme });
-	$("#ferment_log").jqxButton({ template: "primary", width: '140px', theme: theme });
-
 	// Buttons below
 	$("#Delete").jqxButton({ template: "danger", width: '80px', theme: theme });
 	$("#Delete").click(function () {
 		// Open a popup to confirm this action.
 		$('#eventWindow').jqxWindow('open');
 		$("#delOk").click(function () {
-			var data = "delete=true&" + $.param({ uuid: dataRecord.puuid });
+			var data = "delete=true&" + $.param({ uuid: dataRecord.uuid });
 			$.ajax({
 				dataType: 'json',
 				url: url,
@@ -744,20 +2214,24 @@
 
 	$("#Save").jqxButton({ template: "success", width: '90px', theme: theme });
 	$("#Save").click(function () {
-		console.log(dataRecord.puuid);
-
+		console.log(dataRecord.uuid);
+		var fermentablerow = $('#fermentableGrid').jqxGrid('getrows');
+		var hoprow = $('#hopGrid').jqxGrid('getrows');
+		var miscrow = $('#miscGrid').jqxGrid('getrows');
+		var yeastrow = $('#yeastGrid').jqxGrid('getrows');
+		var mashrow = $('#mashGrid').jqxGrid('getrows');
 		var row = {
 			record: my_record,
-			puuid: dataRecord.puuid,
-			pname: $("#pname").val(),
+			uuid: dataRecord.uuid,
+			name: $("#name").val(),
 			code: $("#code").val(),
 			birth: $("#birth").val(),
 			stage: $("#stage").val(),
-			pnotes: $("#pnotes").val(),
+			notes: $("#notes").val(),
 			log_brew: $("#log_brew").val(),
 			log_fermentation: $("#log_fermentation").val(),
 			inventory_reduced: $("#inventory_reduced").val(),
-			plocked: $("#plocked").val(),
+			locked: $("#locked").val(),
 			eq_name: $("#eq_name").val(),
 			eq_boil_size: parseFloat($("#eq_boil_size").jqxNumberInput('decimal')),
 			eq_batch_size: parseFloat($("#eq_batch_size").jqxNumberInput('decimal')),
@@ -781,7 +2255,72 @@
 			eq_kettle_height: dataRecord.eq_kettle_height,
 			eq_mash_volume: parseFloat($("#eq_mash_volume").jqxNumberInput('decimal')),
 			eq_mash_max: parseFloat($("#eq_mash_max").jqxNumberInput('decimal')),
-			eq_efficiency: parseFloat($("#eq_efficiency").jqxNumberInput('decimal'))
+			eq_efficiency: parseFloat($("#eq_efficiency").jqxNumberInput('decimal')),
+	//		st_name: $('#st_name').val(),
+	//		st_letter: $('#st_letter').val(),
+	//		st_guide: $('#st_guide').val(),
+	//		st_type: $('#st_type').val(),
+	//		st_category: $('#st_category').val(),
+	//		st_category_number: parseFloat($("#st_category_number").jqxNumberInput('decimal')),
+			st_og_min: parseFloat($("#st_og_min").jqxNumberInput('decimal')),
+			st_og_max: parseFloat($("#st_og_max").jqxNumberInput('decimal')),
+			st_fg_min: parseFloat($("#st_fg_min").jqxNumberInput('decimal')),
+			st_fg_max: parseFloat($("#st_fg_max").jqxNumberInput('decimal')),
+			st_ibu_min: parseFloat($("#st_ibu_min").jqxNumberInput('decimal')),
+			st_ibu_max: parseFloat($("#st_ibu_max").jqxNumberInput('decimal')),
+			st_color_min: parseFloat($("#st_color_min").jqxNumberInput('decimal')),
+			st_color_max: parseFloat($("#st_color_max").jqxNumberInput('decimal')),
+			st_carb_min: parseFloat($("#st_carb_min").jqxNumberInput('decimal')),
+			st_carb_max: parseFloat($("#st_carb_max").jqxNumberInput('decimal')),
+			st_abv_min: parseFloat($("#st_abv_min").jqxNumberInput('decimal')),
+			st_abv_max: parseFloat($("#st_abv_max").jqxNumberInput('decimal')),
+			type: $("#type").val(),
+			batch_size: parseFloat($("#batch_size").jqxNumberInput('decimal')),
+			boil_size: parseFloat($("#boil_size").jqxNumberInput('decimal')),
+			boil_time: parseFloat($("#boil_time").jqxNumberInput('decimal')),
+			efficiency: parseFloat($("#efficiency").jqxNumberInput('decimal')),
+			est_og: parseFloat($("#est_og").jqxNumberInput('decimal')),
+			est_fg: parseFloat($("#est_fg").jqxNumberInput('decimal')),
+			est_abv: parseFloat($("#est_abv").jqxNumberInput('decimal')),
+			est_color: parseFloat($("#est_color").jqxNumberInput('decimal')),
+			color_method: $("#color_method").val(),
+			est_ibu: parseFloat($("#est_ibu").jqxNumberInput('decimal')),
+			ibu_method: $("#ibu_method").val(),
+			est_carb: parseFloat($("#est_carb").jqxNumberInput('decimal')),
+			mash_name: $("#mash_name").val(),
+			mash_ph: parseFloat($("#mash_ph").jqxNumberInput('decimal')),
+			sparge_temp: parseFloat($("#sparge_temp").jqxNumberInput('decimal')),
+			sparge_ph: parseFloat($("#sparge_ph").jqxNumberInput('decimal')),
+			sparge_volume: parseFloat($("#sparge_volume").jqxNumberInput('decimal')),
+		//      sparge_acid_type: $("#sparge_acid_type").val(),
+		//      sparge_acid_perc: parseFloat($("#sparge_acid_perc").jqxNumberInput('decimal')),
+		//      sparge_acid_amount: parseFloat($("#sparge_acid_amount").jqxNumberInput('decimal')),
+			calc_acid: $("#calc_acid").val(),
+			w1_name: $("#w1_name").val(),
+			w1_amount: parseFloat($("#w1_amount").jqxNumberInput('decimal')),
+			w1_calcium: parseFloat($("#w1_calcium").jqxNumberInput('decimal')),
+			w1_sulfate: parseFloat($("#w1_sulfate").jqxNumberInput('decimal')),
+			w1_chloride: parseFloat($("#w1_chloride").jqxNumberInput('decimal')),
+			w1_sodium: parseFloat($("#w1_sodium").jqxNumberInput('decimal')),
+			w1_magnesium: parseFloat($("#w1_magnesium").jqxNumberInput('decimal')),
+			w1_total_alkalinity: parseFloat($("#w1_total_alkalinity").jqxNumberInput('decimal')),
+			w1_ph: parseFloat($("#w1_ph").jqxNumberInput('decimal')),
+			w1_cost: dataRecord.w1_cost,
+			w2_name: $("#w2_name").val(),
+			w2_amount: parseFloat($("#w2_amount").jqxNumberInput('decimal')),
+			w2_calcium: parseFloat($("#w2_calcium").jqxNumberInput('decimal')),
+			w2_sulfate: parseFloat($("#w2_sulfate").jqxNumberInput('decimal')),
+			w2_chloride: parseFloat($("#w2_chloride").jqxNumberInput('decimal')),
+			w2_sodium: parseFloat($("#w2_sodium").jqxNumberInput('decimal')),
+			w2_magnesium: parseFloat($("#w2_magnesium").jqxNumberInput('decimal')),
+			w2_total_alkalinity: parseFloat($("#w2_total_alkalinity").jqxNumberInput('decimal')),
+			w2_ph: parseFloat($("#w2_ph").jqxNumberInput('decimal')),
+			w2_cost: dataRecord.w2_cost,
+			fermentables: fermentablerow,
+			hops: hoprow,
+			miscs: miscrow,
+			yeasts: yeastrow,
+			mashs: mashrow
 		};
 		var data = "update=true&" + $.param(row);
 		$.ajax({
--- a/www/js/prod_inprod.js	Mon Dec 24 15:52:11 2018 +0100
+++ b/www/js/prod_inprod.js	Mon Dec 24 23:10:52 2018 +0100
@@ -27,7 +27,7 @@
 		cache: false,
 		datafields: [
 			{ name: 'record', type: 'number' },
-			{ name: 'pname', type: 'string' },
+			{ name: 'name', type: 'string' },
 			{ name: 'code', type: 'string' },
 			{ name: 'birth', type: 'string' },
 			{ name: 'stage', type: 'string' },
@@ -61,7 +61,7 @@
 		columns: [
 			{ text: 'Datum', datafield: 'birth', width: 120 },
 			{ text: 'Code', datafield: 'code', width: 120 },
-			{ text: 'Naam', datafield: 'pname' },
+			{ text: 'Naam', datafield: 'name' },
 			{ text: 'Fase', datafield: 'stage', width: 130 },
 			{ text: 'Wijzig', datafield: 'Edit', width: 120, align: 'center', columntype: 'button', cellsrenderer: function () {
 				return "Wijzig";
--- a/www/prod_edit.php	Mon Dec 24 15:52:11 2018 +0100
+++ b/www/prod_edit.php	Mon Dec 24 23:10:52 2018 +0100
@@ -5,54 +5,88 @@
 
    <div id='jqxTabs'>
     <ul>
-     <li style="margin-left: 30px;">Algemeen</li>
+     <li>Algemeen</li>
      <li>Apparatuur</li>
+     <li>Vergistbaar</li>
+     <li>Hoppen</li>
+     <li>Diversen</li>
+     <li>Gist</li>
+     <li>Maischen</li>
+     <li>Water</li>
      <li>Brouwdag</li>
+     <li>Vergisten</li>
+     <li>Verpakken</li>
+     <li>Proeven</li>
     </ul>
 
-    <div>
+    <div> <!-- tab algemeen -->
      <div style="overflow: hidden;">
       <table style="width: 100%;">
        <tr>
-        <td style="vertical-align: top;">
-         <table>
-          <tr>
-           <td style="vertical-align: top; float: right; padding: 3px;">Brouw naam:</td>
-           <td align="left" colspan="5" style="vertical-align: top; padding: 3px;"><input id="pname" /></td>
-          </tr>
-          <tr>
-           <td style="vertical-align: top; float: right; padding: 3px;">Brouw code:</td>
-           <td align="left" style="vertical-align: top; padding: 3px;"><input id="code" /></td>
-           <td style="vertical-align: top; float: right; padding: 3px;">Start planning:</td>
-           <td align="left" style="vertical-align: top;"><div id="birth"></div></td>
-           <td style="vertical-align: top; float: right; padding: 3px;">Brouw fase:</td>
-	   <td align="left" style="vertical-align: top; padding: 3px;"><input id="stage" readonly /></td>
-          </tr>
-          <tr>
-           <td align="right" style="vertical-align: top;">Brouw log:</td>
-           <td align="left"><div id="log_brew"></div></td>
-           <td align="right" style="vertical-align: top;">Vergisting log:</td>
-           <td align="left"><div id="log_fermentation"></div></td>
-           <td align="right" style="vertical-align: top;">Ingredienten afgeboekt:</td>
-	   <td align="left"><div id="inventory_reduced"></div></td>
-          </tr>
-          <tr>
-           <td align="right" style="vertical-align: top;">Afgesloten:</td>
-	   <td align="left" colspan="5" ><div id="plocked"></div></td>
-          </tr>
-          <tr>
-           <td style="vertical-align: top; float: right; padding: 3px;">Opmerkingen:</td>
-           <td colspan="5" style="padding: 3px;"><textarea id="pnotes"></textarea></td>
-          </tr>
-         </table>
-        </td>
-        <td style="vertical-align: top;">
-         <table>
-          <tr><td style="padding: 5px;"><input style="margin-right: 10px;" type="button" id="rec_edit" value="Recept" /></td></tr>
-          <tr><td style="padding: 5px;"><input style="margin-right: 10px;" type="button" id="brew_log"  value="Brouw log" /></td></tr>
-          <tr><td style="padding: 5px;"><input style="margin-right: 10px;" type="button" id="ferment_log"  value="Vergisting log" /></td></tr>
-         </table>
-        </td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouw naam:</td>
+        <td align="left" colspan="5" style="vertical-align: top; padding: 3px;"><input id="name" /></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouw code:</td>
+        <td align="left" style="vertical-align: top; padding: 3px;"><input id="code" /></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Start planning:</td>
+        <td align="left" style="vertical-align: top;"><div id="birth"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouw fase:</td>
+        <td align="left" style="vertical-align: top; padding: 3px;"><input id="stage" readonly /></td>
+       </tr>
+       <tr>
+        <td align="right" style="vertical-align: top;">Brouw log:</td>
+        <td align="left"><div id="log_brew"></div></td>
+        <td align="right" style="vertical-align: top;">Vergisting log:</td>
+        <td align="left"><div id="log_fermentation"></div></td>
+        <td align="right" style="vertical-align: top;">Ingredienten afgeboekt:</td>
+        <td align="left"><div id="inventory_reduced"></div></td>
+       </tr>
+       <tr>
+        <td align="right" style="vertical-align: top;">Afgesloten:</td>
+        <td align="left" colspan="5" ><div id="locked"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Opmerkingen:</td>
+        <td colspan="5" style="padding: 3px;"><textarea id="notes"></textarea></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouw type:</td>
+        <td align="left" style="padding: 3px;"><div id="type"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouwzaal rendement:</td>
+        <td colspan="3" style="padding: 3px;"><div id="efficiency"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Brouw volume:</td>
+        <td style="padding: 3px;"><div id="batch_size"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Kooktijd minuten:</td>
+        <td style="padding: 3px;"><div id="boil_time"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Kook volume:</td>
+        <td style="padding: 3px;"><div id="boil_size"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Start SG:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_og"></div><div style="float: left; margin-left: 15px;" id="st_og_min"></div><div style="float: left; margin-left: 5px;" id="st_og_max"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Eind SG:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_fg"></div><div style="float: left; margin-left: 15px;" id="st_fg_min"></div><div style="float: left; margin-left: 5px;" id="st_fg_max"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Alcohol vol%:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_abv"></div><div style="float: left; margin-left: 15px;" id="st_abv_min"></div><div style="float: left; margin-left: 5px;" id="st_abv_max"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Kleur:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_color"></div><div style="float: left; margin-left: 15px;" id="st_color_min"></div><div style="float: left; margin-left: 5px;" id="st_color_max"></div><div id="bcolor" class='ebccolor'> </div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Kleur methode:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="color_method"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Koolzuur vol:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_carb"></div><div style="float: left; margin-left: 15px;" id="st_carb_min"></div><div style="float: left; margin-left: 5px;" id="st_carb_max"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Bitterheid:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_ibu"></div><div style="float: left; margin-left: 15px;" id="st_ibu_min"></div><div style="float: left; margin-left: 5px;" id="st_ibu_max"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Bitterheid methode:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="ibu_method"></div></td>
+        <td></td>
+        <td></td>
        </tr>
       </table>
       <div style="float: right; margin-top: 30px; margin-bottom: 10px;">
@@ -65,7 +99,7 @@
      </div>
     </div>
 
-    <div> <!-- Tab 2 -->
+    <div> <!-- Apparatuur -->
      <div style="overflow: hidden;">
       <table style="width: 100%;">
        <tr>
@@ -137,6 +171,231 @@
      </div>
     </div>
 
+    <div> <!-- tab vergistbaar -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Kleur:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_color2"></div><div id="bcolor2" class='ebccolor'> </div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Percentage moutstort:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="perc_malts"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Begin SG:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="est_og2"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Percentage suiker:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="perc_sugars"></div></td>
+       </tr>
+       <tr>
+        <td colspan="2"></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Percentage cara:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="perc_cara"></div></td>
+       </tr>
+       <tr>
+        <td colspan="4"><div id="fermentableGrid"></div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab vergistbaar -->
+
+    <div> <!-- tab hoppen -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Bitterheid IBU:</td>
+        <td style="padding: 3px;"><div id="est_ibu2"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Smaak bijdrage:</td>
+        <td style="padding: 3px;"><div id="hop_flavour"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Aroma bijdrage:</td>
+        <td style="padding: 3px;"><div id="hop_aroma"></div></td>
+       </tr>
+       <tr>
+        <td align="right" style="vertical-align: top;">Hoppen:</td>
+        <td align="left" colspan="5"><div id="hopGrid"></div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab hoppen -->
+
+    <div> <!-- tab misc -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td align="right" style="vertical-align: top;">Diversen:</td>
+        <td align="left" colspan="3"><div id="miscGrid">Graat</div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab misc -->
+
+    <div> <!-- tab gisten -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td align="right" style="vertical-align: top;">Gisten:</td>
+        <td align="left" colspan="3"><div id="yeastGrid">Graat</div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab gisten -->
+
+    <div> <!-- tab maischen -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Maischchema:</td>
+        <td align="left" style="vertical-align: top; padding: 3px;"><input id="mash_name" /></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Maish pH:</td>
+        <td style="padding: 3px;"><div id="mash_ph"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Spoelwater temp:</td>
+        <td style="padding: 3px;"><div id="sparge_temp"></div></td>
+       </tr>
+       <tr>
+        <td align="right" style="vertical-align: top; padding: 3px;">Stappen:</td>
+        <td align="left" colspan="5" style="padding: 3px;"><div id="mashGrid">Graat</div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab maischen -->
+
+    <div> <!-- tab water -->
+     <div style="overflow: hidden;">
+      <table style="width: 100%;">
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Bitterheidsindex:</td>
+        <td style="padding: 3px;"><div id="tgt_bu"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Richtgetal Cl/SO4:</td>
+        <td style="padding: 3px;"><div id="tgt_cl_so4"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Doel maisch pH:</td>
+        <td style="padding: 3px;"><div id="tgt_mash_ph"></div></td>
+       </tr>
+       <tr>
+        <td></td>
+        <td align="left" colspan="5">
+         <div id='water_totals'>
+          <table style="width: 100%;">
+           <caption>Water overzicht</caption>
+           <tr>
+            <td align="left" style="vertical-align: top; padding: 3px;">Water profiel</td>
+            <td style="width: 97px; padding: 3px;">Volume</td>
+            <td style="width: 77px; padding: 3px;">Ca</td>
+            <td style="width: 77px; padding: 3px;">Mg</td>
+            <td style="width: 77px; padding: 3px;">Na</td>
+            <td style="width: 77px; padding: 3px;">CaCO3</td>
+            <td style="width: 77px; padding: 3px;">Cl</td>
+            <td style="width: 77px; padding: 3px;">SO4</td>
+            <td style="width: 77px; padding: 3px;">pH</td>
+           </tr>
+           <tr>
+            <td><div id="w1_name"></div></td>
+            <td><div id="w1_amount"></div></td>
+            <td><div id="w1_calcium"></div></td>
+            <td><div id="w1_magnesium"></div></td>
+            <td><div id="w1_sodium"></div></td>
+            <td><div id="w1_total_alkalinity"></div></td>
+            <td><div id="w1_chloride"></div></td>
+            <td><div id="w1_sulfate"></div></td>
+            <td><div id="w1_ph"></div></td>
+           </tr>
+           <tr>
+            <td><div id="w2_name"></div></td>
+            <td><div id="w2_amount"></div></td>
+            <td><div id="w2_calcium"></div></td>
+            <td><div id="w2_magnesium"></div></td>
+            <td><div id="w2_sodium"></div></td>
+            <td><div id="w2_total_alkalinity"></div></td>
+            <td><div id="w2_chloride"></div></td>
+            <td><div id="w2_sulfate"></div></td>
+            <td><div id="w2_ph"></div></td>
+           </tr>
+           <tr>
+            <td style="vertical-align: top; padding: 3px; float: left;">Gemengd water:</td>
+            <td><div id="wg_amount"></div></td>
+            <td><div id="wg_calcium"></div></td>
+            <td><div id="wg_magnesium"></div></td>
+            <td><div id="wg_sodium"></div></td>
+            <td><div id="wg_total_alkalinity"></div></td>
+            <td><div id="wg_chloride"></div></td>
+            <td><div id="wg_sulfate"></div></td>
+            <td><div id="wg_ph"></div></td>
+           </tr>
+           <tr>
+            <td style="vertical-align: top; padding: 3px; float: left;">Behandeld water:</td>
+            <td><div></div></td>
+            <td><div id="wb_calcium"></div></td>
+            <td><div id="wb_magnesium"></div></td>
+            <td><div id="wb_sodium"></div></td>
+            <td><div id="wb_total_alkalinity"></div></td>
+            <td><div id="wb_chloride"></div></td>
+            <td><div id="wb_sulfate"></div></td>
+            <td><div id="wb_ph"></div></td>
+           </tr>
+           <tr>
+            <td style="vertical-align: top; padding: 3px; float: left;">Resultaat:</td>
+            <td><div></div></td>
+            <td><div id="wr_calcium"></div></td>
+            <td><div id="wr_magnesium"></div></td>
+            <td><div id="wr_sodium"></div></td>
+            <td><div id="wr_total_alkalinity"></div></td>
+            <td><div id="wr_chloride"></div></td>
+            <td><div id="wr_sulfate"></div></td>
+            <td><div id="wr_ph"></div></td>
+           </tr>
+           <tr>
+            <td><div id="pr_name"></div></td>
+            <td><div></div></td>
+            <td><div id="pr_calcium"></div></td>
+            <td><div id="pr_magnesium"></div></td>
+            <td><div id="pr_sodium"></div></td>
+            <td><div id="pr_total_alkalinity"></div></td>
+            <td><div id="pr_chloride"></div></td>
+            <td><div id="pr_sulfate"></div></td>
+            <td></td>
+           </tr>
+          </table>
+         </div>
+        </td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Calciumchloride (CaCl2):</td>
+        <td style="padding: 3px;"><div id="wa_cacl2"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Automatisch pH aanpassen:</td>
+        <td style="padding: 3px;"><div id="calc_acid"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Spoelwater volume:</td>
+        <td style="padding: 3px;"><div id="sparge_volume"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Gips (CaSO4):</td>
+        <td style="padding: 3px;"><div id="wa_caso4"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Ontzuren met:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="wa_base_name"></div><div style="float: left; margin-left: 15px;" id="wa_base"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Spoelwater bron:</td>
+        <td style="padding: 3px;"><div id="sparge_source"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Epsom zout (MgSO4):</td>
+        <td style="padding: 3px;"><div id="wa_mgso4"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Aanzuren met:</td>
+        <td style="padding: 3px;"><div style="float: left;" id="wa_acid_name"></div><div style="float: left; margin-left: 15px;" id="wa_acid"></div><div style="float: left; margin-left: 15px;" id="wa_acid_perc"></div></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Spoelwater pH:</td>
+        <td style="padding: 3px;"><div id="sparge_ph"></div></td>
+       </tr>
+       <tr>
+        <td style="vertical-align: top; float: right; padding: 3px;">Keukenzout (NaCl):</td>
+        <td style="padding: 3px;"><div id="wa_nacl"></div></td>
+        <td colspan="2"></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Aanzuren met:</td>
+        <td style="padding: 3px;"><div id="sparge_acid_type"></div></td>
+       </tr>
+       <tr>
+        <td colspan="4"></td>
+        <td style="vertical-align: top; float: right; padding: 3px;">Aanzuren hoeveelheid:</td>
+        <td style="padding: 3px;"><div id="sparge_acid_amount"></div></td>
+       </tr>
+      </table>
+     </div>
+    </div> <!-- tab water -->
+
     <div style="overflow: hidden;"> <!-- Brewday -->
      <table style="width: 100%;">
       <tr>
@@ -254,7 +513,22 @@
        <td><div style="float: left;" id="brew_fermenter_color"></div><div style="float: left; margin-left: 35px;" id="est_color"></div></td>
       </tr>
      </table>
-    </div>
+    </div> <!-- Brewday -->
+
+    <div> <!-- Vergisten -->
+     <div style="overflow: hidden;">
+     </div>
+    </div> <!-- Vergisten -->
+
+    <div> <!-- Verpakken -->
+     <div style="overflow: hidden;">
+     </div>
+    </div> <!-- Verpakken -->
+
+    <div> <!-- Proeven -->
+     <div style="overflow: hidden;">
+     </div>
+    </div> <!-- Proeven -->
 
    </div> <!-- Tabs -->
 

mercurial