<commit_before>
var substringMatcher = function(strs) {
    return function findMatches(q, cb) {
        var matches, substrRegex;

        // an array that will be populated with substring matches
        matches = [];

        // regex used to determine if a string contains the substring `q`
        substrRegex = new RegExp("^"+q, 'i');

        // iterate through the pool of strings and for any string that
        // contains the substring `q`, add it to the `matches` array
        $.each(strs, function(i, str) {
            if (substrRegex.test(str)) {
                // the typeahead jQuery plugin expects suggestions to a
                // JavaScript object, refer to typeahead docs for more info
                matches.push({ value: str });
            }
        });

        cb(matches);
    };
};

var tournamentInfo = {};
var allPlayers = [];
var tournaments = [];

function getRounds(tournament)
{
    // first see if we have this tournament
    var rounds = [];
//    console.log("getting rounds for tournament: " , tournament)
    if(typeof tournamentInfo[tournament] ==  'undefined')
    {
        console.log("failed to find tournament:", tournament);
        return 'undefined';
    }
    else
    {
        var roundsInfo = tournamentInfo[tournament]["rounds"];
        console.log("!!!rounds info :",JSON.stringify(roundsInfo));
        for(var key in roundsInfo)
        {
            console.log("round = ", key);
            rounds.push(key);
        }
        return rounds;
    }
}

$(function() {

    ["#inputAge", "#inputSibSp", "#inputParc", "#inputFare"].forEach(function(e) {
        $(e).slider({
            formatter: function(value) {
                return 'Current value: ' + value;
            }
        })
    });

    var populateTournaments = function() {
        console.log("populate with list of tournaments ");
        var tournamentsUrl = "../tournaments";
        $("#tournamentsUrlCalled").text(tournamentsUrl);
        $.ajax({
            url: tournamentsUrl,
            dataType: "json",
            error: function (jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + textStatus);
            },
            success: function(data) {
                tournamentInfo = data;
//              console.log("tournaments:", tournamentInfo);

                for (var item in tournamentInfo)
                {
//                  console.log("tournament:", item);
                    tournaments.push(item);
                    var rounds = getRounds(item);
                }

                $('#inputTournament').typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1,
                },
                {
                    name: 'tournaments',
                    displayKey: 'value',
                    source: substringMatcher(tournaments)
                })
                 .on('change', function ($e, datum) {
                    console.log("auto completed on tournaments");
                    console.log(datum,": value ", datum["value"]);
                    // get the rounds for this tournament
                    var theRounds = getRounds(datum["value"]);
                    console.log("the rounds ", theRounds);
                    $('#inputRound').empty();
                    for(var i = 0; i < theRounds.length; ++i)
                    {
                        var html = '<option value="' + theRounds[i] + '">' + theRounds[i] +'</option>';
                        $('#inputRound').append(html);
                    }
                    update();
                })
            }
        });

    };

    var populatePlayers = function() {
        console.log("populate with list of players ");
        var playersUrl = "../players";
        $("#playersUrlCalled").text(playersUrl);
        $.ajax({
            url: playersUrl,
            dataType: "json",
            error: function (jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + textStatus);
            },
            success: function(data) {
//              console.log("players:", data);
                allPlayers = data;
                $('#inputOpponent').typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1,
                },
                {
                    name: 'players',
                    displayKey: 'value',
                    source: substringMatcher(allPlayers)
                })
                 .on('change', function ($e, datum) {
                    console.log("auto completed");
                    console.log("<", datum.value, ">");
                    update();
                })

            }
        });
    };

    // Before calling the classifier we want to make sure that
    var checkValid = function()
    {
        var result = true;
        var opp = $('#inputOpponent').val().trim();

        console.log("the opponent is ", opp);

        if ($.inArray(opp, allPlayers) == -1)
        {
            console.log("failed to find player ",opp);
            //alert("failed to find player " + opp);
            return false;
        }
        else
        {
            console.log("player was found ", opp);
        }

        var tournament = $('#inputTournament').val().trim();
        console.log("the tournament is ", tournament);
        if ($.inArray(tournament, tournaments) == -1)
        {
            //alert("failed to find tournament " + tournament);
            return false
        }
        else
        {
            console.log("player was found ", opp);
        }
        return result;
    };

    var update = function() {
        // make sure that the opponent selected is known
        // check that the opponent is a known player

        var formVals = $("#oddsForm").serialize();
        console.log("the option value is ", $('#inputRound').val());
        var cls = $('#inputCls').val();
        if(checkValid() == false)
            return;

//        var url = "http://"+window.location.host+"/v1/blocks/classifyBlock"+cls+"/apply?"+formVals;
        console.log("About to call multiapply");
        var theUrl = "../multiapply?" + formVals;
        console.log("the url is ", theUrl);
        $("#urlCalled").text(theUrl);
         $.ajax({
           url: theUrl,
           dataType: "json",
           error: function (jqXHR, textStatus, errorThrown) {
               console.log("error on calling multiapply");
               alert(jqXHR.status + textStatus);
           },
           success: function(data) {
               //console.log("About to print response", data.results);
               var theResponse = JSON.parse(data.results[0].response);
               console.log("got data!", theResponse);
               console.log("pins = ", theResponse.pins);
               for(var feature in theResponse) {
                   console.log("feature = ", feature);
               }
               var prob = parseFloat(theResponse.pins.prob) * 100;
               console.log("the probability is ", prob);
               console.log("expl:" , data.results[1].response);
               var theExpl = JSON.parse(data.results[1].response);
               console.log("parsed explanation");
               var oppExpl="";
               var tournamentExpl="";
               var roundExpl="";
               for(var feature in theExpl.pins) {
                   console.log("expl feature = ", feature, " value = ", theExpl.pins[feature])
                   if( feature == "expl:OpponentProbWin" ||
                       feature == "expl:OpponentProbWinVsFed" || feature == "expl:OpponentRank")
                   {
                       oppExpl = oppExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
                   else if ( feature == "expl:Tournament" || feature == "expl:Surface" ||
                             feature == "expl:Court" || feature == "expl:Best of" ||
                             feature == "expl:Series")
                   {
                       tournamentExpl = tournamentExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
                   else if( feature == "expl:Round")
                   {
                       roundExpl = roundExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
               }
               console.log("oppExpl:", oppExpl);
               $("#explainOpponent").text(oppExpl);
               $("#explainTournament").text(tournamentExpl);
               $("#explainRound").text(roundExpl);
               $('.progress-bar').css('width', prob+'%').attr('aria-valuenow', prob);
               $('.progress-bar').text(prob.toFixed(2)+"%");
           }
           });

        console.log("!!!selected item", $('#the-basics .typeahead').typeahead('val'));
    };

    // Update the prob is anything changes
    $("form :input").change(function() {
        update();
    });

    // update on load
    populatePlayers();
    populateTournaments();
//    update();
});
<commit_msg>fixing up ux<commit_after>
var substringMatcher = function(strs) {
    return function findMatches(q, cb) {
        var matches, substrRegex;

        // an array that will be populated with substring matches
        matches = [];

        // regex used to determine if a string contains the substring `q`
        substrRegex = new RegExp("^"+q, 'i');

        // iterate through the pool of strings and for any string that
        // contains the substring `q`, add it to the `matches` array
        $.each(strs, function(i, str) {
            if (substrRegex.test(str)) {
                // the typeahead jQuery plugin expects suggestions to a
                // JavaScript object, refer to typeahead docs for more info
                matches.push({ value: str });
            }
        });

        cb(matches);
    };
};

var tournamentInfo = {};
var allPlayers = [];
var tournaments = [];

function getRounds(tournament)
{
    // first see if we have this tournament
    var rounds = [];
//    console.log("getting rounds for tournament: " , tournament)
    if(typeof tournamentInfo[tournament] ==  'undefined')
    {
        console.log("failed to find tournament:", tournament);
        return 'undefined';
    }
    else
    {
        var roundsInfo = tournamentInfo[tournament]["rounds"];
        console.log("!!!rounds info :",JSON.stringify(roundsInfo));
        for(var key in roundsInfo)
        {
            console.log("round = ", key);
            rounds.push(key);
        }
        return rounds;
    }
}

$(function() {

    ["#inputAge", "#inputSibSp", "#inputParc", "#inputFare"].forEach(function(e) {
        $(e).slider({
            formatter: function(value) {
                return 'Current value: ' + value;
            }
        })
    });

    var populateTournaments = function() {
        console.log("populate with list of tournaments ");
        var tournamentsUrl = "../tournaments";
        $("#tournamentsUrlCalled").text(tournamentsUrl);
        $.ajax({
            url: tournamentsUrl,
            dataType: "json",
            error: function (jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + textStatus);
            },
            success: function(data) {
                tournamentInfo = data;
//              console.log("tournaments:", tournamentInfo);

                for (var item in tournamentInfo)
                {
//                  console.log("tournament:", item);
                    tournaments.push(item);
                    var rounds = getRounds(item);
                }

                $('#inputTournament').typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1,
                },
                {
                    name: 'tournaments',
                    displayKey: 'value',
                    source: substringMatcher(tournaments)
                })
                 .on('typeahead:autocompleted', onAutocompleted)
                 .on('typeahead:selected', onAutocompleted)

                function onAutocompleted($e, datum) {
                    console.log("auto completed on tournaments");
                    console.log(datum,": value ", datum["value"]);
                    // get the rounds for this tournament
                    var theRounds = getRounds(datum["value"]);
                    console.log("the rounds ", theRounds);
                    $('#inputRound').empty();
                    for(var i = 0; i < theRounds.length; ++i)
                    {
                        var html = '<option value="' + theRounds[i] + '">' + theRounds[i] +'</option>';
                        $('#inputRound').append(html);
                    }
                    update();
                }
            }
        });

    };

    var populatePlayers = function() {
        console.log("populate with list of players ");
        var playersUrl = "../players";
        $("#playersUrlCalled").text(playersUrl);
        $.ajax({
            url: playersUrl,
            dataType: "json",
            error: function (jqXHR, textStatus, errorThrown) {
                alert(jqXHR.status + textStatus);
            },
            success: function(data) {
//              console.log("players:", data);
                allPlayers = data;
                $('#inputOpponent').typeahead({
                    hint: true,
                    highlight: true,
                    minLength: 1,
                },
                {
                    name: 'players',
                    displayKey: 'value',
                    source: substringMatcher(allPlayers)
                })
                 .on('typeahead:autocompleted', onAutocompleted)
                 .on('typeahead:selected', onAutocompleted)

                function onAutocompleted($e, datum) {
                    console.log("auto completed");
                    console.log("<", datum.value, ">");
                    update();
                }

            }
        });
    };

    // Before calling the classifier we want to make sure that
    var checkValid = function()
    {
        var result = true;
        var opp = $('#inputOpponent').val().trim();

        console.log("the opponent is ", opp);

        if ($.inArray(opp, allPlayers) == -1)
        {
            console.log("failed to find player ",opp);
            //alert("failed to find player " + opp);
            return false;
        }
        else
        {
            console.log("player was found ", opp);
        }

        var tournament = $('#inputTournament').val().trim();
        console.log("the tournament is ", tournament);
        if ($.inArray(tournament, tournaments) == -1)
        {
            //alert("failed to find tournament " + tournament);
            return false
        }
        else
        {
            console.log("player was found ", opp);
        }
        return result;
    };

    var update = function() {
        // make sure that the opponent selected is known
        // check that the opponent is a known player

        var formVals = $("#oddsForm").serialize();
        console.log("the option value is ", $('#inputRound').val());
        var cls = $('#inputCls').val();
        if(checkValid() == false)
            return;

//        var url = "http://"+window.location.host+"/v1/blocks/classifyBlock"+cls+"/apply?"+formVals;
        console.log("About to call multiapply");
        var theUrl = "../multiapply?" + formVals;
        console.log("the url is ", theUrl);
        $("#urlCalled").text(theUrl);
         $.ajax({
           url: theUrl,
           dataType: "json",
           error: function (jqXHR, textStatus, errorThrown) {
               console.log("error on calling multiapply");
               alert(jqXHR.status + textStatus);
           },
           success: function(data) {
               //console.log("About to print response", data.results);
               var theResponse = JSON.parse(data.results[0].response);
               console.log("got data!", theResponse);
               console.log("pins = ", theResponse.pins);
               for(var feature in theResponse) {
                   console.log("feature = ", feature);
               }
               var prob = parseFloat(theResponse.pins.prob) * 100;
               console.log("the probability is ", prob);
               console.log("expl:" , data.results[1].response);
               var theExpl = JSON.parse(data.results[1].response);
               console.log("parsed explanation");
               var oppExpl="";
               var tournamentExpl="";
               var roundExpl="";
               for(var feature in theExpl.pins) {
                   console.log("expl feature = ", feature, " value = ", theExpl.pins[feature])
                   if( feature == "expl:OpponentProbWin" ||
                       feature == "expl:OpponentProbWinVsFed" || feature == "expl:OpponentRank")
                   {
                       oppExpl = oppExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
                   else if ( feature == "expl:Tournament" || feature == "expl:Surface" ||
                             feature == "expl:Court" || feature == "expl:Best of" ||
                             feature == "expl:Series")
                   {
                       tournamentExpl = tournamentExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
                   else if( feature == "expl:Round")
                   {
                       roundExpl = roundExpl + "(" + feature.substr(5) + ":" + theExpl.pins[feature] + ")"
                   }
               }
               console.log("oppExpl:", oppExpl);
               $("#explainOpponent").text(oppExpl);
               $("#explainTournament").text(tournamentExpl);
               $("#explainRound").text(roundExpl);
               $('.progress-bar').css('width', prob+'%').attr('aria-valuenow', prob);
               $('.progress-bar').text(prob.toFixed(2)+"%");
           }
           });

        console.log("!!!selected item", $('#the-basics .typeahead').typeahead('val'));
    };

    // Update the prob is anything changes
    $("form :input").change(function() {
        update();
    });

    // update on load
    populatePlayers();
    populateTournaments();
//    update();
});
<|endoftext|>


<commit_before>var xsIn = ['_R_F', 'J\\_<', '_H_T', 'G\\_<', '_B_L', '\|\\\|\\', 'r\\`', '<R>', 'g_<', '<F>', 'd_<', 'b_<', '_\?\\', 'z\\', 'z`', 'X\\', 'x\\', '_x', '_X', '_w', 'v\\', '_v', 'U\\', 't`', '_t', '_T', 's\\', 's`', 'r\\', 'r`', '_r', 'R\\', '_R', '_q', 'p\\', '_o', 'O\\', '_O', 'n`', '_n', 'N\\', '_N', '_m', 'M\\', '_M', 'l\\', 'l`', '_l', 'L\\', '_L', '_k', 'K\\', 'j\\', '_j', 'J\\', 'I\\', 'h\\', '_h', 'H\\', '_H', 'G\\', '_G', '_F', '_e', 'd`', '_d', '_c', 'B\\', '_B', '_a', '_A', '3\\', '_0', '@\\', '\?\\', '\!\\', ':\\', '\-\\', '_\+', '_\\', '_\}', '_"', '_/', '_\-', '_>', '_=', '_~', '_\^', '\|\\', '\|\|', '>\\', '=\\', '<\\', 'Z', 'z', 'y', 'Y', 'X', 'x', 'w', 'W', 'v', 'V', 'u', 'U', 'T', 't', 's', 'S', 'r', 'R', 'q', 'Q', 'p', 'P', 'O', 'o', 'N', 'n', 'm', 'M', 'l', 'L', 'k', 'K', 'j', 'J', 'i', 'I', 'h', 'H', 'g', 'G', 'f', 'F', 'E', 'e', '@', 'D', 'd', 'C', 'c', 'B', 'b', '{', 'a', 'A', '9', '8', '7', '6', '5', '4', '3', '2', '1', '%', '&', '\}', '"', '\'', '\.', '\?', '\!', ':', '\|', '=', '~', '\^', '`'];
var ipaOut = [' ᷈', 'ʄ', ' ᷄', 'ʛ', ' ᷅', 'ǁ', 'ɻ', '↗', 'ɠ', '↘', 'ɗ', 'ɓ', 'ˤ', 'ʑ', 'ʐ', 'ħ', 'ɧ', '  ̽', '  ̆', 'ʷ', 'ʋ', '  ̬', 'ᵿ', 'ʈ', '  ̤', '  ̋', 'ɕ', 'ʂ', 'ɹ', 'ɽ', '  ̝', 'ʀ', '  ̌', '  ̙', 'ɸ', '  ̞', 'ʘ', '  ̹', 'ɳ', 'ⁿ', 'ɴ', '  ̼', '  ̻', 'ɰ', '  ̄', 'ɺ', 'ɭ', 'ˡ', 'ʟ', '  ̀', '  ̰', 'ɮ', 'ʝ', 'ʲ', 'ɟ', 'ᵻ', 'ɦ', 'ʰ', 'ʜ', '  ́', 'ɢ', 'ˠ', '  ̂', '  ̴', 'ɖ', '  ̪', '  ̜', 'ʙ', '  ̏', '  ̺', '  ̘', 'ɞ', '  ̥', 'ɘ', 'ʕ', 'ǃ', 'ˑ', '‿', '  ̟', '̂', '  ̚', '  ̈', '̌', '  ̠', 'ʼ', '  ̩', '̃', '  ̯', 'ǀ', '‖', 'ʡ', 'ǂ', 'ʢ', 'ʒ', 'z', 'y', 'ʏ', 'χ', 'x', 'w', 'ʍ', 'v', 'ʌ', 'u', 'ʊ', 'θ', 't', 's', 'ʃ', 'r', 'ʁ', 'q', 'ɒ', 'p', 'ʋ', 'ɔ', 'o', 'ŋ', 'n', 'm', 'ɯ', 'l', 'ʎ', 'k', 'ɬ', 'j', 'ɲ', 'i', 'ɪ', 'h', 'ɥ', 'ɡ', 'ɣ', 'f', 'ɱ', 'ɛ', 'e', 'ə', 'ð', 'd', 'ç', 'c', 'β', 'b', 'æ', 'a', 'ɑ', 'œ', 'ɵ', 'ɤ', 'ɐ', 'ɫ', 'ɾ', 'ɜ', 'ø', 'ɨ', 'ˌ', 'ɶ', 'ʉ', '\ˈ', 'ʲ', '.', 'ʔ', 'ꜜ', 'ː', '|', '̩', '̃', 'ꜛ', '˞'];

function ipa() {
  RegExp.escape = function(s) {
    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  var output = document.getElementById("outbox");
  var input = document.getElementById("textbox");

  output.value = input.value;

  for (t in xsIn) {
    var re = new RegExp(RegExp.escape(xsIn[t]), "g");
    output.value = output.value.replace(re, ipaOut[t]);
  }
  htmlOut();
}

function geturl() {
  var url = location.href;
  if (/\?/.test(url) == true) {
    var args = /\?(.*)/.exec(url)[1];
    args=/\+/.test(args) ? args.replace(/\+/g,' ') : args;
    var input = document.getElementById("textbox");
    input.value = decodeURIComponent(args);
  }
}

function htmlOut() {
  var htmlout = document.getElementById("htmlout");
  var input = document.getElementById("outbox");
  htmlout.innerHTML = input.value.replace(/\n/g, "
");
}

function legend() {
  xsort = xsIn.slice(0).sort();
  ltext = document.getElementById("ltext");

  txt = "";
  ltext.innerHTML = "";
  for (t in xsort) {
    i = xsIn.indexOf(xsort[t]);
    txt = txt + "
" + xsIn[i] + "
 →
" + ipaOut[i] + "

";
  }
  var output = document.getElementById("outbox");
  ltext.innerHTML = txt;
}
<commit_msg>+legend toggle<commit_after>var xsIn = ['_R_F', 'J\\_<', '_H_T', 'G\\_<', '_B_L', '\|\\\|\\', 'r\\`', '<R>', 'g_<', '<F>', 'd_<', 'b_<', '_\?\\', 'z\\', 'z`', 'X\\', 'x\\', '_x', '_X', '_w', 'v\\', '_v', 'U\\', 't`', '_t', '_T', 's\\', 's`', 'r\\', 'r`', '_r', 'R\\', '_R', '_q', 'p\\', '_o', 'O\\', '_O', 'n`', '_n', 'N\\', '_N', '_m', 'M\\', '_M', 'l\\', 'l`', '_l', 'L\\', '_L', '_k', 'K\\', 'j\\', '_j', 'J\\', 'I\\', 'h\\', '_h', 'H\\', '_H', 'G\\', '_G', '_F', '_e', 'd`', '_d', '_c', 'B\\', '_B', '_a', '_A', '3\\', '_0', '@\\', '\?\\', '\!\\', ':\\', '\-\\', '_\+', '_\\', '_\}', '_"', '_/', '_\-', '_>', '_=', '_~', '_\^', '\|\\', '\|\|', '>\\', '=\\', '<\\', 'Z', 'z', 'y', 'Y', 'X', 'x', 'w', 'W', 'v', 'V', 'u', 'U', 'T', 't', 's', 'S', 'r', 'R', 'q', 'Q', 'p', 'P', 'O', 'o', 'N', 'n', 'm', 'M', 'l', 'L', 'k', 'K', 'j', 'J', 'i', 'I', 'h', 'H', 'g', 'G', 'f', 'F', 'E', 'e', '@', 'D', 'd', 'C', 'c', 'B', 'b', '{', 'a', 'A', '9', '8', '7', '6', '5', '4', '3', '2', '1', '%', '&', '\}', '"', '\'', '\.', '\?', '\!', ':', '\|', '=', '~', '\^', '`'];
var ipaOut = [' ᷈', 'ʄ', ' ᷄', 'ʛ', ' ᷅', 'ǁ', 'ɻ', '↗', 'ɠ', '↘', 'ɗ', 'ɓ', 'ˤ', 'ʑ', 'ʐ', 'ħ', 'ɧ', '  ̽', '  ̆', 'ʷ', 'ʋ', '  ̬', 'ᵿ', 'ʈ', '  ̤', '  ̋', 'ɕ', 'ʂ', 'ɹ', 'ɽ', '  ̝', 'ʀ', '  ̌', '  ̙', 'ɸ', '  ̞', 'ʘ', '  ̹', 'ɳ', 'ⁿ', 'ɴ', '  ̼', '  ̻', 'ɰ', '  ̄', 'ɺ', 'ɭ', 'ˡ', 'ʟ', '  ̀', '  ̰', 'ɮ', 'ʝ', 'ʲ', 'ɟ', 'ᵻ', 'ɦ', 'ʰ', 'ʜ', '  ́', 'ɢ', 'ˠ', '  ̂', '  ̴', 'ɖ', '  ̪', '  ̜', 'ʙ', '  ̏', '  ̺', '  ̘', 'ɞ', '  ̥', 'ɘ', 'ʕ', 'ǃ', 'ˑ', '‿', '  ̟', '̂', '  ̚', '  ̈', '̌', '  ̠', 'ʼ', '  ̩', '̃', '  ̯', 'ǀ', '‖', 'ʡ', 'ǂ', 'ʢ', 'ʒ', 'z', 'y', 'ʏ', 'χ', 'x', 'w', 'ʍ', 'v', 'ʌ', 'u', 'ʊ', 'θ', 't', 's', 'ʃ', 'r', 'ʁ', 'q', 'ɒ', 'p', 'ʋ', 'ɔ', 'o', 'ŋ', 'n', 'm', 'ɯ', 'l', 'ʎ', 'k', 'ɬ', 'j', 'ɲ', 'i', 'ɪ', 'h', 'ɥ', 'ɡ', 'ɣ', 'f', 'ɱ', 'ɛ', 'e', 'ə', 'ð', 'd', 'ç', 'c', 'β', 'b', 'æ', 'a', 'ɑ', 'œ', 'ɵ', 'ɤ', 'ɐ', 'ɫ', 'ɾ', 'ɜ', 'ø', 'ɨ', 'ˌ', 'ɶ', 'ʉ', '\ˈ', 'ʲ', '.', 'ʔ', 'ꜜ', 'ː', '|', '̩', '̃', 'ꜛ', '˞'];

function ipa() {
  RegExp.escape = function(s) {
    return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
  };

  var output = document.getElementById("outbox");
  var input = document.getElementById("textbox");

  output.value = input.value;

  for (t in xsIn) {
    var re = new RegExp(RegExp.escape(xsIn[t]), "g");
    output.value = output.value.replace(re, ipaOut[t]);
  }
  htmlOut();
}

function geturl() {
  var url = location.href;
  if (/\?/.test(url) == true) {
    var args = /\?(.*)/.exec(url)[1];
    args=/\+/.test(args) ? args.replace(/\+/g,' ') : args;
    var input = document.getElementById("textbox");
    input.value = decodeURIComponent(args);
  }
}

function htmlOut() {
  var htmlout = document.getElementById("htmlout");
  var input = document.getElementById("outbox");
  htmlout.innerHTML = input.value.replace(/\n/g, "
");
}

function toggleLegend() {
  legendBox = document.getElementsByClassName("legend-box")[0];
  state = legendBox.style.display;
  if (state == "") {
    genLegend();
    legendBox.style.display = "inline";
  } else if (state == "none") {
    legendBox.style.display = "inline";
  } else {
    legendBox.style.display = "none";
  }
}

function genLegend() {
  xsort = xsIn.slice(0).sort();
  ltext = document.getElementById("ltext");

  txt = "";
  ltext.innerHTML = "";
  for (t in xsort) {
    i = xsIn.indexOf(xsort[t]);
    txt = txt + "
" + xsIn[i] + "
 →
" + ipaOut[i] + "

";
  }
  var output = document.getElementById("outbox");
  ltext.innerHTML = txt;
}
<|endoftext|>


<commit_before>/*
 * grunt-assets-inline
 * https://github.com/sparanoid/assets-inline
 *
 * Copyright (c) 2015 Tunghsiao Liu
 * Licensed under the MIT license.
 */

/*
 * grunt-html-smoosher
 * https://github.com/motherjones/grunt-html-smoosher
 *
 * Copyright (c) 2013 Ben Breedlove
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  'use strict';

  var cheerio = require('cheerio');
  var path = require('path');
  var url = require('url');
  var uglifyjs = require('uglify-js');

  grunt.registerMultiTask('assets_inline', 'Turn your distribution into something pastable.', function() {

    var options = this.options({
      jsDir: "",
      cssDir: "",
      assetsDir: "",
      minify: false,
      inlineImg: false,
      inlineSvg: true,
      inlineSvgBase64: false,
      includeTag: "",
      assetsUrlPrefix: "",
      verbose: false,
      deleteOriginals: false

    });

    options.cssTags = this.options().cssTags || {
      start: '<style>',
      end: '</style>'
    };

    options.jsTags = this.options().jsTags || {
      start: '<script>',
      end: '</script>'
    };

    var uglifyJS = function(i){return i;};

    if (options.minify){
      uglifyJS = function(input){
        return uglifyjs.minify(input, {fromString: true}).code;
      };
    }

    var processSvg = function(i){return i;};

    processSvg = function(input){
      // replace double quotes with single quotes and remove line breaks for non-base64 SVG inlining.
      return input.replace(/"/g, "'").replace(/(?:\r\n|\r|\n)/g, "");
    };

    var filesToDelete = [];

    this.files.forEach(function(filePair) {
      // Check that the source file exists
      if(filePair.src.length === 0) { return; }

      // init cheerio
      var $ = cheerio.load(grunt.file.read(filePair.src), {
        decodeEntities: false
      });

      grunt.log.writeln(('Reading: ').green + path.resolve(filePair.src.toString()));

      $('style[data-assets-inline]').each(function () {
        var style = $(this).html();
        if(!style) { return; }

        var items = [];
        // https://regex101.com/r/yP1yK0/1
        var regex = /url\(["']?(.*?)["']?\)/ig;
        var item;

        while (item = regex.exec(style)) {
          items.push(item[1]);
        }

        items.forEach(function(v) {
          var src = v;
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (!src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === '/') ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln((' inline: ').cyan + filePath);

          if (options.inlineSvgBase64) {
            style = style.replace(src, 'data:image/svg+xml;base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));
          } else {
            style = style.replace(src, 'data:image/svg+xml;utf8,' + processSvg(grunt.file.read(filePath)));
          }
        });

        $(this).text(style);
      });

      $('link[rel="stylesheet"]').each(function () {
        var style = $(this).attr('href');
        if(!style) { return; }
        if(style.match(/^\/\//)) { return; }
        if(style.indexOf(options.includeTag) === -1) { return; }
        style = style.replace(/\?.+$/, "");

        //get attributes to keep them on the new element
        var attributes = getAttributes($(this));
        if (attributes.href){
          //don't want to re-include the href
          delete attributes.href;
        }
        if (attributes.rel){
          //don't want to rel
          delete attributes.rel;
        }

        if(url.parse(style).protocol) { return; }
        var filePath = (style.substr(0,1) === "/") ? path.resolve(options.cssDir, style.substr(1)) : path.join(path.dirname(filePair.src), style);
        grunt.log.writeln(('    css: ').cyan + filePath);
        $(this).replaceWith(options.cssTags.start + grunt.file.read(filePath) + options.cssTags.end);

        if (options.deleteOriginals) {
          filesToDelete.push(filePath);
        }
      });

      $('script').each(function () {
        var script = $(this).attr('src');
        if(!script) { return; }
        if(script.match(/^\/\//)) { return; }
        if(script.indexOf(options.includeTag) === -1) { return; }
        if(url.parse(script).protocol) { return; }
        script = script.replace(/\?.+$/, "");

        //get attributes to keep them on the new element
        var attributes = getAttributes($(this));
        if (attributes.src){
          delete attributes.src;
        }

        var filePath = (script.substr(0,1) === "/") ? path.resolve(options.jsDir, script.substr(1)) : path.join(path.dirname(filePair.src), script);
        grunt.log.writeln(('     js: ').cyan + filePath);

        //create and replace script with new scipt tag
        $(this).replaceWith(options.jsTags.start + uglifyJS(grunt.file.read(filePath)) + options.jsTags.end);

        if (options.deleteOriginals) {
          filesToDelete.push(filePath);
        }
      });

      if (options.inlineSvg) {
        $('img').each(function () {
          var src = $(this).attr('src');
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (!src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === "/") ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln(('    svg: ').cyan + filePath);

          if (options.inlineSvgBase64) {
            $(this).attr('src', 'data:image/svg+xml;base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));
          } else {
            $(this).attr('src', 'data:image/svg+xml;utf8,' + processSvg(grunt.file.read(filePath)));
          }

          if (options.deleteOriginals) {
            filesToDelete.push(filePath);
          }
        });
      }

      if (options.inlineImg) {
        $('img').each(function () {
          var src = $(this).attr('src');
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === "/") ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln(('  image: ').cyan + filePath);

          $(this).attr('src', 'data:image/' + src.substr(src.lastIndexOf('.')+1) + ';base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));

          if (options.deleteOriginals) {
            filesToDelete.push(filePath);
          }
        });
      }

      var html = $.html();
      // replace relative path
      html = html.replace(/[.]{2}\//g, options.assetsUrlPrefix);
      grunt.file.write(path.resolve(filePair.dest), html);
      grunt.log.writeln(('Created: ').green + path.resolve(filePair.dest) + '\n');
    });

    // Delete the original files
    if (options.deleteOriginals) {
      filesToDelete.forEach(function(filename) {
        if (grunt.file.exists(filename)) {
          grunt.file.delete(filename);
          grunt.log.writeln(('Removed: ').green + filename);
        }
      });
    }

    function getAttributes(el) {
      var attributes = {};
      for (var index in el.attr) {
        var attr = el.attr[index];
        if (options.verbose) {
          grunt.log.writeln(('   attr: ').blue + index + ":" + attr);
        }
        attributes[ index ] = attr;
      }
      return attributes;
    }
  });
};
<commit_msg>fix(core): inlined assets not deleted<commit_after>/*
 * grunt-assets-inline
 * https://github.com/sparanoid/assets-inline
 *
 * Copyright (c) 2015 Tunghsiao Liu
 * Licensed under the MIT license.
 */

/*
 * grunt-html-smoosher
 * https://github.com/motherjones/grunt-html-smoosher
 *
 * Copyright (c) 2013 Ben Breedlove
 * Licensed under the MIT license.
 */

module.exports = function(grunt) {

  'use strict';

  var cheerio = require('cheerio');
  var path = require('path');
  var url = require('url');
  var uglifyjs = require('uglify-js');

  grunt.registerMultiTask('assets_inline', 'Turn your distribution into something pastable.', function() {

    var options = this.options({
      jsDir: "",
      cssDir: "",
      assetsDir: "",
      minify: false,
      inlineImg: false,
      inlineSvg: true,
      inlineSvgBase64: false,
      includeTag: "",
      assetsUrlPrefix: "",
      verbose: false,
      deleteOriginals: false

    });

    options.cssTags = this.options().cssTags || {
      start: '<style>',
      end: '</style>'
    };

    options.jsTags = this.options().jsTags || {
      start: '<script>',
      end: '</script>'
    };

    var uglifyJS = function(i){return i;};

    if (options.minify){
      uglifyJS = function(input){
        return uglifyjs.minify(input, {fromString: true}).code;
      };
    }

    var processSvg = function(i){return i;};

    processSvg = function(input){
      // replace double quotes with single quotes and remove line breaks for non-base64 SVG inlining.
      return input.replace(/"/g, "'").replace(/(?:\r\n|\r|\n)/g, "");
    };

    var filesToDelete = [];

    this.files.forEach(function(filePair) {
      // Check that the source file exists
      if(filePair.src.length === 0) { return; }

      // init cheerio
      var $ = cheerio.load(grunt.file.read(filePair.src), {
        decodeEntities: false
      });

      grunt.log.writeln(('Reading: ').green + path.resolve(filePair.src.toString()));

      $('style[data-assets-inline]').each(function () {
        var style = $(this).html();
        if(!style) { return; }

        var items = [];
        // https://regex101.com/r/yP1yK0/1
        var regex = /url\(["']?(.*?)["']?\)/ig;
        var item;

        while (item = regex.exec(style)) {
          items.push(item[1]);
        }

        items.forEach(function(v) {
          var src = v;
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (!src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === '/') ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln((' inline: ').cyan + filePath);

          if (options.inlineSvgBase64) {
            style = style.replace(src, 'data:image/svg+xml;base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));
          } else {
            style = style.replace(src, 'data:image/svg+xml;utf8,' + processSvg(grunt.file.read(filePath)));
          }

          if (options.deleteOriginals) {
            filesToDelete.push(filePath);
          }
        });

        $(this).text(style);
      });

      $('link[rel="stylesheet"]').each(function () {
        var style = $(this).attr('href');
        if(!style) { return; }
        if(style.match(/^\/\//)) { return; }
        if(style.indexOf(options.includeTag) === -1) { return; }
        style = style.replace(/\?.+$/, "");

        //get attributes to keep them on the new element
        var attributes = getAttributes($(this));
        if (attributes.href){
          //don't want to re-include the href
          delete attributes.href;
        }
        if (attributes.rel){
          //don't want to rel
          delete attributes.rel;
        }

        if(url.parse(style).protocol) { return; }
        var filePath = (style.substr(0,1) === "/") ? path.resolve(options.cssDir, style.substr(1)) : path.join(path.dirname(filePair.src), style);
        grunt.log.writeln(('    css: ').cyan + filePath);
        $(this).replaceWith(options.cssTags.start + grunt.file.read(filePath) + options.cssTags.end);

        if (options.deleteOriginals) {
          filesToDelete.push(filePath);
        }
      });

      $('script').each(function () {
        var script = $(this).attr('src');
        if(!script) { return; }
        if(script.match(/^\/\//)) { return; }
        if(script.indexOf(options.includeTag) === -1) { return; }
        if(url.parse(script).protocol) { return; }
        script = script.replace(/\?.+$/, "");

        //get attributes to keep them on the new element
        var attributes = getAttributes($(this));
        if (attributes.src){
          delete attributes.src;
        }

        var filePath = (script.substr(0,1) === "/") ? path.resolve(options.jsDir, script.substr(1)) : path.join(path.dirname(filePair.src), script);
        grunt.log.writeln(('     js: ').cyan + filePath);

        //create and replace script with new scipt tag
        $(this).replaceWith(options.jsTags.start + uglifyJS(grunt.file.read(filePath)) + options.jsTags.end);

        if (options.deleteOriginals) {
          filesToDelete.push(filePath);
        }
      });

      if (options.inlineSvg) {
        $('img').each(function () {
          var src = $(this).attr('src');
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (!src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === "/") ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln(('    svg: ').cyan + filePath);

          if (options.inlineSvgBase64) {
            $(this).attr('src', 'data:image/svg+xml;base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));
          } else {
            $(this).attr('src', 'data:image/svg+xml;utf8,' + processSvg(grunt.file.read(filePath)));
          }

          if (options.deleteOriginals) {
            filesToDelete.push(filePath);
          }
        });
      }

      if (options.inlineImg) {
        $('img').each(function () {
          var src = $(this).attr('src');
          if (!src) { return; }
          if (src.match(/^\/\//)) { return; }
          if (src.match(/.svg$/i)) { return; }
          if (url.parse(src).protocol) { return; }

          var filePath = (src.substr(0,1) === "/") ? path.resolve(options.assetsDir, src.substr(1)) : path.join(path.dirname(filePair.src), src);
          grunt.log.writeln(('  image: ').cyan + filePath);

          $(this).attr('src', 'data:image/' + src.substr(src.lastIndexOf('.')+1) + ';base64,' + new Buffer(grunt.file.read(filePath, { encoding: null })).toString('base64'));

          if (options.deleteOriginals) {
            filesToDelete.push(filePath);
          }
        });
      }

      var html = $.html();
      // replace relative path
      html = html.replace(/[.]{2}\//g, options.assetsUrlPrefix);
      grunt.file.write(path.resolve(filePair.dest), html);
      grunt.log.writeln(('Created: ').green + path.resolve(filePair.dest) + '\n');
    });

    // Delete the original files
    if (options.deleteOriginals) {
      filesToDelete.forEach(function(filename) {
        if (grunt.file.exists(filename)) {
          grunt.file.delete(filename);
          grunt.log.writeln(('Removed: ').green + filename);
        }
      });
    }

    function getAttributes(el) {
      var attributes = {};
      for (var index in el.attr) {
        var attr = el.attr[index];
        if (options.verbose) {
          grunt.log.writeln(('   attr: ').blue + index + ":" + attr);
        }
        attributes[ index ] = attr;
      }
      return attributes;
    }
  });
};
<|endoftext|>


<commit_before>// grunt-blanket-mocha 0.3.3
//
// Copyright (C) 2013 Dave Cadwallader, Model N, Inc.
// Distributed under the MIT License
//
// Documentation and full license available at:
// https://github.com/ModelN/grunt-blanket-qunit
//
// Based on grunt-mocha
// https://github.com/kmiyashiro/grunt-mocha
// Copyright (c) 2012 "Cowboy" Ben Alman, contributors

'use strict';

// Nodejs libs.
var _             = require('lodash');
var util          = require('util');
var path          = require('path');
var EventEmitter  = require('events').EventEmitter;
var reporters     = require('mocha').reporters;
// Helpers
var helpers       = require('../support/mocha-helpers');

module.exports = function(grunt) {

    var ok, totals, status, coverageThreshold, modulePattern, modulePatternRegex, excludedFiles, customThreshold, customModuleThreshold;
    // External lib.
    var phantomjs = require('grunt-lib-phantomjs').init(grunt);

    var reporter;

    // Growl is optional
    var growl;
    try {
        growl = require('growl');
    } catch(e) {
        growl = function(){};
        grunt.verbose.write('Growl not found, \'npm install growl\' for Growl support');
    }

    // Get an asset file, local to the root of the project.
    var asset = path.join.bind(null, __dirname, '..');

    var printPassFailMessage = function(name, numCovered, numTotal, threshold, printPassing) {
        var percent = (numCovered / numTotal) * 100;

        var pass = (percent >= threshold);

        // If not passed, check if file is marked for manual exclusion. Else, fail it.
        var exclude = _(excludedFiles).some(function (o) {return o.test(name);});
        var result = pass ? "PASS" : ( exclude ? "SKIP" : "FAIL");

        var percentDisplay = Math.floor(percent);
        if (percentDisplay < 10) {
            percentDisplay = "  " + percentDisplay;
        } else if (percentDisplay < 100) {
            percentDisplay = " " + percentDisplay;
        }

        var operator;
        if (percentDisplay > threshold) {
            operator = ">";
        } else if (percentDisplay < threshold) {
            operator = "<";
        } else {
            operator = "=";
        }

        var msg = result + " [" + percentDisplay + "% " + operator +  " " + threshold  + "% ] : " +
                name + " (" + numCovered + " / " + numTotal + ")";

        status.blanketTotal++;
        if (pass) {
            status.blanketPass++;
            if (printPassing || grunt.option('verbose')) {
                grunt.log.writeln(msg.green);
            }
        } else if (result ===  "SKIP"){ //Visually mark that these have been skipped.
            grunt.log.writeln(msg.magenta);
        } else {
            ok = false;
            status.blanketFail++;
            grunt.log.writeln(msg.red);
        }

    };

    // Manage runners listening to phantomjs
    var phantomjsEventManager = (function() {
        var listeners = {};
        var suites = [];

        phantomjs.on('blanket:done', function() {
            phantomjs.halt();
        });

        phantomjs.on('blanket:fileDone', function(thisTotal, filename) {

            if (status.blanketPass === 0 && status.blanketFail === 0 ) {
                grunt.log.writeln();
            }

            var coveredLines = thisTotal[0];
            var totalLines = thisTotal[1];

            var threshold = coverageThreshold;

            // Check for a custom threshold
            var custom = _.find(customThreshold, function (o) {
                return o[0].test(filename);
            });
            if (custom !== undefined) {
                threshold = custom[1];
            }

            printPassFailMessage(filename, coveredLines, totalLines, threshold);

            totals.totalLines += totalLines;
            totals.coveredLines += coveredLines;

            if (modulePatternRegex) {
                var moduleName = filename.match(modulePatternRegex)[1];
                if(!totals.moduleTotalStatements.hasOwnProperty(moduleName)) {
                    totals.moduleTotalStatements[moduleName] = 0;
                    totals.moduleTotalCoveredStatements[moduleName] = 0;
                }

                totals.moduleTotalStatements[moduleName] += totalLines;
                totals.moduleTotalCoveredStatements[moduleName] += coveredLines;
            }
        });

        // Hook on Phantomjs Mocha reporter events.
        phantomjs.on('mocha.*', function(test) {
            var name, fullTitle, slow, err;
            var evt = this.event.replace('mocha.', '');

            // Expand test values (and façace the Mocha test object)
            if (test) {
                fullTitle = test.fullTitle;
                test.fullTitle = function() {
                    return fullTitle;
                };

                slow = this.slow;
                test.slow = function() {
                    return slow;
                };

                test.parent = suites[suites.length - 1] || null;

                err = test.err;
            }

            if (evt === 'suite') {
                suites.push(test);
            } else if (evt === 'suite end') {
                suites.pop(test);
            }

            // Trigger events for each runner listening
            for (name in listeners) {
                listeners[name].emit.call(listeners[name], evt, test, err);
            }
        });

        return {
            add: function(name, runner) {
                listeners[name] = runner;
            },
            remove: function(name) {
                delete listeners[name];
            }
        };
    }());

    // Built-in error handlers.
    phantomjs.on('fail.load', function(url) {
        phantomjs.halt();
        grunt.verbose.write('Running PhantomJS...').or.write('...');
        grunt.log.error();
        grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
    });

    phantomjs.on('fail.timeout', function() {
        phantomjs.halt();
        grunt.log.writeln();
        grunt.warn('PhantomJS timed out, possibly due to a missing Mocha run() call.', 90);
    });

    // Debugging messages.
    phantomjs.on('debug', grunt.log.debug.bind(grunt.log, 'phantomjs'));

    // ==========================================================================
    // TASKS
    // ==========================================================================

    grunt.registerMultiTask('blanket_mocha', 'Run Mocha unit tests in a headless PhantomJS instance.', function() {

        // Merge task-specific and/or target-specific options with these defaults.
        var options = this.options({
            // Output console.log calls
            log: false,
            // Mocha reporter
            reporter: 'Dot',
            // Default PhantomJS timeout.
            timeout: 5000,
            // Mocha-PhantomJS bridge file to be injected.
            inject: asset('phantomjs/bridge.js'),
            // Main PhantomJS script file
            phantomScript: asset('phantomjs/main.js'),
            // Explicit non-file URLs to test.
            urls: [],
            // Fail with grunt.warn on first test failure
            bail: false,
            // Log script errors as grunt errors
            logErrors: false
        });

        ok = true;
        totals = {
            totalLines: 0,
            coveredLines: 0,
            moduleTotalStatements : {},
            moduleTotalCoveredStatements : {}
        };

        status = {blanketTotal: 0, blanketPass: 0, blanketFail: 0};
        coverageThreshold = grunt.option('threshold') || options.threshold;

        modulePattern = grunt.option('modulePattern') || options.modulePattern;
        if (modulePattern) {
            modulePatternRegex = new RegExp(modulePattern);
        }

        var grep = grunt.option('grep');
        options.mocha = options.mocha || {};

        // Get the array of excludedFiles, normalize and prepare them
        // Users should be able to define it in the command-line as an array or include it in the test file.
        excludedFiles =  grunt.option('excludedFiles') || options.excludedFiles || [];
        excludedFiles = _(excludedFiles).map(function (o) {
            return new RegExp(path.normalize(o) + '$');
        }).value();

        // Get the custom thresholds for files, normalize and prepare the file names
        customThreshold = grunt.option('customThreshold') || options.customThreshold || {};
        customThreshold = _(customThreshold).pairs().map(function (o) {
            return [new RegExp(path.normalize(o[0]) + '$'), o[1]];
        }).value();

        customModuleThreshold = grunt.option('customModuleThreshold') || options.customModuleThreshold|| {};

        if (grep) {
            options.mocha.grep = grep;
        }

        // Output console messages if log == true
        if (options.log) {
            phantomjs.removeAllListeners(['console']);
            phantomjs.on('console', grunt.log.writeln);
        } else {
            phantomjs.off('console', grunt.log.writeln);
        }

        // Output errors on script errors
        if (options.logErrors) {
            phantomjs.on('error.*', function(error, stack) {
                var formattedStack = _.map(stack, function(frame) {
                    return "    at " + (frame.function ? frame.function : "undefined") + " (" + frame.file + ":" + frame.line + ")";
                }).join("\n");
                grunt.fail.warn(error + "\n" + formattedStack, 3);
            });
        }

        var optsStr = JSON.stringify(options, null, '  ');
        grunt.verbose.writeln('Options: ' + optsStr);

        // Clean Phantomjs options to prevent any conflicts
        var PhantomjsOptions = _.omit(options, 'reporter', 'urls', 'log', 'bail');

        var phantomOptsStr = JSON.stringify(PhantomjsOptions, null, '  ');
        grunt.verbose.writeln('Phantom options: ' + phantomOptsStr);

        // Combine any specified URLs with src files.
        var urls = options.urls.concat(_.compact(this.filesSrc));

        // Remember all stats from all tests
        var testStats = [];

        // This task is asynchronous.
        var done = this.async();

        // Hijack console.log to capture reporter output
        var dest = this.data.dest;
        var output = [];
        var consoleLog = console.log;
        // Latest mocha xunit reporter sends to process.stdout instead of console
        var processWrite = process.stdout.write;

        // Only hijack if we really need to
        if (dest) {
            console.log = function() {
                consoleLog.apply(console, arguments);
                // FIXME: This breaks older versions of mocha
                // processWrite.apply(process.stdout, arguments);
                output.push(util.format.apply(util, arguments));
            };
        }

        // Process each filepath in-order.
        grunt.util.async.forEachSeries(urls, function(url, next) {
                    grunt.log.writeln('Testing: ' + url);

                    // create a new mocha runner façade
                    var runner = new EventEmitter();
                    phantomjsEventManager.add(url, runner);

                    // Clear runner event listener when test is over
                    runner.on('end', function() {
                        phantomjsEventManager.remove(url);
                    });

                    // Set Mocha reporter
                    var Reporter = null;
                    if (reporters[options.reporter]) {
                        Reporter = reporters[options.reporter];
                    } else {
                        // Resolve external reporter module
                        var externalReporter;
                        try {
                            externalReporter = require.resolve(options.reporter);
                        } catch (e) {
                            // Resolve to local path
                            externalReporter = path.resolve(options.reporter);
                        }

                        if (externalReporter) {
                            try {
                                Reporter = require(externalReporter);
                            }
                            catch (e) { }
                        }
                    }
                    if (Reporter === null) {
                        grunt.fatal('Specified reporter is unknown or unresolvable: ' + options.reporter);
                    }
                    reporter = new Reporter(runner);

                    // Launch PhantomJS.
                    phantomjs.spawn(url, {
                        // Exit code to use if PhantomJS fails in an uncatchable way.
                        failCode: 90,
                        // Additional PhantomJS options.
                        options: PhantomjsOptions,
                        // Do stuff when done.
                        done: function(err) {
                            var stats = runner.stats;
                            testStats.push(stats);

                            if (err) {
                                // Show Growl notice
                                // @TODO: Get an example of this
                                // growl('PhantomJS Error!');

                                // If there was a PhantomJS error, abort the series.
                                grunt.fatal(err);
                                done(false);
                            } else {
                                // If failures, show growl notice
                                if (stats.failures > 0) {
                                    var reduced = helpers.reduceStats([stats]);
                                    var failMsg = reduced.failures + '/' + reduced.tests +
                                            ' tests failed (' + reduced.duration + 's)';

                                    // Show Growl notice, if avail
                                    growl(failMsg, {
                                        image: asset('growl/error.png'),
                                        title: 'Failure in ' + grunt.task.current.target,
                                        priority: 3
                                    });

                                    // Bail tests if bail option is true
                                    if (options.bail) grunt.warn(failMsg);
                                }

                                // Process next file/url
                                next();
                            }
                        }
                    });
                },
                // All tests have been run.
                function() {
                    if (dest) {
                        // Restore console.log to original and write the output
                        console.log = consoleLog;
                        grunt.file.write(dest, output.join('\n'));
                    }
                    grunt.log.writeln();

                    var customThresholdMsg = "", failMsg = "";
                    var numCustomThreshold = Object.keys(customThreshold ).length;
                    if (numCustomThreshold) {
                        customThresholdMsg = "; " + numCustomThreshold + " files used custom threshold";
                    }

                    grunt.log.writeln("Per-File Coverage Results: (" + coverageThreshold + "% minimum" + customThresholdMsg + ")");

                    if (status.blanketFail > 0) {
                        failMsg = "FAIL : " + (status.blanketFail + "/" + status.blanketTotal + " files failed coverage");
                        grunt.log.write(failMsg.red);
                        grunt.log.writeln();
                        ok = false;
                    } else {
                        var blanketPassMsg = "PASS : " + status.blanketPass + " files passed coverage ";
                        grunt.log.write(blanketPassMsg.green);
                        grunt.log.writeln();
                    }

                    var moduleThreshold = grunt.option('moduleThreshold') || options.moduleThreshold;

                    if (moduleThreshold) {

                        grunt.log.writeln();
                        var customModuleThresholdMsg = "";
                        var numCustomModuleThreshold = Object.keys(customModuleThreshold ).length;
                        if (numCustomModuleThreshold) {
                            customModuleThresholdMsg = "; " + numCustomModuleThreshold + " modules used custom threshold";
                        }
                        grunt.log.writeln("Per-Module Coverage Results: (" + moduleThreshold + "% minimum" + customModuleThresholdMsg + ")");

                        if (modulePatternRegex) {
                            for (var thisModuleName in totals.moduleTotalStatements) {
                                if (totals.moduleTotalStatements.hasOwnProperty(thisModuleName)) {
                                    var moduleTotalSt = totals.moduleTotalStatements[thisModuleName];
                                    var moduleTotalCovSt = totals.moduleTotalCoveredStatements[thisModuleName];

                                    var threshold = moduleThreshold;
                                    if (customModuleThreshold[thisModuleName]) {
                                        threshold = customModuleThreshold[thisModuleName];
                                    }

                                    printPassFailMessage(thisModuleName, moduleTotalCovSt, moduleTotalSt, threshold, /*printPassing*/true);
                                }
                            }
                        }
                    }

                    var globalThreshold = grunt.option('globalThreshold') || options.globalThreshold;

                    if (globalThreshold) {
                        grunt.log.writeln();
                        grunt.log.writeln("Global Coverage Results: (" + globalThreshold + "% minimum)");
                        printPassFailMessage("global", totals.coveredLines, totals.totalLines, globalThreshold, /*printPassing*/true);
                    }
                    grunt.log.writeln();

                    grunt.log.write("Unit Test Results: ");

                    var stats = helpers.reduceStats(testStats);

                    if (stats.failures === 0) {
                        var okMsg = stats.tests + ' specs passed!' + ' (' + stats.duration + 's)';

                        growl(okMsg, {
                            image: asset('growl/ok.png'),
                            title: 'Tests passed',
                            priority: 3
                        });

                        grunt.log.write(okMsg.green);
                        grunt.log.writeln();
                    } else {
                        ok = false;
                        failMsg = stats.failures + '/' + stats.tests + ' tests failed (' +
                                stats.duration + 's)';

                        // Show Growl notice, if avail
                        growl(failMsg, {
                            image: asset('growl/error.png'),
                            title: 'Failure in ' + grunt.task.current.target,
                            priority: 3
                        });

                        grunt.log.write(failMsg.red);
                        grunt.log.writeln();
                    }

                    if (!ok) {
                        grunt.warn("Issues were found.");
                    } else {
                        grunt.log.ok("No issues found.");
                    }

                    // Async test done
                    done();
                });
    });
};
<commit_msg>Fixes #9 (dest option)<commit_after>// grunt-blanket-mocha 0.3.3
//
// Copyright (C) 2013 Dave Cadwallader, Model N, Inc.
// Distributed under the MIT License
//
// Documentation and full license available at:
// https://github.com/ModelN/grunt-blanket-qunit
//
// Based on grunt-mocha
// https://github.com/kmiyashiro/grunt-mocha
// Copyright (c) 2012 "Cowboy" Ben Alman, contributors

'use strict';

// Nodejs libs.
var _             = require('lodash');
var util          = require('util');
var path          = require('path');
var EventEmitter  = require('events').EventEmitter;
var reporters     = require('mocha').reporters;
// Helpers
var helpers       = require('../support/mocha-helpers');

module.exports = function(grunt) {

    var ok, totals, status, coverageThreshold, modulePattern, modulePatternRegex, excludedFiles, customThreshold, customModuleThreshold;
    // External lib.
    var phantomjs = require('grunt-lib-phantomjs').init(grunt);

    var reporter;

    // Growl is optional
    var growl;
    try {
        growl = require('growl');
    } catch(e) {
        growl = function(){};
        grunt.verbose.write('Growl not found, \'npm install growl\' for Growl support');
    }

    // Get an asset file, local to the root of the project.
    var asset = path.join.bind(null, __dirname, '..');

    var printPassFailMessage = function(name, numCovered, numTotal, threshold, printPassing) {
        var percent = (numCovered / numTotal) * 100;

        var pass = (percent >= threshold);

        // If not passed, check if file is marked for manual exclusion. Else, fail it.
        var exclude = _(excludedFiles).some(function (o) {return o.test(name);});
        var result = pass ? "PASS" : ( exclude ? "SKIP" : "FAIL");

        var percentDisplay = Math.floor(percent);
        if (percentDisplay < 10) {
            percentDisplay = "  " + percentDisplay;
        } else if (percentDisplay < 100) {
            percentDisplay = " " + percentDisplay;
        }

        var operator;
        if (percentDisplay > threshold) {
            operator = ">";
        } else if (percentDisplay < threshold) {
            operator = "<";
        } else {
            operator = "=";
        }

        var msg = result + " [" + percentDisplay + "% " + operator +  " " + threshold  + "% ] : " +
                name + " (" + numCovered + " / " + numTotal + ")";

        status.blanketTotal++;
        if (pass) {
            status.blanketPass++;
            if (printPassing || grunt.option('verbose')) {
                grunt.log.writeln(msg.green);
            }
        } else if (result ===  "SKIP"){ //Visually mark that these have been skipped.
            grunt.log.writeln(msg.magenta);
        } else {
            ok = false;
            status.blanketFail++;
            grunt.log.writeln(msg.red);
        }

    };

    // Manage runners listening to phantomjs
    var phantomjsEventManager = (function() {
        var listeners = {};
        var suites = [];

        phantomjs.on('blanket:done', function() {
            phantomjs.halt();
        });

        phantomjs.on('blanket:fileDone', function(thisTotal, filename) {

            if (status.blanketPass === 0 && status.blanketFail === 0 ) {
                grunt.log.writeln();
            }

            var coveredLines = thisTotal[0];
            var totalLines = thisTotal[1];

            var threshold = coverageThreshold;

            // Check for a custom threshold
            var custom = _.find(customThreshold, function (o) {
                return o[0].test(filename);
            });
            if (custom !== undefined) {
                threshold = custom[1];
            }

            printPassFailMessage(filename, coveredLines, totalLines, threshold);

            totals.totalLines += totalLines;
            totals.coveredLines += coveredLines;

            if (modulePatternRegex) {
                var moduleName = filename.match(modulePatternRegex)[1];
                if(!totals.moduleTotalStatements.hasOwnProperty(moduleName)) {
                    totals.moduleTotalStatements[moduleName] = 0;
                    totals.moduleTotalCoveredStatements[moduleName] = 0;
                }

                totals.moduleTotalStatements[moduleName] += totalLines;
                totals.moduleTotalCoveredStatements[moduleName] += coveredLines;
            }
        });

        // Hook on Phantomjs Mocha reporter events.
        phantomjs.on('mocha.*', function(test) {
            var name, fullTitle, slow, err;
            var evt = this.event.replace('mocha.', '');

            // Expand test values (and façace the Mocha test object)
            if (test) {
                fullTitle = test.fullTitle;
                test.fullTitle = function() {
                    return fullTitle;
                };

                slow = this.slow;
                test.slow = function() {
                    return slow;
                };

                test.parent = suites[suites.length - 1] || null;

                err = test.err;
            }

            if (evt === 'suite') {
                suites.push(test);
            } else if (evt === 'suite end') {
                suites.pop(test);
            }

            // Trigger events for each runner listening
            for (name in listeners) {
                listeners[name].emit.call(listeners[name], evt, test, err);
            }
        });

        return {
            add: function(name, runner) {
                listeners[name] = runner;
            },
            remove: function(name) {
                delete listeners[name];
            }
        };
    }());

    // Built-in error handlers.
    phantomjs.on('fail.load', function(url) {
        phantomjs.halt();
        grunt.verbose.write('Running PhantomJS...').or.write('...');
        grunt.log.error();
        grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
    });

    phantomjs.on('fail.timeout', function() {
        phantomjs.halt();
        grunt.log.writeln();
        grunt.warn('PhantomJS timed out, possibly due to a missing Mocha run() call.', 90);
    });

    // Debugging messages.
    phantomjs.on('debug', grunt.log.debug.bind(grunt.log, 'phantomjs'));

    // ==========================================================================
    // TASKS
    // ==========================================================================

    grunt.registerMultiTask('blanket_mocha', 'Run Mocha unit tests in a headless PhantomJS instance.', function() {

        // Merge task-specific and/or target-specific options with these defaults.
        var options = this.options({
            // Output console.log calls
            log: false,
            // Mocha reporter
            reporter: 'Dot',
            // Default PhantomJS timeout.
            timeout: 5000,
            // Mocha-PhantomJS bridge file to be injected.
            inject: asset('phantomjs/bridge.js'),
            // Main PhantomJS script file
            phantomScript: asset('phantomjs/main.js'),
            // Explicit non-file URLs to test.
            urls: [],
            // Fail with grunt.warn on first test failure
            bail: false,
            // Log script errors as grunt errors
            logErrors: false
        });

        ok = true;
        totals = {
            totalLines: 0,
            coveredLines: 0,
            moduleTotalStatements : {},
            moduleTotalCoveredStatements : {}
        };

        status = {blanketTotal: 0, blanketPass: 0, blanketFail: 0};
        coverageThreshold = grunt.option('threshold') || options.threshold;

        modulePattern = grunt.option('modulePattern') || options.modulePattern;
        if (modulePattern) {
            modulePatternRegex = new RegExp(modulePattern);
        }

        var grep = grunt.option('grep');
        options.mocha = options.mocha || {};

        // Get the array of excludedFiles, normalize and prepare them
        // Users should be able to define it in the command-line as an array or include it in the test file.
        excludedFiles =  grunt.option('excludedFiles') || options.excludedFiles || [];
        excludedFiles = _(excludedFiles).map(function (o) {
            return new RegExp(path.normalize(o) + '$');
        }).value();

        // Get the custom thresholds for files, normalize and prepare the file names
        customThreshold = grunt.option('customThreshold') || options.customThreshold || {};
        customThreshold = _(customThreshold).pairs().map(function (o) {
            return [new RegExp(path.normalize(o[0]) + '$'), o[1]];
        }).value();

        customModuleThreshold = grunt.option('customModuleThreshold') || options.customModuleThreshold|| {};

        if (grep) {
            options.mocha.grep = grep;
        }

        // Output console messages if log == true
        if (options.log) {
            phantomjs.removeAllListeners(['console']);
            phantomjs.on('console', grunt.log.writeln);
        } else {
            phantomjs.off('console', grunt.log.writeln);
        }

        // Output errors on script errors
        if (options.logErrors) {
            phantomjs.on('error.*', function(error, stack) {
                var formattedStack = _.map(stack, function(frame) {
                    return "    at " + (frame.function ? frame.function : "undefined") + " (" + frame.file + ":" + frame.line + ")";
                }).join("\n");
                grunt.fail.warn(error + "\n" + formattedStack, 3);
            });
        }

        var optsStr = JSON.stringify(options, null, '  ');
        grunt.verbose.writeln('Options: ' + optsStr);

        // Clean Phantomjs options to prevent any conflicts
        var PhantomjsOptions = _.omit(options, 'reporter', 'urls', 'log', 'bail');

        var phantomOptsStr = JSON.stringify(PhantomjsOptions, null, '  ');
        grunt.verbose.writeln('Phantom options: ' + phantomOptsStr);

        // Combine any specified URLs with src files.
        var urls = options.urls.concat(_.compact(this.filesSrc));

        // Remember all stats from all tests
        var testStats = [];

        // This task is asynchronous.
        var done = this.async();

        // Hijack console.log to capture reporter output
        var dest = options.dest;
        var output = [];
        var consoleLog = console.log;
        // Latest mocha xunit reporter sends to process.stdout instead of console
        var processWrite = process.stdout.write;

        // Only hijack if we really need to
        if (dest) {
            console.log = function() {
                consoleLog.apply(console, arguments);
                // FIXME: This breaks older versions of mocha
                // processWrite.apply(process.stdout, arguments);
                output.push(util.format.apply(util, arguments));
            };
        }

        // Process each filepath in-order.
        grunt.util.async.forEachSeries(urls, function(url, next) {
                    grunt.log.writeln('Testing: ' + url);

                    // create a new mocha runner façade
                    var runner = new EventEmitter();
                    phantomjsEventManager.add(url, runner);

                    // Clear runner event listener when test is over
                    runner.on('end', function() {
                        phantomjsEventManager.remove(url);
                    });

                    // Set Mocha reporter
                    var Reporter = null;
                    if (reporters[options.reporter]) {
                        Reporter = reporters[options.reporter];
                    } else {
                        // Resolve external reporter module
                        var externalReporter;
                        try {
                            externalReporter = require.resolve(options.reporter);
                        } catch (e) {
                            // Resolve to local path
                            externalReporter = path.resolve(options.reporter);
                        }

                        if (externalReporter) {
                            try {
                                Reporter = require(externalReporter);
                            }
                            catch (e) { }
                        }
                    }
                    if (Reporter === null) {
                        grunt.fatal('Specified reporter is unknown or unresolvable: ' + options.reporter);
                    }
                    reporter = new Reporter(runner);

                    // Launch PhantomJS.
                    phantomjs.spawn(url, {
                        // Exit code to use if PhantomJS fails in an uncatchable way.
                        failCode: 90,
                        // Additional PhantomJS options.
                        options: PhantomjsOptions,
                        // Do stuff when done.
                        done: function(err) {
                            var stats = runner.stats;
                            testStats.push(stats);

                            if (err) {
                                // Show Growl notice
                                // @TODO: Get an example of this
                                // growl('PhantomJS Error!');

                                // If there was a PhantomJS error, abort the series.
                                grunt.fatal(err);
                                done(false);
                            } else {
                                // If failures, show growl notice
                                if (stats.failures > 0) {
                                    var reduced = helpers.reduceStats([stats]);
                                    var failMsg = reduced.failures + '/' + reduced.tests +
                                            ' tests failed (' + reduced.duration + 's)';

                                    // Show Growl notice, if avail
                                    growl(failMsg, {
                                        image: asset('growl/error.png'),
                                        title: 'Failure in ' + grunt.task.current.target,
                                        priority: 3
                                    });

                                    // Bail tests if bail option is true
                                    if (options.bail) grunt.warn(failMsg);
                                }

                                // Process next file/url
                                next();
                            }
                        }
                    });
                },
                // All tests have been run.
                function() {
                    if (dest) {
                        // Restore console.log to original and write the output
                        console.log = consoleLog;
                        grunt.file.write(dest, output.join('\n'));
                    }
                    grunt.log.writeln();

                    var customThresholdMsg = "", failMsg = "";
                    var numCustomThreshold = Object.keys(customThreshold ).length;
                    if (numCustomThreshold) {
                        customThresholdMsg = "; " + numCustomThreshold + " files used custom threshold";
                    }

                    grunt.log.writeln("Per-File Coverage Results: (" + coverageThreshold + "% minimum" + customThresholdMsg + ")");

                    if (status.blanketFail > 0) {
                        failMsg = "FAIL : " + (status.blanketFail + "/" + status.blanketTotal + " files failed coverage");
                        grunt.log.write(failMsg.red);
                        grunt.log.writeln();
                        ok = false;
                    } else {
                        var blanketPassMsg = "PASS : " + status.blanketPass + " files passed coverage ";
                        grunt.log.write(blanketPassMsg.green);
                        grunt.log.writeln();
                    }

                    var moduleThreshold = grunt.option('moduleThreshold') || options.moduleThreshold;

                    if (moduleThreshold) {

                        grunt.log.writeln();
                        var customModuleThresholdMsg = "";
                        var numCustomModuleThreshold = Object.keys(customModuleThreshold ).length;
                        if (numCustomModuleThreshold) {
                            customModuleThresholdMsg = "; " + numCustomModuleThreshold + " modules used custom threshold";
                        }
                        grunt.log.writeln("Per-Module Coverage Results: (" + moduleThreshold + "% minimum" + customModuleThresholdMsg + ")");

                        if (modulePatternRegex) {
                            for (var thisModuleName in totals.moduleTotalStatements) {
                                if (totals.moduleTotalStatements.hasOwnProperty(thisModuleName)) {
                                    var moduleTotalSt = totals.moduleTotalStatements[thisModuleName];
                                    var moduleTotalCovSt = totals.moduleTotalCoveredStatements[thisModuleName];

                                    var threshold = moduleThreshold;
                                    if (customModuleThreshold[thisModuleName]) {
                                        threshold = customModuleThreshold[thisModuleName];
                                    }

                                    printPassFailMessage(thisModuleName, moduleTotalCovSt, moduleTotalSt, threshold, /*printPassing*/true);
                                }
                            }
                        }
                    }

                    var globalThreshold = grunt.option('globalThreshold') || options.globalThreshold;

                    if (globalThreshold) {
                        grunt.log.writeln();
                        grunt.log.writeln("Global Coverage Results: (" + globalThreshold + "% minimum)");
                        printPassFailMessage("global", totals.coveredLines, totals.totalLines, globalThreshold, /*printPassing*/true);
                    }
                    grunt.log.writeln();

                    grunt.log.write("Unit Test Results: ");

                    var stats = helpers.reduceStats(testStats);

                    if (stats.failures === 0) {
                        var okMsg = stats.tests + ' specs passed!' + ' (' + stats.duration + 's)';

                        growl(okMsg, {
                            image: asset('growl/ok.png'),
                            title: 'Tests passed',
                            priority: 3
                        });

                        grunt.log.write(okMsg.green);
                        grunt.log.writeln();
                    } else {
                        ok = false;
                        failMsg = stats.failures + '/' + stats.tests + ' tests failed (' +
                                stats.duration + 's)';

                        // Show Growl notice, if avail
                        growl(failMsg, {
                            image: asset('growl/error.png'),
                            title: 'Failure in ' + grunt.task.current.target,
                            priority: 3
                        });

                        grunt.log.write(failMsg.red);
                        grunt.log.writeln();
                    }

                    if (!ok) {
                        grunt.warn("Issues were found.");
                    } else {
                        grunt.log.ok("No issues found.");
                    }

                    // Async test done
                    done();
                });
    });
};
<|endoftext|>


<commit_before>var path = require("path")
  , exports = require("./exports")
  , lang = require("./lang")
  , marked = require("marked")
  , fs = require("fs")
  , lodash = require("lodash")

module.exports = {
  update : function(){
    this.value.locals.pages = exports.value
    this.value.locals.lang = lang.value
  },
  value : {
    basedir : path.resolve(__dirname, "../../"),
    locals : {
      pages : exports.value,
      lang : lang.value,
      path : {
        relative : function(from, to){
          if(to.indexOf("http") === 0) {
            return to
          }
          return path.relative(from.replace(/index$/, ""), to).replace(/\\/g, "/")
        },
        join : path.join
      },
      getPages : function(object){
        return lodash.where(exports.value, object)
      },
      getFeedDescription : function(post){
        return marked(
          fs.readFileSync(
            path.resolve(__dirname, "../../pages/", post + ".md"),
            "utf-8"
          )
        )
      },
      formatDate : function(date){
        date = new Date(date)
        function pad(number){
          var string = String(number)
          return Array(3 - string.length).join("0") + string
        }
        return [
          pad(date.getDate()),
          pad(date.getMonth() + 1),
          date.getFullYear()
        ].join("/")
      }
    }
  }
}
<commit_msg>Nicer date formatting (d month YYYY)<commit_after>var path = require("path")
  , exports = require("./exports")
  , lang = require("./lang")
  , marked = require("marked")
  , fs = require("fs")
  , lodash = require("lodash")
  , monthNames = ["jan.", "fév.", "mars", "avril", "mai", "juin", "juil.", "août", "sept.", "oct.", "nov.", "déc."]

module.exports = {
  update : function(){
    this.value.locals.pages = exports.value
    this.value.locals.lang = lang.value
  },
  value : {
    basedir : path.resolve(__dirname, "../../"),
    locals : {
      pages : exports.value,
      lang : lang.value,
      path : {
        relative : function(from, to){
          if(to.indexOf("http") === 0) {
            return to
          }
          return path.relative(from.replace(/index$/, ""), to).replace(/\\/g, "/")
        },
        join : path.join
      },
      getPages : function(object){
        return lodash.where(exports.value, object)
      },
      getFeedDescription : function(post){
        return marked(
          fs.readFileSync(
            path.resolve(__dirname, "../../pages/", post + ".md"),
            "utf-8"
          )
        )
      },
      formatDate : function(date){
        date = new Date(date)

        return [
          date.getDate(),
          monthNames[date.getMonth()],
          date.getFullYear()
        ].join(" ")
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
 * grunt-hashres
 * https://github.com/luismahou/grunt-hashres
 *
 * Copyright (c) 2013 Luismahou
 * Licensed under the MIT license.
 */

'use strict';

var fs    = require('fs'),
    path  = require('path'),
    utils = require('./hashresUtils');

exports.hashAndSub = function(grunt, options) {

  var src              = options.src,
      dest             = options.dest,
      encoding         = options.encoding,
      fileNameFormat   = options.fileNameFormat,
      renameFiles      = options.renameFiles,
      nameToHashedName = {},
      nameToNameSearch = {},
      formatter        = null,
      searchFormatter  = null;

  grunt.log.debug('files: ' + options.files);
  grunt.log.debug('Using encoding ' + encoding);
  grunt.log.debug('Using fileNameFormat ' + fileNameFormat);
  grunt.log.debug(renameFiles ? 'Renaming files' : 'Not renaming files');

  formatter = utils.compileFormat(fileNameFormat);
  searchFormatter = utils.compileSearchFormat(fileNameFormat);

  if (options.files) {
    options.files.forEach(function(f) {
      f.src.forEach(function(src) {
        var md5        = utils.md5(src).slice(0, 8),
            fileName   = path.basename(src),
            lastIndex  = fileName.lastIndexOf('.'),
            renamed    = formatter({
              hash: md5,
              name: fileName.slice(0, lastIndex),
              ext : fileName.slice(lastIndex + 1, fileName.length)
            }),
            nameSearch = searchFormatter({
              hash: /[0-9a-f]{8}/,
              name: fileName.slice(0, lastIndex),
              ext: fileName.slice(lastIndex + 1, fileName.length)
            });

        // Mapping the original name with hashed one for later use.
        nameToHashedName[fileName] = renamed;
        nameToNameSearch[fileName] = nameSearch;

        // Renaming the file
        if (renameFiles) {
          fs.renameSync(src, path.resolve(path.dirname(src), renamed));
        }
        grunt.log.write(src + ' ').ok(renamed);
      });

      // sort by length
      // It is very useful when we have bar.js and foo-bar.js
      // @crodas
      var files = [];
      for (var name in nameToHashedName) {
        files.push([name, nameToHashedName[name]]);
      }
      files.sort(function(a, b) {
        return b[0].length - a[0].length;
      });

      // Substituting references to the given files with the hashed ones.
      grunt.file.expand(f.dest).forEach(function(f) {
        var destContents = fs.readFileSync(f, encoding);
        files.forEach(function(value) {
          grunt.log.debug('Substituting ' + value[0] + ' by ' + value[1]);
          destContents = destContents.replace(new RegExp("([\'\"\/=])"+utils.preg_quote(value[0])+"(\\?[0-9a-z]+)?", "g"), "$1"+value[1]+"$2");

          grunt.log.debug('Substituting ' + nameToNameSearch[value[0]] + ' by ' + value[1]);
          destContents = destContents.replace(
                new RegExp('\/' + nameToNameSearch[value[0]], "g"),
                '/' + value[1]
            );
        });
        grunt.log.debug('Saving the updated contents of the outination file');
        fs.writeFileSync(f, destContents, encoding);
      });
    });
  }
};
<commit_msg>fix rename problems with file name like {name}.{hash}.{ext}<commit_after>/*
 * grunt-hashres
 * https://github.com/luismahou/grunt-hashres
 *
 * Copyright (c) 2013 Luismahou
 * Licensed under the MIT license.
 */

'use strict';

var fs    = require('fs'),
    path  = require('path'),
    utils = require('./hashresUtils');

exports.hashAndSub = function(grunt, options) {

  var src              = options.src,
      dest             = options.dest,
      encoding         = options.encoding,
      fileNameFormat   = options.fileNameFormat,
      renameFiles      = options.renameFiles,
      nameToHashedName = {},
      nameToNameSearch = {},
      formatter        = null,
      searchFormatter  = null;

  grunt.log.debug('files: ' + options.files);
  grunt.log.debug('Using encoding ' + encoding);
  grunt.log.debug('Using fileNameFormat ' + fileNameFormat);
  grunt.log.debug(renameFiles ? 'Renaming files' : 'Not renaming files');

  formatter = utils.compileFormat(fileNameFormat);
  searchFormatter = utils.compileSearchFormat(fileNameFormat);

  if (options.files) {
    options.files.forEach(function(f) {
      f.src.forEach(function(src) {
        var md5        = utils.md5(src).slice(0, 8),
            fileName   = path.basename(src),
            lastIndex  = fileName.lastIndexOf('.'),
            renamed    = formatter({
              hash: md5,
              name: fileName.slice(0, lastIndex).replace(/\.hash-[0-9a-f]{8}/g, ''),
              ext : fileName.slice(lastIndex + 1, fileName.length)
            }),
            nameSearch = searchFormatter({
              hash: /[0-9a-f]{8}/,
              name: fileName.slice(0, lastIndex),
              ext: fileName.slice(lastIndex + 1, fileName.length)
            });

        // Mapping the original name with hashed one for later use.
        nameToHashedName[fileName] = renamed;
        nameToNameSearch[fileName] = nameSearch;

        // Renaming the file
        if (renameFiles) {
          fs.renameSync(src, path.resolve(path.dirname(src), renamed));
        }
        grunt.log.write(src + ' ').ok(renamed);
      });

      // sort by length
      // It is very useful when we have bar.js and foo-bar.js
      // @crodas
      var files = [];
      for (var name in nameToHashedName) {
        files.push([name, nameToHashedName[name]]);
      }
      files.sort(function(a, b) {
        return b[0].length - a[0].length;
      });

      // Substituting references to the given files with the hashed ones.
      grunt.file.expand(f.dest).forEach(function(f) {
        var destContents = fs.readFileSync(f, encoding);
        files.forEach(function(value) {
          grunt.log.debug('Substituting ' + value[0] + ' by ' + value[1]);
          destContents = destContents.replace(new RegExp("([\'\"\/=])"+utils.preg_quote(value[0])+"(\\?[0-9a-z]+)?", "g"), "$1"+value[1]+"$2");

          grunt.log.debug('Substituting ' + nameToNameSearch[value[0]] + ' by ' + value[1]);
          destContents = destContents.replace(
                new RegExp('\/' + nameToNameSearch[value[0]], "g"),
                '/' + value[1]
            );
        });
        grunt.log.debug('Saving the updated contents of the outination file');
        fs.writeFileSync(f, destContents, encoding);
      });
    });
  }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node
const _ = require('lodash')
const historyData = require('./historyData')
const DateTime = require('dateutils').DateTime
var DateFormat = require('dateutils').DateFormat
var DateLocale = require('dateutils').DateLocale
const format = require('../format')
const rates = require('../rates')
var historyHtml = require('./history.html')
var headHtml = require('../head.html')

const times = _.range(60, 230, 5).map(format.formatIsoTime)

module.exports = {
    historyResponse: historyResponse
}

function historyResponse(req, res) {
    res.send(historyMarkup(req.query.location, historyData))
}

function historyMarkup(location, historyData) {
    const historyDataGrouped = groupByDate(historyData)
    const sortedDates = historyDataGrouped.map(x=>x.dateTime)
    const firstDate = DateTime.fromIsoDateTime(_.first(sortedDates))
    const lastDate = DateTime.fromIsoDateTime(_.last(sortedDates))
    var days = firstDate.distanceInDays(lastDate)
    var dates = _.range(0, days + 1).map(num=>firstDate.plusDays(num)).map(date=>({
        dateTime:      date,
        formattedDate: DateFormat.format(date, 'D j.n', DateLocale.FI)
    }))
    const weeklyAvailability = getWeeklyAvailability(historyData)
    const rates = getRates()
    return headHtml() + historyHtml({
            times:                   times,
            dates:                   dates,
            weeklyAvailability:      weeklyAvailability,
            rates:                   rates,
            location:                location,
            _:                       _,
            findAvailabilityForDate: findAvailabilityForDate
        })

    function findAvailabilityForDate(date, time) {
        return _.get(_.find(historyDataGrouped, row=> row.dateTime === date.toISODateString() + 'T' + time), 'available', [])
    }
}

function groupByDate(data) {
    return _.map(_.groupBy(data.map(mapData), 'dateTime'), (v, k) => ({
        dateTime:  k,
        available: _.map(_.groupBy(v, 'location'), (v, k)=>({location: k, available: v.length}))
    })).sort((a, b) => a.dateTime > b.dateTime ? 1 : -1)
}

function mapData(res) {
    const dateTimeStr = res.date + 'T' + res.time
    const dateTime = DateTime.fromIsoDateTime(dateTimeStr)
    return {
        dateTime:    dateTimeStr,
        time:        res.time, //'22:00',
        date:        res.date,// '2015-12-08',
        location:    res.location,//'taivallahti',
        type:        res.type,//'indoor',
        price:       res.price,//24,
        dateTimeObj: dateTime,
        weekDay:     dateTime.getDay()
    }
}

var timesObj = {}
times.forEach(time=>timesObj[time] = 0)

function getWeeklyAvailability(historyData) {
    return _.map(_.groupBy(historyData.map(mapData), 'weekDay'), (availableForWeekday) => {
        const dates = Object.keys(_.groupBy(availableForWeekday, 'date'))
        const availablePerTime = _.groupBy(availableForWeekday, 'time')
        return times.map(time=> time in availablePerTime ? availablePerTime[time].length / dates.length : 0)
    })
}

function getRates() {
    return {
        locations: _.map(rates, (ratesPerTime, location) => location),
        prices:    _.map(rates, ratesPerTime => _.flatten(_.zip.apply(_, _.map(_.get(ratesPerTime, 'indoor', ratesPerTime)))))
    }
}
<commit_msg>Replace var with const<commit_after>#!/usr/bin/env node
const _ = require('lodash')
const historyData = require('./historyData')
const DateTime = require('dateutils').DateTime
const DateFormat = require('dateutils').DateFormat
const DateLocale = require('dateutils').DateLocale
const format = require('../format')
const rates = require('../rates')
const historyHtml = require('./history.html')
const headHtml = require('../head.html')

const times = _.range(60, 230, 5).map(format.formatIsoTime)

module.exports = {
    historyResponse: historyResponse
}

function historyResponse(req, res) {
    res.send(historyMarkup(req.query.location, historyData))
}

function historyMarkup(location, historyData) {
    const historyDataGrouped = groupByDate(historyData)
    const sortedDates = historyDataGrouped.map(x=>x.dateTime)
    const firstDate = DateTime.fromIsoDateTime(_.first(sortedDates))
    const lastDate = DateTime.fromIsoDateTime(_.last(sortedDates))
    var days = firstDate.distanceInDays(lastDate)
    var dates = _.range(0, days + 1).map(num=>firstDate.plusDays(num)).map(date=>({
        dateTime:      date,
        formattedDate: DateFormat.format(date, 'D j.n', DateLocale.FI)
    }))
    const weeklyAvailability = getWeeklyAvailability(historyData)
    const rates = getRates()
    return headHtml() + historyHtml({
            times:                   times,
            dates:                   dates,
            weeklyAvailability:      weeklyAvailability,
            rates:                   rates,
            location:                location,
            _:                       _,
            findAvailabilityForDate: findAvailabilityForDate
        })

    function findAvailabilityForDate(date, time) {
        return _.get(_.find(historyDataGrouped, row=> row.dateTime === date.toISODateString() + 'T' + time), 'available', [])
    }
}

function groupByDate(data) {
    return _.map(_.groupBy(data.map(mapData), 'dateTime'), (v, k) => ({
        dateTime:  k,
        available: _.map(_.groupBy(v, 'location'), (v, k)=>({location: k, available: v.length}))
    })).sort((a, b) => a.dateTime > b.dateTime ? 1 : -1)
}

function mapData(res) {
    const dateTimeStr = res.date + 'T' + res.time
    const dateTime = DateTime.fromIsoDateTime(dateTimeStr)
    return {
        dateTime:    dateTimeStr,
        time:        res.time, //'22:00',
        date:        res.date,// '2015-12-08',
        location:    res.location,//'taivallahti',
        type:        res.type,//'indoor',
        price:       res.price,//24,
        dateTimeObj: dateTime,
        weekDay:     dateTime.getDay()
    }
}

var timesObj = {}
times.forEach(time=>timesObj[time] = 0)

function getWeeklyAvailability(historyData) {
    return _.map(_.groupBy(historyData.map(mapData), 'weekDay'), (availableForWeekday) => {
        const dates = Object.keys(_.groupBy(availableForWeekday, 'date'))
        const availablePerTime = _.groupBy(availableForWeekday, 'time')
        return times.map(time=> time in availablePerTime ? availablePerTime[time].length / dates.length : 0)
    })
}

function getRates() {
    return {
        locations: _.map(rates, (ratesPerTime, location) => location),
        prices:    _.map(rates, ratesPerTime => _.flatten(_.zip.apply(_, _.map(_.get(ratesPerTime, 'indoor', ratesPerTime)))))
    }
}
<|endoftext|>


<commit_before>import { herokuConfig } from './util'
import { extend, pick } from 'lodash'

const keys = [
  'ASSET_HOST',
  'ASSET_PATH',
  'AWS_ACCESS_KEY_ID',
  'AWS_S3_BUCKET',
  'AWS_S3_HOST',
  'AWS_SECRET_ACCESS_KEY',
  'FACEBOOK_APP_ID',
  'FILEPICKER_API_KEY',
  'GOOGLE_BROWSER_KEY',
  'GOOGLE_CLIENT_ID',
  'HOST',
  'LOG_LEVEL',
  'NODE_ENV',
  'SEGMENT_KEY',
  'SLACK_APP_CLIENT_ID',
  'SOCKET_HOST',
  'UPSTREAM_HOST'
]

export default function (done) {
  herokuConfig().info((err, vars) => {
    if (err) done(err)

    extend(process.env, pick(vars, keys))
    done()
  })
}
<commit_msg>add all feature flag env variables during deployment<commit_after>import { herokuConfig } from './util'
import { each, includes } from 'lodash'

const keys = [
  'ASSET_HOST',
  'ASSET_PATH',
  'AWS_ACCESS_KEY_ID',
  'AWS_S3_BUCKET',
  'AWS_S3_HOST',
  'AWS_SECRET_ACCESS_KEY',
  'FACEBOOK_APP_ID',
  'FILEPICKER_API_KEY',
  'GOOGLE_BROWSER_KEY',
  'GOOGLE_CLIENT_ID',
  'HOST',
  'LOG_LEVEL',
  'NODE_ENV',
  'SEGMENT_KEY',
  'SLACK_APP_CLIENT_ID',
  'SOCKET_HOST',
  'UPSTREAM_HOST'
]

export default function (done) {
  herokuConfig().info((err, vars) => {
    if (err) done(err)
    each(vars, (v, k) => {
      if (includes(keys, k) || k.startsWith('FEATURE_FLAG_')) {
        process.env[k] = v
      }
    })

    done()
  })
}
<|endoftext|>