Created
October 3, 2011 14:07
-
-
Save jvanderhoof/1259171 to your computer and use it in GitHub Desktop.
Brewershub - Backbone JS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| _.templateSettings = { | |
| evaluate : /<\?([\s\S]+?)\?>/g, | |
| interpolate : /<\?=([\s\S]+?)\?>/g | |
| }; | |
| (function($) { | |
| ////// VIEWS /////// | |
| window.IngredientView = Backbone.View.extend({ | |
| tagName: 'li', | |
| events: { | |
| 'click .add': 'addIngredient' | |
| }, | |
| initialize: function() { | |
| _.bindAll(this, 'render'); | |
| this.initializeTemplate(); | |
| }, | |
| initializeTemplate: function() { | |
| this.template = _.template($(this.template).html()); | |
| }, | |
| render: function() { | |
| $(this.el).html(this.template(this.model.toJSON())); | |
| return this; | |
| } | |
| }); | |
| window.HopView = IngredientView.extend({ | |
| template: "#hop-template", | |
| className: 'hop', | |
| addIngredient: function() { | |
| window.recipe.hops.add(new RecipeHop({'hop': this.model, 'name': this.model.get('name')})); | |
| } | |
| }); | |
| window.YeastView = IngredientView.extend({ | |
| template: "#yeast-template", | |
| className: 'yeast', | |
| addIngredient: function() { | |
| window.recipe.yeasts.add(new RecipeYeast({'yeast': this.model, 'name': this.model.get('name')})); | |
| } | |
| }); | |
| window.FermentableView = IngredientView.extend({ | |
| template: "#fermentable-template", | |
| className: 'fermentable', | |
| addIngredient: function() { | |
| window.recipe.fermentables.add(new RecipeFermentable({'fermentable': this.model, 'name': this.model.get('name')})); | |
| } | |
| }); | |
| window.PantryView = Backbone.View.extend({ | |
| el: '#pantry', | |
| events: { | |
| 'click .toggle': 'toggleIngredients' | |
| }, | |
| toggleIngredients: function() { | |
| alert('start'); | |
| $(event.target).next().toggle(); | |
| alert('done'); | |
| }, | |
| initialize: function() { | |
| _.bindAll(this, 'render', 'toggleIngredients'); | |
| this.template = _.template($('#pantry-template').html()); | |
| this.model.hops.bind('reset', this.render); | |
| this.model.fermentables.bind('reset', this.render); | |
| this.model.yeasts.bind('reset', this.render); | |
| }, | |
| render: function() { | |
| console.log('render pantry'); | |
| $(this.el).html(this.template({})); | |
| this.model.hops.each(function(hop) { | |
| var view = new HopView({model: hop}); | |
| this.$("."+hop.get('sub_type').replace(' / ', '_').toLowerCase()).append(view.render().el); | |
| }); | |
| this.model.yeasts.each(function(yeast) { | |
| var view = new YeastView({model: yeast}); | |
| this.$("."+yeast.get('sub_type').toLowerCase()).append(view.render().el); | |
| }); | |
| this.model.fermentables.each(function(fermentable) { | |
| var view = new FermentableView({model: fermentable}); | |
| this.$("."+fermentable.get('sub_type').toLowerCase()).append(view.render().el); | |
| }); | |
| return this; | |
| } | |
| }); | |
| window.RecipeView = Backbone.View.extend({ | |
| el: '#recipe', | |
| events: { | |
| 'change .action_input': 'setAttributes' | |
| }, | |
| initialize: function() { | |
| _.bindAll(this, 'render'); | |
| this.model.fermentables.bind('add', this.render, this); | |
| this.model.yeasts.bind('add', this.render, this); | |
| this.model.hops.bind('add', this.render, this); | |
| this.template = _.template($('#recipe-template').html()); | |
| }, | |
| setAttributes: function(){ | |
| this.model.set({ | |
| 'batch_size': to_number(this.$('#batch_size').val()), | |
| 'boil_size': to_number(this.$('#boil_size').val()), | |
| 'efficiency': to_number(this.$('#efficiency').val()), | |
| 'mash_time': to_number(this.$('#mash_time').val()), | |
| 'mash_temp': to_number(this.$('#mash_temp').val()), | |
| 'mash_thickness': to_number(this.$('#mash_thickness').val()) | |
| }); | |
| this.model.recalculate(); | |
| }, | |
| render: function() { | |
| $(this.el).html(this.template({model: this.model})); | |
| this.pantryView = new PantryView({model: this.options.pantry}); | |
| this.model.fermentables.each(function(fermentable){ | |
| var view = new RecipeFermentableView({model: fermentable}); | |
| this.$('.fermentables').append(view.render().el); | |
| }); | |
| this.model.hops.each(function(hop){ | |
| var view = new RecipeHopView({model: hop}); | |
| this.$('.hops').append(view.render().el); | |
| }); | |
| this.model.yeasts.each(function(yeast){ | |
| var view = new RecipeYeastView({model: yeast}); | |
| this.$('.yeasts').append(view.render().el); | |
| }); | |
| return this; | |
| } | |
| }); | |
| window.RecipeIngredientView = Backbone.View.extend({ | |
| tagName: 'li', | |
| events: { | |
| 'change .action_input': 'setAttributes', | |
| 'click .remove': 'removeIngredient' | |
| }, | |
| initialize: function() { | |
| _.bindAll(this, 'render', 'removeIngredient'); | |
| this.initializeTemplate(); | |
| }, | |
| initializeTemplate: function() { | |
| this.template = _.template($(this.template).html()); | |
| }, | |
| render: function() { | |
| $(this.el).html(this.template(this.model.toJSON())); | |
| $(this.el).find('input').first().focus(); | |
| return this; | |
| }, | |
| setAttributes: function(){ | |
| var ozs = to_number(this.$('.ozs').val()); | |
| this.model.set({amount_ozs: ozs}); | |
| window.recipe.recalculate(); | |
| } | |
| }); | |
| window.RecipeFermentableView = window.RecipeIngredientView.extend({ | |
| template: "#recipe-fermentable-template", | |
| removeIngredient: function(){ | |
| window.recipe.fermentables.remove(this.model); | |
| window.recipe.recalculate(); | |
| }, | |
| setAttributes: function(){ | |
| var lbs = to_number(this.$('.lbs').val()); | |
| var ozs = to_number(this.$('.ozs').val()); | |
| this.model.set({amount_lbs: lbs, amount_ozs: ozs}); | |
| window.recipe.recalculate(); | |
| } | |
| }); | |
| window.RecipeHopView = window.RecipeIngredientView.extend({ | |
| template: "#recipe-hop-template", | |
| removeIngredient: function(){ | |
| window.recipe.hops.remove(this.model); | |
| window.recipe.recalculate(); | |
| }, | |
| setAttributes: function(){ | |
| var time = to_number(this.$('.time').val()); | |
| var ozs = to_number(this.$('.ozs').val()); | |
| var form = this.$('.form').val(); | |
| var phase = this.$('.phase').val(); | |
| this.model.set({'amount_time': time, 'amount_ozs': ozs, 'form': form, 'phase': phase}); | |
| window.recipe.recalculate(); | |
| } | |
| }); | |
| window.RecipeYeastView = window.RecipeIngredientView.extend({ | |
| template: "#recipe-yeast-template", | |
| removeIngredient: function(){ | |
| window.recipe.yeasts.remove(this.model); | |
| window.recipe.recalculate(); | |
| } | |
| }); | |
| ////// MODELS /////// | |
| window.Ingredient = Backbone.RelationalModel.extend({}); | |
| window.Yeast = Ingredient.extend({}); | |
| window.Fermentable = Ingredient.extend({}); | |
| window.Hop = Ingredient.extend({}); | |
| window.Pantry = Backbone.Model.extend({ | |
| initialize: function() { | |
| this.hops = new Hops(); | |
| this.hops.fetch(); | |
| this.fermentables = new Fermentables(); | |
| this.fermentables.fetch(); | |
| this.yeasts = new Yeasts(); | |
| this.yeasts.fetch(); | |
| } | |
| }); | |
| window.RecipeIngredient = Backbone.RelationalModel.extend({ | |
| defaults: { | |
| 'amount_ozs': 0, | |
| 'amount_lbs': 0, | |
| 'amount_gms': 0, | |
| 'amount_time': 30 | |
| }, | |
| }); | |
| window.RecipeFermentable = RecipeIngredient.extend({ | |
| relations: [{ | |
| type: Backbone.HasOne, | |
| key: 'fermentable', | |
| relatedModel: 'Fermentable' | |
| }] | |
| }); | |
| window.RecipeHop = RecipeIngredient.extend({ | |
| relations: [{ | |
| type: Backbone.HasOne, | |
| key: 'hop', | |
| relatedModel: 'Hop' | |
| }], | |
| defaults: { | |
| 'amount_ozs': 0, | |
| 'amount_gms': 0, | |
| 'amount_time': 30, | |
| 'form': 'pellet', | |
| 'phase': 'boil' | |
| } | |
| }); | |
| window.RecipeYeast = RecipeIngredient.extend({ | |
| type: Backbone.HasOne, | |
| key: 'yeast', | |
| relatedModel: 'Yeast' | |
| }); | |
| window.Recipe = Backbone.RelationalModel.extend({ | |
| relations: [{ | |
| type: Backbone.HasMany, | |
| key: 'fermentables', | |
| relatedModel: 'RecipeFermentable', | |
| collectionType: 'RecipeFermentables' | |
| }, { | |
| type: Backbone.HasMany, | |
| key: 'hops', | |
| relatedModel: 'RecipeHop', | |
| collectionType: RecipeHops | |
| }, { | |
| type: Backbone.HasMany, | |
| key: 'yeasts', | |
| relatedModel: 'RecipeYeast', | |
| collectionType: 'RecipeYeasts' | |
| }], | |
| defaults: { | |
| 'title': '', | |
| 'batch_size': 5, | |
| 'boil_size': 3, | |
| 'efficiency': 70, | |
| 'mash_time': 60, | |
| 'mash_temp': 140, | |
| 'mash_thickness': 1.25, | |
| 'original_gravity': 1.0, | |
| 'final_gravity': 1.0, | |
| 'srm': 0, | |
| 'abw': 0, | |
| 'abv': 0, | |
| 'ibu': 0, | |
| 'mash': true | |
| }, | |
| initialize: function() { | |
| this.fermentables = new RecipeFermentables(); | |
| this.hops = new RecipeHops(); | |
| this.yeasts = new RecipeYeasts(); | |
| }, | |
| rational_tanh: function(x) { | |
| if (x < -3) { return -1; } else { if (x > 3){ return 1; } else { return x * ( 27 + x * x ) / ( 27 + 9 * x * x ); } } | |
| }, | |
| rager: function(boil_gravity) { | |
| var recipe = this; | |
| var ga = 0; | |
| if (boil_gravity > 1.05) { ga = (boil_gravity - 1.050) / 0.2; } | |
| var temp_ibu = 0; | |
| recipe.hops.each(function(hop){ | |
| if (hop.get('phase') == 'boil') { | |
| var amount = hop.get('amount_ozs'); | |
| var ingr = hop.get('hop'); | |
| var time = hop.get('amount_time'); | |
| var aau = ((ingr.get('aau_low') + ingr.get('aau_high')) / 2) / 100; | |
| var utilization = (18.11 + 13.86 * recipe.rational_tanh((time - 31.32) / 18.27)) / 100; | |
| if (hop.get('form') == 'pellet') { utilization *= 1.15; } | |
| temp_ibu += (amount * utilization * aau * 7462)/(recipe.get('batch_size') * (1 + ga)); | |
| } | |
| }); | |
| recipe.set({'ibu': Math.round(temp_ibu)}); | |
| }, | |
| recalculate: function() { | |
| var total_ppg = 0; | |
| var total_color = 0; | |
| var attenuation = 0; | |
| var recipe = this; | |
| this.fermentables.each(function(fermentable){ | |
| var amount = fermentable.get('amount_lbs') + fermentable.get('amount_ozs')/16; | |
| var ingr = fermentable.get('fermentable'); | |
| if (ingr.get('fully_fermentable')) { | |
| total_ppg += amount * ingr.get('typical_ppg'); | |
| } else { | |
| total_ppg += amount * ingr.get('typical_ppg') * recipe.get('efficiency')/100; | |
| } | |
| total_color += ingr.get('color') * amount/recipe.get('batch_size'); | |
| }); | |
| this.yeasts.each(function(yeast){ | |
| var amount = 1; //yeast.get('amount_ozs'); | |
| var ingr = yeast.get('yeast'); | |
| if (attenuation < ingr.get('attenuation')/100) { | |
| attenuation = ingr.get('attenuation')/100; | |
| } | |
| }); | |
| var og = (1 + Math.round(total_ppg/recipe.get('batch_size'))/1000); | |
| var boil_gravity = 1 + Math.round(total_ppg/recipe.get('boil_size'))/1000; | |
| var final_gravity = Math.round((og*1000 - 1000) - ((og*1000-1000)*attenuation)+1000)/1000; | |
| recipe.set({'original_gravity': og, | |
| 'final_gravity': final_gravity, | |
| 'srm': Math.round(1.4922 * Math.round(Math.pow(total_color,0.6859))) | |
| }); | |
| this.rager(boil_gravity); | |
| if (window.recipeView) { window.recipeView.render() }; | |
| } | |
| }); | |
| ////// COLLECTIONS /////// | |
| window.Ingredients = Backbone.Collection.extend({}); | |
| window.Hops = Ingredients.extend({ | |
| model: Hop, | |
| url: '/hops' | |
| }); | |
| window.Yeasts = Ingredients.extend({ | |
| model: Yeast, | |
| url: '/yeasts' | |
| }); | |
| window.Fermentables = Ingredients.extend({ | |
| model: Fermentable, | |
| url: '/fermentables' | |
| }); | |
| window.RecipeIngredients = Backbone.Collection.extend({}); | |
| window.RecipeFermentables = RecipeIngredients.extend({model: RecipeFermentable}); | |
| window.RecipeHops = RecipeIngredients.extend({model: RecipeHop}); | |
| window.RecipeYeasts = RecipeIngredients.extend({model: RecipeYeast}); | |
| ////// APP //////////// | |
| window.pantry = new Pantry(); | |
| window.recipe = new Recipe(); | |
| $(document).ready(function() { | |
| recipeView = new RecipeView({model: window.recipe, pantry: window.pantry}); | |
| $('#content').append(recipeView.render().el); | |
| }); | |
| })(jQuery); | |
| function to_number(value) { | |
| return (isNaN(value) ? 0 : parseFloat(value)); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment