<commit_before>#!/usr/bin/env node

// This script retrieves the next week of scheduled movie airings from Turner
// Classic Movies (TCM) and displays those movies that match a set of favorite
// genres, actors, directors, or screenwriters, or have four-star ratings from
// Leonard Maltin.

"use strict";

const async = require('async');
const dateformat = require('dateformat');
const fs = require('fs');
const handlebars = require('handlebars');
const request = require('request');

// See <http://www.tcm.com/tcmws/v1/docs/welcome.html>
// for web service interface details.
const tcmwsBase = 'http://www.tcm.com/tcmws/v1';

const numberOfDays = 7;

// List of available genres: <http://www.tcm.com/tcmws/v1/schedule/genres.json>
const favoriteGenres = [
    'Black Comedy',
    'Crime',
    'Film Noir',
    'Horror',
    'Mystery',
    'Noir',
    'Sci-Fi',
    'Spy',
    'Thriller',
    'War',
    'Western',
];

const favoriteActors = [
    'Alan Arkin',
    'Alec Guinness',
    'Anthony Hopkins',
    'Anthony Perkins',
    'Anthony Quinn',
    'Ava Gardner',
    'Barbara Stanwyck',
    'Basil Rathbone',
    'Bela Lugosi',
    'Bette Davis',
    'Boris Karloff',
    'Burt Lancaster',
    'Cary Grant',
    'Cesar Romero',
    'Charles Laughton',
    'Christopher Lee',
    'Claire Trevor',
    'Claude Rains',
    'Clint Eastwood',
    'David Niven',
    'Dean Martin',
    'Deborah Kerr',
    'Edward G. Robinson',
    'Eli Wallach',
    'Fred MacMurray',
    'Fredric March',
    'Gabriel Byrne',
    'Gary Cooper',
    'Gene Hackman',
    'Gene Tierney',
    'Gene Wilder',
    'George Sanders',
    'Glenn Ford',
    'Greer Garson',
    'Gregory Peck',
    'Groucho Marx',
    'Harry Belafonte',
    'Hedy Lamarr',
    'Henry Fonda',
    'Honor Blackman',
    'Humphrey Bogart',
    'Ingrid Bergman',
    'Jack Lemmon',
    'James Coburn',
    'James Garner',
    'James Stewart',
    'Jean Arthur',
    'Joan Fontaine',
    'John Carradine',
    'John Cusack',
    'Kirk Douglas',
    'Lana Turner',
    'Lauren Bacall',
    'Lawrence Tierney',
    'Lee Van Cleef',
    'Marlene Dietrich',
    "Maureen O'Hara",
    'Max Von Sydow',
    'Michael Caine',
    'Orson Welles',
    'Paul Newman',
    'Peter Cushing',
    'Peter Lorre',
    "Peter O'Toole",
    'Peter Sellers',
    'Richard Crenna',
    'Richard Harris',
    'Richard Pryor',
    'Rita Hayworth',
    'Robert Montgomery',
    'Ronald Colman',
    'Robert Redford',
    'Sammy Davis Jr.',
    'Sean Connery',
    'Sidney Poitier',
    'Sophia Loren',
    'Steve McQueen',
    'Sydney Greenstreet',
    'Tony Curtis',
    'Toshiro Mifune',
    'Vera Miles',
    'Vincent Price',
    'William Holden',
    'William Powell',
    'Yvonne De Carlo',
];

const favoriteDirectors = [
    'Akria Kurosawa',
    'Alfred Hitchcock',
    'Billy Wilder',
    'David Lean',
    'Ethan Coen',
    'Francis Coppola',
    'James Whale',
    'Joel Coen',
    'John Ford',
    'John Frankenheimer',
    'John Huston',
    'John Sturges',
    'Mel Brooks',
    'Oliver Stone',
    'Orson Welles',
    'Otto Preminger',
    'Robert Altman',
    'Sam Peckinpah',
    'Sergio Leone',
    'Sidney Lumet',
    'Stanley Kubrick',
    'Terence Fisher',
    'Woody Allen',
];

const favoriteWriters = [
    'Ben Hecht',
    'Dashiell Hammett',
    'David Mamet',
    'Dorothy B. Hughes',
    'Ethan Coen',
    'Jay Dratler',
    'Jo Eisinger',
    'Joel Coen',
    'Jules Furthman',
    'Lawrence Kasdan',
    'Leigh Brackett',
    'Orson Welles',
    'Preston Sturges',
    'Raymond Chandler',
    'W. R. Burnett',
];

const isFavoriteGenre = {};
favoriteGenres.forEach(x => isFavoriteGenre[x] = true);

const isFavoriteActor = {};
favoriteActors.forEach(x => isFavoriteActor[x] = true);

const isFavoriteDirector = {};
favoriteDirectors.forEach(x => isFavoriteDirector[x] = true);

const isFavoriteWriter = {};
favoriteWriters.forEach(x => isFavoriteWriter[x] = true);

// Return true if the program matches any of our favorite genres, actors, directors, or writers.
function matchesFavorites(program, title, credits) {
    return title.genres.some(genre => isFavoriteGenre[genre]) ||
        names(actors(credits)).some(actor => isFavoriteActor[actor]) ||
        names(directors(credits)).some(director => isFavoriteDirector[director]) ||
        names(writers(credits)).some(writer => isFavoriteWriter[writer]);
}

// Return current date, e.g., '2016-10-25'
function currentDateString() {
    const now = new Date();
    return dateformat(now, 'yyyy-mm-dd');
}

// Return 'name' property values of array elements.
function names(arr) {
    return arr.map(x => x.name);
}

// Return names from an array of objects, as a comma-separated string.
function namesString(arr) {
    return names(arr).join(', ');
}

// Extract the actor objects from credits.
function actors(credits) {
    return credits.filter(x => x.roleName == 'ACTOR');
}

// Extract the director objects from credits.
function directors(credits) {
    return credits.filter(x => x.roleName == 'DIRECTOR');
}

// Extract the writer objects from credits.
function writers(credits) {
    return credits.filter(x => x.roleName == 'WRITER');
}

function isFourStarMaltinRating(maltin) {
    return maltin && (maltin.rating == '****');
}

// Make a request for JSON data.
// Callback takes arguments (error, body), where body is a JSON object.
function getJSON(url, callback) {
    const options = {
        url: url,
        json: true
    };
    request.get(options, (error, response, body) => {
        if (!error && response.statusCode != 200) {
            error = new Error(`Unexpected response status code ${response.statusCode}`);
            body = null;
        }
        callback(error, body);
    });
}

// Retrieve title information from web service.
// Callback takes arguments (error, title).
function getTitle(titleId, callback) {
    const url = `${tcmwsBase}/title/${titleId}.json`;
    getJSON(url, (error, body) => {
        const title = error ? null : body.tcm.title;
        callback(error, title);
    });
}

// Retrieve credits information from web service.
// Callback takes arguments (error, credits).
function getCredits(titleId, callback) {
    const url = `${tcmwsBase}/title/${titleId}/credits.json`;
    getJSON(url, (error, body) => {
        const credits = error ? null : body.tcm.title.credits;
        callback(error, credits);
    });
}

// Get additional data for a title.  Callback takes arguments (error, title, credits).
function getDataForTitleId(titleId, callback) {
    async.parallel(
        [
            done => getTitle(titleId, done),
            done => getCredits(titleId, done)
        ],
        (error, results) => {
            if (error) {
                callback(error);
            }
            else {
                callback(null, results[0], results[1]);
            }
        }
    );
}

// Get daily schedules.
// Callback takes arguments (error, schedulesArray).
function getSchedules(callback) {
    const url = `${tcmwsBase}/schedule/us/est/${currentDateString()}.json?days=${numberOfDays}&includes=maltin`;
    getJSON(url, (error, body) => {
        var schedules = error ? null : body.tcm.schedules;
        callback(error, schedules);
    });
}

function logTitle(title) {
    console.log(`Description: ${title.description}`);
    console.log(`Genres: ${title.genres.join(', ')}`);
}

function logCredits(credits) {
    console.log(`Directed by: ${namesString(directors(credits)) || 'N/A'}`);
    console.log(`Written by: ${namesString(writers(credits)) || 'N/A'}`);
    console.log(`Cast: ${namesString(actors(credits)) || 'N/A'}`);
}

function logMaltinRating(maltin) {
    if (maltin && maltin.rating) {
        console.log(`Maltin Rating: ${maltin.rating}`);
    }
}

function showProgramIfInteresting(program, callback) {
    const titleId = program.titleId;
    getDataForTitleId(program.titleId, (error, title, credits, review) => {
        if (error) {
            console.log(error);
        }
        else {
            if (isFourStarMaltinRating(program.maltin) || matchesFavorites(program, title, credits)) {
                console.log('');
                console.log(`${program.time} - ${program.name} (${program.releaseYear})`);
                logTitle(title);
                logCredits(credits);
                logMaltinRating(program.maltin);
            }
        }
        callback(error);
    });
}

function logSchedule(schedule, callback) {
    console.log('');
    console.log('----------');
    console.log(schedule.startDate);
    console.log('----------');
    async.eachSeries(schedule.programs, showProgramIfInteresting, callback);
}

function logSchedules() {
    getSchedules((error, schedules) => {
        if (error) {
            console.log(error);
        }
        else {
            async.eachSeries(schedules, logSchedule);
        }
    });
}

// HTML output

// For each 'programs' element in 'schedules', request title and credits data
// and then add 'genres', 'actors', 'directors', and 'writers' lists to
// the program, and set 'isAMatch' if the program meets our criteria.
function fillProgramData(programs, callback) {
    async.eachSeries(
        programs,
        (program, done) => {
            getDataForTitleId(program.titleId, (error, title, credits) => {
                if (error) {
                    console.log(error);
                }
                else {
                    program.genres = title.genres;
                    program.actors = actors(credits);
                    program.directors = directors(credits);
                    program.writers = writers(credits);
                    program.isAMatch= isFourStarMaltinRating(program.maltin) || matchesFavorites(program, title, credits);
                }
                done(error);
            });
        },
        (error) => {
            callback(error);
        }
    );
}

function htmlSchedules() {
    getSchedules((error, schedules) => {
        if (error) {
            console.log(error);
        }
        else {
            async.eachSeries(
                schedules,
                (schedule, done) => fillProgramData(schedule.programs, done),
                (error) => {
                    if (error) {
                        console.log(error);
                    }
                    else {
                        fs.readFile('template.html', 'utf-8', (error, source) => {
                            const template = handlebars.compile(source);
                            const html = template({ schedules: schedules });
                            console.log(html)
                        });
                    }
                }
            );
        }
    });
}

//logSchedules();
htmlSchedules();

// vim: set ts=8 sw=4 tw=0 et :
<commit_msg>Transform a title like "Lone Ranger, The" to "The Lone Ranger"<commit_after>#!/usr/bin/env node

// This script retrieves the next week of scheduled movie airings from Turner
// Classic Movies (TCM) and displays those movies that match a set of favorite
// genres, actors, directors, or screenwriters, or have four-star ratings from
// Leonard Maltin.

"use strict";

const async = require('async');
const dateformat = require('dateformat');
const fs = require('fs');
const handlebars = require('handlebars');
const request = require('request');

// See <http://www.tcm.com/tcmws/v1/docs/welcome.html>
// for web service interface details.
const tcmwsBase = 'http://www.tcm.com/tcmws/v1';

const numberOfDays = 7;

// List of available genres: <http://www.tcm.com/tcmws/v1/schedule/genres.json>
const favoriteGenres = [
    'Black Comedy',
    'Crime',
    'Film Noir',
    'Horror',
    'Mystery',
    'Noir',
    'Sci-Fi',
    'Spy',
    'Thriller',
    'War',
    'Western',
];

const favoriteActors = [
    'Alan Arkin',
    'Alec Guinness',
    'Anthony Hopkins',
    'Anthony Perkins',
    'Anthony Quinn',
    'Ava Gardner',
    'Barbara Stanwyck',
    'Basil Rathbone',
    'Bela Lugosi',
    'Bette Davis',
    'Boris Karloff',
    'Burt Lancaster',
    'Cary Grant',
    'Cesar Romero',
    'Charles Laughton',
    'Christopher Lee',
    'Claire Trevor',
    'Claude Rains',
    'Clint Eastwood',
    'David Niven',
    'Dean Martin',
    'Deborah Kerr',
    'Edward G. Robinson',
    'Eli Wallach',
    'Fred MacMurray',
    'Fredric March',
    'Gabriel Byrne',
    'Gary Cooper',
    'Gene Hackman',
    'Gene Tierney',
    'Gene Wilder',
    'George Sanders',
    'Glenn Ford',
    'Greer Garson',
    'Gregory Peck',
    'Groucho Marx',
    'Harry Belafonte',
    'Hedy Lamarr',
    'Henry Fonda',
    'Honor Blackman',
    'Humphrey Bogart',
    'Ingrid Bergman',
    'Jack Lemmon',
    'James Coburn',
    'James Garner',
    'James Stewart',
    'Jean Arthur',
    'Joan Fontaine',
    'John Carradine',
    'John Cusack',
    'Kirk Douglas',
    'Lana Turner',
    'Lauren Bacall',
    'Lawrence Tierney',
    'Lee Van Cleef',
    'Marlene Dietrich',
    "Maureen O'Hara",
    'Max Von Sydow',
    'Michael Caine',
    'Orson Welles',
    'Paul Newman',
    'Peter Cushing',
    'Peter Lorre',
    "Peter O'Toole",
    'Peter Sellers',
    'Richard Crenna',
    'Richard Harris',
    'Richard Pryor',
    'Rita Hayworth',
    'Robert Montgomery',
    'Ronald Colman',
    'Robert Redford',
    'Sammy Davis Jr.',
    'Sean Connery',
    'Sidney Poitier',
    'Sophia Loren',
    'Steve McQueen',
    'Sydney Greenstreet',
    'Tony Curtis',
    'Toshiro Mifune',
    'Vera Miles',
    'Vincent Price',
    'William Holden',
    'William Powell',
    'Yvonne De Carlo',
];

const favoriteDirectors = [
    'Akria Kurosawa',
    'Alfred Hitchcock',
    'Billy Wilder',
    'David Lean',
    'Ethan Coen',
    'Francis Coppola',
    'James Whale',
    'Joel Coen',
    'John Ford',
    'John Frankenheimer',
    'John Huston',
    'John Sturges',
    'Mel Brooks',
    'Oliver Stone',
    'Orson Welles',
    'Otto Preminger',
    'Robert Altman',
    'Sam Peckinpah',
    'Sergio Leone',
    'Sidney Lumet',
    'Stanley Kubrick',
    'Terence Fisher',
    'Woody Allen',
];

const favoriteWriters = [
    'Ben Hecht',
    'Dashiell Hammett',
    'David Mamet',
    'Dorothy B. Hughes',
    'Ethan Coen',
    'Jay Dratler',
    'Jo Eisinger',
    'Joel Coen',
    'Jules Furthman',
    'Lawrence Kasdan',
    'Leigh Brackett',
    'Orson Welles',
    'Preston Sturges',
    'Raymond Chandler',
    'W. R. Burnett',
];

const isFavoriteGenre = {};
favoriteGenres.forEach(x => isFavoriteGenre[x] = true);

const isFavoriteActor = {};
favoriteActors.forEach(x => isFavoriteActor[x] = true);

const isFavoriteDirector = {};
favoriteDirectors.forEach(x => isFavoriteDirector[x] = true);

const isFavoriteWriter = {};
favoriteWriters.forEach(x => isFavoriteWriter[x] = true);

// Return true if the program matches any of our favorite genres, actors, directors, or writers.
function matchesFavorites(program, title, credits) {
    return title.genres.some(genre => isFavoriteGenre[genre]) ||
        names(actors(credits)).some(actor => isFavoriteActor[actor]) ||
        names(directors(credits)).some(director => isFavoriteDirector[director]) ||
        names(writers(credits)).some(writer => isFavoriteWriter[writer]);
}

// Return current date, e.g., '2016-10-25'
function currentDateString() {
    const now = new Date();
    return dateformat(now, 'yyyy-mm-dd');
}

// Return 'name' property values of array elements.
function names(arr) {
    return arr.map(x => x.name);
}

// Return names from an array of objects, as a comma-separated string.
function namesString(arr) {
    return names(arr).join(', ');
}

// Extract the actor objects from credits.
function actors(credits) {
    return credits.filter(x => x.roleName == 'ACTOR');
}

// Extract the director objects from credits.
function directors(credits) {
    return credits.filter(x => x.roleName == 'DIRECTOR');
}

// Extract the writer objects from credits.
function writers(credits) {
    return credits.filter(x => x.roleName == 'WRITER');
}

function isFourStarMaltinRating(maltin) {
    return maltin && (maltin.rating == '****');
}

// Make a request for JSON data.
// Callback takes arguments (error, body), where body is a JSON object.
function getJSON(url, callback) {
    const options = {
        url: url,
        json: true
    };
    request.get(options, (error, response, body) => {
        if (!error && response.statusCode != 200) {
            error = new Error(`Unexpected response status code ${response.statusCode}`);
            body = null;
        }
        callback(error, body);
    });
}

// Retrieve title information from web service.
// Callback takes arguments (error, title).
function getTitle(titleId, callback) {
    const url = `${tcmwsBase}/title/${titleId}.json`;
    getJSON(url, (error, body) => {
        const title = error ? null : body.tcm.title;
        callback(error, title);
    });
}

// Retrieve credits information from web service.
// Callback takes arguments (error, credits).
function getCredits(titleId, callback) {
    const url = `${tcmwsBase}/title/${titleId}/credits.json`;
    getJSON(url, (error, body) => {
        const credits = error ? null : body.tcm.title.credits;
        callback(error, credits);
    });
}

// Get additional data for a title.  Callback takes arguments (error, title, credits).
function getDataForTitleId(titleId, callback) {
    async.parallel(
        [
            done => getTitle(titleId, done),
            done => getCredits(titleId, done)
        ],
        (error, results) => {
            if (error) {
                callback(error);
            }
            else {
                callback(null, results[0], results[1]);
            }
        }
    );
}

// Get daily schedules.
// Callback takes arguments (error, schedulesArray).
function getSchedules(callback) {
    const url = `${tcmwsBase}/schedule/us/est/${currentDateString()}.json?days=${numberOfDays}&includes=maltin`;
    getJSON(url, (error, body) => {
        var schedules = error ? null : body.tcm.schedules;
        callback(error, schedules);
    });
}

function logTitle(title) {
    console.log(`Description: ${title.description}`);
    console.log(`Genres: ${title.genres.join(', ')}`);
}

function logCredits(credits) {
    console.log(`Directed by: ${namesString(directors(credits)) || 'N/A'}`);
    console.log(`Written by: ${namesString(writers(credits)) || 'N/A'}`);
    console.log(`Cast: ${namesString(actors(credits)) || 'N/A'}`);
}

function logMaltinRating(maltin) {
    if (maltin && maltin.rating) {
        console.log(`Maltin Rating: ${maltin.rating}`);
    }
}

function showProgramIfInteresting(program, callback) {
    const titleId = program.titleId;
    getDataForTitleId(program.titleId, (error, title, credits, review) => {
        if (error) {
            console.log(error);
        }
        else {
            if (isFourStarMaltinRating(program.maltin) || matchesFavorites(program, title, credits)) {
                console.log('');
                console.log(`${program.time} - ${program.name} (${program.releaseYear})`);
                logTitle(title);
                logCredits(credits);
                logMaltinRating(program.maltin);
            }
        }
        callback(error);
    });
}

function logSchedule(schedule, callback) {
    console.log('');
    console.log('----------');
    console.log(schedule.startDate);
    console.log('----------');
    async.eachSeries(schedule.programs, showProgramIfInteresting, callback);
}

function logSchedules() {
    getSchedules((error, schedules) => {
        if (error) {
            console.log(error);
        }
        else {
            async.eachSeries(schedules, logSchedule);
        }
    });
}

// HTML output

// For each 'programs' element in 'schedules', request title and credits data
// and then add 'genres', 'actors', 'directors', and 'writers' lists to
// the program, and set 'isAMatch' if the program meets our criteria.
function fillProgramData(programs, callback) {
    async.eachSeries(
        programs,
        (program, done) => {
            getDataForTitleId(program.titleId, (error, title, credits) => {
                if (error) {
                    console.log(error);
                }
                else {
                    if (program.name.endsWith(', The')) {
                        // Change a title like "Lone Ranger, The" to "The Lone Ranger"
                        program.name = 'The ' + program.name.substr(0, program.name.length - 5);
                    }
                    program.genres = title.genres;
                    program.actors = actors(credits);
                    program.directors = directors(credits);
                    program.writers = writers(credits);
                    program.isAMatch= isFourStarMaltinRating(program.maltin) || matchesFavorites(program, title, credits);
                }
                done(error);
            });
        },
        (error) => {
            callback(error);
        }
    );
}

function htmlSchedules() {
    getSchedules((error, schedules) => {
        if (error) {
            console.log(error);
        }
        else {
            async.eachSeries(
                schedules,
                (schedule, done) => fillProgramData(schedule.programs, done),
                (error) => {
                    if (error) {
                        console.log(error);
                    }
                    else {
                        fs.readFile('template.html', 'utf-8', (error, source) => {
                            const template = handlebars.compile(source);
                            const html = template({ schedules: schedules });
                            console.log(html)
                        });
                    }
                }
            );
        }
    });
}

//logSchedules();
htmlSchedules();

// vim: set ts=8 sw=4 tw=0 et :
<|endoftext|>


<commit_before>// Usage:
// $ phantomjs tests.js

var page = require('webpage').create();
var url = 'src/index.html';

page.onResourceError = function(e) {
    console.log("Error loading " + e.url);
    phantom.exit();
}

function fail() {
    console.log("Failed");
    phantom.exit();
}

function next() {
    if (tests.length > 0) {
        var testsStr = tests.length == 1 ? "test" : "tests";
        console.log(tests.length + " " + testsStr + " remaining");
        tests.shift()();
    }
    else {
        console.log("Finished with 0 failures");
        phantom.exit();
    }
}

tests = [

// Page loads with status of 'success'
function() {
page.open(url, function(status) {
    if (status != "success") {
        console.log("Page did not load with status 'success'");
        fail();
    }
    next();
});
},

// Page has text
function() {
page.open(url, function(status) {
    var content = page.evaluate(function() {
        return document.body.textContent.trim();
    });
    if (!content) {
        console.log("Page does not have text");
        fail();
    }
    next();
});
},

// Entering mnemonic generates addresses
function() {
page.open(url, function(status) {
    var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
    // set the phrase
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability").trigger("input");
    });
    // get the address
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Mnemonic did not generate address");
            console.log("Expected: " + expected);
            console.log("Got: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Random button generates random mnemonic
function() {
page.open(url, function(status) {
    // check initial phrase is empty
    var phrase = page.evaluate(function() {
        return $(".phrase").text();
    });
    if (phrase != "") {
        console.log("Initial phrase is not blank");
        fail();
    }
    // press the 'generate' button
    page.evaluate(function() {
        $(".generate").click();
    });
    // get the new phrase
    setTimeout(function() {
        var phrase = page.evaluate(function() {
            return $(".phrase").val();
        });
        if (phrase.length <= 0) {
            console.log("Phrase not generated by pressing button");
            fail();
        }
        next();
    }, 1000);
});
},

// Mnemonic length can be customized
function() {
page.open(url, function(status) {
    // set the length to 6
    var expectedLength = "6";
    page.evaluate(function() {
        $(".strength option[selected]").removeAttr("selected");
        $(".strength option[value=6]").prop("selected", true);
    });
    // press the 'generate' button
    page.evaluate(function() {
        $(".generate").click();
    });
    // check the new phrase is six words long
    setTimeout(function() {
        var actualLength = page.evaluate(function() {
            var words = $(".phrase").val().split(" ");
            return words.length;
        });
        if (actualLength != expectedLength) {
            console.log("Phrase not generated with correct length");
            console.log("Expected: " + expectedLength);
            console.log("Actual: " + actualLength);
            fail();
        }
        next();
    }, 1000);
});
},

// Passphrase can be set
function() {
page.open(url, function(status) {
    // set the phrase and passphrase
    var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".passphrase").val("secure_passphrase").trigger("input");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Passphrase results in wrong address");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to bitcoin testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=1]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Bitcoin testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to litecoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=2]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Litecoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to dogecoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=3]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Dogecoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to shadowcash
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=4]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Shadowcash address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to shadowcash testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=5]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Shadowcash testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to viacoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=6]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Viacoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to viacoin testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=7]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Viacoin testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to jumbucks
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=8]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Jumbucks address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to clam
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=9]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("CLAM address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// TODO finish these tests
// BIP39 seed is set from phrase
// BIP32 root key is set from phrase

// Tabs show correct addresses when changed

// BIP44 derivation path is shown
// BIP44 extended private key is shown
// BIP44 extended public key is shown
// BIP44 purpose field changes address list
// BIP44 coin field changes address list
// BIP44 account field changes address list
// BIP44 external/internal field changes address list

// BIP32 derivation path can be set
// BIP32 can use hardened derivation paths
// BIP32 extended private key is shown
// BIP32 extended public key is shown

// Derivation path is shown in table
// Derivation path for address can be hardened
// Derivation path visibility can be toggled
// Address is shown
// Addresses are shown in order of derivation path
// Address visibility can be toggled
// Private key is shown
// Private key visibility can be toggled

// More addresses can be generated
// A custom number of additional addresses can be generated
// Additional addresses are shown in order of derivation path

// BIP32 root key can be set by the user
// Setting BIP32 root key clears the existing phrase, passphrase and seed
// Clearing of phrase, passphrase and seed can be cancelled by user
// Custom BIP32 root key is used when changing the derivation path

// Incorrect mnemonic shows error
// Incorrect word shows suggested replacement
// Incorrect BIP32 root key shows error
// Derivation path not starting with m shows error
// Derivation path containing invalid characters shows useful error

// Github Issue 11: Default word length is 15
// https://github.com/dcpos/bip39/issues/11

// Github Issue 12: Generate more rows with private keys hidden
// https://github.com/dcpos/bip39/issues/12

// Github Issue 19: Mnemonic is not sensitive to whitespace
// https://github.com/dcpos/bip39/issues/19

// Github Issue 23: Use correct derivation path when changing tabs
// https://github.com/dcpos/bip39/issues/23

];

console.log("Running tests...");
next();
<commit_msg>Test bip39 seed value as generated from mnemonic<commit_after>// Usage:
// $ phantomjs tests.js

var page = require('webpage').create();
var url = 'src/index.html';

page.onResourceError = function(e) {
    console.log("Error loading " + e.url);
    phantom.exit();
}

function fail() {
    console.log("Failed");
    phantom.exit();
}

function next() {
    if (tests.length > 0) {
        var testsStr = tests.length == 1 ? "test" : "tests";
        console.log(tests.length + " " + testsStr + " remaining");
        tests.shift()();
    }
    else {
        console.log("Finished with 0 failures");
        phantom.exit();
    }
}

tests = [

// Page loads with status of 'success'
function() {
page.open(url, function(status) {
    if (status != "success") {
        console.log("Page did not load with status 'success'");
        fail();
    }
    next();
});
},

// Page has text
function() {
page.open(url, function(status) {
    var content = page.evaluate(function() {
        return document.body.textContent.trim();
    });
    if (!content) {
        console.log("Page does not have text");
        fail();
    }
    next();
});
},

// Entering mnemonic generates addresses
function() {
page.open(url, function(status) {
    var expected = "1Di3Vp7tBWtyQaDABLAjfWtF6V7hYKJtug";
    // set the phrase
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability").trigger("input");
    });
    // get the address
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Mnemonic did not generate address");
            console.log("Expected: " + expected);
            console.log("Got: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Random button generates random mnemonic
function() {
page.open(url, function(status) {
    // check initial phrase is empty
    var phrase = page.evaluate(function() {
        return $(".phrase").text();
    });
    if (phrase != "") {
        console.log("Initial phrase is not blank");
        fail();
    }
    // press the 'generate' button
    page.evaluate(function() {
        $(".generate").click();
    });
    // get the new phrase
    setTimeout(function() {
        var phrase = page.evaluate(function() {
            return $(".phrase").val();
        });
        if (phrase.length <= 0) {
            console.log("Phrase not generated by pressing button");
            fail();
        }
        next();
    }, 1000);
});
},

// Mnemonic length can be customized
function() {
page.open(url, function(status) {
    // set the length to 6
    var expectedLength = "6";
    page.evaluate(function() {
        $(".strength option[selected]").removeAttr("selected");
        $(".strength option[value=6]").prop("selected", true);
    });
    // press the 'generate' button
    page.evaluate(function() {
        $(".generate").click();
    });
    // check the new phrase is six words long
    setTimeout(function() {
        var actualLength = page.evaluate(function() {
            var words = $(".phrase").val().split(" ");
            return words.length;
        });
        if (actualLength != expectedLength) {
            console.log("Phrase not generated with correct length");
            console.log("Expected: " + expectedLength);
            console.log("Actual: " + actualLength);
            fail();
        }
        next();
    }, 1000);
});
},

// Passphrase can be set
function() {
page.open(url, function(status) {
    // set the phrase and passphrase
    var expected = "15pJzUWPGzR7avffV9nY5by4PSgSKG9rba";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".passphrase").val("secure_passphrase").trigger("input");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Passphrase results in wrong address");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to bitcoin testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "mucaU5iiDaJDb69BHLeDv8JFfGiyg2nJKi";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=1]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Bitcoin testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to litecoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "LQ4XU8RX2ULPmPq9FcUHdVmPVchP9nwXdn";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=2]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Litecoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to dogecoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "DPQH2AtuzkVSG6ovjKk4jbUmZ6iXLpgbJA";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=3]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Dogecoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to shadowcash
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "SiSZtfYAXEFvMm3XM8hmtkGDyViRwErtCG";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=4]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Shadowcash address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to shadowcash testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=5]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Shadowcash testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to viacoin
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "Vq9Eq4N5SQnjqZvxtxzo7hZPW5XnyJsmXT";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=6]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Viacoin address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to viacoin testnet
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "tM2EDpVKaTiEg2NZg3yKg8eqjLr55BErHe";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=7]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Viacoin testnet address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to jumbucks
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "JLEXccwDXADK4RxBPkRez7mqsHVoJBEUew";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=8]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("Jumbucks address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// Network can be set to clam
function() {
page.open(url, function(status) {
    // set the phrase and coin
    var expected = "xCp4sakjVx4pUAZ6cBCtuin8Ddb6U1sk9y";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
        $(".network option[selected]").removeAttr("selected");
        $(".network option[value=9]").prop("selected", true);
        $(".network").trigger("change");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".address:first").text();
        });
        if (actual != expected) {
            console.log("CLAM address is incorrect");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// BIP39 seed is set from phrase
function() {
page.open(url, function(status) {
    // set the phrase
    var expected = "20da140d3dd1df8713cefcc4d54ce0e445b4151027a1ab567b832f6da5fcc5afc1c3a3f199ab78b8e0ab4652efd7f414ac2c9a3b81bceb879a70f377aa0a58f3";
    page.evaluate(function() {
        $(".phrase").val("abandon abandon ability");
        $(".phrase").trigger("input");
    });
    // check the address is generated correctly
    setTimeout(function() {
        var actual = page.evaluate(function() {
            return $(".seed").val();
        });
        if (actual != expected) {
            console.log("BIP39 seed is incorrectly generated from mnemonic");
            console.log("Expected: " + expected);
            console.log("Actual: " + actual);
            fail();
        }
        next();
    }, 1000);
});
},

// BIP32 root key is set from phrase

// Tabs show correct addresses when changed

// BIP44 derivation path is shown
// BIP44 extended private key is shown
// BIP44 extended public key is shown
// BIP44 purpose field changes address list
// BIP44 coin field changes address list
// BIP44 account field changes address list
// BIP44 external/internal field changes address list

// BIP32 derivation path can be set
// BIP32 can use hardened derivation paths
// BIP32 extended private key is shown
// BIP32 extended public key is shown

// Derivation path is shown in table
// Derivation path for address can be hardened
// Derivation path visibility can be toggled
// Address is shown
// Addresses are shown in order of derivation path
// Address visibility can be toggled
// Private key is shown
// Private key visibility can be toggled

// More addresses can be generated
// A custom number of additional addresses can be generated
// Additional addresses are shown in order of derivation path

// BIP32 root key can be set by the user
// Setting BIP32 root key clears the existing phrase, passphrase and seed
// Clearing of phrase, passphrase and seed can be cancelled by user
// Custom BIP32 root key is used when changing the derivation path

// Incorrect mnemonic shows error
// Incorrect word shows suggested replacement
// Incorrect BIP32 root key shows error
// Derivation path not starting with m shows error
// Derivation path containing invalid characters shows useful error

// Github Issue 11: Default word length is 15
// https://github.com/dcpos/bip39/issues/11

// Github Issue 12: Generate more rows with private keys hidden
// https://github.com/dcpos/bip39/issues/12

// Github Issue 19: Mnemonic is not sensitive to whitespace
// https://github.com/dcpos/bip39/issues/19

// Github Issue 23: Use correct derivation path when changing tabs
// https://github.com/dcpos/bip39/issues/23

];

console.log("Running tests...");
next();
<|endoftext|>


<commit_before>/* global describe, it */
/* jshint unused:false */

var demand = require('must'),
	di = require('./index'),
	_ = require('lodash');

var thrownErr = new Error("Should've been caught by asyncdi");

var fn_basic = function() { return true; };
var fn_async = function(callback) { setTimeout(callback, 500, null, true) };
var fn_one = function(one) { return true; };
var fn_scope = function(){ return this; };
var fn_error = function() { throw thrownErr };
var fn_reflect_args = function() { return _.toArray(arguments); };
var fn_overwritten_toString = function(){ return true};
fn_overwritten_toString.toString = function(){
	return "toString overwritten";
};
var scope = {};
var notAFunction = {};

describe('AsyncDI', function() {
	describe('new Wrapper', function() {
		it('must be an instance of Wrapper', function() {
			new di.Wrapper(fn_basic).must.be.an.instanceof(di.Wrapper);
		});
	});
	describe('()', function() {
		it('must be an instance of Wrapper', function() {
			di(fn_basic).must.be.an.instanceof(di.Wrapper);
		});
	});
	describe('(notAFunction)', function(){
		it('must throw an error', function(){
			demand(function(){
				di(notAFunction);
			}).to.throw(/function/i);
		});
	});
	describe('(fn_overwritten_toString)', function(){
		it("should not throw an error", function(){
			demand(function(){
				di(fn_overwritten_toString);
			}).to.not.throw();
		});
	});
	describe('fn_basic.isAsync', function() {
		it('must not be async', function() {
			demand(di(fn_basic).isAsync).not.be.true();
		});
	});
	describe('fn_async.isAsync', function() {
		it('must be async', function() {
			demand(di(fn_async).isAsync).be.true();
		});
	});
	describe('fn_basic.deps', function() {
		it('must be empty', function() {
			demand(di(fn_async).deps).eql([]);
		});
	});
	describe('fn_basic.call(callback)', function() {
		it('must callback(null, true)', function(done) {
			di(fn_basic).call(function(err, val) {
				demand(err).be.null();
				demand(val).be.true();
				done();
			});
		});
	});
	describe('fn_basic.call()', function() {
		it('must return true', function() {
			di(fn_basic).call().must.be.true();
		});
	});
	describe('fn_async.call(callback)', function() {
		it('must callback(null, true)', function(done) {
			di(fn_async).call(function(err, val) {
				demand(err).be.null();
				demand(val).be.true();
				done();
			});
		});
	});
	describe('fn_one.requires', function() {
		it('must require `one`', function() {
			demand(di(fn_one).requires.one).be.true();
		});
	});
	describe('fn_one.requires', function() {
		it('must not require `two`', function() {
			demand(di(fn_one).requires.two).be.undefined();
		});
	});
	describe('(fn_reflect_args).provides', function() {
		it('accept an array of values', function(done) {
			var a={},
				b={};
			di(fn_reflect_args).provides([a,b]).call(function(err, val) {
				demand(val).eql([a,b]);
				done();
			});
		});
	});
	describe('(fn_scope).call(scope, callback)', function(){
		it('must return scope', function(done){
			di(fn_scope).call(scope, function(err, val){
				demand(val).equal(scope);
				done();
			});
		});
	});
	describe('(fn_scope, scope).call(callback)', function(){
		it('must return scope', function(done){
			di(fn_scope, scope).call(function(err, val){
				demand(val).equal(scope);
				done();
			});
		});
	});
	describe('(fn_error).call(callback)', function(){
		it('must return thrownErr', function(done){
			di(fn_error).call(function(err, val){
				demand(err).equal(thrownErr);
				done();
			});
		});
	});
});
<commit_msg>Reduced timeout for async fn<commit_after>/* global describe, it */
/* jshint unused:false */

var demand = require('must'),
	di = require('./index'),
	_ = require('lodash');

var thrownErr = new Error("Should've been caught by asyncdi");

var fn_basic = function() { return true; };
var fn_async = function(callback) { setTimeout(callback, 0, null, true) };
var fn_one = function(one) { return true; };
var fn_scope = function(){ return this; };
var fn_error = function() { throw thrownErr };
var fn_reflect_args = function() { return _.toArray(arguments); };
var fn_overwritten_toString = function(){ return true};
fn_overwritten_toString.toString = function(){
	return "toString overwritten";
};
var scope = {};
var notAFunction = {};

describe('AsyncDI', function() {
	describe('new Wrapper', function() {
		it('must be an instance of Wrapper', function() {
			new di.Wrapper(fn_basic).must.be.an.instanceof(di.Wrapper);
		});
	});
	describe('()', function() {
		it('must be an instance of Wrapper', function() {
			di(fn_basic).must.be.an.instanceof(di.Wrapper);
		});
	});
	describe('(notAFunction)', function(){
		it('must throw an error', function(){
			demand(function(){
				di(notAFunction);
			}).to.throw(/function/i);
		});
	});
	describe('(fn_overwritten_toString)', function(){
		it("should not throw an error", function(){
			demand(function(){
				di(fn_overwritten_toString);
			}).to.not.throw();
		});
	});
	describe('fn_basic.isAsync', function() {
		it('must not be async', function() {
			demand(di(fn_basic).isAsync).not.be.true();
		});
	});
	describe('fn_async.isAsync', function() {
		it('must be async', function() {
			demand(di(fn_async).isAsync).be.true();
		});
	});
	describe('fn_basic.deps', function() {
		it('must be empty', function() {
			demand(di(fn_async).deps).eql([]);
		});
	});
	describe('fn_basic.call(callback)', function() {
		it('must callback(null, true)', function(done) {
			di(fn_basic).call(function(err, val) {
				demand(err).be.null();
				demand(val).be.true();
				done();
			});
		});
	});
	describe('fn_basic.call()', function() {
		it('must return true', function() {
			di(fn_basic).call().must.be.true();
		});
	});
	describe('fn_async.call(callback)', function() {
		it('must callback(null, true)', function(done) {
			di(fn_async).call(function(err, val) {
				demand(err).be.null();
				demand(val).be.true();
				done();
			});
		});
	});
	describe('fn_one.requires', function() {
		it('must require `one`', function() {
			demand(di(fn_one).requires.one).be.true();
		});
	});
	describe('fn_one.requires', function() {
		it('must not require `two`', function() {
			demand(di(fn_one).requires.two).be.undefined();
		});
	});
	describe('(fn_reflect_args).provides', function() {
		it('accept an array of values', function(done) {
			var a={},
				b={};
			di(fn_reflect_args).provides([a,b]).call(function(err, val) {
				demand(val).eql([a,b]);
				done();
			});
		});
	});
	describe('(fn_scope).call(scope, callback)', function(){
		it('must return scope', function(done){
			di(fn_scope).call(scope, function(err, val){
				demand(val).equal(scope);
				done();
			});
		});
	});
	describe('(fn_scope, scope).call(callback)', function(){
		it('must return scope', function(done){
			di(fn_scope, scope).call(function(err, val){
				demand(val).equal(scope);
				done();
			});
		});
	});
	describe('(fn_error).call(callback)', function(){
		it('must return thrownErr', function(done){
			di(fn_error).call(function(err, val){
				demand(err).equal(thrownErr);
				done();
			});
		});
	});
});
<|endoftext|>


<commit_before>var x11 = require('x11')
var X

var Layout = require('./layout')
var u      = require('./utils')
var ease   = require('vec2-easing')

require('./xorg')(function (err, client, display) {
  if(err) throw err

  var rw = client.root, _prevFocus

  var layouts = [new Layout(rw)]

  var l = layouts[0]

  client.client.require('randr', function(Randr) {
    Randr.SelectInput(rw.id, Randr.NotifyMask.ScreenChange);
  });

  rw.on('RRScreenChangeNotify', function(ev, win) {
    console.log(ev);
    if(win.bounds)
      win.bounds.size.set(ev.width, ev.height)
    else
      win.resize(ev.width, ev.height)
    l.layout();
  });

  function cycleLayout(dir) {
    l._delay = Date.now() + l.delay
    l.hide()
    l = u.relative(layouts, l, dir || 1)
    l.show()
  }

  //create a new window, but don't add it to the tree.

  var EV = x11.eventMask.Exposure | x11.eventMask.SubstructureRedirect
      | x11.eventMask.MapRequest | x11.eventMask.SubstructureNotify
      | x11.eventMask.EnterWindow

  rw.set({eventMask: EV}, function(err) {
    if (err && err.error == 10) {
        console.error('Error: another window manager already running.');
        process.exit(1);
    }
  })

  rw.children(function (err, children) {
    children.forEach(function (win) {
      win.kill()
      l.remove(win)
    })
    l.layout()
  })

  rw.on('MapRequest', function (ev, win) {
    //load the window's properties, and then lay it out.
    win.load(function () {
      //add to current layout
      var b = win.bounds
      win.bounds = ease(b, 300)
      win.bounds.size = ease(b.size, 300)
      win.configure({borderWidth: 1})
      win.on('focus', function () {
        if(_prevFocus)
          _prevFocus.set({ borderPixel: 0x0 })
        win.set({borderPixel: 0xffff00})
        _prevFocus = win
      })
      win.map()
      l.add(win)
      win.focus()
      win.raise()
      l.layout()
    })
    win.set({eventMask: x11.eventMask.EnterWindow})
  })

  rw.on('DestroyNotify', function (ev, win) {
    l.remove(win)
    //UGLY HACK AROUND STRANGE ERROR WHERE
    //KB SHORTCUTS STOP WORKING WHEN YOU CLOSE ALL THE WINDOWS
//    if(!l.tiles.length)
  //    spawn(process.env.TERM || 'xterm')
  })

  rw.on('ConfigureRequest', function (ev, win) {
    //prevent windows from sizing themselves?
    if(win.bounds)
      win.bounds.size.set(ev.width, ev.height)
    else
      win.resize(ev.width, ev.height)
  })

  var spawn = require('child_process').spawn

  //open terminal
  //Command-T/K
  rw.onKey(0x40, 45, function (ev) {
    if(ev.down)
      spawn(process.env.TERM || 'xterm')
  })

  //Command-C/I
  rw.onKey(0x40, 31, function (ev) {
    if(ev.down)
      spawn(process.env.BROWSER || 'chromium')
  })

  //Command-Esc
  rw.onKey(0x40, 9, function (ev) {
    if(ev.down) {
      console.log('quiting...')
      process.exit(0)
    }
  })

  //Command-Space
  rw.onKey(0x40, 65, function (ev) {
    if(ev.down) l.toggle()
  })

  //Command-Left
  rw.onKey(0x40, 113, function (ev) {
    if(ev.down) l.cycle(-1)
  })
  rw.onKey(0x40, 114, function (ev) {
    if(ev.down) l.cycle(1)
  })

  //Command-Left
  rw.onKey(0x41, 113, function (ev) {
    if(ev.down) l.move(-1)
  })

  rw.onKey(0x41, 114, function (ev) {
    if(ev.down) l.move(1)
  })

  rw.onKey(0x40, 111, function (ev) {
    if(ev.down) cycleLayout(1)
  })

  rw.onKey(0x40, 116 , function (ev) {
    if(ev.down) cycleLayout(-1)
  })

  //Ctrl-N
  rw.onKey(0x40, 46 , function (ev) {
    if(!ev.down) return
    console.log('new layout')
    l.hide()
    layouts.push(l = new Layout(rw))
    l.show()
  })

  function close (ev) {
    if(l.tiles.length === 0) {
      if(layouts.length === 1) {
        console.log('all windows are closed')
        process.exit(0)
      }
      return closeLayout(ev)
    }
    if(ev.down && l.focused) {
      var _focused = l.focused
      //if there are no tiles in this layout,
      //close the space.
      if(l.tiles.length > 1)
      l.cycle(-1)
      _focused.close()
    }
  }

  function closeLayout (ev) {
    if(!ev.down) return
    var _l = u.relative(layouts, l, -1)
    if(layouts.length <= 1) return
    l.hide()
    l.closeAll()
    if(layouts.length) u.remove(layouts, l)
    l = _l
    _l.show()
  }

  rw.onKey(0x40, 53, close) //command-Q
  rw.onKey(0x40, 59, close) //command-W
  rw.onKey(0x41, 59, closeLayout) //command-W
})

<commit_msg>resize root window on screen resize<commit_after>var x11 = require('x11')
var X

var Layout = require('./layout')
var u      = require('./utils')
var ease   = require('vec2-easing')

require('./xorg')(function (err, client, display) {
  if(err) throw err

  var rw = client.root, _prevFocus

  var layouts = [new Layout(rw)]

  var l = layouts[0]

  client.client.require('randr', function(Randr) {
    Randr.SelectInput(rw.id, Randr.NotifyMask.ScreenChange);
  });

  rw.on('RRScreenChangeNotify', function(ev) {
    console.log(rw);
    rw.bounds.size.set(ev.width, ev.height)
    l.layout();
  });

  function cycleLayout(dir) {
    l._delay = Date.now() + l.delay
    l.hide()
    l = u.relative(layouts, l, dir || 1)
    l.show()
  }

  //create a new window, but don't add it to the tree.

  var EV = x11.eventMask.Exposure | x11.eventMask.SubstructureRedirect
      | x11.eventMask.MapRequest | x11.eventMask.SubstructureNotify
      | x11.eventMask.EnterWindow

  rw.set({eventMask: EV}, function(err) {
    if (err && err.error == 10) {
        console.error('Error: another window manager already running.');
        process.exit(1);
    }
  })

  rw.children(function (err, children) {
    children.forEach(function (win) {
      win.kill()
      l.remove(win)
    })
    l.layout()
  })

  rw.on('MapRequest', function (ev, win) {
    //load the window's properties, and then lay it out.
    win.load(function () {
      //add to current layout
      var b = win.bounds
      win.bounds = ease(b, 300)
      win.bounds.size = ease(b.size, 300)
      win.configure({borderWidth: 1})
      win.on('focus', function () {
        if(_prevFocus)
          _prevFocus.set({ borderPixel: 0x0 })
        win.set({borderPixel: 0xffff00})
        _prevFocus = win
      })
      win.map()
      l.add(win)
      win.focus()
      win.raise()
      l.layout()
    })
    win.set({eventMask: x11.eventMask.EnterWindow})
  })

  rw.on('DestroyNotify', function (ev, win) {
    l.remove(win)
    //UGLY HACK AROUND STRANGE ERROR WHERE
    //KB SHORTCUTS STOP WORKING WHEN YOU CLOSE ALL THE WINDOWS
//    if(!l.tiles.length)
  //    spawn(process.env.TERM || 'xterm')
  })

  rw.on('ConfigureRequest', function (ev, win) {
    //prevent windows from sizing themselves?
    if(win.bounds)
      win.bounds.size.set(ev.width, ev.height)
    else
      win.resize(ev.width, ev.height)
  })

  var spawn = require('child_process').spawn

  //open terminal
  //Command-T/K
  rw.onKey(0x40, 45, function (ev) {
    if(ev.down)
      spawn(process.env.TERM || 'xterm')
  })

  //Command-C/I
  rw.onKey(0x40, 31, function (ev) {
    if(ev.down)
      spawn(process.env.BROWSER || 'chromium')
  })

  //Command-Esc
  rw.onKey(0x40, 9, function (ev) {
    if(ev.down) {
      console.log('quiting...')
      process.exit(0)
    }
  })

  //Command-Space
  rw.onKey(0x40, 65, function (ev) {
    if(ev.down) l.toggle()
  })

  //Command-Left
  rw.onKey(0x40, 113, function (ev) {
    if(ev.down) l.cycle(-1)
  })
  rw.onKey(0x40, 114, function (ev) {
    if(ev.down) l.cycle(1)
  })

  //Command-Left
  rw.onKey(0x41, 113, function (ev) {
    if(ev.down) l.move(-1)
  })

  rw.onKey(0x41, 114, function (ev) {
    if(ev.down) l.move(1)
  })

  rw.onKey(0x40, 111, function (ev) {
    if(ev.down) cycleLayout(1)
  })

  rw.onKey(0x40, 116 , function (ev) {
    if(ev.down) cycleLayout(-1)
  })

  //Ctrl-N
  rw.onKey(0x40, 46 , function (ev) {
    if(!ev.down) return
    console.log('new layout')
    l.hide()
    layouts.push(l = new Layout(rw))
    l.show()
  })

  function close (ev) {
    if(l.tiles.length === 0) {
      if(layouts.length === 1) {
        console.log('all windows are closed')
        process.exit(0)
      }
      return closeLayout(ev)
    }
    if(ev.down && l.focused) {
      var _focused = l.focused
      //if there are no tiles in this layout,
      //close the space.
      if(l.tiles.length > 1)
      l.cycle(-1)
      _focused.close()
    }
  }

  function closeLayout (ev) {
    if(!ev.down) return
    var _l = u.relative(layouts, l, -1)
    if(layouts.length <= 1) return
    l.hide()
    l.closeAll()
    if(layouts.length) u.remove(layouts, l)
    l = _l
    _l.show()
  }

  rw.onKey(0x40, 53, close) //command-Q
  rw.onKey(0x40, 59, close) //command-W
  rw.onKey(0x41, 59, closeLayout) //command-W
})

<|endoftext|>


<commit_before>'use strict';

var FPS = 59.8261;
var TICK_MS = 10;
var MINIMUM_TIME_MS = 14000;
var ONE_MINUTE_MS = 60000;

var gen5CalibrationInput = document.getElementById('gen-5-calibration-input');
var gen5TargetSecondsInput = document.getElementById('gen-5-target-seconds-input');
var gen5SecondHitInput = document.getElementById('gen-5-second-hit-input');
var gen5StartButton = document.getElementById('gen-5-start-button');
var gen5TimeRemaining = document.getElementById('gen-5-time-remaining');
var gen5MinutesBefore = document.getElementById('gen-5-minutes-before');

var gen4CalibratedDelayInput = document.getElementById('gen-4-calibrated-delay-input');
var gen4CalibratedSecondsInput = document.getElementById('gen-4-calibrated-seconds-input');
var gen4TargetDelayInput = document.getElementById('gen-4-target-delay-input');
var gen4TargetSecondsInput = document.getElementById('gen-4-target-seconds-input');
var gen4DelayHitInput = document.getElementById('gen-4-delay-hit-input');
var gen4StartButton = document.getElementById('gen-4-start-button');
var gen4TimeRemaining1 = document.getElementById('gen-4-time-remaining-1');
var gen4TimeRemaining2 = document.getElementById('gen-4-time-remaining-2');
var gen4MinutesBefore = document.getElementById('gen-4-minutes-before');

var countdownCheckbox = document.getElementById('countdown-checkbox');
var soundTypeDropdown = document.getElementById('sound-type-dropdown');
var numSoundsInput = document.getElementById('num-sounds-input');
var soundsIntervalInput = document.getElementById('sounds-interval-input');

var audios = {
  tick: new Audio('tick.wav'),
  beep: new Audio('beep.wav'),
  pop: new Audio('pop.wav'),
  ding: new Audio('ding.wav')
};

function zeroPadNum (num, length) {
  var str = num + '';
  return str.length >= length ? str : ('0'.repeat(length) + str).slice(-length);
}

function getFormattedTime (ms) {
  return zeroPadNum(Math.floor(ms / 1000), 2) + ':' + zeroPadNum(Math.floor(ms % 1000 / TICK_MS), 2);
}

var noop = function () {};

class Timer {
  constructor (opts) {
    this.onStart = opts.onStart || noop;
    this.onStop = opts.onStop || noop;
    this.onChange = opts.onChange || noop;
    this._totalTime = opts.totalTime || null;
    this._intervalTimer = null;
    this._timeRemaining = this._totalTime;
  }
  isActive () {
    return this._intervalTimer !== null;
  }
  toggle () {
    if (this.isActive()) {
      this.stop();
    } else {
      this.start();
    }
  }
  start () {
    if (!this.isActive()) {
      this._stopPoint = Date.now() + this._totalTime;
      this._audioTimers = [];
      if (countdownCheckbox.checked) {
        for (var i = 0; i < numSoundsInput.value; i++) {
          this._audioTimers.push(setTimeout(function () {
            audios[soundTypeDropdown.value].play();
          }, this._totalTime - i * soundsIntervalInput.value));
        }
      }
      this._intervalTimer = setInterval(this._tick.bind(this), TICK_MS);
      this._stopTimer = setTimeout(this.stop.bind(this), this._totalTime);
      this.onStart();
      this.onChange();
    }
  }
  stop () {
    if (this.isActive()) {
      this._audioTimers.forEach(clearTimeout);
      this._audioTimers = [];
      clearInterval(this._intervalTimer);
      clearTimeout(this._stopTimer);
      this._intervalTimer = null;
      this._timeRemaining = this._totalTime;
      this.onStop();
      this.onChange();
    }
  }
  getTimeRemaining () {
    return this._timeRemaining;
  }
  getTotalTime () {
    return this._totalTime;
  }
  setTotalTime (ms) {
    this._totalTime = ms;
    if (!this.isActive()) {
      this._timeRemaining = ms;
    }
    this.onChange();
  }
  _tick () {
    if (this._intervalTimer) {
      this._timeRemaining = this._stopPoint - Date.now();
      this.onChange();
    }
  }
}

var gen5Timer = new Timer({
  onStart () {
    gen5StartButton.innerHTML = 'Stop';
  },
  onStop () {
    gen5StartButton.innerHTML = 'Start';
    gen5MinutesBefore.innerHTML = Math.floor(this.getTotalTime() / ONE_MINUTE_MS);
  },
  onChange () {
    gen5TimeRemaining.innerHTML = getFormattedTime(this.getTimeRemaining());
  },
  totalTime: calculateGen5TotalTime()
});

gen5Timer.onChange();

var initialGen4Times = calculateGen4Times();
var gen4MinutesBeforeVal = Math.floor((initialGen4Times[0] + initialGen4Times[1]) / ONE_MINUTE_MS);
gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;

var gen4Timer1 = new Timer({
  onStart () {
    gen4StartButton.innerHTML = 'Stop';
  },
  onStop () {
    gen4Timer2.start();
  },
  onChange () {
    if (!gen4Timer2.isActive()) {
      gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTimeRemaining());
      if (!this.isActive()) {
        gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
      }
    }
  },
  totalTime: initialGen4Times[0]
});

var gen4Timer2 = new Timer({
  onStart () {
    gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTotalTime());
    gen4TimeRemaining2.innerHTML = getFormattedTime(0);
  },
  onStop () {
    gen4StartButton.innerHTML = 'Start';
    gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
    gen4TimeRemaining1.innerHTML = getFormattedTime(gen4Timer1.getTotalTime());
    gen4TimeRemaining2.innerHTML = getFormattedTime(this.getTotalTime());
  },
  onChange () {
    if (this.isActive()) {
      gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTimeRemaining());
    }
  },
  totalTime: initialGen4Times[1]
});

gen4Timer1.onChange();
gen4TimeRemaining2.innerHTML = getFormattedTime(gen4Timer2.getTotalTime());

// eslint-disable-next-line
function toggleGen4Timers () {
  if (gen4Timer1.isActive() || gen4Timer2.isActive()) {
    gen4Timer1.stop();
    gen4Timer2.stop();
  } else {
    gen4Timer1.start();
  }
}

function toDelay (delay) {
  return Math.round(delay * FPS);
}

function toMs (ms) {
  return Math.round(ms / FPS);
}

function getGen5Time (calibration, targetSec) {
  return 1000 * (targetSec + toMs(calibration)) + 200;
}

function getGen5CalibrationOffset (targetSec, result) {
  return toDelay(targetSec === result ? 0 : targetSec - result + (result > targetSec ? 0.5 : -0.5));
}

function calculateGen5TotalTime () {
  var time = getGen5Time(+gen5CalibrationInput.value, +gen5TargetSecondsInput.value);
  if (time < MINIMUM_TIME_MS) {
    time += ONE_MINUTE_MS;
  }
  return time;
}

function updateGen5TotalTime () {
  gen5Timer.setTotalTime(calculateGen5TotalTime());
  if (!gen5Timer.isActive()) {
    gen5MinutesBefore.innerHTML = Math.floor(gen5Timer.getTotalTime() / ONE_MINUTE_MS);
  }
}

// eslint-disable-next-line
function updateGen5Calibration () {
  var targetSec = +gen5TargetSecondsInput.value;
  var secondHit = +gen5SecondHitInput.value;
  if (Number.isFinite(targetSec) && Number.isFinite(secondHit)) {
    gen5CalibrationInput.value = +gen5CalibrationInput.value + getGen5CalibrationOffset(targetSec, secondHit);
    updateGen5TotalTime();
  }
}

function calculateGen4Times () {
  var targetSec = +gen4TargetSecondsInput.value;
  var targetDelay = +gen4TargetDelayInput.value;
  var calibratedDelay = +gen4CalibratedDelayInput.value;
  var calibratedSeconds = +gen4CalibratedSecondsInput.value;
  var secondTimeMs = toMs(targetDelay * 1000 - calibratedDelay * 1000) + calibratedSeconds * 1000;
  var firstTimeMs = ((targetSec * 1000 - secondTimeMs) % ONE_MINUTE_MS + ONE_MINUTE_MS) % ONE_MINUTE_MS + 200;
  if (firstTimeMs < MINIMUM_TIME_MS) {
    firstTimeMs += ONE_MINUTE_MS;
  }
  return [firstTimeMs, secondTimeMs];
}

// eslint-disable-next-line
function updateGen4Times () {
  var totalTimes = calculateGen4Times();
  gen4Timer1.setTotalTime(totalTimes[0]);
  gen4Timer2.setTotalTime(totalTimes[1]);
  gen4MinutesBeforeVal = Math.floor((totalTimes[0] + totalTimes[1]) / ONE_MINUTE_MS);
  if (!gen4Timer1.isActive() && !gen4Timer2.isActive()) {
    gen4TimeRemaining2.innerHTML = getFormattedTime(gen4Timer2.getTotalTime());
    gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
  }
}

// eslint-disable-next-line
function updateGen4Calibration () {
  var targetDelay = +gen4TargetDelayInput.value;
  var delayHit = +gen4DelayHitInput.value;
  if (Number.isFinite(targetDelay) && Number.isFinite(delayHit)) {
    gen4CalibratedDelayInput.value = +gen4CalibratedDelayInput.value + Math.round((delayHit - targetDelay) / 2)
  }
  updateGen4Times();
}

// eslint-disable-next-line
function updateCountdownOptions () {
  var isDisabled = !countdownCheckbox.checked;
  soundTypeDropdown.disabled = isDisabled;
  numSoundsInput.disabled = isDisabled;
  soundsIntervalInput.disabled = isDisabled;
}
<commit_msg>Fix a rounding issue with gen 5<commit_after>'use strict';

var FPS = 59.8261;
var TICK_MS = 10;
var MINIMUM_TIME_MS = 14000;
var ONE_MINUTE_MS = 60000;

var gen5CalibrationInput = document.getElementById('gen-5-calibration-input');
var gen5TargetSecondsInput = document.getElementById('gen-5-target-seconds-input');
var gen5SecondHitInput = document.getElementById('gen-5-second-hit-input');
var gen5StartButton = document.getElementById('gen-5-start-button');
var gen5TimeRemaining = document.getElementById('gen-5-time-remaining');
var gen5MinutesBefore = document.getElementById('gen-5-minutes-before');

var gen4CalibratedDelayInput = document.getElementById('gen-4-calibrated-delay-input');
var gen4CalibratedSecondsInput = document.getElementById('gen-4-calibrated-seconds-input');
var gen4TargetDelayInput = document.getElementById('gen-4-target-delay-input');
var gen4TargetSecondsInput = document.getElementById('gen-4-target-seconds-input');
var gen4DelayHitInput = document.getElementById('gen-4-delay-hit-input');
var gen4StartButton = document.getElementById('gen-4-start-button');
var gen4TimeRemaining1 = document.getElementById('gen-4-time-remaining-1');
var gen4TimeRemaining2 = document.getElementById('gen-4-time-remaining-2');
var gen4MinutesBefore = document.getElementById('gen-4-minutes-before');

var countdownCheckbox = document.getElementById('countdown-checkbox');
var soundTypeDropdown = document.getElementById('sound-type-dropdown');
var numSoundsInput = document.getElementById('num-sounds-input');
var soundsIntervalInput = document.getElementById('sounds-interval-input');

var audios = {
  tick: new Audio('tick.wav'),
  beep: new Audio('beep.wav'),
  pop: new Audio('pop.wav'),
  ding: new Audio('ding.wav')
};

function zeroPadNum (num, length) {
  var str = num + '';
  return str.length >= length ? str : ('0'.repeat(length) + str).slice(-length);
}

function getFormattedTime (ms) {
  return zeroPadNum(Math.floor(ms / 1000), 2) + ':' + zeroPadNum(Math.floor(ms % 1000 / TICK_MS), 2);
}

var noop = function () {};

class Timer {
  constructor (opts) {
    this.onStart = opts.onStart || noop;
    this.onStop = opts.onStop || noop;
    this.onChange = opts.onChange || noop;
    this._totalTime = opts.totalTime || null;
    this._intervalTimer = null;
    this._timeRemaining = this._totalTime;
  }
  isActive () {
    return this._intervalTimer !== null;
  }
  toggle () {
    if (this.isActive()) {
      this.stop();
    } else {
      this.start();
    }
  }
  start () {
    if (!this.isActive()) {
      this._stopPoint = Date.now() + this._totalTime;
      this._audioTimers = [];
      if (countdownCheckbox.checked) {
        for (var i = 0; i < numSoundsInput.value; i++) {
          this._audioTimers.push(setTimeout(function () {
            audios[soundTypeDropdown.value].play();
          }, this._totalTime - i * soundsIntervalInput.value));
        }
      }
      this._intervalTimer = setInterval(this._tick.bind(this), TICK_MS);
      this._stopTimer = setTimeout(this.stop.bind(this), this._totalTime);
      this.onStart();
      this.onChange();
    }
  }
  stop () {
    if (this.isActive()) {
      this._audioTimers.forEach(clearTimeout);
      this._audioTimers = [];
      clearInterval(this._intervalTimer);
      clearTimeout(this._stopTimer);
      this._intervalTimer = null;
      this._timeRemaining = this._totalTime;
      this.onStop();
      this.onChange();
    }
  }
  getTimeRemaining () {
    return this._timeRemaining;
  }
  getTotalTime () {
    return this._totalTime;
  }
  setTotalTime (ms) {
    this._totalTime = ms;
    if (!this.isActive()) {
      this._timeRemaining = ms;
    }
    this.onChange();
  }
  _tick () {
    if (this._intervalTimer) {
      this._timeRemaining = this._stopPoint - Date.now();
      this.onChange();
    }
  }
}

var gen5Timer = new Timer({
  onStart () {
    gen5StartButton.innerHTML = 'Stop';
  },
  onStop () {
    gen5StartButton.innerHTML = 'Start';
    gen5MinutesBefore.innerHTML = Math.floor(this.getTotalTime() / ONE_MINUTE_MS);
  },
  onChange () {
    gen5TimeRemaining.innerHTML = getFormattedTime(this.getTimeRemaining());
  },
  totalTime: calculateGen5TotalTime()
});

gen5Timer.onChange();

var initialGen4Times = calculateGen4Times();
var gen4MinutesBeforeVal = Math.floor((initialGen4Times[0] + initialGen4Times[1]) / ONE_MINUTE_MS);
gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;

var gen4Timer1 = new Timer({
  onStart () {
    gen4StartButton.innerHTML = 'Stop';
  },
  onStop () {
    gen4Timer2.start();
  },
  onChange () {
    if (!gen4Timer2.isActive()) {
      gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTimeRemaining());
      if (!this.isActive()) {
        gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
      }
    }
  },
  totalTime: initialGen4Times[0]
});

var gen4Timer2 = new Timer({
  onStart () {
    gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTotalTime());
    gen4TimeRemaining2.innerHTML = getFormattedTime(0);
  },
  onStop () {
    gen4StartButton.innerHTML = 'Start';
    gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
    gen4TimeRemaining1.innerHTML = getFormattedTime(gen4Timer1.getTotalTime());
    gen4TimeRemaining2.innerHTML = getFormattedTime(this.getTotalTime());
  },
  onChange () {
    if (this.isActive()) {
      gen4TimeRemaining1.innerHTML = getFormattedTime(this.getTimeRemaining());
    }
  },
  totalTime: initialGen4Times[1]
});

gen4Timer1.onChange();
gen4TimeRemaining2.innerHTML = getFormattedTime(gen4Timer2.getTotalTime());

// eslint-disable-next-line
function toggleGen4Timers () {
  if (gen4Timer1.isActive() || gen4Timer2.isActive()) {
    gen4Timer1.stop();
    gen4Timer2.stop();
  } else {
    gen4Timer1.start();
  }
}

function toDelay (delay) {
  return Math.round(delay * FPS);
}

function toMs (ms) {
  return Math.round(ms / FPS);
}

function getGen5Time (calibration, targetSec) {
  return 1000 * targetSec + toMs(1000 * calibration) + 200;
}

function getGen5CalibrationOffset (targetSec, result) {
  return toDelay(targetSec === result ? 0 : targetSec - result + (result > targetSec ? 0.5 : -0.5));
}

function calculateGen5TotalTime () {
  var time = getGen5Time(+gen5CalibrationInput.value, +gen5TargetSecondsInput.value);
  if (time < MINIMUM_TIME_MS) {
    time += ONE_MINUTE_MS;
  }
  return time;
}

function updateGen5TotalTime () {
  gen5Timer.setTotalTime(calculateGen5TotalTime());
  if (!gen5Timer.isActive()) {
    gen5MinutesBefore.innerHTML = Math.floor(gen5Timer.getTotalTime() / ONE_MINUTE_MS);
  }
}

// eslint-disable-next-line
function updateGen5Calibration () {
  var targetSec = +gen5TargetSecondsInput.value;
  var secondHit = +gen5SecondHitInput.value;
  if (Number.isFinite(targetSec) && Number.isFinite(secondHit)) {
    gen5CalibrationInput.value = +gen5CalibrationInput.value + getGen5CalibrationOffset(targetSec, secondHit);
    updateGen5TotalTime();
  }
}

function calculateGen4Times () {
  var targetSec = +gen4TargetSecondsInput.value;
  var targetDelay = +gen4TargetDelayInput.value;
  var calibratedDelay = +gen4CalibratedDelayInput.value;
  var calibratedSeconds = +gen4CalibratedSecondsInput.value;
  var secondTimeMs = toMs(targetDelay * 1000 - calibratedDelay * 1000) + calibratedSeconds * 1000;
  var firstTimeMs = ((targetSec * 1000 - secondTimeMs) % ONE_MINUTE_MS + ONE_MINUTE_MS) % ONE_MINUTE_MS + 200;
  if (firstTimeMs < MINIMUM_TIME_MS) {
    firstTimeMs += ONE_MINUTE_MS;
  }
  return [firstTimeMs, secondTimeMs];
}

// eslint-disable-next-line
function updateGen4Times () {
  var totalTimes = calculateGen4Times();
  gen4Timer1.setTotalTime(totalTimes[0]);
  gen4Timer2.setTotalTime(totalTimes[1]);
  gen4MinutesBeforeVal = Math.floor((totalTimes[0] + totalTimes[1]) / ONE_MINUTE_MS);
  if (!gen4Timer1.isActive() && !gen4Timer2.isActive()) {
    gen4TimeRemaining2.innerHTML = getFormattedTime(gen4Timer2.getTotalTime());
    gen4MinutesBefore.innerHTML = gen4MinutesBeforeVal;
  }
}

// eslint-disable-next-line
function updateGen4Calibration () {
  var targetDelay = +gen4TargetDelayInput.value;
  var delayHit = +gen4DelayHitInput.value;
  if (Number.isFinite(targetDelay) && Number.isFinite(delayHit)) {
    gen4CalibratedDelayInput.value = +gen4CalibratedDelayInput.value + Math.round((delayHit - targetDelay) / 2)
  }
  updateGen4Times();
}

// eslint-disable-next-line
function updateCountdownOptions () {
  var isDisabled = !countdownCheckbox.checked;
  soundTypeDropdown.disabled = isDisabled;
  numSoundsInput.disabled = isDisabled;
  soundsIntervalInput.disabled = isDisabled;
}
<|endoftext|>


<commit_before>/*! timer.js v1.1.1
 * https://github.com/adamcik/media-progress-timer
 * Copyright (c) 2015 Thomas Adamcik
 * Licensed under the Apache License, Version 2.0 */

(function() {

"use strict";

var now = typeof window.performance !== 'undefined' &&
          typeof window.performance.now !== 'undefined' &&
          window.performance.now || Date.now ||
          function() { return new Date().getTime(); };

function ProgressTimer(options) {
    if (!(this instanceof ProgressTimer)) {
        return new ProgressTimer(options);
    }
    if (typeof options === 'function') {
        options = {'callback': options};
    } else if (typeof options !== 'object') {
        throw "ProgressTimer must be called with a callback or options.";
    }
    if (typeof options.callback !== 'function') {
        throw "ProgressTimer needs a callback to operate.";
    }

    this._state = this._initialize(null, null);
    this._running = false;
    this._updateRate = Math.max(options.updateRate || 100, 10);
    this._callback = options.callback;
    this._fallback = typeof window.requestAnimationFrame === 'undefined' ||
                     options.disableRequestAnimationFrame|| false;

    if (!this._fallback) {
        this._initialUpdate = this._scheduleAnimationFrame;
        this._scheduleUpdate = this._scheduleAnimationFrame;
    }
}

ProgressTimer.prototype.start = function(position, duration) {
    this._state = this._initialize(position, duration);
    this._callback(this._initialPosition, this._duration);
    this._running = true;
    this._initialUpdate();
};

ProgressTimer.prototype.resume = function() {
    if (!this._running) {
        var state = this._state;
        this.start(state.previousPosition, state.duration);
    }
};

ProgressTimer.prototype.stop = function() {
    this._running = false;
};

ProgressTimer.prototype.reset = function() {
    this.start(0, 0);
};

ProgressTimer.prototype._initialize = function(position, duration) {
    return {
        initialTimestamp: null,
        previousTimestamp: null,
        initialPosition: Math.max(position || 0, 0),
        previousPosition: null,
        duration: typeof duration === 'number' ? Math.max(duration, 0) : null
    };
};

ProgressTimer.prototype._initialUpdate = function() {
    this._update(now());
};

ProgressTimer.prototype._scheduleUpdate = function(state) {
    var adjustedTimeout = state.previousTimestamp + this._updateRate - now();
    setTimeout((function() {
        this._update(now());
    }).bind(this), adjustedTimeout);
};

ProgressTimer.prototype._scheduleAnimationFrame = function(state) {
    window.requestAnimationFrame(this._update.bind(this));
};

ProgressTimer.prototype._update = function(timestamp) {
    if (!this._running) {
        return;
    }

    var state = this._state;
    state.initialTimestamp = state.initialTimestamp || timestamp;
    state.previousTimestamp = timestamp;

    var position = Math.floor(
        state.initialPosition + (timestamp - state.initialTimestamp));

    if (state.duration === null || position < state.duration) {
        var delta = position - state.previousPosition;
        if (this._fallback || delta >= this._updateRate) {
            this._callback(position, state.duration);
            state.previousPosition = position;
        }
        this._scheduleUpdate(state);
    } else {
        this._running = false;
        this._callback(state.duration, state.duration);
    }
};

if(typeof module !== "undefined"){
    module.exports = ProgressTimer;
}
else{
    window.ProgressTimer = ProgressTimer;
}

}());
<commit_msg>Set previousPosition=initialPosition to begin with and ensure position <= duration<commit_after>/*! timer.js v1.1.1
 * https://github.com/adamcik/media-progress-timer
 * Copyright (c) 2015 Thomas Adamcik
 * Licensed under the Apache License, Version 2.0 */

(function() {

"use strict";

var now = typeof window.performance !== 'undefined' &&
          typeof window.performance.now !== 'undefined' &&
          window.performance.now || Date.now ||
          function() { return new Date().getTime(); };

function ProgressTimer(options) {
    if (!(this instanceof ProgressTimer)) {
        return new ProgressTimer(options);
    }
    if (typeof options === 'function') {
        options = {'callback': options};
    } else if (typeof options !== 'object') {
        throw "ProgressTimer must be called with a callback or options.";
    }
    if (typeof options.callback !== 'function') {
        throw "ProgressTimer needs a callback to operate.";
    }

    this._state = this._initialize(null, null);
    this._running = false;
    this._updateRate = Math.max(options.updateRate || 100, 10);
    this._callback = options.callback;
    this._fallback = typeof window.requestAnimationFrame === 'undefined' ||
                     options.disableRequestAnimationFrame|| false;

    if (!this._fallback) {
        this._initialUpdate = this._scheduleAnimationFrame;
        this._scheduleUpdate = this._scheduleAnimationFrame;
    }
}

ProgressTimer.prototype.start = function(position, duration) {
    this._state = this._initialize(position, duration);
    this._callback(this._initialPosition, this._duration);
    this._running = true;
    this._initialUpdate();
};

ProgressTimer.prototype.resume = function() {
    if (!this._running) {
        var state = this._state;
        this.start(state.previousPosition, state.duration);
    }
};

ProgressTimer.prototype.stop = function() {
    this._running = false;
};

ProgressTimer.prototype.reset = function() {
    this.start(0, 0);
};

ProgressTimer.prototype._initialize = function(position, duration) {
    position = Math.max(position || 0, 0);
    if (typeof duration === 'number') {
        duration = Math.max(duration, 0);
        position = Math.min(position, duration);
    } else {
        duration = null;
    }
    return {
        initialTimestamp: null,
        previousTimestamp: null,
        initialPosition: position,
        previousPosition: position,
        duration: duration
    };
};

ProgressTimer.prototype._initialUpdate = function() {
    this._update(now());
};

ProgressTimer.prototype._scheduleUpdate = function(state) {
    var adjustedTimeout = state.previousTimestamp + this._updateRate - now();
    setTimeout((function() {
        this._update(now());
    }).bind(this), adjustedTimeout);
};

ProgressTimer.prototype._scheduleAnimationFrame = function(state) {
    window.requestAnimationFrame(this._update.bind(this));
};

ProgressTimer.prototype._update = function(timestamp) {
    if (!this._running) {
        return;
    }

    var state = this._state;
    state.initialTimestamp = state.initialTimestamp || timestamp;
    state.previousTimestamp = timestamp;

    var position = Math.floor(
        state.initialPosition + (timestamp - state.initialTimestamp));

    if (state.duration === null || position < state.duration) {
        var delta = position - state.previousPosition;
        if (this._fallback || delta >= this._updateRate) {
            this._callback(position, state.duration);
            state.previousPosition = position;
        }
        this._scheduleUpdate(state);
    } else {
        this._running = false;
        this._callback(state.duration, state.duration);
    }
};

if(typeof module !== "undefined"){
    module.exports = ProgressTimer;
}
else{
    window.ProgressTimer = ProgressTimer;
}

}());
<|endoftext|>