Guest User

Picobrew Zymatic Code

a guest
Oct 3rd, 2017
204
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //Global Variables
  2. var pageLoaded = false;
  3. var advFermSteps = [];
  4. var advFermHeaders = [];
  5. var beerStylesJSON;
  6. var styleGuides = [];
  7. var fermentableIngredients;
  8. var hopIngredients;
  9. var maxFermentableCount = 12;
  10. var maxHopCount = 12;
  11. var maxFermStepCount = 5;
  12. var recipeGrains = [];
  13. var recipeGrainHeaders = [];
  14. var recipeHops = [];
  15. var recipeHopsHeaders = [];
  16.  
  17.  
  18. var recipeAdjType = new Array();
  19. var recipeAdjAmt = new Array();
  20. var recipeAdjTime = new Array();
  21.  
  22. var recipeFermType = new Array();
  23. var recipeFermTemp = new Array();
  24. var recipeFermTime = new Array();
  25.  
  26. var grainSum = 0;
  27. var gravitySum = 0;
  28. var colorSum = 0;
  29.  
  30. //constant
  31. var maxGrainTypes = 8;
  32.  
  33. //added efficiency var for scope
  34. var eff = 0;
  35.  
  36. //get variable values
  37. var origBit;
  38. var recipeName;
  39. var recipeAuth;
  40.  
  41. var type;
  42.  
  43. var H2O;
  44. var origName;
  45. var origAuth;
  46. var mashTemp;
  47. var mashTime;
  48. var mashType;
  49. var boilTime;
  50. var boilTemp;
  51. var boilType;
  52. var ABV;
  53. var OG;
  54. var FG;
  55. var IBU;
  56. var SRM;
  57. var fermentorWort = $("[name = beerWort]").val();
  58. //var fermentorWort = finBeer + 0.2;
  59. var yeastAA;
  60. var yeastPitch;
  61.  
  62.  
  63. var mashType;
  64.  
  65. var custText;
  66. var guid;
  67. var notes;
  68. var cookTemp;
  69. var cookTime;
  70.  
  71. var waterType;
  72. var amend1;
  73. var amend2;
  74. var amend3;
  75. var amend4;
  76. var amendAmt1;
  77. var amendAmt2;
  78. var amendAmt3;
  79. var amendAmt4;
  80.  
  81. var adj1;
  82. var adjAmt1;
  83. var adjTime1;
  84. var adjUse1;
  85.  
  86. var adj2;
  87. var adjAmt2;
  88. var adjTime2;
  89. var adjUse2;
  90.  
  91. var beerText;
  92.  
  93. //String Outputs for US and Metric
  94. var grainLimitUS = 9.0;
  95. var grainLimitMetric = ConvertLBsToKG(grainLimitUS).toFixed(2);
  96. var grainLimitUSString = grainLimitUS.toString() + " lbs";
  97. var grainLimitMetricString = grainLimitMetric.toString() + " kgs";
  98. var finBeerMaxUS = 3.5;
  99. var finBeerMaxMetric = 13.25;
  100. var hopLimitUS = 1.5;
  101. var hopLimitMetric = 43;
  102. var hopLimitUSString = "1.5 oz."
  103. var hopLimitMetricString = "43 grams"
  104. var mashTempMaxUS = 207;
  105. var mashTempMaxMetric = ConvertCelciusToFahrenheit(207);
  106. //Yeast Data
  107. var yeast;
  108. var yeastName = new Array();
  109. var yeastMfg = new Array(); //array
  110. var yeastNumber = new Array(); //array
  111. var yeastMinAtten = new Array(); //array
  112. var yeastMaxAtten = new Array(); //array
  113. var yeastMinTemp = new Array(); //array
  114. var yeastMaxTemp = new Array(); //array
  115. var yeastFloculation = new Array(); //array
  116. var yeastID = new Array();
  117.  
  118. //Global Functions
  119. function ConverGToLiters(gallons)
  120. {
  121.     return 3.78541178 * gallons;
  122. }
  123.  
  124. function ConvertLBsToKG(pounds)
  125. {
  126.     return 0.45359237 * pounds;
  127. }
  128. function ConvertOZtoKG(oz)
  129. {
  130.     return 0.0283495231 * oz;
  131. }
  132.  
  133. function ConvertCelciusToFahrenheit(celcius)
  134. {
  135.     return parseFloat((celcius * 1.8) + 32.0);
  136. }
  137.  
  138. function RoundedConvertFahrenheitToCelcius(fahrenheit)
  139. {
  140.     return parseFloat(((fahrenheit - 32) / 1.8).toFixed(1));
  141. }
  142.  
  143. function RoundedConvertLitersToGallons(liters)
  144. {
  145.     return parseFloat((liters * 0.26417205).toFixed(2));
  146. }
  147.  
  148. function RoundedConvertKgToLbs(kg)
  149. {
  150.     return parseFloat((kg * 2.20462262).toFixed(2));
  151. }
  152.  
  153. function RoundedConvertGallonToLiters(gallon)
  154. {
  155.     return parseFloat((gallon * 3.78541178).toFixed(2));
  156. }
  157.  
  158. function ConvertOunceToGram(ounces)
  159. {
  160.     return ounces * 28.3495231;
  161. }
  162.  
  163. function ConvertGramToOunce(grams)
  164. {
  165.     return grams * 0.03527396;
  166. }
  167.  
  168. $(document).ready(function ()
  169. {
  170.     //page load modal
  171.     $('#loadModal').modal('show');
  172.  
  173.     var grainLoad = false;
  174.     var hopLoad = false;
  175.     var yeastLoad = false;
  176.     var styleLoad = false;
  177.  
  178.     //Load variables when document is loaded
  179.     custText = parseInt($("[name = custText]").val());
  180.     amend1 = $("#waterAmend1").val();
  181.     amend2 = $("#waterAmend2").val();
  182.     amend3 = $("#waterAmend3").val();
  183.     amend4 = $("#waterAmend4").val();
  184.     H2O = $("#H2O").text();
  185.  
  186.     origBit = $("[name = origBit]").val();
  187.     recipeName = $("[name = recipe_name]").val();
  188.     recipeAuth = $("[name = recAuth]").val();
  189.  
  190.     type = $("[name = recipe_type]").val();
  191.  
  192.  
  193.     origName = $("#origName").val();
  194.     origAuth = $("#origAuth").val();
  195.     mashTemp = $("[name = mashTemp]").val();
  196.     mashTime = $("[name = mashTime]").val();
  197.     mashType = $("[name = mash_type]").val();
  198.     boilTime = parseInt($("#boilTime").val());
  199.     boilTemp = $("[name = boilTemp]").val();
  200.     boilType = $("#boilType").val();
  201.     ABV = $("#ABV").text();
  202.     OG = $("#OG").val();
  203.     FG = $("#FG").text();
  204.     IBU = $("#IBU").val();
  205.     SRM = $("#SRM").text();
  206.     //var fermentorWort = $("[name = beerWort]").val();
  207.     fermentorWort = finBeer + 0.2;
  208.     yeastAA = $("[name = yeastExp]").val();
  209.     yeastPitch = $("[name = yeastPitch]").val();
  210.     mashType = $("#mashType").val();
  211.  
  212.     guid = $("[name = previous]").val();
  213.     notes = $("[name = recipe_Notes]").val();
  214.     cookTemp = unitInUS ? $("[name = cookTemp]").val() : ConvertCelciusToFahrenheit($("[name = cookTemp]").val());
  215.     cookTime = $("[name = cookTime]").val();
  216.  
  217.     waterType = $("[name=water]").val();
  218.     amendAmt1 = $("#amendAmt1").val();
  219.     amendAmt2 = $("#amendAmt2").val();
  220.     amendAmt3 = $("#amendAmt3").val();
  221.     amendAmt4 = $("#amendAmt4").val();
  222.  
  223.     adj1 = $("[name=adj1]").val();
  224.     adjAmt1 = $("[name=adjAmt1]").val();
  225.     adjTime1 = $("[name=adjTime1]").val();
  226.     adjUse1 = $("[name=adjUse1]").val();
  227.  
  228.     adj2 = $("[name=adj2]").val();
  229.     adjAmt2 = $("[name=adjAmt2]").val();
  230.     adjTime2 = $("[name=adjTime2]").val();
  231.     adjUse2 = $("[name=adjUse2]").val();
  232.  
  233.     beerText = $("#beer-text").val();
  234.    
  235.  
  236.     if (!!fermentablesList && fermentablesList.length > 0) {
  237.  
  238.     }
  239.     //Document Ready Helper Function
  240.     function loadLogic(type)
  241.     {
  242.         //Set the loaded variable to true whenthe AJAX call has completed
  243.         switch (type)
  244.         {
  245.             case 0:
  246.                 grainLoad = true;
  247.                 break;
  248.             case 1:
  249.                 hopLoad = true;
  250.                 break;
  251.             case 2:
  252.                 yeastLoad = true;
  253.                 break;
  254.             case 3:
  255.                 styleLoad = true;
  256.                 break;
  257.             default:
  258.                 break;
  259.         }
  260.  
  261.         //When all AJAX calls have been completed, return access of crafter to user
  262.         if (grainLoad && hopLoad && yeastLoad && styleLoad)
  263.         {
  264.             //Populate the dynamic data
  265.             populateFermentationSteps();
  266.             checkAdvancedFermSchedule();
  267.             init();
  268.             //Set the excessive boil time and don't allow any value less than 0
  269.             $("#excessBoil").val(Math.max(0, $("#boilTime").val() - parseInt($("#hopBoil").html())));
  270.             //hide loading modal
  271.             $('#loadModal').modal('hide');
  272.             loaded();
  273.         }
  274.     }
  275.  
  276.  
  277.  
  278.     $("#saveButtonOther").hide();
  279.  
  280.     //Get BeerStyles and save into JSON interpreted variable;
  281.     $.ajax({
  282.         url: "/Json/BeerJson.cshtml",
  283.         dataType: "json",
  284.         success: function (data)
  285.         {
  286.             beerStylesJSON = data;
  287.             populateBeverageStyles();
  288.             loadLogic(3);
  289.         }
  290.     });
  291.  
  292.     //Get Fermentables\Grains
  293.     $.ajax({
  294.         url: "/Json/grainsJson.cshtml",
  295.         dataType: "json",
  296.         success: function (data)
  297.         {
  298.             fermentableIngredients = data;
  299.             populateFermentablesIngredients();
  300.             if (!!fermentablesList && fermentablesList.length > 0) {
  301.                 recipeGrains = fermentablesList;
  302.                 for (var i = 0; i < recipeGrains.length; i++) {
  303.                     recipeGrains[i].Ingredient = fermentableIngredients[recipeGrains[i].FermentableID];
  304.                 }
  305.             }
  306.             organizeGrains();
  307.             loadLogic(0);
  308.         }
  309.     });
  310.  
  311.     //hops
  312.     $.ajax({
  313.         url: "/Json/hopsJson.cshtml",
  314.         dataType: "json",
  315.         success: function (data)
  316.         {
  317.             hopIngredients = data;
  318.             populateHopIngredients();
  319.             if (!!hopList && hopList.length > 0) {
  320.                 recipeHops = hopList;
  321.                 for (var i = 0; i < recipeHops.length; i++) {
  322.                     recipeHops[i].Ingredient = hopIngredients[recipeHops[i].HopID];
  323.                 }
  324.             }
  325.             organizeHops();
  326.             loadLogic(1);
  327.         }
  328.     });
  329.  
  330.  
  331.  
  332.     //var recipeYeastType = 0;
  333.  
  334.     $.ajax({
  335.         url: "/Json/yeastJson.cshtml",
  336.         dataType: "json",
  337.         success: function (data)
  338.         {
  339.             yeast = data;
  340.             for (var i = 0; i < yeast.length; i++)
  341.             {
  342.                 var yID = yeast[i].YeastID
  343.                 yeastName[yID] = yeast[i].Name;
  344.                 yeastMfg[yID] = yeast[i].Laboratory; //array
  345.                 yeastNumber[yID] = yeast[i].ProductID; //array
  346.                 yeastMinAtten[yID] = yeast[i].MinAtten; //array
  347.                 yeastMaxAtten[yID] = yeast[i].MaxAtten; //array
  348.                 yeastMinTemp[yID] = yeast[i].MinTemp; //array
  349.                 yeastMaxTemp[yID] = yeast[i].MaxTemp; //array
  350.                 yeastFloculation[yID] = yeast[i].FlocculationCode; //array
  351.                 yeastID[yID] = yeast[i].YeastID;
  352.             }
  353.             loadLogic(2);
  354.         }
  355.     });
  356.  
  357.  
  358.  
  359.     //updates bounds for default style
  360.     function loaded()
  361.     {
  362.         //?? Why run twice?
  363.         vitaStatsChangeBounds();
  364.         textRecipeDescription();
  365.         pageLoaded = true;
  366.         if (!unitInUS)
  367.         {
  368.             //Change Units and re-run validations
  369.             unitchange();
  370.         }
  371.         recalcForm();
  372.  
  373.         vitaStatsChangeBounds();
  374.         textRecipeDescription();
  375.         //recalculate the form
  376.         recalcForm();
  377.     }
  378.  
  379.     function validHopTime()
  380.     {
  381.         var isValid = false;
  382.         var count = 0;
  383.         var hopTimes = new Array();
  384.         for (var i = 1; i < 8; i++)
  385.         {
  386.             var time = parseInt($('[name=hopTime' + i + ']').val());
  387.             if (time != 0)
  388.             {
  389.                 var index = hopTimes.indexOf(time);
  390.                 if (index < 0)
  391.                 {
  392.                     hopTimes[count] = time;
  393.                     count++;
  394.                 }
  395.             }
  396.         }
  397.         if (count > 4)
  398.         {
  399.             return false;
  400.         }
  401.         return true;
  402.     }
  403.  
  404.     $("[name = yeast]").change(function ()
  405.     {
  406.         var temp = this.value;
  407.         $('[name=yeast]').removeClass("error");
  408.         if (temp == 0)
  409.         {
  410.             $("#yeastAtten").html("0 - 0");
  411.             $("#yeastTemp").html("0 - 0");
  412.             $("[name=yeastExp]").val(70);
  413.             $("[name=yeastPitch]").val(unitInUS ? 75 : RoundedConvertFahrenheitToCelcius(75));
  414.             $("[name = yeast]").addClass("error");
  415.             var warning = "Yeast is a required field";
  416.             $('#warnings-yeasts').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  417.         }
  418.         else
  419.         {
  420.             $("[name = yeast]").removeClass("error");
  421.             $('#warnings-yeasts').empty();
  422.             $("#yeastAtten").html(yeastMinAtten[temp] + " - " + yeastMaxAtten[temp]);
  423.             $("#yeastTemp").html((unitInUS ? yeastMinTemp[temp] : RoundedConvertFahrenheitToCelcius(yeastMinTemp[temp])) + " - " + (unitInUS ? yeastMaxTemp[temp] : RoundedConvertFahrenheitToCelcius(yeastMaxTemp[temp])));
  424.             $("[name=yeastExp]").val(parseInt((yeastMinAtten[temp] + yeastMaxAtten[temp]) / 2));
  425.             yeastPitch = parseInt((yeastMinTemp[temp] + yeastMaxTemp[temp]) / 2);
  426.             $("[name=yeastPitch]").val(unitInUS ? yeastPitch : RoundedConvertFahrenheitToCelcius(yeastPitch));
  427.  
  428.             $('[name=yeastPitch]').removeClass("error");
  429.             $('[name=yeastExp]').removeClass("error");
  430.         }
  431.         fermScheduleChange($("#fermTypeSelection")[0]);
  432.         recalcForm();
  433.     });
  434.  
  435.     $("[name=finBeer]").change(function ()
  436.     {
  437.         if (unitInUS)
  438.         {
  439.             finBeer = parseFloat($("[name=finBeer]").val());
  440.         } else
  441.         {
  442.             finBeer = RoundedConvertLitersToGallons(parseFloat($("[name=finBeer]").val()));
  443.         }
  444.         recalcForm();
  445.     });
  446.  
  447.     function validateAmendment(target) {
  448.         var index = target.dataset.index;
  449.         if (!!index) {
  450.             var input = $("#amendAmt" + index)[0];
  451.             var amount = parseInt(input.value);
  452.             if (amount < 0) {
  453.                 $(input).val(0);
  454.                 var warning = "Water amendment amount " + index + " is not a number. Reset to 0.";
  455.                 $('#warnings-water').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  456.             }
  457.         }
  458.     }
  459.  
  460.     $(document).on("change", ".water-amend-amount", function () {
  461.         validateAmendment(this);
  462.     });
  463.  
  464.     function validYeast()
  465.     {
  466.         var validExp = false;
  467.         var validPitch = false;
  468.         var maximum = 100;
  469.         if (!unitInUS)
  470.         {
  471.             maximum = RoundedConvertFahrenheitToCelcius(maximum);
  472.         }
  473.         $('#all').wrap('<form id="temp_form_id" />');
  474.         $('#temp_form_id').validate({
  475.             rules: {
  476.                 yeastPitch: {
  477.                     required: true,
  478.                     number: true,
  479.                     max: maximum,
  480.                     min: 0
  481.                 },
  482.                 yeastExp: {
  483.                     required: true,
  484.                     number: true,
  485.                     max: 100,
  486.                     min: 0
  487.                 }
  488.             },
  489.             errorPlacement: function ()
  490.             {
  491.                 return false;
  492.             }
  493.         });
  494.         $('#warnings-yeasts').empty();
  495.         validPitch = $('[name=yeastPitch]').valid();
  496.         validExp = $('[name=yeastExp]').valid();
  497.         $('#all').unwrap();
  498.         count = 0;
  499.         if (!validPitch)
  500.         {
  501.             if (unitInUS)
  502.             {
  503.                 $('[name=yeastPitch]').val(70);
  504.             } else
  505.             {
  506.                 $('[name=yeastPitch]').val(RoundedConvertFahrenheitToCelcius(70));
  507.             }
  508.  
  509.             count++;
  510.             var warning = "Yeast pitch temp is out of range, must be between 0 and " + (unitInUS ? "100 F" : RoundedConvertFahrenheitToCelcius(100) + "C") + ". Reset.";
  511.             $('#warnings-yeasts').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  512.         } else
  513.         {
  514.             yeastPitch = unitInUS ? $('[name=yeastPitch]').val() : ConvertCelciusToFahrenheit($('[name=yeastPitch]').val());
  515.         }
  516.         if (!validExp)
  517.         {
  518.             $('[name=yeastExp]').val(75);
  519.             count++;
  520.             var warning = "Yeast expected AA is out of range, must be between 0 and 100. Reset.";
  521.             $('#warnings-yeasts').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  522.         }
  523.         if (count > 0)
  524.         {
  525.             return false;
  526.         }
  527.         return true;
  528.     }
  529.  
  530.     $('[name=yeastExp]').change(function ()
  531.     {
  532.         var valid = validYeast();
  533.         if (valid)
  534.         {
  535.             recalcForm();
  536.         }
  537.  
  538.     });
  539.  
  540.     $('[name=yeastPitch]').change(function ()
  541.     {
  542.         validYeast();
  543.         fermScheduleChange($("#fermTypeSelection")[0]);
  544.     });
  545.  
  546.     function validMash()
  547.     {
  548.         var maximum = 207;
  549.         if (!unitInUS)
  550.         {
  551.             maximum = RoundedConvertFahrenheitToCelcius(maximum);
  552.         }
  553.         var validTemp = false;
  554.         var validTime = false;
  555.         $('#all').wrap('<form id="temp_form_id" />');
  556.         $('#temp_form_id').validate({
  557.             rules: {
  558.                 mashTemp: {
  559.                     required: true,
  560.                     max: maximum,
  561.                     min: 0
  562.                 },
  563.                 mashTime: {
  564.                     required: true,
  565.                     digits: true,
  566.                     max: 360,
  567.                     min: 0
  568.                 }
  569.             },
  570.             errorPlacement: function ()
  571.             {
  572.                 return false;
  573.             }
  574.         });
  575.         $('#warnings-mash').empty();
  576.         validTemp = $('[name=mashTemp]').valid();
  577.         validTime = $('[name=mashTime]').valid();
  578.         $('#all').unwrap();
  579.         count = 0;
  580.         if (!validTemp)
  581.         {
  582.             if (unitInUS)
  583.             {
  584.                 $('[name=mashTemp]').val(152);
  585.             } else
  586.             {
  587.                 $('[name=mashTemp]').val(RoundedConvertFahrenheitToCelcius(152));
  588.             }
  589.             count++;
  590.             var warning = "Mash Temp out of range (" + (unitInUS ? "0-207 F" : "0 - 97 C") + "). Reset to default.";
  591.             $('#warnings-mash').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  592.         }
  593.         if (!validTime)
  594.         {
  595.             $('[name=mashTime]').val(90);
  596.             count++;
  597.             var warning = "Mash time out of range (max 360). Reset to default.";
  598.             $('#warnings-mash').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  599.         }
  600.         if (count > 0)
  601.         {
  602.             return;
  603.         }
  604.         recalcForm();
  605.     }
  606.  
  607.     $('[name=mashTemp]').change(function ()
  608.     {
  609.         validMash();
  610.     });
  611.  
  612.     $('[name=mashTime]').change(function ()
  613.     {
  614.         validMash();
  615.     });
  616.  
  617.     function validBoil()
  618.     {
  619.         var maximum = 207;
  620.         if (!unitInUS)
  621.         {
  622.             maximum = RoundedConvertFahrenheitToCelcius(maximum);
  623.         }
  624.         var validTemp = false;
  625.         var validTime = false;
  626.         $('#all').wrap('<form id="temp_form_id" />');
  627.         $('#temp_form_id').validate({
  628.             rules: {
  629.                 boilTemp: {
  630.                     required: true,
  631.                     max: maximum,
  632.                     min: 0
  633.                 },
  634.                 boilTime: {
  635.                     required: true,
  636.                     digits: true,
  637.                     max: 360,
  638.                     min: 0
  639.                 }
  640.             },
  641.             errorPlacement: function ()
  642.             {
  643.                 return false;
  644.             }
  645.         });
  646.         $('#warnings-boil').empty();
  647.         validTemp = $('[name=boilTemp]').valid();
  648.         validTime = $('#boilTime').valid();
  649.         $('#all').unwrap();
  650.         count = 0;
  651.         if (!validTemp)
  652.         {
  653.             if (unitInUS)
  654.             {
  655.                 $('[name=boilTemp]').val(207);
  656.             } else
  657.             {
  658.                 $('[name=boilTemp]').val(RoundedConvertFahrenheitToCelcius(207));
  659.             }
  660.             count++;
  661.             var warning = "Boil Temp out of range (" + (unitInUS ? "0-207 F" : "0 - 97 C") + "). Reset to default.";
  662.             $('#warnings-boil').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  663.         }
  664.         if (!validTime)
  665.         {
  666.             $('#boilTime').val(90);
  667.             count++;
  668.             var warning = "Boil time out of range (max 360). Reset to default.";
  669.             $('#warnings-boil').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  670.         }
  671.         if (count > 0)
  672.         {
  673.             return;
  674.         }
  675.         recalcForm();
  676.     }
  677.  
  678.     $('[name=boilTemp]').change(function ()
  679.     {
  680.         validBoil();
  681.     });
  682.  
  683.     $('#boilTime').change(function ()
  684.     {
  685.         validBoil();
  686.     });
  687.  
  688.     function validFerm()
  689.     {
  690.         var validTemp1 = false;
  691.         var validTime1 = false;
  692.         var validTemp2 = false;
  693.         var validTime2 = false;
  694.  
  695.         var maximum = 100;
  696.         if (!unitInUS)
  697.         {
  698.             maximum = RoundedConvertFahrenheitToCelcius(maximum);
  699.         }
  700.         $('#all').wrap('<form id="temp_form_id" />');
  701.         $('#temp_form_id').validate({
  702.             rules: {
  703.                 fermTemp1: {
  704.                     required: true,
  705.                     max: maximum,
  706.                     min: 0
  707.                 },
  708.                 fermTemp2: {
  709.                     required: true,
  710.                     max: maximum,
  711.                     min: 0
  712.                 },
  713.                 fermTime1: {
  714.                     required: true,
  715.                     digits: true,
  716.                     min: 0
  717.                 },
  718.                 fermTime2: {
  719.                     required: true,
  720.                     digits: true,
  721.                     min: 0
  722.                 }
  723.             },
  724.             errorPlacement: function ()
  725.             {
  726.                 return false;
  727.             }
  728.         });
  729.         $('#warnings-ferm').empty();
  730.         validTemp1 = $('[name=fermTemp1]').valid();
  731.         validTime1 = $('[name=fermTime1]').valid();
  732.         validTemp2 = $('[name=fermTemp2]').valid();
  733.         validTime2 = $('[name=fermTime2]').valid();
  734.         $('#all').unwrap();
  735.         count = 0;
  736.         if (!validTemp1)
  737.         {
  738.             $('[name=fermTemp1]').val(0);
  739.             count++;
  740.             var warning = "Ferm temp out of range, reset to 0.";
  741.             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  742.         }
  743.         if (!validTime1)
  744.         {
  745.             $('[name=fermTime1]').val(0);
  746.             count++;
  747.             var warning = "Ferm time out of range, reset to 0.";
  748.             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  749.         }
  750.         if (!validTemp2)
  751.         {
  752.             $('[name=fermTemp2]').val(0);
  753.             count++;
  754.             var warning = "Ferm temp out of range, reset to 0.";
  755.             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  756.         }
  757.         if (!validTime2)
  758.         {
  759.             $('[name=fermTime2]').val(0);
  760.             count++;
  761.             var warning = "Ferm time out of range, must be rounded to the nearest minute greater than zero, reset to 0.";
  762.             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  763.         }
  764.     }
  765.  
  766.     function validAdj()
  767.     {
  768.         var validAmt1 = false;
  769.         var validTime1 = false;
  770.         var validAmt2 = false;
  771.         var validTime2 = false;
  772.         $('#all').wrap('<form id="temp_form_id" />');
  773.         $('#temp_form_id').validate({
  774.             rules: {
  775.                 adjAmt1: {
  776.                     required: true,
  777.                     number: true,
  778.                     min: 0
  779.                 },
  780.                 adjTime1: {
  781.                     required: true,
  782.                     digits: true,
  783.                     min: 0
  784.                 },
  785.                 adjAmt2: {
  786.                     required: true,
  787.                     number: true,
  788.                     min: 0
  789.                 },
  790.                 adjTime2: {
  791.                     required: true,
  792.                     digits: true,
  793.                     min: 0
  794.                 }
  795.             },
  796.             errorPlacement: function ()
  797.             {
  798.                 return false;
  799.             }
  800.         });
  801.         $('#warnings-adj').empty();
  802.         validAmt1 = $('[name=adjAmt1]').valid();
  803.         validTime1 = $('[name=adjTime1]').valid();
  804.         validAmt2 = $('[name=adjAmt2]').valid();
  805.         validTime2 = $('[name=adjTime2]').valid();
  806.         $('#all').unwrap();
  807.         count = 0;
  808.         if (!validAmt1)
  809.         {
  810.             $('[name=adjAmt1]').val(0);
  811.             count++;
  812.             var warning = "Adjunct quantity out of range, reset to 0.";
  813.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  814.         } else
  815.         {
  816.             adjAmt1 = unitInUS ? $("[name=adjAmt1]").val() : ConvertOunceToGram($("[name=adjAmt1]").val()).toFixed(2);
  817.         }
  818.         if (!validTime1)
  819.         {
  820.             $('[name=adjTime1]').val(0);
  821.             count++;
  822.             var warning = "Adjunct time out of range, reset to 0.";
  823.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  824.         }
  825.         if (!validAmt2)
  826.         {
  827.             $('[name=adjAmt2]').val(0);
  828.             count++;
  829.             var warning = "Adjunct quantity out of range, reset to 0.";
  830.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  831.         } else
  832.         {
  833.             adjAmt2 = unitInUS ? $("[name=adjAmt2]").val() : ConvertOunceToGram($("[name=adjAmt2]").val()).toFixed(2);
  834.         }
  835.         if (!validTime2)
  836.         {
  837.             $('[name=adjTime2]').val(0);
  838.             count++;
  839.             var warning = "Adjunct time out of range, must be rounded to the nearest minute greater than zero, reset to 0.";
  840.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  841.         }
  842.  
  843.         recalcForm();
  844.     }
  845.  
  846.     $('[name=adj1]').change(function ()
  847.     {
  848.         if ($('[name=adj1]').val() == 0)
  849.         {
  850.             $('[name=adjAmt1]').val(0);
  851.             $('[name=adjAmt1]').removeClass("error");
  852.             $('[name=adjTime1]').val(0);
  853.             $('[name=adjTime1]').removeClass("error");
  854.             $('[name=adj1]').removeClass("error");
  855.         }
  856.     });
  857.  
  858.     $('[name=adj2]').change(function ()
  859.     {
  860.         if ($('[name=adj2]').val() == 0)
  861.         {
  862.             $('[name=adjAmt2]').val(0);
  863.             $('[name=adjAmt2]').removeClass("error");
  864.             $('[name=adjTime2]').val(0);
  865.             $('[name=adjTime2]').removeClass("error");
  866.             $('[name=adj2]').removeClass("error");
  867.         }
  868.     });
  869.  
  870.     $('[name=adjAmt1]').change(function ()
  871.     {
  872.         validAdj();
  873.     });
  874.  
  875.     $('[name=adjAmt2]').change(function ()
  876.     {
  877.         validAdj();
  878.     });
  879.  
  880.     $('[name=adjTime1]').change(function ()
  881.     {
  882.         validAdj();
  883.     });
  884.  
  885.     $('[name=adjTime2]').change(function ()
  886.     {
  887.         validAdj();
  888.     });
  889.  
  890.     $("#OG").change(function ()
  891.     {
  892.         $("#warning-vitalstats").empty();
  893.         var temp = parseFloat($("#OG").val());
  894.         solveOG();
  895.         checkBetweenBounds();
  896.         $("[name=OG]").val(Math.round(OG * 1000) / 1000);
  897.         recalcForm();
  898.     });
  899.     $("#IBU").change(function ()
  900.     {
  901.         var temp = parseFloat($("#IBU").val());
  902.         if (IBU != 0 && temp != 0)
  903.         {
  904.             var iter = 0;
  905.             var errorThresh = 0.5;
  906.             while (Math.abs(temp - IBU) > errorThresh && iter < 10)
  907.             {
  908.                 var scaleFactor = temp / IBU;
  909.                 solveIBU(scaleFactor);
  910.                 iter = iter + 1;
  911.             }
  912.             $("#IBU").html(Math.round(IBU));
  913.             recalcForm();
  914.         }
  915.     });
  916.     $("#BeerStyle").change(function ()
  917.     {
  918.         vitaStatsChangeBounds();
  919.         textRecipeDescription();
  920.     });
  921.  
  922.     function clearErrors()
  923.     {
  924.         $('#warnings-general').empty();
  925.         $('#warnings-all').empty();
  926.         $('#warnings-grains').empty();
  927.         $('#warnings-hops').empty();
  928.         $('#warnings-adj').empty();
  929.         $('#warnings-water').empty();
  930.         $('#warnings-mash').empty();
  931.         $('#warnings-boil').empty();
  932.         $('#warnings-yeasts').empty();
  933.         $('#warnings-ferm').empty();
  934.         $('#warnings-notes').empty();
  935.     }
  936.  
  937.     function beerVis()
  938.     {
  939.         $("#beerRec1").show();
  940.         $("#beerRec2").show();
  941.         $("#beerRec3").show();
  942.         $("#beerRec4").show();
  943.         $("#beerRec5").show();
  944.         $("#beerRec6").show();
  945.         $("#otherRec1").hide();
  946.         $("#otherRec2").hide();
  947.         $("#saveButtonOther").hide();
  948.         $("#saveButton").show();
  949.     }
  950.  
  951.     function otherVis()
  952.     {
  953.         $("#beerRec1").hide();
  954.         $("#beerRec2").hide();
  955.         $("#beerRec3").hide();
  956.         $("#beerRec4").hide();
  957.         $("#beerRec5").hide();
  958.         $("#beerRec6").hide();
  959.         $("#otherRec1").show();
  960.         $("#otherRec2").show();
  961.         $("#saveButtonOther").show();
  962.         $("#saveButton").hide();
  963.     }
  964.  
  965.     $("#type").change(function ()
  966.     {
  967.         clearErrors();
  968.         var val = $("#type").val();
  969.         if (val == 0)
  970.         {
  971.             beerVis();
  972.         }
  973.         else if (val == 1)
  974.         {
  975.             otherVis();
  976.         }
  977.     });
  978.  
  979.     $('[name=recipe_name]').change(function ()
  980.     {
  981.         origBit = $("[name = origBit]").val();
  982.         if (origBit == 0)
  983.         {
  984.             $('#origName').val($('[name=recipe_name]').val());
  985.         }
  986.     });
  987.  
  988.     //Function to send post data to the recipe saver
  989.     $("#saveButton").click(function ()
  990.     {
  991.         $('#warnings-general').empty();
  992.         var isValid = validAll();
  993.         $(document).scrollTop(0);
  994.         if (!isValid)
  995.         {
  996.             return;
  997.         }
  998.        
  999.         $("#buttonHide").hide();
  1000.         $('#msgBod2').html("<p>Saving Recipe</p>");
  1001.         $('#loadModal').modal('show');
  1002.         var numSes = $("#numSes").val();
  1003.         //Fix the arrays to ensure post data is complete
  1004.  
  1005.         recalcForm();
  1006.         gatherNonCalc();
  1007.  
  1008.         var serializedFermSteps = JSON.stringify(advFermSteps);
  1009.         var serializedIngredients = JSON.stringify(recipeGrains);
  1010.         var serializedHops = JSON.stringify(recipeHops);
  1011.  
  1012.         $.ajax({
  1013.             type: "POST",
  1014.             url: "saverecipe.cshtml",
  1015.             //first one is the variable the other file will get. second one is the variable i send over
  1016.  
  1017.             data: {
  1018.                 Name: recipeName,
  1019.                 User: recipeAuth,
  1020.                 Notes: notes,
  1021.                 wort: finBeer,
  1022.                 boil: boilTime,
  1023.                 originalGravity: OG,
  1024.                 finalGravity: FG,
  1025.                 bitternes: IBU,
  1026.                 color: SRM,
  1027.                 alcohol: ABV,
  1028.                 MashTemp: mashTemp,
  1029.                 MashTime: mashTime,
  1030.                 boilTemp: boilTemp,
  1031.                 WaterAmount: H2O,
  1032.                 Eff: eff,
  1033.                 beerNumber: $("#BeerStyle").val(),
  1034.                 Type: type,
  1035.                 origGUID: guid,
  1036.                 origName: origName,
  1037.                 origAuth: origAuth,
  1038.                 boilType: boilType,
  1039.                 mashType: mashType,
  1040.                 yeastAA: yeastAA,
  1041.                 yeastPitch: yeastPitch,
  1042.                 waterType: waterType,
  1043.                 numSes: numSes,
  1044.                 yeast: recipeYeastType,
  1045.                 adj1: adj1,
  1046.                 adj2: adj2,
  1047.                 adjAmt1: adjAmt1,
  1048.                 adjAmt2: adjAmt2,
  1049.                 adjTime1: adjTime1,
  1050.                 adjTime2: adjTime2,
  1051.                 adjUse1: adjUse1,
  1052.                 adjUse2: adjUse2,
  1053.                 uGUID: uGUID,
  1054.                 beerText: beerText,
  1055.                 amend1: amend1,
  1056.                 amend2: amend2,
  1057.                 amend3: amend3,
  1058.                 amend4: amend4,
  1059.                 amendAmt1: amendAmt1,
  1060.                 amendAmt2: amendAmt2,
  1061.                 amendAmt3: amendAmt3,
  1062.                 amendAmt4: amendAmt4,
  1063.                 mashType: mashType,
  1064.                 fermentationSteps: serializedFermSteps,
  1065.                 grainIngredients: serializedIngredients,
  1066.                 hopIngredients: serializedHops,
  1067.                 fileGuid: fileGuid,
  1068.                 importBit: fileImport
  1069.             },
  1070.             success: function (stuff)
  1071.             {
  1072.                 if (stuff.indexOf("!!!") != -1)
  1073.                 {
  1074.                     var returned = stuff.toString();
  1075.                     var startI = returned.indexOf("!!!") + 3;
  1076.                     var endI = returned.indexOf("???");
  1077.                     var newGUID = returned.substr(startI, endI - startI);
  1078.                     $("#loadModal").hide();
  1079.                     clearErrors();
  1080.                     window.location.replace('/Members/Recipes/ParseRecipe.cshtml?id=' + newGUID);
  1081.                 }
  1082.                 else if (stuff.indexOf("###") != -1)
  1083.                 {
  1084.                     $('#loadModal').modal('hide');
  1085.                     $('#msgHead').css('background-color', 'red');
  1086.                     $('#myModalLabel').text("Error");
  1087.                     stuff = stuff.replace("###", "");
  1088.                     $('#msgBod').html(stuff);
  1089.                     $("#buttonHide").show();
  1090.                     $("#savingMsg").hide();
  1091.                     window.setTimeout($('#myModal').modal('show'), 500);
  1092.                 }
  1093.                 else
  1094.                 {
  1095.                     window.location.replace('/');
  1096.                 }
  1097.             }
  1098.         });
  1099.  
  1100.     });
  1101.  
  1102.     $("#cancel").click(function ()
  1103.     {
  1104.         window.location.replace('/Members/User/Brewhouse.cshtml');
  1105.     });
  1106.  
  1107.     function validAllOther()
  1108.     {
  1109.         var validName = false;
  1110.         var validNotes = false;
  1111.         var validTemp = false;
  1112.         var validTime = false;
  1113.         $('#all').wrap('<form id="temp_form_id" />');
  1114.         $('#temp_form_id').validate({
  1115.             rules: {
  1116.                 recipe_name: {
  1117.                     required: true,
  1118.                     maxlength: 20
  1119.                 },
  1120.                 cookTime: {
  1121.                     required: true,
  1122.                     digits: true,
  1123.                     max: 3570,
  1124.                     min: 1
  1125.                 },
  1126.                 cookTemp: {
  1127.                     required: true,
  1128.                     max: unitInUS ? 207 : RoundedConvertFahrenheitToCelcius(207),
  1129.                     min: unitInUS ? 33 : RoundedConvertFahrenheitToCelcius(33)
  1130.                 },
  1131.                 recipe_Notes: {
  1132.                     required: false,
  1133.                     maxlength: 4000
  1134.                 }
  1135.             },
  1136.             errorPlacement: function ()
  1137.             {
  1138.                 return false;
  1139.             }
  1140.         });
  1141.         clearErrors();
  1142.         validName = $('[name=recipe_name]').valid();
  1143.         validNotes = $('[name=recipe_Notes]').valid();
  1144.         validTemp = $('[name=cookTemp]').valid();
  1145.         validTime = $('[name=cookTime]').valid();
  1146.         $('#all').unwrap();
  1147.         if (recipeName.indexOf('#') > 0)
  1148.         {
  1149.             validName = false;
  1150.         }
  1151.         else if (recipeName.indexOf('/') > 0)
  1152.         {
  1153.             validName = false;
  1154.         }
  1155.         else if (recipeName.indexOf('|') > 0)
  1156.         {
  1157.             validName = false;
  1158.         }
  1159.         count = 0;
  1160.         if (!validName)
  1161.         {
  1162.             count++;
  1163.             var warning = "Recipe name is invalid (mandatory, 20 characters or less). Non-English characters and special characters except ' . _ - are not allowed.";
  1164.             $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1165.         }
  1166.         if (!validNotes)
  1167.         {
  1168.             count++;
  1169.             var warning = "Recipe notes are invalid (max length 4000).";
  1170.             $('#warnings-notes').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1171.         }
  1172.         if (!validTemp)
  1173.         {
  1174.             $('[name=cookTemp]').val(unitInUS ? 207 : RoundedConvertFahrenheitToCelcius(207));
  1175.             count++;
  1176.             var warning = "Cook temp out of bounds " + (unitInUS ? "(33-207 F)." : "(0.5-97 C).") + " Reset to max.";
  1177.             $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1178.         }
  1179.         if (!validTime)
  1180.         {
  1181.             $('[name=cookTime]').val(60);
  1182.             count++;
  1183.             var warning = "Cook time out of bounds (1-3570). Reset to 60.";
  1184.             $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1185.         }
  1186.         if (count > 0)
  1187.         {
  1188.             return false;
  1189.         }
  1190.         return true;
  1191.     }
  1192.  
  1193.     $("#saveButtonOther").click(function ()
  1194.     {
  1195.  
  1196.         var isValid = validAllOther();
  1197.  
  1198.         if (!isValid)
  1199.         {
  1200.             var warning = "The recipe is not valid. Please review error messages and try again.";
  1201.             $('#warnings-general').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1202.             return;
  1203.         }
  1204.         gatherNonCalc();
  1205.         type = parseInt($("[name = recipe_type]").val());
  1206.         notes = $("[name = recipe_Notes]").val();
  1207.         cookTemp = unitInUS ? $("[name = cookTemp]").val() : ConvertCelciusToFahrenheit($("[name = cookTemp]").val());
  1208.         cookTime = $("[name = cookTime]").val();
  1209.  
  1210.         $("#buttonHide").hide();
  1211.         $("#savingMsg").show();
  1212.  
  1213.         $.ajax({
  1214.             type: "POST",
  1215.             url: "saveother.cshtml",
  1216.             data: {
  1217.                 Name: recipeName, User: recipeAuth, Notes: notes, Time: cookTime, Temp: cookTemp, uGUID: uGUID, origName: origName, origAuth: origAuth, Type: type
  1218.             },
  1219.             success: function (stuff)
  1220.             {
  1221.                 if (stuff.indexOf("!!!") != -1)
  1222.                 {
  1223.                     var returned = stuff.toString();
  1224.                     var startI = returned.indexOf("!!!") + 3;
  1225.                     var endI = returned.indexOf("???");
  1226.                     var newGUID = returned.substr(startI, endI - startI);
  1227.                     $("#loadModal").hide();
  1228.                     clearErrors();
  1229.                     window.location.replace('/Members/Recipes/ParseRecipe.cshtml?id=' + newGUID);
  1230.                 }
  1231.                 else if (stuff.indexOf("###") != -1)
  1232.                 {
  1233.                     $('#loadModal').modal('hide');
  1234.                     $('#msgHead').css('background-color', 'red');
  1235.                     $('#myModalLabel').text("Error");
  1236.                     stuff = stuff.replace("###", "");
  1237.                     $('#msgBod').html(stuff);
  1238.                     $("#buttonHide").show();
  1239.                     $("#savingMsg").hide();
  1240.                     window.setTimeout($('#myModal').modal('show'), 500);
  1241.                 }
  1242.                 else
  1243.                 {
  1244.                     window.location.replace('/');
  1245.                 }
  1246.             }
  1247.         });
  1248.     });
  1249.  
  1250.     //TODO?? DELETE?
  1251.     $("#beer-text").change(function ()
  1252.     {
  1253.         $('#warnings-all').empty();
  1254.         if ($("#beer-text").val() == "")
  1255.         {
  1256.             custText = 0;
  1257.             $("[name = custText]").val(0);
  1258.             textRecipeDescription();
  1259.         }
  1260.         else
  1261.         {
  1262.             $("[name = custText]").val(1);
  1263.             custText = 1;
  1264.         }
  1265.         var recReg = /^\s*[a-zA-Z0-9\'!&(,)?\/._\-\s]+\s*$/; ;
  1266.         beerText = $("#beer-text").val();
  1267.         if (beerText.match(recReg) == null)
  1268.         {
  1269.             custText = 0;
  1270.             $("[name = custText]").val(0);
  1271.             textRecipeDescription();
  1272.             var warning = "Tasting notes are invalid, only alphanumeric input and the characters , ( ) ? ! ' . - and _ are permitted. ";
  1273.             $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1274.         }
  1275.     });
  1276.  
  1277.     $("#mashType").change(function ()
  1278.     {
  1279.         mashType = $("#mashType").val();
  1280.         if (mashType == 0)
  1281.         {
  1282.             $('[name = mashTemp]').prop('disabled', false);
  1283.             $('[name = mashTime]').prop('disabled', false);
  1284.         }
  1285.         else if (mashType == 1)
  1286.         {
  1287.             $('[name = mashTemp]').prop('disabled', true);
  1288.             $('[name = mashTime]').prop('disabled', true);
  1289.         }
  1290.         recalcForm();
  1291.     });
  1292.  
  1293.     $("#recipe_name").change(function ()
  1294.     {
  1295.         var recReg = /^\s*[a-zA-Z0-9\'._\-\s]+\s*$/;
  1296.         recipeName = $("[name = recipe_name]").val();
  1297.         if (recipeName.match(recReg) == null)
  1298.         {
  1299.             var warning = "Recipe name is invalid, only alphanumeric input and the characters ' . - and _ are permitted. Max length is 20 characters.";
  1300.             $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  1301.             $("[name = recipe_name]").val("New Recipe");
  1302.         }
  1303.     });
  1304.  
  1305.     $(document).on("focus", "div.search > input", function(){
  1306.         var results = $(this.parentNode).children(".results");
  1307.         for(var i = 0; i < results.length; i++) {
  1308.             $(results[i]).show();
  1309.         }
  1310.     });
  1311.  
  1312.     $(document).on("mouseenter", ".results", function () {
  1313.         $(this).data("user-focus", true);
  1314.     })
  1315.  
  1316.     $(document).on("mouseleave", ".results", function () {
  1317.         $(this).data("user-focus", false);
  1318.     })
  1319.  
  1320.     $(document).on("blur", "div.search > input", function(){
  1321.         var results = $(this.parentNode).children(".results");
  1322.         for (var i = 0; i < results.length; i++) {
  1323.             if (!$(results[i]).data("user-focus")) {
  1324.                 $(results[i]).hide();
  1325.             }
  1326.         }
  1327.     });
  1328.  
  1329.     $("#cookTemp").change(function () {
  1330.         cookTemp = unitInUS ? this.valueAsNumber : ConvertCelciusToFahrenheit(this.valueAsNumber);
  1331.     });
  1332.  
  1333.     function delayHide(target, delay) {
  1334.         setTimeout(function(){
  1335.             $(target).hide();
  1336.         }, delay);
  1337.     }
  1338.  
  1339.  
  1340.  
  1341.     function init() {
  1342.         var maxHopTime = 0;
  1343.         for (var i = 0; i < recipeHops.length; i++) {
  1344.             if (recipeHops[i].Time > maxHopTime) {
  1345.                 maxHopTime = recipeHops[i].Time;
  1346.             }
  1347.         }
  1348.         $("#hopBoil").html(maxHopTime);
  1349.     }
  1350. });
  1351.  
  1352. //Fermentation Standard Temperatures
  1353. var aleFermTemp = 66;
  1354. var aleFermTimeDays = 10;
  1355. var lagerFermTemp = 55;
  1356. var lagerTemp = 35;
  1357.  
  1358. //=====================CHANGE EVENT HANDLERS=====================
  1359. //Function that executes when unit changes are made
  1360. function unitchange()
  1361. {
  1362.     var unitSelected = document.getElementById("unit");
  1363.     if (unitSelected.options[unitSelected.selectedIndex].value == 0)
  1364.         //US Unit Changes
  1365.     {
  1366.         unitInUS = true;
  1367.  
  1368.         //Change all temp labels to show F for symbol instead of C
  1369.         $(".temperature").each(function ()
  1370.         {
  1371.             $(this).html($(this).html().replace("(C)", "(F)"));
  1372.  
  1373.         });
  1374.  
  1375.         //Change all liquid to show G symbol instead of L
  1376.         $(".liquid").each(function ()
  1377.         {
  1378.             $(this).html($(this).html().replace("(L)", "(G)"));
  1379.  
  1380.         });
  1381.  
  1382.         //Change all liquid to show G symbol instead of L
  1383.         $(".poundKilo").each(function ()
  1384.         {
  1385.             $(this).html($(this).html().replace("(kg)", "(lb)"));
  1386.  
  1387.         });
  1388.  
  1389.         $(".ounceGram").each(function ()
  1390.         {
  1391.             $(this).html($(this).html().replace("(g)", "(oz)"));
  1392.  
  1393.         });
  1394.  
  1395.         $(".tspGram").each(function ()
  1396.         {
  1397.             $(this).html($(this).html().replace("(g)", "(tsp)"));
  1398.  
  1399.         });
  1400.  
  1401.         //Sous Vide Changes
  1402.         $("#cookTemp").val(cookTemp);
  1403.  
  1404.         //Remove Metric Warning
  1405.         var metricWarning = document.getElementById("metricWarning");
  1406.         metricWarning.style.visibility = 'hidden';
  1407.         metricWarning.style.display = 'none';
  1408.  
  1409.         //Beer Volume in US units
  1410.         $("#finBeer").val(parseFloat(finBeer).toFixed(2));
  1411.  
  1412.         for (var i = 0; i < recipeGrains.length; i++)
  1413.         {
  1414.             var grainPositionName = "grain" + (i + 1);
  1415.             var grainPositionAmount = "grainsamt" + (i + 1);
  1416.             var tempNum = parseFloat(isNaN(recipeGrains[i]["Amount"]) ? 0 : recipeGrains[i]["Amount"]).toFixed(2);
  1417.             //If whole number, don't show decimal
  1418.             if (tempNum % 1 == 0)
  1419.             {
  1420.                 tempNum = Math.round(tempNum);
  1421.             }
  1422.             $("#" + grainPositionAmount).val(tempNum);
  1423.         }
  1424.  
  1425.         //Change properties of each hop amount field
  1426.         $(".hop-amount").each(function ()
  1427.         {
  1428.             $(this).prop(
  1429.             {
  1430.                 'min': 0,
  1431.                 'max': 1.5,
  1432.                 'step': 0.1
  1433.             });
  1434.         })
  1435.  
  1436.         //Populate any existing values
  1437.         for (var i = 0; i < recipeHops.length; i++)
  1438.         {
  1439.             var hopPositionAmount = "hopAmt" + (i + 1);
  1440.             var tempNum = parseFloat(isNaN(recipeHops[i]["Amount"]) ? 0 : recipeHops[i]["Amount"]).toFixed(2);
  1441.             if (tempNum % 1 == 0)
  1442.             {
  1443.                 tempNum = Math.round(tempNum);
  1444.             }
  1445.             $("#" + hopPositionAmount).val(tempNum);
  1446.         }
  1447.  
  1448.         $('#mashTemp').val(mashTemp);
  1449.         $('#mashTemp').prop(
  1450.         {
  1451.             'max': 207,
  1452.             'step': 1
  1453.         });
  1454.  
  1455.         $('#boilTemp').val(boilTemp);
  1456.         $('#boilTemp').prop(
  1457.         {
  1458.             'max': 207,
  1459.             'step': 1
  1460.         });
  1461.  
  1462.  
  1463.         var yeastType = $("[name=yeast]").val();
  1464.         if (yeastType == 0)
  1465.         {
  1466.             $("#yeastTemp").html("0 - 0");
  1467.             $("[name=yeastPitch]").val(0);
  1468.         } else
  1469.         {
  1470.             $("#yeastTemp").html(yeastMinTemp[yeastType] + " - " + yeastMaxTemp[yeastType]);
  1471.             $("[name=yeastPitch]").val(yeastPitch);
  1472.         }
  1473.         $('#yeastPitchTemp').prop(
  1474.         {
  1475.             'max': 100,
  1476.             'step': 1
  1477.         });
  1478.  
  1479.  
  1480.         var fermCount = 0;
  1481.         //Change temp values to F values
  1482.         $('#advFermSched tr').has('td').each(function ()
  1483.         {
  1484.             $('td', $(this)).each(function (index, item)
  1485.             {
  1486.                 $('input', $(this)).each(function (index, item)
  1487.                 {
  1488.                     if ($(this).data("name") == "Temp")
  1489.                     {
  1490.                         if (!!$(this).val())
  1491.                         {
  1492.                             if (advFermSteps.length > fermCount)
  1493.                             {
  1494.                                 $(this).val(advFermSteps[fermCount]["Temp"]);
  1495.                             }
  1496.                         }
  1497.                         $(this).prop("max", 85);
  1498.                         $(this).prop("min", 32);
  1499.                         $(this).prop("step", 1);
  1500.                     }
  1501.                 })
  1502.             })
  1503.             fermCount += 1;
  1504.         });
  1505.  
  1506.         //adjunct amount changes
  1507.         $('[name=adjAmt1]').val(adjAmt1);
  1508.         $('[name=adjAmt2]').val(adjAmt2);
  1509.  
  1510.         //Change properties of each hop amount field
  1511.         $(".adjunctAmount").each(function ()
  1512.         {
  1513.             $(this).prop(
  1514.             {
  1515.                 'min': 0,
  1516.                 'max': 1.5,
  1517.                 'step': 0.1
  1518.             });
  1519.         })
  1520.  
  1521.     } else if (unitSelected.options[unitSelected.selectedIndex].value == 1)
  1522.         //Metric Unit Changes
  1523.     {
  1524.         unitInUS = false;
  1525.         //Replace all temperature F symbols with C
  1526.         $(".temperature").each(function ()
  1527.         {
  1528.             $(this).html($(this).html().replace("F", "C"));
  1529.  
  1530.         });
  1531.  
  1532.         //Change all liquid to show G symbol instead of L
  1533.         $(".liquid").each(function ()
  1534.         {
  1535.             $(this).html($(this).html().replace("(G)", "(L)"));
  1536.  
  1537.         });
  1538.  
  1539.         $(".poundKilo").each(function ()
  1540.         {
  1541.             $(this).html($(this).html().replace("(lb)", "(kg)"));
  1542.  
  1543.         });
  1544.  
  1545.         $(".ounceGram").each(function ()
  1546.         {
  1547.             $(this).html($(this).html().replace("(oz)", "(g)"));
  1548.  
  1549.         });
  1550.  
  1551.         $(".tspGram").each(function ()
  1552.         {
  1553.             $(this).html($(this).html().replace("(tsp)", "(g)"));
  1554.  
  1555.         });
  1556.  
  1557.         //Sous Vide Changes
  1558.         //Convert temperature to Celcius
  1559.         $("#cookTemp").val(RoundedConvertFahrenheitToCelcius(cookTemp));
  1560.  
  1561.         //Warn user of Metric Conversion
  1562.         var metricWarning = document.getElementById("metricWarning");
  1563.         metricWarning.style.visibility = 'visible';
  1564.         metricWarning.style.display = 'block';
  1565.  
  1566.         //Change Finished Beer to Liter Measurement
  1567.         $("#finBeer").val(RoundedConvertGallonToLiters(finBeer));
  1568.  
  1569.         //Populate all grain values with converted metric values
  1570.         for (var i = 0; i < recipeGrains.length; i++)
  1571.         {
  1572.             var grainPositionName = "grain" + (i + 1);
  1573.             var grainPositionAmount = "grainsamt" + (i + 1);
  1574.             var tempNum = parseFloat(isNaN(recipeGrains[i]["Amount"]) ? 0 : ConvertLBsToKG(recipeGrains[i]["Amount"])).toFixed(2);
  1575.             if (tempNum % 1 == 0)
  1576.             {
  1577.                 tempNum = Math.round(tempNum);
  1578.             }
  1579.             if (recipeGrains[i]["Amount"] != 0)
  1580.             {
  1581.                 $("#" + grainPositionAmount).val(tempNum);
  1582.             }
  1583.         }
  1584.  
  1585.         //Change properties for gram units for min and max
  1586.         $(".hop-amount").each(function ()
  1587.         {
  1588.             $(this).prop(
  1589.             {
  1590.                 'min': 0,
  1591.                 'max': 43,
  1592.                 'step': 1
  1593.             });
  1594.         });
  1595.  
  1596.         //Populate any preexisting hop amounts
  1597.         for (var i = 0; i < recipeHops.length; i++)
  1598.         {
  1599.             var hopPositionAmount = "hopAmt" + (i + 1);
  1600.             $("#" + hopPositionAmount).val(Math.round(parseFloat(isNaN(recipeHops[i]["Amount"]) ? 0 : ConvertOunceToGram(recipeHops[i]["Amount"]))));
  1601.         }
  1602.  
  1603.         //Change Mash Temperature
  1604.         $('#mashTemp').val(RoundedConvertFahrenheitToCelcius(mashTemp));
  1605.         $('#mashTemp').prop(
  1606.         {
  1607.             'max': 97.2,
  1608.             'step': 0.1
  1609.         });
  1610.  
  1611.         //Change Boil temperature
  1612.         $('#boilTemp').val(RoundedConvertFahrenheitToCelcius(boilTemp));
  1613.         $('#boilTemp').prop(
  1614.         {
  1615.             'max': 97.2,
  1616.             'step': 0.1
  1617.         });
  1618.  
  1619.         //Change yeast temperatures
  1620.         $('#yeastRangeTemp').val(RoundedConvertFahrenheitToCelcius(mashTemp));
  1621.  
  1622.         var yeastType = $("[name=yeast]").val();
  1623.         if (yeastType == 0)
  1624.         {
  1625.             $("#yeastTemp").html("0 - 0");
  1626.             $("[name=yeastPitch]").val(0);
  1627.         } else
  1628.         {
  1629.             $("#yeastTemp").html(RoundedConvertFahrenheitToCelcius(yeastMinTemp[yeastType]) + " - " + RoundedConvertFahrenheitToCelcius(yeastMaxTemp[yeastType]));
  1630.             $("[name=yeastPitch]").val(parseFloat(RoundedConvertFahrenheitToCelcius(yeastPitch)).toFixed(1));
  1631.         }
  1632.         $('#yeastPitchTemp').prop(
  1633.         {
  1634.             'max': 37.8,
  1635.             'step': 0.1
  1636.         });
  1637.  
  1638.  
  1639.         var fermCount = 0;
  1640.         //Change temp values to Celcius values
  1641.         $('#advFermSched tr').has('td').each(function ()
  1642.         {
  1643.             $('td', $(this)).each(function (index, item)
  1644.             {
  1645.                 $('input', $(this)).each(function (index, item)
  1646.                 {
  1647.                     if ($(this).data("name") == "Temp")
  1648.                     {
  1649.                         if (!!$(this).val())
  1650.                         {
  1651.                             if (advFermSteps.length > fermCount)
  1652.                             {
  1653.                                 $(this).val(RoundedConvertFahrenheitToCelcius(advFermSteps[fermCount]["Temp"]));
  1654.                             }
  1655.                         }
  1656.                         $(this).prop("max", RoundedConvertFahrenheitToCelcius(85));
  1657.                         $(this).prop("min", RoundedConvertFahrenheitToCelcius(32));
  1658.                         $(this).prop("step", 0.1);
  1659.                     }
  1660.                 })
  1661.             })
  1662.             fermCount += 1;
  1663.         });
  1664.  
  1665.         //adjunct amount changes
  1666.         $('[name=adjAmt1]').val(ConvertOunceToGram(adjAmt1).toFixed(0));
  1667.         $('[name=adjAmt2]').val(ConvertOunceToGram(adjAmt2).toFixed(0));
  1668.  
  1669.         //Change properties of each hop amount field
  1670.         $(".adjunctAmount").each(function ()
  1671.         {
  1672.             $(this).prop(
  1673.             {
  1674.                 'min': 0,
  1675.                 'max': 43,
  1676.                 'step': 1
  1677.             });
  1678.         })
  1679.     }
  1680.     storeAdvancedFermentation();
  1681.     changeNormalScheduleDescription();
  1682.     recalcForm();
  1683. }
  1684.  
  1685. //Executes when the selection box for the fermentation schedule is changed
  1686. function fermScheduleChange(select)
  1687. {
  1688.     var value = parseInt(select.value);
  1689.     switch (value)
  1690.     {
  1691.         default:
  1692.         case 0:
  1693.             //Hide Other Tables
  1694.             hideAdvFermSchedTable();
  1695.             //Show Normal Table
  1696.             normalFermentation(document.getElementById("normalFermSelection"));
  1697.             showNormalTable();
  1698.             break;
  1699.         case 1:
  1700.             //Hide Other Tables
  1701.             hideNormalTable();
  1702.             //Show Advanaced Table
  1703.             showAdvFermSchedTable();
  1704.             break;
  1705.     }
  1706.  
  1707.     changeNormalScheduleDescription();
  1708.  
  1709.     //Fermentation Schedule Change Helper Functions
  1710.     //Shows the Table for the Normal Options
  1711.     function showNormalTable()
  1712.     {
  1713.         //Show normal schedule
  1714.         var table = document.getElementById("normalFermSched");
  1715.         table.style.visibility = 'visible';
  1716.         table.style.display = 'block';
  1717.     }
  1718.  
  1719.     //Hides the Table for the Normal Options
  1720.     function hideNormalTable()
  1721.     {
  1722.         //Hide the normal schedule
  1723.         var table = document.getElementById("normalFermSched");
  1724.         table.style.visibility = 'hidden';
  1725.         table.style.display = 'none';
  1726.     }
  1727.  
  1728.     //Show advanced table
  1729.     function showAdvFermSchedTable()
  1730.     {
  1731.         var table = document.getElementById("advFermSched");
  1732.         table.style.visibility = 'visible';
  1733.         table.style.display = 'block';
  1734.         var notes = document.getElementById("advanceNotifications");
  1735.         notes.style.visibility = 'visible';
  1736.         notes.style.display = 'block';
  1737.     }
  1738.  
  1739.     //Hide advanaced table
  1740.     function hideAdvFermSchedTable()
  1741.     {
  1742.         var table = document.getElementById("advFermSched");
  1743.         table.style.visibility = 'hidden';
  1744.         table.style.display = 'none';
  1745.         var notes = document.getElementById("advanceNotifications");
  1746.         notes.style.visibility = 'hidden';
  1747.         notes.style.display = 'none';
  1748.     }
  1749. }
  1750.  
  1751. //Normal schedule option has changed, this will change the description to the proper description
  1752. function changeNormalScheduleDescription()
  1753. {
  1754.     var aleFermDescription = "Cool to " + (unitInUS ? $("#advFermTemp1").val() + " F" : parseFloat($("#advFermTemp1").val()) + " C") + " and keep temperature consistent for " + $("#advFermDay1").val() + " Days";
  1755.     var lagerFermDescription = "Cool to " + (unitInUS ? $("#advFermTemp1").val() + " F" : parseFloat($("#advFermTemp1").val()) + " C") + " and keep temperature consistent for 3 weeks. Rack to a lagering container and keep at " + (unitInUS ? $("#advFermTemp2").val() + " F" : parseFloat($("#advFermTemp2").val()) + " C") + " for another 4 weeks.";
  1756.     if ($("#fermTypeSelection").val() == 0)
  1757.     {
  1758.         if (document.getElementById("normalFermSelection").value == 0)
  1759.         {
  1760.             $("#fermDescription").html(aleFermDescription);
  1761.         }
  1762.         else if (document.getElementById("normalFermSelection").value == 1)
  1763.         {
  1764.             $("#fermDescription").html(lagerFermDescription);
  1765.         }
  1766.     }
  1767.     else
  1768.     {
  1769.         $("#fermDescription").html("");
  1770.     }
  1771. }
  1772.  
  1773. //After a grain is selected, this function will execute.
  1774. function changeFermentable(target)
  1775. {
  1776.     var search = target.parentNode;
  1777.     while(search.className !== "search") {
  1778.         search = search.parentNode;
  1779.     }
  1780.     var grainIndex = search.dataset.index;
  1781.     var searchInput = $(search).children("input")[0];
  1782.     var grainAmtID = "grainsamt" + grainIndex;
  1783.     $('#warnings-grains').empty();
  1784.     $(searchInput).removeClass("error");
  1785.     //if the selection is 0, this is the blank selection
  1786.     var ingredientID = parseInt(target.dataset.value);
  1787.     if (ingredientID !== 0)
  1788.     {
  1789.         //Set both values as required
  1790.         $(searchInput).prop("required", true);
  1791.         $(searchInput).data("ingredientID", ingredientID.toString());
  1792.         $(searchInput).val(target.innerText);
  1793.         $("#" + grainAmtID).prop("required", true);
  1794.     }
  1795.     $(searchInput).removeClass("error");
  1796.  
  1797.     //hide the results if they are open
  1798.     $(target.parentNode).hide();
  1799.  
  1800.     storeFermentables();
  1801.     recalcForm();
  1802. }
  1803.  
  1804. function removeFermentable(target) {
  1805.     var search = target.parentNode;
  1806.     while (search.className !== "search") {
  1807.         search = search.parentNode;
  1808.     }
  1809.     var searchInput = $(search).children("input")[0];
  1810.     $(searchInput).removeData("ingredientID");
  1811.     storeFermentables();
  1812.     recalcForm();
  1813. }
  1814.  
  1815. function grainAmountChange(input)
  1816. {
  1817.     $('#warnings-grains').empty();
  1818.     $('#warnings-grains2').empty();
  1819.     $(input).removeClass("error");
  1820.     //If input is empty, set to 0 instead
  1821.     if (input.value == "")
  1822.     {
  1823.         input.value = 0;
  1824.     }
  1825.     //Store values and then recalculate form
  1826.     storeFermentables();
  1827.     recalcForm();
  1828. }
  1829.  
  1830. function hopAmountChange(input)
  1831. {
  1832.     if (!input.value)
  1833.     {
  1834.         input.value = 0;
  1835.     }
  1836.     storeHops();
  1837.     recalcForm();
  1838. }
  1839.  
  1840. //Beer Style Change Function
  1841. function BeerStyleChange()
  1842. {
  1843.     vitaStatsChangeBounds();
  1844.     textRecipeDescription();
  1845. }
  1846.  
  1847. function LoadBeverageStyles(styleGuide) {
  1848.     if(!styleGuide) {
  1849.         styleGuide = 'BJCP 2015';
  1850.     }
  1851.     $("#BeerStyle").empty();
  1852.     for (var styleID in beerStylesJSON)
  1853.     {
  1854.         //Add all the different style guides to a list to filter
  1855.         if(beerStylesJSON[styleID].StyleGuide === styleGuide) {
  1856.             $("#BeerStyle").append('<option class="bjcp-style-list" value="' + beerStylesJSON[styleID].StyleID + '" data-styleCode="' + styleID + '" data-guide="' + beerStylesJSON[styleID].StyleGuide + '">' + beerStylesJSON[styleID].CatNumCode + beerStylesJSON[styleID].CatLettCode + '. ' + beerStylesJSON[styleID].StyleNameCode + '</option>');
  1857.         }
  1858.     }
  1859. }
  1860.  
  1861. function BeerStyleGuideChange(){
  1862.     var selectedStyle = $("#BeerStyleGuide").val()
  1863.     LoadBeverageStyles(selectedStyle);
  1864.     $("#BeerStyle").val($("#BeerStyle option[data-guide='" + selectedStyle +"']")[0].value)
  1865. }
  1866.  
  1867. function changeHop(target)
  1868. {
  1869.     var search = target.parentNode;
  1870.     while (search.className !== "search") {
  1871.         search = search.parentNode;
  1872.     }
  1873.     var hopIndex = search.dataset.index;
  1874.     var searchInput = $(search).children("input")[0];
  1875.     $(searchInput).removeClass("error");
  1876.     var ingredientID = parseInt(target.dataset.value);
  1877.     //if the selection is 0, this is the blank selection
  1878.     if (ingredientID !== 0)
  1879.     {
  1880.         $(searchInput).data("ingredientID", ingredientID.toString());
  1881.         $(searchInput).val(target.innerText);
  1882.         $("#hopAA" + hopIndex).val(hopIngredients[ingredientID].Alpha);
  1883.         validateHop(hopIndex);
  1884.     }
  1885.     $(target.parentNode).hide();
  1886.     storeHops();
  1887.     recalcForm();
  1888. }
  1889.  
  1890. function removeHop(target) {
  1891.     var search = target.parentNode;
  1892.     while (search.className !== "search") {
  1893.         search = search.parentNode;
  1894.     }
  1895.     var searchInput = $(search).children("input")[0];
  1896.     $(searchInput).removeData("ingredientID");
  1897.     storeHops();
  1898.     recalcForm();
  1899. }
  1900.  
  1901. function hopInputChange(input)
  1902. {
  1903.     $(input).removeClass("error");
  1904.     if (!input.value)
  1905.     {
  1906.         input.value = 0;
  1907.     }
  1908.     storeHops();
  1909.     recalcForm();
  1910. }
  1911.  
  1912. //=====================GENERAL FUNCTIONS=====================
  1913.  
  1914. //Uses information in the crafter to recalculate the values for the beer
  1915. function recalcForm()
  1916. {
  1917.     //Get the total amount of water
  1918.     //H2O = parseFloat($("#H2O").text());
  1919.     //If in metric, convert to gallons
  1920.     //if (!unitInUS)
  1921.     //{
  1922.     //    H2O = RoundedConvertLitersToGallons(H2O);
  1923.     //}
  1924.  
  1925.     //get the recipe\beverage style value
  1926.     recipeStyle = parseInt($("[name = recipe_style]").val());
  1927.     //Finished Beer volume
  1928.     fermentorWort = finBeer + 0.2;
  1929.  
  1930.     //Recipe type, Beer or Souse Vide
  1931.     type = parseInt($("[name = recipe_type]").val());
  1932.  
  1933.  
  1934.     mashTemp = unitInUS ? parseInt($("[name = mashTemp]").val()) : parseInt(ConvertCelciusToFahrenheit(parseFloat($("[name = mashTemp]").val())));
  1935.     mashTime = parseInt($("[name = mashTime]").val());
  1936.     mashType = parseInt($("[name = mash_type]").val());
  1937.     boilTime = parseInt($("#boilTime").val());
  1938.     boilTemp = unitInUS ? parseInt($("[name = boilTemp]").val()) : parseInt(ConvertCelciusToFahrenheit(parseFloat($("[name = boilTemp]").val())));
  1939.     boilType = parseInt($("#boilType").val());
  1940.     yeastAA = parseInt($("[name = yeastExp]").val());
  1941.     recipeYeastType = parseInt($("[name = yeast]").val());
  1942.     yeastPitch = unitInUS ? parseInt($("[name = yeastPitch]").val()) : parseInt(ConvertCelciusToFahrenheit(parseFloat($("[name = yeastPitch]").val())));
  1943.     recipeName = $("[name = recipe_name]").val();
  1944.     notes = $("[name = recipe_Notes]").val();
  1945.     beerText = $("#beer-text").val();
  1946.  
  1947.     //DISABLED RESET
  1948.     //reset boilTime for loop 1/31
  1949.     //boilTime = 0;
  1950.  
  1951.     //Sum all hop amounts
  1952.     var hopAmt = 0.0;
  1953.     for (var i = 0; i < recipeHops.length; i++)
  1954.     {
  1955.         hopAmt += parseFloat(recipeHops[i]["Amount"]);
  1956.     }
  1957.     //Check summation of grains
  1958.     grainSumCheck();
  1959.     //get total of grains in lbs from the array
  1960.     grainSum = grainSumLbs();
  1961.  
  1962.     //Get the total and reflect in the HTML for either US or metric
  1963.     $(".grainSum").html(Math.round((unitInUS ? grainSum : ConvertLBsToKG(grainSum)) * 100) / 100);
  1964.  
  1965.     var mashThickness;
  1966.     var efficiency;
  1967.  
  1968.     if (mashType == 0)
  1969.     {
  1970.         //calc water amount
  1971.         var grainLossPerLb = 0.104;
  1972.         //calculate efficiency based on mash thickness
  1973.         if (grainSum > 0)
  1974.         {
  1975.             H2O = parseFloat(fermentorWort) + parseFloat((grainSum * grainLossPerLb)) + 0.030 + (0.046 * hopAmt);
  1976.             mashThickness = (H2O * 4) / grainSum;
  1977.             efficiency = 0.55;
  1978.         }
  1979.         else
  1980.         {
  1981.             H2O = 0;
  1982.             mashThickness = 0;
  1983.             efficiency = 0;
  1984.         }
  1985.     }
  1986.  
  1987.     else if (mashType == 1)
  1988.     {
  1989.         var grainLossPerLb = 0.06;
  1990.         //calculate efficiency based on mash thickness
  1991.         if (grainSum > 0)
  1992.         {
  1993.             H2O = parseFloat(fermentorWort) + parseFloat((grainSum * grainLossPerLb)) + 0.030 + (0.046 * hopAmt);
  1994.             mashThickness = (H2O * 4) / grainSum;
  1995.             efficiency = -(0.0087 * Math.pow(mashThickness, 4)) + (0.1245 * Math.pow(mashThickness, 3)) - (0.6438 * Math.pow(mashThickness, 2)) + (1.4384 * mashThickness) - 0.3769;
  1996.         } else
  1997.         {
  1998.             H2O = 0;
  1999.             mashThickness = 0;
  2000.             efficiency = 0;
  2001.         }
  2002.     }
  2003.     eff = efficiency;
  2004.     //calculate gravity point contributions for each row of grain
  2005.     gravitySum = 0;
  2006.     colorSum = 0;
  2007.  
  2008.     for (var i = 0; i < recipeGrains.length; i++)
  2009.     {
  2010.         //Only calculate if FermentableID is non Zero
  2011.         if (recipeGrains[i]["FermentableID"])
  2012.         {
  2013.             recipeGrains[i]["Gravity"] = efficiency * (fermentableIngredients[recipeGrains[i]["FermentableID"]].Yield * recipeGrains[i]["Amount"] / fermentorWort);
  2014.             $("#gravityPts" + (i + 1)).html(Math.round(recipeGrains[i]["Gravity"] * 100) / 100);
  2015.             gravitySum += recipeGrains[i]["Gravity"];
  2016.         }
  2017.         else
  2018.         {
  2019.             $("#gravityPts" + (i + 1)).html(0);
  2020.         }
  2021.     }
  2022.  
  2023.     $(".gravitySum").html(Math.round(gravitySum * 100) / 100);
  2024.  
  2025.     //calculate the percentage of grains
  2026.     for (var i = 0; i < recipeGrains.length; i++)
  2027.     {
  2028.         if (recipeGrains[i]["FermentableID"])
  2029.         {
  2030.             if (parseFloat(grainSum) > 0)
  2031.             {
  2032.                 recipeGrains[i]["Percent"] = Math.round((recipeGrains[i]["Amount"] / grainSum) * 100);
  2033.                 $("#percent" + (i + 1)).html(recipeGrains[i]["Percent"]);
  2034.             }
  2035.             else
  2036.             {
  2037.                 $("#percent" + (i + 1)).html(0);
  2038.             }
  2039.         }
  2040.         else
  2041.         {
  2042.             $("#percent" + (i + 1)).html(0);
  2043.         }
  2044.     }
  2045.  
  2046.     //Calculate the color point contributino for each row of grain
  2047.     for (var i = 0; i < recipeGrains.length; i++)
  2048.     {
  2049.         //Check if Fermentable ID is not zero
  2050.         if (recipeGrains[i]["FermentableID"])
  2051.         {
  2052.             recipeGrains[i]["Color"] = (fermentableIngredients[recipeGrains[i]["FermentableID"]].Color * recipeGrains[i]["Amount"]) / fermentorWort;
  2053.             $("#colorPts" + (i + 1)).html(Math.round(recipeGrains[i]["Color"] * 10) / 10);
  2054.             colorSum += recipeGrains[i]["Color"];
  2055.         }
  2056.         else
  2057.         {
  2058.             $("#colorPts" + (i + 1)).html(0);
  2059.         }
  2060.     }
  2061.  
  2062.     //Solve for OG if needed... only allow this if og not 0;
  2063.     if (gravitySum == 0)
  2064.     {
  2065.         OG = 0;
  2066.     } else
  2067.     {
  2068.         OG = 1.000 + (gravitySum / 1000);
  2069.     }
  2070.  
  2071.     //read in the hops stats and populate the fields
  2072.     //(have to do this last because we need to know some of the Vital Stats like OG and Volume
  2073.     var bignessFactor = 1.65 * Math.pow(0.000125, (OG - 1));
  2074.     var kettleUtilization = 4.25; // This number is 4.15 in classic Tinseth, ours is larger because of our lower utilization
  2075.     IBU = 0;
  2076.  
  2077.  
  2078.     hopAmt = 0.0;
  2079.     var maxHopTime = 0
  2080.     for (var i = 0; i < recipeHops.length; i++)
  2081.     {
  2082.         var hopAlphaVal = $("#hopAA" + (i + 1)).val();
  2083.         var hopTimeVal = $("#hopTime" + (i + 1)).val();
  2084.         var boilTimeFactor = (1 - Math.pow(2.72, (-0.04 * hopTimeVal))) / kettleUtilization;
  2085.         var hopsUtilizationRate = bignessFactor * boilTimeFactor;
  2086.  
  2087.         hopAmt += parseFloat(recipeHops[i]["Amount"]);
  2088.         recipeHops[i]["IBU"] = hopsUtilizationRate * (recipeHops[i]["Amount"] * (recipeHops[i]["Alpha"] / 100) * 7490) / fermentorWort;
  2089.         var temp = "#ibuAMT" + i;
  2090.         $("#ibuAMT" + (i + 1)).html(Math.round(recipeHops[i]["IBU"] * 10) / 10);
  2091.         IBU += recipeHops[i]["IBU"];
  2092.  
  2093.         //add to boilTime
  2094.         if (hopTimeVal > 0 && hopTimeVal > maxHopTime)
  2095.         {
  2096.             maxHopTime = parseInt(hopTimeVal);
  2097.         }
  2098.     }
  2099.  
  2100.  
  2101.  
  2102.     boilTime = maxHopTime;
  2103.  
  2104.     boilTime += parseInt($("#excessBoil").val());
  2105.  
  2106.     var TI = $("[name=IBU]").val();
  2107.     /*
  2108.     if (IBU != 0 && TI != 0) { //if form submit type = solve for ibu
  2109.     //scale the grain in the recipe up
  2110.     var iter = 0;
  2111.     var errorThresh = 0.5;
  2112.  
  2113.     while (Math.abs(TI - IBU > errorThresh) && iter < 10) {
  2114.     var scaleFactor = TI / IBU;
  2115.     solveIBU(scaleFactor);
  2116.     iter = iter + 1;
  2117.     }
  2118.     }
  2119.     */
  2120.     //calculate OG from grains and water amount
  2121.     if (OG == 0)
  2122.     {
  2123.         FG = 0;
  2124.         SRM = 0;
  2125.         ABV = 0;
  2126.     } else
  2127.     {
  2128.         FG = 1.0 + ((OG - 1.000) * (100.0 - yeastAA) / 100);
  2129.         SRM = 1.4922 * Math.pow(colorSum, 0.6859);
  2130.         ABV = (OG - FG) * 130.0;
  2131.     }
  2132.     $("#hopBoil").html(maxHopTime);
  2133.     $("#boilTime").val(boilTime);
  2134.     $("#H2O").html(unitInUS ? Math.round(H2O * 100) / 100 : RoundedConvertGallonToLiters(H2O));
  2135.     $("#OG").val(Math.round(OG * 1000) / 1000);
  2136.     $("#FG").html(Math.round(FG * 1000) / 1000);
  2137.     $("#IBU").val(Math.round(IBU));
  2138.     $("#SRM").html(Math.round(SRM));
  2139.     $("#ABV").html(Math.round(ABV * 10) / 10);
  2140.     checkBetweenBounds();
  2141.     changePicture();
  2142.     textRecipeDescription();
  2143. }
  2144.  
  2145. function changePicture()
  2146. {
  2147.     var SrmValue = $("#SRM").html();
  2148.     var approxColor = parseInt(SrmValue);
  2149.     if (approxColor == 0)
  2150.     {
  2151.         approxColor = 1;
  2152.     } else if (approxColor > 40)
  2153.     {
  2154.         approxColor = 40;
  2155.     }
  2156.     approxColor = Math.round(approxColor);
  2157.     var image = imageContainer + "/images/SRM_Small/" + approxColor + ".png";
  2158.     $("#beer-pic").attr("src", image);
  2159. }
  2160.  
  2161. function textRecipeDescription()
  2162. {
  2163.     if (custText == 0)
  2164.     {
  2165.         var desc = ""
  2166.         if (SRM == 0)
  2167.         {
  2168.             desc = desc + "A clear"
  2169.         }
  2170.         else if (SRM < 4)
  2171.         {
  2172.             desc = desc + "A very pale"
  2173.         }
  2174.         else if (SRM < 7)
  2175.         {
  2176.             desc = desc + "A pale"
  2177.         }
  2178.         else if (SRM < 11)
  2179.         {
  2180.             desc = desc + "A golden"
  2181.         }
  2182.         else if (SRM < 20)
  2183.         {
  2184.             desc = desc + "An amber"
  2185.         }
  2186.         else if (SRM < 29)
  2187.         {
  2188.             desc = desc + "A brown"
  2189.         }
  2190.         else if (SRM < 39)
  2191.         {
  2192.             desc = desc + "A dark"
  2193.         }
  2194.         else
  2195.         {
  2196.             desc = desc + "A black"
  2197.         }
  2198.  
  2199.  
  2200.         var buguRatio = IBU / (1000 * (OG - 1))
  2201.         if (OG < 0.2 && buguRatio < 0.35)
  2202.         {
  2203.             desc = desc + ", watery,"
  2204.         }
  2205.         else if (buguRatio < 0.35)
  2206.         {
  2207.             desc = desc + ", very sweet,"
  2208.         }
  2209.         else if (buguRatio < 0.45)
  2210.         {
  2211.             desc = desc + ", malty,"
  2212.         }
  2213.         else if (buguRatio < 0.65)
  2214.         {
  2215.             desc = desc + ", balanced,"
  2216.         }
  2217.         else if (buguRatio < 0.85)
  2218.         {
  2219.             desc = desc + ", hoppy,"
  2220.         }
  2221.         else if (buguRatio < 0.99)
  2222.         {
  2223.             desc = desc + ", bitter,"
  2224.         }
  2225.         else
  2226.         {
  2227.             desc = desc + ", intensely bitter,"
  2228.         }
  2229.         if (OG < 1.030)
  2230.         {
  2231.             desc = desc + " very light-bodied"
  2232.         }
  2233.         else if (OG < 1.040)
  2234.         {
  2235.             desc = desc + " light bodied"
  2236.         }
  2237.         else if (OG < 1.060)
  2238.         {
  2239.             desc = desc + " medium-bodied"
  2240.         }
  2241.         else if (OG < 1.070)
  2242.         {
  2243.             desc = desc + " full-bodied"
  2244.         }
  2245.         if (recipeStyle == 0)
  2246.         {
  2247.             desc = desc + " custom beer."
  2248.         }
  2249.         else
  2250.         {
  2251.             desc = desc + " " + beerStylesJSON[$("#BeerStyle option:selected").data("stylecode")].StyleNameCode + ".";
  2252.         }
  2253.         $("#beer-text").val(desc);
  2254.     }
  2255. }
  2256.  
  2257. //TODO May not longer be needed
  2258. function validateGrains(select)
  2259. {
  2260.     var grainIndex = select.id.slice(-1);
  2261.     var arrayIndex = parseInt(grainIndex) - 1;
  2262.     var grainAmtID = "grainsamt" + grainIndex;
  2263.     $('#warnings-grains').empty();
  2264.     //Store the current textbox value in the respective array location
  2265.     recipeGrains[arrayIndex]["FermentableID"] = select.value;
  2266.     if (unitInUS)
  2267.     {
  2268.         recipeGrains[arrayIndex]["Amount"] = parseFloat($("#" + grainAmtID).val());
  2269.     }
  2270.     else
  2271.     {
  2272.         recipeGrains[arrayIndex]["Amount"] = RoundedConvertKgToLbs(parseFloat($("#" + grainAmtID).val()));
  2273.     }
  2274.     storeFermentables();
  2275.     recalcForm();
  2276. }
  2277.  
  2278. function validateHop(index)
  2279. {
  2280.     var hopRowID = "hopRowNum" + (parseInt(index) - 1);
  2281.     var valid = true;
  2282.     //Wrap the current row into a temp form
  2283.     $("#" + hopRowID).wrap('<form id="temp_form_id" />');
  2284.     //Run validation on fields.
  2285.     $('#temp_form_id').validate();
  2286.     //Empty Warnings Field
  2287.     $('#warnings-hops').empty();
  2288.     //If hop values are valid
  2289.     if (!$("#hopAmt" + index).valid())
  2290.     {
  2291.         var warning = "Recipe hop amount is required and must be a number no greater than " + (unitInUS ? hopLimitUSString : hopLimitMetricString) + ". Hop amount in excess of " + (unitInUS ? hopLimitUSString : hopLimitMetricString) + " can be split into multiple cages.";
  2292.         $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2293.         valid = false;
  2294.     }
  2295.  
  2296.     if (!$("#hopAA" + index).valid())
  2297.     {
  2298.         var warning = "Recipe hop alpha is required and must be a number no greater than 20.0.";
  2299.         $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2300.         valid = false;
  2301.     }
  2302.     if (!$("#hopTime" + index).valid())
  2303.     {
  2304.         var warning = "Recipe hop time is required and must be a number greater than 300, rounded to the nearest minute.";
  2305.         $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2306.         valid = false;
  2307.     }
  2308.  
  2309.     //Unwrap from temp form
  2310.     $("#" + hopRowID).unwrap();
  2311. }
  2312.  
  2313. function gatherNonCalc()
  2314. {
  2315.     recipeName = $("[name = recipe_name]").val();
  2316.     recipeAuth = $("[name = recAuth]").val();
  2317.     origName = $("#origName").val();
  2318.     origAuth = $("#origAuth").val();
  2319.     recipeStyle = $("[name = recipe_style]").val();
  2320.     type = $("[name = recipe_type]").val();
  2321.     mashTemp = unitInUS ? $("[name = mashTemp]").val() : Math.round(ConvertCelciusToFahrenheit($("[name = mashTemp]").val()));
  2322.     mashTime = $("[name = mashTime]").val();
  2323.     mashType = $("[name = mash_type]").val();
  2324.     boilTime = $("#boilTime").val();
  2325.     boilTemp = unitInUS ? $("[name = boilTemp]").val() : Math.round(ConvertCelciusToFahrenheit($("[name = boilTemp]").val()));
  2326.     boilType = $("#boilType").val();
  2327.     yeastAA = $("[name = yeastExp]").val();
  2328.     yeastPitch = unitInUS ? $("[name = yeastPitch]").val() : Math.round(ConvertCelciusToFahrenheit(parseFloat($("[name = yeastPitch]").val())));
  2329.     recipeYeastType = $("[name = yeast]").val();
  2330.     notes = $("[name = recipe_Notes]").val();
  2331.  
  2332.     waterType = $("[name=water]").val();
  2333.     amend1 = $("#waterAmend1").val();
  2334.     amend2 = $("#waterAmend2").val();
  2335.     amend3 = $("#waterAmend3").val();
  2336.     amend4 = $("#waterAmend4").val();
  2337.     amendAmt1 = $("#amendAmt1").val();
  2338.     amendAmt2 = $("#amendAmt2").val();
  2339.     amendAmt3 = $("#amendAmt3").val();
  2340.     amendAmt4 = $("#amendAmt4").val();
  2341.  
  2342.     adj1 = $("[name=adj1]").val();
  2343.     adjAmt1 = unitInUS ? $("[name=adjAmt1]").val() : ConvertGramToOunce($("[name=adjAmt1]").val());
  2344.     adjTime1 = $("[name=adjTime1]").val();
  2345.     adjUse1 = $("[name=adjUse1]").val();
  2346.  
  2347.     adj2 = $("[name=adj2]").val();
  2348.     adjAmt2 = unitInUS ? $("[name=adjAmt2]").val() : ConvertGramToOunce($("[name=adjAmt2]").val());
  2349.     adjTime2 = $("[name=adjTime2]").val();
  2350.     adjUse2 = $("[name=adjUse2]").val();
  2351. }
  2352.  
  2353. //once the beer type is changed, the bounds for min and max are changed.
  2354. function vitaStatsChangeBounds()
  2355. {
  2356.     var selectedStyle = $("#BeerStyle option:selected").data("stylecode");
  2357.     $("#minOG").html(beerStylesJSON[selectedStyle].MinOG);
  2358.     $("#maxOG").html(beerStylesJSON[selectedStyle].MaxOG);
  2359.     $("#minFG").html(beerStylesJSON[selectedStyle].MinFG);
  2360.     $("#maxFG").html(beerStylesJSON[selectedStyle].MaxFG);
  2361.     $("#minIBU").html(beerStylesJSON[selectedStyle].MinIBU);
  2362.     $("#maxIBU").html(beerStylesJSON[selectedStyle].MaxIBU);
  2363.     $("#minSRM").html(beerStylesJSON[selectedStyle].MinSRM);
  2364.     $("#maxSRM").html(beerStylesJSON[selectedStyle].MaxSRM);
  2365.     $("#minABV").html(beerStylesJSON[selectedStyle].MinABV);
  2366.     $("#maxABV").html(beerStylesJSON[selectedStyle].MaxABV);
  2367.     checkBetweenBounds();
  2368. }
  2369.  
  2370. //Return summation of grain amounts in US units
  2371. function grainSumLbs()
  2372. {
  2373.     totalSum = 0.0;
  2374.     for (var i = 0; i < recipeGrains.length; i++)
  2375.     {
  2376.         if (recipeGrains[i]["FermentableID"])
  2377.         {
  2378.             totalSum += parseFloat(recipeGrains[i]["Amount"]);
  2379.         }
  2380.     }
  2381.     return totalSum;
  2382. }
  2383.  
  2384. function validGrainFinal()
  2385. {
  2386.     for (var i = 0; i < recipeGrains.length; i++)
  2387.     {
  2388.         if (recipeGrains[i]["Amount"] < 0)
  2389.         {
  2390.             var warning = "Recipe Fermentable has a value less than 0.";
  2391.             $('#warnings-grains').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2392.             return false;
  2393.         }
  2394.         //If recipe amount is less than minimum amount of 1lb
  2395.         if (recipeGrains[i]["FermentableID"] && grainSumLbs() < 1)
  2396.         {
  2397.             var warning = "Recipe fermentable is invalid due to minimum Fermentable amount of " + (unitInUS ? "1lb." : ConvertLBsToKG(1).toFixed(2) + "kg.");
  2398.             $('#warnings-grains').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2399.             return false;
  2400.         }
  2401.     }
  2402.     return true;
  2403. }
  2404.  
  2405. function validHopsFinal()
  2406. {
  2407.     var isValid = true;
  2408.     $('#warnings-hops').empty();
  2409.     for (var i = 0; isValid && i < recipeHops.length; i++)
  2410.     {
  2411.  
  2412.         if (recipeHops[i]["HopID"])
  2413.         {
  2414.             if (recipeHops[i]["Amount"] <= 0 || recipeHops[i]["Amount"] > 1.5)
  2415.             {
  2416.                 var warning = "\nHop amount must be greater than 0 and less than " + (unitInUS ? "1.5 oz." : "42 grams.");
  2417.                 $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2418.                 isValid = false;
  2419.             }
  2420.  
  2421.             if (recipeHops[i]["Alpha"] <= 0 || recipeHops[i]["Alpha"] > 20)
  2422.             {
  2423.                 var warning = "\nHop Alpha Acid must be greater than 0 and less than 20 percent";
  2424.                 $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2425.                 isValid = false;
  2426.             }
  2427.  
  2428.             if (recipeHops[i]["Time"] <= 0 || recipeHops[i]["Time"] > 360)
  2429.             {
  2430.                 var warning = "Hop time is required and must be less than 360, rounded to the nearest minute.";
  2431.                 $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2432.                 isValid = false;
  2433.             }
  2434.         }
  2435.     }
  2436.     return isValid;
  2437. }
  2438.  
  2439. //Helper Function
  2440. function validAdjFinal()
  2441. {
  2442.     var isNameValid = false;
  2443.     var count = 0;
  2444.     $('#warnings-adj').empty();
  2445.     if ($("[name=adjAmt1]").val() != "0" || $("[name=adjTime1]").val() != "0")
  2446.     {
  2447.         $('#all').wrap('<form id="temp_form_id" />');
  2448.         $('#temp_form_id').validate({
  2449.             rules: {
  2450.                 adj1: {
  2451.                     required: true,
  2452.                     min: 1
  2453.                 }
  2454.             },
  2455.             errorPlacement: function ()
  2456.             {
  2457.                 return false;
  2458.             }
  2459.         });
  2460.         isNameValid = $('[name=adj1]').valid();
  2461.         $('#all').unwrap();
  2462.  
  2463.         if (!isNameValid && count == 0)
  2464.         {
  2465.             var warning = "Adjunct name is missing (mandatory).";
  2466.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2467.             count++;
  2468.         } else
  2469.         {
  2470.             adjAmt1 = unitInUS ? $("[name=adjAmt1]").val() : ConvertOunceToGram($("[name=adjAmt1]").val()).toFixed(2);
  2471.         }
  2472.     }
  2473.     if ($("[name=adjAmt2]").val() != "0" || $("[name=adjTime2]").val() != "0")
  2474.     {
  2475.         $('#all').wrap('<form id="temp_form_id" />');
  2476.         $('#temp_form_id').validate({
  2477.             rules: {
  2478.                 adj2: {
  2479.                     required: true,
  2480.                     min: 1
  2481.                 }
  2482.             },
  2483.             errorPlacement: function ()
  2484.             {
  2485.                 return false;
  2486.             }
  2487.         });
  2488.         isNameValid = $('[name=adj2]').valid();
  2489.         $('#all').unwrap();
  2490.  
  2491.         if (!isNameValid && count == 0)
  2492.         {
  2493.             var warning = "Adjunct name is missing (mandatory).";
  2494.             $('#warnings-adj').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2495.             count++;
  2496.         } else
  2497.         {
  2498.             adjAmt2 = unitInUS ? $("[name=adjAmt2]").val() : ConvertOunceToGram($("[name=adjAmt2]").val()).toFixed(2);
  2499.         }
  2500.     }
  2501.     if (count > 0)
  2502.     {
  2503.         return false;
  2504.     }
  2505.     return true;
  2506. }
  2507.  
  2508. //Helper Function
  2509. function validAll()
  2510. {
  2511.     //Check for any instances of the error class
  2512.     var errorInstances = $(".error");
  2513.  
  2514.     //Empty all errors
  2515.     $('#warnings-all').empty();
  2516.     $('#warnings-yeasts').empty();
  2517.     $('#warnings-notes').empty();
  2518.     $('#warnings-hops').empty();
  2519.     $('#warnings-grains').empty();
  2520.  
  2521.     //Run validation on grains hops and adjuncts
  2522.     var isValid = errorInstances.length == 0 && validGrainFinal() && validHopsFinal() && validAdjFinal();
  2523.  
  2524.     //Wrap all inputs into a temp form and run validation
  2525.     $('#all').wrap('<form id="temp_form_id" />');
  2526.     $('#temp_form_id').validate();
  2527.  
  2528.  
  2529.     var isNameValid = $('#recipe_name').valid();
  2530.     var isStyleValid = $('#BeerStyle').valid();
  2531.     var isYeastValid = $('[name=yeast]').valid();
  2532.     var isNoteValid = $('#notes-text-area').valid();
  2533.     var isQuantValid = $('[name=finBeer]').valid();
  2534.  
  2535.     //Unwrap form
  2536.     $('#all').unwrap();
  2537.  
  2538.     //TODO Change to Regex
  2539.     if (recipeName.indexOf('#') > 0)
  2540.     {
  2541.         isNameValid = false;
  2542.     }
  2543.     else if (recipeName.indexOf('/') > 0)
  2544.     {
  2545.         isNameValid = false;
  2546.     }
  2547.     else if (recipeName.indexOf('|') > 0)
  2548.     {
  2549.         isNameValid = false;
  2550.     }
  2551.  
  2552.     if (!isNameValid)
  2553.     {
  2554.         var warning = "Recipe name is invalid (mandatory, 20 characters or less). Non-English characters and special characters except ' . _ - are not allowed.";
  2555.         $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2556.         isValid = false;
  2557.     }
  2558.     if (!isQuantValid)
  2559.     {
  2560.         var warning = "Finished beer quantity is out of range.";
  2561.         $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2562.         isValid = false;
  2563.     }
  2564.     if (recipeGrains.length == 0 || recipeGrains[0]["FermentableID"] == 0)
  2565.     {
  2566.         $("#selectgrain1").addClass("error");
  2567.         var warning = "At least one grain in the first row is required";
  2568.         $('#warnings-grains').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2569.         isValid = false;
  2570.     }
  2571.     /*if (recipeHops.length == 0 || recipeHops[0]["HopID"] == 0)
  2572.     {
  2573.     $("#selecthop1").addClass("error");
  2574.     var warning = "At least one hop in the first row is required.";
  2575.     $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2576.     isValid = false;
  2577.     }*/
  2578.     if (!isStyleValid)
  2579.     {
  2580.         var warning = "Recipe style is required.";
  2581.         $('#warnings-all').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2582.         isValid = false;
  2583.     }
  2584.     if (!isYeastValid || $('[name=yeast]').val() == 0)
  2585.     {
  2586.         $('[name=yeast]').addClass("error");
  2587.         var warning = "Recipe yeast is required.";
  2588.         $('#warnings-yeasts').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2589.         isValid = false;
  2590.     }
  2591.     if ($("#excessBoil").val() < 0)
  2592.     {
  2593.         var warning = "Pre-Hop Boil is less than 0";
  2594.         $('#warnings-boil').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2595.         isValid = false;
  2596.     }
  2597.     if (!isNoteValid)
  2598.     {
  2599.         var warning = "Recipe notes are invalid, 4000 character limit.";
  2600.         $('#warnings-notes').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2601.         isValid = false;
  2602.     }
  2603.  
  2604.     if (!isValid)
  2605.     {
  2606.         var warning = "Errors on page preventing submission, please make appropriate corrections and try again.";
  2607.         $('#warnings-general').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2608.         return false;
  2609.     }
  2610.     return isValid;
  2611. }
  2612.  
  2613. //see if the calculated values are within bounds, then adds red or green border to show if they are
  2614. function checkBetweenBounds()
  2615. {
  2616.     var min = parseFloat($("#minOG").text());
  2617.     var max = parseFloat($("#maxOG").text());
  2618.     var value = parseFloat($("#OG").val());
  2619.     if (value < min || value > max)
  2620.     {
  2621.         $("#OG").attr("class", "vital-stat-element-bad");
  2622.         $("#OGTable").attr("class", "vital-stat-element-bad");
  2623.     }
  2624.     else
  2625.     {
  2626.         $("#OG").attr("class", "vital-stat-element-good");
  2627.         $("#OGTable").attr("class", "vital-stat-element-good");
  2628.     }
  2629.     min = parseFloat($("#minFG").text());
  2630.     max = parseFloat($("#maxFG").text());
  2631.     value = parseFloat($("#FG").text());
  2632.     if (value < min || value > max)
  2633.     {
  2634.         $("#FG").attr("class", "vital-stat-element-bad");
  2635.     }
  2636.     else
  2637.     {
  2638.         $("#FG").attr("class", "vital-stat-element-good");
  2639.     }
  2640.     min = parseFloat($("#minIBU").text());
  2641.     max = parseFloat($("#maxIBU").text());
  2642.     value = parseFloat($("#IBU").val());
  2643.     if (value < min || value > max)
  2644.     {
  2645.         $("#IBU").attr("class", "vital-stat-element-bad");
  2646.         $("#IBUTable").attr("class", "vital-stat-element-bad");
  2647.     }
  2648.     else
  2649.     {
  2650.         $("#IBU").attr("class", "vital-stat-element-good");
  2651.         $("#IBUTable").attr("class", "vital-stat-element-good");
  2652.     }
  2653.     min = parseFloat($("#minSRM").text());
  2654.     max = parseFloat($("#maxSRM").text());
  2655.     value = parseFloat($("#SRM").text());
  2656.     if (value < min || value > max)
  2657.     {
  2658.         $("#SRM").attr("class", "vital-stat-element-bad");
  2659.     }
  2660.     else
  2661.     {
  2662.         $("#SRM").attr("class", "vital-stat-element-good");
  2663.     }
  2664. }
  2665.  
  2666. function calcVitalStats(scaleFactor)
  2667. {
  2668.     fermentorWort = finBeer + 0.2;
  2669.     var hopAmt = 0.0;
  2670.     for (var i = 0; i < recipeHops.length; i++)
  2671.     {
  2672.         hopAmt += parseFloat(recipeHops[i]["Amount"]);
  2673.     }
  2674.  
  2675.     //get the total grain amount from the array that contains lbs values
  2676.     grainSum = grainSumLbs();
  2677.  
  2678.     //Change HTML total to the sum of the input textboxes
  2679.     $(".grainSum").html(Math.round((unitInUS ? grainSum : ConvertLBsToKG(grainSum)) * 100) / 100);
  2680.  
  2681.     var mashThickness;
  2682.     var efficiency;
  2683.     if (mashType == 0)
  2684.     {
  2685.         //calc water amount
  2686.         var grainLossPerLb = 0.104;
  2687.         //calculate efficiency based on mash thickness
  2688.         if (grainSum > 0)
  2689.         {
  2690.             H2O = parseFloat(fermentorWort) + parseFloat((grainSum * grainLossPerLb)) + 0.030 + (0.046 * hopAmt);
  2691.  
  2692.             mashThickness = (H2O * 4) / grainSum;
  2693.             efficiency = 0.55;
  2694.         } else
  2695.         {
  2696.             H2O = 0;
  2697.             mashThickness = 0;
  2698.             efficiency = 0;
  2699.         }
  2700.     }
  2701.  
  2702.     else if (mashType == 1)
  2703.     {
  2704.         var grainLossPerLb = 0.06;
  2705.         //calculate efficiency based on mash thickness
  2706.         if (grainSum > 0)
  2707.         {
  2708.             H2O = parseFloat(fermentorWort) + parseFloat((grainSum * grainLossPerLb)) + 0.030 + (0.046 * hopAmt);
  2709.  
  2710.             mashThickness = (H2O * 4) / grainSum;
  2711.             efficiency = -(0.0087 * Math.pow(mashThickness, 4)) + (0.1245 * Math.pow(mashThickness, 3)) - (0.6438 * Math.pow(mashThickness, 2)) + (1.4384 * mashThickness) - 0.3769;
  2712.         } else
  2713.         {
  2714.             H2O = 0;
  2715.             mashThickness = 0;
  2716.             efficiency = 0;
  2717.         }
  2718.     }
  2719.     eff = efficiency;
  2720.     //calculate gravity point contributions for each row of grain
  2721.     gravitySum = 0;
  2722.     colorSum = 0;
  2723.     for (var i = 0; i < recipeGrains.length; i++)
  2724.     {
  2725.         //Calculate gravity
  2726.         recipeGrains[i]["Gravity"] = efficiency * (fermentableIngredients[recipeGrains[i]["FermentableID"]].Yield * recipeGrains[i]["Amount"] / fermentorWort);
  2727.         //Change HTML value of Gravity to calculated and rounded value
  2728.         $("#gravityPts" + (i + 1)).html(Math.round(recipeGrains[i]["Gravity"] * 100) / 100);
  2729.         //Sum all gravity values
  2730.         gravitySum += recipeGrains[i]["Gravity"];
  2731.     }
  2732.     $(".gravitySum").html(Math.round(gravitySum * 100) / 100);
  2733.  
  2734.     for (var i = 0; i < recipeGrains.length; i++)
  2735.     {
  2736.         if (parseFloat(grainSum) > 0)
  2737.         {
  2738.             var grainAmount = $("#grainsamt" + (i + 1)).val()
  2739.             recipeGrains[i]["Percent"] = Math.round((grainAmount / grainSum) * 100);
  2740.             $("#percent" + (i + 1)).html(recipeGrains[i]["Percent"]);
  2741.         }
  2742.     }
  2743.  
  2744.     //Calculate the color point contributino for each row of grain
  2745.     for (var i = 0; i < recipeGrains.length; i++)
  2746.     {
  2747.         recipeGrains[i]["Color"] = (fermentableIngredients[recipeGrains[i]["FermentableID"]].Color * recipeGrains[i]["Amount"]) / fermentorWort;
  2748.         var temp = "#colorPts" + i;
  2749.         $("#colorPts" + (i + 1)).html(Math.round(recipeGrains[i]["Color"] * 10) / 10);
  2750.         colorSum += recipeGrains[i]["Color"];
  2751.     }
  2752.     //Solve for OG if needed... only allow this if og not 0;
  2753.     if (gravitySum == 0)
  2754.     {
  2755.         OG = 0;
  2756.     } else
  2757.     {
  2758.         OG = 1.000 + (gravitySum / 1000);
  2759.     }
  2760. }
  2761.  
  2762. function solveIBU(scaleFactor)
  2763. {
  2764.     IBU = 0;
  2765.     var kettleUtilization = 4.25;
  2766.     var bignessFactor = 1.65 * Math.pow(0.000125, (OG - 1));
  2767.     for (var i = 0; i < recipeHops.length; i++)
  2768.     {
  2769.         var boilTimeFactor = (1 - Math.pow(2.72, (-0.04 * recipeHops[i]["Time"]))) / kettleUtilization;
  2770.         var hopsUtilizationRate = bignessFactor * boilTimeFactor;
  2771.         if (recipeHops[i]["Amount"] > 0)
  2772.         {
  2773.             recipeHops[i]["Amount"] *= scaleFactor;
  2774.             recipeHops[i]["IBU"] = hopsUtilizationRate * (recipeHops[i]["Amount"] * (recipeHops[i]["Alpha"] / 100) * 7490) / fermentorWort;
  2775.  
  2776.             $("#ibuAMT" + (i + 1)).html(Math.round(parseFloat(recipeHops[i]["IBU"] * 100)) / 100);
  2777.             $("#hopAmt" + (i + 1)).val(unitInUS ? Math.round(parseFloat(recipeHops[i]["Amount"] * 100)) / 100 : ConvertOunceToGram(parseFloat(recipeHops[i]["Amount"])).toFixed(0));
  2778.             IBU += recipeHops[i]["IBU"];
  2779.         }
  2780.     }
  2781. }
  2782.  
  2783.  
  2784. function solveOG()
  2785. {
  2786.     //solve for OG if needed.. only allow this if OG not 0!
  2787.     if (gravitySum == 0)
  2788.     {
  2789.         OG = 0;
  2790.     } else
  2791.     {
  2792.         OG = 1.00 + (gravitySum / 1000)
  2793.     }
  2794.     var TG = parseFloat($("#OG").val());
  2795.     if (OG != 0 && TG != 0 && OG)
  2796.     {
  2797.         //scale the grain in the recipe up
  2798.         var iter = 0;
  2799.         var errorThresh = 0.0009;
  2800.         while (Math.abs(TG - OG) > errorThresh && iter < 10)
  2801.         {
  2802.             var scaleFactor = (TG - 1) / (OG - 1);
  2803.             //calcVitalStats(scaleFactor);
  2804.             for (var i = 0; i < recipeGrains.length; i++)
  2805.             {
  2806.                 recipeGrains[i]["Amount"] = Math.max(scaleFactor * recipeGrains[i]["Amount"], 0.01)
  2807.                 $("#grainsamt" + (i + 1)).val(unitInUS ? recipeGrains[i]["Amount"].toFixed(2) : ConvertLBsToKG(recipeGrains[i]["Amount"]).toFixed(1));
  2808.  
  2809.             }
  2810.             calcVitalStats(scaleFactor);
  2811.             iter = iter + 1;
  2812.         }
  2813.     }
  2814.     $("#H2O").html(Math.round(H2O * 100) / 100);
  2815.     $("#OG").html(Math.round(OG * 1000) / 1000);
  2816.     $("#FG").html(Math.round(FG * 1000) / 1000);
  2817.     $("#IBU").html(Math.round(IBU));
  2818.     $("#SRM").html(Math.round(SRM));
  2819.     $("#ABV").html(Math.round(ABV * 10) / 10);
  2820.  
  2821.     checkBetweenBounds();
  2822.     changePicture();
  2823.     textRecipeDescription();
  2824.     recalcForm();
  2825. }
  2826.  
  2827. //calculates vital stats
  2828.  
  2829. //Get sum of all grain inputs and return total weight
  2830. function grainSumCheck()
  2831. {
  2832.  
  2833.     var grainTotal = 0.0;
  2834.     for (var grain in recipeGrains)
  2835.     {
  2836.         grainTotal += parseFloat(recipeGrains[grain]["Amount"]);
  2837.     }
  2838.     if (grainTotal > grainLimitUS)
  2839.     {
  2840.         $('#warnings-grains2').empty();
  2841.         var warning = "Grain limit of compartment estimated to be " + (unitInUS ? grainLimitUSString : grainLimitMetricString) + " (excluding sugars which can be added to keg). Be advised that exceeding this limit reduces calculation accuracy and may not fit in the machine.";
  2842.         $('#warnings-grains2').append("<div class=\"alert alert-info alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2843.     }
  2844.     else
  2845.     {
  2846.         $('#warnings-grains2').empty();
  2847.     }
  2848.     return grainTotal;
  2849. }
  2850.  
  2851. //Executes when the beer type in the Normal Fermentation Schedule is Selected
  2852. //Fermentation Steps
  2853. function normalFermentation(select)
  2854. {
  2855.  
  2856.     var value = parseInt(select.value);
  2857.     switch (value)
  2858.     {
  2859.         default:
  2860.         case 0:
  2861.             //Change step table to the normal ale schedule
  2862.             normalAleSchedule();
  2863.             break;
  2864.         case 1:
  2865.             normalLagerSchedule();
  2866.             break;
  2867.     }
  2868.     changeNormalScheduleDescription();
  2869. }
  2870.  
  2871.  
  2872.  
  2873. //Executes on any text input change for the advanced editor
  2874. //table to pull information from is id advFermSched
  2875. //Fermentation Steps
  2876. function storeAdvancedFermentation()
  2877. {
  2878.     $('#warnings-ferm').empty();
  2879.     //Clear the current array
  2880.     while (advFermSteps.length > 0)
  2881.     {
  2882.         advFermSteps.pop();
  2883.     }
  2884.  
  2885.     //get title headers as strings
  2886.     $('#advFermSched th').each(function (index, item)
  2887.     {
  2888.         advFermHeaders[index] = $(item).data("name");
  2889.     });
  2890.  
  2891.     $('#advFermSched tr').has('td').each(function ()
  2892.     {
  2893.         var fermStep = {};
  2894.         var store = true;
  2895.         $('td', $(this)).each(function (index, item)
  2896.         {
  2897.             //Check if step is still intended to be stored
  2898.             if (advFermHeaders[index] == "Step")
  2899.             {
  2900.                 fermStep[advFermHeaders[index]] = $(item).html();
  2901.             } else
  2902.             {
  2903.                 fermStep[advFermHeaders[index]] = $('input', $(this)).val().trim();
  2904.                 //Check if this field is populated with a value
  2905.                 if (!!fermStep[advFermHeaders[index]])
  2906.                 {
  2907.                     $('input', $(this)).removeClass("error");
  2908.                     //Check value if this is not a name field
  2909.                     if (advFermHeaders[index] != "Name")
  2910.                     {
  2911.                         var value = parseFloat($('input', $(this)).val());
  2912.                         var max = parseFloat($('input', $(this)).prop("max"));
  2913.                         var min = parseFloat($('input', $(this)).prop("min"));
  2914.                         if (value > max || value < min)
  2915.                         {
  2916.                             $('input', $(this)).addClass("error");
  2917.                             var warning = "Step " + fermStep["Step"] + ": " + advFermHeaders[index] + " value is out of acceptable ranges (" + $('input', $(this)).prop("min") + " - " + $('input', $(this)).prop("max") + ")";
  2918.                             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2919.                         }
  2920.                     }
  2921.                     //Check if the name field is empty
  2922.                     if (!fermStep["Name"])
  2923.                     {
  2924.                         $("#advFermName" + $('input', $(this))[0].id.slice(-1)).addClass("error");
  2925.                     }
  2926.                     else
  2927.                     {
  2928.                         $("#advFermName" + $('input', $(this))[0].id.slice(-1)).removeClass("error");
  2929.                     }
  2930.  
  2931.                 } else
  2932.                 {
  2933.                     //If name is populated, add error class to other values
  2934.                     if (!!fermStep["Name"])
  2935.                     {
  2936.                         $('input', $(this)).addClass("error");
  2937.                     }
  2938.                     else
  2939.                     {
  2940.                         $('input', $(this)).removeClass("error");
  2941.                     }
  2942.                     //Don't store any incomplete entries
  2943.                     store = false;
  2944.                 }
  2945.             }
  2946.         })
  2947.  
  2948.         //If any of the fields are populated and name is empty, throw a warning
  2949.         if (!fermStep["Name"] && (!!fermStep["Temp"] || !!fermStep["Days"] || !!fermStep["Hours"]))
  2950.         {
  2951.             var warning = "Step " + fermStep["Step"] + ": Empty Name";
  2952.             $('#warnings-ferm').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  2953.         }
  2954.         else
  2955.         {
  2956.             $("#advFermName" + fermStep["Step"]).removeClass("error");
  2957.         }
  2958.         //If the current unit is in celcius, convert to F as a whole number
  2959.         if (!unitInUS && pageLoaded)
  2960.         {
  2961.             fermStep["Temp"] = ConvertCelciusToFahrenheit(fermStep["Temp"]).toFixed(0);
  2962.         }
  2963.         //If all values were populated, store the ferm step
  2964.         if (store)
  2965.         {
  2966.             advFermSteps.push(fermStep);
  2967.         }
  2968.     });
  2969. }
  2970.  
  2971. //Calculate number of characters remaining on the Notes Input
  2972. //Recipe Notes
  2973. function noteCharRemaining()
  2974. {
  2975.     var limit = 4000;
  2976.     var notes = document.getElementById("notes-text-area");
  2977.     var remaining = limit - notes.value.length;
  2978.     var charsRemainRow = document.getElementById("noteCharRemainRow");
  2979.     if (remaining < limit)
  2980.     {
  2981.         charsRemainRow.style.visibility = 'visible';
  2982.         charsRemainRow.style.display = 'block';
  2983.         $("#notesRemaining").html(remaining + " characters remaining.");
  2984.     } else
  2985.     {
  2986.         charsRemainRow.style.visibility = 'hidden';
  2987.         charsRemainRow.style.display = 'none';
  2988.     }
  2989. }
  2990.  
  2991. //Populate the advanced steps with normal ale
  2992. //Fermentation Steps
  2993. function normalAleSchedule()
  2994. {
  2995.     //clear all current values in the schedule
  2996.     clearAdvanacedSchedule();
  2997.     //Populate step table with normal ale steps
  2998.     //Step 1 is fermentation at 66F
  2999.     document.getElementById("advFermName1").value = "Fermentation";
  3000.     if ($("[name=yeast]").val() != 0)
  3001.     {
  3002.         document.getElementById("advFermTemp1").value = yeastPitch;
  3003.         changeNormalScheduleDescription();
  3004.     }
  3005.     else
  3006.     {
  3007.         document.getElementById("advFermTemp1").value = aleFermTemp;
  3008.         changeNormalScheduleDescription();
  3009.     }
  3010.     document.getElementById("advFermDay1").value = aleFermTimeDays;
  3011.     document.getElementById("advFermHour1").value = 0;
  3012.     //Step 2 is chilling to slow fermentation
  3013.     document.getElementById("advFermName2").value = "Crash Chill";
  3014.     document.getElementById("advFermTemp2").value = 44;
  3015.     document.getElementById("advFermDay2").value = 0;
  3016.     document.getElementById("advFermHour2").value = 0;
  3017.     //Check if page is loaded to determine if the value needs to be displayed in metric if necessary
  3018.     if (!unitInUS && pageLoaded)
  3019.     {
  3020.         document.getElementById("advFermTemp1").value = RoundedConvertFahrenheitToCelcius(document.getElementById("advFermTemp1").value);
  3021.         document.getElementById("advFermTemp2").value = RoundedConvertFahrenheitToCelcius(document.getElementById("advFermTemp2").value);
  3022.     }
  3023.     storeAdvancedFermentation();
  3024. }
  3025.  
  3026. //Populate advanced steps with normal lager shcedule
  3027. //Fermentation Steps
  3028. function normalLagerSchedule()
  3029. {
  3030.     //clear all current values in the schedule
  3031.     clearAdvanacedSchedule();
  3032.     //Populate step table with normal ale steps
  3033.     //Step 1 is fermentation at 66F
  3034.     document.getElementById("advFermName1").value = "Primary Fermentation";
  3035.     if ($("[name=yeast]").val() != 0)
  3036.     {
  3037.         document.getElementById("advFermTemp1").value = yeastPitch;
  3038.         changeNormalScheduleDescription();
  3039.     }
  3040.     else
  3041.     {
  3042.         document.getElementById("advFermTemp1").value = lagerFermTemp;
  3043.         changeNormalScheduleDescription();
  3044.     }
  3045.     document.getElementById("advFermDay1").value = 21;
  3046.     document.getElementById("advFermHour1").value = 0;
  3047.     //Step 2 is chilling to stop fermentation
  3048.     document.getElementById("advFermName2").value = "Lagering";
  3049.     document.getElementById("advFermTemp2").value = 35;
  3050.     document.getElementById("advFermDay2").value = 28;
  3051.     document.getElementById("advFermHour2").value = 0;
  3052.        
  3053.     //Check if page is loaded to determine if the value needs to be displayed in metric if necessary
  3054.     if (!unitInUS && pageLoaded)
  3055.     {
  3056.         document.getElementById("advFermTemp1").value = RoundedConvertFahrenheitToCelcius(document.getElementById("advFermTemp1").value);
  3057.         document.getElementById("advFermTemp2").value = RoundedConvertFahrenheitToCelcius(document.getElementById("advFermTemp2").value);
  3058.     }
  3059.  
  3060.     storeAdvancedFermentation();
  3061. }
  3062.  
  3063. //Clear contents of the advanced schedule
  3064. //Fermentation Steps
  3065. function clearAdvanacedSchedule()
  3066. {
  3067.     $('#advFermSched tr').has('td').each(function ()
  3068.     {
  3069.         //If there is input clear the values
  3070.         if (!!$('input', $(this)).val())
  3071.         {
  3072.             $('input', $(this)).val("");
  3073.         }
  3074.     });
  3075. }
  3076.  
  3077. //Store Fermentables\Grains
  3078. //re-orders all selections to the top
  3079. function storeFermentables()
  3080. {
  3081.     //Clear the current array
  3082.     recipeGrains = [];
  3083.  
  3084.     //get title headers as strings
  3085.     $('#mash-table th').each(function (index, item)
  3086.     {
  3087.         recipeGrainHeaders[index] = $(item).data("name");
  3088.     });
  3089.  
  3090.     $('#mash-table tr').has('td').each(function ()
  3091.     {
  3092.         if (this.id != "fermentablesTotal")
  3093.         {
  3094.             var ingredient = {};
  3095.             var store = true;
  3096.             $('td', $(this)).each(function (index, item)
  3097.             {
  3098.                 if (recipeGrainHeaders[index] == "FermentableID")
  3099.                 {
  3100.                     ingredient[recipeGrainHeaders[index]] = parseInt($('input', $(this)).data("ingredientID"));
  3101.                     if (!!ingredient[recipeGrainHeaders[index]])
  3102.                     {
  3103.                         ingredient.Ingredient = fermentableIngredients[ingredient[recipeGrainHeaders[index]]];
  3104.                     }
  3105.                     else
  3106.                     {
  3107.                         return true;
  3108.                     }
  3109.                 }
  3110.                 else if (recipeGrainHeaders[index] == "Amount")
  3111.                 {
  3112.                     $('input', $(this)).removeClass("error");
  3113.                     ingredient[recipeGrainHeaders[index]] = parseFloat($('input', $(this)).val());
  3114.                     if (ingredient[recipeGrainHeaders[index]] < 0)
  3115.                     {
  3116.                         $('input', $(this)).addClass("error");
  3117.                         var warning = "Grain Amount can't be less than 0.";
  3118.                         $('#warnings-grains').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  3119.                     }
  3120.                 }
  3121.                 else
  3122.                 {
  3123.                     ingredient[recipeGrainHeaders[index]] = parseFloat($(item).html());
  3124.                     $(item).html("0");
  3125.                 }
  3126.             })
  3127.  
  3128.             //If the current unit is in celcius, convert to F as a whole number
  3129.             if (!unitInUS && pageLoaded)
  3130.             {
  3131.                 ingredient["Amount"] = RoundedConvertKgToLbs(ingredient["Amount"]);
  3132.             }
  3133.             //Store if there are no null values and both the ID and Amount are populated.
  3134.             if (ingredient["FermentableID"])
  3135.             {
  3136.                 recipeGrains.push(ingredient);
  3137.             }
  3138.         }
  3139.     });
  3140.     organizeGrains();
  3141. }
  3142.  
  3143. function organizeGrains(){
  3144.     //clear all current fields
  3145.     $("tr.recipegrain").each(function (index)
  3146.     {
  3147.         if (index < recipeGrains.length)
  3148.         {
  3149.             $('input.fermentable-search', $(this)).val(recipeGrains[index].Ingredient.Name);
  3150.             $('input.fermentable-search', $(this)).data("ingredientID", recipeGrains[index].FermentableID);
  3151.             $('input.fermentable-search', $(this)).prop('disabled', true);
  3152.             $('span.ingredient-remove', $(this)).show();
  3153.             if (unitInUS) {
  3154.                 $('input.fermentable-amount', $(this)).val(recipeGrains[index].Amount);
  3155.  
  3156.             } else {
  3157.                 $('input.fermentable-amount', $(this)).val(Math.round(ConvertLBsToKG(recipeGrains[index].Amount) * 100) / 100);
  3158.             }
  3159.         }
  3160.         else
  3161.         {
  3162.             $('input.fermentable-search', $(this)).prop('disabled', false);
  3163.             $('input.fermentable-search', $(this)).val("");
  3164.             $('input.fermentable-search', $(this)).removeData("ingredientID");
  3165.             $('span.ingredient-remove', $(this)).hide();
  3166.             $('input.fermentable-amount', $(this)).val(0);
  3167.             $('td.fermentable-gravity', $(this)).html(0);
  3168.             $('td.fermentable-color', $(this)).html(0);
  3169.             $('td.fermentable-percent', $(this)).html(0);
  3170.         }
  3171.     });
  3172. }
  3173.  
  3174. function storeHops()
  3175. {
  3176.     //Clear any warnings
  3177.     $('#warnings-hops').empty();
  3178.     //Clear the current array
  3179.     while (recipeHops.length > 0)
  3180.     {
  3181.         recipeHops.pop();
  3182.     }
  3183.  
  3184.     //get title headers as strings
  3185.     $('#hops-table th').each(function (index, item)
  3186.     {
  3187.         recipeHopsHeaders[index] = $(item).data("name");
  3188.     });
  3189.  
  3190.     $('#hops-table tr').has('td').each(function ()
  3191.     {
  3192.         var hop = {};
  3193.         var store = true;
  3194.         $('td', $(this)).each(function (index, item)
  3195.         {
  3196.             //Check if step is still intended to be stored
  3197.             if (recipeHopsHeaders[index] == "HopID")
  3198.             {
  3199.                 hop[recipeHopsHeaders[index]] = parseInt($('input', $(this)).data("ingredientID"));
  3200.                 if (!!hop[recipeHopsHeaders[index]]) {
  3201.                     hop.Ingredient = hopIngredients[hop[recipeHopsHeaders[index]]];
  3202.                 }
  3203.                 else
  3204.                 {
  3205.                     return true;
  3206.                 }
  3207.             }
  3208.             else if (recipeHopsHeaders[index] == "Amount" || recipeHopsHeaders[index] == "Time" || recipeHopsHeaders[index] == "Alpha")
  3209.             {
  3210.                 $('input', $(this)).removeClass("error");
  3211.                 hop[recipeHopsHeaders[index]] = parseFloat($('input', $(this)).val());
  3212.                 var min = parseFloat($('input', $(this)).attr("min"));
  3213.                 var max = parseFloat($('input', $(this)).attr("max"));
  3214.                 if (hop[recipeHopsHeaders[index]] < min || hop[recipeHopsHeaders[index]] > max)
  3215.                 {
  3216.                     $('input', $(this)).addClass("error");
  3217.                     var warning = "";
  3218.                     if ($('input', $(this))[0].id.indexOf("hopAmt") != -1)
  3219.                     {
  3220.                         warning = "Recipe hop amount is required and must be a number no greater than " + (unitInUS ? hopLimitUSString : hopLimitMetricString) + ". Hop amount in excess of " + (unitInUS ? hopLimitUSString : hopLimitMetricString) + " can be split into multiple cages.";
  3221.                     } else if ($('input', $(this))[0].id.indexOf("hopTime") != -1)
  3222.                     {
  3223.                         warning = "Recipe hop time is required and must be a number greater than 300, rounded to the nearest minute.";
  3224.                     } else if ($('input', $(this))[0].id.indexOf("hopAA") != -1)
  3225.                     {
  3226.                         warning = "Hop Alpha Acid must be greater than 0 and less than 20 percent.";
  3227.                     }
  3228.  
  3229.                     $('#warnings-hops').append("<div class=\"alert alert-warning alert-dismissable\"><button type=\"button\" class=\"close\" data-dismiss=\"alert\" aria-hidden=\"true\">&times\;</button>" + warning + "</div>");
  3230.                 }
  3231.             }
  3232.  
  3233.             else if (recipeHopsHeaders[index] == "Use")
  3234.             {
  3235.                 hop[recipeHopsHeaders[index]] = $('select', $(this)).val();
  3236.             }
  3237.             else
  3238.             {
  3239.                 hop[recipeHopsHeaders[index]] = parseFloat($(item).html());
  3240.             }
  3241.         });
  3242.         //If the current unit is in celcius, convert to F as a whole number
  3243.         if (!unitInUS && pageLoaded)
  3244.         {
  3245.             hop["Amount"] = Math.min(ConvertGramToOunce(hop["Amount"]), 1.5);
  3246.         }
  3247.         //If all values were populated, store the ferm step
  3248.         if (hop["HopID"])
  3249.         {
  3250.             recipeHops.push(hop);
  3251.         }
  3252.     });
  3253.     organizeHops();
  3254. }
  3255.  
  3256. function organizeHops() {
  3257.     //clear all current fields
  3258.     $("tr.recipehop").each(function (index) {
  3259.         if (index < recipeHops.length) {
  3260.             $('input.hop-search', $(this)).val(recipeHops[index].Ingredient.Name);
  3261.             $('input.hop-search', $(this)).data("ingredientID", recipeHops[index].HopID);
  3262.             $('input.hop-search', $(this)).prop('disabled', true);
  3263.             $('span.ingredient-remove', $(this)).show();
  3264.             if (unitInUS) {
  3265.                 $('input.hop-amount', $(this)).val(recipeHops[index].Amount);
  3266.             } else {
  3267.                 $('input.hop-amount', $(this)).val(Math.round(ConvertOunceToGram(recipeHops[index].Amount)));
  3268.             }
  3269.             $('input.hop-alphaacid', $(this)).val(recipeHops[index].Alpha);
  3270.             $('input.hop-time', $(this)).val(recipeHops[index].Time);
  3271.         }
  3272.         else
  3273.         {
  3274.             $('input.hop-search', $(this)).prop('disabled', false);
  3275.             $('input.hop-search', $(this)).val("");
  3276.             $('input.hop-search', $(this)).removeData("ingredientID");
  3277.             $('span.ingredient-remove', $(this)).hide();
  3278.             $('input.hop-amount', $(this)).val(0);
  3279.             $('input.hop-alphaacid', $(this)).val(0);
  3280.             $('input.hop-time', $(this)).val(0);
  3281.             $('td.hop-ibu', $(this)).html("0")
  3282.         }
  3283.     });
  3284. }
  3285.  
  3286. function checkAdvancedFermSchedule()
  3287. {
  3288.     //Determine if the standard Fermentation Options should be shown or the advanced steps
  3289.     if (!!fermSteps)
  3290.     {
  3291.         if (JSON.stringify(fermSteps) == normalAleFermSteps)
  3292.         {
  3293.             //Change to Ale
  3294.             document.getElementById("normalFermSelection").getElementsByTagName('option')[0].selected = 'selected';
  3295.             //Change the schedule to Normal Ale
  3296.             fermScheduleChange(document.getElementById("fermTypeSelection"))
  3297.  
  3298.         }
  3299.         else if (JSON.stringify(fermSteps) == normalLagerFermSteps)
  3300.         {
  3301.             //Change to Lager
  3302.             document.getElementById("normalFermSelection").getElementsByTagName('option')[1].selected = 'selected';
  3303.             //Change the schedule to Normal Lager
  3304.             fermScheduleChange(document.getElementById("fermTypeSelection"))
  3305.         }
  3306.             //Empty, show NONE option
  3307.         else if (fermSteps.length == 0)
  3308.         {
  3309.             normalAleSchedule();
  3310.         }
  3311.             //If neither of the above, it's advanaced Steps
  3312.         else
  3313.         {
  3314.             document.getElementById("fermTypeSelection").getElementsByTagName('option')[1].selected = 'selected';
  3315.         }
  3316.         fermScheduleChange(document.getElementById("fermTypeSelection"));
  3317.     }
  3318.     else
  3319.     {
  3320.         normalAleSchedule();
  3321.     }
  3322. }
  3323.  
  3324. //=====================POPULATE FUNCTIONS=====================
  3325.  
  3326. //Populate Options for BJCP Styles
  3327. function populateBeverageStyles()
  3328. {
  3329.     //Populate the Select Dropdown with all beverage style options
  3330.     //Find all the styles based on expected ordering by style
  3331.     var styleGuide = "";
  3332.     for (var styleID in beerStylesJSON)
  3333.     {
  3334.         //Add all the different style guides to a list to filter
  3335.         if(beerStylesJSON[styleID].StyleGuide != styleGuide) {
  3336.             styleGuides.push(beerStylesJSON[styleID].StyleGuide);
  3337.             styleGuide = beerStylesJSON[styleID].StyleGuide;
  3338.         }
  3339.     }
  3340.     //Add the style guide option for each guide
  3341.     for(var i = 0; i < styleGuides.length; i++) {
  3342.         $("#BeerStyleGuide").append('<option value="' + styleGuides[i] + '">' + styleGuides[i] +'</option>');
  3343.     }
  3344.  
  3345.     if (recipeStyle > 0)
  3346.     {
  3347.         LoadBeverageStyles(beerStylesJSON[recipeStyle].StyleGuide);
  3348.         $("#BeerStyle").val(recipeStyle);
  3349.     }
  3350.     else
  3351.     {
  3352.         LoadBeverageStyles();
  3353.     }
  3354.     //Set the style guide to the guide used for the beer style
  3355.     $("#BeerStyleGuide").val($("#BeerStyle option:selected").data("guide"));
  3356. }
  3357.  
  3358. //Populate Fermentation\Grain Input Table
  3359. function populateFermentablesIngredients()
  3360. {
  3361.     //Create as many rows as needed
  3362.     for (i = 0; i < maxFermentableCount; i++)
  3363.     {
  3364.         //Create starting row tag and beginning column
  3365.         var rowNumberID = "fermentableRowNum" + i;
  3366.         $("#mash-table").append('<tr class="recipegrain" id="' + rowNumberID + '"></tr>');
  3367.  
  3368.         var colNumberID = "fermentableRow" + i + "Col0";
  3369.         $("#" + rowNumberID).append('<td id="' + colNumberID + '"></td>')
  3370.  
  3371.         //Create Selection Box with name grain(i) in new Row
  3372.         var grainResultID = "grainResults" + (i + 1);
  3373.         var grainSelectID = "grainSelect" + (i + 1);
  3374.         $("#" + colNumberID).append('<div class="search" id="' + grainSelectID + '" data-index="' + (i + 1) + '"><input class="fermentable-search" type="text" oninput="filterResults(this)" onfocus="filterResults(this)" " /><div class="remove-container"><span class="ingredient-remove glyphicon glyphicon-remove" onclick="removeFermentable(this)"></div></span><dl class="results" id="' + grainResultID + '"></dl></div>')
  3375.  
  3376.         //First element generates the HTML while all additional elements can copy the HTML generated
  3377.         if (i == 0)
  3378.         {
  3379.             //Populate with options for selection
  3380.             for (var ingredient in fermentableIngredients)
  3381.             {
  3382.                 //Get the Type from FermTypeCode
  3383.                 var type = fermentableIngredients[ingredient].FermTypeCode.replace(/\s+/g, '').toLowerCase();
  3384.  
  3385.                 //Check if type exists, if not create it
  3386.                 if (document.getElementById("gp-" + type) === null)
  3387.                 {
  3388.                     $("#" + grainResultID).append('<dt class="no-select" id="gp-' + type + '" data-type="' + type + '" data-filter="" onclick="hideResults(this)">' + fermentableIngredients[ingredient].FermTypeCode + '</dt>');
  3389.                 }
  3390.  
  3391.                 $("#" + grainResultID).append('<dd data-value="' + fermentableIngredients[ingredient].FermentableID + '" data-type="' + type + '" data-filter="' + fermentableIngredients[ingredient].Name + '" onclick="changeFermentable(this)">' + fermentableIngredients[ingredient].Name + '</dd>');
  3392.             }
  3393.             //Sort the list
  3394.             var $grainResult = $("#" + grainResultID);
  3395.             var $grainOptions = $grainResult.children();
  3396.             $grainOptions.sort(function (a, b) {
  3397.                 var at = a.getAttribute("data-type").toLowerCase(),
  3398.                     bt = b.getAttribute("data-type").toLowerCase();
  3399.  
  3400.                 if (at === bt)
  3401.                 {
  3402.                     var an = a.getAttribute("data-filter").toLowerCase(),
  3403.                         bn = b.getAttribute("data-filter").toLowerCase();
  3404.                     if (an > bn) {
  3405.                         return 1;
  3406.                     }
  3407.                     if (an < bn) {
  3408.                         return -1;
  3409.                     }
  3410.                     return 0;
  3411.                 }
  3412.                 else
  3413.                 {
  3414.                     if (at > bt) {
  3415.                         return 1;
  3416.                     }
  3417.                     if (at < bt) {
  3418.                         return -1;
  3419.                     }
  3420.                     return 0;
  3421.                 }
  3422.                
  3423.                
  3424.             });
  3425.             $grainOptions.detach().appendTo($grainResult);
  3426.         }
  3427.         else
  3428.         {
  3429.             $("#" + grainResultID).html($("#grainResults1").html());
  3430.         }
  3431.  
  3432.  
  3433.         //Create column for Number Input
  3434.         var mashAmountID = "mash-table-amount" + i
  3435.         $("#" + rowNumberID).append('<td id="' + mashAmountID + '"></td>');
  3436.         //Create Input Form
  3437.         $("#" + mashAmountID).append('<input class="fermentable-amount recipe-numeric-input" id="grainsamt' + (i + 1) + '" type="number" min="0" step="0.01" value="0" onChange="grainAmountChange(this)">');
  3438.  
  3439.         //Column for Gravity Points
  3440.         $("#" + rowNumberID).append('<td class="fermentable-gravity" id="gravityPts' + (i + 1) + '">' + 0 + '</td>');
  3441.  
  3442.         //Column for Color Points
  3443.         $("#" + rowNumberID).append('<td class="fermentable-color" id="colorPts' + (i + 1) + '">' + 0 + '</td>');
  3444.  
  3445.         //Column for Ingredient Percentage Total
  3446.         $("#" + rowNumberID).append('<td class="fermentable-percent" id="percent' + (i + 1) + '">' + 0 + '</td>');
  3447.     }
  3448.  
  3449.     //Append total row after
  3450.     $("#mash-table").append('<tr class="reciperow" id="fermentablesTotal">\n\t<td style="font-weight: bold">Total</td>\n\t\t<td style="font-weight: bold" class="grainSum"></td>\n\t\t<td style="font-weight: bold" class="gravitySum"></td>\n\t\t<td style="font-weight: bold"></td>\n\t\t<td style="font-weight: bold"></td>\n\t</tr>');
  3451. }
  3452.  
  3453. function populateHopIngredients()
  3454. {
  3455.     for (var i = 0; i < maxHopCount; i++)
  3456.     {
  3457.         //Create starting Row
  3458.         var rowNumberID = "hopRowNum" + i;
  3459.         $("#hops-table").append('<tr class="recipehop" id="' + rowNumberID + '"></tr>');
  3460.  
  3461.         //Create starting column that will contain selection
  3462.         var colNumberID = "hopRow" + i + "Col0";
  3463.         $("#" + rowNumberID).append('<td id="' + colNumberID + '"></td>')
  3464.  
  3465.         //Create Selection Box with name grain(i) in new Row
  3466.         var hopResultID = "hopResults" + (i + 1);
  3467.         var hopSelectID = "hopSelect" + (i + 1);
  3468.         $("#" + colNumberID).append('<div class="search" id="' + hopSelectID + '" data-index="' + (i + 1) + '"><input class="hop-search" type="text" oninput="filterResults(this)" onfocus="filterResults(this)" " /><div class="remove-container"><span class="ingredient-remove glyphicon glyphicon-remove" onclick="removeHop(this)"></div></span><dl class="results" id="' + hopResultID + '"></dl></div>')
  3469.  
  3470.         //First element generates the HTML while all additional elements can copy the HTML generated
  3471.         if (i == 0) {
  3472.             //Populate with options for selection
  3473.             for (var ingredient in hopIngredients)
  3474.             {
  3475.                 $("#" + hopResultID).append('<dd data-value="' + hopIngredients[ingredient].HopID + '" data-filter="' + hopIngredients[ingredient].Name + '" onclick="changeHop(this)">' + hopIngredients[ingredient].Name + '</dd>');
  3476.             }
  3477.             //Sort the list
  3478.             var $hopResult = $("#" + hopResultID);
  3479.             var $hopOptions = $hopResult.children("dd");
  3480.             $hopOptions.sort(function (a, b) {
  3481.                 var an = a.getAttribute("data-filter").toLowerCase(),
  3482.                     bn = b.getAttribute("data-filter").toLowerCase();
  3483.                 if (an > bn) {
  3484.                     return 1;
  3485.                 }
  3486.                 if (an < bn) {
  3487.                     return -1;
  3488.                 }
  3489.                 return 0;
  3490.             });
  3491.             $hopOptions.detach().appendTo($hopResult);
  3492.         }
  3493.         else {
  3494.             $("#" + hopResultID).html($("#hopResults1").html());
  3495.         }
  3496.  
  3497.         //Add the column for amount and the input
  3498.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input valid hop-amount" id="hopAmt' + (i + 1) + '" type="number" step="0.01" min="0" max="1.5" value="0" onChange="hopInputChange(this)"></td>');
  3499.  
  3500.         //Add the column for AA and the input
  3501.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input valid hop-alphaacid" id="hopAA' + (i + 1) + '" type="number" step="0.1" min="0" max="20" value="0" onChange="hopInputChange(this)"></td>');
  3502.  
  3503.         //Add the column for Hop Use and the Select
  3504.         var hopUseID = "hopUse" + (i + 1);
  3505.         $("#" + rowNumberID).append('<td><select class="selectS valid hop-use" id="' + hopUseID + '"></select></td>');
  3506.  
  3507.         //Add boil option
  3508.         $("#" + hopUseID).append('<option value="Boil">Boil</option>')
  3509.  
  3510.         //Add the column for Time and the input
  3511.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input valid hop-time" id="hopTime' + (i + 1) + '" type="number" step="1" min="0" max="300" value="0" onChange="hopInputChange(this)"></td>')
  3512.  
  3513.         //Add text HTML for IBU
  3514.         $("#" + rowNumberID).append('<td class="recipe-ibu-amt hop-ibu" id="ibuAMT' + (i + 1) + '">0</td>');
  3515.  
  3516.     }
  3517. }
  3518.  
  3519. function populateFermentationSteps()
  3520. {
  3521.     for (var i = 0; i < maxFermStepCount; i++)
  3522.     {
  3523.         var rowNumberID = "fermStepRow" + i;
  3524.         $("#advFermSched").append('<tr class="reciperow" id="' + rowNumberID + '"></tr>');
  3525.  
  3526.         //Step Number Column
  3527.         $("#" + rowNumberID).append('<td class="advRecipeFirstCol" id="advFermStep' + (i + 1) + '">' + (i + 1) + '</td>');
  3528.  
  3529.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input" id="advFermName' + (i + 1) + '" style="width: 95%; text-align: left" onchange="storeAdvancedFermentation()" maxlength="20"></td>');
  3530.  
  3531.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input fermTemp" type="number" min="32" max="85" step="1" data-name="Temp" id="advFermTemp' + (i + 1) + '" onchange="storeAdvancedFermentation()"></td>');
  3532.  
  3533.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input" type="number" min="0" max="45" step="1" id="advFermDay' + (i + 1) + '" onchange="storeAdvancedFermentation()"></td>');
  3534.  
  3535.         $("#" + rowNumberID).append('<td><input class="recipe-numeric-input" type="number" min="0" max="24" step="0.1" id="advFermHour' + (i + 1) + '" onchange="storeAdvancedFermentation()"></td>');
  3536.  
  3537.         if (fermSteps.length > i)
  3538.         {
  3539.             $("#advFermName" + (i + 1)).val(fermSteps[i].Name);
  3540.             $("#advFermTemp" + (i + 1)).val(fermSteps[i].Temp);
  3541.             //Get total time in minutes
  3542.             var minuteSum = 0;
  3543.             minuteSum += fermSteps[i].Days * 1440;
  3544.             minuteSum += fermSteps[i].Hours * 60;
  3545.             minuteSum += fermSteps[i].Minutes;
  3546.             //Make sure the number is always positive
  3547.             minuteSum = Math.max(0, minuteSum);
  3548.             $("#advFermDay" + (i + 1)).val(Math.floor(minuteSum / 1440));
  3549.             $("#advFermHour" + (i + 1)).val(minuteSum % 1440 / 60);
  3550.         }
  3551.     }
  3552.  
  3553.     storeAdvancedFermentation();
  3554. }
  3555.  
  3556. function filterResults(target) {
  3557.     var filterText = target.value.toLowerCase();
  3558.     var results = $(target.parentNode).children(".results");
  3559.     if(results.length > 0) {
  3560.         for(var i = 0; i < results.length; i++) {
  3561.             var items = $(results[i]).children("dd");
  3562.             for(var j = 0; j < items.length; j++) {
  3563.                 var itemFilter = $(items[j]).data("filter");
  3564.                 if(!!itemFilter) {
  3565.                     if(itemFilter.toLowerCase().indexOf(filterText) > -1) {
  3566.                         $(items[j]).show();
  3567.                     } else {
  3568.                         $(items[j]).hide();
  3569.                     }
  3570.                 }
  3571.             }
  3572.         }
  3573.     }
  3574. }
  3575.  
  3576. function hideResults(target) {
  3577.     $(target.parentNode).hide();
  3578. }
Advertisement
Add Comment
Please, Sign In to add comment