text:
import _ from 'lodash';
import { EventEmitter } from 'events';

import oilsStore from 'stores/oils';

let defaults = {
    name: '',
    description: '',
    notes: '',

    oils: [],
    weights: {},
    recipe: {},

    soapType: 'naoh',
    ratioNaoh: 50,
    ratioKoh: 50,
    kohPurity: 90,
    uom: 'percent',
    totalWeight: 500,
    totalUom: 'gram',
    superFat: 5,
    waterRatio: 38,
    fragrance: 3,
    recipeLyeConcentration: 30,
    totalsIncludeWater: false,
    superfatAfter: false,
    lyeCalcType: 'ratio',
    lyeWaterLyeRatio: 1,
    lyeWaterWaterRatio: 3,
    waterDiscount: 0,
    fragranceType: 'ratio',
    fragrancePpo: 30,
    visibility: 0
};

export default class extends EventEmitter {

    constructor() {
        super();

        this.setDefaults();

        this.on( 'changing', this.adjustHybridLyeFields );
        this.on( 'changing', this.convertUnitesOnPercentageUomChanges );
        this.on( 'changing', this.convertPpoUnitesOnPercentageUomChanges );
    }

    setRecipe( recipe ) {
        let summary = _.omit( recipe, 'settings' );

        this.recipe = _.extend( {}, this.recipe, summary, recipe.settings );
    }

    setModelValue( key, value ) {
        this.emit( 'changing', key, value );
        _.set( this.recipe, key, value );
        this.calculateRecipe();

        this.emit( 'calculated' );
    }

    setDefaults() {
        this.recipe = _.extend( {}, defaults );

        this.recipe.oils = null;
        this.recipe.weights = null;
        this.recipe.recipe = null;

        this.recipe.oils = [];
        this.recipe.weights = {};
        this.recipe.recipe = {};
    }

    reset() {
        this.setDefaults();

        this.calculateRecipe();
        this.emit( 'calculated' );
    }

    getModelValue( key ) {
        return _.get( this.recipe, key );
    }

    recipe() {
        return this.recipe;
    }

    isPercentRecipe() {
        return this.recipe.uom === 'percent';
    }

    isKohRecipe() {
        return this.recipe.soapType === 'koh';
    }

    isMixedRecipe() {
        return this.recipe.soapType === 'mixed';
    }

    isUomGrams() {
        return this.uomToUse() === 'gram';
    }

    isPrivate() {
        return Number( this.recipe.visibility ) === 0;
    }

    isPrivateFriends() {
        return Number( this.recipe.visibility ) === 2;
    }

    mixedTotalRatios() {
        return Number( this.recipe.ratioNaoh ) + Number( this.recipe.ratioKoh );
    }

    roundPlaces() {
        return this.roundPlacesForUom( this.uomToUse() );
    }

    roundPlacesForUom( uom ) {
        return {
            gram: 1,
            ounce: 2,
            kilo: 3,
            pound: 3
        }[ uom ];
    }

    sapForSoapType( lye, oil ) {
        let factors = {
            koh: ( this.recipe.kohPurity / 100 ),
            naoh: 1.403
        };

        return oil.sap / factors[ lye ];
    }

    setRecipeOilsByIds( oilIds ) {
        let existingOilsIds = _.pluck( this.recipe.oils, 'id' );
        let toRemoveOilsIds = _.difference( existingOilsIds, oilIds );
        let toKeepIds = _.intersection( existingOilsIds, oilIds );
        let toAdd = _.difference( oilIds, toKeepIds );

        _.each( toRemoveOilsIds, oilId => {
            let oil = _.find( this.recipe.oils, { id: Number( oilId ) } );
            this.removeOil( oil, true );
        } );

        _.each( toAdd, oilId => {
            let oil = oilsStore.getOilById( oilId );
            this.addOil( oil, true );
        } );

        this.calculateRecipe();
        this.emit( 'calculated' );
    }

    addOil( oil, skipUpdate = false ) {
        this.recipe.oils = _.union( this.recipe.oils, [ oil ] );

        if ( skipUpdate === false ) {
            this.calculateRecipe();
            this.emit( 'calculated' );
        }
    }

    removeOil( oil, skipUpdate = false ) {
        this.recipe.oils = _.without( this.recipe.oils, oil );
        delete this.recipe.weights[ oil.id ];

        if ( skipUpdate === false ) {
            this.calculateRecipe();
            this.emit( 'calculated' );
        }
    }

    getOilWeight( oil ) {
        return this.recipe.weights[ oil.id ] || '';
    }

    setOilWeight( oil, weight ) {
        //accepts numeric only
        if ( _.isFinite( Number( weight ) ) || weight === '.' ) {
            this.recipe.weights[ oil.id ] = weight;

            this.calculateRecipe();
            this.emit( 'calculated' );
        }
    }

    uomToUse() {
        if ( this.isPercentRecipe() ) {
            return this.recipe.totalUom;
        } else {
            return this.recipe.uom;
        }
    }

    totalWeight() {
        if ( this.recipe.totalsIncludeWater ) {
            return this.recipe.totalWeight / ( 1 + ( this.recipe.waterRatio / 100 ) );
        } else {
            return this.recipe.totalWeight;
        }
    }

    recipeOilsWeightsRatios() {
        let totalOilWeight;

        if ( this.isPercentRecipe() ) {
            totalOilWeight = this.totalWeight();
        } else {
            totalOilWeight = _.get( this.recipe, 'summary.totals.totalOilWeight' );
        }

        if ( totalOilWeight ) {
            return _.map( this.recipe.weights, ( weightOrRation, oilId ) => {
                let oil;
                let ratio;
                let weight;

                oil = _.find( this.recipe.oils, { id: Number( oilId ) } );

                if ( this.isPercentRecipe() ) {
                    ratio = weightOrRation / 100;
                    weight = totalOilWeight * ratio;
                } else {
                    ratio = weightOrRation / totalOilWeight;
                    weight = weightOrRation;
                }

                return {
                    oil,
                    ratio,
                    weight
                };
            } );
        }
    }

    sumWeights() {
        return _.sum( this.recipe.weights );
    }

    countWeights() {
        return _.filter( this.recipe.weights, ( weight ) => {
            return Number( weight ) > 0;
        } ).length;
    }

    countOils() {
        return this.recipe.oils.length;
    }

    calculateRecipe() {
        let totalOilWeight;
        let totalWaterWeight;
        let totalLye;
        let totalNaoh;
        let totalKoh;
        let totalSuperfat;
        let totalBatchWeight;
        let lyeConcentration;
        let waterLyeRatio;
        let fragranceWeight;

        let breakdowns;
        let properties;
        let saturations;

        let recipeLyeConcentration;

        //total weights either % ratios or uoms
        if ( this.isPercentRecipe() ) {
            totalOilWeight = this.totalWeight();
        } else {
            totalOilWeight = this.sumWeights();
        }

        if ( this.superfatAfter() ) {
            totalSuperfat = totalOilWeight * ( this.recipe.superFat / 100 );
        } else {
            totalSuperfat = 0;
        }

        if ( this.isMixedRecipe() ) {
            totalNaoh = _.sum( this.recipe.weights, ( weightOrRatio, oilId ) => {
                return this.lyeRatioWeightForOilId( 'naoh', weightOrRatio, oilId );
            } );
            totalKoh = _.sum( this.recipe.weights, ( weightOrRatio, oilId ) => {
                return this.lyeRatioWeightForOilId( 'koh', weightOrRatio, oilId );
            } );

            totalLye = totalNaoh + totalKoh;
        } else {
            totalLye = _.sum( this.recipe.weights, ( weightOrRatio, oilId ) => {
                return this.lyeWeightForOilId( this.recipe.soapType, weightOrRatio, oilId );
            } );
        }

        if ( this.recipe.fragranceType === 'ratio' ) {
            fragranceWeight = totalOilWeight * ( this.recipe.fragrance / 100 );
        } else {
            if ( _.contains( [ 'kilo', 'gram' ], this.uomToUse() ) ) {
                fragranceWeight = totalOilWeight * ( this.recipe.fragrancePpo / 1000 );
            } else {
                fragranceWeight = totalOilWeight * ( this.recipe.fragrancePpo / 16 );
            }
        }

        if ( this.isLyeConentration() ) {
            recipeLyeConcentration = (this.recipe.recipeLyeConcentration || 100) / 100;
            totalWaterWeight = ( totalLye / recipeLyeConcentration ) - totalLye;
            lyeConcentration = 100 * recipeLyeConcentration;
        } else if ( this.isLyeWaterRatio() ) {
            totalWaterWeight = totalLye * this.recipe.lyeWaterWaterRatio;
            lyeConcentration = 100 * (totalLye / ( totalWaterWeight + totalLye ));
        } else {
            totalWaterWeight = totalOilWeight * ( this.recipe.waterRatio / 100 );
            lyeConcentration = 100 * (totalLye / ( totalWaterWeight + totalLye ));
        }

        totalWaterWeight = totalWaterWeight - ( ((this.recipe.waterDiscount || 0) / 100) * totalWaterWeight );

        totalBatchWeight = Number( totalOilWeight ) + Number( totalWaterWeight ) + Number( totalLye ) + Number( fragranceWeight ) + Number( totalSuperfat );

        if ( totalWaterWeight + totalLye ) {
            waterLyeRatio = totalWaterWeight / totalLye;

            breakdowns = this.recipeOilFatBreakdowns();
            properties = this.recipeOilProperties();
            saturations = this.recipeOilSaturations();
        }

        this.recipe.summary = {
            totals: {
                totalOilWeight,
                totalWaterWeight,
                fragranceWeight,
                totalNaoh,
                totalKoh,
                totalLye,
                totalSuperfat,
                totalBatchWeight,
                lyeConcentration,
                waterLyeRatio
            },
            breakdowns,
            properties,
            saturations
        };
    }

    lyeWeightForOilId( lye, weightRatio, oilId ) {
        if ( weightRatio ) {
            let oilWeight;
            let oil;
            let grams;
            let lyeGrams;

            if ( this.isPercentRecipe() ) {
                oilWeight = this.totalWeight() * ( weightRatio / 100 );
            } else {
                oilWeight = weightRatio;
            }

            grams = this.convertToGrams( oilWeight );
            oil = _.find( this.recipe.oils, { id: Number( oilId ) } );
            lyeGrams = this.sapForSoapType( lye, oil ) * grams;

            //factor in superfat discount
            if ( !(this.recipe.superfatAfter) ) {
                lyeGrams = lyeGrams - ( this.recipe.superFat / 100 ) * lyeGrams;
            }

            return this.convertToUom( lyeGrams );
        } else {
            return 0;
        }
    }

    lyeRatioWeightForOilId( lye, weightRatio, oilId ) {
        let amount;
        let ratio;

        amount = this.lyeWeightForOilId( lye, weightRatio, oilId );
        ratio = this.recipe[ 'ratio' + _.capitalize( lye ) ] / 100;

        return ratio * amount;
    }

    convertToGrams( weightOrRatio ) {
        return weightOrRatio * this.conversions()[ this.uomToUse() ];
    }

    convertToUom( grams ) {
        return grams / this.conversions()[ this.uomToUse() ];
    }

    conversions() {
        return {
            gram: 1,
            kilo: 0.001,
            pound: 0.00220462,
            ounce: 0.035274
        };
    }

    oilsToRatioIterator( block ) {
        let total;

        total = this.sumWeights();

        _.each( this.recipe.weights, ( weightOrRatio, oilId ) => {
            let oil;
            let ratio;

            if ( this.isPercentRecipe() ) {
                ratio = weightOrRatio / 100;
            } else {
                ratio = weightOrRatio / total;
            }

            oil = _.find( this.recipe.oils, { id: Number( oilId ) } );

            block( oil, ratio );
        }, this );
    }

    recipeOilProperties() {
        return _.tap( {}, result => {
            this.oilsToRatioIterator( ( oil, ratio ) => {
                _.each( oil.properties, ( value, key ) => {
                    result[ key ] = ( result[ key ] || 0 ) + oil.properties[ key ] * ratio;
                } );
                _.each( [ 'iodine', 'ins' ], key => {
                    result[ key ] = ( result[ key ] || 0 ) + oil[ key ] * ratio;
                } );
            } );
        } );
    }

    recipeOilFatBreakdowns() {
        return _.tap( {}, result => {
            this.oilsToRatioIterator( ( oil, ratio ) => {
                _.each( oil.breakdown, ( acidRatio, fattyAcid ) => {
                    result[ fattyAcid ] = ( result[ fattyAcid ] || 0 ) + acidRatio * ratio;
                } );
            } );
        } );
    }

    recipeOilSaturations() {
        return _.tap( {}, result => {
            this.oilsToRatioIterator( ( oil, ratio ) => {
                let saturated = _( oil.breakdown )
                        .filter( ( percent, type ) => _.contains( [ 'caprylic', 'capric', 'lauric', 'myristic', 'palmitic', 'stearic' ], type ) )
                        .sum() * ratio;

                result.saturated = ( result.saturated || 0 ) + saturated;
            } );
            result.unsaturated = 100 - result.saturated;
        } );
    }

    soapTypeToLye() {
        return {
            naoh: 'NaOH',
            koh: 'KOH',
            mixed: 'NaOH and KOH'
        }[ this.recipe.soapType ];
    }

    convertWeightToGrams( weight ) {
        return weight / this.conversions()[ this.uomToUse() ];
    }

    totalsIncludeWater() {
        return this.recipe.totalsIncludeWater;
    }

    superfatAfter() {
        return this.recipe.superfatAfter;
    }

    waterDiscount() {
        return this.recipe.waterDiscount;
    }

    isLyeConentration() {
        return this.recipe.lyeCalcType === 'concentration';
    }

    isLyeWaterRatio() {
        return this.recipe.lyeCalcType === 'lyewater';
    }

    adjustHybridLyeFields( key, value ) {

        if ( key === 'ratioNaoh' ) {
            this.recipe.ratioKoh = 100 - value;
        } else if ( key === 'ratioKoh' ) {
            this.recipe.ratioNaoh = 100 - value;
        }

    }

    convertUnitesOnPercentageUomChanges( key, value ) {
        let weightInGrams;

        if ( this.isPercentRecipe() ) {
            if ( key === 'totalUom' ) {
                weightInGrams = this.recipe.totalWeight / this.conversions()[ this.recipe.totalUom ];
                this.recipe.totalWeight = weightInGrams * this.conversions()[ value ];
            }
        }
    }

    convertPpoUnitesOnPercentageUomChanges( key, value ) {
        let from;
        let to;

        if ( _.contains( ['uom', 'totalUom'], key ) ) {

            if ( this.recipe.uom === 'percent' ) {
                from = this.recipe.totalUom;
                to = value;
            } else if ( value === 'percent' ) {
                from = this.recipe.uom;
                to = this.recipe.totalUom;
            } else {
                from = this.recipe[ key ];
                to = value;
            }

            if ( _.contains( [ 'gram', 'kilo' ], from ) && _.contains( [ 'ounce', 'pound' ], to ) ) {
                this.recipe.fragrancePpo = (this.recipe.fragrancePpo / 1000) / (1 / 16);
            } else if ( _.contains( [ 'ounce', 'pound' ], from ) && _.contains( [ 'gram', 'kilo' ], to ) ) {
                this.recipe.fragrancePpo = (this.recipe.fragrancePpo / 16) / (1 / 1000);
            }

        }

    }

}


text:
import _ from 'lodash';

let fattyOils = {
    caprylic: '8:0',
    capric: '10:0',
    lauric: '12:0',
    myristic: '14:0',
    palmitic: '16:0',
    stearic: '18:0',
    ricinoleic: '18:1',
    oleic: '18:1',
    linoleic: '18:2',
    linolenic: '18:3',
    eicosenoic: '20:1',
    docosenoid: '22:1',
    erucic: '22:1',
    docosadienoic: '22:2'
};

export default class {

    constructor( oil ) {
        this.oil = oil;
    }

    getExtendedOil() {
        this.extendOil();
        return this.oil;
    }

    extendOil() {
        this.oil.properties = this.buildProperties();
        this.oil.saturation = this.saturationBreakdown();
    }

    buildProperties() {
        return {
            bubbly: this.propertyValueForOil( this.oil, [ 'lauric', 'myristic', 'ricinoleic', 'caprylic', 'capric' ] ),
            cleansing: this.propertyValueForOil( this.oil, [ 'lauric', 'myristic', 'caprylic', 'capric' ] ),
            condition: this.propertyValueForOil( this.oil, [ 'ricinoleic', 'oleic', 'linoleic', 'linolenic', 'eicosenoic', 'docosenoid', 'docosadienoic', 'erucic' ] ),
            hardness: this.propertyValueForOil( this.oil, [ 'lauric', 'myristic', 'palmitic', 'stearic', 'caprylic', 'capric' ] ),
            longevity: this.propertyValueForOil( this.oil, [ 'palmitic', 'stearic' ] ),
            stable: this.propertyValueForOil( this.oil, [ 'palmitic', 'stearic', 'ricinoleic' ] )
        };
    }

    saturationBreakdown() {
        return _.transform( this.saturationBreakdowns(), ( result, fats, group ) => {
            result[ group ] = this.propertyValueForOil( this.oil, fats );
        } );
    }

    saturationBreakdowns() {
        return {
            saturated: _( fattyOils ).pick( this.filterByMatch( ':0' ) ).keys().value(),
            monoSaturated: _( fattyOils ).pick( this.filterByMatch( ':1' ) ).keys().value(),
            polySaturated: _( fattyOils ).pick( this.filterByMatch( ':2' ) ).keys().value()
        };
    }

    propertyValueForOil( oil, fats ) {
        return _.reduce( fats, ( total, fat ) => {
            return total + ( oil.breakdown[ fat ] || 0 );
        }, 0 );
    }

    filterByMatch( matcher ) {
        return oilType => {
            return oilType.match( matcher );
        };
    }

}

repo_name: nazar/soapee-ui

repo_language: JavaScript

mime_type: text/plain


text:
import Reflux from 'reflux';

import oilStore from 'stores/oil';
import oilActions from 'actions/oil';

export default Reflux.createStore( {

    oil: null,
    comments: [],

    init() {
        this.listenTo( oilStore, gotOil.bind( this ) );
        this.listenTo( oilActions.getOilComments.completed, gotComments.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    count() {
        return this.comments.length;
    },

    addComment( comment ) {
        return oilActions.addCommentToOil( comment, this.oil )
            .then( addToComments.bind( this ) );
    }

} );

//////////////////////////
//// Private

function gotOil( oil ) {
    this.oil = oil;
    oilActions.getOilComments( this.oil );
}

function gotComments( comments ) {
    this.comments = comments;
    doTrigger.call( this );
}

function addToComments( comment ) {
    this.comments.unshift( comment );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.comments );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import { getOils } from 'resources/oils';
import Oil from 'models/oil';

export default Reflux.createStore( {

    store: [],

    init() {
        this.loadOils();
    },

    getInitialState() {
        return this.store;
    },

    getAllFats() {
        return [
            'capric', 'caprylic', 'docosadienoic', 'docosenoid', 'eicosenoic', 'erucic', 'lauric', 'linoleic', 'linolenic', 'myristic', 'oleic', 'palmitic', 'ricinoleic', 'stearic'
        ];
    },

    getOilById( oilId ) {
        return _.find( this.store, { id: oilId } );
    },

    getFlatOilProperties() {
        return _.map( this.store, oil => {
            return {
                id: oil.id,
                name: oil.name,
                sap: oil.sap,
                iodine: oil.iodione,
                ins: oil.ins,

                bubbly: oil.properties.bubbly,
                cleansing: oil.properties.cleansing,
                condition: oil.properties.condition,
                hardness: oil.properties.hardness,
                longevity: oil.properties.longevity,
                stability: oil.properties.stable,

                capric: oil.breakdown.capric || 0,
                caprylic: oil.breakdown.caprylic || 0,
                docosadienoic: oil.breakdown.docosadienoic || 0,
                docosenoid: oil.breakdown.docosenoid || 0,
                eicosenoic: oil.breakdown.eicosenoic || 0,
                erucic: oil.breakdown.erucic || 0,
                lauric: oil.breakdown.lauric || 0,
                linoleic: oil.breakdown.linoleic || 0,
                linolenic: oil.breakdown.linolenic || 0,
                myristic: oil.breakdown.myristic || 0,
                oleic: oil.breakdown.oleic || 0,
                palmitic: oil.breakdown.palmitic || 0,
                ricinoleic: oil.breakdown.ricinoleic || 0,
                stearic: oil.breakdown.stearic || 0,

                saturated: oil.saturation.saturated,
                monoSaturated: oil.saturation.monoSaturated,
                polySaturated: oil.saturation.polySaturated
            };
        } );
    },

    oilPropertyGroupings() {
        return {
            'fats-common': [ 'name', 'sap', 'lauric', 'linoleic', 'linolenic', 'myristic', 'oleic', 'palmitic', 'ricinoleic', 'stearic' ],
            'fats-all':    [ 'name', 'sap', 'capric', 'caprylic', 'docosadienoic', 'docosenoid', 'eicosenoic', 'erucic', 'lauric', 'linoleic', 'linolenic', 'myristic', 'oleic', 'palmitic', 'ricinoleic', 'stearic' ],
            properties:    [ 'name', 'sap', 'bubbly', 'cleansing', 'condition', 'hardness', 'longevity', 'stability' ],
            saturation:    [ 'name', 'sap', 'saturated', 'monoSaturated', 'polySaturated' ]
        };

    },

    loadOils() {
        function assignToStore( data ) {
            this.store = _.map( data, oil => (new Oil( oil )).getExtendedOil() );
        }

        function doTrigger() {
            this.trigger( this.store );
        }

        getOils()
            .then( assignToStore.bind( this ) )
            .then( doTrigger.bind( this ) );
    }

} );


text:
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';
import recipeStore from 'stores/recipe';

export default Reflux.createStore( {

    recipe: null,
    store: [],
    count: 0,

    init() {
        this.listenTo( recipeStore, gotRecipe.bind( this ) );
        this.listenTo( recipeActions.getRecipeJournals.completed, gotJournals.bind( this ) );
        this.listenTo( recipeActions.addRecipeJournal.completed, addToStore.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

//////////////////////////
//// Private

function gotRecipe( recipeStore ) {
    this.recipe = recipeStore.recipe;
}

function gotJournals( journal ) {
    this.count = journal.count;
    this.store = journal.journals;

    doTrigger.call( this );
}

function addToStore( journal ) {
    this.count = Number(this.count) + 1;
    this.store.unshift( journal );

    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';
import userActions from 'actions/user';

export default Reflux.createStore( {

    store: null,

    init() {
        this.listenTo( userActions.getProfile.completed, gotProfile.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

///////////////////
////

function gotProfile( profile ) {
    this.store = profile;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import authStore from 'stores/auth';
import meActions from 'actions/me';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( authStore, authenticated.bind( this ) );
        this.listenTo( meActions.getMyUserNotifications.completed, gotNotifications.bind( this ) );
        this.listenTo( meActions.dismissNotification.completed, getNotifications.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    size() {
        return (this.store || []).length;
    },

    sizeUnread() {
        return _.filter( this.store, { read: false } ).length;
    }

} );

//////////////////////////
//// Private

function authenticated( user ) {
    if ( user && user.id ) {
        getNotifications();
    }
}

function getNotifications() {
    meActions.getMyUserNotifications();
}

function gotNotifications( notifications ) {
    this.store = notifications;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import meActions from 'actions/me';
import statusUpdateActions from 'actions/statusUpdate';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( meActions.getMyStatusUpdates.completed, gotStatusUpdates.bind( this ) );
        this.listenTo( statusUpdateActions.addStatusUpdate.completed, gotNewStatusUpdate.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

//////////////////////////
//// Private

function gotStatusUpdates( statusUpdates ) {
    this.store = statusUpdates;
    doTrigger.call( this );
}

function gotNewStatusUpdate( statusUpdate ) {
    this.store.unshift( statusUpdate );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import statusUpdateStore from 'stores/statusUpdate';
import statusUpdateActions from 'actions/statusUpdate';

export default Reflux.createStore( {

    statusUpdate: null,
    store: [],

    init() {
        this.listenTo( statusUpdateStore, gotStatusUpdate.bind( this ) );
        this.listenTo( statusUpdateActions.getStatusUpdateComments.completed, gotComments.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    count() {
        return this.store.length;
    },

    addComment( comment ) {
        return statusUpdateActions.addComment( comment, this.statusUpdate )
            .then( addToComments.bind( this ) );
    }

} );

//////////////////////////
//// Private

function gotStatusUpdate( statusUpdate ) {
    this.statusUpdate = statusUpdate;
}

function gotComments( comments ) {
    this.store = comments;
    doTrigger.call( this );
}

function addToComments( comment ) {
    this.store.unshift( comment );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import authStore from 'stores/auth';
import meActions from 'actions/me';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( authStore, authenticated.bind( this ) );
        this.listenTo( meActions.getMyFriends.completed, gotMyFriends.bind( this ) );
        this.listenTo( meActions.addFriend.completed, getFriends.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

//////////////////////////
//// Private

function authenticated( user ) {
    if ( user && user.id ) {
        getFriends();
    }
}

function getFriends() {
    meActions.getMyFriends();
}

function gotMyFriends( friends ) {
    this.store = friends;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import userProfileStore from 'stores/userProfile';
import authStore from 'stores/auth';

import userActions from 'actions/user';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( userProfileStore, gotUserProfile.bind( this ) );
        this.listenTo( userActions.getFriends.completed, gotFriends.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    iAmNotFriendOfUser() {
        return _.filter( this.store, { id: authStore.userId() } ).length === 0;
    }

} );

///////////////////
////

function gotUserProfile( user ) {
    userActions.getFriends( user );
}

function gotFriends( users ) {
    this.store = users;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import userProfileStore from 'stores/userProfile';
import userActions from 'actions/user';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( userProfileStore, gotUserProfile.bind( this ) );
        this.listenTo( userActions.getRecipes.completed, gotRecipes.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

///////////////////
////

function gotUserProfile( user ) {
    userActions.getRecipes( user );
}

function gotRecipes( recipes ) {
    this.store = recipes;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import feedActions from 'actions/feed';

export default Reflux.createStore( {

    store: [],
    count: 0,

    init() {
        this.listenTo( feedActions.getFeed, reset.bind( this ) );
        this.listenTo( feedActions.getFeed.completed, gotFeed.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    totalPages() {
        return _.ceil( this.count / 15 );
    },

    pagerVisible() {
        return this.count > 15;
    }

} );

//////////////////////////
//// Private

function reset() {
    this.store = [];
    doTrigger.call( this );
}

function gotFeed( feed ) {
    this.store = feed.feed;
    this.count = Number( feed.count );

    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';
import meActions from 'actions/me';

export default Reflux.createStore( {

    store: {
        myRecipes: null,          //todo - might want to break this out into its own store
        myFavouriteRecipes: null  //todo - might want to break this out into its own store
    },

    init() {
        this.listenTo( meActions.getMyProfile.completed, gotProfile.bind( this ) );
        this.listenTo( meActions.updateMyProfile.completed, gotProfile.bind( this ) );

        this.listenTo( meActions.getMyRecipes.completed, gotMyRecipes.bind( this ) );
        this.listenTo( meActions.getMyFavouriteRecipes.completed, gotMyFavouriteRecipes.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    setStoreValue( key, value ) {
        _.set( this.store, key, value );
        doTrigger.call( this );
    },

    getStoreValue( key ) {
        return _.get( this.store, key );
    }

} );

///////////////////
////

function gotProfile( profile ) {
    this.store = _.extend( {}, this.store, profile );
    doTrigger.call( this );
}

function gotMyRecipes( recipes ) {
    _.set( this.store, 'myRecipes', recipes );
    doTrigger.call( this );
}

function gotMyFavouriteRecipes( recipes ) {
    _.set( this.store, 'myFavouriteRecipes', recipes );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import statusUpdateActions from 'actions/statusUpdate';

export default Reflux.createStore( {

    store: null,

    init() {
        this.listenTo( statusUpdateActions.getStatusUpdate.completed, gotStatusUpdate.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

//////////////////////////
//// Private

function gotStatusUpdate( statusUpdate ) {
    this.store = statusUpdate;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';
import ga from 'react-ga';

import authActions from 'actions/auth';
import recipeActions from 'actions/recipe';

export default Reflux.createStore( {

    init() {
        //User
        this.listenTo( authActions.signupOrLoginThirdParty.completed, analytics.bind( this, 'Users', 'signupOrLoginThirdParty' ) );
        this.listenTo( authActions.signupLocal.completed, analytics.bind( this, 'Users', 'signupLocal' ) );
        this.listenTo( authActions.loginLocal.completed, analytics.bind( this, 'Users', 'loginLocal' ) );

        //Recipes
        this.listenTo( recipeActions.createRecipe, analytics.bind( this, 'Recipes', 'createRecipe' ) );
        this.listenTo( recipeActions.updateRecipe.completed, analytics.bind( this, 'updateRecipe' ) );
        this.listenTo( recipeActions.deleteRecipe.completed, analytics.bind( this, 'deleteRecipe' ) );
    }
} );

//////////////////////////
//// Private

function analytics( category, action ) {
    ga.event( {
        category,
        action
    } );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import RecipeModel from 'models/recipe';
import recipeActions from 'actions/recipe';

export default Reflux.createStore( {

    init() {
        this.store = new RecipeModel();
        this.store.on( 'calculated', doTrigger.bind( this ) );

        this.listenTo( recipeActions.resetRecipe, reset.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    sapForNaOh( oil ) {   //todo - shouldn't really be in here...
        return _.round( oil.sap / 1.403, 3 );
    }

} );

//////////////////////
///// Private

function reset() {
    this.store.reset();
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';

import RecipeModel from 'models/recipe';
import OilModel from 'models/oil';

export default Reflux.createStore( {

    store: null,

    init() {
        this.store = new RecipeModel();

        this.store.on( 'calculated', doTrigger.bind( this ) );

        this.listenTo( recipeActions.getRecipeById.completed, gotRecipe.bind( this ) );
        this.listenTo( recipeActions.getRecipeById.failed, setError.bind( this ) );

    },

    getInitialState() {
        return this.store;
    },

    ///public methods

    calculate() {
        this.store.calculateRecipe();
    }

} );

//////////////////////////
//// Private

function gotRecipe( recipe ) {
    //a recipe has also oils, which needs to be extended
    recipe.oils = _.map( recipe.oils, oil => ( new OilModel( oil ).getExtendedOil() ) );

    this.store.setRecipe( recipe );

    doTrigger.call( this );
}

function setError( error ) {
    this.trigger( {
        error: error.responseJSON
    } );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';
import recipeJournalStore from 'stores/recipeJournal';

export default Reflux.createStore( {

    recipeJournal: null,
    comments: [],

    init() {
        this.listenTo( recipeJournalStore, gotRecipeJournal.bind( this ) );
        this.listenTo( recipeActions.getRecipeJournalComments.completed, gotComments.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    count() {
        return this.comments.length;
    },

    addComment( comment ) {
        return recipeActions.addCommentToRecipeJournal( comment, {id: this.recipeJournal.recipe_id}, this.recipeJournal )
            .then( addToComments.bind( this ) );
    }

} );

//////////////////////////
//// Private

function gotRecipeJournal( recipeJournal ) {
    this.recipeJournal = recipeJournal;
}

function gotComments( comments ) {
    this.comments = comments;
    doTrigger.call( this );
}

function addToComments( comment ) {
    this.comments.unshift( comment );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.comments );
}


text:
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';
import recipeStore from 'stores/recipe';

export default Reflux.createStore( {

    recipe: null,
    store: null,

    init() {
        this.listenTo( recipeStore, gotRecipe.bind( this ) );
        this.listenTo( recipeActions.getRecipeJournal, this.reset );
        this.listenTo( recipeActions.getRecipeJournal.completed, gotJournal.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    reset() {
        this.store = null;
        doTrigger.call( this );
    }

} );

//////////////////////////
//// Private

function gotRecipe( recipeStore ) {
    this.recipe = recipeStore.recipe;
}

function gotJournal( journal ) {
    this.store = journal;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import meActions from 'actions/me';

export default Reflux.createStore( {

    store: [],

    init() {
        this.listenTo( meActions.getMyFriendsRecipes.completed, gotRecipes.bind( this ) );
        this.listenTo( meActions.addFriend.completed, getFriends.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

//////////////////////////
//// Private

function getFriends() {
    meActions.getMyFriendsRecipes();
}

function gotRecipes( recipes ) {
    this.store = recipes;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';

export default Reflux.createStore( {

    store: [],
    count: 0,

    init() {
        this.listenTo( recipeActions.getRecipes, reset.bind( this ) );
        this.listenTo( recipeActions.getRecipes.completed, gotRecipes.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    ///public methods

    totalPages() {
        return _.ceil( this.count / 10 );
    }

} );

//////////////////////////
//// Private

function reset() {
    this.store = [];

    doTrigger.call( this );
}

function gotRecipes( data ) {
    this.store = data.recipes;
    this.count = Number( data.count );

    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';
import oilActions from 'actions/oil';

import OilModel from 'models/oil';

export default Reflux.createStore( {

    store: null,

    init() {
        this.listenTo( oilActions.getOilById.completed, gotOil.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

///////////////////
////

function gotOil( oil ) {
    this.store = new OilModel( oil ).getExtendedOil();

    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Reflux from 'reflux';

import recipeActions from 'actions/recipe';
import recipeStore from 'stores/recipe';

export default Reflux.createStore( {

    recipe: null,
    comments: [],

    init() {
        this.listenTo( recipeStore, gotRecipe.bind( this ) );
        this.listenTo( recipeActions.getRecipeComments.completed, gotComments.bind( this ) );
    },

    getInitialState() {
        return this.store;
    },

    count() {
        return this.comments.length;
    },

    addComment( comment ) {
        return recipeActions.addCommentToRecipe( comment, this.recipe )
            .then( addToComments.bind( this ) );
    }

} );

//////////////////////////
//// Private

function gotRecipe( recipeStore ) {
    this.recipe = recipeStore.recipe;
}

function gotComments( comments ) {
    this.comments = comments;
    doTrigger.call( this );
}

function addToComments( comment ) {
    this.comments.unshift( comment );
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.comments );
}


text:
import _ from 'lodash';
import Reflux from 'reflux';

import authActions from 'actions/auth';
import { getCurrentUser } from 'resources/auths';

export default Reflux.createStore( {

    store: {},

    init() {
        this.listenTo( authActions.signupOrLoginThirdParty.completed, authenticated.bind( this ) );
        this.listenTo( authActions.signupLocal.completed, authenticated.bind( this ) );
        this.listenTo( authActions.loginLocal.completed, authenticated.bind( this ) );

        this.listenTo( authActions.logout.completed, loggedOut.bind( this ) );

        this.authenticate();
    },

    getInitialState() {
        return this.store;
    },

    ///public methods

    authenticate() {
        return getCurrentUser()
            .then( authenticated.bind( this ) )
            .catch( swallowIfNotAuthorised );
    },

    isAuthenticated() {
        return _.keys( this.store ).length > 0;
    },

    avatarUrl() {
        return this.store.imageUrl;
    },

    userName() {
        return this.store.name;
    },

    userId() {
        return this.store.id;
    },

    isMyId( userId ) {
        return Number( userId ) === this.store.id;
    }

} );

//////////////////////////
//// Private

function authenticated( authUser ) {
    this.store = authUser;

    doTrigger.call( this );
}

function loggedOut() {
    this.store = {};

    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}

function swallowIfNotAuthorised( e ) {
    if ( e.status !== 403 ) {
        throw e;
    }
}


text:
import Reflux from 'reflux';
import meActions from 'actions/me';

export default Reflux.createStore( {

    store: null,

    init() {
        this.listenTo( meActions.getComments.completed, gotComments.bind( this ) );
    },

    getInitialState() {
        return this.store;
    }

} );

///////////////////
////

function gotComments( comments ) {
    this.store = comments;
    doTrigger.call( this );
}

function doTrigger() {
    this.trigger( this.store );
}


text:
import Checkit from 'checkit';

export default class {

    constructor( password ) {
        this.password = password;
    }

    execute() {
        let rules = new Checkit( {
            password: [ 'required', 'alphaDash', 'minLength:6', 'maxLength:20' ]
        } );

        return rules.run( {
            password: this.password
        } );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( payload ) {
        this.payload = payload;
    }

    execute() {
        let rules = new Checkit( {
            name: [ 'required', 'minLength:3', 'maxLength:50' ],
            email: [ 'email' ]
        } );

        return rules.run( this.payload );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( payload ) {
        this.payload = payload;
    }

    execute() {
        let rules = new Checkit( {
            username: [ 'required', 'alphaDash', 'minLength:3', 'maxLength:12' ],
            password: [ 'required', 'alphaDash', 'minLength:6', 'maxLength:20' ]
        } );

        return rules.run( this.payload );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( email ) {
        this.email = email;
    }

    execute() {
        let rules = new Checkit( {
            email: [ 'email' ]
        } );

        return rules.run( this.email );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( fieldName, value ) {
        this.fieldName = fieldName;
        this.value = value;
    }

    execute() {
        let rules = new Checkit( {
            [this.fieldName]: [ 'required' ]
        } );

        return rules.run( {
            [ this.fieldName ]: this.value
        } );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( payload ) {
        this.payload = payload;
    }

    execute() {
        let rules = new Checkit( {
            name: [ 'required', 'minLength:3', 'maxLength:50' ]
        } );

        return rules.run( this.payload );
    }
}


text:
import Checkit from 'checkit';
import { usernameExists, emailExists } from 'resources/auths';

export default class {

    constructor( payload ) {
        this.payload = payload;
    }

    execute() {
        let rules = new Checkit( {
            username: [ 'required', 'alphaDash', 'minLength:3', 'maxLength:12', this.checkIfUsernameExists ],
            password: [ 'required', 'alphaDash', 'minLength:6', 'maxLength:20' ],
            email: [ 'email', this.checkIfEmailExists ]
        } );

        return rules.run( this.payload );
    }

    checkIfUsernameExists( username ) {
        return usernameExists( username )
            .then( result => {
                if ( result.exists ) {
                    throw new Error( 'Username already exists. Please pick another or login instead.' );
                }
            } );
    }

    checkIfEmailExists( email ) {
        return emailExists( email )
            .then( result => {
                if ( result.exists ) {
                    throw new Error( 'Email already exists. Please pick another or login instead.' );
                }
            } );
    }
}


text:
import Checkit from 'checkit';

export default class {

    constructor( payload ) {
        this.payload = payload;
    }

    execute() {
        let rules = new Checkit( {
            comment: [ 'required', 'maxLength:2000' ]
        } );

        return rules.run( this.payload );
    }
}


text:
import when from 'when';
import { get, post } from 'utils/http';

import baseUrl from 'utils/baseUrl';

export function getOils() {
    return when(
        get( baseUrl( 'oils' ) )
    );
}

export function getOilComments( oil ) {
    return when(
        get( baseUrl( `oils/${oil.id}/comments` ) )
    );
}

export function addCommentToOil( comment, oil ) {
    return when(
        post( baseUrl( `oils/${oil.id}/comments` ), {
            params: {
                comment
            }
        } )
    );
}

export function getOilById( oilId ) {
    return when(
        get( baseUrl( `oils/${oilId}` ) )
    );
}


text:
import when from 'when';
import { get } from 'utils/http';

import baseUrl from 'utils/baseUrl';

export function getProfile( userId ) {
    return when(
        get( baseUrl( `users/${userId}` ) )
    );
}

export function getRecipes( user ) {
    return when(
        get( baseUrl( `users/${user.id}/recipes` ) )
    );
}

export function getFriends( user ) {
    return when(
        get( baseUrl( `users/${user.id}/friends` ) )
    );
}


text:
import when from 'when';

import { post } from 'utils/http';
import baseUrl from 'utils/baseUrl';

export function requestPasswordReset( email ) {
    return when(
        post( baseUrl( 'resets/request-reset' ), {
            params: {
                email
            }
        } )
    );
}

export function verifyCode( token, code ) {
    return when(
        post( baseUrl( 'resets/verify' ), {
            params: {
                token,
                code
            }
        } )
    );
}

export function resetPassword( token, code, password ) {
    return when(
        post( baseUrl( 'resets/reset-password' ), {
            params: {
                token,
                code,
                password
            }
        } )
    );
}


text:
import _ from 'lodash';
import when from 'when';
import { get, post, put, del } from 'utils/http';

import baseUrl from 'utils/baseUrl';

export function getStatusUpdate( id ) {
    return when(
        get( baseUrl( `status-updates/${id}` ) )
    );
}

export function addStatusUpdate( update ) {
    let params = {
        update
    };

    return when(
        post( baseUrl( 'status-updates' ), {
            params
        } )
    );
}

export function updateStatusUpdate( statusUpdate ) {
    return when(
        put( baseUrl( `status-updates/${statusUpdate.id}` ), {
            params: {
                update: statusUpdate.update,
                deleting: {
                    imageables: _( statusUpdate.images ).filter( { deleting: true } ).pluck( 'id' ).value()
                }
            }
        } )
    );
}

export function deleteStatusUpdate( statusUpdate ) {
    return when(
        del( baseUrl( `status-updates/${statusUpdate.id}` ) )
    );
}

export function getStatusUpdateComments( statusUpdate ) {
    return when(
        get( baseUrl( `status-updates/${statusUpdate.id}/comments` ) )
    );
}

export function addComment( comment, statusUpdate ) {
    return when(
        post( baseUrl( `status-updates/${statusUpdate.id}/comments` ), {
            params: {
                comment
            }
        } )
    );
}


text:
import when from 'when';
import { get } from 'utils/http';

import baseUrl from 'utils/baseUrl';

export function getFeed( options = {} ) {
    return when(
        get( baseUrl( 'feedables' ), {
            params: {
                page: options.page
            }
        } )
    );
}


text:
import when from 'when';
import {del, get, post, put} from 'utils/http';

import baseUrl from 'utils/baseUrl';

export function getMyProfile() {
    return when(
        get( baseUrl( 'me' ) )
    );
}

export function updateMyProfile( packet ) {
    return when(
        post( baseUrl( 'me' ), {
            params: {
                name: packet.name,
                email: packet.email,
                about: packet.about
            }
        } )
    );
}

export function getMyRecipes() {
    return when(
        get( baseUrl( 'me/recipes' ) )
    );
}

export function getMyFavouriteRecipes() {
    return when(
        get( baseUrl( 'me/favourite/recipes' ) )
    );
}

export function getComments() {
    return when(
        get( baseUrl( 'me/comments' ) )
    );
}

export function addRecipeToFavourites( recipe ) {
    return when(
        put( baseUrl( `me/favourite/recipes/${recipe.id}` ) )
    );
}

export function removeRecipeFromFavourites( recipe ) {
    return when(
        del( baseUrl( `me/favourite/recipes/${recipe.id}` ) )
    );
}

export function getMyUserNotifications() {
    return when(
        get( baseUrl( 'me/notifications' ) )
    );
}

export function dismissNotification( notification ) {
    return when(
        put( baseUrl( `me/notifications/${notification.id}` ), {
                params: {
                    read: true
                }
            }
        )
    );
}

export function getMyFriends() {
    return when(
        get( baseUrl( `me/friends` ) )
    );
}

export function getMyFriendsRecipes() {
    return when(
        get( baseUrl( `me/friends/recipes` ) )
    );
}

export function addFriend( targetUser ) {
    return when(
        post( baseUrl( `me/friends/${targetUser.id}` ) )
    );
}

export function getMyStatusUpdates() {
    return when(
        get( baseUrl( 'me/status-updates' ) )
    );
}


text:
import {resolve} from 'url';

import config from 'config';

export function imageableUrl( image ) {
    return resolve( config.imageUrl, [ image.path, image.file_name ].join( '/' ) );
}

export function imageableThumbUrl( image ) {
    return resolve( config.imageUrl, [ image.path, 'thumb-' + image.file_name ].join( '/' ) );
}


text:
/*global FB*/

import when from 'when';

export default function authenticate( doLogin ) {

    return authoriseUser()
        .then( extractAccessToken );

    function authoriseUser() {
        return when.promise( ( resolve, reject ) => {

            FB.getLoginStatus( response => {
                if ( response.status === 'connected' ) {
                    resolve( response );
                } else if ( doLogin ) {
                    FB.login( response => {
                        if ( response.authResponse ) {
                            resolve( response );
                        } else {
                            reject();
                        }
                    } );
                } else {
                    reject();
                }
            } );

        } );
    }

    function extractAccessToken( response ) {
        return response.authResponse.accessToken;
    }

}


text:
/*global gapi*/

import when from 'when';

import config from 'config';

// define googleAuth as a module var as Chrome seems to hang if it is
// returned in initialiseSdk after the gapi initi call... i.e.
//  return gapi.auth2.init( {
//      client_id: config.auth.googleClientId,
//      scope: 'profile'
//  } );
// might be a when issue....

let googleAuth;

export default function authenticate( doLogin ) {

    return loadAuth2Sdk()
        .then( initialiseSdk )
        .then( authoriseUser )
        .then( extractAccessToken );

    function loadAuth2Sdk() {
        return when.promise( resolve => {
            gapi.load( 'auth2', resolve );
        } );

    }

    function initialiseSdk() {
        if ( !googleAuth ) {
            googleAuth = gapi.auth2.init( {
                client_id: config.auth.googleClientId,
                scope: 'profile'
            } );
        }
    }

    function authoriseUser() {
        return when.promise( ( resolve, reject ) => {
            googleAuth.then( () => {
                if ( googleAuth.isSignedIn.get() ) {
                    resolve( getGoogleUserAuthResponse() );
                } else {
                    if ( doLogin ) {
                        when( googleAuth.signIn() )
                            .then( getGoogleUserAuthResponse )
                            .then( resolve )
                            .catch( reject );
                    } else {
                        reject();
                    }

                }
            } );
        } );

        function getGoogleUserAuthResponse() {
            return googleAuth.currentUser.get().getAuthResponse();
        }
    }

    function extractAccessToken( response ) {
        return response.access_token;
    }

}


text:
import _ from 'lodash';
import when from 'when';

import { get, post, put, del } from 'utils/http';
import baseUrl from 'utils/baseUrl';

export function createRecipe( recipeModel ) {
    let packet = recipeModelToPacket( recipeModel );

    return when(
        post( baseUrl( 'recipes' ), {
            params: packet
        } )
    );
}

export function updateRecipe( recipeModel ) {
    let packet = recipeModelToPacket( recipeModel );

    return when(
        put( baseUrl( `recipes/${ recipeModel.getModelValue( 'id' ) }` ), {
            params: packet
        } )
    );
}

export function deleteRecipe( recipeModel ) {
    return when(
        del( baseUrl( `recipes/${ recipeModel.getModelValue( 'id' ) }` ) )
    );
}

export function getRecipes( options = {} ) {
    return when(
        get( baseUrl( 'recipes' ), {
            params: {
                page: options.page
            }
        } )
    );
}

export function getRecipeById( id ) {
    return when(
        get( baseUrl( `recipes/${id}` ) )
    );
}

export function getRecipeComments( recipe ) {
    return when(
        get( baseUrl( `recipes/${recipe.id}/comments` ) )
    );
}

export function addCommentToRecipe( comment, recipe ) {
    return when(
        post( baseUrl( `recipes/${recipe.id}/comments` ), {
            params: {
                comment
            }
        } )
    );
}

export function getRecipeJournal( recipe, journal ) {
    return when(
        get( baseUrl( `recipes/${recipe.id}/journals/${journal.id}` ) )
    );
}

export function getRecipeJournals( recipe ) {
    return when(
        get( baseUrl( `recipes/${recipe.id}/journals` ) )
    );
}

export function addRecipeJournal( recipe, journal ) {
    return when(
        post( baseUrl( `recipes/${recipe.id}/journals` ), {
            params: {
                journal
            }
        } )
    );
}

export function updateRecipeJournal( recipe, journal ) {
    return when(
        put( baseUrl( `recipes/${recipe.id}/journals/${journal.id}` ), {
            params: {
                journal: journal.journal,
                deleting: {
                    imageables: _( journal.images ).filter( { deleting: true } ).pluck( 'id' ).value()
                }
            }
        } )
    );
}

export function deleteRecipeJournal( recipe, journal ) {
    return when(
        del( baseUrl( `recipes/${recipe.id}/journals/${journal.id}` ) )
    );
}

export function getRecipeJournalComments( recipe, journal ) {
    return when(
        get( baseUrl( `recipes/${recipe.id}/journals/${journal.id}/comments` ) )
    );
}

export function addCommentToRecipeJournal( comment, recipe, recipeJournal ) {
    return when(
        post( baseUrl( `recipes/${recipe.id}/journals/${recipeJournal.id}/comments` ), {
            params: {
                comment
            }
        } )
    );
}

///////////////
/// private

function recipeModelToPacket( recipeModel ) {
    let recipe = recipeModel.recipe;

    return {
        name: recipe.name,
        description: recipe.description,
        notes: recipe.notes,
        visibility: recipe.visibility,

        settings: {
            kohPurity: recipe.kohPurity,
            ratioNaoh: recipe.ratioNaoh,
            ratioKoh: recipe.ratioKoh,
            soapType: recipe.soapType,
            superFat: recipe.superFat,
            totalUom: recipe.totalUom,
            totalWeight: recipe.totalWeight,
            uom: recipe.uom,
            waterRatio: recipe.waterRatio,
            recipeLyeConcentration: recipe.recipeLyeConcentration,
            lyeCalcType: recipe.lyeCalcType,
            lyeWaterLyeRatio: recipe.lyeWaterLyeRatio,
            lyeWaterWaterRatio: recipe.lyeWaterWaterRatio,
            waterDiscount: recipe.waterDiscount,
            fragranceType: recipe.fragranceType,
            fragrancePpo: recipe.fragrancePpo
        },

        oils: _.pluck( recipe.oils, 'id' ),
        weights: recipe.weights,
        summary: recipe.summary,

        deleting: {
            imageables: _( recipe.images ).filter( { deleting: true } ).pluck( 'id' ).value()
        }
    };
}


text:
import when from 'when';

import { post, get } from 'utils/http';
import baseUrl from 'utils/baseUrl';

export function getCurrentUser() {
    return when(
        get( baseUrl( 'auths/current-user' ) )
    );
}

export function signupOrLoginThirdParty( provider, accessToken ) {
    return when(
        post( baseUrl( 'auths' ), {
            params: {
                provider,
                accessToken
            }
        } )
    );
}

export function signupLocal( username, password, email ) {
    return when(
        post( baseUrl( 'auths' ), {
            params: {
                provider: 'local',
                userDetails: {
                    username,
                    password,
                    email
                }
            }
        } )
    );
}

export function loginLocal( username, password ) {
    return when(
        post( baseUrl( 'auths/login' ), {
            params: {
                username,
                password
            }
        } )
    );
}

export function logout() {
    return when(
        post( baseUrl( 'auths/logout' ) )
    );
}

export function usernameExists( username ) {
    return when(
        get( baseUrl( `auths/users/${username}/exists` ) )
    );
}

export function emailExists( email ) {
    return when(
        get( baseUrl( `auths/users/${email}/email` ) )
    );
}


text:
/*=========================================
 * animatedModal.js: Version 1.0
 * author: João Pereira
 * website: http://www.joaopereira.pt  - http://joaopereirawd.github.io/animatedModal.js
 * email: joaopereirawd@gmail.com
 * Licensed MIT
 =========================================*/

(function ($) {

    $.fn.animatedModal = function(options) {
        var self = this;
        var modal = $(this);

        //Defaults
        var settings = $.extend({
            modalTarget:'animatedModal',
            position:'fixed',
            width:'100%',
            height:'100%',
            top:'0px',
            left:'0px',
            zIndexIn: '9999',
            zIndexOut: '-9999',
            color: '#39BEB9',
            opacityIn:'1',
            opacityOut:'0',
            animatedIn:'zoomIn',
            animatedOut:'zoomOut',
            animationDuration:'.6s',
            overflow:'auto',
            // Callbacks
            beforeOpen: function() {},
            afterOpen: function() {},
            beforeClose: function() {},
            afterClose: function() {}

        }, options);

        var closeBt = $('.close-'+settings.modalTarget);

        //console.log(closeBt)

        var href = $(modal).attr('href'),
            id = $('body').find('#'+settings.modalTarget),
            idConc = '#'+id.attr('id');
        //console.log(idConc);
        // Default Classes
        id.addClass('animated');
        id.addClass(settings.modalTarget+'-off');

        //Init styles
        var initStyles = {
            'position':settings.position,
            'width':settings.width,
            'height':settings.height,
            'top':settings.top,
            'left':settings.left,
            'background-color':settings.color,
            'overflow-y':settings.overflow,
            'z-index':settings.zIndexOut,
            'opacity':settings.opacityOut,
            '-webkit-animation-duration':settings.animationDuration
        };
        //Apply stles
        id.css(initStyles);

        modal.click(function(event) {
            event.preventDefault();
            self.open();
        });

        closeBt.click(function(event) {
            event.preventDefault();
            self.close();
        });

        this.open = function() {
            $('body, html').css({'overflow':'hidden'});
            if (href == idConc) {
                if (id.hasClass(settings.modalTarget+'-off')) {
                    id.removeClass(settings.animatedOut);
                    id.removeClass(settings.modalTarget+'-off');
                    id.addClass(settings.modalTarget+'-on');
                }

                if (id.hasClass(settings.modalTarget+'-on')) {
                    settings.beforeOpen();
                    id.css({'opacity':settings.opacityIn,'z-index':settings.zIndexIn});
                    id.addClass(settings.animatedIn);
                    id.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', afterOpen);
                };
            }
        };

        this.close = function() {
            $('body, html').css({'overflow':'auto'});

            settings.beforeClose(); //beforeClose
            if (id.hasClass(settings.modalTarget+'-on')) {
                id.removeClass(settings.modalTarget+'-on');
                id.addClass(settings.modalTarget+'-off');
            }

            if (id.hasClass(settings.modalTarget+'-off')) {
                id.removeClass(settings.animatedIn);
                id.addClass(settings.animatedOut);
                id.one('webkitAnimationEnd mozAnimationEnd MSAnimationEnd oanimationend animationend', afterClose);
            };
        };

        function afterClose () {
            id.css({'z-index':settings.zIndexOut});
            settings.afterClose(); //afterClose
        }

        function afterOpen () {
            settings.afterOpen(); //afterOpen
        }

        return this;

    }; // End animatedModal.js

}(jQuery));


text:
//https://github.com/ifightcrime/bootstrap-growl
(function() {
    var $;

    $ = jQuery;

    $.bootstrapGrowl = function(message, options) {
        var $alert, css, offsetAmount;
        options = $.extend({}, $.bootstrapGrowl.default_options, options);
        $alert = $("
");
        $alert.attr("class", "bootstrap-growl alert");
        if (options.type) {
            $alert.addClass("alert-" + options.type);
        }
        if (options.allowDismiss) {
            $alert.addClass("alert-dismissible");
            $alert.append("<button  class=\"close\" data-dismiss=\"alert\" type=\"button\">×Close</button>");
        }
        $alert.append(message);
        if (options.top_offset) {
            options.offset = {
                from: "top",
                amount: options.top_offset
            };
        }
        offsetAmount = options.offset.amount;
        $(".bootstrap-growl").each(function() {
            return offsetAmount = Math.max(offsetAmount, parseInt($(this).css(options.offset.from)) + $(this).outerHeight() + options.stackupSpacing);
        });
        css = {
            "position": (options.ele === "body" ? "fixed" : "absolute"),
            "margin": 0,
            "z-index": "9999",
            "display": "none"
        };
        css[options.offset.from] = offsetAmount + "px";
        $alert.css(css);
        if (options.width !== "auto") {
            $alert.css("width", options.width + "px");
        }
        $(options.ele).append($alert);
        switch (options.align) {
            case "center":
                $alert.css({
                    "left": "50%",
                    "margin-left": "-" + ($alert.outerWidth() / 2) + "px"
                });
                break;
            case "left":
                $alert.css("left", "20px");
                break;
            default:
                $alert.css("right", "20px");
        }
        $alert.fadeIn();
        if (options.delay > 0) {
            $alert.delay(options.delay).fadeOut(function() {
                return $(this).alert("close");
            });
        }
        return $alert;
    };

    $.bootstrapGrowl.default_options = {
        ele: "body",
        type: "info",
        offset: {
            from: "top",
            amount: 20
        },
        align: "right",
        width: 250,
        delay: 4000,
        allowDismiss: true,
        stackupSpacing: 10
    };

}).call(this);


text:
import React from 'react';

import LocalAuthenticationForm from 'components/localAuthenticationForm';
import ValidateSignupFields from 'services/validateSignupFields';
import authActions from 'actions/auth';

export default React.createClass( {

    getInitialState() {
        return {
            errors: {}
        };
    },

    getDefaultProps() {
        return {
            onSignedUp: () => {}
        };
    },

    render() {
        return (
            <div className="local-signup-form">

                <LocalAuthenticationForm
                    buttonCaption={this.props.buttonCaption || 'Get Started' }
                    errors={ this.state.errors }
                    showEmail={true}
                    onButtonClick={this.signup}
                    />

            </div>
        );
    },

    signup( payload ) {
        this.setState( {
            errors: {}
        } );

        validateSignup.call( this, payload )
            .with( this )
            .then( signupLocal )
            .then( this.props.onSignedUp )
            .catch( setErrors );
    }

} );

//////////////////////

function validateSignup( payload ) {
    return new ValidateSignupFields( payload )
        .execute();
}

function signupLocal( payload ) {
    return authActions.signupLocal( payload.username, payload.password, payload.email );
}

function setErrors( e ) {
    if ( e.name === 'CheckitError' ) { //local validation
        this.setState( {
            errors: e.toJSON()
        } );
    } else if ( e.status === 422 ) { //server validation
        this.setState( {
            errors: e.responseJSON.fields
        } );
    }
}


text:
import _ from 'lodash';
import React from 'react';
import { Link } from 'react-router';

export default React.createClass( {

    render() {
        return (

                        Recipe Name

                    { _.map( this.props.recipes, this.renderRow ) }

        );
    },

    renderRow( recipe ) {
        return (
            <tr key={ `recipe-table-${recipe.id}` }>
                <Link to="recipe" params={ { id: recipe.id } }>{ recipe.name }</Link>

        );
    }

} );


text:
import _ from 'lodash';
import React from 'react';
import Reflux from 'reflux';

import authStore from 'stores/auth';
import recipeJournalsStore from 'stores/recipeJournals';

import RecipeJournalAdd from 'components/recipeJournalAdd';
import RecipeJournalItem from 'components/recipeJournalItem';

export default React.createClass( {

    mixins: [
        Reflux.connect( recipeJournalsStore, 'journals' )
    ],

    render() {
        return (
            <div className="recipe-journals">
                { this.isMyRecipe() &&
                    <RecipeJournalAdd
                        recipe={ this.props.recipe }
                        />
                }

                { _.get( this.state.journals, 'length' ) > 0 && <legend>Latest Journal Entries</legend> }
                { _.map( this.state.journals, this.renderJournal ) }

            </div>
        );
    },

    renderJournal( recipeJournal ) {
        return (
            <RecipeJournalItem
                recipe={ this.props.recipe }
                recipeJournal={ recipeJournal }
                />
        );
    },

    isMyRecipe() {
        return authStore.isAuthenticated()
            && authStore.isMyId( this.props.recipe.user_id );
    }

} );


text:
import React from 'react';
import cx from 'classnames';
import {Link, State} from 'react-router';

export default React.createClass( {

    mixins: [
        State
    ],

    activeClassForTo( to ) {
        return cx( { active: this.isActive( to ) } );
    },

    render() {
        return (

<Link {...this.props}>{this.props.children}</Link>

        );
    }

} );


text:
import React, {findDOMNode} from 'react';
import cx from 'classnames';

export default React.createClass( {

    getInitialState() {
        return {
            entering: true
        };
    },

    componentDidMount() {
        this.$modal = $( this.getDOMNode() )
            .find( '.modal' )
            .modal( {
                backdrop: false,
                keyboard: false
            } );

        $( 'body' )
            .bind( 'mousedown', this.handleMouseClickOutside )
            .bind( 'keyup', this.handleEscapeKey );

        this.props.registerCloseFunction( this.animatedClose );
    },

    componentWillUnmount() {
        $( 'body' )
            .unbind( 'mousedown', this.handleMouseClickOutside )
            .unbind( 'keyup', this.handleEscapeKey );
    },

    render() {
        let modalClasses = cx( 'modal animated', {
            fade: this.state.entering,
            bounceOutUp: !this.state.entering
        } );

        let dialogClasses = cx( 'modal-dialog', {
            'modal-lg': this.props.largeModal,
            'modal-sm': this.props.smallModal
        } );

        return (

                                <button type="button" className="close" onClick={this.animatedClose} aria-label="Close">×</button>
                                { this.props.title &&
{this.props.title}
 }

                            { this.props.children }

        );
    },

    animatedClose() {
        $('body')
            .removeClass( 'modal-open' )
            .css( { 'padding-right': '' } );

        setTimeout( () => {
            this.$modal.modal( 'hide' );
            this.props.closePortal();
        }, 700 );

        this.setState( {
            entering: false
        } );
    },

    handleMouseClickOutside( e ) {
        if ( isNodeInRoot( e.target, findDOMNode( this.refs.content ) ) ) {
            return;
        }

        //account for clicking on scroll bar if modal exceeds window height
        if ( ($( 'body' ).width() - e.pageX) <= getScrollBarWidth() ) {
            return;
        }

        e.preventDefault();
        this.animatedClose();
    },

    handleEscapeKey( e ) {
        if ( e.which === 27 ) { // escape key maps to keycode `27`
            e.preventDefault();
            this.animatedClose();
        }
    }

} );

/////////

function isNodeInRoot( node, root ) {
    while ( node ) {
        if ( node === root ) {
            return true;
        }
        node = node.parentNode;
    }
    return false;
}

function getScrollBarWidth () {
    var $outer = $('
').css({visibility: 'hidden', width: 100, overflow: 'scroll'}).appendTo('body'),
        widthWithScroll = $('
').css({width: '100%'}).appendTo($outer).outerWidth();
    $outer.remove();
    return 100 - widthWithScroll;
}


text:
import _ from 'lodash';
import moment from 'moment';
import React from 'react';
import { Link } from 'react-router';

import MarkedDisplay from 'components/markedDisplay';
import UserAvatar from 'components/userAvatar';

export default React.createClass( {

    render() {
        let comment = this.props.comment;

        return (

                    <UserAvatar
                        user={ comment.user }
                        />

                            <Link to="userProfile" params={ { id: comment.user_id } }>{ comment.user.name }</Link>

                            { moment( comment.created_at ).fromNow() }

                        { this.renderExtraLinks() }

                    <MarkedDisplay
                        content={ comment.comment }
                        />

        );
    },

    renderExtraLinks() {
        function renderLink( link ) {
            return (
                { link }
            );
        }

        return _.map( this.props.links, renderLink, this );
    }

} );


text:
import _ from 'lodash';
import React from 'react';

import AddComment from 'components/addComment';
import Comment from 'components/comment';

export default React.createClass( {

    componentDidMount: function () {
        this.unsubscribe = this.props.store.listen( this.onComments );
    },

    componentWillUnmount: function () {
        this.unsubscribe();
    },

    getInitialState() {
        return {
            comments: null
        };
    },

    render() {
        return (

                <AddComment
                    onNewComment={ this.addComment }
                    />
                { _.get( this.state.comments, 'length' ) > 0 && <legend>Comments</legend> }
                { _.map( this.state.comments, this.renderComment, this ) }

        );
    },

    renderComment( comment ) {
        return (
            <div className="animate zoomIn" key={ `comment-${ comment.id }` }>
                <Comment
                    comment={ comment }
                    />

        );
    },

    onComments: function ( comments ) {
        this.setState( {
            comments
        } );
    },

    addComment( comment ) {
        return this.props.store.addComment( comment );
    }

} );


text:
import _ from 'lodash';
import React from 'react';
import Reflux from 'reflux';
import cx from 'classnames';

import meActions from 'actions/me';
import userNotificationsStore from 'stores/userNotifications';

import UserAvatar from 'components/userAvatar';

export default React.createClass( {

    mixins: [
        Reflux.connect( userNotificationsStore, 'notifications' )
    ],

    render() {
        return (

                    { _.map( this.props.notifications, this.renderNotification ) }

        );
    },

    renderNotification( notification ) {
        let classNames = cx( 'user-notification', notification.user_notifiable_type, {
            unread: notification.read === false
        } );

        return (
            <li className={ classNames } key={ `notification-${notification.id}` }>
                <a href={ `/users/${notification.userNotifiable.user_id}` }>
                    <UserAvatar
                        user={ notification.userNotifiable.user }
                        />

                { this.renderMessageForNotifiable( notification ) }
                { this.renderActionsForNotifiable( notification ) }

        );
    },

    renderMessageForNotifiable( notification ) {
        let message;

        if ( notification.user_notifiable_type === 'friendships' ) {
            message = notification.message;
        } else if ( notification.user_notifiable_type === 'comments' ) {
            message = JSON.parse( notification.message );

            if ( message.commentable === 'recipes' ) {
                message = (

                        { notification.userNotifiable.user.name } commented on your <a href={ `/recipes/${message.recipe.id}` }>{ message.recipe.name } recipe

                );
            } else if ( message.commentable === 'status_updates' ) {
                message = (

                        { notification.userNotifiable.user.name } commented on your <a href={ `/recipes/${message.statusUpdate.id}` }>status update

                );
            } else {
                throw new Error( `Comment commentable type ${message.commentable} is not recognised` );
            }
        }

        return (

                { message }

        );
    },

    renderActionsForNotifiable( notification ) {
        let actions = [];

        if ( !(notification.read) ) {
            if ( notification.user_notifiable_type === 'friendships' ) {
                if ( notification.type === 1 ) {
                    actions.push( <button className="btn btn-success" onClick={ this.confirmFriend( notification ) }> Approve</button> );
                }
                actions.push( <button className="btn btn-warning" onClick={ this.dismissNotification( notification ) }> Dismiss</button> );
            } else if ( notification.user_notifiable_type === 'comments' ) {
                actions.push( <button className="btn btn-warning" onClick={ this.dismissNotification( notification ) }> Dismiss</button> );
            }

            return (

                    { actions }

            );
        }
    },

    confirmFriend( notification ) {
        return () => {
            meActions.addFriend( notification.userNotifiable.user )
                .then( () => meActions.dismissNotification( notification ) );
        };
    },

    dismissNotification( notification ) {
        return () => {
            meActions.dismissNotification( notification );
        };
    }

} );


text:
import _ from 'lodash';
import React from 'react';

import oilsStore from 'stores/oils';
import calculatorStore from 'stores/calculator';

export default React.createClass( {

    shouldComponentUpdate( nextProps ) {
        return !( _.isEqual( this.props, nextProps ) );
    },

    render() {
        let oil = this.props.oil;

        return (

                { oil &&

                                Sap KOH
NaOH
                                {oil.sap}
{calculatorStore.sapForNaOh(oil)}

                                Iodine
                                {oil.iodine}

                                INS
                                {oil.ins}

                            {this.gap()}
                            {this.renderFats()}
                            {this.gap()}
                            {this.renderProperties()}
                            {this.gap()}
                            {this.renderSaturation()}

                }

        );
    },

    gap() {
        return (

        );
    },

    renderFats() {
        let oil = this.props.oil;

        return _.transform( oilsStore.getAllFats(), ( output, fat ) => {
            let breakdown = oil.breakdown[ fat ];

            if ( breakdown ) {
                output.push(

                        {_.capitalize(fat)}
                        {breakdown}%

                );
            }
        }, [] );
    },

    renderProperties() {
        let oil = this.props.oil;

        function render( property ) {
            return (

                    {_.capitalize( property )}
                    {oil.properties[ property ]}%

            );
        }

        return _( oil.properties )
            .keys()
            .sort()
            .map( render, this )
            .value();
    },

    renderSaturation() {
        let oil = this.props.oil;

        return _.map( oil.saturations, ( satType, saturation ) => {
            return (

                    {_.capitalize(saturation)}:
                    {satType}%

            );
        } );
    }

} );


text:
import React from 'react';

export default React.createClass( {

    render() {
        return (

        );
    }

} );


text:
import _ from 'lodash';
import React from 'react';
import cx from 'classnames';
import { Button, Thumbnail } from 'react-bootstrap';

import { imageableThumbUrl } from 'resources/imageable';

export default React.createClass( {

    render() {
        return (

                { _.map( this.props.images, this.renderImage ) }

        );
    },

    renderImage( image ) {
        return (

                <Thumbnail src={ imageableThumbUrl( image ) } className={ cx( { deleting: this.deleting( image ) } ) }>

                        <Button bsStyle='warning'
                                active={ this.deleting( image ) }
                                onClick={ this.markForDelete( image ) }>
                             Delete
                        </Button>

                </Thumbnail>

        );
    },

    deleting( image ) {
        return image.deleting;
    },

    markForDelete( image ) {
        return () => {
            image.deleting = !image.deleting;
            this.forceUpdate();
        };
    }
} );


text:
import React from 'react';

import RecipeBreakdown from 'components/recipeBreakdown';
import RecipeTotals from 'components/recipeTotals';
import RecipeFattyAcids from 'components/recipeFattyAcids';
import RecipeProperties from 'components/recipeProperties';
import MarkedDisplay from 'components/markedDisplay';

export default React.createClass( {

    render() {
        let recipeName =  this.props.recipe.getModelValue( 'name' );
        let recipeDescription = this.props.recipe.getModelValue( 'description');
        let recipeNotes = this.props.recipe.getModelValue( 'notes');

        return (

                    { recipeName &&

{ recipeName }

                    }

                    { recipeDescription &&

                                    <MarkedDisplay content={ recipeDescription } />

                    }

Recipe Oils

                            <RecipeBreakdown
                                recipe={ this.props.recipe }
                                />

Recipe Totals

                            <RecipeTotals
                                recipe={ this.props.recipe }
                                />

Summaries

                            <RecipeProperties
                                recipe={ this.props.recipe }
                                withRange={true}
                                />

Fatty Acids %

                            <RecipeFattyAcids
                                recipe={ this.props.recipe }
                                />

                    { recipeNotes &&

Notes

                                    <MarkedDisplay content={ recipeNotes } />

                    }

        );
    }

} );


text:
import React from 'react';
import Portal from 'react-portal';

import BootstrapModal from 'components/bootstrapModal';

export default React.createClass({

    render() {
        let Modal = this.props.modal;

        return (
            <Portal
                openByClickOn={this.props.elementToClick}
                closeOnEsc={false}
                closeOnOutsideClick={false}
                >
                <BootstrapModal
                    largeModal={this.props.large}
                    title={this.props.title}
                    registerCloseFunction={this.registerCloseFunction}
                    >
                    <Modal
                        action={this.props.action}
                        closeModal={this.closeModal}
                        />
                </BootstrapModal>
            </Portal>
        );
    },

    registerCloseFunction( closeModalFunc ) {
        this.closeModalFunc = closeModalFunc;
    },

    closeModal() {
        this.closeModalFunc();
    }

});


text:
import _ from 'lodash';
import React from 'react';

import { imageableThumbUrl } from 'resources/imageable';

export default React.createClass( {

    render() {
        return (

                    { _.map( this.props.images, this.renderImage ) }

        );
    },

    renderImage( image ) {
        return (

        );
    }

} );


text:
import React from 'react';
import Reflux from 'reflux';
import Griddle from 'griddle-react';
import cx from 'classnames';
import { Link } from 'react-router';

import oilsStore from 'stores/oils';

import GriddlePager from 'components/griddlePager';
import Spinner from 'components/spinner';

let oilLinkColumn = React.createClass( {

    render() {
        return (
            <Link to="oil" params={ { id: this.props.rowData.id } }>{ this.props.rowData.name }</Link>
        );
    }
} );

export default React.createClass( {

    mixins:[
        Reflux.connect( oilsStore, 'oils' )
    ],

    getInitialState() {
        return {
            gridColumns: 'fats-common'
        };
    },

    render() {
        let data;

        if ( this.state.oils.length > 0 ) {
            data = oilsStore.getFlatOilProperties();

            return (

                    {this.renderColumnButtons()}

                        <Griddle
                            results={data}
                            tableClassName="table table-striped table-hover table-bordered table-condensed"
                            columns={ this.getViewColumns() }
                            columnMetadata={ this.defineSelectedMetadata() }
                            useGriddleStyles={false}
                            showFilter={true}
                            showSettings={true}
                            initialSort="name"
                            resultsPerPage="150"
                            settingsText="Select Columns"
                            settingsToggleClassName="btn btn-default btn-sm"
                            useCustomPagerComponent="true"
                            customPagerComponent={GriddlePager}
                            />

            );
        } else {
            return <Spinner />;
        }
    },

    renderColumnButtons() {
        let fattyClass    = cx( 'btn btn-default btn-sm', { active: this.state.gridColumns === 'fats-common' } );
        let fattyAllClass = cx( 'btn btn-default btn-sm', { active: this.state.gridColumns === 'fats-all' } );
        let propClass     =  cx( 'btn btn-default btn-sm', { active: this.state.gridColumns === 'properties' } );
        let saturationClass     =  cx( 'btn btn-default btn-sm', { active: this.state.gridColumns === 'saturation' } );

        return (

                    <button className={propClass} onClick={this.switchViewTo('properties')}>Properties</button>
                    <button className={saturationClass} onClick={this.switchViewTo('saturation')}>Saturates</button>
                    <button className={fattyClass} onClick={this.switchViewTo('fats-common')}>Fatty Acids - Common</button>
                    <button className={fattyAllClass} onClick={this.switchViewTo('fats-all')}>Fatty Acids - All</button>

        );
    },

    switchViewTo( view ) {
        return () => {
            this.setState( {
                gridColumns: view
            } );
        };
    },

    getViewColumns() {
        let columns = oilsStore.oilPropertyGroupings();

        return columns[ this.state.gridColumns ];
    },

    defineSelectedMetadata() {
        return [
            {
                columnName: 'name',
                customComponent: oilLinkColumn
            },
            {
                columnName: 'monoSaturated',
                displayName: 'mono-unsaturated %'
            },
            {
                columnName: 'polySaturated',
                displayName: 'poly-unsaturated %'
            }
        ];
    }

} );


text:
import _ from 'lodash';
import React from 'react';

export default React.createClass( {

    render() {
        return (

                    { this.renderOrderedBreakdowns() }

        );
    },

    renderOrderedBreakdowns() {
        let breakdowns = this.props.recipe.getModelValue( 'summary.breakdowns' );

        return _( breakdowns )
            .keys()
            .sort()
            .map( fattyAcid => {
                let value = _.round( breakdowns[ fattyAcid ] );

                if (  value ) {
                    return (
                        <tr key={ `fatty-acid-prop-${ fattyAcid }` }>
                            {_.capitalize( fattyAcid )}
                            {value}

                    );
                }
            } )
            .compact()
            .value();
    }

} );