<commit_before>function generate (inputs) {

    var previous_autotune = inputs.previous_autotune;
    var pumpprofile = inputs.pumpprofile;
    var pumpbasalprofile = pumpprofile.basalprofile;
    //console.error(pumpbasalprofile);
    var basalprofile = previous_autotune.basalprofile;
    //console.error(basalprofile);
    var isf_profile = previous_autotune.isfProfile;
    //console.error(isf_profile);
    var isf = isf_profile.sensitivities[0].sensitivity;
    //console.error(isf);
    var carb_ratio = previous_autotune.carb_ratio;
    //console.error(carb_ratio);
    var csf = isf / carb_ratio;
    // conditional on there being a pump profile; if not then skip
    if (pumpprofile) { pump_isf_profile = pumpprofile.isfProfile; }
    if (pump_isf_profile && pump_isf_profile.sensitivities[0]) {
        pumpISF = pump_isf_profile.sensitivities[0].sensitivity;
        pump_carb_ratio = pumpprofile.carb_ratio;
        pumpCSF = pumpISF / pump_carb_ratio;
    }
    //console.error(csf);
    var prepped_glucose = inputs.prepped_glucose;
    var csf_glucose = prepped_glucose.csf_glucose_data;
    //console.error(csf_glucose[0]);
    var isf_glucose = prepped_glucose.isf_glucose_data;
    //console.error(isf_glucose[0]);
    var basal_glucose = prepped_glucose.basal_glucose_data;
    //console.error(basal_glucose[0]);

    // convert the basal profile to hourly if it isn't already
    hourlybasalprofile = [];
    hourlypumpprofile = [];
    for (var i=0; i < 24; i++) {
        // aututuned basal profile
        for (var j=0; j < basalprofile.length; ++j) {
            if (basalprofile[j].minutes <= i * 60) {
                hourlybasalprofile[i] = JSON.parse(JSON.stringify(basalprofile[j]));
            }
        }
        hourlybasalprofile[i].i=i;
        hourlybasalprofile[i].minutes=i*60;
        hourlybasalprofile[i].rate=Math.round(hourlybasalprofile[i].rate*1000)/1000
        // pump basal profile
        if (pumpbasalprofile && pumpbasalprofile[0]) {
            for (var j=0; j < pumpbasalprofile.length; ++j) {
                //console.error(pumpbasalprofile[j]);
                if (pumpbasalprofile[j].minutes <= i * 60) {
                    hourlypumpprofile[i] = JSON.parse(JSON.stringify(pumpbasalprofile[j]));
                }
            }
            hourlypumpprofile[i].i=i;
            hourlypumpprofile[i].minutes=i*60;
            hourlypumpprofile[i].rate=Math.round(hourlypumpprofile[i].rate*1000)/1000
        }
    }
    //console.error(hourlypumpprofile);
    //console.error(hourlybasalprofile);

    // look at net deviations for each hour
    for (var hour=0; hour < 24; hour++) {
        var deviations = 0;
        for (var i=0; i < basal_glucose.length; ++i) {
            //console.error(basal_glucose[i].dateString);
            splitString = basal_glucose[i].dateString.split("T");
            timeString = splitString[1];
            splitTime = timeString.split(":");
            myHour = parseInt(splitTime[0]);
            if (hour == myHour) {
                //console.error(basal_glucose[i].deviation);
                deviations += parseFloat(basal_glucose[i].deviation);
            }
        }
        deviations = Math.round( deviations * 1000 ) / 1000
        //console.error("Hour",hour.toString(),"total deviations:",deviations,"mg/dL");
        // calculate how much less or additional basal insulin would have been required to eliminate the deviations
        // only apply 20% of the needed adjustment to keep things relatively stable
        basalNeeded = 0.2 * deviations / isf;
        basalNeeded = Math.round( basalNeeded * 1000 ) / 1000
        // if basalNeeded is positive, adjust each of the 1-3 hour prior basals by 10% of the needed adjustment
        console.error("Hour",hour,"basal adjustment needed:",basalNeeded,"U/hr");
        if (basalNeeded > 0 ) {
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                //console.error(offsetHour);
                hourlybasalprofile[offsetHour].rate += basalNeeded / 3;
                hourlybasalprofile[offsetHour].rate=Math.round(hourlybasalprofile[offsetHour].rate*1000)/1000
            }
        // otherwise, figure out the percentage reduction required to the 1-3 hour prior basals
        // and adjust all of them downward proportionally
        } else if (basalNeeded < 0) {
            var threeHourBasal = 0;
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                threeHourBasal += hourlybasalprofile[offsetHour].rate;
            }
            var adjustmentRatio = 1.0 + basalNeeded / threeHourBasal;
            console.error(adjustmentRatio);
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                hourlybasalprofile[offsetHour].rate = hourlybasalprofile[offsetHour].rate * adjustmentRatio;
                hourlybasalprofile[offsetHour].rate=Math.round(hourlybasalprofile[offsetHour].rate*1000)/1000
            }
        }
    }
    if (pumpbasalprofile && pumpbasalprofile[0]) {
        for (var hour=0; hour < 24; hour++) {
            //console.error(hourlybasalprofile[hour],hourlypumpprofile[hour].rate*1.2);
            // 20% caps
            var maxrate = hourlypumpprofile[hour].rate * 1.2;
            var minrate = hourlypumpprofile[hour].rate / 1.2;
            if (hourlybasalprofile[hour].rate > maxrate ) {
                console.error("Limiting hour",hour,"basal to",maxrate.toFixed(2),"(which is 20% above pump basal of",hourlypumpprofile[hour].rate,")");
                hourlybasalprofile[hour].rate = maxrate;
            } else if (hourlybasalprofile[hour].rate < minrate ) {
                console.error("Limiting hour",hour,"basal to",minrate.toFixed(2),"(which is 20% below pump basal of",hourlypumpprofile[hour].rate,")");
                hourlybasalprofile[hour].rate = minrate;
            }
            hourlybasalprofile[hour].rate = Math.round(hourlybasalprofile[hour].rate*1000)/1000;
        }
    }

    console.error(hourlybasalprofile);
    basalprofile = hourlybasalprofile;

    // calculate median deviation and bgi in data attributable to ISF
    var deviations = [];
    var bgis = [];
    var avgDeltas = [];
    var ratios = [];
    var count = 0;
    for (var i=0; i < isf_glucose.length; ++i) {
       deviation = parseFloat(isf_glucose[i].deviation);
       deviations.push(deviation);
       bgi = parseFloat(isf_glucose[i].bgi);
       bgis.push(bgi);
       avgDelta = parseFloat(isf_glucose[i].avgDelta);
       avgDeltas.push(avgDelta);
       ratio = 1 + deviation / bgi;
       //console.error("Deviation:",deviation,"BGI:",bgi,"avgDelta:",avgDelta,"ratio:",ratio);
       ratios.push(ratio);
       count++;
    }
    avgDeltas.sort(function(a, b){return a-b});
    bgis.sort(function(a, b){return a-b});
    deviations.sort(function(a, b){return a-b});
    ratios.sort(function(a, b){return a-b});
    p50deviation = percentile(deviations, 0.50);
    p50bgi = percentile(bgis, 0.50);
    p50ratios = Math.round( percentile(ratios, 0.50) * 1000)/1000;

    // calculate what adjustments to ISF would have been necessary to bring median deviation to zero
    fullNewISF = isf * p50ratios;
    fullNewISF = Math.round( fullNewISF * 1000 ) / 1000;
    // and apply 10% of that adjustment
    var newISF = ( 0.9 * isf ) + ( 0.1 * fullNewISF );
    if (typeof(pumpISF) !== 'undefined') {
        var maxISF = pumpISF * 1.2;
        var minISF = pumpISF / 1.2;
        if (newISF > maxISF) {
            console.error("Limiting ISF to",maxISF.toFixed(2),"(which is 20% above pump ISF of",pumpISF,")");
            newISF = maxISF;
        } else if (newISF < minISF) {
            console.error("Limiting ISF to",minISF.toFixed(2),"(which is 20% below pump ISF of",pumpISF,")");
            newISF = minISF;
        }
    }
    newISF = Math.round( newISF * 1000 ) / 1000;
    //console.error(avgRatio);
    //console.error(newISF);
    console.error("p50deviation:",p50deviation,"p50bgi",p50bgi,"p50ratios:",p50ratios,"Old ISF:",isf,"fullNewISF:",fullNewISF,"newISF:",newISF);

    isf = newISF;

    // calculate net deviations while carbs are absorbing
    // measured from carb entry until COB and deviations both drop to zero

    var deviations = 0;
    var mealCarbs = 0;
    var totalMealCarbs = 0;
    var totalDeviations = 0;
    //console.error(csf_glucose[0].mealAbsorption);
    //console.error(csf_glucose[0]);
    for (var i=0; i < csf_glucose.length; ++i) {
        //console.error(csf_glucose[i].mealAbsorption, i);
        if ( csf_glucose[i].mealAbsorption === "start" ) {
            deviations = 0;
            mealCarbs = parseInt(csf_glucose[i].mealCarbs);
        } else if (csf_glucose[i].mealAbsorption === "end") {
            deviations += parseFloat(csf_glucose[i].deviation);
            // compare the sum of deviations from start to end vs. current CSF * mealCarbs
            //console.error(csf,mealCarbs);
            csfRise = csf * mealCarbs;
            //console.error(deviations,isf);
            console.error("csfRise:",csfRise,"deviations:",deviations);
            totalMealCarbs += mealCarbs;
            totalDeviations += deviations;

        } else {
            deviations += Math.max(0*previous_autotune.min_5m_carbimpact,parseFloat(csf_glucose[i].deviation));
            mealCarbs = Math.max(mealCarbs, parseInt(csf_glucose[i].mealCarbs));
        }
    }
    if (totalMealCarbs == 0) { totalMealCarbs += mealCarbs; }
    if (totalDeviations == 0) { totalDeviations += deviations; }
    //console.error(totalDeviations, totalMealCarbs);
    if (totalMealCarbs == 0) {
        fullNewCSF = csf;
    } else {
        fullNewCSF = Math.round( (totalDeviations / totalMealCarbs)*100 )/100;
    }
    newCSF = ( 0.9 * csf ) + ( 0.1 * fullNewCSF );
    if (typeof(pumpCSF) !== 'undefined') {
        var maxCSF = pumpCSF * 1.2;
        var minCSF = pumpCSF / 1.2;
        if (newCSF > maxCSF) {
            console.error("Limiting CSF to",maxCSF.toFixed(2),"(20% above pump CSF of",pumpCSF,")");
            newCSF = maxCSF;
        } else if (newCSF < minCSF) {
            console.error("Limiting CSF to",minCSF.toFixed(2),"(20% below pump CSF of",pumpCSF,")");
            newCSF = minCSF;
        } else { console.error("newCSF",newCSF,"is within 20% of",pumpCSF); }
    }
    newCSF = Math.round( newCSF * 1000 ) / 1000;
    console.error("totalMealCarbs:",totalMealCarbs,"totalDeviations:",totalDeviations,"fullNewCSF:",fullNewCSF,"newCSF:",newCSF);
    csf = newCSF;

    // reconstruct updated version of previous_autotune as autotune_output
    autotune_output = previous_autotune;
    autotune_output.basalprofile = basalprofile;
    isf_profile.sensitivities[0].sensitivity = isf;
    autotune_output.isfProfile = isf_profile;
    autotune_output.csf = csf;
    carb_ratio = isf / csf;
    carb_ratio = Math.round( carb_ratio * 1000 ) / 1000;
    autotune_output.carb_ratio = carb_ratio;

    return autotune_output;
}

exports = module.exports = generate;

// From https://gist.github.com/IceCreamYou/6ffa1b18c4c8f6aeaad2
// Returns the value at a given percentile in a sorted numeric array.
// "Linear interpolation between closest ranks" method
function percentile(arr, p) {
    if (arr.length === 0) return 0;
    if (typeof p !== 'number') throw new TypeError('p must be a number');
    if (p <= 0) return arr[0];
    if (p >= 1) return arr[arr.length - 1];

    var index = arr.length * p,
        lower = Math.floor(index),
        upper = lower + 1,
        weight = index % 1;

    if (upper >= arr.length) return arr[lower];
    return arr[lower] * (1 - weight) + arr[upper] * weight;
}

// Returns the percentile of the given value in a sorted numeric array.
function percentRank(arr, v) {
    if (typeof v !== 'number') throw new TypeError('v must be a number');
    for (var i = 0, l = arr.length; i < l; i++) {
        if (v <= arr[i]) {
            while (i < l && v === arr[i]) i++;
            if (i === 0) return 0;
            if (v !== arr[i-1]) {
                i += (v - arr[i-1]) / (arr[i] - arr[i-1]);
            }
            return i / l;
        }
    }
    return 1;
}
<commit_msg>Adding to-do about dinner carbs not absorbed at midnight<commit_after>function generate (inputs) {

    var previous_autotune = inputs.previous_autotune;
    var pumpprofile = inputs.pumpprofile;
    var pumpbasalprofile = pumpprofile.basalprofile;
    //console.error(pumpbasalprofile);
    var basalprofile = previous_autotune.basalprofile;
    //console.error(basalprofile);
    var isf_profile = previous_autotune.isfProfile;
    //console.error(isf_profile);
    var isf = isf_profile.sensitivities[0].sensitivity;
    //console.error(isf);
    var carb_ratio = previous_autotune.carb_ratio;
    //console.error(carb_ratio);
    var csf = isf / carb_ratio;
    // conditional on there being a pump profile; if not then skip
    if (pumpprofile) { pump_isf_profile = pumpprofile.isfProfile; }
    if (pump_isf_profile && pump_isf_profile.sensitivities[0]) {
        pumpISF = pump_isf_profile.sensitivities[0].sensitivity;
        pump_carb_ratio = pumpprofile.carb_ratio;
        pumpCSF = pumpISF / pump_carb_ratio;
    }
    //console.error(csf);
    var prepped_glucose = inputs.prepped_glucose;
    var csf_glucose = prepped_glucose.csf_glucose_data;
    //console.error(csf_glucose[0]);
    var isf_glucose = prepped_glucose.isf_glucose_data;
    //console.error(isf_glucose[0]);
    var basal_glucose = prepped_glucose.basal_glucose_data;
    //console.error(basal_glucose[0]);

    // convert the basal profile to hourly if it isn't already
    hourlybasalprofile = [];
    hourlypumpprofile = [];
    for (var i=0; i < 24; i++) {
        // aututuned basal profile
        for (var j=0; j < basalprofile.length; ++j) {
            if (basalprofile[j].minutes <= i * 60) {
                hourlybasalprofile[i] = JSON.parse(JSON.stringify(basalprofile[j]));
            }
        }
        hourlybasalprofile[i].i=i;
        hourlybasalprofile[i].minutes=i*60;
        hourlybasalprofile[i].rate=Math.round(hourlybasalprofile[i].rate*1000)/1000
        // pump basal profile
        if (pumpbasalprofile && pumpbasalprofile[0]) {
            for (var j=0; j < pumpbasalprofile.length; ++j) {
                //console.error(pumpbasalprofile[j]);
                if (pumpbasalprofile[j].minutes <= i * 60) {
                    hourlypumpprofile[i] = JSON.parse(JSON.stringify(pumpbasalprofile[j]));
                }
            }
            hourlypumpprofile[i].i=i;
            hourlypumpprofile[i].minutes=i*60;
            hourlypumpprofile[i].rate=Math.round(hourlypumpprofile[i].rate*1000)/1000
        }
    }
    //console.error(hourlypumpprofile);
    //console.error(hourlybasalprofile);

    // look at net deviations for each hour
    for (var hour=0; hour < 24; hour++) {
        var deviations = 0;
        for (var i=0; i < basal_glucose.length; ++i) {
            //console.error(basal_glucose[i].dateString);
            splitString = basal_glucose[i].dateString.split("T");
            timeString = splitString[1];
            splitTime = timeString.split(":");
            myHour = parseInt(splitTime[0]);
            if (hour == myHour) {
                //console.error(basal_glucose[i].deviation);
                deviations += parseFloat(basal_glucose[i].deviation);
            }
        }
        deviations = Math.round( deviations * 1000 ) / 1000
        //console.error("Hour",hour.toString(),"total deviations:",deviations,"mg/dL");
        // calculate how much less or additional basal insulin would have been required to eliminate the deviations
        // only apply 20% of the needed adjustment to keep things relatively stable
        basalNeeded = 0.2 * deviations / isf;
        basalNeeded = Math.round( basalNeeded * 1000 ) / 1000
        // if basalNeeded is positive, adjust each of the 1-3 hour prior basals by 10% of the needed adjustment
        console.error("Hour",hour,"basal adjustment needed:",basalNeeded,"U/hr");
        if (basalNeeded > 0 ) {
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                //console.error(offsetHour);
                hourlybasalprofile[offsetHour].rate += basalNeeded / 3;
                hourlybasalprofile[offsetHour].rate=Math.round(hourlybasalprofile[offsetHour].rate*1000)/1000
            }
        // otherwise, figure out the percentage reduction required to the 1-3 hour prior basals
        // and adjust all of them downward proportionally
        } else if (basalNeeded < 0) {
            var threeHourBasal = 0;
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                threeHourBasal += hourlybasalprofile[offsetHour].rate;
            }
            var adjustmentRatio = 1.0 + basalNeeded / threeHourBasal;
            console.error(adjustmentRatio);
            for (var offset=-3; offset < 0; offset++) {
                offsetHour = hour + offset;
                if (offsetHour < 0) { offsetHour += 24; }
                hourlybasalprofile[offsetHour].rate = hourlybasalprofile[offsetHour].rate * adjustmentRatio;
                hourlybasalprofile[offsetHour].rate=Math.round(hourlybasalprofile[offsetHour].rate*1000)/1000
            }
        }
    }
    if (pumpbasalprofile && pumpbasalprofile[0]) {
        for (var hour=0; hour < 24; hour++) {
            //console.error(hourlybasalprofile[hour],hourlypumpprofile[hour].rate*1.2);
            // 20% caps
            var maxrate = hourlypumpprofile[hour].rate * 1.2;
            var minrate = hourlypumpprofile[hour].rate / 1.2;
            if (hourlybasalprofile[hour].rate > maxrate ) {
                console.error("Limiting hour",hour,"basal to",maxrate.toFixed(2),"(which is 20% above pump basal of",hourlypumpprofile[hour].rate,")");
                hourlybasalprofile[hour].rate = maxrate;
            } else if (hourlybasalprofile[hour].rate < minrate ) {
                console.error("Limiting hour",hour,"basal to",minrate.toFixed(2),"(which is 20% below pump basal of",hourlypumpprofile[hour].rate,")");
                hourlybasalprofile[hour].rate = minrate;
            }
            hourlybasalprofile[hour].rate = Math.round(hourlybasalprofile[hour].rate*1000)/1000;
        }
    }

    console.error(hourlybasalprofile);
    basalprofile = hourlybasalprofile;

    // calculate median deviation and bgi in data attributable to ISF
    var deviations = [];
    var bgis = [];
    var avgDeltas = [];
    var ratios = [];
    var count = 0;
    for (var i=0; i < isf_glucose.length; ++i) {
       deviation = parseFloat(isf_glucose[i].deviation);
       deviations.push(deviation);
       bgi = parseFloat(isf_glucose[i].bgi);
       bgis.push(bgi);
       avgDelta = parseFloat(isf_glucose[i].avgDelta);
       avgDeltas.push(avgDelta);
       ratio = 1 + deviation / bgi;
       //console.error("Deviation:",deviation,"BGI:",bgi,"avgDelta:",avgDelta,"ratio:",ratio);
       ratios.push(ratio);
       count++;
    }
    avgDeltas.sort(function(a, b){return a-b});
    bgis.sort(function(a, b){return a-b});
    deviations.sort(function(a, b){return a-b});
    ratios.sort(function(a, b){return a-b});
    p50deviation = percentile(deviations, 0.50);
    p50bgi = percentile(bgis, 0.50);
    p50ratios = Math.round( percentile(ratios, 0.50) * 1000)/1000;

    // calculate what adjustments to ISF would have been necessary to bring median deviation to zero
    fullNewISF = isf * p50ratios;
    fullNewISF = Math.round( fullNewISF * 1000 ) / 1000;
    // and apply 10% of that adjustment
    var newISF = ( 0.9 * isf ) + ( 0.1 * fullNewISF );
    if (typeof(pumpISF) !== 'undefined') {
        var maxISF = pumpISF * 1.2;
        var minISF = pumpISF / 1.2;
        if (newISF > maxISF) {
            console.error("Limiting ISF to",maxISF.toFixed(2),"(which is 20% above pump ISF of",pumpISF,")");
            newISF = maxISF;
        } else if (newISF < minISF) {
            console.error("Limiting ISF to",minISF.toFixed(2),"(which is 20% below pump ISF of",pumpISF,")");
            newISF = minISF;
        }
    }
    newISF = Math.round( newISF * 1000 ) / 1000;
    //console.error(avgRatio);
    //console.error(newISF);
    console.error("p50deviation:",p50deviation,"p50bgi",p50bgi,"p50ratios:",p50ratios,"Old ISF:",isf,"fullNewISF:",fullNewISF,"newISF:",newISF);

    isf = newISF;

    // calculate net deviations while carbs are absorbing
    // measured from carb entry until COB and deviations both drop to zero

    var deviations = 0;
    var mealCarbs = 0;
    var totalMealCarbs = 0;
    var totalDeviations = 0;
    //console.error(csf_glucose[0].mealAbsorption);
    //console.error(csf_glucose[0]);
    for (var i=0; i < csf_glucose.length; ++i) {
        //console.error(csf_glucose[i].mealAbsorption, i);
        if ( csf_glucose[i].mealAbsorption === "start" ) {
            deviations = 0;
            mealCarbs = parseInt(csf_glucose[i].mealCarbs);
        } else if (csf_glucose[i].mealAbsorption === "end") {
            deviations += parseFloat(csf_glucose[i].deviation);
            // compare the sum of deviations from start to end vs. current CSF * mealCarbs
            //console.error(csf,mealCarbs);
            csfRise = csf * mealCarbs;
            //console.error(deviations,isf);
            console.error("csfRise:",csfRise,"deviations:",deviations);
            totalMealCarbs += mealCarbs;
            totalDeviations += deviations;

        } else {
            deviations += Math.max(0*previous_autotune.min_5m_carbimpact,parseFloat(csf_glucose[i].deviation));
            mealCarbs = Math.max(mealCarbs, parseInt(csf_glucose[i].mealCarbs));
        }
    }
    // at midnight, write down the mealcarbs as total meal carbs (to prevent special case of when only one meal and it not finishing absorbing by midnight)
    // TODO: figure out what to do with dinner carbs that don't finish absorbing by midnight
    if (totalMealCarbs == 0) { totalMealCarbs += mealCarbs; }
    if (totalDeviations == 0) { totalDeviations += deviations; }
    //console.error(totalDeviations, totalMealCarbs);
    if (totalMealCarbs == 0) {
        fullNewCSF = csf;
    } else {
        fullNewCSF = Math.round( (totalDeviations / totalMealCarbs)*100 )/100;
    }
    newCSF = ( 0.9 * csf ) + ( 0.1 * fullNewCSF );
    if (typeof(pumpCSF) !== 'undefined') {
        var maxCSF = pumpCSF * 1.2;
        var minCSF = pumpCSF / 1.2;
        if (newCSF > maxCSF) {
            console.error("Limiting CSF to",maxCSF.toFixed(2),"(20% above pump CSF of",pumpCSF,")");
            newCSF = maxCSF;
        } else if (newCSF < minCSF) {
            console.error("Limiting CSF to",minCSF.toFixed(2),"(20% below pump CSF of",pumpCSF,")");
            newCSF = minCSF;
        } else { console.error("newCSF",newCSF,"is within 20% of",pumpCSF); }
    }
    newCSF = Math.round( newCSF * 1000 ) / 1000;
    console.error("totalMealCarbs:",totalMealCarbs,"totalDeviations:",totalDeviations,"fullNewCSF:",fullNewCSF,"newCSF:",newCSF);
    csf = newCSF;

    // reconstruct updated version of previous_autotune as autotune_output
    autotune_output = previous_autotune;
    autotune_output.basalprofile = basalprofile;
    isf_profile.sensitivities[0].sensitivity = isf;
    autotune_output.isfProfile = isf_profile;
    autotune_output.csf = csf;
    carb_ratio = isf / csf;
    carb_ratio = Math.round( carb_ratio * 1000 ) / 1000;
    autotune_output.carb_ratio = carb_ratio;

    return autotune_output;
}

exports = module.exports = generate;

// From https://gist.github.com/IceCreamYou/6ffa1b18c4c8f6aeaad2
// Returns the value at a given percentile in a sorted numeric array.
// "Linear interpolation between closest ranks" method
function percentile(arr, p) {
    if (arr.length === 0) return 0;
    if (typeof p !== 'number') throw new TypeError('p must be a number');
    if (p <= 0) return arr[0];
    if (p >= 1) return arr[arr.length - 1];

    var index = arr.length * p,
        lower = Math.floor(index),
        upper = lower + 1,
        weight = index % 1;

    if (upper >= arr.length) return arr[lower];
    return arr[lower] * (1 - weight) + arr[upper] * weight;
}

// Returns the percentile of the given value in a sorted numeric array.
function percentRank(arr, v) {
    if (typeof v !== 'number') throw new TypeError('v must be a number');
    for (var i = 0, l = arr.length; i < l; i++) {
        if (v <= arr[i]) {
            while (i < l && v === arr[i]) i++;
            if (i === 0) return 0;
            if (v !== arr[i-1]) {
                i += (v - arr[i-1]) / (arr[i] - arr[i-1]);
            }
            return i / l;
        }
    }
    return 1;
}
<|endoftext|>


<commit_before>// BRICO
var through = require('through')
var fern = require('fern')

module.exports = Bricoleur

function Bricoleur (opts) {
  if (!opts) opts = {}
  var self = this

  this.addSocket = function (id, socketAdded) { // direct communication layer
    self[id] = through(SocWrite, SocEnd, {autoDestroy:false})
    self[id].key = id
    self[id].pipe(self.api).pipe(through(function write (chunk) {
      console.log(chunk)
    }, function () {
      this.emit('end')
    }))

    socketAdded()
  }

  this.removeSocket = function (id) {
    self[id].destroy()
    delete self[id]
  }

  function SocWrite (chunk) {
    // route to function ?!?
    console.log(chunk)
    this.emit('data',chunk)
  }

  function SocEnd () {
    var k = this.key
    this.emit('end')
    console.log('closed socket: '+k)
  }

  this.api = new fern({prop:'api',struct:API})

  var API = {
    test: function (msg,cb) {
      console.log(msg)
      if (process.browser) window.document.title = obj.msg
      cb(msg)
    },
    loadEnv: function (user,cb) {
    },
    make: function (mod,cb) {
      var libName = mod.id.toUpperCase()
      var lib = require(libName)
      _[mod.id] = new lib()
      if (process.browser && mod.html) _[mod.id].render(html)
      // verify module creation somehow
    },
    unmake: function (mod,cb) {
      _[mod.id].destroy()
    },
    conn: function (conss,cb) { // fix ugliness
      conns.forEach(function handleConnection (conn) {
        if (conn.match(/\+/) !== null) {
          var modA = conn.split('+')[0]
          , modB = conn.split('+')[1]
          _[modA].pipe(_[modB])
        }
        if (conn.match(/\-/) !== null) {
          var modA = conn.split('-')[0]
          , modB = conn.split('-')[1]
          modA.unpipe(modB)
        }
      })
    }
  }
}
<commit_msg>update for new fern opts<commit_after>// BRICO
var through = require('through')
var fern = require('fern')

module.exports = Bricoleur

function Bricoleur (opts) {
  if (!opts) opts = {}
  var self = this

  this.addSocket = function (id, socketAdded) { // direct communication layer
    self[id] = through(SocWrite, SocEnd, {autoDestroy:false})
    self[id].key = id
    self[id].pipe(self.api).pipe(through(function write (chunk) {
      console.log(chunk)
    }, function () {
      this.emit('end')
    }))

    socketAdded()
  }

  this.removeSocket = function (id) {
    self[id].destroy()
    delete self[id]
  }

  function SocWrite (chunk) {
    // route to function ?!?
    console.log(chunk)
    this.emit('data',chunk)
  }

  function SocEnd () {
    var k = this.key
    this.emit('end')
    console.log('closed socket: '+k)
  }

  var API = {
    test: function (msg,cb) {
      console.log(msg)
      if (process.browser) window.document.title = msg
      cb(msg)
    },
    loadEnv: function (user,cb) {
    },
    make: function (mod,cb) {
      var libName = mod.id.toUpperCase()
      var lib = require(libName)
      _[mod.id] = new lib()
      if (process.browser && mod.html) _[mod.id].render(html)
      // verify module creation somehow
    },
    unmake: function (mod,cb) {
      _[mod.id].destroy()
    },
    conn: function (conss,cb) { // fix ugliness
      conns.forEach(function handleConnection (conn) {
        if (conn.match(/\+/) !== null) {
          var modA = conn.split('+')[0]
          , modB = conn.split('+')[1]
          _[modA].pipe(_[modB])
        }
        if (conn.match(/\-/) !== null) {
          var modA = conn.split('-')[0]
          , modB = conn.split('-')[1]
          modA.unpipe(modB)
        }
      })
    }
  }

  this.api = new fern({key:'api',tree:API})
}
<|endoftext|>


<commit_before>function TreeCtrl($scope){

	var elem = document.getElementById('canvas');
	var context = elem.getContext('2d');

	context.fillStyle   = '#000';
	context.lineWidth   = 1;

	// Set these variables and open the file to see your tree
	$scope.treeVars = {
		initialAngle : -82, // Initial angle of the trunk
		length : 135, // Length of the first stem
		width : 17, // Width of the first stem
		leftAlpha : 1.8,
		rightAlpha : 2.2,
		leftAngle : 19, // Left delta
		rightAngle : 31, // Right delta
		level : 16,  // Level of recursion
	}

	/*  Here are some examples of interesting inputs

	tree           		initilAngle 	length 	width 	leftAlpha 	rightAlpha 	leftAngle 	rightAngle 	level
	Square				-90				250		35		1.2 		1.2 		90 			90			12
	Symetrical 			-90 			180 	21 		1.2 		1.2 		25 			25 			12
	Tree with foilage	-82				135		13 		1.8 		2.2 		19 			31			16
	Bronchial			-90 			90 		10 		3.0 		3.0 		33 			33 			10
	One-Sided			-95				150 	12 		1.8 		0.002 		30 			0 			16
	Goofy				-90				236		45 		1.2 		2.4 		48			85			12 // and set rightLengthFactor to equal the leftAlpha

	*/

	var deg_to_rad = Math.PI / 180.0;

	var repeatCounter = 0;
	var lineDrawn = false;
	var timerId = undefined;
	var timerId2 = undefined;
	var timerId3 = undefined;
	var drawing = false;

	function calcFactors(){
		$scope.leftWidthFactor = Math.pow(2, -1/$scope.treeVars.leftAlpha);
		$scope.rightWidthFactor = Math.pow(2, -1/$scope.treeVars.rightAlpha);
		$scope.leftLenghtFactor = Math.pow(2, -2/(3*$scope.treeVars.leftAlpha));
		$scope.rightLengthFactor = Math.pow(2, -2/(3*$scope.treeVars.right12Alpha)); // For expected tree
	//	$scope.rightLengthFactor = Math.pow(2, -2/(3*$scope.treeVars.leftAlpha)); // For goofy tree
	}

	function drawLine(x1, y1, x2, y2, width){
		context.beginPath();
		context.lineWidth = width;
		context.moveTo(x1, y1);
		context.lineTo(x2, y2);
		context.closePath();
		context.stroke();
		// context.rect(x2, y2, width, width);
		//context.arc(x2, y2, width/1.5, 0, 2*Math.PI);
		// context.fill();
	}

	function drawTree(x1, y1, width, length, angle, level){
		if ((level != 0) && (drawing == true)) {
			var x2 = x1 + (Math.cos(angle * deg_to_rad) * length);
			var y2 = y1 + (Math.sin(angle * deg_to_rad) * length);
			drawLine(x1, y1, x2, y2, width);
			timerId = setTimeout(function () {
				drawTree(x2, y2, width*$scope.leftWidthFactor, length*$scope.leftLenghtFactor, angle - $scope.treeVars.leftAngle, level - 1)
			}, 180);
			timerId2 = setTimeout(function () {
				drawTree(x2, y2, width*$scope.rightWidthFactor, length*$scope.rightLengthFactor, angle + $scope.treeVars.rightAngle, level - 1)
			}, 180);
//			drawLine(x1, y1, x2, y2, width);
//			drawTree(x2, y2, width*leftWidthFactor, length*leftLenghtFactor, angle - $scope.treeVars.leftAngle, level - 1);
//			drawTree(x2, y2, width*rightWidthFactor, length*rightLengthFactor, angle + $scope.treeVars.rightAngle, level - 1);
		}
	}

	$scope.makeTree = function() {
		//for (var i=0; i < 6; i+=2) {
		drawing = true;
		calcFactors();
		drawTree(600, 800, $scope.treeVars.width, $scope.treeVars.length, $scope.treeVars.initialAngle+(repeatCounter*40), $scope.treeVars.level);
		timerId3 = setTimeout($scope.makeTree, 100);
		repeatCounter++;
		if (repeatCounter>0) {
			// drawing = false;
			clearTimeout(timerId3)
			repeatCounter = 0;
		};
	}

	$scope.clearTree = function() {
		//clearTimeout(timerId);
		//clearTimeout(timerId2);
		clearTimeout(timerId3);
		context.clearRect(0, 0, 1200, 1800);
		repeatCounter = 0;
		drawing = false;
	}

	calcFactors();
}
<commit_msg>fixed typo<commit_after>function TreeCtrl($scope){

	var elem = document.getElementById('canvas');
	var context = elem.getContext('2d');

	context.fillStyle   = '#000';
	context.lineWidth   = 1;

	// Set these variables and open the file to see your tree
	$scope.treeVars = {
		initialAngle : -82, // Initial angle of the trunk
		length : 135, // Length of the first stem
		width : 17, // Width of the first stem
		leftAlpha : 1.8,
		rightAlpha : 2.2,
		leftAngle : 19, // Left delta
		rightAngle : 31, // Right delta
		level : 16,  // Level of recursion
	}

	/*  Here are some examples of interesting inputs

	tree           		initilAngle 	length 	width 	leftAlpha 	rightAlpha 	leftAngle 	rightAngle 	level
	Square				-90				250		35		1.2 		1.2 		90 			90			12
	Symetrical 			-90 			180 	21 		1.2 		1.2 		25 			25 			12
	Tree with foilage	-82				135		13 		1.8 		2.2 		19 			31			16
	Bronchial			-90 			90 		10 		3.0 		3.0 		33 			33 			10
	One-Sided			-95				150 	12 		1.8 		0.002 		30 			0 			16
	Goofy				-90				236		45 		1.2 		2.4 		48			85			12 // and set rightLengthFactor to equal the leftAlpha

	*/

	var deg_to_rad = Math.PI / 180.0;

	var repeatCounter = 0;
	var lineDrawn = false;
	var timerId = undefined;
	var timerId2 = undefined;
	var timerId3 = undefined;
	var drawing = false;

	function calcFactors(){
		$scope.leftWidthFactor = Math.pow(2, -1/$scope.treeVars.leftAlpha);
		$scope.rightWidthFactor = Math.pow(2, -1/$scope.treeVars.rightAlpha);
		$scope.leftLenghtFactor = Math.pow(2, -2/(3*$scope.treeVars.leftAlpha));
		$scope.rightLengthFactor = Math.pow(2, -2/(3*$scope.treeVars.rightAlpha)); // For expected tree
	//	$scope.rightLengthFactor = Math.pow(2, -2/(3*$scope.treeVars.leftAlpha)); // For goofy tree
	}

	function drawLine(x1, y1, x2, y2, width){
		context.beginPath();
		context.lineWidth = width;
		context.moveTo(x1, y1);
		context.lineTo(x2, y2);
		context.closePath();
		context.stroke();
		// context.rect(x2, y2, width, width);
		//context.arc(x2, y2, width/1.5, 0, 2*Math.PI);
		// context.fill();
	}

	function drawTree(x1, y1, width, length, angle, level){
		if ((level != 0) && (drawing == true)) {
			var x2 = x1 + (Math.cos(angle * deg_to_rad) * length);
			var y2 = y1 + (Math.sin(angle * deg_to_rad) * length);
			drawLine(x1, y1, x2, y2, width);
			timerId = setTimeout(function () {
				drawTree(x2, y2, width*$scope.leftWidthFactor, length*$scope.leftLenghtFactor, angle - $scope.treeVars.leftAngle, level - 1)
			}, 180);
			timerId2 = setTimeout(function () {
				drawTree(x2, y2, width*$scope.rightWidthFactor, length*$scope.rightLengthFactor, angle + $scope.treeVars.rightAngle, level - 1)
			}, 180);
//			drawLine(x1, y1, x2, y2, width);
//			drawTree(x2, y2, width*leftWidthFactor, length*leftLenghtFactor, angle - $scope.treeVars.leftAngle, level - 1);
//			drawTree(x2, y2, width*rightWidthFactor, length*rightLengthFactor, angle + $scope.treeVars.rightAngle, level - 1);
		}
	}

	$scope.makeTree = function() {
		//for (var i=0; i < 6; i+=2) {
		drawing = true;
		calcFactors();
		drawTree(600, 800, $scope.treeVars.width, $scope.treeVars.length, $scope.treeVars.initialAngle+(repeatCounter*40), $scope.treeVars.level);
		timerId3 = setTimeout($scope.makeTree, 100);
		repeatCounter++;
		if (repeatCounter>0) {
			// drawing = false;
			clearTimeout(timerId3)
			repeatCounter = 0;
		};
	}

	$scope.clearTree = function() {
		//clearTimeout(timerId);
		//clearTimeout(timerId2);
		clearTimeout(timerId3);
		context.clearRect(0, 0, 1200, 1800);
		repeatCounter = 0;
		drawing = false;
	}

	calcFactors();
}
<|endoftext|>


<commit_before>var Utility = {};

/**
 * Converts degrees to radians
 * @param {integer} degree - Degrees
 */
Utility.toRadians = function(degree)
{
  return degree * Math.PI / 180
}

/**
 * Returns a random point
 * @param {integer} x       - X-coordinate on which to base the random point
 * @param {integer} y       - Y-coordinate on which to base the random point
 * @param {integer} buffer  - Buffer used in generating a random point
 */
Utility.getRandomPoint = function(x, y, buffer)
{
  return {
    x: Utility.getRandomInt(x - buffer, x + buffer),
    y: Utility.getRandomInt(y - buffer, y + buffer)
  };
}

/**
 * Returns a random integer between the maximum (inclusive) and minimum (inclusive)
 * Credit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
 * @param {integer} min - Minimum (inclusive) range of the random integer
 * @param {integer} max - Maximum (inclusive) range of the random integer
 */
Utility.getRandomInt = function(min, max)
{
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Returns a random RGB color
 */
Utility.getRandomColor = function()
{
  var red = Utility.getRandomInt(0, 255);
  var blue = Utility.getRandomInt(0, 255);
  var green = Utility.getRandomInt(0, 255);

  return 'rgb(' + red + ', ' + blue + ',' + green + ')';
}

/**
 * Returns a random boolean
 */
Utility.getRandomBoolean = function()
{
  return !!Utility.getRandomInt(0, 1);
}

/**
 * Returns a random number between the maximum (inclusive) and minimum (inclusive)
 * Credit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
 * @param {number} min - Minimum (inclusive) range of the random integer
 * @param {number} max - Maximum (inclusive) range of the random integer
 */
Utility.getRandom = function(min, max)
{
  return Math.random() * (max - min) + min;
}

/**
 * Determines if two values are within a certain range of eachother
 * @param {integer} firstValue  - First value to use in comparison
 * @param {integer} secondValue - Second value to use in comparison
 * @param {integer} range       - Range to use in comparison
 */
Utility.within = function(firstValue, secondValue, range)
{
  return Math.abs(firstValue - secondValue) <= range;
}

/**
 * Condenses an array, removing all null values
 * @param {object[]} items - Array, containing any data type
 */
Utility.condense = function(items)
{
  var newItems = [];

  var itemsLength = items.length;
  for(var i=0; i<itemsLength; i++)
  {
    var item = items[i];

    if(item === null)
    {
      continue;
    }

    newItems.push(item);
  }

  return newItems;
}

/**
 * Determines if two circles intersect
 * @param {object} circleA - First circle to test
 * @param {object} circleB - First circle to test
 */
Utility.doCirclesIntersect = function(circleA, circleB) {

  var radiiDiff = Math.pow(circleA.radius - circleB.radius, 2);
  var radiiSum = Math.pow(circleA.radius + circleB.radius, 2);
  var centerDiff = Math.pow(circleA.x - circleB.x, 2) + Math.pow(circleA.y - circleB.y, 2);

  return (radiiDiff <= centerDiff) && (centerDiff <= radiiSum);

};
<commit_msg>Fixed glitch range intersection testing<commit_after>var Utility = {};

/**
 * Converts degrees to radians
 * @param {integer} degree - Degrees
 */
Utility.toRadians = function(degree)
{
  return degree * Math.PI / 180
}

/**
 * Returns a random point
 * @param {integer} x       - X-coordinate on which to base the random point
 * @param {integer} y       - Y-coordinate on which to base the random point
 * @param {integer} buffer  - Buffer used in generating a random point
 */
Utility.getRandomPoint = function(x, y, buffer)
{
  return {
    x: Utility.getRandomInt(x - buffer, x + buffer),
    y: Utility.getRandomInt(y - buffer, y + buffer)
  };
}

/**
 * Returns a random integer between the maximum (inclusive) and minimum (inclusive)
 * Credit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
 * @param {integer} min - Minimum (inclusive) range of the random integer
 * @param {integer} max - Maximum (inclusive) range of the random integer
 */
Utility.getRandomInt = function(min, max)
{
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

/**
 * Returns a random RGB color
 */
Utility.getRandomColor = function()
{
  var red = Utility.getRandomInt(0, 255);
  var blue = Utility.getRandomInt(0, 255);
  var green = Utility.getRandomInt(0, 255);

  return 'rgb(' + red + ', ' + blue + ',' + green + ')';
}

/**
 * Returns a random boolean
 */
Utility.getRandomBoolean = function()
{
  return !!Utility.getRandomInt(0, 1);
}

/**
 * Returns a random number between the maximum (inclusive) and minimum (inclusive)
 * Credit: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
 * @param {number} min - Minimum (inclusive) range of the random integer
 * @param {number} max - Maximum (inclusive) range of the random integer
 */
Utility.getRandom = function(min, max)
{
  return Math.random() * (max - min) + min;
}

/**
 * Determines if two values are within a certain range of eachother
 * @param {integer} firstValue  - First value to use in comparison
 * @param {integer} secondValue - Second value to use in comparison
 * @param {integer} range       - Range to use in comparison
 */
Utility.within = function(firstValue, secondValue, range)
{
  return Math.abs(firstValue - secondValue) <= range;
}

/**
 * Condenses an array, removing all null values
 * @param {object[]} items - Array, containing any data type
 */
Utility.condense = function(items)
{
  var newItems = [];

  var itemsLength = items.length;
  for(var i=0; i<itemsLength; i++)
  {
    var item = items[i];

    if(item === null)
    {
      continue;
    }

    newItems.push(item);
  }

  return newItems;
}

/**
 * Determines if two circles intersect
 * @param {object} circleA - First circle to test
 * @param {object} circleB - First circle to test
 */
Utility.doCirclesIntersect = function(circleA, circleB) {

  var radiiSum = Math.pow(circleA.radius + circleB.radius, 2);
  var centerDiff = Math.pow(circleA.x - circleB.x, 2) + Math.pow(circleA.y - circleB.y, 2);

  return (centerDiff <= radiiSum);

};
<|endoftext|>


<commit_before>/* globals $, console */

$(document).ready(function(){

	'use strict';
	function setupPlayer(){
		// Обьявляем все необходимые переменные
        var trackName,
            stationPoster,
            flag = true,
            // Уровень звука
            volumeMusic = 1,
            // Станция по умолчанию
            defStation = 2,
            volumePercent,
            volumeSave,
            // Для локального хранилища
            LCSTRG,
            next_prev_Position,
            // Обьявляем переменную присваемваем ей массив с радиостанциями
            stantion = [{
                name: "Buffalo",
                src: "http://stream-uk1.radioparadise.com/mp3-192"
            }, {
                name: "Radio Rocks",
                src: "http://online-radioroks2.tavrmedia.ua/RadioROKS"
            }, {
                name: "Radio z Kryjivky",
                src: "http://stream.mjoy.ua:8000/radio-z-kryjivky"
            }, {
                name: "KissFM_deep",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_deep"
            }, {
                name: "KissFM_digital",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_digital"
            }, {
                name: "KissFM_trance",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_trance"
            }, {
                name: "Amsterdam Trance",
                src: "http://sc-atr.1.fm:7700/;stream.nsv"
            }, {
                name: "MFM Station",
                src: "http://radio.mfm.ua:8080/online128"
            }, {
                name: "LoungeFM",
                src: "http://cast.loungefm.com.ua/loungefm"
            }, {
                name: "Metal Voice",
                src: "http://www.metalvoice.net:8000/listen"
            }, {
                name: "12 Panks",
                src: "http://12punks-high.rautemusik.fm/;stream.nsv"
            }],
			// Обьявляем переменную которой присваивам элемент аудио
			audio = new Audio();

		// Создаем обьект аудио с необходимым потоком
		function audioBegining(numerStation){
			// Указываем адресс потока
			audio.src = stantion[numerStation].src;
			// Сохраняем номер в LocalStorage
			LCSTRG.setLCSTRG(numerStation);
			// Название текущей станции
			$('#NameStation').html(stantion[numerStation].name);
			$('#NameStation').attr('title', stantion[numerStation].name);
			// Подсцветка текущей радиостанции
			$('#drop_down_streams>ol li').removeClass('orangeBG');
			$('#drop_down_streams>ol li[number="'+numerStation+'"]').addClass('orangeBG');
			// Позиция для кнопок следующая/предыдущая композиция
			next_prev_Position = numerStation;
		}

		//Реализация кнопок  следующая/предыдущая композиция
		function nexr_prev(point){
			// Определяем куда по списку двигаться
			if(point == "next"){
				if(+next_prev_Position < (stantion.length-1)){
					next_prev_Position = +next_prev_Position+1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				} else if(next_prev_Position = (stantion.length-1)){
					next_prev_Position = 0;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				}
			} else if(point == "prev"){
				if(+next_prev_Position == 0){
					next_prev_Position = stantion.length-1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				} else if(+next_prev_Position <= (stantion.length-1)){
					next_prev_Position = +next_prev_Position-1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				}
			}
		}

		// Функция вывода станций в список
		function st_list(){
			for(var i = 0; i<stantion.length; i++){
				$("#drop_down_streams>ol").append('
'+stantion[i].name+'
');
			}
		} st_list();

		// Локальное хранилище последней станции
		// Сначала делаем проверку на поддержку localStorage а потом уже производим операции
		LCSTRG = {
			setLCSTRG: function(number){
				if('localStorage' in window){
					// Удаляем из хранилища предыдущее значение ключа
					localStorage.removeItem('number');
					// Отправляем новое значение ключа
					localStorage.setItem('number', number);
				} else{
					console.log("LocalStorage is not detected!")
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			getLCSTRG: function(){
				// Переменная, для хранения полученных из хранилища данных
				var item;
				if('localStorage' in window){
					item = +localStorage.getItem('number');
					// Отправляем значение ключа
					console.log('Station - '+localStorage.getItem('number'));
					// Станция по умолчанию(усли юзер зашел первый раз == ноль)
					if(item == null){
						item = 0;
					}
					defStation = item;
				} else{
					console.log("LocalStorage is not detected!")
					defStation = 0;
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			setBG: function(color){
				if('localStorage' in window){
					// Удаляем из хранилища предыдущее значение ключа
					localStorage.removeItem('bgColor');
					// Отправляем новое значение ключа
					localStorage.setItem('bgColor', color);
				} else{
					console.log("LocalStorage is not detected!")
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			getBG: function(){
				// Переменная, для хранения полученных из хранилища данных
				var item;
				if('localStorage' in window){
					item = localStorage.getItem('bgColor');
					// Отправляем новое значение ключа
					console.log('Color theme - '+localStorage.getItem('bgColor'));
					// Станция по умолчанию(усли юзер зашел первый раз == ноль)
					if(item == null){
						item = "dark";
					}
					// Присваеваем цветовую
					bgcolor(item);
				} else{
					console.log("LocalStorage is not detected!")
					bgcolor('light');
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			}
		};
		//Станция и фон по умолчанию
		LCSTRG.getLCSTRG().getBG();

		// Функция смены фона
		function bgcolor(color){
			$('#player').removeClass('light').removeClass('dark').addClass(color);
			$('div[data-role=theme-switcher]').attr('themes', color);
			LCSTRG.setBG(color);
			// Подпись кнопки - противоположная цвету обложки
			if(color == "light"){
				$('div[data-role=theme-switcher]').html('Dark');
			} else if(color == "dark"){
				$('div[data-role=theme-switcher]').html('Light');
			}
		}

		// Смена фона кнопки плей
		function playButtonBG(val){
			$('#playStop').removeClass('play').removeClass('pause').addClass(val);
		}
		// Функция play/stop
		function play_stop(){
			if( flag == true ){
				audio.play();
				playButtonBG('pause')
				flag = false;
			} else{
				audio.pause();
				flag = true;
				playButtonBG('play')
			}
		};

		$("body").click(function(event){
			// Реализуем проигрывание
			if(event.target.id == "playStop"){
				play_stop();
			}

			//Нажатие кнопки звука
			if(event.target.id == "volume"){
				if($("#volume").attr("on") == "true"){
					volumeSave = audio.volume;
					volumePercent = $('.ui-widget-header').css('width');
					$('.ui-widget-header').css('width', '0');
					$("#volume").attr("on", "false")
					audio.volume = 0;
				} else{
					$('.ui-widget-header').css('width', volumePercent);
					$("#volume").attr("on", "true")
					audio.volume = volumeSave;
				}
			}

			// Переключение цвета фона
			if($(event.target).attr('data-role') == "theme-switcher"){
				if($(event.target).attr('themes') == 'dark'){
					bgcolor('light')
					console.log("dark");
				} else if($(event.target).attr('themes') == 'light'){
					bgcolor('dark')
					console.log("light");
				}
			}

			// Выпадающий список станций
			if(event.target.id == 'btn_streams_list'){
				if($("#btn_streams_list").attr("on") == "false"){
					$('#drop_down_streams').animate({'height':'220px'},500);
					$("#btn_streams_list").attr("on", "true")
					console.log("Open streams list");
				} else{
					$('#drop_down_streams').animate({'height':'0'},500);
					$("#btn_streams_list").attr("on", "false")
					console.log("Close streams list");
				}
			}

			// Выбор станции в списке
			if(	$(event.target).attr('group') == "choice"){
				audio.pause();
				audioBegining($(event.target).attr('number'));
				audio.play();
				playButtonBG('pause');
				//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
				flag = false;
			}

			// Выбор станции next/prev
			if(	event.target.id == "next"){
				nexr_prev("next");
			} else if(event.target.id == "prev"){
				nexr_prev("prev");
			}
		});

		// Реализация слайдера звука(jQuery ui)
		$( ".slider" ).slider({
		 	animate: true,
		    range: "min",
		    value: 1,
		    min: 0,
		    max: 1,
			step: 0.05,
			//Получаем значение и выводим его на странице
		    slide: function( event, ui ) {
	            if(ui.value > 0){
	            	audio.volume = ui.value;
	            	$("#volume").attr("on", "true")
	        	}
	            else if(ui.value <= 0){
	            	audio.volume = 0;
	            	$("#volume").attr("on", "false")
	            }
		    }
		});
		// Дефолтная установка станции
		audioBegining(defStation);
	};
	setupPlayer();

});
<commit_msg>Чистка скрипта.<commit_after>/* globals $, console */

$(document).ready(function(){

	'use strict';
	function setupPlayer(){
		// Обьявляем все необходимые переменные
        var flag = true,
            // Уровень звука
            volumeMusic = 1,
            // Станция по умолчанию
            defStation = 2,
            // Для звука
            volumePercent,
            volumeSave,
            // Для локального хранилища
            LCSTRG,
            // Переключение станций
            next_prev_Position,
            // Обьявляем переменную присваемваем ей массив с радиостанциями
            stantion = [{
                name: "Buffalo",
                src: "http://stream-uk1.radioparadise.com/mp3-192"
            }, {
                name: "Radio Rocks",
                src: "http://online-radioroks2.tavrmedia.ua/RadioROKS"
            }, {
                name: "Radio z Kryjivky",
                src: "http://stream.mjoy.ua:8000/radio-z-kryjivky"
            }, {
                name: "KissFM_deep",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_deep"
            }, {
                name: "KissFM_digital",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_digital"
            }, {
                name: "KissFM_trance",
                src: "http://online-kissfm.tavrmedia.ua/KissFM_trance"
            }, {
                name: "Amsterdam Trance",
                src: "http://sc-atr.1.fm:7700/;stream.nsv"
            }, {
                name: "MFM Station",
                src: "http://radio.mfm.ua:8080/online128"
            }, {
                name: "LoungeFM",
                src: "http://cast.loungefm.com.ua/loungefm"
            }, {
                name: "Metal Voice",
                src: "http://www.metalvoice.net:8000/listen"
            }, {
                name: "12 Panks",
                src: "http://12punks-high.rautemusik.fm/;stream.nsv"
            }],
			// Обьявляем переменную которой присваивам элемент аудио
			audio = new Audio();

		// Создаем обьект аудио с необходимым потоком
		function audioBegining(numerStation){
			// Указываем адресс потока
			audio.src = stantion[numerStation].src;
			// Сохраняем номер в LocalStorage
			LCSTRG.setLCSTRG(numerStation);
			// Название текущей станции
			$('#NameStation').html(stantion[numerStation].name);
			$('#NameStation').attr('title', stantion[numerStation].name);
			// Подсцветка текущей радиостанции
			$('#drop_down_streams>ol li').removeClass('orangeBG');
			$('#drop_down_streams>ol li[number="'+numerStation+'"]').addClass('orangeBG');
			// Позиция для кнопок следующая/предыдущая композиция
			next_prev_Position = numerStation;
		}

		//Реализация кнопок  следующая/предыдущая композиция
		function nexr_prev(point){
			// Определяем куда по списку двигаться
			if(point == "next"){
				if(+next_prev_Position < (stantion.length-1)){
					next_prev_Position = +next_prev_Position+1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				} else if(next_prev_Position = (stantion.length-1)){
					next_prev_Position = 0;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				}
			} else if(point == "prev"){
				if(+next_prev_Position == 0){
					next_prev_Position = stantion.length-1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				} else if(+next_prev_Position <= (stantion.length-1)){
					next_prev_Position = +next_prev_Position-1;
					audioBegining(next_prev_Position);
					audio.play();
					playButtonBG('pause');
					//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
					flag = false;
				}
			}
		}

		// Функция вывода станций в список
		function st_list(){
			for(var i = 0; i<stantion.length; i++){
				$("#drop_down_streams>ol").append('
'+stantion[i].name+'
');
			}
		} st_list();

		// Локальное хранилище последней станции
		// Сначала делаем проверку на поддержку localStorage а потом уже производим операции
		LCSTRG = {
			setLCSTRG: function(number){
				if('localStorage' in window){
					// Удаляем из хранилища предыдущее значение ключа
					localStorage.removeItem('number');
					// Отправляем новое значение ключа
					localStorage.setItem('number', number);
				} else{
					console.log("LocalStorage is not detected!")
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			getLCSTRG: function(){
				// Переменная, для хранения полученных из хранилища данных
				var item;
				if('localStorage' in window){
					item = +localStorage.getItem('number');
					// Отправляем значение ключа
					console.log('Station - '+localStorage.getItem('number'));
					// Станция по умолчанию(усли юзер зашел первый раз == ноль)
					if(item == null){
						item = 0;
					}
					defStation = item;
				} else{
					console.log("LocalStorage is not detected!")
					defStation = 0;
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			setBG: function(color){
				if('localStorage' in window){
					// Удаляем из хранилища предыдущее значение ключа
					localStorage.removeItem('bgColor');
					// Отправляем новое значение ключа
					localStorage.setItem('bgColor', color);
				} else{
					console.log("LocalStorage is not detected!")
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			},
			getBG: function(){
				// Переменная, для хранения полученных из хранилища данных
				var item;
				if('localStorage' in window){
					item = localStorage.getItem('bgColor');
					// Отправляем новое значение ключа
					console.log('Color theme - '+localStorage.getItem('bgColor'));
					// Станция по умолчанию(усли юзер зашел первый раз == ноль)
					if(item == null){
						item = "dark";
					}
					// Присваеваем цветовую
					bgcolor(item);
				} else{
					console.log("LocalStorage is not detected!")
					bgcolor('light');
				}
				//Возвращаем обьект, для последовательного вызова методов
				return this;
			}
		};
		//Станция и фон по умолчанию
		LCSTRG.getLCSTRG().getBG();

		// Функция смены фона
		function bgcolor(color){
			$('#player').removeClass('light').removeClass('dark').addClass(color);
			$('div[data-role=theme-switcher]').attr('themes', color);
			LCSTRG.setBG(color);
			// Подпись кнопки - противоположная цвету обложки
			if(color == "light"){
				$('div[data-role=theme-switcher]').html('Dark');
			} else if(color == "dark"){
				$('div[data-role=theme-switcher]').html('Light');
			}
		}

		// Смена фона кнопки плей
		function playButtonBG(val){
			$('#playStop').removeClass('play').removeClass('pause').addClass(val);
		}
		// Функция play/stop
		function play_stop(){
			if( flag == true ){
				audio.play();
				playButtonBG('pause')
				flag = false;
			} else{
				audio.pause();
				flag = true;
				playButtonBG('play')
			}
		};

		$("body").click(function(event){
			// Реализуем проигрывание
			if(event.target.id == "playStop"){
				play_stop();
			}

			//Нажатие кнопки звука
			if(event.target.id == "volume"){
				if($("#volume").attr("on") == "true"){
					volumeSave = audio.volume;
					volumePercent = $('.ui-widget-header').css('width');
					$('.ui-widget-header').css('width', '0');
					$("#volume").attr("on", "false")
					audio.volume = 0;
				} else{
					$('.ui-widget-header').css('width', volumePercent);
					$("#volume").attr("on", "true")
					audio.volume = volumeSave;
				}
			}

			// Переключение цвета фона
			if($(event.target).attr('data-role') == "theme-switcher"){
				if($(event.target).attr('themes') == 'dark'){
					bgcolor('light')
				} else if($(event.target).attr('themes') == 'light'){
					bgcolor('dark')
				}
			}

			// Выпадающий список станций
			if(event.target.id == 'btn_streams_list'){
				if($("#btn_streams_list").attr("on") == "false"){
					$('#drop_down_streams').animate({'height':'220px'},500);
					$("#btn_streams_list").attr("on", "true")
				} else{
					$('#drop_down_streams').animate({'height':'0'},500);
					$("#btn_streams_list").attr("on", "false")
				}
			}

			// Выбор станции в списке
			if(	$(event.target).attr('group') == "choice"){
				audio.pause();
				audioBegining($(event.target).attr('number'));
				audio.play();
				playButtonBG('pause');
				//Учитываем будто кнопка плей была нажата(дабы не нарушать заданные нами прежде условия)
				flag = false;
			}

			// Выбор станции next/prev
			if(	event.target.id == "next"){
				nexr_prev("next");
			} else if(event.target.id == "prev"){
				nexr_prev("prev");
			}
		});

		// Реализация слайдера звука(jQuery ui)
		$( ".slider" ).slider({
		 	animate: true,
		    range: "min",
		    value: 1,
		    min: 0,
		    max: 1,
			step: 0.05,
			//Получаем значение и выводим его на странице
		    slide: function( event, ui ) {
	            if(ui.value > 0){
	            	audio.volume = ui.value;
	            	$("#volume").attr("on", "true")
	        	}
	            else if(ui.value <= 0){
	            	audio.volume = 0;
	            	$("#volume").attr("on", "false")
	            }
		    }
		});
		// Дефолтная установка станции
		audioBegining(defStation);
	};
	setupPlayer();

});
<|endoftext|>


<commit_before>'use strict'

const Raw = require('./raw')

class Query {

  uniq (array) {
    return Array.from(new Set(array))
  }

  constructor (model) {
    this.includes = {}
    this.db = model.db
    this.model = model
    this.table = model.table
    this.query = this.table
    this.from = this.table
  }

  send () {
    return this.db.query(this.query)
  }

  insert (values) {
    this.query = this.query.insert(values).returning(this.table.star())
    return this.send().then((result) => result.rows[0])
  }

  update (values) {
    this.query = this.query.update(values)
    return this.send()
  }

  delete () {
    this.query = this.query.delete()
    return this.send()
  }

  count () {
    this.query = this.query.select('count(*) as count').from(this.from)
    return this.send().then((result) => +result.rows[0].count)
  }

  all () {
    let Model = this.model
    let includes = this.includes

    // Use aliases
    this.query = this.query.select(this.table.star()).from(this.from)

    // Load models
    return this.send().then((result) => {
      // Construct some models
      let models = result.rows.map((row) => new Model(row))

      // Load includes
      return Promise.all(Object.keys(includes).map((name) => {
        let relation = Model.relations[name]
        let conditions = {}
        let key = relation.key
        let many = relation.many

        conditions[many ? key : 'id'] = this.uniq(models.map((model) => {
          return model[many ? 'id' : key]
        }))

        // Attach includes
        return relation.model
        .where(conditions)
        .include(includes[name])
        .all().then((includes) => {
          let byId = {}
          if (many) {
            for (let model of models) {
              model[name] = []
              byId[model.id] = model
            }
            for (let include of includes) {
              byId[include[key]][name].push(include)
            }
          } else {
            for (let include of includes) byId[include.id] = include
            for (let model of models) model[name] = byId[model[key]]
          }
        })
      })).then(() => models)
    })
  }

  find (id) {
    if (id != null) return this.where({id: id}).find()
    return this.limit(1).all().then((models) => {
      return models.length ? models[0] : null
    })
  }

  paginate (page, count) {
    return this.offset((page - 1) * count).limit(count + 1).all()
    .then((models) => {
      let more = models.length > count
      if (models.length > count) models = models.slice(0, count)
      models.more = more
      return models
    })
  }

  not (values) {
    return this._where(this.model, values, true)
  }

  where (values) {
    if (typeof values === 'string') {
      let params = Array.prototype.slice.call(arguments, 1)
      this.query = this.query.where(new Raw(values, params))
      return this
    }
    return this._where(this.model, values)
  }

  _where (model, values, not) {
    let table = model.table

    for (let name of Object.keys(values)) {
      let condition
      let value = values[name]

      if (value == null) {
        condition = table[name][not ? 'isNotNull' : 'isNull']()
      } else if (Array.isArray(value)) {
        condition = table[name][not ? 'notIn' : 'in'](value)
      } else if (value instanceof Query) {
        condition = table[name][not ? 'notIn' : 'in'](value.query)
      } else if (value instanceof Date || typeof value !== 'object') {
        condition = table[name][not ? 'notEquals' : 'equals'](value)
      } else {
        this._where(model.relations[name].model, value, not)
        continue
      }
      this.query = this.query.where(condition)
    }
    return this
  }

  limit () {
    this.query = this.query.limit.apply(this.query, arguments)
    return this
  }

  offset () {
    this.query = this.query.offset.apply(this.query, arguments)
    return this
  }

  order () {
    for (let arg of arguments) {
      if (typeof arg === 'string') {
        this.query = this.query.order(this.table[arg])
      } else if (Array.isArray(arg)) {
        this.query = this.query.order(this.table[arg[0]][arg[1]])
      } else {
        this.query = this.query.order(arg)
      }
    }
    return this
  }

  include () {
    this._include(this.includes, Array.prototype.slice.call(arguments))
    return this
  }

  _include (hash, object) {
    if (object == null) return

    if (typeof object === 'string') {
      if (!hash[object]) hash[object] = {}
      return
    }

    if (Array.isArray(object)) {
      for (let item of object) this._include(hash, item)
      return
    }

    for (let key of Object.keys(object)) {
      if (!hash[key]) hash[key] = {}
      this._include(hash[key], object[key])
    }
  }

  select () {
    for (let arg of arguments) {
      if (Array.isArray(arg)) {
        this.query = this.query.select(new Raw(arg[0], arg.slice(1)))
      } else {
        if (this.model.properties[arg]) arg = this.table[arg]
        this.query = this.query.select(arg)
      }
    }
    return this
  }

  join () {
    this._join(this.model, Array.prototype.slice.call(arguments))
    return this
  }

  leftJoin () {
    this._join(this.model, Array.prototype.slice.call(arguments), 'left')
    return this
  }

  _join (model, name, type) {
    if (Array.isArray(name)) {
      for (let item of name) this._join(model, item, type)
      return
    }

    if (typeof name === 'object') {
      for (let key of Object.keys(name)) {
        this._join(model, key, type)
        this._join(model.relations[key].model, name[key], type)
      }
      return
    }

    let condition
    let relation = model.relations[name]

    if (relation.many) {
      condition = relation.model.table[relation.key].equals(model.table.id)
    } else {
      condition = model.table[relation.key].equals(relation.model.table.id)
    }

    let method = type === 'left' ? 'leftJoin' : 'join'
    this.from = this.from[method](relation.model.table).on(condition)
  }

  groupBy (sql) {
    let params = Array.prototype.slice.call(arguments, 1)
    this.query = this.query.group(new Raw(sql, params))
    return this
  }

}

module.exports = Query
<commit_msg>Syntax.<commit_after>'use strict'

const Raw = require('./raw')

class Query {

  uniq (array) {
    return Array.from(new Set(array))
  }

  constructor (model) {
    this.includes = {}
    this.db = model.db
    this.model = model
    this.table = model.table
    this.query = this.table
    this.from = this.table
  }

  send () {
    return this.db.query(this.query)
  }

  insert (values) {
    this.query = this.query.insert(values).returning(this.table.star())
    return this.send().then((result) => result.rows[0])
  }

  update (values) {
    this.query = this.query.update(values)
    return this.send()
  }

  delete () {
    this.query = this.query.delete()
    return this.send()
  }

  count () {
    this.query = this.query.select('count(*) as count').from(this.from)
    return this.send().then((result) => +result.rows[0].count)
  }

  all () {
    const Model = this.model
    const includes = this.includes

    // Use aliases
    this.query = this.query.select(this.table.star()).from(this.from)

    // Load models
    return this.send().then((result) => {
      // Construct some models
      const models = result.rows.map((row) => new Model(row))

      // Load includes
      return Promise.all(Object.keys(includes).map((name) => {
        const relation = Model.relations[name]
        const conditions = {}
        const key = relation.key
        const many = relation.many

        conditions[many ? key : 'id'] = this.uniq(models.map((model) => {
          return model[many ? 'id' : key]
        }))

        // Attach includes
        return relation.model
        .where(conditions)
        .include(includes[name])
        .all().then((includes) => {
          const byId = {}
          if (many) {
            for (const model of models) {
              model[name] = []
              byId[model.id] = model
            }
            for (const include of includes) {
              byId[include[key]][name].push(include)
            }
          } else {
            for (const include of includes) byId[include.id] = include
            for (const model of models) model[name] = byId[model[key]]
          }
        })
      })).then(() => models)
    })
  }

  find (id) {
    if (id != null) return this.where({id: id}).find()
    return this.limit(1).all().then((models) => {
      return models.length ? models[0] : null
    })
  }

  paginate (page, count) {
    return this.offset((page - 1) * count).limit(count + 1).all()
    .then((models) => {
      const more = models.length > count
      if (models.length > count) models = models.slice(0, count)
      models.more = more
      return models
    })
  }

  not (values) {
    return this._where(this.model, values, true)
  }

  where (values) {
    if (typeof values === 'string') {
      const params = Array.prototype.slice.call(arguments, 1)
      this.query = this.query.where(new Raw(values, params))
      return this
    }
    return this._where(this.model, values)
  }

  _where (model, values, not) {
    const table = model.table

    for (const name of Object.keys(values)) {
      let condition
      const value = values[name]

      if (value == null) {
        condition = table[name][not ? 'isNotNull' : 'isNull']()
      } else if (Array.isArray(value)) {
        condition = table[name][not ? 'notIn' : 'in'](value)
      } else if (value instanceof Query) {
        condition = table[name][not ? 'notIn' : 'in'](value.query)
      } else if (value instanceof Date || typeof value !== 'object') {
        condition = table[name][not ? 'notEquals' : 'equals'](value)
      } else {
        this._where(model.relations[name].model, value, not)
        continue
      }
      this.query = this.query.where(condition)
    }
    return this
  }

  limit () {
    this.query = this.query.limit.apply(this.query, arguments)
    return this
  }

  offset () {
    this.query = this.query.offset.apply(this.query, arguments)
    return this
  }

  order () {
    for (const arg of arguments) {
      if (typeof arg === 'string') {
        this.query = this.query.order(this.table[arg])
      } else if (Array.isArray(arg)) {
        this.query = this.query.order(this.table[arg[0]][arg[1]])
      } else {
        this.query = this.query.order(arg)
      }
    }
    return this
  }

  include () {
    this._include(this.includes, Array.prototype.slice.call(arguments))
    return this
  }

  _include (hash, object) {
    if (object == null) return

    if (typeof object === 'string') {
      if (!hash[object]) hash[object] = {}
      return
    }

    if (Array.isArray(object)) {
      for (const item of object) this._include(hash, item)
      return
    }

    for (const key of Object.keys(object)) {
      if (!hash[key]) hash[key] = {}
      this._include(hash[key], object[key])
    }
  }

  select () {
    for (let arg of arguments) {
      if (Array.isArray(arg)) {
        this.query = this.query.select(new Raw(arg[0], arg.slice(1)))
      } else {
        if (this.model.properties[arg]) arg = this.table[arg]
        this.query = this.query.select(arg)
      }
    }
    return this
  }

  join () {
    this._join(this.model, Array.prototype.slice.call(arguments))
    return this
  }

  leftJoin () {
    this._join(this.model, Array.prototype.slice.call(arguments), 'left')
    return this
  }

  _join (model, name, type) {
    if (Array.isArray(name)) {
      for (const item of name) this._join(model, item, type)
      return
    }

    if (typeof name === 'object') {
      for (const key of Object.keys(name)) {
        this._join(model, key, type)
        this._join(model.relations[key].model, name[key], type)
      }
      return
    }

    let condition
    const relation = model.relations[name]

    if (relation.many) {
      condition = relation.model.table[relation.key].equals(model.table.id)
    } else {
      condition = model.table[relation.key].equals(relation.model.table.id)
    }

    const method = type === 'left' ? 'leftJoin' : 'join'
    this.from = this.from[method](relation.model.table).on(condition)
  }

  groupBy (sql) {
    const params = Array.prototype.slice.call(arguments, 1)
    this.query = this.query.group(new Raw(sql, params))
    return this
  }

}

module.exports = Query
<|endoftext|>


<commit_before>'use strict';

const logger = require('heroku-logger');
const helpers = require('../../helpers');
const rivescript = require('../../rivescript');

module.exports = function getRivescriptReply() {
  return (req, res, next) => {
    rivescript.getReply(req.conversation, req.inboundMessageText)
      .then((rivescriptRes) => {
        logger.debug('rivescript.getReply', rivescriptRes);
        req.rivescriptReplyText = rivescriptRes.text;
        req.rivescriptMatch = rivescriptRes.match;
        req.rivescriptTopic = rivescriptRes.topic;
        return next();
      })
      .catch(err => helpers.sendErrorResponse(res, err));
  };
};
<commit_msg>Fixes topic bug<commit_after>'use strict';

const logger = require('heroku-logger');
const helpers = require('../../helpers');
const rivescript = require('../../rivescript');

module.exports = function getRivescriptReply() {
  return (req, res, next) => {
    rivescript.getReply(req.conversation, req.inboundMessageText)
      .then((rivescriptRes) => {
        logger.debug('rivescript.getReply', rivescriptRes);
        req.rivescriptReplyText = rivescriptRes.text;
        req.rivescriptMatch = rivescriptRes.match;
        req.rivescriptReplyTopic = rivescriptRes.topic;
        return next();
      })
      .catch(err => helpers.sendErrorResponse(res, err));
  };
};
<|endoftext|>


<commit_before>// Simple initialization
var Storage = require('../lib'),
	async = require('async');

Storage.init({
	custom: {
		provider: Storage.Providers.LocalSystem,
		path: 'practical'
	}
});

// Because no process.env.storage is specified, we use 'amazon' for now
var client = Storage.get('custom');

async.waterfall([
	function uploadFile(callback) {
		client.upload('../LICENSE', 'testing/license.md', function (err, res) {
			console.log(res);
			callback(err);
		});
	},
	function downloadFile(callback) {
		client.download('license.md', 'practical/license2.md', function (err) {
			callback(err);
		});
	},
	function removeFile(callback) {
		client.remove('license2.md', function (err) {
			callback(err);
		});
	}
], function (err) {
	if (err) {
		console.log('Error - ', err);
	} else {
		console.log('Successful');
	}
});

<commit_msg>Updated example<commit_after>// Simple initialization
var Storage = require('../lib'),
	async = require('async');

Storage.init({
	custom: {
		provider: Storage.Providers.LocalSystem,
		container: 'practical'
	}
});

// Because no process.env.storage is specified, we use 'amazon' for now
var client = Storage.get('custom');

async.waterfall([
	function uploadFile(callback) {
		client.upload('../LICENSE', 'testing/license.md', function (err, res) {
			console.log(res);
			callback(err);
		});
	},
	function downloadFile(callback) {
		client.download('testing/license.md', 'license2.md', function (err) {
			callback(err);
		});
	},
	function removeFile(callback) {
		client.remove('testing/license.md', function (err) {
			callback(err);
		});
	}
], function (err) {
	if (err) {
		console.log('Error - ', err);
	} else {
		console.log('Successful');
	}
});

<|endoftext|>


<commit_before>'use strict';
// ==UserScript==
// @name         mbz-loujine-common
// @namespace    mbz-loujine
// @author       loujine
// @version      2016.01.27
// @description  musicbrainz.org: common functions
// @compatible   firefox+greasemonkey
// @licence      CC BY-NC-SA 3.0 (https://creativecommons.org/licenses/by-nc-sa/3.0/)
// @grant        none
// @run-at       document-end
// ==/UserScript==

// from musicbrainz-server/root/static/scripts/tests/typeInfo.js
var server = {
    'link': {
        'instrument': 148,
        'vocals': 149,
        'orchestra': 150,
        'conductor': 151,
        'performer': 156,
        'work': 278,
        'place': 693,
        'area': 698
    },
    'attr': {
        'strings': 69,
        'cello': 84,
        'violin': 86,
        'piano': 180,
        'bowedStrings': 275,
        'live': 578,
        'partial': 579
    },
    // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
    // we wait for `server.timeout` milliseconds between two queries
    'timeout': 1000
};

var works = {
    'type': '<select class="setwork">' +
'<option selected> </option>' +
'<option value="17">Song</option>' +
'<option value="1">Aria</option>' +
'<option value="25">Audio drama</option>' +
'<option value="2">Ballet</option>' +
'<option value="26">Beijing opera</option>' +
'<option value="3">Cantata</option>' +
'<option value="4">Concerto</option>' +
'<option value="20">Étude</option>' +
'<option value="30">Incidental music</option>' +
'<option value="7">Madrigal</option>' +
'<option value="8">Mass</option>' +
'<option value="9">Motet</option>' +
'<option value="29">Musical</option>' +
'<option value="10">Opera</option>' +
'<option value="24">Operetta</option>' +
'<option value="11">Oratorio</option>' +
'<option value="12">Overture</option>' +
'<option value="13">Partita</option>' +
'<option value="28">Play</option>' +
'<option value="21">Poem</option>' +
'<option value="23">Prose</option>' +
'<option value="14">Quartet</option>' +
'<option value="5">Sonata</option>' +
'<option value="15">Song-cycle</option>' +
'<option value="22">Soundtrack</option>' +
'<option value="6">Suite</option>' +
'<option value="18">Symphonic poem</option>' +
'<option value="16">Symphony</option>' +
'<option value="19">Zarzuela</option>' +
'</select>',
    'lang': '<select class="setlang">' +
'<option> </option>' +
'<optgroup label="Frequently used">' +
'<option class="language" value="284">[Multiple languages]</option>' +
'<option class="language" value="486" selected="selected">[No lyrics]</option>' +
'<option class="language" value="18">Arabic</option>' +
'<option class="language" value="76">Chinese</option>' +
'<option class="language" value="98">Czech</option>' +
'<option class="language" value="100">Danish</option>' +
'<option class="language" value="113">Dutch</option>' +
'<option class="language" value="120">English</option>' +
'<option class="language" value="131">Finnish</option>' +
'<option class="language" value="134">French</option>' +
'<option class="language" value="145">German</option>' +
'<option class="language" value="159">Greek</option>' +
'<option class="language" value="195">Italian</option>' +
'<option class="language" value="198">Japanese</option>' +
'<option class="language" value="224">Korean</option>' +
'<option class="language" value="338">Polish</option>' +
'<option class="language" value="340">Portuguese</option>' +
'<option class="language" value="353">Russian</option>' +
'<option class="language" value="393">Spanish</option>' +
'<option class="language" value="403">Swedish</option>' +
'<option class="language" value="433">Turkish</option>' +
'</optgroup>' +
'</select>',
    'key': '<select class="setkey">' +
'<option selected> </option>' +
'<option value="2">C major</option>' +
'<option value="3">C minor</option>' +
'<option value="4">C-sharp major</option>' +
'<option value="5">C-sharp minor</option>' +
'<option value="6">D-flat major</option>' +
'<option value="7">D-flat minor</option>' +
'<option value="8">D major</option>' +
'<option value="9">D minor</option>' +
'<option value="10">D-sharp minor</option>' +
'<option value="11">E-flat major</option>' +
'<option value="12">E-flat minor</option>' +
'<option value="13">E major</option>' +
'<option value="14">E minor</option>' +
'<option value="15">E-sharp minor</option>' +
'<option value="16">F-flat major</option>' +
'<option value="17">F major</option>' +
'<option value="18">F minor</option>' +
'<option value="19">F-sharp major</option>' +
'<option value="20">F-sharp minor</option>' +
'<option value="21">G-flat major</option>' +
'<option value="22">G major</option>' +
'<option value="23">G minor</option>' +
'<option value="24">G-sharp major</option>' +
'<option value="25">G-sharp minor</option>' +
'<option value="26">A-flat major</option>' +
'<option value="27">A-flat minor</option>' +
'<option value="28">A major</option>' +
'<option value="29">A minor</option>' +
'<option value="30">A-sharp minor</option>' +
'<option value="31">B-flat major</option>' +
'<option value="32">B-flat minor</option>' +
'<option value="33">B major</option>' +
'<option value="34">B minor</option>' +
'<option value="789">C Dorian</option>' +
'<option value="790">D Dorian</option>' +
'<option value="791">E Dorian</option>' +
'<option value="792">F Dorian</option>' +
'<option value="793">G Dorian</option>' +
'<option value="794">A Dorian</option>' +
'<option value="795">B Dorian</option>' +
'</select>'
};

// https://www.wikidata.org/wiki/Wikidata:List_of_properties/Person
var wikidata = {
    'entities': {
        person: 5,
        male: 6581097,
        female: 6581072
    },
    'fields': {
        type: 'P31',
        gender: 'P21',
        citizen: 'P27',
        isni: 'P213',
        birthDate: 'P569',
        birthPlace: 'P19',
        deathDate: 'P570',
        deathPlace: 'P20',
        mbidArtist: 'P434',
        mbidArea: 'P982'
    }
};

var requests = function () {
    var self = {};

    self._request = function (verb, url, param, callback) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                    callback(xhr);
            }
        };
        xhr.open(verb, url, true);
        if (verb === 'POST') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('Content-length', param.length);
            xhr.setRequestHeader('Connection', 'close');
        }
        xhr.timeout = 10000;
        xhr.ontimeout = function () {
            console.error('The request for ' + url + ' timed out.');
            };
        xhr.send(param);
    };

    self.GET = function (url, callback) {
        self._request('GET', url, null, function (xhr) {
            if (xhr.status === 200 && xhr.responseText !== null) {
                callback(xhr.responseText);
            } else {
                console.log('Error ', xhr.status, ': ', url);
            }
        });
    };

    self.POST = function (url, param, callback) {
        self._request('POST', url, param, function (xhr) {
            callback(xhr.status);
        });
    };
    return self;
}();

var helper = function () {
    var self = {};

    // musicbrainz-server/root/static/scripts/common/utility/formatTrackLength.js
    self.formatTrackLength = function(milliseconds) {
        if (!milliseconds) {
            return '';
        }
        if (milliseconds < 1000) {
            return milliseconds + ' ms';
        }
        var oneMinute = 60;
        var oneHour = 60 * oneMinute;
        var seconds = Math.round(milliseconds / 1000.0);
        var hours = Math.floor(seconds / oneHour);
        seconds = seconds % oneHour;
        var minutes = Math.floor(seconds / oneMinute);
        seconds = seconds % oneMinute;
        var result = ('00' + seconds).slice(-2);
        if (hours > 0) {
            result = hours + ':' + ('00' + minutes).slice(-2) + ':' + result;
        } else {
            result = minutes + ':' + result;
        }
        return result;
    };

    self.comparefct = function(a, b) {
        // Sort function for performers in the recording artist list
        var link = server.link,
            order = [link.vocals, link.instrument, link.orchestra,
                     link.conductor, link.performer];
        if (a.link === b.link) {return 0;}
        return order.indexOf(a.link) > order.indexOf(b.link) ? 1 : -1;
    };
    return self;
}();

var sidebar = function ($) {
    var self = {};

    // edit notes
    self.editNote = function (meta, msg) {
        msg = msg || '';
        var signature = '\n —\n' + `GM script: "${meta.name}" (${meta.version})\n\n`;
        return (msg + signature);
    };

    self.container = function () {
        var $container;
        if ($('div#loujine-sidebar').length) {
            $container = $('div#loujine-sidebar');
        } else {
            $container = $('

', {
                'id': 'loujine-sidebar',
                'css': {
                    'background-color': 'white',
                    'padding': '8px',
                    'margin': '0px -6px 6px',
                    'border': '2px dotted #736DAB'
                }
            }).append(
                $('

', {'text': 'loujine GM tools'})
            );
        }
        $('h2.rating').before($container);
        return $container;
    };

    return self;
}($);

var relEditor = function (MB, $) {
    var self = {};

    // edit note
    self.editNote = function (meta, msg) {
        msg = msg || '';
        var vm = MB.releaseRelationshipEditor,
            existingMsg = vm.editNote(),
            signature = '\n —\n' +
                        `GM script: "${meta.name}" (${meta.version})\n\n`;
        vm.editNote(existingMsg + msg + signature);
    };

    self.container = function () {
        var $container;
        if ($('div#loujine-menu').length) {
            $container = $('div#loujine-menu');
        } else {
            $container = $('

', {
                'id': 'loujine-menu',
                'css': {
                    'background-color': 'white',
                    'padding': '8px',
                    'margin': '0px -6px 6px',
                    'border': '5px dotted #736DAB'
                }
            }).append(
                $('

', {'text': 'loujine GM tools'})
            );
        }
        return $container;
    };

    return self;
}(MB, $);
<commit_msg>Back quotes to simple quotes. PS. version part starting with “0” bugs chrome or another browser I forgot which (not really important on libs, though, only on actual greasemonkeys for auto updates)<commit_after>'use strict';
// ==UserScript==
// @name         mbz-loujine-common
// @namespace    mbz-loujine
// @author       loujine
// @version      2016.1.27.1920
// @description  musicbrainz.org: common functions
// @compatible   firefox+greasemonkey
// @licence      CC BY-NC-SA 3.0 (https://creativecommons.org/licenses/by-nc-sa/3.0/)
// @grant        none
// @run-at       document-end
// ==/UserScript==

// from musicbrainz-server/root/static/scripts/tests/typeInfo.js
var server = {
    'link': {
        'instrument': 148,
        'vocals': 149,
        'orchestra': 150,
        'conductor': 151,
        'performer': 156,
        'work': 278,
        'place': 693,
        'area': 698
    },
    'attr': {
        'strings': 69,
        'cello': 84,
        'violin': 86,
        'piano': 180,
        'bowedStrings': 275,
        'live': 578,
        'partial': 579
    },
    // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
    // we wait for 'server.timeout' milliseconds between two queries
    'timeout': 1000
};

var works = {
    'type': '<select class="setwork">' +
'<option selected> </option>' +
'<option value="17">Song</option>' +
'<option value="1">Aria</option>' +
'<option value="25">Audio drama</option>' +
'<option value="2">Ballet</option>' +
'<option value="26">Beijing opera</option>' +
'<option value="3">Cantata</option>' +
'<option value="4">Concerto</option>' +
'<option value="20">Étude</option>' +
'<option value="30">Incidental music</option>' +
'<option value="7">Madrigal</option>' +
'<option value="8">Mass</option>' +
'<option value="9">Motet</option>' +
'<option value="29">Musical</option>' +
'<option value="10">Opera</option>' +
'<option value="24">Operetta</option>' +
'<option value="11">Oratorio</option>' +
'<option value="12">Overture</option>' +
'<option value="13">Partita</option>' +
'<option value="28">Play</option>' +
'<option value="21">Poem</option>' +
'<option value="23">Prose</option>' +
'<option value="14">Quartet</option>' +
'<option value="5">Sonata</option>' +
'<option value="15">Song-cycle</option>' +
'<option value="22">Soundtrack</option>' +
'<option value="6">Suite</option>' +
'<option value="18">Symphonic poem</option>' +
'<option value="16">Symphony</option>' +
'<option value="19">Zarzuela</option>' +
'</select>',
    'lang': '<select class="setlang">' +
'<option> </option>' +
'<optgroup label="Frequently used">' +
'<option class="language" value="284">[Multiple languages]</option>' +
'<option class="language" value="486" selected="selected">[No lyrics]</option>' +
'<option class="language" value="18">Arabic</option>' +
'<option class="language" value="76">Chinese</option>' +
'<option class="language" value="98">Czech</option>' +
'<option class="language" value="100">Danish</option>' +
'<option class="language" value="113">Dutch</option>' +
'<option class="language" value="120">English</option>' +
'<option class="language" value="131">Finnish</option>' +
'<option class="language" value="134">French</option>' +
'<option class="language" value="145">German</option>' +
'<option class="language" value="159">Greek</option>' +
'<option class="language" value="195">Italian</option>' +
'<option class="language" value="198">Japanese</option>' +
'<option class="language" value="224">Korean</option>' +
'<option class="language" value="338">Polish</option>' +
'<option class="language" value="340">Portuguese</option>' +
'<option class="language" value="353">Russian</option>' +
'<option class="language" value="393">Spanish</option>' +
'<option class="language" value="403">Swedish</option>' +
'<option class="language" value="433">Turkish</option>' +
'</optgroup>' +
'</select>',
    'key': '<select class="setkey">' +
'<option selected> </option>' +
'<option value="2">C major</option>' +
'<option value="3">C minor</option>' +
'<option value="4">C-sharp major</option>' +
'<option value="5">C-sharp minor</option>' +
'<option value="6">D-flat major</option>' +
'<option value="7">D-flat minor</option>' +
'<option value="8">D major</option>' +
'<option value="9">D minor</option>' +
'<option value="10">D-sharp minor</option>' +
'<option value="11">E-flat major</option>' +
'<option value="12">E-flat minor</option>' +
'<option value="13">E major</option>' +
'<option value="14">E minor</option>' +
'<option value="15">E-sharp minor</option>' +
'<option value="16">F-flat major</option>' +
'<option value="17">F major</option>' +
'<option value="18">F minor</option>' +
'<option value="19">F-sharp major</option>' +
'<option value="20">F-sharp minor</option>' +
'<option value="21">G-flat major</option>' +
'<option value="22">G major</option>' +
'<option value="23">G minor</option>' +
'<option value="24">G-sharp major</option>' +
'<option value="25">G-sharp minor</option>' +
'<option value="26">A-flat major</option>' +
'<option value="27">A-flat minor</option>' +
'<option value="28">A major</option>' +
'<option value="29">A minor</option>' +
'<option value="30">A-sharp minor</option>' +
'<option value="31">B-flat major</option>' +
'<option value="32">B-flat minor</option>' +
'<option value="33">B major</option>' +
'<option value="34">B minor</option>' +
'<option value="789">C Dorian</option>' +
'<option value="790">D Dorian</option>' +
'<option value="791">E Dorian</option>' +
'<option value="792">F Dorian</option>' +
'<option value="793">G Dorian</option>' +
'<option value="794">A Dorian</option>' +
'<option value="795">B Dorian</option>' +
'</select>'
};

// https://www.wikidata.org/wiki/Wikidata:List_of_properties/Person
var wikidata = {
    'entities': {
        person: 5,
        male: 6581097,
        female: 6581072
    },
    'fields': {
        type: 'P31',
        gender: 'P21',
        citizen: 'P27',
        isni: 'P213',
        birthDate: 'P569',
        birthPlace: 'P19',
        deathDate: 'P570',
        deathPlace: 'P20',
        mbidArtist: 'P434',
        mbidArea: 'P982'
    }
};

var requests = function () {
    var self = {};

    self._request = function (verb, url, param, callback) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                    callback(xhr);
            }
        };
        xhr.open(verb, url, true);
        if (verb === 'POST') {
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('Content-length', param.length);
            xhr.setRequestHeader('Connection', 'close');
        }
        xhr.timeout = 10000;
        xhr.ontimeout = function () {
            console.error('The request for ' + url + ' timed out.');
            };
        xhr.send(param);
    };

    self.GET = function (url, callback) {
        self._request('GET', url, null, function (xhr) {
            if (xhr.status === 200 && xhr.responseText !== null) {
                callback(xhr.responseText);
            } else {
                console.log('Error ', xhr.status, ': ', url);
            }
        });
    };

    self.POST = function (url, param, callback) {
        self._request('POST', url, param, function (xhr) {
            callback(xhr.status);
        });
    };
    return self;
}();

var helper = function () {
    var self = {};

    // musicbrainz-server/root/static/scripts/common/utility/formatTrackLength.js
    self.formatTrackLength = function(milliseconds) {
        if (!milliseconds) {
            return '';
        }
        if (milliseconds < 1000) {
            return milliseconds + ' ms';
        }
        var oneMinute = 60;
        var oneHour = 60 * oneMinute;
        var seconds = Math.round(milliseconds / 1000.0);
        var hours = Math.floor(seconds / oneHour);
        seconds = seconds % oneHour;
        var minutes = Math.floor(seconds / oneMinute);
        seconds = seconds % oneMinute;
        var result = ('00' + seconds).slice(-2);
        if (hours > 0) {
            result = hours + ':' + ('00' + minutes).slice(-2) + ':' + result;
        } else {
            result = minutes + ':' + result;
        }
        return result;
    };

    self.comparefct = function(a, b) {
        // Sort function for performers in the recording artist list
        var link = server.link,
            order = [link.vocals, link.instrument, link.orchestra,
                     link.conductor, link.performer];
        if (a.link === b.link) {return 0;}
        return order.indexOf(a.link) > order.indexOf(b.link) ? 1 : -1;
    };
    return self;
}();

var sidebar = function ($) {
    var self = {};

    // edit notes
    self.editNote = function (meta, msg) {
        msg = msg || '';
        var signature = '\n —\n' + 'GM script: "${meta.name}" (${meta.version})\n\n';
        return (msg + signature);
    };

    self.container = function () {
        var $container;
        if ($('div#loujine-sidebar').length) {
            $container = $('div#loujine-sidebar');
        } else {
            $container = $('

', {
                'id': 'loujine-sidebar',
                'css': {
                    'background-color': 'white',
                    'padding': '8px',
                    'margin': '0px -6px 6px',
                    'border': '2px dotted #736DAB'
                }
            }).append(
                $('

', {'text': 'loujine GM tools'})
            );
        }
        $('h2.rating').before($container);
        return $container;
    };

    return self;
}($);

var relEditor = function (MB, $) {
    var self = {};

    // edit note
    self.editNote = function (meta, msg) {
        msg = msg || '';
        var vm = MB.releaseRelationshipEditor,
            existingMsg = vm.editNote(),
            signature = '\n —\n' +
                        'GM script: "${meta.name}" (${meta.version})\n\n';
        vm.editNote(existingMsg + msg + signature);
    };

    self.container = function () {
        var $container;
        if ($('div#loujine-menu').length) {
            $container = $('div#loujine-menu');
        } else {
            $container = $('

', {
                'id': 'loujine-menu',
                'css': {
                    'background-color': 'white',
                    'padding': '8px',
                    'margin': '0px -6px 6px',
                    'border': '5px dotted #736DAB'
                }
            }).append(
                $('

', {'text': 'loujine GM tools'})
            );
        }
        return $container;
    };

    return self;
}(MB, $);
<|endoftext|>