UNPKG

nutrition-label-jquery-plugin

Version:

To be able to create a FDA-style nutrition label with any nutrition data source

1,148 lines (1,006 loc) 153 kB
/* ***************************************************************************************************************************************************+ * NUTRITIONIX.com | * | * This plugin allows you to create a fully customizable nutrition label | * | * @authors Leo Joseph Gajitos <leejay22@gmail.com>, Rommel Malang <genesis23rd@gmail.com> and Yurko Fedoriv <yurko.fedoriv@gmail.com> | * @copyright Copyright (c) 2017 Nutritionix. | * @license This Nutritionix jQuery Nutrition Label is dual licensed under the MIT and GPL licenses. | * @link http://www.nutritionix.com | * @github http://github.com/nutritionix/nutrition-label | * @current version 11.0.13 | * @stable version 11.0.4 | * @supported browser Firefox, Chrome, IE8+ | * @description To be able to create a FDA-style nutrition label with any nutrition data source | * | ***************************************************************************************************************************************************+ */ //these variables are declared using eval /*global globalTab1, globalTab2, globalTab3, globalTab4, globalTab5, globalTab6, globalTab7 */ ;(function($) { // eslint-disable-line no-extra-semi //initializing the global tab variables (for debugging and editing purposes) //global tab variables are used to make the printing of the html code readable when you copy the code using firebug => inspect => copy innerhtml for (let x = 1; x < 8; x++) { let tab = ''; for (let y = 1; y <= x; y++) { tab += '\t'; } eval('var globalTab' + x + ' = "' + tab + '";'); } $.fn.nutritionLabel = function(option, settings) { if (typeof option === 'object') { settings = option; init(settings, $(this)); } else if (typeof option === 'string' && option !== '') { //destroy the nutrition label's html code if (option === 'destroy') { new NutritionLabel().destroy($(this)); //allows the user to hide the nutrition value } else if (option === 'hide') { new NutritionLabel().hide($(this)); //allows the user to show the nutrition value } else if (option === 'show') { new NutritionLabel().show($(this)); } else { let values = []; let elements = this.each(function() { let data = $(this).data('_nutritionLabel'); if (data) { if ($.fn.nutritionLabel.defaultSettings[option] !== undefined) { if (settings !== undefined) { //set the option and create the nutrition label data.settings[option] = settings; init(data.settings, $(this)); } else { //return the value of a setting - can only be used after the label is created / initiated values.push(data.settings[option]); } } } else if ($.fn.nutritionLabel.defaultSettings[option] !== undefined) { //set the option and create the nutrition label //this is a special case so the single value setting will still work even if the label hasn't been initiated yet if (settings !== undefined) { $.fn.nutritionLabel.defaultSettings[option] = settings; init(null, $(this)); } } }); //return the value of a setting if (values.length === 1) { return values[0]; } //return the setting values or the elements return values.length > 0 ? values : elements; } //end of => else if (typeof option === 'string' && option !== '') } else if (typeof option === 'undefined' || option === '') { //if no value / option is supplied, simply create the label using the default values init(settings, $(this)); } };//end of => $.fn.nutritionLabel = function(option, settings) $.fn.nutritionLabel.defaultSettings = { //default fixedWidth of the nutrition label width: 280, //to allow custom width - usually needed for mobile sites allowCustomWidth: false, widthCustom: 'auto', //to allow the label to have no border allowNoBorder: false, //to enable rounding of the nutritional values based on the FDA rounding rules http://goo.gl/RMD2O allowFDARounding: false, //to enabled the google analytics event logging allowGoogleAnalyticsEventLog: false, gooleAnalyticsFunctionName: 'ga', //enable triggering of user function on quantity change: global function name userFunctionNameOnQuantityChange: null, //enable triggering of user function on quantity change: handler instance userFunctionOnQuantityChange: null, //when set to true, this will hide the values if they are not applicable hideNotApplicableValues: false, //when set to true, this will hide all the percent daily values hidePercentDailyValues: false, //the brand name of the item for this label (eg. just salad) brandName: 'Brand where this item belongs to', //to scroll the ingredients if the innerheight is > scrollHeightComparison scrollLongIngredients: false, scrollHeightComparison: 100, //the height in px of the ingredients div scrollHeightPixel: 95, //this is to set how many decimal places will be shown on the nutrition values (calories, fat, protein, vitamin a, iron, etc) decimalPlacesForNutrition: 1, //this is to set how many decimal places will be shown for the "% daily values*" decimalPlacesForDailyValues: 0, //this is to set how many decimal places will be shown for the serving unit quantity textbox decimalPlacesForQuantityTextbox: 1, //to scroll the item name if the jQuery.height() is > scrollLongItemNamePixel scrollLongItemName: true, scrollLongItemNamePixel: 36, //this is needed to fix some issues on the 2018 label as the layout of the label is very different than the legacy one scrollLongItemNamePixel2018Override: 34, //this will only affect Vitamin D (valueVitaminD), Calcium (valueCalcium), Iron (valueIron) and Potassium (valuePotassium_2018). //this will tell the system to process these values as base values and not % Daily Values (default) useBaseValueFor2018LabelAndNotDVPercentage: false, //this is for the 2018 version //show the customizable link at the bottom showBottomLink: false, //url for the customizable link at the bottom urlBottomLink: 'http://www.nutritionix.com', //link name for the customizable link at the bottom nameBottomLink: 'Nutritionix', //this value can be changed and the value of the nutritions will be affected directly //the computation is "current nutrition value" * "serving unit quantity value" = "final nutrition value" //this can't be less than zero, all values less than zero is converted to zero //the textbox to change this value is visible / enabled by default //if the initial value of the serving size unit quantity is less than or equal to zero, it is converted to 1.0 //when enabled, user can change this value by clicking the arrow or changing the value on the textbox and pressing enter. //the value on the label will be updated automatically //different scenarios and the result if this feature is enabled //NOTE 1: [ ] => means a textbox will be shown //NOTE 2: on all cases below showServingUnitQuantityTextbox == true AND showServingUnitQuantity == true //if showServingUnitQuantity == false, the values that should be on the 'serving size div' are empty or null //CASE 1a: valueServingSizeUnit != '' (AND NOT null) && valueServingUnitQuantity >= 0 //RESULT: textServingSize [valueServingUnitQuantity] valueServingSizeUnit //NOTE 3: on all cases below showServingUnitQuantityTextbox == true AND showItemName == true //if showItemName == false, the values that should be on the 'item name div' are empty or null //CASE 1b: valueServingSizeUnit != '' (AND NOT null) && valueServingUnitQuantity <= 0 //RESULT: [valueServingUnitQuantity default to 1.0] itemName //CASE 3a: valueServingSizeUnit == '' (OR null) && valueServingUnitQuantity > 0 //RESULT: [valueServingUnitQuantity] itemName //CASE 3b: valueServingSizeUnit == '' (OR null) && valueServingUnitQuantity <= 0 //RESULT: [valueServingUnitQuantity default to 1.0] itemName //NOTE 4: to see the different resulting labels, check the html/demo-texbox-case*.html files valueServingUnitQuantity: 1.0, valueServingSizeUnit: '', showServingUnitQuantityTextbox: true, //the name of the item for this label (eg. cheese burger or mayonnaise) itemName: 'Item / Ingredient Name', showServingUnitQuantity: true, //allow hiding of the textbox arrows hideTextboxArrows: false, //this is for the 2018 version and will only work when showServingUnitQuantity is false. see https://github.com/nutritionix/nutrition-label/issues/123 showOnlyTheTextServingSize: false, //these 2 settings are used internally. //this is just added here instead of a global variable to prevent a bug when there are multiple instances of the plugin like on the demo pages originalServingUnitQuantity: 0, //this is used to fix the computation issue on the textbox nutritionValueMultiplier: 1, //this is used for the computation of the servings per container totalContainerQuantity: 1, //default calorie intake calorieIntake: 2000, //these are the recommended daily intake values dailyValueTotalFat: 65, //this should be 70 for the uk version dailyValueSatFat: 20, dailyValueCholesterol: 300, dailyValueSodium: 2400, dailyValuePotassium: 3500, dailyValuePotassium_2018: 4700, //this is for the 2018 version dailyValueCarb: 300, //this should be 260 for the uk version dailyValueFiber: 25, dailyValueCalcium: 1300, //this is for the 2018 version dailyValueIron: 18, dailyValueVitaminD: 20, dailyValueAddedSugar: 50, dailyValueSugar: 100, //this should be 90 for the uk version dailyValueEnergyKcal: 2000, //this is for the uk version dailyValueProtein: 50, //this is for the uk version dailyValueSalt: 6, //this is for the uk version //these values can be change to hide some nutrition values showCalories: true, showFatCalories: true, showTotalFat: true, showSatFat: true, showTransFat: true, showPolyFat: false, showMonoFat: false, showCholesterol: true, showSodium: true, showPotassium: false, //this is for the legacy version showPotassium_2018: true, //this is for the 2018 version showTotalCarb: true, showFibers: true, showSugars: true, showAddedSugars: true, showSugarAlcohol: false, //this is for the 2018 version showProteins: true, showVitaminA: true, showVitaminC: true, showVitaminD: true, //this is for the 2018 version showCalcium: true, //this is for the 2018 version showIron: true, showCaffeine: true, //this is for the 2018 version showServingWeightGrams: true, //these values can be change to hide some nutrition daily values //take note that the setting 'hidePercentDailyValues' override these values showDailyTotalFat: true, showDailySatFat: true, showDailyCholesterol: true, showDailySodium: true, showDailyPotassium: true, //this is for the legacy version showDailyPotassium_2018: true, //this is for the 2018 version showDailyTotalCarb: true, showDailyFibers: true, showDailySugars: false, showDailyAddedSugars: true, //this is for the 2018 version showDailyVitaminD: true, //this is for the 2018 version showDailyCalcium: true, //this is for the 2018 version showDailyIron: true, showDailyProtein: true, //this is for the uk version showDailyEnergy: true, //this is for the uk version //to show the 'amount per serving' text showAmountPerServing: true, //to show the 'servings per container' data and replace the default 'Serving Size' value (without unit and servings per container text and value) showServingsPerContainer: false, //to show the item name. there are special cases where the item name is replaced with 'servings per container' value showItemName: true, //to show the item name for the UK version. the main difference with the previous setting is this feature is set to false by default. the UK version will ignore the showItemName setting showItemNameForUK: false, //show the brand where this item belongs to showBrandName: false, //to show the ingredients value or not showIngredients: true, //to show the calorie diet info at the bottom of the label showCalorieDiet: false, //to show the customizable footer which can contain html and js codes showCustomFooter: false, //see https://github.com/nutritionix/nutrition-label/issues/93 and then https://github.com/nutritionix/nutrition-label/issues/104 indentSugarAndRemoveBoldStyleFor2018Label: true, boldCaffeine: true, //this is for the 2018 version //to show the disclaimer text or not showDisclaimer: false, //the height in px of the disclaimer div scrollDisclaimerHeightComparison: 100, scrollDisclaimer: 95, valueDisclaimer: 'Please note that these nutrition values are estimated based on our standard serving portions. ' + 'As food servings may have a slight variance each time you visit, please expect these values to be with in 10% +/- of your actual meal. ' + 'If you have any questions about our nutrition calculator, please contact Nutritionix.', ingredientLabel: 'INGREDIENTS:', valueCustomFooter: '', //the are to set some values as 'not applicable'. this means that the nutrition label will appear but the value will be a 'gray dash' naCalories: false, naFatCalories: false, naTotalFat: false, naSatFat: false, naTransFat: false, naPolyFat: false, naMonoFat: false, naCholesterol: false, naSodium: false, naPotassium: false, //this is for the legacy version naPotassium_2018: false, //this is for the 2018 version naTotalCarb: false, naFibers: false, naSugars: false, naAddedSugars: false, //this is for the 2018 version naSugarAlcohol: false, naProteins: false, naVitaminA: false, naVitaminC: false, naVitaminD: false, //this is for the 2018 version naCalcium: false, //this is for the 2018 version naIron: false, naCaffeine: false, //this is for the 2018 version //these are the default values for the nutrition info valueServingWeightGrams: 0, valueServingPerContainer: 1, valueCalories: 0, valueFatCalories: 0, valueTotalFat: 0, valueSatFat: 0, valueTransFat: 0, valuePolyFat: 0, valueMonoFat: 0, valueCholesterol: 0, valueSodium: 0, valuePotassium: 0, //this is for the legacy version valuePotassium_2018: 0, //this is for the 2018 version valueTotalCarb: 0, valueFibers: 0, valueSugars: 0, valueAddedSugars: 0, //this is for the 2018 version valueSugarAlcohol: 0, valueProteins: 0, valueVitaminA: 0, valueVitaminC: 0, valueVitaminD: 0, //this is for the 2018 version valueCalcium: 0, //this is for the 2018 version valueIron: 0, valueCaffeine: 0, //this is for the 2018 version //customizable units for the values unitCalories: '', unitFatCalories: '', unitTotalFat: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitSatFat: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitTransFat: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitPolyFat: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitMonoFat: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitCholesterol: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', unitSodium: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', unitPotassium: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', //this is for the legacy version unitPotassium_base: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', //this is for the 2018 version unitPotassium_percent: '%', //this is for the 2018 version unitTotalCarb: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitFibers: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitSugars: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitAddedSugars: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', //this is for the 2018 version unitSugarAlcohol: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitProteins: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', unitVitaminA: '%', unitVitaminC: '%', unitVitaminD_base: '<span aria-hidden="true">mcg</span><span class="sr-only"> micrograms</span>', //this is for the 2018 version unitVitaminD_percent: '%', //this is for the 2018 version unitCalcium: '%', //this is for the 2018 version unitCalcium_base: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', //this is for the 2018 version unitCalcium_percent: '%', //this is for the 2018 version unitIron: '%', unitIron_base: '<span aria-hidden="true">mg</span><span class="sr-only"> milligrams</span>', //this is for the 2018 version unitIron_percent: '%', //this is for the 2018 version unitServingWeight: '<span aria-hidden="true">g</span><span class="sr-only"> grams</span>', //this is for the 2018 version unitEnergy_kj: 'kj', //this is for the uk version unitEnergy_kcal: 'kcal', //this is for the uk version unitSalt: 'g', //this is for the uk version unitGramOrMlForThePer100Part: 'g', //this is for the uk version unitCaffeine: 'mg', //this is for the 2018 version //these are the values for the optional calorie diet valueCol1CalorieDiet: 2000, valueCol2CalorieDiet: 2500, valueCol1DietaryTotalFat: 0, valueCol2DietaryTotalFat: 0, valueCol1DietarySatFat: 0, valueCol2DietarySatFat: 0, valueCol1DietaryCholesterol: 0, valueCol2DietaryCholesterol: 0, valueCol1DietarySodium: 0, valueCol2DietarySodium: 0, valueCol1DietaryPotassium: 0, valueCol2DietaryPotassium: 0, valueCol1DietaryTotalCarb: 0, valueCol2DietaryTotalCarb: 0, valueCol1Dietary: 0, valueCol2Dietary: 0, //these text settings is so you can create nutrition labels in different languages or to simply change them to your need textNutritionFacts: 'Nutrition Facts', textDailyValues: 'Daily Value', textServingSize: 'Serving Size:', textServingsPerContainer: 'Servings Per Container', textAmountPerServing: 'Amount Per Serving', textCalories: 'Calories', textFatCalories: 'Calories from Fat', textTotalFat: 'Total Fat', textSatFat: 'Saturated Fat', textTransFat: '<em>Trans</em> Fat', textPolyFat: 'Polyunsaturated Fat', textMonoFat: 'Monounsaturated Fat', textCholesterol: 'Cholesterol', textSodium: 'Sodium', textPotassium: 'Potassium', textTotalCarb: 'Total Carbohydrates', textFibers: 'Dietary Fiber', textSugars: 'Sugars', textAddedSugars1: 'Includes ', textAddedSugars2: ' Added Sugars', //this is for the 2018 version textSugarAlcohol: 'Sugar Alcohol', textProteins: 'Protein', textVitaminA: 'Vitamin A', textVitaminC: 'Vitamin C', textVitaminD: 'Vitamin D', //this is for the 2018 version textCalcium: 'Calcium', //this is for the 2018 version textIron: 'Iron', textNotApplicable: '-', ingredientList: 'None', textPercentDailyPart1: 'Percent Daily Values are based on a', textPercentDailyPart2: 'calorie diet', textPercentDaily2018VersionPart1: 'The % Daily Value (DV) tells you how much a nutrient in a serving of food contributes to a daily diet. ', textPercentDaily2018VersionPart2: ' calories a day is used for general nutrition advice.', textGoogleAnalyticsEventCategory: 'Nutrition Label', textGoogleAnalyticsEventActionUpArrow: 'Quantity Up Arrow Clicked', textGoogleAnalyticsEventActionDownArrow: 'Quantity Down Arrow Clicked', textGoogleAnalyticsEventActionTextbox: 'Quantity Textbox Changed', textUKTypicalValues: 'Typical Values', //this is for the uk version textUKReferenceIntake: 'Reference intake of an average adult', //this is for the uk version textUKPer100: 'Per', //this is for the uk version textUKDefaultServingNameIfEmpty: 'Serving', //this is for the uk version textDataNotAvailable: 'Data not available', textAriaLabelIncreaseQuantityArrow: 'Increase the Quantity Arrow', textAriaLabelDecreaseQuantityArrow: 'Decrease the Quantity Arrow', textAriaLabelChangeQuantityTextbox: 'Change the Quantity Textbox', textCalorieDietHtmlLegacyLessThan: 'Less than', textCalorieDietHtmlLegacyDietary: 'Dietary', textCaffeine: 'Caffeine', //this is for the 2018 version //if the showLegacyVersion is true, the system will show the legacy version //if both the showLegacyVersion and showUKVersion are false, the system will show the 2018 version //if both the showLegacyVersion is false and showUKVersion is true, the system will show the uk version showLegacyVersion: true, showUKVersion: false, //for the uk label, If the serving name is empty, have it default to "Serving" convertEmptyServingNametoServingForUKLabel: true, //more details here https://github.com/nutritionix/nutrition-label/issues/77#issuecomment-323510972 legacyVersion: 1 };//end of => $.fn.nutritionLabel.defaultSettings //this will store the unique individual properties for each instance of the plugin function NutritionLabel(settings, $elem) { this.nutritionLabel = null; this.settings = settings; this.$elem = $elem; return this; } function cleanSettings(settings) { let numericIndex = [ 'calorieIntake', 'dailyValueCalcium', 'dailyValueCarb', 'dailyValueCholesterol', 'dailyValueEnergyKcal', 'dailyValueFiber', 'dailyValueIron', 'dailyValuePotassium', 'dailyValueProtein', 'dailyValueSalt', 'dailyValueSatFat', 'dailyValueSodium', 'dailyValueSugar', 'dailyValueTotalFat', 'dailyValueVitaminD', 'decimalPlacesForDailyValues', 'decimalPlacesForNutrition', 'decimalPlacesForQuantityTextbox', 'scrollHeightComparison', 'scrollHeightPixel', 'scrollLongItemNamePixel', 'scrollLongItemNamePixel2018Override', 'valueAddedSugars', 'valueCaffeine', 'valueCalcium', 'valueCalories', 'valueCholesterol', 'valueCol1CalorieDiet', 'valueCol1Dietary', 'valueCol1DietaryCholesterol', 'valueCol1DietaryPotassium', 'valueCol1DietarySatFat', 'valueCol1DietarySodium', 'valueCol1DietaryTotalCarb', 'valueCol1DietaryTotalFat', 'valueCol2CalorieDiet', 'valueCol2Dietary', 'valueCol2DietaryCholesterol', 'valueCol2DietaryPotassium', 'valueCol2DietarySatFat', 'valueCol2DietarySodium', 'valueCol2DietaryTotalCarb', 'valueCol2DietaryTotalFat', 'valueFatCalories', 'valueFibers', 'valueIron', 'valueMonoFat', 'valuePolyFat', 'valuePotassium', 'valueProteins', 'valueSatFat', 'valueServingPerContainer', 'valueServingSize', 'valueServingUnitQuantity', 'valueServingWeightGrams', 'valueSodium', 'valueSugarAlcohol', 'valueSugars', 'valueTotalCarb', 'valueTotalFat', 'valueTransFat', 'valueVitaminA', 'valueVitaminC', 'valueVitaminD', 'width' ]; $.each(settings, function(index, value) { if (jQuery.inArray(index, numericIndex) !== -1) { settings[index] = parseFloat(settings[index]); if (isNaN(settings[index]) || settings[index] === undefined) { settings[index] = 0; } } }); if (settings['valueServingUnitQuantity'] < 0) { settings['valueServingUnitQuantity'] = 0; } return settings; }//end of => function cleanSettings(settings) function updateNutritionValueWithMultiplier(settings) { let nutritionIndex = [ 'valueAddedSugars', 'valueCaffeine', 'valueCalcium', 'valueCalories', 'valueCholesterol', 'valueFatCalories', 'valueFibers', 'valueIron', 'valueMonoFat', 'valuePolyFat', 'valuePotassium', 'valuePotassium_2018', 'valueProteins', 'valueSatFat', 'valueServingWeightGrams', 'valueSodium', 'valueSugarAlcohol', 'valueSugars', 'valueTotalCarb', 'valueTotalFat', 'valueTransFat', 'valueVitaminA', 'valueVitaminC', 'valueVitaminD' ]; $.each(settings, function(index, value) { if (jQuery.inArray(index, nutritionIndex) !== -1) { settings[index] = parseFloat(settings[index]); if (isNaN(settings[index]) || settings[index] === undefined) { settings[index] = 0; } settings[index] = parseFloat(settings[index]) * parseFloat(settings['valueServingUnitQuantity']) * parseFloat(settings['nutritionValueMultiplier']); } }); if (parseFloat(settings['valueServingUnitQuantity']) == 0) { settings['valueServingPerContainer'] = 0; } else if (!isNaN(settings['valueServingPerContainer']) && settings['valueServingPerContainer'] != undefined) { settings['valueServingPerContainer'] = parseFloat(settings.totalContainerQuantity) / parseFloat(settings['valueServingUnitQuantity']); } return settings; }//end of => function updateNutritionValueWithMultiplier(settings) function init(settings, $elem) { //merge the default settins with the user supplied settings let $settings = $.extend({}, $.fn.nutritionLabel.defaultSettings, settings || {}); let $originalCleanSettings = cleanSettings($.extend({}, $.fn.nutritionLabel.defaultSettings, settings || {})); $settings.totalContainerQuantity = parseFloat($settings.valueServingPerContainer) * parseFloat($settings['valueServingUnitQuantity']); $originalCleanSettings.totalContainerQuantity = parseFloat($originalCleanSettings.valueServingPerContainer) * parseFloat($originalCleanSettings['valueServingUnitQuantity']); //clean the settings and make sure that all numeric settings are really numeric, if not, force them to be $settings = cleanSettings($settings); $originalCleanSettings = cleanSettings($originalCleanSettings); $settings.nutritionValueMultiplier = $settings.valueServingUnitQuantity <= 0 ? 1 : 1 / $settings.valueServingUnitQuantity; //update the nutrition values with the multiplier let $updatedsettings = updateNutritionValueWithMultiplier($settings); $settings.originalServingUnitQuantity = $updatedsettings.valueServingUnitQuantity; //if the original value is <= 0, set it to 1.0 if ($updatedsettings.valueServingUnitQuantity <= 0) { $originalCleanSettings.valueServingUnitQuantity = 1; $updatedsettings = updateNutritionValueWithMultiplier($originalCleanSettings); $updatedsettings.valueServingUnitQuantity = 1; } //initalize the nutrition label and create / recreate it var nutritionLabel = new NutritionLabel($updatedsettings, $elem); if ($updatedsettings.showLegacyVersion) { //($localSettings nutritionLabel $elem forLegacyLabel forInitialization forUKLabel) updateValuesAfterAQuantityChanged($settings, nutritionLabel, $elem, true, true, false); //if the text box for the unit quantity is shown if ($settings.showServingUnitQuantityTextbox) { //if the arrows are not hidden if (!$settings.hideTextboxArrows) { //increase the unit quantity by clicking the up arrow $('#' + $elem.attr('id')).on('click', '.unitQuantityUp', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), 1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, true, false); }); //decrease the unit quantity by clicking the down arrow $('#' + $elem.attr('id')).on('click', '.unitQuantityDown', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), -1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, true, false); }); } //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('change', '.unitQuantityBox', function(e) { e.preventDefault(); //($thisTextbox $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, true, false); }); //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('keydown', '.unitQuantityBox', function(e) { if (e.keyCode == 13) { e.preventDefault(); //($thisTextbox $localSettings, nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, true, false); } }); } //end of => if ($updatedsettings.showLegacyVersion) } else if ($updatedsettings.showUKVersion) { //this part is for the uk version //($localSettings nutritionLabel $elem forLegacyLabel forInitialization forUKLabel) updateValuesAfterAQuantityChanged($settings, nutritionLabel, $elem, false, true, true); //if the text box for the unit quantity is shown if ($settings.showServingUnitQuantityTextbox) { //if the arrows are not hidden if (!$settings.hideTextboxArrows) { //increase the unit quantity by clicking the up arrow $('#' + $elem.attr('id')).on('click', '.uk_nf-unitQuantityUp', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), 1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, true); }); //decrease the unit quantity by clicking the down arrow $('#' + $elem.attr('id')).on('click', '.uk_nf-unitQuantityDown', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), -1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, true); }); } //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('change', '.uk_nf-unitQuantityBox', function(e) { e.preventDefault(); //($thisTextbox $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, true); }); //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('keydown', '.uk_nf-unitQuantityBox', function(e) { if (e.keyCode == 13) { e.preventDefault(); //($thisTextbox $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, true); } }); } //end of => else if ($updatedsettings.showUKVersion) } else { //this part is for the 2018 version //($localSettings nutritionLabel $elem forLegacyLabel forInitialization forUKLabel) updateValuesAfterAQuantityChanged($settings, nutritionLabel, $elem, false, true, false); //if the text box for the unit quantity is shown if ($settings.showServingUnitQuantityTextbox) { //if the arrows are not hidden if (!$settings.hideTextboxArrows) { //increase the unit quantity by clicking the up arrow $('#' + $elem.attr('id')).on('click', 'div.nf-unitQuantityUp', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), 1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, false); }); //decrease the unit quantity by clicking the down arrow $('#' + $elem.attr('id')).on('click', 'div.nf-unitQuantityDown', function(e) { e.preventDefault(); //($thisQuantity changeValueBy $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityByArrow($(this), -1, updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, false); }); } //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('change', '.nf-unitQuantityBox', function(e) { e.preventDefault(); //($thisTextbox $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, false); }); //the textbox unit quantity value is changed $('#' + $elem.attr('id')).on('keydown', '.nf-unitQuantityBox', function(e) { if (e.keyCode == 13) { e.preventDefault(); //($thisTextbox $localSettings nutritionLabel $elem forLegacyLabel forUKLabel) changeQuantityTextbox($(this), updateTheSettingsAfterAnEvent($settings, settings), nutritionLabel, $elem, false, false); } }); } }//end of => else => if ($updatedsettings.showLegacyVersion) => else if ($updatedsettings.showUKVersion) //store the object for later reference $elem.data('_nutritionLabel', nutritionLabel); }//end of => function init(settings, $elem) function updateTheSettingsAfterAnEvent($localSettings, localSettings) { let $localSettingsHolder = cleanSettings($.extend({}, $.fn.nutritionLabel.defaultSettings, localSettings || {})); $localSettingsHolder.originalServingUnitQuantity = $localSettings.originalServingUnitQuantity; $localSettingsHolder.totalContainerQuantity = $localSettings.totalContainerQuantity; $localSettingsHolder.nutritionValueMultiplier = $localSettingsHolder.valueServingUnitQuantity <= 0 ? 1 : 1 / $localSettingsHolder.valueServingUnitQuantity; return $localSettingsHolder; } function addScrollToItemDiv($localElem, $localSettings, localNameClass, forLegacyLabel) { let local_scrollLongItemNamePixel = parseInt($localSettings.scrollLongItemNamePixel); if (!forLegacyLabel) { local_scrollLongItemNamePixel = parseInt($localSettings.scrollLongItemNamePixel2018Override); } //as of 05/14/2017 inline class only appears on the legacy version if ($('#' + $localElem.attr('id') + ' .' + localNameClass + '.inline').val() != undefined) { if ($('#' + $localElem.attr('id') + ' .' + localNameClass + '.inline').height() > local_scrollLongItemNamePixel + 1) { $('#' + $localElem.attr('id') + ' .' + localNameClass + '.inline').css({ 'margin-left': '3.90em', 'height': local_scrollLongItemNamePixel + 'px', 'overflow-y': 'auto' }); } } else { if (forLegacyLabel) { if ($('#' + $localElem.attr('id') + ' .' + localNameClass).height() > local_scrollLongItemNamePixel + 1) { $('#' + $localElem.attr('id') + ' .' + localNameClass).css({ 'height': local_scrollLongItemNamePixel + 'px', 'overflow-y': 'auto' }); } } else { if ($('#' + $localElem.attr('id') + ' .' + localNameClass + ' div').height() >= local_scrollLongItemNamePixel + 1) { $('#' + $localElem.attr('id') + ' .' + localNameClass + ' div').css({ 'height': local_scrollLongItemNamePixel + 'px', 'overflow-y': 'auto' }); } } } } function notApplicableHover($localElem) { //this code is for pages with multiple nutrition labels generated by the plugin like the demo page if ($localElem.attr('id') !== undefined && $localElem.attr('id') !== '') { $('#' + $localElem.attr('id') + ' .notApplicable').on('mouseenter', function() { $('#' + $localElem.attr('id') + ' .naTooltip').css({ top: $(this).position().top + 'px', left: $(this).position().left + 10 + 'px' }).show(); }); $('#' + $localElem.attr('id') + ' .notApplicable').on('mouseleave', function() { $('#' + $localElem.attr('id') + ' .naTooltip').hide(); }); } else { $('#' + $localElem.attr('id') + ' .notApplicable').on('mouseenter', function() { $('.naTooltip').css({ top: $(this).position().top + 'px', left: $(this).position().left + 10 + 'px' }).show(); }); $('#' + $localElem.attr('id') + ' .notApplicable').on('mouseleave', function() { $('.naTooltip').hide(); }); } } function updateScrollingFeature($localElem, $localSettings, localIDToScroll, localScrollHeightComparison, localScrollHeight) { let $parentElement; if ($localElem.attr('id') !== undefined && $localElem.attr('id') !== '') { //this code is for pages with multiple nutrition labels generated by the plugin like the demo page $parentElement = $('#' + $localElem.attr('id') + ' #' + localIDToScroll).parent(); } else { $parentElement = $('#' + localIDToScroll).parent(); } if ($parentElement.innerHeight() > localScrollHeightComparison) { $parentElement.addClass('scroll').css({ height: localScrollHeight + 'px' }); } } function updateValuesAfterAQuantityChanged($localSettings, nutritionLabel, $elem, forLegacyLabel, forInitialization, forUKLabel) { let ingredientListID = 'ingredientList'; let calcDisclaimerTextID = 'calcDisclaimerText'; let nameElementClass = 'name'; if (!forLegacyLabel && !forUKLabel) { //for the 2018 label ingredientListID = 'nf-ingredientList'; calcDisclaimerTextID = 'nf-calcDisclaimerText'; nameElementClass = 'nf-item-name'; } else if (!forLegacyLabel && forUKLabel) { //for the uk label ingredientListID = 'uk_nf-ingredient-statement'; calcDisclaimerTextID = 'uk_nf-disclaimer'; } if (!forInitialization) { $localSettings = updateNutritionValueWithMultiplier($localSettings); nutritionLabel = new NutritionLabel($localSettings, $elem); } //if the showLegacyVersion is true, the system will show the legacy version //if both the showLegacyVersion and showUKVersion are false, the system will show the 2018 version //if both the showLegacyVersion is false and showUKVersion is true, the system will show the uk version if (forLegacyLabel) { $elem.html(nutritionLabel.generateLegacy()); } else if (forUKLabel) { $elem.html(nutritionLabel.generateUK()); } else { $elem.html(nutritionLabel.generate2018()); } //scroll the ingredients of the innerheight is > $localSettings.scrollHeightComparison and the settings showIngredients and scrollLongIngredients are true if ($localSettings.showIngredients && $localSettings.scrollLongIngredients) { //($localElem $localSettings localIDToScroll localScrollHeightComparison localScrollHeight) updateScrollingFeature($elem, $localSettings, ingredientListID, $localSettings.scrollHeightComparison, $localSettings.scrollHeightPixel); } //scroll the disclaimer if the height of the disclaimer div is greater than scrollDisclaimerHeightComparison if ($localSettings.showDisclaimer) { //($localElem $localSettings localIDToScroll localScrollHeightComparison localScrollHeight) updateScrollingFeature($elem, $localSettings, calcDisclaimerTextID, $localSettings.scrollDisclaimerHeightComparison, $localSettings.scrollDisclaimer); } //this code is for pages with multiple nutrition labels generated by the plugin like the demo page notApplicableHover($elem); //for the legacy and 2018 label if (!forUKLabel) { //add a scroll on long item names if ($localSettings.scrollLongItemName) { addScrollToItemDiv($elem, $localSettings, nameElementClass, forLegacyLabel); } } if (!forInitialization) { return $localSettings; } }//end of => function updateValuesAfterAQuantityChanged($localSettings, $elem, ingredientListID, calcDisclaimerTextID, forLegacyLabel, forInitialization, forUKLabel) function handleQuantityChange($localSettings, source, previousValue, newValue) { let handler; if ($localSettings.userFunctionOnQuantityChange) { handler = $localSettings.userFunctionOnQuantityChange; } else if ($localSettings.userFunctionNameOnQuantityChange) { handler = window[$localSettings.userFunctionNameOnQuantityChange]; } if (typeof handler === 'function') { handler(source, previousValue, newValue); } } function changeQuantityTextbox($thisTextbox, $localSettings, nutritionLabel, $elem, forLegacyLabel, forUKLabel) { let nixLabelBeforeQuantityID = 'nixLabelBeforeQuantity'; let previousValue; let textBoxValue; if (!forLegacyLabel & !forUKLabel) { nixLabelBeforeQuantityID = 'nf-nixLabelBeforeQuantity'; } else if (!forLegacyLabel & forUKLabel) { nixLabelBeforeQuantityID = 'uk_nf-nixLabelBeforeQuantity'; } previousValue = parseFloat($('#' + $elem.attr('id') + ' #' + nixLabelBeforeQuantityID).val()); textBoxValue = !regIsPosNumber($thisTextbox.val()) ? previousValue : parseFloat($thisTextbox.val()); $thisTextbox.val( roundLoDash(textBoxValue, $localSettings.decimalPlacesForQuantityTextbox) ); $localSettings.valueServingUnitQuantity = textBoxValue; //($localSettings nutritionLabel $elem forLegacyLabel forInitialization forUKLabel) $localSettings = updateValuesAfterAQuantityChanged($localSettings, nutritionLabel, $elem, forLegacyLabel, false, forUKLabel); if ($localSettings.allowGoogleAnalyticsEventLog) { window[$localSettings.gooleAnalyticsFunctionName]( 'send', 'event', $localSettings.textGoogleAnalyticsEventCategory, $localSettings.textGoogleAnalyticsEventActionTextbox ); } handleQuantityChange( $localSettings, 'textbox', roundLoDash(previousValue, $localSettings.decimalPlacesForQuantityTextbox), roundLoDash(textBoxValue, $localSettings.decimalPlacesForQuantityTextbox) ); }//end of => function changeQuantityTextbox($thisTextbox, $localSettings, nutritionLabel, $elem, forLegacyLabel, forUKLabel) function changeQuantityByArrow($thisQuantity, changeValueBy, $localSettings, nutritionLabel, $elem, forLegacyLabel, forUKLabel) { let unitQuantityBoxClass = 'unitQuantityBox'; if (!forLegacyLabel & !forUKLabel) { unitQuantityBoxClass = 'nf-unitQuantityBox'; } else if (!forLegacyLabel & forUKLabel) { unitQuantityBoxClass = 'uk_nf-unitQuantityBox'; } let currentQuantity = parseFloat($thisQuantity.parent().parent().find('input.' + unitQuantityBoxClass).val()); if (isNaN(currentQuantity)) { currentQuantity = 1.0; } let beforeCurrentQuantityWasChanged = currentQuantity; //see https://github.com/nutritionix/nutrition-label/issues/14 for an explanation on this part if (currentQuantity <= 1 && changeValueBy == -1) { changeValueBy = -0.5; currentQuantity += changeValueBy; } else if (currentQuantity < 1 && changeValueBy == 1) { changeValueBy = 0.5; currentQuantity += changeValueBy; } else if (currentQuantity <= 2 && currentQuantity > 1 && changeValueBy == -1) { currentQuantity = 1; } else { currentQuantity += changeValueBy; } if (currentQuantity < 0) { currentQuantity = 0; } $thisQuantity.parent().parent().find('input.' + unitQuantityBoxClass).val( roundLoDash(currentQuantity, $localSettings.decimalPlacesForQuantityTextbox) ); $localSettings.valueServingUnitQuantity = currentQuantity; //($localSettings nutritionLabel $elem forLegacyLabel forInitialization forUKLabel) $localSettings = updateValuesAfterAQuantityChanged($localSettings, nutritionLabel, $elem, forLegacyLabel, false, forUKLabel); if ($localSettings.allowGoogleAnalyticsEventLog) { if (changeValueBy > 0) { window[$localSettings.gooleAnalyticsFunctionName]( 'send', 'event', $localSettings.textGoogleAnalyticsEventCategory, $localSettings.textGoogleAnalyticsEventActionUpArrow ); } else { window[$localSettings.gooleAnalyticsFunctionName]( 'send', 'event', $localSettings.textGoogleAnalyticsEventCategory, $localSettings.textGoogleAnalyticsEventActionDownArrow ); } } handleQuantityChange( $localSettings, changeValueBy > 0 ? 'up arrow' : 'down arrow', beforeCurrentQuantityWasChanged, currentQuantity ); }//end of => function changeQuantityByArrow($thisQuantity, changeValueBy, $localSettings, nutritionLabel, $elem, forLegacyLabel, forUKLabel) //round the value to the nearest number function roundToNearestNum(input, nearest) { return nearest < 0 ? Math.round(input * nearest) / nearest : Math.round(input / nearest) * nearest; } function roundCalories(toRound, decimalPlace) { toRound = roundCaloriesRule(toRound); if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } return toRound; } function roundFat(toRound, decimalPlace) { toRound = roundFatRule(toRound); if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } return toRound; } function roundSodium(toRound, decimalPlace) { toRound = roundSodiumRule(toRound); if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } return toRound; } function roundPotassium(toRound, decimalPlace) { toRound = roundPotassiumRule(toRound); if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } return toRound; } function roundCholesterol(toRound, decimalPlace) { let normalVersion = true; let roundResult = roundCholesterolRule(toRound); if (roundResult === false) { normalVersion = false; } else { toRound = roundResult; } if (normalVersion) { if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } } else { toRound = '< 5'; } return toRound; } function roundCarbFiberSugarProtein(toRound, decimalPlace) { let normalVersion = true; let roundResult = roundCarbFiberSugarProteinRule(toRound); if (roundResult === false) { normalVersion = false; } else { toRound = roundResult; } if (normalVersion) { if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } } else { toRound = '< 1'; } return toRound; } function roundCaffeine(toRound, decimalPlace) { toRound = roundToNearestNum(toRound, 1); if (toRound > 0) { toRound = roundLoDash(toRound, decimalPlace); } return toRound; } //Calories and Calories from Fat rounding rule function roundCaloriesRule(toRound) { if (toRound < 5) { return 0; } else if (toRound <= 50) { //50 cal - express to nearest 5 cal increment return roundToNearestNum(toRound, 5); } //> 50 cal - express to nearest 10 cal increment return roundToNearestNum(toRound, 10); } //Total Fat, Saturated Fat, Polyunsaturated Fat and Monounsaturated Fat rounding rule function roundFatRule(toRound) { if (toRound < 0.5) { return 0; } else if (toRound < 5) { //< 5 g - express to nearest .5g increment return roundToNearestN