<commit_before>(function(n, $){

    n.recipes = n.recipes || {}, n.recipes.builder = n.recipes.builder || {};
    ns = n.recipes.builder;
    ns.model = ns.model || {};

    ns.model.RecipeAddition = function(){

        var writeTimeoutInstance = null;
        this.editing = ko.observable(false);

        this.amount = ko.observable();
        this.unit = ko.observable();
        this.use = ko.observable();
        this.minutes = ko.observable();

        this.form = ko.observable();
        this.alpha_acid = ko.observable();

        this.ingredient = ko.observable();

        var write = $.proxy(function(value){
            clearTimeout(writeTimeoutInstance);
            var result = n.recipes.units.from_str(value), amount = result[0],
                unit = result[1];
            if(isNaN(amount)){
                amount = 0;
                unit = 'POUND';
            }
            this.amount(amount);
            this.unit(unit);
        }, this);

        this.readable_amount = ko.computed({
            read: function(){
                if(this.amount())
                    return n.recipes.units.to_str(this.amount(), this.unit());
            },
            write: write,
            owner: this
        });

        this.sortable_minutes = ko.computed(function(){
            if(this.use() == 'FIRST WORT')
                return 60 * 60;
            if(this.use() == 'POST BOIL' || this.use() == 'FLAME-OUT')
                return -1;
            return this.minutes();
        }, this);

        this.delayedWrite = $.proxy(function(obj, evt){
            clearTimeout(writeTimeoutInstance);
            var value = evt.currentTarget.value;
            writeTimeoutInstance = setTimeout(write, 1750, value);
        }, this);

        this.removeAddition = $.proxy(function(addition) {
            this.recipe.mash.additions.remove(addition);
            this.recipe.boil.additions.remove(addition);
            this.recipe.fermentation.additions.remove(addition);
        }, ns);

        this.mash_percentage = ko.computed({
            read: function(){
                var sum = 0.0;
                $.each(ns.recipe.mash.additions(), function(_, a){
                    var type = (a.ingredient().printed_type || '').toUpperCase();
                    if(type != 'GRAIN' && type != 'EXTRACT')
                        return;
                    sum += a.amount();
                });

                return ((this.amount() / sum) * 100).toFixed(1) + '%';
            },
            owner: this
        });

    };
<commit_msg>Avoid a bug in mash percentage calculation.<commit_after>(function(n, $){

    n.recipes = n.recipes || {}, n.recipes.builder = n.recipes.builder || {};
    ns = n.recipes.builder;
    ns.model = ns.model || {};

    ns.model.RecipeAddition = function(){

        var writeTimeoutInstance = null;
        this.editing = ko.observable(false);

        this.amount = ko.observable();
        this.unit = ko.observable();
        this.use = ko.observable();
        this.minutes = ko.observable();

        this.form = ko.observable();
        this.alpha_acid = ko.observable();

        this.ingredient = ko.observable();

        var write = $.proxy(function(value){
            clearTimeout(writeTimeoutInstance);
            var result = n.recipes.units.from_str(value), amount = result[0],
                unit = result[1];
            if(isNaN(amount)){
                amount = 0;
                unit = 'POUND';
            }
            this.amount(amount);
            this.unit(unit);
        }, this);

        this.readable_amount = ko.computed({
            read: function(){
                if(this.amount())
                    return n.recipes.units.to_str(this.amount(), this.unit());
            },
            write: write,
            owner: this
        });

        this.sortable_minutes = ko.computed(function(){
            if(this.use() == 'FIRST WORT')
                return 60 * 60;
            if(this.use() == 'POST BOIL' || this.use() == 'FLAME-OUT')
                return -1;
            return this.minutes();
        }, this);

        this.delayedWrite = $.proxy(function(obj, evt){
            clearTimeout(writeTimeoutInstance);
            var value = evt.currentTarget.value;
            writeTimeoutInstance = setTimeout(write, 1750, value);
        }, this);

        this.removeAddition = $.proxy(function(addition) {
            this.recipe.mash.additions.remove(addition);
            this.recipe.boil.additions.remove(addition);
            this.recipe.fermentation.additions.remove(addition);
        }, ns);

        this.mash_percentage = ko.computed({
            read: function(){
                var sum = 0.0;
                $.each(ns.recipe.mash.additions(), function(_, a){
                    var type = (a.ingredient().printed_type || '').toUpperCase();
                    if(type != 'GRAIN' && type != 'EXTRACT')
                        return;
                    sum += a.amount();
                });

                if(isNaN(this.amount() / sum))
                    return '0%'; // Avoid zero division

                return ((this.amount() / sum) * 100).toFixed(1) + '%';
            },
            owner: this
        });

    };

{'fraction_non_alphanumeric': 0.14429657794676806, 'fraction_numerical': 0.007034220532319392, 'mean_word_length': 1.5613437195715676, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4921822', 'n_tokens_mistral': 1547, 'n_tokens_neox': 1438, 'n_words': 261}