<commit_before>/*Initialize Magnific Popup when a link with class "open-configure-popup-link" is clicked*/
    $('.open-configure-popup-link').magnificPopup({
      type:'inline',
      midClick: true // Allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source in href.
    });

//Show/hid advanced configuration options when clicked via jQuery library
    $( ".show-advanced-options" ).click(function() {
        $( "#advanced-options" ).toggle( "fast", function() {
        });
    });

function calculateLostRate() {
/*Return the calculated lost rate based on the user provided reached-rate*/
    "use strict";
    //Obtain reached-rate form element
    var reachedRate = document.getElementById("reached-rate").value,

        //Calculate lost rate
        lostRate = (100 - reachedRate) / 100;

    return lostRate;
}

function secondsPerPeriod(period) {
/*Return the calculated number of seconds based on a time period argument*/
/*The 'period' argument is expected to be one of the following strings:
"Minute"
"Hour"
"Day"
"Week"
"Year"
*/
    "use strict";

    //Constant time values
    var SECONDS_PER_MINUTE = 60,
        MINUTES_PER_HOUR = 60,
        HOURS_PER_DAY = 24,
        DAYS_PER_WEEK = 7,
        DAYS_A_YEAR = 365,

        //Calculate seconds per period
        secondsHour = SECONDS_PER_MINUTE * MINUTES_PER_HOUR,
        secondsDay = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY,
        secondsWeek = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK,
        secondsYear = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_A_YEAR;

    switch (period) {
    case "Minute":
        return SECONDS_PER_MINUTE;
    case "Hour":
        return secondsHour;
    case "Day":
        return secondsDay;
    case "Week":
        return secondsWeek;
    case "Year":
        return secondsYear;
    default:
        //return error message if receive non-expected input
        return "Please provide a string of Minute, Hour, Day, Week, or Year as the period";
    }
}

function calculateDeathsPerPeriod(period, type) {
/*Return the number of people who die based on the provided period of time argument and the type argument (total deaths or deaths of lost)*/
/*The 'period' argument is expected to be one of the following strings:
"Second"
"Minute"
"Hour"
"Day"
"Week"
"Year"

*The 'type' argument is expected to be one of the following strings:
"Total"
"Lost"
*/

    "use strict";

    //Obtain Population form element
    var population = document.getElementById("population").value,

        //Obtain Death Rate form element
        deathRate = document.getElementById("death-rate").value,

        //Obtain Lost Rate
        lostRate = calculateLostRate(),

        //Convert crude death rate to percentage
        deathRatePercentage = deathRate / 1000,

        //Calculate deaths a year
        deathsYear = deathRatePercentage * population,

        //Calculate deaths per second
        deathsPerSecond = deathsYear / secondsPerPeriod("Year");

    if (type === "Total") {
        switch (period) {
        case "Second":
            return deathsPerSecond;
        case "Minute":
            var deathsPerMinute = deathsPerSecond * secondsPerPeriod("Minute");
            return deathsPerMinute;
        case "Hour":
            var deathsPerHour = deathsPerSecond * secondsPerPeriod("Hour");
            return deathsPerHour;
        case "Day":
            var deathsPerDay = deathsPerSecond * secondsPerPeriod("Day");
            return deathsPerDay;
        case "Week":
            var deathsPerWeek = deathsPerSecond * secondsPerPeriod("Week");
            return deathsPerWeek;
        case "Year":
            var deathsPerYear = deathsPerSecond * secondsPerPeriod("Year");
            return deathsPerYear;
        default:
            //return error message if receive non-expected input
            return "Error 1";
        }
    }

    if (type === "Lost") {
        switch (period) {
        case "Second":
            var deathsPerSecondLost = calculateDeathsPerPeriod("Second", "Total") * lostRate;
            return deathsPerSecondLost;
        case "Minute":
            var deathsPerMinuteLost = calculateDeathsPerPeriod("Minute", "Total") * lostRate;
            return deathsPerMinuteLost;
        case "Hour":
            var deathsPerHourLost = calculateDeathsPerPeriod("Hour", "Total") * lostRate;
            return deathsPerHourLost;
        case "Day":
            var deathsPerDayLost = calculateDeathsPerPeriod("Day", "Total") * lostRate;
            return deathsPerDayLost;
        case "Week":
            var deathsPerWeekLost = calculateDeathsPerPeriod("Week", "Total") * lostRate;
            return deathsPerWeekLost;
        case "Year":
            var deathsPerYearLost = calculateDeathsPerPeriod("Year", "Total") * lostRate;
            return deathsPerYearLost;
        default:
            //return error message if receive non-expected input
            return "Error 2";
        }
    }
}

function calculateCounterRate() {
/*Return the value needed to make the by  mortality rate count-up counter run at the correct rate based on user inputs*/

    "use strict";

    //Calculate variable to be used by setTimeout within the display() function
    var forTimeOut = 100 / calculateDeathsPerPeriod("Second", "Lost");  //not sure why deaths of lost per second is divided into 100, but it works
    return forTimeOut;
}

function startTime(languageCode) {
/*Configure and display the time elapsed timer (i.e, jquery.countdown)*/

    "use strict";

    $(function () {

        //set variable used by jquery.countdown to the current time
        var austDay = new Date();

        //configure jquery.countdown to start counting up (i.e., since) from the variable defined above
        //make the display format pretty
        $('#defaultCountdown').countdown($.extend({since: austDay, format: 'yodhmS', layout: '{d<}{dn} {dl}, {d>}{h<}{hn} {hl}, {h>}{m<}{mn} {ml}, {m>}{sn} {sl}'},$.countdown.regionalOptions[languageCode]));

    });
}

/*Display the mortality rate count-up counter, inserting commas as necessary*/
/*By George Chiang. (JK's ultimate JavaScript tutorial and free JavaScripts site!)
http://www.javascriptkit.com
Credit MUST stay intact for use*/
var milisec = 0;
var seconds = 0;
document.counterContainer.counter.value = '0';

function display() {
    "use strict";

    var forTimeOut = calculateCounterRate();

    if (milisec >= 9) {
        milisec = 0;
        seconds += 1;
    } else {
        milisec += 1;
    }

    Number.prototype.insertComma = function () {
        var s = this.toString(),
            temp = '';
        for (var i=s.length-1;i>=0;i-=3) {
            if ((i-3)>=0) temp = "," + s.substr(i-2, 3) + temp;
            else temp = s.substring(0, i+1) + temp;
        }
        return temp;
    };

    document.counterContainer.counter.value = seconds.insertComma();

    setTimeout("display()", forTimeOut);
}

function insertDefaults() {
    //Insert values into configuration form based on world statistics
    $("#population").val("7098495231");
    $("#death-rate").val("7.9");
    $("#reached-rate").val("7.9");
    $("#location").val("the world");

    /*
    References:
    - Population: estimated world population at midyear 2013 as reported at http://www.census.gov/population/international/data/idb/region.php?N=%20Results%20&T=6&A=aggregate&RT=0&Y=2013&R=1&C=
    - Crude Death Rate: Based on estimated death rate (per 1,000 population) for the world as reported at https://www.cia.gov/library/publications/the-world-factbook/geos/xx.html
    - Reached Rate: Based on population percentage of Evangelicals at http://www.operationworld.org/wrld
    */

}

function obtainLanguageCode(language) {
/*Return the language code based on user selected language*/

    "use strict";

    var languageCode = "";

    //language codes used must match one of the provided translation files within src/js/vendor/countdown and src/js/vendor/numeral-js/languages
    switch (language) {
    case "Spanish":
        languageCode = "es";
        break;
    case "Portuguese":
        languageCode = "pt-br";
        break;
    default:
        //default to blank if none of the above match (a blank language code will use English)
        languageCode = "";
    }

    return languageCode;
}

function ok() {
/*Start the timer and counter and hide buttons*/

    "use strict";

    //Obtain output-type form element
    var period = document.getElementById("output-type").value,

    //Obtain summary type (total/everyone or lost)
        type = $('input[name="summary-type"]:checked').val(),

    //obtain the number of deaths for the specified period and summary type
        deathsPerPeriod = calculateDeathsPerPeriod(period, type),

    //Obtain Location form element
        location = document.getElementById("location").value,

    //Obtain language form element
        language = document.getElementById("language").value,

    //Initiate variables to hold translations
        rtpApproximately = "Around",
        rtpPeople = "people in",
        rtpLost = "lost people in",
        rtpEternity = "die every",
        rtpBegan = "Since",
        rtpAgo = "ago, the following number of those people have died and entered eternity without Christ:",
        rtpDefaultCountdown = "0 seconds",
        rtpDownload = "Download",
        rtpSecond = "Second",
        rtpSeconds = "Seconds",
        rtpMinute = "Minute",
        rtpHour = "Hour",
        rtpDay = "Day",
        rtpWeek = "Week",
        rtpYear = "Year";

    /*Show different translations based on selected language*/
    if (language === "Spanish") {
        rtpApproximately = "Acerca de";
        rtpPeople = "personas en";
        rtpLost = "personas perdidas en";
        rtpEternity = "mueren cada";
        rtpBegan = "Desde";
        rtpAgo = "atrás, el siguiente número de esas personas han muerto y entrado a la eternidad sin Cristo";
        rtpDownload = "Descargar";
        rtpSecond = "Segundo";
        rtpSeconds = "Segundos";
        rtpMinute = "Minuto";
        rtpHour = "Hora";
        rtpDay = "Día";
        rtpWeek = "Semana";
        rtpYear = "Año";
    }

    if (language === "Portuguese") {
        rtpApproximately = "Cerca de";
        rtpPeople = "pessoas em";
        rtpLost = "pessoas perdidas em";
        rtpEternity = "morrem a cada";
        rtpBegan = "Nos últimos";
        rtpAgo = "o seguinte numero dessas pessoas acabaram de morrer e passarão a vida eterna sem Jesus Cristo:";
        rtpDownload = "Baixar";
        rtpSecond = "Segundo";
        rtpSeconds = "Segundos";
        rtpMinute = "Minuto";
        rtpHour = "Hora";
        rtpDay = "Dia";
        rtpWeek = "Semana";
        rtpYear = "Ano";
    }

    //Change summary type text if lost is selected
    if (type === "Lost") {
        rtpPeople = rtpLost;
    }

    //set language for numeral-js based on provided language code
    numeral.language(obtainLanguageCode(language));

    //change 0 seconds placeholder wording based on language code
    rtpDefaultCountdown = "0 " + rtpSeconds;

    //Set period text based on user's selection (Second, Minute, Hour, Day, Week, or Year) and corresponding translation
    switch (period) {
    case "Second":
        period = rtpSecond;
        break;
    case "Minute":
        period = rtpMinute;
        break;
    case "Hour":
        period = rtpHour;
        break;
    case "Day":
        period = rtpDay;
        break;
    case "Week":
        period = rtpWeek;
        break;
    case "Year":
        period = rtpYear;
        break;
    default:
        //return error message if receive non-expected input
        alert('Please provide a string of Second, Minute, Hour, Day, Week, or Year as the period');
    }

    //Insert the corresponding summary-type text into page via jQuery library
    $('.rtp-approximately').html(rtpApproximately);
    $('.rtp-people').html(rtpPeople);
    $('.rtp-eternity').html(rtpEternity);
    $('.rtp-began').html(rtpBegan);
    $('.rtp-ago').html(rtpAgo);
    $('#defaultCountdown').html(rtpDefaultCountdown);
    $('.rtp-download').html(rtpDownload);

    //set numeral-js default format to rounded number with comma (e.g., 1,000)
    numeral.defaultFormat('0,0');

    //format the deaths per period using the default format
    deathsPerPeriod = numeral(deathsPerPeriod).format();

    //Insert the output-type period (day, week, etc.) into page via jQuery library
    $('.period-holder').html(period.toLowerCase());

    //Insert the calculated deaths per day into page via jQuery library
    $('#deathsPerPeriod').html(deathsPerPeriod);

    //Insert the location into page via jQuery library
    $('.location-holder').html(location);

    //Insert the corresponding summary-type text into page via jQuery library
    $('.summary-type-holder').html(rtpPeople);

    //close the configuration pop-up
    $.magnificPopup.close();
}

function initiateDisplay() {
/*Initiate and display everything when start is invoked by the user*/

    "use strict";

    //Obtain language form element
    var language = document.getElementById("language").value;

    //start the time elapsed timer, passing in selected language
    startTime(obtainLanguageCode(language));

    //start the mortality counter
    display();

    //hide the configuration link text
    $("p.configure").hide();
}<commit_msg>Made start use defaults if none are provided via configure<commit_after>/*Initialize Magnific Popup when a link with class "open-configure-popup-link" is clicked*/
    $('.open-configure-popup-link').magnificPopup({
      type:'inline',
      midClick: true // Allow opening popup on middle mouse click. Always set it to true if you don't provide alternative source in href.
    });

//Show/hid advanced configuration options when clicked via jQuery library
    $( ".show-advanced-options" ).click(function() {
        $( "#advanced-options" ).toggle( "fast", function() {
        });
    });

function calculateLostRate() {
/*Return the calculated lost rate based on the user provided reached-rate*/
    "use strict";
    //Obtain reached-rate form element
    var reachedRate = document.getElementById("reached-rate").value,

        //Calculate lost rate
        lostRate = (100 - reachedRate) / 100;

    return lostRate;
}

function secondsPerPeriod(period) {
/*Return the calculated number of seconds based on a time period argument*/
/*The 'period' argument is expected to be one of the following strings:
"Minute"
"Hour"
"Day"
"Week"
"Year"
*/
    "use strict";

    //Constant time values
    var SECONDS_PER_MINUTE = 60,
        MINUTES_PER_HOUR = 60,
        HOURS_PER_DAY = 24,
        DAYS_PER_WEEK = 7,
        DAYS_A_YEAR = 365,

        //Calculate seconds per period
        secondsHour = SECONDS_PER_MINUTE * MINUTES_PER_HOUR,
        secondsDay = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY,
        secondsWeek = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_PER_WEEK,
        secondsYear = SECONDS_PER_MINUTE * MINUTES_PER_HOUR * HOURS_PER_DAY * DAYS_A_YEAR;

    switch (period) {
    case "Minute":
        return SECONDS_PER_MINUTE;
    case "Hour":
        return secondsHour;
    case "Day":
        return secondsDay;
    case "Week":
        return secondsWeek;
    case "Year":
        return secondsYear;
    default:
        //return error message if receive non-expected input
        return "Please provide a string of Minute, Hour, Day, Week, or Year as the period";
    }
}

function calculateDeathsPerPeriod(period, type) {
/*Return the number of people who die based on the provided period of time argument and the type argument (total deaths or deaths of lost)*/
/*The 'period' argument is expected to be one of the following strings:
"Second"
"Minute"
"Hour"
"Day"
"Week"
"Year"

*The 'type' argument is expected to be one of the following strings:
"Total"
"Lost"
*/

    "use strict";

    //Obtain Population form element
    var population = document.getElementById("population").value,

        //Obtain Death Rate form element
        deathRate = document.getElementById("death-rate").value,

        //Obtain Lost Rate
        lostRate = calculateLostRate(),

        //Convert crude death rate to percentage
        deathRatePercentage = deathRate / 1000,

        //Calculate deaths a year
        deathsYear = deathRatePercentage * population,

        //Calculate deaths per second
        deathsPerSecond = deathsYear / secondsPerPeriod("Year");

    if (type === "Total") {
        switch (period) {
        case "Second":
            return deathsPerSecond;
        case "Minute":
            var deathsPerMinute = deathsPerSecond * secondsPerPeriod("Minute");
            return deathsPerMinute;
        case "Hour":
            var deathsPerHour = deathsPerSecond * secondsPerPeriod("Hour");
            return deathsPerHour;
        case "Day":
            var deathsPerDay = deathsPerSecond * secondsPerPeriod("Day");
            return deathsPerDay;
        case "Week":
            var deathsPerWeek = deathsPerSecond * secondsPerPeriod("Week");
            return deathsPerWeek;
        case "Year":
            var deathsPerYear = deathsPerSecond * secondsPerPeriod("Year");
            return deathsPerYear;
        default:
            //return error message if receive non-expected input
            return "Error 1";
        }
    }

    if (type === "Lost") {
        switch (period) {
        case "Second":
            var deathsPerSecondLost = calculateDeathsPerPeriod("Second", "Total") * lostRate;
            return deathsPerSecondLost;
        case "Minute":
            var deathsPerMinuteLost = calculateDeathsPerPeriod("Minute", "Total") * lostRate;
            return deathsPerMinuteLost;
        case "Hour":
            var deathsPerHourLost = calculateDeathsPerPeriod("Hour", "Total") * lostRate;
            return deathsPerHourLost;
        case "Day":
            var deathsPerDayLost = calculateDeathsPerPeriod("Day", "Total") * lostRate;
            return deathsPerDayLost;
        case "Week":
            var deathsPerWeekLost = calculateDeathsPerPeriod("Week", "Total") * lostRate;
            return deathsPerWeekLost;
        case "Year":
            var deathsPerYearLost = calculateDeathsPerPeriod("Year", "Total") * lostRate;
            return deathsPerYearLost;
        default:
            //return error message if receive non-expected input
            return "Error 2";
        }
    }
}

function calculateCounterRate() {
/*Return the value needed to make the by  mortality rate count-up counter run at the correct rate based on user inputs*/

    "use strict";

    //Calculate variable to be used by setTimeout within the display() function
    var forTimeOut = 100 / calculateDeathsPerPeriod("Second", "Lost");  //not sure why deaths of lost per second is divided into 100, but it works
    return forTimeOut;
}

function startTime(languageCode) {
/*Configure and display the time elapsed timer (i.e, jquery.countdown)*/

    "use strict";

    $(function () {

        //set variable used by jquery.countdown to the current time
        var austDay = new Date();

        //configure jquery.countdown to start counting up (i.e., since) from the variable defined above
        //make the display format pretty
        $('#defaultCountdown').countdown($.extend({since: austDay, format: 'yodhmS', layout: '{d<}{dn} {dl}, {d>}{h<}{hn} {hl}, {h>}{m<}{mn} {ml}, {m>}{sn} {sl}'},$.countdown.regionalOptions[languageCode]));

    });
}

/*Display the mortality rate count-up counter, inserting commas as necessary*/
/*By George Chiang. (JK's ultimate JavaScript tutorial and free JavaScripts site!)
http://www.javascriptkit.com
Credit MUST stay intact for use*/
var milisec = 0;
var seconds = 0;
document.counterContainer.counter.value = '0';

function display() {
    "use strict";

    var forTimeOut = calculateCounterRate();

    if (milisec >= 9) {
        milisec = 0;
        seconds += 1;
    } else {
        milisec += 1;
    }

    Number.prototype.insertComma = function () {
        var s = this.toString(),
            temp = '';
        for (var i=s.length-1;i>=0;i-=3) {
            if ((i-3)>=0) temp = "," + s.substr(i-2, 3) + temp;
            else temp = s.substring(0, i+1) + temp;
        }
        return temp;
    };

    document.counterContainer.counter.value = seconds.insertComma();

    setTimeout("display()", forTimeOut);
}

function insertDefaults() {
    //Insert values into configuration form based on world statistics
    $("#population").val("7098495231");
    $("#death-rate").val("7.9");
    $("#reached-rate").val("7.9");
    $("#location").val("the world");

    /*
    References:
    - Population: estimated world population at midyear 2013 as reported at http://www.census.gov/population/international/data/idb/region.php?N=%20Results%20&T=6&A=aggregate&RT=0&Y=2013&R=1&C=
    - Crude Death Rate: Based on estimated death rate (per 1,000 population) for the world as reported at https://www.cia.gov/library/publications/the-world-factbook/geos/xx.html
    - Reached Rate: Based on population percentage of Evangelicals at http://www.operationworld.org/wrld
    */

}

function obtainLanguageCode(language) {
/*Return the language code based on user selected language*/

    "use strict";

    var languageCode = "";

    //language codes used must match one of the provided translation files within src/js/vendor/countdown and src/js/vendor/numeral-js/languages
    switch (language) {
    case "Spanish":
        languageCode = "es";
        break;
    case "Portuguese":
        languageCode = "pt-br";
        break;
    default:
        //default to blank if none of the above match (a blank language code will use English)
        languageCode = "";
    }

    return languageCode;
}

function ok() {
/*Start the timer and counter and hide buttons*/

    "use strict";

    //Obtain output-type form element
    var period = document.getElementById("output-type").value,

    //Obtain summary type (total/everyone or lost)
        type = $('input[name="summary-type"]:checked').val(),

    //obtain the number of deaths for the specified period and summary type
        deathsPerPeriod = calculateDeathsPerPeriod(period, type),

    //Obtain Location form element
        location = document.getElementById("location").value,

    //Obtain language form element
        language = document.getElementById("language").value,

    //Initiate variables to hold translations
        rtpApproximately = "Around",
        rtpPeople = "people in",
        rtpLost = "lost people in",
        rtpEternity = "die every",
        rtpBegan = "Since",
        rtpAgo = "ago, the following number of those people have died and entered eternity without Christ:",
        rtpDefaultCountdown = "0 seconds",
        rtpDownload = "Download",
        rtpSecond = "Second",
        rtpSeconds = "Seconds",
        rtpMinute = "Minute",
        rtpHour = "Hour",
        rtpDay = "Day",
        rtpWeek = "Week",
        rtpYear = "Year";

    /*Show different translations based on selected language*/
    if (language === "Spanish") {
        rtpApproximately = "Acerca de";
        rtpPeople = "personas en";
        rtpLost = "personas perdidas en";
        rtpEternity = "mueren cada";
        rtpBegan = "Desde";
        rtpAgo = "atrás, el siguiente número de esas personas han muerto y entrado a la eternidad sin Cristo";
        rtpDownload = "Descargar";
        rtpSecond = "Segundo";
        rtpSeconds = "Segundos";
        rtpMinute = "Minuto";
        rtpHour = "Hora";
        rtpDay = "Día";
        rtpWeek = "Semana";
        rtpYear = "Año";
    }

    if (language === "Portuguese") {
        rtpApproximately = "Cerca de";
        rtpPeople = "pessoas em";
        rtpLost = "pessoas perdidas em";
        rtpEternity = "morrem a cada";
        rtpBegan = "Nos últimos";
        rtpAgo = "o seguinte numero dessas pessoas acabaram de morrer e passarão a vida eterna sem Jesus Cristo:";
        rtpDownload = "Baixar";
        rtpSecond = "Segundo";
        rtpSeconds = "Segundos";
        rtpMinute = "Minuto";
        rtpHour = "Hora";
        rtpDay = "Dia";
        rtpWeek = "Semana";
        rtpYear = "Ano";
    }

    //Change summary type text if lost is selected
    if (type === "Lost") {
        rtpPeople = rtpLost;
    }

    //set language for numeral-js based on provided language code
    numeral.language(obtainLanguageCode(language));

    //change 0 seconds place-holder wording based on language code
    rtpDefaultCountdown = "0 " + rtpSeconds;

    //Set period text based on user's selection (Second, Minute, Hour, Day, Week, or Year) and corresponding translation
    switch (period) {
    case "Second":
        period = rtpSecond;
        break;
    case "Minute":
        period = rtpMinute;
        break;
    case "Hour":
        period = rtpHour;
        break;
    case "Day":
        period = rtpDay;
        break;
    case "Week":
        period = rtpWeek;
        break;
    case "Year":
        period = rtpYear;
        break;
    default:
        //return error message if receive non-expected input
        alert('Please provide a string of Second, Minute, Hour, Day, Week, or Year as the period');
    }

    //Insert the corresponding summary-type text into page via jQuery library
    $('.rtp-approximately').html(rtpApproximately);
    $('.rtp-people').html(rtpPeople);
    $('.rtp-eternity').html(rtpEternity);
    $('.rtp-began').html(rtpBegan);
    $('.rtp-ago').html(rtpAgo);
    $('#defaultCountdown').html(rtpDefaultCountdown);
    $('.rtp-download').html(rtpDownload);

    //set numeral-js default format to rounded number with comma (e.g., 1,000)
    numeral.defaultFormat('0,0');

    //format the deaths per period using the default format
    deathsPerPeriod = numeral(deathsPerPeriod).format();

    //Insert the output-type period (day, week, etc.) into page via jQuery library
    $('.period-holder').html(period.toLowerCase());

    //Insert the calculated deaths per day into page via jQuery library
    $('#deathsPerPeriod').html(deathsPerPeriod);

    //Insert the location into page via jQuery library
    $('.location-holder').html(location);

    //Insert the corresponding summary-type text into page via jQuery library
    $('.summary-type-holder').html(rtpPeople);

    //close the configuration pop-up
    $.magnificPopup.close();
}

function initiateDisplay() {
/*Initiate and display everything when start is invoked by the user*/

    "use strict";

    //Obtain necessary form elements
    var population = document.getElementById("population").value,
        deathRate = document.getElementById("death-rate").value,
        location = document.getElementById("location").value,
        reachedRate = document.getElementById("reached-rate").value,
        language = document.getElementById("language").value;

    //if form is blank, insert default values before running
    if (population === "" && deathRate === "" && location === "" && reachedRate === "") {
        //Insert values into configuration form based on world statistics
        $("#population").val("7098495231");
        $("#death-rate").val("7.9");
        $("#reached-rate").val("7.9");
    }

    //start the time elapsed timer, passing in selected language
    startTime(obtainLanguageCode(language));

    //start the mortality counter
    display();

    //hide the configuration link text
    $("p.configure").hide();
}<|endoftext|>


<commit_before>var API_URL = 'http://api.wunderground.com/api/7a47b141d9b58611/conditions/q/'
var lookup = document.querySelector('.lookup');

lookup.onclick = function() {
  var input = document.querySelector('input');
  var zip = input.value;

  getJSON(API_URL + zip + '.json', function (data) {
    var span = document.querySelector('span');
    var hum = document.querySelector('.hum');

    span.innerHTML = data.current_observation.temp_f;
    hum.innerHTML = data.current_observation.relative_humidity;
  });
}

function getJSON(url, cb) {
  var xhr = new XMLHttpRequest();

  xhr.open('GET', url);
  xhr.onload = function () {
    if (this.status >= 200 && this.status < 400) {
      cb(JSON.parse(this.response));
    }
  };

  xhr.send();
}

//SPLIT BETWEEN THE TWO BUTTON ACTIONS.
//Button functions above.
//Table containing 7 day forecast / geolocation below

var API_URL = 'http://api.wunderground.com/api/7a47b141d9b58611/conditions/q/'
var loc = document.querySelector('.location');

loc.onclick = function() {
  navigator.geolocation.getCurrentPosition(function(position) {
    var lat = position.coords.latitude;
    var long = position.coords.longitude;

    getJSON(API_URL + lat + "," + long + '.json', function (data) {
      var span = document.querySelector('span');
      var hum = document.querySelector('.hum');
      var city = document.querySelector('.city');
      var sky = document.querySelector('.sky');
      var win = document.querySelector('.win');
      var humi = document.querySelector('.humi');
      var tempf = document.querySelector('.tempf');
      var tempc = document.querySelector('.tempc');

      span.innerHTML = data.current_observation.temp_f;
      hum.innerHTML = data.current_observation.relative_humidity;
      city.innerHTML = data.current_observation.observation_location.city;
      sky.innerHTML = data.current_observation.weather;
      win.innerHTML = data.current_observation.wind_mph;
      humi.innerHTML = data.current_observation.relative_humidity;
      tempf.innerHTML = data.current_observation.temp_f;
      tempc.innerHTML = data.current_observation.temp_c;

    });
  });
};
<commit_msg>refactored to deliver weather on page load and table works with zip lookup too<commit_after>var API_URL = 'http://api.wunderground.com/api/7a47b141d9b58611/conditions/q/'
var lookup = document.querySelector('.lookup');

lookup.onclick = function() {
  var input = document.querySelector('input');
  var zip = input.value;

  getJSON(API_URL + zip + '.json', function (data) {
      var span = document.querySelector('span');
      var hum = document.querySelector('.hum');
      var city = document.querySelector('.city');
      var sky = document.querySelector('.sky');
      var win = document.querySelector('.win');
      var hum = document.querySelector('.hum');
      var tempf = document.querySelector('.tempf');

      span.innerHTML = data.current_observation.temp_f;
      hum.innerHTML = data.current_observation.relative_humidity;
      city.innerHTML = data.current_observation.observation_location.city;
      sky.innerHTML = data.current_observation.weather;
      win.innerHTML = data.current_observation.wind_mph;
      tempf.innerHTML = data.current_observation.temp_f;
      console.log("searching")
  });
}

function getJSON(url, cb) {
  var xhr = new XMLHttpRequest();

  xhr.open('GET', url);
  xhr.onload = function () {
    if (this.status >= 200 && this.status < 400) {
      cb(JSON.parse(this.response));
    }
  };

  xhr.send();
}

//SPLIT BETWEEN THE TWO BUTTON ACTIONS.
//Button functions above.
//Table containing 7 day forecast / geolocation below

var API_URL = 'http://api.wunderground.com/api/7a47b141d9b58611/conditions/q/'
var loc = document.querySelector('.location');

loc.onclick = function() {
  navigator.geolocation.getCurrentPosition(function(position) {
    var lat = position.coords.latitude;
    var long = position.coords.longitude;

    getJSON(API_URL + lat + "," + long + '.json', function (data) {
      var span = document.querySelector('span');
      var hum = document.querySelector('.hum');
      var city = document.querySelector('.city');
      var sky = document.querySelector('.sky');
      var win = document.querySelector('.win');
      var humi = document.querySelector('.humi');
      var tempf = document.querySelector('.tempf');

      span.innerHTML = data.current_observation.temp_f;
      hum.innerHTML = data.current_observation.relative_humidity;
      city.innerHTML = data.current_observation.observation_location.city;
      sky.innerHTML = data.current_observation.weather;
      win.innerHTML = data.current_observation.wind_mph;
      humi.innerHTML = data.current_observation.relative_humidity;
      tempf.innerHTML = data.current_observation.temp_f;
      console.log(data)
    });
  });
};

window.onload = function() {
  navigator.geolocation.getCurrentPosition(function(position) {
    var lat = position.coords.latitude;
    var long = position.coords.longitude;

    getJSON(API_URL + lat + "," + long + '.json', function (data) {
      var span = document.querySelector('span');
      var hum = document.querySelector('.hum');
      var city = document.querySelector('.city');
      var sky = document.querySelector('.sky');
      var win = document.querySelector('.win');
      var tempf = document.querySelector('.tempf');
      var precip = document.querySelector('.precip');

      span.innerHTML = data.current_observation.temp_f;
      hum.innerHTML = data.current_observation.relative_humidity;
      city.innerHTML = data.current_observation.observation_location.city;
      sky.innerHTML = data.current_observation.weather;
      win.innerHTML = data.current_observation.wind_mph;
      tempf.innerHTML = data.current_observation.temp_f;
      precip.innerHTML = data.current_observation.precip_today_in;
    });
  });
};
<|endoftext|>


<commit_before>/*! url - v1.8.6 - 2013-11-22 */
window.url = function() {
    function e(e) {
        return !isNaN(parseFloat(e)) && isFinite(e)
    }
    return function(t, n) {
        var r = n || window.location.toString();
        if (!t) return r;
        t = t.toString(), "//" === r.substring(0, 2) ? r = "http:" + r : 1 === r.split("://").length && (r = "http://" + r), n = r.split("/");
        var i = {
                auth: ""
            },
            s = n[2].split("@");
        1 === s.length ? s = s[0].split(":") : (i.auth = s[0], s = s[1].split(":")), i.protocol = n[0], i.hostname = s[0], i.port = s[1] || ("https" === i.protocol.split(":")[0].toLowerCase() ? "443" : "80"), i.pathname = (n.length > 3 ? "/" : "") + n.slice(3, n.length).join("/").split("?")[0].split("#")[0];
        var o = i.pathname;
        "/" === o.charAt(o.length - 1) && (o = o.substring(0, o.length - 1));
        var u = i.hostname,
            f = u.split("."),
            l = o.split("/");
        if ("hostname" === t) return u;
        if ("domain" === t) return /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(u) ? u : f.slice(-2).join(".");
        if ("sub" === t) return f.slice(0, f.length - 2).join(".");
        if ("port" === t) return i.port;
        if ("protocol" === t) return i.protocol.split(":")[0];
        if ("auth" === t) return i.auth;
        if ("user" === t) return i.auth.split(":")[0];
        if ("pass" === t) return i.auth.split(":")[1] || "";
        if ("path" === t) return i.pathname;
        if ("." === t.charAt(0)) {
            if (t = t.substring(1), e(t)) return t = parseInt(t, 10), f[0 > t ? f.length + t : t - 1] || ""
        } else {
            if (e(t)) return t = parseInt(t, 10), l[0 > t ? l.length + t : t] || "";
            if ("file" === t) return l.slice(-1)[0];
            if ("filename" === t) return l.slice(-1)[0].split(".")[0];
            if ("fileext" === t) return l.slice(-1)[0].split(".")[1] || "";
            if ("?" === t.charAt(0) || "#" === t.charAt(0)) {
                var c = r,
                    h = null;
                if ("?" === t.charAt(0) ? c = (c.split("?")[1] || "").split("#")[0] : "#" === t.charAt(0) && (c = c.split("#")[1] || ""), !t.charAt(1)) return c;
                t = t.substring(1), c = c.split("&");
                for (var p = 0, d = c.length; d > p; p++)
                    if (h = c[p].split("="), h[0] === t) return h[1] || "";
                return null
            }
        }
        return ""
    }
}(), "undefined" != typeof jQuery && jQuery.extend({
    url: function(e, t) {
        return window.url(e, t)
    }
});
window.rgbHex = function() {
    function e(e) {
        return !isNaN(parseFloat(e)) && isFinite(e)
    }

    function t(e) {
        return e.replace(/^\s+|\s+$/g, "")
    }

    function n(n) {
        return n = t(n), e(n) && n >= 0 && 255 >= n
    }

    function r(e) {
        return /^[0-9a-f]{3}$|^[0-9a-f]{6}$/i.test(t(e))
    }

    function i(e) {
        return e = parseInt(e, 10).toString(16), 1 === e.length ? "0" + e : e
    }

    function s(e) {
        return parseInt(e, 16).toString()
    }

    function o(t) {
        return t = t.split(","), (3 === t.length || 4 === t.length) && n(t[0]) && n(t[1]) && n(t[2]) ? 4 !== t.length || e(t[3]) ? "#" + i(t[0]).toUpperCase() + i(t[1]).toUpperCase() + i(t[2]).toUpperCase() : null : null
    }

    function u(e) {
        return r(e) ? (3 === e.length && (e = e.charAt(0) + e.charAt(0) + e.charAt(1) + e.charAt(1) + e.charAt(2) + e.charAt(2)), "rgb(" + s(e.substr(0, 2)) + "," + s(e.substr(2, 2)) + "," + s(e.substr(4, 2)) + ")") : void 0
    }

    function a(e) {
        return e.replace(/\s/g, "")
    }
    return function(e) {
        if (!e) return null;
        var n = null,
            r = /^rgba?\((.*)\);?$/,
            i = /^#/;
        return e = t(e.toString()), "transparent" === e || "rgba(0,0,0,0)" === a(e) ? "transparent" : r.test(e) ? o(e.match(r)[1]) : i.test(e) ? u(e.split("#").reverse()[0]) : (n = e.split(","), 1 === n.length ? u(e) : 3 === n.length || 4 === n.length ? o(e) : void 0)
    }
}(), jQuery && jQuery.extend({
    rgbHex: function(e) {
        return window.rgbHex(e)
    }
});
(function(e) {
    e.fn.colourBrightness = function() {
        var e, t, n, r, i = this.css("background-color");
        if (i.match(/^rgb/)) {
            i = i.match(/rgb\(([^)]+)\)/)[1];
            i = i.split(/ *, */).map(Number);
            e = i[0];
            t = i[1];
            n = i[2]
        } else if ("#" == i[0] && 7 == i.length) {
            e = parseInt(i.slice(1, 3), 16);
            t = parseInt(i.slice(3, 5), 16);
            n = parseInt(i.slice(5, 7), 16)
        } else if ("#" == i[0] && 4 == i.length) {
            e = parseInt(i[1] + i[1], 16);
            t = parseInt(i[2] + i[2], 16);
            n = parseInt(i[3] + i[3], 16)
        }
        r = (e * 299 + t * 587 + n * 114) / 1e3;
        r < 125 ? this.removeClass("light").addClass("dark") : this.removeClass("dark").addClass("light")
    }
})(jQuery);
$(window).load(function() {
    function i() {
        var n, r, i;
        n = window.location.href;
        r = window.url("#", n);
        window.url("#", n) ? i = window.url("#", n) : i = window.url("-1", n);
        if (i) {
            if (i.match("^[0-9A-Fa-f]{3}$") || i.match("^[0-9A-Fa-f]{6}$")) {
                e.val("#" + i);
                colour = $.rgbHex(e.val());
                if (colour) {
                    t.val(colour);
                    t.select()
                } else t.val("");
                $("body").css("background-color", t.val());
                $("body").colourBrightness()
            }
        } else e.focus()
    }
    var e = $("#hex"),
        t = $("#rgb"),
        n = $("#hex").val(),
        r = $("#rgb").val();
    window.onhashchange = i;
    i();
    e.bind("blur keyup", function(e) {
        var colour = $.rgbHex($("#hex").val());
        colour ? $("#rgb").val(colour) : $("#rgb").val("");
        $("body").css("background-color", t.val());
        $("body").colourBrightness();
        e.keyCode == 13 && t.select()
    });
    t.bind("blur keyup", function(t) {
        var colour = $.rgbHex($("#rgb").val());
        colour ? $("#hex").val(colour) : $("#hex").val("");
        $("body").css("background-color", e.val());
        $("body").colourBrightness();
        t.keyCode == 13 && e.select()
    });
    $(".tweet").click(function(e) {
        var t = 575,
            n = 400,
            r = ($(window).width() - t) / 2,
            i = ($(window).height() - n) / 2,
            s = this.href,
            o = "status=1,width=" + t + ",height=" + n + ",top=" + i + ",left=" + r;
        window.open(s, "twitter", o);
        return !1
    })
});<commit_msg>feat(app): update title bar color based on current color<commit_after>/*! url - v1.8.6 - 2013-11-22 */
window.url = function() {
    function e(e) {
        return !isNaN(parseFloat(e)) && isFinite(e)
    }
    return function(t, n) {
        var r = n || window.location.toString();
        if (!t) return r;
        t = t.toString(), "//" === r.substring(0, 2) ? r = "http:" + r : 1 === r.split("://").length && (r = "http://" + r), n = r.split("/");
        var i = {
                auth: ""
            },
            s = n[2].split("@");
        1 === s.length ? s = s[0].split(":") : (i.auth = s[0], s = s[1].split(":")), i.protocol = n[0], i.hostname = s[0], i.port = s[1] || ("https" === i.protocol.split(":")[0].toLowerCase() ? "443" : "80"), i.pathname = (n.length > 3 ? "/" : "") + n.slice(3, n.length).join("/").split("?")[0].split("#")[0];
        var o = i.pathname;
        "/" === o.charAt(o.length - 1) && (o = o.substring(0, o.length - 1));
        var u = i.hostname,
            f = u.split("."),
            l = o.split("/");
        if ("hostname" === t) return u;
        if ("domain" === t) return /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/.test(u) ? u : f.slice(-2).join(".");
        if ("sub" === t) return f.slice(0, f.length - 2).join(".");
        if ("port" === t) return i.port;
        if ("protocol" === t) return i.protocol.split(":")[0];
        if ("auth" === t) return i.auth;
        if ("user" === t) return i.auth.split(":")[0];
        if ("pass" === t) return i.auth.split(":")[1] || "";
        if ("path" === t) return i.pathname;
        if ("." === t.charAt(0)) {
            if (t = t.substring(1), e(t)) return t = parseInt(t, 10), f[0 > t ? f.length + t : t - 1] || ""
        } else {
            if (e(t)) return t = parseInt(t, 10), l[0 > t ? l.length + t : t] || "";
            if ("file" === t) return l.slice(-1)[0];
            if ("filename" === t) return l.slice(-1)[0].split(".")[0];
            if ("fileext" === t) return l.slice(-1)[0].split(".")[1] || "";
            if ("?" === t.charAt(0) || "#" === t.charAt(0)) {
                var c = r,
                    h = null;
                if ("?" === t.charAt(0) ? c = (c.split("?")[1] || "").split("#")[0] : "#" === t.charAt(0) && (c = c.split("#")[1] || ""), !t.charAt(1)) return c;
                t = t.substring(1), c = c.split("&");
                for (var p = 0, d = c.length; d > p; p++)
                    if (h = c[p].split("="), h[0] === t) return h[1] || "";
                return null
            }
        }
        return ""
    }
}(), "undefined" != typeof jQuery && jQuery.extend({
    url: function(e, t) {
        return window.url(e, t)
    }
});
window.rgbHex = function() {
    function e(e) {
        return !isNaN(parseFloat(e)) && isFinite(e)
    }

    function t(e) {
        return e.replace(/^\s+|\s+$/g, "")
    }

    function n(n) {
        return n = t(n), e(n) && n >= 0 && 255 >= n
    }

    function r(e) {
        return /^[0-9a-f]{3}$|^[0-9a-f]{6}$/i.test(t(e))
    }

    function i(e) {
        return e = parseInt(e, 10).toString(16), 1 === e.length ? "0" + e : e
    }

    function s(e) {
        return parseInt(e, 16).toString()
    }

    function o(t) {
        return t = t.split(","), (3 === t.length || 4 === t.length) && n(t[0]) && n(t[1]) && n(t[2]) ? 4 !== t.length || e(t[3]) ? "#" + i(t[0]).toUpperCase() + i(t[1]).toUpperCase() + i(t[2]).toUpperCase() : null : null
    }

    function u(e) {
        return r(e) ? (3 === e.length && (e = e.charAt(0) + e.charAt(0) + e.charAt(1) + e.charAt(1) + e.charAt(2) + e.charAt(2)), "rgb(" + s(e.substr(0, 2)) + "," + s(e.substr(2, 2)) + "," + s(e.substr(4, 2)) + ")") : void 0
    }

    function a(e) {
        return e.replace(/\s/g, "")
    }
    return function(e) {
        if (!e) return null;
        var n = null,
            r = /^rgba?\((.*)\);?$/,
            i = /^#/;
        return e = t(e.toString()), "transparent" === e || "rgba(0,0,0,0)" === a(e) ? "transparent" : r.test(e) ? o(e.match(r)[1]) : i.test(e) ? u(e.split("#").reverse()[0]) : (n = e.split(","), 1 === n.length ? u(e) : 3 === n.length || 4 === n.length ? o(e) : void 0)
    }
}(), jQuery && jQuery.extend({
    rgbHex: function(e) {
        return window.rgbHex(e)
    }
});
(function(e) {
    e.fn.colourBrightness = function() {
        var e, t, n, r, i = this.css("background-color");
        if (i.match(/^rgb/)) {
            i = i.match(/rgb\(([^)]+)\)/)[1];
            i = i.split(/ *, */).map(Number);
            e = i[0];
            t = i[1];
            n = i[2]
        } else if ("#" == i[0] && 7 == i.length) {
            e = parseInt(i.slice(1, 3), 16);
            t = parseInt(i.slice(3, 5), 16);
            n = parseInt(i.slice(5, 7), 16)
        } else if ("#" == i[0] && 4 == i.length) {
            e = parseInt(i[1] + i[1], 16);
            t = parseInt(i[2] + i[2], 16);
            n = parseInt(i[3] + i[3], 16)
        }
        r = (e * 299 + t * 587 + n * 114) / 1e3;
        r < 125 ? this.removeClass("light").addClass("dark") : this.removeClass("dark").addClass("light");

        // update title bar color (W10 app)
        if (typeof Windows !== 'undefined') {
            var titleBar = Windows.UI.ViewManagement.ApplicationView.getForCurrentView().titleBar;
            var backgroundColor;
            var foregroundColor;

            // dark theme
            if (r < 125) {
                // create color
                backgroundColor = { a: 255, r: e, g: t, b: n };
                foregroundColor = { a: 255, r: 255, g: 255, b: 255 };

                // title bar
                titleBar.backgroundColor = backgroundColor;
                titleBar.foregroundColor = foregroundColor;

                // title bar buttons
                titleBar.buttonBackgroundColor = backgroundColor;
                titleBar.buttonForegroundColor  = foregroundColor;
            }
            // light theme
            else {
                backgroundColor = { a: 255, r: e, g: t, b: n };
                foregroundColor = { a: 255, r: 0, g: 0, b: 0 };

                // title bar
                titleBar.backgroundColor = backgroundColor;
                titleBar.foregroundColor = foregroundColor;

                // title bar buttons
                titleBar.buttonBackgroundColor = backgroundColor;
                titleBar.buttonForegroundColor  = foregroundColor;
            }
        }
    }
})(jQuery);
$(window).load(function() {
    function i() {
        var n, r, i;
        n = window.location.href;
        r = window.url("#", n);
        window.url("#", n) ? i = window.url("#", n) : i = window.url("-1", n);
        if (i) {
            if (i.match("^[0-9A-Fa-f]{3}$") || i.match("^[0-9A-Fa-f]{6}$")) {
                e.val("#" + i);
                colour = $.rgbHex(e.val());
                if (colour) {
                    t.val(colour);
                    t.select()
                } else t.val("");
                $("body").css("background-color", t.val());
                $("body").colourBrightness()
            }
        } else e.focus()
    }
    var e = $("#hex"),
        t = $("#rgb"),
        n = $("#hex").val(),
        r = $("#rgb").val();
    window.onhashchange = i;
    i();
    e.bind("blur keyup", function(e) {
        var colour = $.rgbHex($("#hex").val());
        colour ? $("#rgb").val(colour) : $("#rgb").val("");
        $("body").css("background-color", t.val());
        $("body").colourBrightness();
        e.keyCode == 13 && t.select()
    });
    t.bind("blur keyup", function(t) {
        var colour = $.rgbHex($("#rgb").val());
        colour ? $("#hex").val(colour) : $("#hex").val("");
        $("body").css("background-color", e.val());
        $("body").colourBrightness();
        t.keyCode == 13 && e.select()
    });
    $(".tweet").click(function(e) {
        var t = 575,
            n = 400,
            r = ($(window).width() - t) / 2,
            i = ($(window).height() - n) / 2,
            s = this.href,
            o = "status=1,width=" + t + ",height=" + n + ",top=" + i + ",left=" + r;
        window.open(s, "twitter", o);
        return !1
    })
});<|endoftext|>


<commit_before>var config;

config = require('../../lib/protractor.conf.js').config;

config.capabilities = {
  browserName: 'firefox'
};

config.framework = 'mocha';

config.specs = [
  '../generalStepDefs/support/initGlobals.js',
  '../**/*.spec.js'
];

config.allScriptsTimeout = 20000;

exports.config = config;
<commit_msg>Update config import since it is the full export object now<commit_after>var config;

config = require('../../lib/protractor.conf.js');

config.capabilities = {
  browserName: 'firefox'
};

config.framework = 'mocha';

config.specs = [
  '../generalStepDefs/support/initGlobals.js',
  '../**/*.spec.js'
];

config.allScriptsTimeout = 20000;

exports.config = config;
<|endoftext|>


<commit_before>(function(window, document) {
  var angular = window.angular,
    ga = window.ga,
    localStorage = window.localStorage || {},

    POMODORO_STR = 'pomodoro',

    app = angular.module(POMODORO_STR, []),
    isDev = localStorage['isDev'],

    SECOND = 1000,
    MINUTE = 60 * SECOND,
    SCALE = isDev ? SECOND : MINUTE,

    DURATIONS = {
      pomodoro: 25 * SCALE,
      shortBreak: 5 * SCALE,
      longBreak: 15 * SCALE
    },

    DATE_FORMAT = 'mm:ss',
    DEFAULT_TASK_NAME = 'Some task',

    notifications = window.Notification;

  /**
   * @param {String} [name]
   * @constructor
   */
  function Task(name) {
    this.name = name || '';
    this.periods = [];
  }

  /**
   * @param {String} type
   * @constructor
   */
  function Period(type) {
    this.type = type;
    this.time = new Date().getTime();
  }

  /**
   * @return {Task[]}
   */
  function loadTasks() {
    return angular.fromJson(localStorage['tasks'] || '[]');
  }

  /**
   * @param {Task[]} tasks
   */
  function saveTasks(tasks) {
    localStorage['tasks'] = angular.toJson(tasks);
  }

  function soundAlert() {
    // http://stackoverflow.com/questions/8733330/why-cant-i-play-sounds-more-than-once-using-html5-audio-tag
    var audio = document.createElement('audio');
    audio.src = 'assets/alert.mp3';
    audio.load();
    audio.play();
  }

  function notifyAll(taskName) {
    document.title = 'Time is up!' + (taskName ? ' - ' + taskName : '') + ' - Pomodoro Tracker';

    soundAlert();

    var notification = new Notification('Pomodoro Tracker', {
      tag: 'pomodoro-tracker',
      body: (taskName ? taskName + ' - ' : '') + 'Time is up!'
    });

    // Automatically close notification after 5 seconds
    notification.onshow = function() {
      setTimeout(function() { notification.close(); }, 5 * SECOND);
    };
  }

  /**
   * @param {Number|Date} date
   * @return {Number}
   */
  function dropTime(date) {
    date = new Date(date);
    return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
  }

  /**
   * @return {Number}
   */
  function todayDate() {
    return dropTime(new Date());
  }

  app.filter('todayPeriods', function() {
    return function(tasks) {
      var today = todayDate();

      return tasks.reduce(function(result, task) {
        return result.concat(task.periods || []);
      }, []).filter(function(period) {
          return today === dropTime(period.time);
        });
    };
  });

  app.filter('pomodoros', function() {
    return function(periods) {
      return periods.filter(function(period) {
        return period.type === POMODORO_STR;
      }).length || '';
    };
  });

  app.controller('MainCtrl', ['$scope', '$interval', 'dateFilter', function($scope, $interval, dateFilter) {
    var _this = this,
      tasks = $scope.tasks = loadTasks();

    window.onbeforeunload = function() {
      if (_this.isTicking()) return "Still ticking!";
    };

    $scope.timerType = POMODORO_STR;
    $scope.formattedTime = dateFilter(DURATIONS.pomodoro, DATE_FORMAT);
    $scope.currentTask = new Task();

    /**
     * @param {Task} task
     */
    $scope.removeTask = function(task) {
      tasks.splice(tasks.indexOf(task), 1);
      saveTasks(tasks);
    };

    _this.updateTimeDisplay = function(timeLeft, taskName) {
      $scope.formattedTime = dateFilter(timeLeft, DATE_FORMAT);
      document.title = $scope.formattedTime + (taskName ? ' - ' + taskName : '') + ' - Pomodoro Tracker';
    };

    /**
     * @return {Boolean}
     */
    _this.isTicking = function() {
      return !!_this.countdownInterval;
    };

    /**
     * @param {Number} duration
     * @param {String} durationType
     */
    _this.startCountdown = function(duration, durationType) {
      if (!duration || !durationType || _this.isTicking()) return;

      var timeLeft = duration,
        currentTask = $scope.currentTask;

      // Looking for task with same name
      tasks.some(function(task) {
        if (!currentTask.name.match(new RegExp(task.name.trim(), 'gi'))) return false;

        $scope.currentTask = currentTask = task;
        return true;
      });

      $scope.timerType = durationType;

      _this.updateTimeDisplay(timeLeft, currentTask.name);
      _this.countdownInterval = $interval(function() {
        timeLeft -= SECOND;
        _this.updateTimeDisplay(timeLeft, currentTask.name);
      }, SECOND, timeLeft / SECOND);

      _this.countdownInterval.then(function timeIsOut() {
        _this.countdownInterval = null;

        if (!currentTask.name) currentTask.name = DEFAULT_TASK_NAME;

        if (!~tasks.indexOf(currentTask)) tasks.unshift(currentTask);

        currentTask.periods.push(new Period(durationType));
        $scope.currentTask = new Task(currentTask.name);

        saveTasks(tasks);

        notifyAll(currentTask.name);

        ga('send', 'event', durationType, 'end');
      });
    };

    _this.cancelCountdown = function() {
      $interval.cancel(_this.countdownInterval);
      _this.countdownInterval = null;
    };

    _this.restartCountdown = function(duration, durationType) {
      _this.cancelCountdown();
      _this.startCountdown(duration, durationType);
    };

    /**
     * Switch currently selected task.
     * @param {Task} task
     * @public
     */
    $scope.select = function(task) {
      $scope.currentTask = task;
    };

    /**
     * @param {String} durationType
     * @public
     */
    $scope.start = function(durationType) {
      document.activeElement.blur();

      var duration = DURATIONS[durationType];
      if (!duration) return;

      _this.restartCountdown(duration, durationType);

      ga('send', 'event', durationType, 'start');
    };

    /**
     * @public
     */
    $scope.submit = function() {
      document.activeElement.blur();

      if (_this.isTicking()) return;
      _this.restartCountdown(DURATIONS[POMODORO_STR], POMODORO_STR);
    };

    // HTML5 Notifications
    $scope.showNotifyButton = notifications && notifications.permission !== 'granted';
    $scope.turnNotifications = function() {
      notifications.requestPermission();
      $scope.showNotifyButton = false;
    };
  }]);
})(window, document);
<commit_msg>Fixed quote style<commit_after>(function(window, document) {
  var angular = window.angular,
    ga = window.ga,
    localStorage = window.localStorage || {},

    POMODORO_STR = 'pomodoro',

    app = angular.module(POMODORO_STR, []),
    isDev = localStorage['isDev'],

    SECOND = 1000,
    MINUTE = 60 * SECOND,
    SCALE = isDev ? SECOND : MINUTE,

    DURATIONS = {
      pomodoro: 25 * SCALE,
      shortBreak: 5 * SCALE,
      longBreak: 15 * SCALE
    },

    DATE_FORMAT = 'mm:ss',
    DEFAULT_TASK_NAME = 'Some task',

    notifications = window.Notification;

  /**
   * @param {String} [name]
   * @constructor
   */
  function Task(name) {
    this.name = name || '';
    this.periods = [];
  }

  /**
   * @param {String} type
   * @constructor
   */
  function Period(type) {
    this.type = type;
    this.time = new Date().getTime();
  }

  /**
   * @return {Task[]}
   */
  function loadTasks() {
    return angular.fromJson(localStorage['tasks'] || '[]');
  }

  /**
   * @param {Task[]} tasks
   */
  function saveTasks(tasks) {
    localStorage['tasks'] = angular.toJson(tasks);
  }

  function soundAlert() {
    // http://stackoverflow.com/questions/8733330/why-cant-i-play-sounds-more-than-once-using-html5-audio-tag
    var audio = document.createElement('audio');
    audio.src = 'assets/alert.mp3';
    audio.load();
    audio.play();
  }

  function notifyAll(taskName) {
    document.title = 'Time is up!' + (taskName ? ' - ' + taskName : '') + ' - Pomodoro Tracker';

    soundAlert();

    var notification = new Notification('Pomodoro Tracker', {
      tag: 'pomodoro-tracker',
      body: (taskName ? taskName + ' - ' : '') + 'Time is up!'
    });

    // Automatically close notification after 5 seconds
    notification.onshow = function() {
      setTimeout(function() { notification.close(); }, 5 * SECOND);
    };
  }

  /**
   * @param {Number|Date} date
   * @return {Number}
   */
  function dropTime(date) {
    date = new Date(date);
    return new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime();
  }

  /**
   * @return {Number}
   */
  function todayDate() {
    return dropTime(new Date());
  }

  app.filter('todayPeriods', function() {
    return function(tasks) {
      var today = todayDate();

      return tasks.reduce(function(result, task) {
        return result.concat(task.periods || []);
      }, []).filter(function(period) {
          return today === dropTime(period.time);
        });
    };
  });

  app.filter('pomodoros', function() {
    return function(periods) {
      return periods.filter(function(period) {
        return period.type === POMODORO_STR;
      }).length || '';
    };
  });

  app.controller('MainCtrl', ['$scope', '$interval', 'dateFilter', function($scope, $interval, dateFilter) {
    var _this = this,
      tasks = $scope.tasks = loadTasks();

    window.onbeforeunload = function() {
      if (_this.isTicking()) return 'Still ticking!';
    };

    $scope.timerType = POMODORO_STR;
    $scope.formattedTime = dateFilter(DURATIONS.pomodoro, DATE_FORMAT);
    $scope.currentTask = new Task();

    /**
     * @param {Task} task
     */
    $scope.removeTask = function(task) {
      tasks.splice(tasks.indexOf(task), 1);
      saveTasks(tasks);
    };

    _this.updateTimeDisplay = function(timeLeft, taskName) {
      $scope.formattedTime = dateFilter(timeLeft, DATE_FORMAT);
      document.title = $scope.formattedTime + (taskName ? ' - ' + taskName : '') + ' - Pomodoro Tracker';
    };

    /**
     * @return {Boolean}
     */
    _this.isTicking = function() {
      return !!_this.countdownInterval;
    };

    /**
     * @param {Number} duration
     * @param {String} durationType
     */
    _this.startCountdown = function(duration, durationType) {
      if (!duration || !durationType || _this.isTicking()) return;

      var timeLeft = duration,
        currentTask = $scope.currentTask;

      // Looking for task with same name
      tasks.some(function(task) {
        if (!currentTask.name.match(new RegExp(task.name.trim(), 'gi'))) return false;

        $scope.currentTask = currentTask = task;
        return true;
      });

      $scope.timerType = durationType;

      _this.updateTimeDisplay(timeLeft, currentTask.name);
      _this.countdownInterval = $interval(function() {
        timeLeft -= SECOND;
        _this.updateTimeDisplay(timeLeft, currentTask.name);
      }, SECOND, timeLeft / SECOND);

      _this.countdownInterval.then(function timeIsOut() {
        _this.countdownInterval = null;

        if (!currentTask.name) currentTask.name = DEFAULT_TASK_NAME;

        if (!~tasks.indexOf(currentTask)) tasks.unshift(currentTask);

        currentTask.periods.push(new Period(durationType));
        $scope.currentTask = new Task(currentTask.name);

        saveTasks(tasks);

        notifyAll(currentTask.name);

        ga('send', 'event', durationType, 'end');
      });
    };

    _this.cancelCountdown = function() {
      $interval.cancel(_this.countdownInterval);
      _this.countdownInterval = null;
    };

    _this.restartCountdown = function(duration, durationType) {
      _this.cancelCountdown();
      _this.startCountdown(duration, durationType);
    };

    /**
     * Switch currently selected task.
     * @param {Task} task
     * @public
     */
    $scope.select = function(task) {
      $scope.currentTask = task;
    };

    /**
     * @param {String} durationType
     * @public
     */
    $scope.start = function(durationType) {
      document.activeElement.blur();

      var duration = DURATIONS[durationType];
      if (!duration) return;

      _this.restartCountdown(duration, durationType);

      ga('send', 'event', durationType, 'start');
    };

    /**
     * @public
     */
    $scope.submit = function() {
      document.activeElement.blur();

      if (_this.isTicking()) return;
      _this.restartCountdown(DURATIONS[POMODORO_STR], POMODORO_STR);
    };

    // HTML5 Notifications
    $scope.showNotifyButton = notifications && notifications.permission !== 'granted';
    $scope.turnNotifications = function() {
      notifications.requestPermission();
      $scope.showNotifyButton = false;
    };
  }]);
})(window, document);
<|endoftext|>


<commit_before>// swapiModule is from https://github.com/cfjedimaster/SWAPI-Wrapper

function getAllPeople (page) {
  var people = {}

  // Outter promise that will wait til inner promises are done being collected
  return new Promise(function (outerRes, outerRej) {
    function getPeoplePage(page) {
      // inner promises for each page of data from swapi API...
      return new Promise(function (innerRes, innerRej) {
        swapiModule.getPeople(page, function (swapipage) {
          swapipage.results.forEach( character => {
            people[character.name] = character;
          });
          if (swapipage.next !== null) {
            innerRes(getPeoplePage(page + 1));
          }
          else {
            outerRes(people);
          }
        })
      })
    }
    getPeoplePage(page);
  })
}  // end of getAllPeople()

getAllPeople(1)
  .then(function (data) {
    var images = {};
    // populate the images object with urls for each character
    for (k in data) {
      images[k] = `/img/${k.replace(/\s/g, '_').toLowerCase()}.jpg`;
    }
    // add the img_url to each character object
    for (k in data) {
      if ( images[k] ) {
          data[k]["img_url"] = images[k];
      }
    }
    // testing testing...
    console.log(data);
    console.log(images);
    //console.log(`Who is this?  Name: ${data["BB8"].name}  Image URL: ${data["BB8"].img_url}`);
    return data;

  }) //Yep the data gets into the next promise just fine...
  .then(function(data) {
    generateWhoQuestion(data);
    //console.log(`Who is ${data[char].name}? Image: ${data[char].img_url}`);
  })

function generateWhoQuestion(obj){
  // assign the answer and the alternatives...
  let char = fetchRandomCharacter(obj);
  let answer = char;
  let false1 = fetchRandomCharacter(obj);
  let false2 = fetchRandomCharacter(obj);
  let false3 = fetchRandomCharacter(obj);
  let false4 = fetchRandomCharacter(obj);
  let choices = [false1, false2, false3, false4];

  // push the right answer onto the array of answers
  choices.push(char);

  // make sure no duplicates exist in the answer array
  if (noDupes(choices)){
    console.log('No Duplicates!!!!');
  } else {
    console.log('There be Duplicates!!!!');
    // NEED TO CREATE A REMOVE_DUPES() function here...
    //generateWhoQuestion(obj);
  }

  console.log('before shuffle...');
  console.log(`${choices}`);

  // Shuffle the array sequence to the same anwer doesn't
  // always appear in the same spot...
  shuffleArray(choices);
  console.log('after shuffle...');

  // Here's the questions, followed the an array of possible answers...
  console.log(`Who is this: ${obj[char].img_url}?`);
  console.log(`${choices}`);
}  // end of generateWhoQuestion()

function noDupes(arr){
  let unique = [...new Set(arr)];
  if (unique.length !== arr.length){
    return false;
  } else {
    return true;
  }
}

function shuffleArray(arr){
  for (let i = arr.length; i; i--){
    let j = Math.floor(Math.random() * i);
    [arr[i-1], arr[j]] = [arr[j], arr[i-1]];
  }
  return arr;
}

function fetchRandomCharacter(obj) {
  var keys = [];
  for (key in obj) {
    keys.push(key);
  }
  return keys[Math.floor(Math.random() * keys.length)];
}

// Set up the array for all characters; populated it from the swapi site

//var allCharacters = [];

// swapiModule is from https://github.com/cfjedimaster/SWAPI-Wrapper

// function getData(page, arr){
//   swapiModule.getPeople(page, function(data){
//     data.results.forEach(function(character){
//       arr.push(character);
//       console.log("Name: ", character.name);
//     });
//     if (data.next) { getData(page+1, arr); }
//   })
//   return arr;
// }

// function getData(page, arr){
//   return new Promise(function(resolve){
//     swapiModule.getPeople(page, function(data){
//       data.results.forEach(function(character){
//         arr.push(character);
//         console.log("Name: ", character.name);
//       });
//       if (data.next) { getData(page+1, arr); }
//     })
//     resolve(arr);
//   })
// }  // end of getData()

// getData(1, allCharacters).then(function(array){ console.log("HERE: ",array[83].name)});

// setTimeout( function() {
//   console.log("Here: ",allCharacters[83].name);
// },  3000);
<commit_msg>basic functionality working now I think...<commit_after>// swapiModule is from https://github.com/cfjedimaster/SWAPI-Wrapper

function getAllPeople (page) {
  var people = {}

  // Outter promise that will wait til inner promises are done being collected
  return new Promise(function (outerRes, outerRej) {
    function getPeoplePage(page) {
      // inner promises for each page of data from swapi API...
      return new Promise(function (innerRes, innerRej) {
        swapiModule.getPeople(page, function (swapipage) {
          swapipage.results.forEach( character => {
            people[character.name] = character;
          });
          if (swapipage.next !== null) {
            innerRes(getPeoplePage(page + 1));
          }
          else {
            outerRes(people);
          }
        })
      })
    }
    getPeoplePage(page);
  })
}  // end of getAllPeople()

getAllPeople(1)
  .then(function (data) {
    var images = {};
    // populate the images object with urls for each character
    for (k in data) {
      images[k] = `/img/${k.replace(/\s/g, '_').toLowerCase()}.jpg`;
    }
    // add the img_url to each character object
    for (k in data) {
      if ( images[k] ) {
          data[k]["img_url"] = images[k];
      }
    }
    // testing testing...
    console.log(data);
    console.log(images);

    return data;

  }) //Yep the data gets into the next promise just fine...
  .then(function(data) {
    for (num=0; num<5; num++){
      generateWhoQuestion(data);
    }
  })

function generateWhoQuestion(obj){
  // assign the answer and the alternatives...
  let char = fetchRandomCharacter(obj);
  let answer = char;
  let false1 = fetchRandomCharacter(obj);
  let false2 = fetchRandomCharacter(obj);
  let false3 = fetchRandomCharacter(obj);
  let false4 = fetchRandomCharacter(obj);
  let choices = [false1, false2, false3, false4];

  // push the right answer onto the array of answers
  choices.push(char);

  // make sure no duplicates exist in the answer array
  if (noDupes(choices)){
    console.log('No Duplicates!!!!');
  } else {
    console.log('There be Duplicates!!!!');
    removeDupes(choices, obj);
  }

  shuffleArray(choices);

  // Here're the questions, followed the an array of possible answers...
  console.log(`Who is this: ${obj[char].img_url}?`);
  console.log(`${choices}`);
}  // end of generateWhoQuestion()

function noDupes(arr){
  let unique = [...new Set(arr)];
  if (unique.length !== arr.length){
    return false;
  } else {
    return true;
  }
}

function removeDupes(arr, obj){
  if ( arr[0] === arr[1] ) {
    arr[0] = fetchRandomCharacter(obj);
  }
  if ( arr[0] === arr[2] ) {
    arr[0] = fetchRandomCharacter(obj);
  }
  if (arr[0] === arr[3] ) {
    arr[0] = fetchRandomCharacter(obj);
  }
  if (arr[0] === arr[4] ) {
      arr[0] = fetchRandomCharacter(obj);
  }
  if (arr[1] === arr[2] ) {
    arr[1] = fetchRandomCharacter(obj);
  }
  if (arr[1] === arr[3] ) {
    arr[1] = fetchRandomCharacter(obj);
  }
  if (arr[1] === arr[4] ) {
    arr[1] = fetchRandomCharacter(obj);
  }
  if (arr[2] === arr[3] ) {
    arr[2] = fetchRandomCharacter(obj);
  }
  if (arr[2] === arr[4] ) {
    arr[2] = fetchRandomCharacter(obj);
  }
  if ( arr[3] === arr[4] ) {
    arr[3] = fetchRandomCharacter(obj)
  }
  return arr;
}

function shuffleArray(arr){
  for (let i = arr.length; i; i--){
    let j = Math.floor(Math.random() * i);
    [arr[i-1], arr[j]] = [arr[j], arr[i-1]];
  }
  return arr;
}

function fetchRandomCharacter(obj) {
  var keys = [];
  for (key in obj) {
    keys.push(key);
  }
  return keys[Math.floor(Math.random() * keys.length)];
}
<|endoftext|>


<commit_before>import $ from "jquery";

// Emote data
import EmoteSet from "./emoteset";

// Tab completion
import Completion from "./completion";

// ReactJS reversing
import {getOwnerInstance} from "./reverse";

// Mutation helpers
import {mutationFind, mutationFindRemoved} from "./mutationhelpers";

function getInternalProps(e) {
    if (e === undefined) {
        return undefined;
    }

    try {
        return getOwnerInstance(e, {include: ["Message"]}).memoizedProps;
    } catch (err) {
        return undefined;
    }
}

// Get a consistent, but unpredictable index from the message snowflake ID
// See: https://github.com/twitter/snowflake/tree/snowflake-2010#solution
function getMessageSeed(e) {
    try {
        // Shift (roughly) 22 bits (10^3 ~ 2^10)
        // All numbers in JS are 64-bit floats, so this is necessary to avoid a huge loss of precision
        return Number(getInternalProps(e).message.id.slice(0, -6)) >>> 2;
    } catch (err) {
        // Something (not surprisingly) broke, but this isn't critical enough to completely bail over
        console.error("getMessageSeed:", e, err);
        return 0;
    }
}

// jQuery Plugins

// Get or set the scroll distance from the bottom of an element
// Usage identical to scrollTop()
$.fn.scrollBottom = function (val) {
    var elem = this[0];
    if (val === undefined) {
        if (elem === undefined) {
            return undefined;
        }
        return elem.scrollHeight - (this.scrollTop() + this.height());
    }
    if (elem === undefined) {
        return this;
    }
    return this.scrollTop(elem.scrollHeight - (val + this.height()));
};

// Get the set of text nodes contained within a set of elements
$.fn.textNodes = function () {
    return this.map(function () {
        const textNodes = [];
        let textNodeWalker, currentNode;

        if ($(this).find("code").length > 0) {
            // If there are any code blocks, rejecting that whole subtree is a bit faster
            textNodeWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
                acceptNode: function (node) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        return NodeFilter.FILTER_ACCEPT;
                    }
                    if (node.nodeName.toLowerCase() === "code") {
                        return NodeFilter.FILTER_REJECT;
                    }
                    return NodeFilter.FILTER_SKIP;
                }
            });
        } else {
            textNodeWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT);
        }

        while ((currentNode = textNodeWalker.nextNode()) !== null) {
            textNodes.push(currentNode);
        }
        return textNodes;
    });
};

// Parse for standard emotes in message text
$.fn.parseEmotesStandard = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    this.textNodes().each(function () {
        var sub = [];
        // separate out potential emotes
        // all standard emotes are composed of characters in [a-zA-Z0-9_], i.e. \w between two colons, :
        // use a regex with a capture group, so that we can preserve separators
        var words = this.data.split(/(:[\w#*]+:)/g);
        // non-emoteable words, for building a new text node if necessary
        var nonEmote = [];
        // whether the text in this node has been modified
        var modified = false;

        var seed = 0;
        var message = $(this).closest(".message").not(".message-sending");
        // Don't look up the useless id for messages being sent
        if (message.length !== 0) {
            // Get a seed for rolls
            seed = getMessageSeed(message[0]);
        }

        for (var i = 0; i < words.length; i += 2) {
            // words[i] is a separator
            // words[i+1] is our potential emote, or undefined

            // Keep the separator
            nonEmote.push(words[i]);
            if (words[i+1] === undefined) {
                break;
            }

            var emote;
            for (var set of emoteSets) {
                emote = set.createEmote(/^:([^:]+):$/.exec(words[i+1])[1], seed);
                if (emote !== undefined) {
                    break;
                }
            }
            if (emote !== undefined) {
                modified = true;
                // Create a new text node from any previous text
                sub.push(document.createTextNode(nonEmote.join("")));
                // Clear out stored words
                nonEmote = [];
                // Add the emote element
                sub.push(emote);
            } else {
                // Unrecognized as emote, keep the word
                nonEmote.push(words[i+1]);
            }
        }
        // If no emotes were found, leave this text node unchanged
        if (modified) {
            // Replace this node's contents with remaining text
            this.data = nonEmote.join("");
        }
        $(this).before(sub);
    });

    return this;
};

// Parse for Twitch-style emotes in message text
$.fn.parseEmotesTwitch = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    // Find and replace Twitch-style emotes
    // This requires picking apart text nodes more carefully
    this.textNodes().each(function () {
        var sub = [];
        // separate out potential emotes
        // all twitch emotes (that we care about) are composed of characters in [a-zA-Z0-9_], i.e. \w
        // use a regex with a capture group, so that we can preserve separators
        var words = this.data.split(/(\W+)/g);
        // non-emoteable words, for building a new text node if necessary
        var nonEmote = [];
        // whether the text in this node has been modified
        var modified = false;
        for (var i = 0; i < words.length; i += 2) {
            // words[i] is our potential emote
            // words[i+1] is a separator, or undefined
            var emote;
            for (var set of emoteSets) {
                emote = set.createEmote(words[i]);
                if (emote !== undefined) {
                    break;
                }
            }
            if (emote !== undefined) {
                modified = true;
                // Create a new text node from any previous text
                sub.push(document.createTextNode(nonEmote.join("")));
                // Clear out stored words
                nonEmote = [];
                // Add the emote element
                sub.push(emote);
            } else {
                // Unrecognized as emote, keep the word
                nonEmote.push(words[i]);
            }
            // Keep the separator
            nonEmote.push(words[i+1]);
        }
        // If no emotes were found, leave this text node unchanged
        if (modified) {
            // Replace this node's contents with remaining text
            this.data = nonEmote.join("");
        }
        $(this).before(sub);
    });

    return this;
};

// Parse emotes (of any style) in message text
$.fn.parseEmotes = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    var standardSets = [];
    var twitchSets = [];
    for (var set of emoteSets) {
        if (set.emoteStyle === EmoteSet.emoteStyle.STANDARD) {
            standardSets.push(set);
        } else if (set.emoteStyle === EmoteSet.emoteStyle.TWITCH) {
            twitchSets.push(set);
        }
    }

    // Process messages for emote replacements
    this.parseEmotesStandard(standardSets).parseEmotesTwitch(twitchSets);

    // Properly jumboify emotes/emoji in messages with no other text
    this.not(".topic-2QX7LI.expandable-26ClD4").has(".emoji").each(function () {
        // Get the "edited" text, if any, regardless of how it's styled or localized
        var edited = $(this).find(".edited").text();
        // Get the remaining message text
        var text = this.textContent.replace(edited, "").trim();
        if (text.length === 0) {
            $(this).find(".emoji").addClass("jumboable");
        } else {
            $(this).find(".emoji").removeClass("jumboable");
        }
    });

    return this;
};

// Replace title text with fancy tooltips
$.fn.fancyTooltip = function () {
    return this.filter("[title]").each(function () {
        var title = $(this).attr("title");
        $(this).addClass("kawaii-fancytooltip").removeAttr("title");
        $(this).on("mouseover.fancyTooltip", function () {
            // Create and insert tooltip
            var tooltip = $("<div>").append(title).addClass("tooltip tooltip-top tooltip-black");
            $(".tooltips").append(tooltip);

            // Position the tooltip
            var tooltipRect = tooltip[0].getBoundingClientRect();
            var targetRect = this.getBoundingClientRect();
            var position = {
                top: targetRect.top - tooltipRect.height - 8,
                left: targetRect.left + targetRect.width/2 - tooltipRect.width/2,
            }
            tooltip.offset(position);

            // Set a handler to destroy the tooltip
            $(this).on("mouseout.fancyTooltip", function () {
                // remove this handler
                $(this).off("mouseout.fancyTooltip");
                tooltip.remove();
            });
        });
    });
};

// Attach observer to start triggering mutations
function startObserver(observer) {
    // Get everything except tooltips
    const selector = [
        "#app-mount>:not(.tooltips)",
    ].join(",");
    for (let target of document.querySelectorAll(selector)) {
        observer.observe(target, { childList:true, subtree:true, characterData:true });
    }
}

// Detach observer to stop triggering mutations
function stopObserver(observer) {
    observer.disconnect();
}

function processMutation(mutation, observer) {
    // Get the set of messages and/or topic area affected by this mutation
    const selector = [
        ".markup",
        ".message-content",
        ".topic-2QX7LI.expandable-26ClD4",
        ".content-8biNdB.selectable",
    ].join(",");
    const messages = mutationFind(mutation, selector)
        .not(":has(.message-content)");

    // Ignore changes made here
    stopObserver(observer);

    // Figure out whether we're scrolled to the bottom
    const messagesContainer = $(".messages");
    const atBottom = messagesContainer.scrollBottom() < 0.5;

    // Process messages
    messages.parseEmotes([smutbaseEmotes, partyParrotEmotes, twitchEmotes, twitchSubEmotes])
        .find(".kawaii-parseemotes").fancyTooltip();

    // Clean up any remaining tooltips
    mutationFindRemoved(mutation, ".kawaii-fancytooltip")
        .trigger("mouseout.fancyTooltip");

    // Ensure we're still scrolled to the bottom if necessary
    if (atBottom) {
        messagesContainer.scrollBottom(0);
    }

    // Resume observer
    startObserver(observer);
}

function aggregateMutations(mutations) {
    // Aggregate mutations
    const target = [], addedNodes = [], removedNodes = [];
    for (let mutation of mutations) {
        target.push(mutation.target);
        for (let node of mutation.addedNodes) {
            addedNodes.push(node);
        }
        for (let node of mutation.removedNodes) {
            removedNodes.push(node);
        }
    }
    return {target, addedNodes, removedNodes};
}

// Watch for new chat messages
const chat_observer = new MutationObserver(function (mutations, observer) {
    processMutation(aggregateMutations(mutations), observer);
});

// Watch for top-level changes (language change, connection issues)
const root_observer = new MutationObserver(function (mutations, observer) {
    const {addedNodes} = aggregateMutations(mutations);
    if (addedNodes.length) {
        // Reparse the page and reconnect the observer
        parseEmoteSet();
    }
});

function parseEmoteSet() {
    processMutation({addedNodes: [document]}, chat_observer);
}

import {twitchEmotes, twitchSubEmotes} from "./emoteset/twitch";
import {smutbaseEmotes} from "./emoteset/smutbase";
import {partyParrotEmotes} from "./emoteset/partyparrot";
import {discordEmoji} from "./emoteset/stock";

function loadEmoteSets() {
    // Load the emote sets if necessary, and parse the document as they load
    twitchEmotes.load().then(parseEmoteSet);
    // Hold off on loading this set by default until we add a settings panel
    //twitchSubEmotes.load().then(parseEmoteSet);
    smutbaseEmotes.load().then(parseEmoteSet);
    partyParrotEmotes.load().then(parseEmoteSet);
    discordEmoji.load();
}

export default function KawaiiDiscord() {}

KawaiiDiscord.prototype.load = function () {};

KawaiiDiscord.prototype.unload = function () {};

var updateRate = 2*60*60*1000;
var updateIntervalID;

KawaiiDiscord.prototype.start = function () {
    loadEmoteSets();

    // FIXME: handle lazy loading of webpack modules properly
    window.setTimeout(()=>Completion.start([smutbaseEmotes, partyParrotEmotes, twitchEmotes, twitchSubEmotes, discordEmoji]), 1000);

    startObserver(chat_observer);
    root_observer.observe(document.querySelector("#app-mount"), { childList:true });

    updateIntervalID = setInterval(loadEmoteSets, 2*60*60*1000);
};

KawaiiDiscord.prototype.stop = function () {
    clearInterval(updateIntervalID);

    root_observer.disconnect();
    stopObserver(chat_observer);

    Completion.stop();

    // Swap every emote back to its original text
    $(".kawaii-parseemotes").each(function() {
        var emote = $(this);
        emote.replaceWith(document.createTextNode(emote.attr("alt")));
    });
};

KawaiiDiscord.prototype.getSettingsPanel = function () {
    return "";
};

// vim: et:ts=4:sw=4:sts=4
<commit_msg>Update name for Message React component<commit_after>import $ from "jquery";

// Emote data
import EmoteSet from "./emoteset";

// Tab completion
import Completion from "./completion";

// ReactJS reversing
import {getOwnerInstance} from "./reverse";

// Mutation helpers
import {mutationFind, mutationFindRemoved} from "./mutationhelpers";

function getInternalProps(e) {
    if (e === undefined) {
        return undefined;
    }

    try {
        return getOwnerInstance(e, {include: ["FluxContainer(t)"]}).memoizedProps;
    } catch (err) {
        return undefined;
    }
}

// Get a consistent, but unpredictable index from the message snowflake ID
// See: https://github.com/twitter/snowflake/tree/snowflake-2010#solution
function getMessageSeed(e) {
    try {
        // Shift (roughly) 22 bits (10^3 ~ 2^10)
        // All numbers in JS are 64-bit floats, so this is necessary to avoid a huge loss of precision
        return Number(getInternalProps(e).message.id.slice(0, -6)) >>> 2;
    } catch (err) {
        // Something (not surprisingly) broke, but this isn't critical enough to completely bail over
        console.error("getMessageSeed:", e, err);
        return 0;
    }
}

// jQuery Plugins

// Get or set the scroll distance from the bottom of an element
// Usage identical to scrollTop()
$.fn.scrollBottom = function (val) {
    var elem = this[0];
    if (val === undefined) {
        if (elem === undefined) {
            return undefined;
        }
        return elem.scrollHeight - (this.scrollTop() + this.height());
    }
    if (elem === undefined) {
        return this;
    }
    return this.scrollTop(elem.scrollHeight - (val + this.height()));
};

// Get the set of text nodes contained within a set of elements
$.fn.textNodes = function () {
    return this.map(function () {
        const textNodes = [];
        let textNodeWalker, currentNode;

        if ($(this).find("code").length > 0) {
            // If there are any code blocks, rejecting that whole subtree is a bit faster
            textNodeWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
                acceptNode: function (node) {
                    if (node.nodeType === Node.TEXT_NODE) {
                        return NodeFilter.FILTER_ACCEPT;
                    }
                    if (node.nodeName.toLowerCase() === "code") {
                        return NodeFilter.FILTER_REJECT;
                    }
                    return NodeFilter.FILTER_SKIP;
                }
            });
        } else {
            textNodeWalker = document.createTreeWalker(this, NodeFilter.SHOW_TEXT);
        }

        while ((currentNode = textNodeWalker.nextNode()) !== null) {
            textNodes.push(currentNode);
        }
        return textNodes;
    });
};

// Parse for standard emotes in message text
$.fn.parseEmotesStandard = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    this.textNodes().each(function () {
        var sub = [];
        // separate out potential emotes
        // all standard emotes are composed of characters in [a-zA-Z0-9_], i.e. \w between two colons, :
        // use a regex with a capture group, so that we can preserve separators
        var words = this.data.split(/(:[\w#*]+:)/g);
        // non-emoteable words, for building a new text node if necessary
        var nonEmote = [];
        // whether the text in this node has been modified
        var modified = false;

        var seed = 0;
        var message = $(this).closest(".message").not(".message-sending");
        // Don't look up the useless id for messages being sent
        if (message.length !== 0) {
            // Get a seed for rolls
            seed = getMessageSeed(message[0]);
        }

        for (var i = 0; i < words.length; i += 2) {
            // words[i] is a separator
            // words[i+1] is our potential emote, or undefined

            // Keep the separator
            nonEmote.push(words[i]);
            if (words[i+1] === undefined) {
                break;
            }

            var emote;
            for (var set of emoteSets) {
                emote = set.createEmote(/^:([^:]+):$/.exec(words[i+1])[1], seed);
                if (emote !== undefined) {
                    break;
                }
            }
            if (emote !== undefined) {
                modified = true;
                // Create a new text node from any previous text
                sub.push(document.createTextNode(nonEmote.join("")));
                // Clear out stored words
                nonEmote = [];
                // Add the emote element
                sub.push(emote);
            } else {
                // Unrecognized as emote, keep the word
                nonEmote.push(words[i+1]);
            }
        }
        // If no emotes were found, leave this text node unchanged
        if (modified) {
            // Replace this node's contents with remaining text
            this.data = nonEmote.join("");
        }
        $(this).before(sub);
    });

    return this;
};

// Parse for Twitch-style emotes in message text
$.fn.parseEmotesTwitch = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    // Find and replace Twitch-style emotes
    // This requires picking apart text nodes more carefully
    this.textNodes().each(function () {
        var sub = [];
        // separate out potential emotes
        // all twitch emotes (that we care about) are composed of characters in [a-zA-Z0-9_], i.e. \w
        // use a regex with a capture group, so that we can preserve separators
        var words = this.data.split(/(\W+)/g);
        // non-emoteable words, for building a new text node if necessary
        var nonEmote = [];
        // whether the text in this node has been modified
        var modified = false;
        for (var i = 0; i < words.length; i += 2) {
            // words[i] is our potential emote
            // words[i+1] is a separator, or undefined
            var emote;
            for (var set of emoteSets) {
                emote = set.createEmote(words[i]);
                if (emote !== undefined) {
                    break;
                }
            }
            if (emote !== undefined) {
                modified = true;
                // Create a new text node from any previous text
                sub.push(document.createTextNode(nonEmote.join("")));
                // Clear out stored words
                nonEmote = [];
                // Add the emote element
                sub.push(emote);
            } else {
                // Unrecognized as emote, keep the word
                nonEmote.push(words[i]);
            }
            // Keep the separator
            nonEmote.push(words[i+1]);
        }
        // If no emotes were found, leave this text node unchanged
        if (modified) {
            // Replace this node's contents with remaining text
            this.data = nonEmote.join("");
        }
        $(this).before(sub);
    });

    return this;
};

// Parse emotes (of any style) in message text
$.fn.parseEmotes = function (emoteSets) {
    if (emoteSets === undefined || emoteSets.length === 0) {
        return this;
    }

    var standardSets = [];
    var twitchSets = [];
    for (var set of emoteSets) {
        if (set.emoteStyle === EmoteSet.emoteStyle.STANDARD) {
            standardSets.push(set);
        } else if (set.emoteStyle === EmoteSet.emoteStyle.TWITCH) {
            twitchSets.push(set);
        }
    }

    // Process messages for emote replacements
    this.parseEmotesStandard(standardSets).parseEmotesTwitch(twitchSets);

    // Properly jumboify emotes/emoji in messages with no other text
    this.not(".topic-2QX7LI.expandable-26ClD4").has(".emoji").each(function () {
        // Get the "edited" text, if any, regardless of how it's styled or localized
        var edited = $(this).find(".edited").text();
        // Get the remaining message text
        var text = this.textContent.replace(edited, "").trim();
        if (text.length === 0) {
            $(this).find(".emoji").addClass("jumboable");
        } else {
            $(this).find(".emoji").removeClass("jumboable");
        }
    });

    return this;
};

// Replace title text with fancy tooltips
$.fn.fancyTooltip = function () {
    return this.filter("[title]").each(function () {
        var title = $(this).attr("title");
        $(this).addClass("kawaii-fancytooltip").removeAttr("title");
        $(this).on("mouseover.fancyTooltip", function () {
            // Create and insert tooltip
            var tooltip = $("<div>").append(title).addClass("tooltip tooltip-top tooltip-black");
            $(".tooltips").append(tooltip);

            // Position the tooltip
            var tooltipRect = tooltip[0].getBoundingClientRect();
            var targetRect = this.getBoundingClientRect();
            var position = {
                top: targetRect.top - tooltipRect.height - 8,
                left: targetRect.left + targetRect.width/2 - tooltipRect.width/2,
            }
            tooltip.offset(position);

            // Set a handler to destroy the tooltip
            $(this).on("mouseout.fancyTooltip", function () {
                // remove this handler
                $(this).off("mouseout.fancyTooltip");
                tooltip.remove();
            });
        });
    });
};

// Attach observer to start triggering mutations
function startObserver(observer) {
    // Get everything except tooltips
    const selector = [
        "#app-mount>:not(.tooltips)",
    ].join(",");
    for (let target of document.querySelectorAll(selector)) {
        observer.observe(target, { childList:true, subtree:true, characterData:true });
    }
}

// Detach observer to stop triggering mutations
function stopObserver(observer) {
    observer.disconnect();
}

function processMutation(mutation, observer) {
    // Get the set of messages and/or topic area affected by this mutation
    const selector = [
        ".markup",
        ".message-content",
        ".topic-2QX7LI.expandable-26ClD4",
        ".content-8biNdB.selectable",
    ].join(",");
    const messages = mutationFind(mutation, selector)
        .not(":has(.message-content)");

    // Ignore changes made here
    stopObserver(observer);

    // Figure out whether we're scrolled to the bottom
    const messagesContainer = $(".messages");
    const atBottom = messagesContainer.scrollBottom() < 0.5;

    // Process messages
    messages.parseEmotes([smutbaseEmotes, partyParrotEmotes, twitchEmotes, twitchSubEmotes])
        .find(".kawaii-parseemotes").fancyTooltip();

    // Clean up any remaining tooltips
    mutationFindRemoved(mutation, ".kawaii-fancytooltip")
        .trigger("mouseout.fancyTooltip");

    // Ensure we're still scrolled to the bottom if necessary
    if (atBottom) {
        messagesContainer.scrollBottom(0);
    }

    // Resume observer
    startObserver(observer);
}

function aggregateMutations(mutations) {
    // Aggregate mutations
    const target = [], addedNodes = [], removedNodes = [];
    for (let mutation of mutations) {
        target.push(mutation.target);
        for (let node of mutation.addedNodes) {
            addedNodes.push(node);
        }
        for (let node of mutation.removedNodes) {
            removedNodes.push(node);
        }
    }
    return {target, addedNodes, removedNodes};
}

// Watch for new chat messages
const chat_observer = new MutationObserver(function (mutations, observer) {
    processMutation(aggregateMutations(mutations), observer);
});

// Watch for top-level changes (language change, connection issues)
const root_observer = new MutationObserver(function (mutations, observer) {
    const {addedNodes} = aggregateMutations(mutations);
    if (addedNodes.length) {
        // Reparse the page and reconnect the observer
        parseEmoteSet();
    }
});

function parseEmoteSet() {
    processMutation({addedNodes: [document]}, chat_observer);
}

import {twitchEmotes, twitchSubEmotes} from "./emoteset/twitch";
import {smutbaseEmotes} from "./emoteset/smutbase";
import {partyParrotEmotes} from "./emoteset/partyparrot";
import {discordEmoji} from "./emoteset/stock";

function loadEmoteSets() {
    // Load the emote sets if necessary, and parse the document as they load
    twitchEmotes.load().then(parseEmoteSet);
    // Hold off on loading this set by default until we add a settings panel
    //twitchSubEmotes.load().then(parseEmoteSet);
    smutbaseEmotes.load().then(parseEmoteSet);
    partyParrotEmotes.load().then(parseEmoteSet);
    discordEmoji.load();
}

export default function KawaiiDiscord() {}

KawaiiDiscord.prototype.load = function () {};

KawaiiDiscord.prototype.unload = function () {};

var updateRate = 2*60*60*1000;
var updateIntervalID;

KawaiiDiscord.prototype.start = function () {
    loadEmoteSets();

    // FIXME: handle lazy loading of webpack modules properly
    window.setTimeout(()=>Completion.start([smutbaseEmotes, partyParrotEmotes, twitchEmotes, twitchSubEmotes, discordEmoji]), 1000);

    startObserver(chat_observer);
    root_observer.observe(document.querySelector("#app-mount"), { childList:true });

    updateIntervalID = setInterval(loadEmoteSets, 2*60*60*1000);
};

KawaiiDiscord.prototype.stop = function () {
    clearInterval(updateIntervalID);

    root_observer.disconnect();
    stopObserver(chat_observer);

    Completion.stop();

    // Swap every emote back to its original text
    $(".kawaii-parseemotes").each(function() {
        var emote = $(this);
        emote.replaceWith(document.createTextNode(emote.attr("alt")));
    });
};

KawaiiDiscord.prototype.getSettingsPanel = function () {
    return "";
};

// vim: et:ts=4:sw=4:sts=4
<|endoftext|>


<commit_before>"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const electron_1 = require("electron");
const input_validation_service_1 = require("./input-validation-service");
const config_1 = require("./config");
let mainWindow;
let inputValidationService = new input_validation_service_1.InputValidationService();
let executionService = config_1.Config.getExecutionService();
function createMainWindow() {
    mainWindow = new electron_1.BrowserWindow({
        center: true,
        autoHideMenuBar: true,
        frame: false,
        show: false
    });
    mainWindow.loadURL(`file://${__dirname}/../main.html`);
    mainWindow.on("close", () => {
        mainWindow = null;
    });
    registerGlobalShortCuts();
}
;
function registerGlobalShortCuts() {
    electron_1.globalShortcut.register("alt+space", toggleWindow);
}
function toggleWindow() {
    if (mainWindow.isVisible()) {
        mainWindow.hide();
    }
    else {
        mainWindow.show();
    }
}
electron_1.app.on("ready", createMainWindow);
electron_1.app.on("window-all-closed", () => {
    electron_1.app.quit();
});
electron_1.ipcMain.on("hide-window", (event, arg) => {
    mainWindow.hide();
});
electron_1.ipcMain.on("get-search", (event, arg) => {
    let userInput = arg;
    let result = inputValidationService.getSearchResult(userInput);
    event.sender.send("get-search-response", result);
});
electron_1.ipcMain.on("execute", (event, arg) => {
    let executionArgument = arg;
    executionService.execute(executionArgument);
    mainWindow.hide();
});
//# sourceMappingURL=main.js.map<commit_msg>Removed transpiled js from source control<commit_after><|endoftext|>