Lots and lots of refactoring into multiple files. Also changed wordlist to be plaintext, updated robots.txt (heh), and did general cleanup. This is to prepare for potentially having multiple wordlists.

This commit is contained in:
Douglas Muth 2023-05-07 15:18:02 -04:00
parent 9ac50e200d
commit 5bbadd825c
11 changed files with 8693 additions and 733 deletions

View File

@ -0,0 +1,2 @@
Originally obtained from: https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases

File diff suppressed because it is too large Load Diff

View File

@ -118,6 +118,19 @@
</div> <!--/ row -->
<div class="row">
<noscript>
<div class="col-md-4" ></div>
<div class="col-md-4" >
<div class="alert alert-danger" role="alert">
<b>You must enable Javascript to use this app.</b>
</div>
</div>
</noscript>
</div><!-- /row -->
<div class="row">
<div class="col-md-12">
@ -140,11 +153,11 @@
<br/>
<a name="roll_dice_button" ></a>
<button type="button" class="btn btn-default btn-lg btn-primary" id="roll_dice" data-test="button">
<span class="glyphicon glyphicon-play" aria-hidden="true" ></span> Roll Dice!
<button type="button" class="btn btn-default btn-lg btn-primary" id="roll_dice" data-test="button" disabled=true>
<span id="roll_dice_icon" class="glyphicon glyphicon-play" aria-hidden="true" ></span> <span id="roll_dice_text">Loading...</span>
</button>
</div>
</div><!-- /col-md-12 -->
</div><!-- /row -->
<p/>

View File

@ -1,62 +1,49 @@
#
# robots.txt
#
# This file is to prevent the crawling and indexing of certain parts
# of your site by web crawlers and spiders run by sites like Yahoo!
# and Google. By telling these "robots" where not to go on your site,
# you save bandwidth and server resources.
#
# This file will be ignored unless it is at the root of your host:
# Used: http://example.com/robots.txt
# Ignored: http://example.com/site/robots.txt
#
# For more information about the robots.txt standard, see:
# http://www.robotstxt.org/wc/robots.html
#
# For syntax checking, see:
# http://www.sxw.org.uk/computing/robots/check.html
#
# Sorry Skynet, you can't have any humans today!
#
User-agent: Skynet
Disallow: /humanity/
#
# Nice try, T-800, but we're not letting you travel back in time!
#
User-agent: T-800
Disallow: /past/
#
# Sorry T-1000, we don't want any liquid metal on my Dice!
#
User-agent: T-1000
Disallow: /liquid/
#
# SarahConnor, you may be a target for Terminators, but you can't roll my dice!
#
User-agent: SarahConnor
Disallow: /target/
#
# JohnConnor, we know you're the leader of the Resistance, but we're not giving you access to our future plans!
#
User-agent: JohnConnor
Disallow: /future/
#
# Hey ArnoldSchwarzenegger, we know you played the Terminator, but we're not allowing actors here.
#
User-agent: ArnoldSchwarzenegger
Disallow: /actors/
#
# You can only terminate your search once every 10 seconds.
#
User-agent: *
Crawl-delay: 10
#
# From http://www.gotfuturama.com/Multimedia/AsciiArt/dispatch.cgi?target=Bender1
#
# QR
# #t(@
# @R
# @S
# RSQ
# @K@
# #KQ7
# RSQ@
# @%GC(7Q/
# #@K%C6QKCCC6@
# (@%CC3%C3CC%CCCC6Q
# GCCC%C3CC66CCCsCCC@
# sQ3C6CCCC%CCC3CCCCC%C7
# @6%CCCCCOC6CsCCC66O%s@
# GsCCC36CC6CC6%%CC%CC%@
# GC36C3s%CCCCsCC%C%C%CsG
# sCCC666CCCCC6%CC%CCsRG%#7
# C6%C63CRQGC3C77%7(t((t((7Q^
# sC3CC@C((t((7QG#K#@@@@@@@@G7Q
# S6CCO#(t((@%/%O@@@@@@O(//R@@sC
# QsCC6st(%@ CG @@s@
# @OCCC%((%R/C@ RC @@7@
# @OO3s@(((@/ (R/ @#R(
# @s3CCCQC(t%%GQRQQQ##QGCGC((7R
# GGCCCsCCCG36SGQKSSSGsss
# Gs6C6CCCCCCCCCCs6CC6CC3
# /@CCC3Cs3GSQQQ#QRGQS@@Q
# @3%C@@(@///%///((((
# @CCOR/(@(O%O%%((((((
# GCCs@%(@///%////(((C
# CCCCG@(@%%CQ%%%%O(/#C
# sC3C%CCGGQQ#QQGGG6CC#@
# @((%@#3CC3%C3CC%CCCCsQC((Q7
# #Qt((t((7%Q#R@QK@Q#QG37((t((7@
# %3((t(((((((t((t((t((t(tt((t(t((t@
# %7t((t((t((((tt((((((((t(tt(t((t(((#C
#

161
src/dice.js Normal file
View File

@ -0,0 +1,161 @@
let Promise = require("bluebird");
let lib = require("./lib.js")();
module.exports = function(arg) {
//
// Export our functions.
//
return({
rollDice: rollDice,
});
}
/**
* Convert a number from base 10 into base 6.
*
* @param integer roll The random value.
* @param integer num_dice The number of dice we're returning.
*
* @return array An array of the base 6 numbers.
*/
function getBase6 (roll, num_dice) {
let retval = [];
//
// Sanity check
//
let max_dice_roll = Math.pow(6, num_dice) - 1;
if (roll > max_dice_roll) {
throw(new Error("Value too large!"));
}
if (roll < 0) {
throw(new Error("Value cannot be negative!"));
}
//
// Go through each die, starting with the most significant one, and
// get its value.
//
let num_dice_left = num_dice - 1;
let dice_value_left = roll;
for (let i = num_dice_left; i >= 0; i--) {
let die_value = Math.pow(6, i);
let value = Math.floor( dice_value_left / die_value);
let left = dice_value_left % die_value;
retval.push(value);
dice_value_left = dice_value_left - (die_value * value);
}
return(retval);
} // End of getBase6()
/**
* Convert a base-6 number to a dice roll
*
* @param array roll An array of integers in base-6 notation
* @param integer num_dice The number of dice rolled
*
* @return array An array of integers representing dice rolls
*/
function convertBase6ToDice (roll, num_dice) {
let retval = [];
if (roll.length != num_dice) {
throw("Mismatch between size of roll (" + roll.length + ") and number of dice (" + num_dice + ")");
}
for (let k in roll) {
let num = roll[k];
if (num < 0) {
throw("Value " + num + " is negative!");
}
if (num > 5) {
throw("Value " + num + " is too large!");
}
num++;
retval.push(num);
}
return(retval);
} // End of convertBase6ToDice()
/**
* Get the maximum value from the number of dice we're rolling.
* This is in a separate function so it is testable.
*/
function getNumValuesFromNumDice (num_dice) {
let retval;
if (num_dice == 0) {
throw("Number of dice cannot be zero!");
} else if (num_dice < 0){
throw("Number of dice is negative!");
}
retval = Math.pow(6, num_dice);
return(retval);
} // End of getNumValuesFromNumDice()
/**
* This is our main entry point for rolling dice.
*
* Get our maximum number for a random value, turn it into base-6,
* then turn it into a dice roll!
*
* @return object An object that contains a dice roll and the raw random value.
*
*/
function rollDice(num_dice) {
return new Promise(function(resolve, reject) {
let retval = {};
let max = getNumValuesFromNumDice(num_dice);
Promise.try(function() {
return(lib.getRandomValue(max - 1));
}).then(function(random) {
let base6 = getBase6(random, num_dice);
let dice = convertBase6ToDice(base6, num_dice);
retval.value = random;
retval.roll = dice;
resolve(retval);
});
}); // End of Promise()
} // End of rollDice()

338
src/display.js Normal file
View File

@ -0,0 +1,338 @@
let lib = require("./lib.js")();
// Functions that relate to rolling dice
dice = require("./dice.js")();
wordlist = require("./wordlist.js")();
// Misc utilities
util = require("./util.js")();
let Promise = require("bluebird");
module.exports = function(arg) {
//
// Export our functions.
//
return({
rollDiceHandler: rollDiceHandler,
});
}
/**
* Dispaly all of our rows (dice rolls) and the results.
*/
function _rows(rows) {
//
// Now display those rows.
//
_row(rows, function() {
//
// And then display the results
//
results(function() {
//
// Set the height of this back to auto so we don't have unused space.
// I'm amazed that we don't see a "flash" of the results div
// temporarily shrinking, but this seems to work as per what I saw
// at http://stackoverflow.com/questions/5003220/javascript-jquery-animate-to-auto-height
//
// Well then.
//
let height = jQuery(".results").height();
jQuery(".results").css("height", "auto");
let new_height = jQuery(".results").height();
jQuery(".results").height(height);
jQuery(".results").animate({height: new_height}, 400);
//
// All done with our results, re-enable the button!
//
jQuery("#roll_dice").prop("disabled", false);
});
});
} // End of _rows()
/**
* This function displays each dice roll.
*
* @param array rows Array of rows of dice rolls that we had.
* @param object cb Our callback to fire when done
* @param integer in_fadein_duration How long before fading in a roll of dice
* @param integer in_fadeout_delay How long before fading out the diceroll
*
*/
function _row(rows, cb, in_fadein_duration, in_fadeout_delay) {
let fadein_duration = in_fadein_duration || 250;
let fadeout_delay = in_fadeout_delay || 400;
if (rows.length) {
//
// Grab a row, and hide each of the dice and the word in it.
//
let row = rows.shift();
let html = row.hide().appendTo(".results");
html.find(".dice_element").each(function(i, value) {
jQuery(value).hide();
});
//
// Now show the row, and loop through each element, fading in
// the dice and the word in sequence.
//
html.show(fadein_duration, function() {
jQuery(this).find(".dice_element").each(function(i, value) {
let delay = i * 100;
setTimeout(function() {
jQuery(value).show();
}, delay);
});
//
// Decrease the delays with subsequent rolls so that users
// don't get impatent.
// (I know I did when rolling 8 dice!)
//
fadein_duration -= 25;
//fadeout_delay -= 50;
//
// Now fade out the entire row, and call ourselves again
// so we can repeat with the next row.
//
jQuery(this).delay(fadeout_delay)
.fadeOut(fadeout_delay, function() {
_row(rows, cb, fadein_duration, fadeout_delay);
});
});
} else {
//
// All done with displaying rows, fire our callback and get outta here.
//
cb();
}
} // End of row()
/**
* Display the actual results.
*
* @param cb object Optional callback to fire when done
*/
function results(cb) {
jQuery(".results_words_key").hide().clone().appendTo(".results");
jQuery(".results_words_value").hide().clone().appendTo(".results");
jQuery(".results").append("<br clear=\"all\" />");
jQuery(".results_phrase_key").hide().clone().appendTo(".results");
jQuery(".results_phrase_value").hide().clone().appendTo(".results");
jQuery(".results").append("<br clear=\"all\" />");
jQuery(".results_num_possible_key").hide().clone().appendTo(".results");
jQuery(".results_num_possible_value").hide().clone().appendTo(".results");
jQuery(".results .results_words_key").fadeIn(500).promise().then(function() {
return(jQuery(".results .results_words_value").fadeIn(500).promise())
}).then(function() {
return(jQuery(".results .results_phrase_key").fadeIn(400).promise());
}).then(function() {
return(jQuery(".results .results_phrase_value").fadeIn(400).promise());
}).then(function() {
return(jQuery(".results .results_num_possible_key").fadeIn(300).promise());
}).then(function() {
return(jQuery(".results .results_num_possible_value").fadeIn(300).promise());
}).then(function() {
if (cb) {
cb();
}
});
} // End of results()
/**
* Do some preliminary work, such as clearing out results and scrolling.
*/
function rollDiceHandlerPre() {
//
// Clear out more space when mobile
//
// In the future, I should just use a media query in CSS
//
let target_height = 300;
if (util.is_mobile()) {
target_height = 400;
}
jQuery(".results").animate({height: target_height}, 400);
//
// If we're running on an iPhone or similar, scroll down so that we can
// see the dice rolls and passphrase.
//
if (util.is_mobile()) {
let aTag = $("a[name='roll_dice_button']");
$("html,body").animate({scrollTop: aTag.offset().top}, "slow");
}
//
// Remove any old results
//
jQuery(".results").empty();
} // End of rollDiceHandlerPre()
/**
* Our handler for what to do when the "Roll Dice" button is clicked".
* It generates the passphrase and updates the HTML.
*/
function rollDiceHandler(e) {
//
// Disable our button while generating results
//
jQuery("#roll_dice").prop("disabled", true);
rollDiceHandlerPre();
//
// Make our dice rolls
//
let num_dice = jQuery(".dice_button.active").html();
console.log(`Rolling ${num_dice} dice...`);
let num_passwords = Number(Math.pow(6, (window.Diceware.num_dice_per_roll * num_dice)));
let passphrase = new Array();
let rolls = new Array();
//
// Create an array of empty items, since this is the only way I can
// figure out to do a loop in Bluebird at this time. Ugh.
//
let items = [];
for (i=0; i<num_dice; i++) { items.push(null); }
Promise.map(items, function(element) {
//
// Do our dice rolls all at once.
//
return(dice.rollDice(window.Diceware.num_dice_per_roll));
}).then(function(data) {
//
// Now that we have the results, get the word for each roll,
// save the roll, and push the word onto the passphrase.
//
data.forEach(function(row) {
let roll = {};
roll.dice = row;
//console.log("Debug Dice Roll", JSON.stringify(roll.dice)); // Debugging
roll.word = util.get_word(wordlist.get, roll.dice.value);
rolls.push(roll);
passphrase.push(roll.word);
});
//
// Store the number of dice rolled in a data attribute for
// inspection by Cypress or another test.
//
let results_num_dice = jQuery("#results-num-dice");
results_num_dice.text(num_dice);
rollDiceHandlerPost(rolls, passphrase, num_passwords);
});
} // End of rollDiceHandler()
/**
* Our post work, of displaying the results of our dice rolls.
*/
function rollDiceHandlerPost(rolls, passphrase, num_passwords) {
//
// Populate our results by cloning the hidden base rows which
// represent each die.
//
jQuery(".results_words_value").html(passphrase.join(" "));
//
// Separate words in the phrase by "Word Break Opportunity"
// tag so they will wrap properly on a narrow/mobile screen.
//
jQuery(".results_phrase_value").html(passphrase.join("<wbr>"));
//
// Convert the number of passwords to something based on the
// locale and then add in <wbr> tags so they too will wrap.
//
num_passwords = lib.convertBigNumberToString(num_passwords);
num_passwords_html = num_passwords.toLocaleString("fullwide");
num_passwords_html = num_passwords_html.replace(/,/g, ",<wbr>");
jQuery(".results_num_possible_value").html(num_passwords_html);
let rows = new Array();
for (let key in rolls) {
let roll = rolls[key];
let row = jQuery("<div></div>");
//
// Clone and append specific dice to this row.
//
for (let key2 in roll.dice.roll) {
let die = roll.dice.roll[key2];
let classname = ".source .dice" + die;
let tmp = jQuery(classname).clone().appendTo(row);
}
//
// Now append the word
//
let dice_word = jQuery(".dice_word").clone();
dice_word.html("\"" + roll.word + "\"");
row.append(dice_word);
//
// And clear to the next line
//
row.append("<br clear=\"all\" />");
if ("skip_animation" in window.Diceware.get_data) {
console.log("Debug value 'skip_animation' set, not showing the dice!");
} else {
rows.push(row);
}
}
_rows(rows);
} // End of rollDiceHandlerPost()

View File

@ -2,416 +2,38 @@
* Our main Javascript file.
*/
var Promise = require("bluebird");
let Promise = require("bluebird");
var lib = require("./lib.js");
let lib = require("./lib.js")();
//
// This "put everything in an object" approach is a holdover from
// before I started using webpack. Yay legacy code.
// I'm not a huge fan of globals, but the alternative was passing this variable into
// nearly every module, which gave me headaches trying to keep track of the dependency.
// I am storing things in modules (such as the wordlist) where I can...
//
var Diceware = {};
window.Diceware = {};
// Functions that relate to displaying dice
display = require("./display.js")()
// Wordlist handling
wordlist = require("./wordlist.js")()
// Misc utilities
util = require("./util.js")();
//
// How many dice per roll?
//
Diceware.num_dice_per_roll = 5;
window.Diceware.num_dice_per_roll = 5;
/**
* Look up a word from our wordlist.
*
* @param object wordlist Our hash table of dice rolls and their corresponding words.
* @param integer index
*
* @return string The word from the dicelist
* Set the handlers
*/
Diceware.get_word = function(wordlist, index) {
var retval = wordlist[index];
if (retval) {
retval = retval[0].toUpperCase() + retval.slice(1);
} else {
retval = "((Word not found in wordlist)) ";
}
return(retval);
}
/**
* This function displays each dice roll.
*
* @param array rows Array of rows of dice rolls that we had.
* @param object cb Our callback to fire when done
* @param integer in_fadein_duration How long before fading in a roll of dice
* @param integer in_fadeout_delay How long before fading out the diceroll
*
*/
Diceware.display_row = function(rows, cb, in_fadein_duration, in_fadeout_delay) {
var fadein_duration = in_fadein_duration || 250;
var fadeout_delay = in_fadeout_delay || 400;
if (rows.length) {
//
// Grab a row, and hide each of the dice and the word in it.
//
var row = rows.shift();
var html = row.hide().appendTo(".results");
html.find(".dice_element").each(function(i, value) {
jQuery(value).hide();
});
//
// Now show the row, and loop through each element, fading in
// the dice and the word in sequence.
//
html.show(fadein_duration, function() {
jQuery(this).find(".dice_element").each(function(i, value) {
var delay = i * 100;
setTimeout(function() {
jQuery(value).show();
}, delay);
});
//
// Decrease the delays with subsequent rolls so that users
// don't get impatent.
// (I know I did when rolling 8 dice!)
//
fadein_duration -= 25;
//fadeout_delay -= 50;
//
// Now fade out the entire row, and call ourselves again
// so we can repeat with the next row.
//
jQuery(this).delay(fadeout_delay)
.fadeOut(fadeout_delay, function() {
Diceware.display_row(rows, cb, fadein_duration, fadeout_delay);
});
});
} else {
//
// All done with displaying rows, fire our callback and get outta here.
//
cb();
}
} // End of display_row()
/**
* Display the actual results.
*
* @param cb object Optional callback to fire when done
*/
Diceware.display_results = function(cb) {
jQuery(".results_words_key").hide().clone().appendTo(".results");
jQuery(".results_words_value").hide().clone().appendTo(".results");
jQuery(".results").append("<br clear=\"all\" />");
jQuery(".results_phrase_key").hide().clone().appendTo(".results");
jQuery(".results_phrase_value").hide().clone().appendTo(".results");
jQuery(".results").append("<br clear=\"all\" />");
jQuery(".results_num_possible_key").hide().clone().appendTo(".results");
jQuery(".results_num_possible_value").hide().clone().appendTo(".results");
jQuery(".results .results_words_key").fadeIn(500, function() {
jQuery(".results .results_words_value").fadeIn(500, function() {
jQuery(".results .results_phrase_key").fadeIn(400, function() {
jQuery(".results .results_phrase_value").fadeIn(400, function() {
jQuery(".results .results_num_possible_key").fadeIn(300, function() {
jQuery(".results .results_num_possible_value").fadeIn(300, function() {
if (cb) {
cb();
}
});
});
});
});
});
});
} // End of display_results()
/**
* Return the width of the browser window.
*/
Diceware.get_width = function() {
return(jQuery(window).width());
}
/**
* Return true if we are running on a mobile screen.
*/
Diceware.is_mobile = function() {
if (Diceware.get_width() <= 480) {
return(true);
}
return(false);
} // End of is_mobile()
/**
* Do some preliminary work, such as clearing out results and scrolling.
*/
Diceware.rollDiceHandlerPre = function() {
//
// Clear out more space when mobile
//
// In the future, I should just use a media query in CSS
//
var target_height = 300;
if (Diceware.is_mobile()) {
target_height = 400;
}
jQuery(".results").animate({height: target_height}, 400);
//
// If we're running on an iPhone or similar, scroll down so that we can
// see the dice rolls and passphrase.
//
if (Diceware.is_mobile()) {
var aTag = $("a[name='roll_dice_button']");
$("html,body").animate({scrollTop: aTag.offset().top}, "slow");
}
//
// Remove any old results
//
jQuery(".results").empty();
} // End of rollDiceHandlerPre()
/**
* Our handler for what to do when the "Roll Dice" button is clicked".
* It generates the passphrase and updates the HTML.
*/
Diceware.rollDiceHandler = function(e) {
//
// Disable our button while generating results
//
jQuery("#roll_dice").prop("disabled", true);
Diceware.rollDiceHandlerPre();
//
// Make our dice rolls
//
var num_dice = jQuery(".dice_button.active").html();
var num_passwords = Number(Math.pow(6, (Diceware.num_dice_per_roll * num_dice)));
var passphrase = new Array();
var rolls = new Array();
//
// Create an array of empty items, since this is the only way I can
// figure out to do a loop in Bluebird at this time. Ugh.
//
var items = [];
for (i=0; i<num_dice; i++) { items.push(null); }
Promise.map(items, function(element) {
//
// Do our dice rolls all at once.
//
return(lib.rollDice(Diceware.num_dice_per_roll));
}).then(function(data) {
//
// Now that we have the results, get the word for each roll,
// save the roll, and push the word onto the passphrase.
//
data.forEach(function(row) {
var roll = {};
roll.dice = row;
//console.log("Debug Dice Roll", JSON.stringify(roll.dice)); // Debugging
roll.word = Diceware.get_word(wordlist, roll.dice.value);
rolls.push(roll);
passphrase.push(roll.word);
});
//
// Store the number of dice rolled in a data attribute for
// inspection by Cypress or another test.
//
var results_num_dice = jQuery("#results-num-dice");
results_num_dice.text(num_dice);
Diceware.rollDiceHandlerPost(rolls, passphrase, num_passwords);
});
} // End of rollDiceHandler()
/**
* Our post work, of displaying the results of our dice rolls.
*/
Diceware.rollDiceHandlerPost = function(rolls, passphrase, num_passwords) {
//
// Populate our results by cloning the hidden base rows which
// represent each die.
//
jQuery(".results_words_value").html(passphrase.join(" "));
//
// Separate words in the phrase by "Word Break Opportunity"
// tag so they will wrap properly on a narrow/mobile screen.
//
jQuery(".results_phrase_value").html(passphrase.join("<wbr>"));
//
// Convert the number of passwords to something based on the
// locale and then add in <wbr> tags so they too will wrap.
//
num_passwords = convertBigNumberToString(num_passwords);
num_passwords_html = num_passwords.toLocaleString("fullwide");
num_passwords_html = num_passwords_html.replace(/,/g, ",<wbr>");
jQuery(".results_num_possible_value").html(num_passwords_html);
var rows = new Array();
for (var key in rolls) {
var roll = rolls[key];
var row = jQuery("<div></div>");
//
// Clone and append specific dice to this row.
//
for (var key2 in roll.dice.roll) {
var die = roll.dice.roll[key2];
var classname = ".source .dice" + die;
var tmp = jQuery(classname).clone().appendTo(row);
}
//
// Now append the word
//
var dice_word = jQuery(".dice_word").clone();
dice_word.html("\"" + roll.word + "\"");
row.append(dice_word);
//
// And clear to the next line
//
row.append("<br clear=\"all\" />");
if ("skip_animation" in Diceware.get_data) {
console.log("Debug value 'skip_animation' set, not showing the dice!");
} else {
rows.push(row);
}
}
//
// Now display those rows.
//
Diceware.display_row(rows, function() {
//
// And then display the results
//
Diceware.display_results(function() {
//
// Set the height of this back to auto so we don't have unused space.
// I'm amazed that we don't see a "flash" of the results div
// temporarily shrinking, but this seems to work as per what I saw
// at http://stackoverflow.com/questions/5003220/javascript-jquery-animate-to-auto-height
//
// Well then.
//
var height = jQuery(".results").height();
jQuery(".results").css("height", "auto");
var new_height = jQuery(".results").height();
jQuery(".results").height(height);
jQuery(".results").animate({height: new_height}, 400);
//
// All done with our results, re-enable the button!
//
jQuery("#roll_dice").prop("disabled", false);
});
});
} // End of rollDiceHandlerPost()
/**
* Turn our GET method data into an associative array.
*/
Diceware.extractGetData = function(get_data) {
var retval = {};
if (!location.search) {
return(retval);
}
var get = get_data.substring(1);
var pairs = get.split("&");
for (var k in pairs) {
var row = pairs[k];
var pair = row.split("=");
var key = pair[0];
var value = pair[1];
retval[key] = value;
}
return(retval);
} // End of extractGetData()
/**
* Our main function when being used via the UI. We call this to set up our jQuery hooks.
*
* I should probably refactor this more in the future--this function came about
* when I changed the code from self-contained to contained in an external object
* in preparation for Qunit testing...
*
*/
Diceware.go = function() {
console.log("Thanks for checking out my code! You can find the Git repo at https://github.com/dmuth/diceware, my blog at https://www.dmuth.org/, or you can bug me on Twitter at https://twitter.com/dmuth");
console.log("Version: $Id$");
Diceware.get_data = Diceware.extractGetData(location.search);
console.log("GET Data: " + JSON.stringify(Diceware.get_data)); // Debugging
function set_handlers() {
//
// Handler to mark the clicked number of dice button as active.
@ -421,14 +43,20 @@ Diceware.go = function() {
jQuery(e.target).addClass("active");
});
jQuery("#roll_dice").on("click", display.rollDiceHandler);
jQuery("#roll_dice").on("click", Diceware.rollDiceHandler);
} // End of set_handlers()
/**
* Run our pre-flight checks.
*/
function run_preflight_checks() {
//
// If we're not on a mobile, bring in the GitHub ribbon.
//
if (!Diceware.is_mobile()) {
if (!util.is_mobile()) {
jQuery("#github_ribbon").fadeIn(1000);
}
@ -436,62 +64,43 @@ Diceware.go = function() {
jQuery(".source .bad_crypto").clone().hide().fadeIn(800).appendTo(".message");
}
} // End of run_preflight_checks()
var dice = 5;
if (Diceware.get_data["dice"]) {
if (Diceware.get_data["dice"] >= 5 && Diceware.get_data["dice"] <= 7) {
dice = Diceware.get_data["dice"];
Diceware.num_dice_per_roll = dice;
}
}
console.log("Rolling " + Diceware.num_dice_per_roll + " dice per roll");
/**
* Our main function when being used via the UI. We call this to set up our jQuery hooks.
*
*/
function go() {
var file = "wordlist-" + dice + "-dice.js";
if (dice == 5) {
//
// 5 Dice? Let's use the EFF version.
//
file = "wordlist-" + dice + "-dice-eff.js";
}
console.log("Thanks for checking out my code! You can find the Git repo at https://github.com/dmuth/diceware, my blog at https://www.dmuth.org/, or you can bug me on Twitter at https://twitter.com/dmuth");
var js = "./assets/wordlist/" + file;
console.log("Looks like we're loading '" + js + "'!");
console.log("Version: $Id$");
//
// Load our wordlist.
//
jQuery.getScript(js).done(
function(data) {
//
// Set our handlers
//
set_handlers()
//
// If "debug" is set in the GET data, roll the dice on page load.
// Speed up my development a bit. :-)
//
var debug = location.search.indexOf("debug");
//
// Run our pre-flight checks
//
run_preflight_checks()
if (Diceware.get_data["debug"] && Diceware.get_data["debug"] > 0) {
//
// Get the filename of the wordlist that we're loading.
//
let file = wordlist.get_filename()
console.log(`Looks like we're loading ${file["filename"]}!`);
var num = Diceware.get_data["debug"];
if (num < 2) {
num = 2;
} else if (num > 8) {
num = 8;
}
//
// Load the wordlist.
//
let debug = location.search.indexOf("debug");
window.Diceware.get_data = util.extract_get_data(location.search);
console.log("GET Data: " + JSON.stringify(window.Diceware.get_data)); // Debugging
var id="#button-dice-" + num;
jQuery(id).click();
console.log("Debug mode enabled, auto-rolling " + num + " times!");
jQuery("#roll_dice").click();
}
}).fail(
function(jqxhr, settings, exception) {
console.log("Error loading Javascript:", jqxhr.status, settings, exception);
});
wordlist.load(file, window.Diceware.get_data, debug)
} // End of go()
@ -499,6 +108,6 @@ Diceware.go = function() {
//
// Run go() automatically, as that is the webpack way.
//
Diceware.go();
go();

View File

@ -1,14 +1,28 @@
var Promise = require("bluebird");
var randomNumber = require("random-number-csprng");
let Promise = require("bluebird");
let randomNumber = require("random-number-csprng");
module.exports = function(arg) {
//
// Export our functions.
//
return({
iCanHasGoodCrypto: iCanHasGoodCrypto,
convertBigNumberToString: convertBigNumberToString,
getRandomValue: getRandomValue,
});
}
/**
* Return true if we have a function that returns cryptographically random
* values. False otherwise.
*/
module.exports.iCanHasGoodCrypto = iCanHasGoodCrypto = function() {
function iCanHasGoodCrypto() {
//
// If we don't have a Window variable, we're in Node.js, probably doing unit tests, so
@ -36,7 +50,7 @@ module.exports.iCanHasGoodCrypto = iCanHasGoodCrypto = function() {
/**
* Return a random integer between 0 and max, inclusive.
*/
module.exports.getRandomValue = getRandomValue = function(max) {
function getRandomValue(max) {
return new Promise(function(resolve, reject) {
if (max <= 0) {
@ -72,151 +86,10 @@ module.exports.getRandomValue = getRandomValue = function(max) {
} // End of getRandomValue()
/**
* Convert a number from base 10 into base 6.
*
* @param integer roll The random value.
* @param integer num_dice The number of dice we're returning.
*
* @return array An array of the base 6 numbers.
*/
module.exports.getBase6 = getBase6 = function(roll, num_dice) {
var retval = [];
//
// Sanity check
//
var max_dice_roll = Math.pow(6, num_dice) - 1;
if (roll > max_dice_roll) {
throw(new Error("Value too large!"));
}
if (roll < 0) {
throw(new Error("Value cannot be negative!"));
}
//
// Go through each die, starting with the most significant one, and
// get its value.
//
var num_dice_left = num_dice - 1;
var dice_value_left = roll;
for (var i = num_dice_left; i >= 0; i--) {
var die_value = Math.pow(6, i);
var value = Math.floor( dice_value_left / die_value);
var left = dice_value_left % die_value;
retval.push(value);
dice_value_left = dice_value_left - (die_value * value);
}
return(retval);
} // End of getBase6()
/**
* Convert a base-6 number to a dice roll
*
* @param array roll An array of integers in base-6 notation
* @param integer num_dice The number of dice rolled
*
* @return array An array of integers representing dice rolls
*/
module.exports.convertBase6ToDice = convertBase6ToDice = function(roll, num_dice) {
var retval = [];
if (roll.length != num_dice) {
throw("Mismatch between size of roll (" + roll.length + ") and number of dice (" + num_dice + ")");
}
for (var k in roll) {
var num = roll[k];
if (num < 0) {
throw("Value " + num + " is negative!");
}
if (num > 5) {
throw("Value " + num + " is too large!");
}
num++;
retval.push(num);
}
return(retval);
} // End of convertBase6ToDice()
/**
* Get the maximum value from the number of dice we're rolling.
* This is in a separate function so it is testable.
*/
module.exports.getNumValuesFromNumDice = getNumValuesFromNumDice = function(num_dice) {
var retval;
if (num_dice == 0) {
throw("Number of dice cannot be zero!");
} else if (num_dice < 0){
throw("Number of dice is negative!");
}
retval = Math.pow(6, num_dice);
return(retval);
} // End of getNumValuesFromNumDice()
/**
* This is our main entry point for rolling dice.
*
* Get our maximum number for a random value, turn it into base-6,
* then turn it into a dice roll!
*
* @return object An object that contains a dice roll and the raw random value.
*
*/
module.exports.rollDice = rollDice = function(num_dice) {
return new Promise(function(resolve, reject) {
var retval = {};
var max = getNumValuesFromNumDice(num_dice);
Promise.try(function() {
return(getRandomValue(max - 1));
}).then(function(random) {
var base6 = getBase6(random, num_dice);
var dice = convertBase6ToDice(base6, num_dice);
retval.value = random;
retval.roll = dice;
resolve(retval);
});
}); // End of Promise()
} // End of rollDice()
/**
* Convert a big number to a string for readability.
*/
module.exports.convertBigNumberToString = convertBigNumberToString = function(bignum) {
function convertBigNumberToString(bignum) {
//
// Default to what we passed in, in case we don't get a match.

94
src/util.js Normal file
View File

@ -0,0 +1,94 @@
let Promise = require("bluebird");
module.exports = function(arg) {
//
// Export our functions.
//
return({
is_mobile: is_mobile,
get_word: get_word,
extract_get_data: extract_get_data,
});
}
/**
* Return the width of the browser window.
*/
function get_width() {
return(jQuery(window).width());
}
/**
* Return true if we are running on a mobile screen.
*/
function is_mobile() {
if (get_width() <= 480) {
return(true);
}
return(false);
} // End of is_mobile()
/**
* Look up a word from our wordlist.
*
* @param callback A callback which returns our currently loaded wordlist
* @param integer index
*
* @return string The word from the dicelist
*/
function get_word(cb_wordlist, index) {
let retval = cb_wordlist()[index];
if (retval) {
retval = retval[0].toUpperCase() + retval.slice(1);
} else {
retval = "((Word not found in wordlist)) ";
}
return(retval);
} // End of get_word()
/**
* Turn our GET method data into an associative array.
*/
function extract_get_data(get_data) {
let retval = {};
if (!location.search) {
return(retval);
}
let get = get_data.substring(1);
let pairs = get.split("&");
for (let k in pairs) {
let row = pairs[k];
let pair = row.split("=");
let key = pair[0];
let value = pair[1];
retval[key] = value;
}
return(retval);
} // End of extractGetData()

106
src/wordlist.js Normal file
View File

@ -0,0 +1,106 @@
let Promise = require("bluebird");
//
// Our loaded wordlist.
// This is an array of one word per line.
//
let _wordlist;
module.exports = function(arg) {
//
// Export our functions.
//
return({
get_filename: get_filename,
load: load,
get: get,
});
}
/**
* Figure out what filename we're loading.
*/
get_filename = function() {
let retval = {};
//
// For now, we're using the 5 dice file from the EFF.
// I expect to add support for multiple dice files in the future.
//
file = "wordlist-eff-5-dice.txt";
retval = {
file: "EFF Wordlist",
filename: `./assets/wordlist/${file}`,
}
return(retval);
} // End of get_filename()
/**
* Load our wordlist with an XHR request.
*/
load = function(file, get_data, debug) {
jQuery("#roll_dice_text").html(`Loading wordlist '${file["file"]}'...`);
jQuery("#roll_dice").prop("disabled", true);
fetch(file["filename"]).then(function(response) {
if (!response.ok) {
throw Error(`HTTP ${response.status}: ${response.statusText}`);
}
return(response.text());
}).then(function(data) {
_wordlist = data.split(/\r?\n/);
console.log(`File ${file["filename"]} loaded!`);
jQuery("#roll_dice_text").html("Roll Dice!");
jQuery("#roll_dice").prop("disabled", false);
//
// If "debug" is set in the GET data, roll the dice on page load.
// Speed up my development a bit. :-)
//
if (get_data["debug"] && get_data["debug"] > 0) {
let num = get_data["debug"];
if (num < 2) {
num = 2;
} else if (num > 8) {
num = 8;
}
let id="#button-dice-" + num;
jQuery(id).click();
console.log("Debug mode enabled, auto-rolling " + num + " times!");
jQuery("#roll_dice").click();
}
}).catch(function(error) {
console.log(`Error loading file ${file["filename"]}: `, error);
});
} // End of load()
/**
* Return our wordlist.
*/
function get() {
return(_wordlist);
}

View File

@ -3,7 +3,8 @@ var should = require('should');
var Promise = require("bluebird");
var diceware = require("../src/lib.js")
var lib = require("../src/lib.js")();
var dice = require("../src/dice.js")();
describe("Diceware", function() {
@ -13,20 +14,20 @@ describe("Diceware", function() {
it("Should pass", function(done) {
Promise.try(function() {
return(diceware.getRandomValue(-1));
return(lib.getRandomValue(-1));
}).catch(function(err) {
err.should.match(/can't be less or equal to zero/);
return(diceware.getRandomValue(0));
return(lib.getRandomValue(0));
}).catch(function(err) {
err.should.match(/can't be less or equal to zero/);
return(diceware.getRandomValue(1));
return(lib.getRandomValue(1));
}).then(function(num) {
num.should.be.aboveOrEqual(0);
num.should.be.lessThanOrEqual(1);
return(diceware.getRandomValue(999));
return(lib.getRandomValue(999));
}).then(function(num) {
num.should.be.aboveOrEqual(0);
@ -46,27 +47,27 @@ describe("Diceware", function() {
describe("getBase6()", function() {
it("Better pass", function() {
diceware.getBase6(0, 1).should.containDeepOrdered([0]);
diceware.getBase6(1, 1).should.containDeepOrdered([1]);
diceware.getBase6(5, 1).should.containDeepOrdered([5]);
diceware.getBase6(6, 2).should.containDeepOrdered([1, 0]);
diceware.getBase6(12, 2).should.containDeepOrdered([2, 0]);
diceware.getBase6(35, 2).should.containDeepOrdered([5, 5]);
diceware.getBase6(36, 3).should.containDeepOrdered([1, 0, 0]);
diceware.getBase6(180, 3).should.containDeepOrdered([5, 0, 0]);
diceware.getBase6(215, 3).should.containDeepOrdered([5, 5, 5]);
diceware.getBase6(216, 4).should.containDeepOrdered([1, 0, 0, 0]);
diceware.getBase6(1080, 4).should.containDeepOrdered([5, 0, 0, 0]);
diceware.getBase6(1295, 4).should.containDeepOrdered([5, 5, 5, 5]);
diceware.getBase6(7775, 5).should.containDeepOrdered([5, 5, 5, 5, 5]);
diceware.getBase6(46655, 6).should.containDeepOrdered([5, 5, 5, 5, 5, 5]);
diceware.getBase6(279935, 7).should.containDeepOrdered([5, 5, 5, 5, 5, 5, 5]);
diceware.getBase6(1679615, 8).should.containDeepOrdered([5, 5, 5, 5, 5, 5, 5, 5]);
dice.getBase6(0, 1).should.containDeepOrdered([0]);
dice.getBase6(1, 1).should.containDeepOrdered([1]);
dice.getBase6(5, 1).should.containDeepOrdered([5]);
dice.getBase6(6, 2).should.containDeepOrdered([1, 0]);
dice.getBase6(12, 2).should.containDeepOrdered([2, 0]);
dice.getBase6(35, 2).should.containDeepOrdered([5, 5]);
dice.getBase6(36, 3).should.containDeepOrdered([1, 0, 0]);
dice.getBase6(180, 3).should.containDeepOrdered([5, 0, 0]);
dice.getBase6(215, 3).should.containDeepOrdered([5, 5, 5]);
dice.getBase6(216, 4).should.containDeepOrdered([1, 0, 0, 0]);
dice.getBase6(1080, 4).should.containDeepOrdered([5, 0, 0, 0]);
dice.getBase6(1295, 4).should.containDeepOrdered([5, 5, 5, 5]);
dice.getBase6(7775, 5).should.containDeepOrdered([5, 5, 5, 5, 5]);
dice.getBase6(46655, 6).should.containDeepOrdered([5, 5, 5, 5, 5, 5]);
dice.getBase6(279935, 7).should.containDeepOrdered([5, 5, 5, 5, 5, 5, 5]);
dice.getBase6(1679615, 8).should.containDeepOrdered([5, 5, 5, 5, 5, 5, 5, 5]);
should.throws(function() { diceware.getBase6(6, 1) }, /Value too large/);
should.throws(function() { diceware.getBase6(36, 2) }, /Value too large/);
should.throws(function() { diceware.getBase6(216, 3) }, /Value too large/);
should.throws(function() { diceware.getBase6(-1, 1) }, /Value cannot be negative/);
should.throws(function() { dice.getBase6(6, 1) }, /Value too large/);
should.throws(function() { dice.getBase6(36, 2) }, /Value too large/);
should.throws(function() { dice.getBase6(216, 3) }, /Value too large/);
should.throws(function() { dice.getBase6(-1, 1) }, /Value cannot be negative/);
});
});
@ -75,31 +76,31 @@ describe("Diceware", function() {
describe("convertBase6ToDice()", function() {
it("Oughta pass", function() {
diceware.convertBase6ToDice([0], 1).should.containDeepOrdered([1]);
diceware.convertBase6ToDice([5], 1).should.containDeepOrdered([6]);
diceware.convertBase6ToDice([1, 0], 2).should.containDeepOrdered([2, 1]);
diceware.convertBase6ToDice([2, 0], 2).should.containDeepOrdered([3, 1]);
diceware.convertBase6ToDice([5, 5], 2).should.containDeepOrdered([6, 6]);
diceware.convertBase6ToDice([1, 0, 0], 3).should.containDeepOrdered([2, 1, 1]);
diceware.convertBase6ToDice([5, 0, 0], 3).should.containDeepOrdered([6, 1, 1]);
diceware.convertBase6ToDice([5, 5, 5], 3).should.containDeepOrdered([6, 6, 6]);
diceware.convertBase6ToDice([1, 0, 0, 0], 4).should.containDeepOrdered([2, 1, 1, 1]);
diceware.convertBase6ToDice([5, 0, 0, 0], 4).should.containDeepOrdered([6, 1, 1, 1]);
diceware.convertBase6ToDice([5, 5, 5, 5], 4).should.containDeepOrdered([6, 6, 6, 6]);
diceware.convertBase6ToDice([5, 5, 5, 5, 5], 5).should.containDeepOrdered([6, 6, 6, 6, 6]);
diceware.convertBase6ToDice([5, 5, 5, 5, 5, 5], 6).should.containDeepOrdered([6, 6, 6, 6, 6, 6]);
diceware.convertBase6ToDice([5, 5, 5, 5, 5, 5, 5], 7).should.containDeepOrdered([6, 6, 6, 6, 6, 6, 6]);
diceware.convertBase6ToDice([5, 5, 5, 5, 5, 5, 5, 5], 8).should.containDeepOrdered([6, 6, 6, 6, 6, 6, 6, 6]);
dice.convertBase6ToDice([0], 1).should.containDeepOrdered([1]);
dice.convertBase6ToDice([5], 1).should.containDeepOrdered([6]);
dice.convertBase6ToDice([1, 0], 2).should.containDeepOrdered([2, 1]);
dice.convertBase6ToDice([2, 0], 2).should.containDeepOrdered([3, 1]);
dice.convertBase6ToDice([5, 5], 2).should.containDeepOrdered([6, 6]);
dice.convertBase6ToDice([1, 0, 0], 3).should.containDeepOrdered([2, 1, 1]);
dice.convertBase6ToDice([5, 0, 0], 3).should.containDeepOrdered([6, 1, 1]);
dice.convertBase6ToDice([5, 5, 5], 3).should.containDeepOrdered([6, 6, 6]);
dice.convertBase6ToDice([1, 0, 0, 0], 4).should.containDeepOrdered([2, 1, 1, 1]);
dice.convertBase6ToDice([5, 0, 0, 0], 4).should.containDeepOrdered([6, 1, 1, 1]);
dice.convertBase6ToDice([5, 5, 5, 5], 4).should.containDeepOrdered([6, 6, 6, 6]);
dice.convertBase6ToDice([5, 5, 5, 5, 5], 5).should.containDeepOrdered([6, 6, 6, 6, 6]);
dice.convertBase6ToDice([5, 5, 5, 5, 5, 5], 6).should.containDeepOrdered([6, 6, 6, 6, 6, 6]);
dice.convertBase6ToDice([5, 5, 5, 5, 5, 5, 5], 7).should.containDeepOrdered([6, 6, 6, 6, 6, 6, 6]);
dice.convertBase6ToDice([5, 5, 5, 5, 5, 5, 5, 5], 8).should.containDeepOrdered([6, 6, 6, 6, 6, 6, 6, 6]);
should.throws(function() { diceware.convertBase6ToDice([-1], 1); }, /negative/, "Negative value");
should.throws(function() { diceware.convertBase6ToDice([0, -1], 2); }, /negative/, "Negative value");
should.throws(function() { diceware.convertBase6ToDice([-1, 0], 2); }, /negative/, "Negative value");
should.throws(function() { diceware.convertBase6ToDice([6], 1); }, /too large/, "too large");
should.throws(function() { diceware.convertBase6ToDice([6, 0], 2); }, /too large/, "too large");
should.throws(function() { diceware.convertBase6ToDice([0, 6], 2); }, /too large/, "too large");
should.throws(function() { dice.convertBase6ToDice([-1], 1); }, /negative/, "Negative value");
should.throws(function() { dice.convertBase6ToDice([0, -1], 2); }, /negative/, "Negative value");
should.throws(function() { dice.convertBase6ToDice([-1, 0], 2); }, /negative/, "Negative value");
should.throws(function() { dice.convertBase6ToDice([6], 1); }, /too large/, "too large");
should.throws(function() { dice.convertBase6ToDice([6, 0], 2); }, /too large/, "too large");
should.throws(function() { dice.convertBase6ToDice([0, 6], 2); }, /too large/, "too large");
should.throws(function() { diceware.convertBase6ToDice([0], 2); }, /mismatch/i, "Mismatch");
should.throws(function() { diceware.convertBase6ToDice([0, 0], 1); }, /mismatch/i, "Mismatch");
should.throws(function() { dice.convertBase6ToDice([0], 2); }, /mismatch/i, "Mismatch");
should.throws(function() { dice.convertBase6ToDice([0, 0], 1); }, /mismatch/i, "Mismatch");
});
});
@ -110,30 +111,30 @@ describe("Diceware", function() {
Promise.try(function() {
diceware.getNumValuesFromNumDice(1).should.equal(6);
diceware.getNumValuesFromNumDice(2).should.equal(36);
diceware.getNumValuesFromNumDice(3).should.equal(216);
diceware.getNumValuesFromNumDice(4).should.equal(1296);
diceware.getNumValuesFromNumDice(5).should.equal(7776);
diceware.getNumValuesFromNumDice(6).should.equal(46656);
diceware.getNumValuesFromNumDice(7).should.equal(279936);
diceware.getNumValuesFromNumDice(8).should.equal(1679616);
lib.getNumValuesFromNumDice(1).should.equal(6);
lib.getNumValuesFromNumDice(2).should.equal(36);
lib.getNumValuesFromNumDice(3).should.equal(216);
lib.getNumValuesFromNumDice(4).should.equal(1296);
lib.getNumValuesFromNumDice(5).should.equal(7776);
lib.getNumValuesFromNumDice(6).should.equal(46656);
lib.getNumValuesFromNumDice(7).should.equal(279936);
lib.getNumValuesFromNumDice(8).should.equal(1679616);
should.throws(function() { diceware.getNumValuesFromNumDice(0); }, /zero/, "Zero");
should.throws(function() { diceware.getNumValuesFromNumDice(-1); }, /negative/, "Negative value");
should.throws(function() { lib.getNumValuesFromNumDice(0); }, /zero/, "Zero");
should.throws(function() { lib.getNumValuesFromNumDice(-1); }, /negative/, "Negative value");
//
// Test out our helper function first
//
return(diceware.rollDice(1));
return(lib.rollDice(1));
}).then(function(dice) {
dice.roll.length.should.be.equal(1);
return(diceware.rollDice(3));
return(lib.rollDice(3));
}).then(function(dice) {
dice.roll.length.should.be.equal(3);
return(diceware.rollDice(8));
return(lib.rollDice(8));
}).then(function(dice) {
dice.roll.length.should.be.equal(8);
@ -141,20 +142,20 @@ describe("Diceware", function() {
//
// These may fail infrequently if the random number is zero.
//
return(diceware.rollDice(3));
return(lib.rollDice(3));
}).then(function(dice) {
parseInt(dice.value).should.ok;
return(diceware.rollDice(8));
return(lib.rollDice(8));
}).then(function(dice) {
parseInt(dice.value).should.ok;
return(diceware.rollDice(0));
return(lib.rollDice(0));
}).catch(function(error) {
error.should.match(/zero/);
return(diceware.rollDice(-1));
return(lib.rollDice(-1));
}).catch(function(error) {
error.should.match(/negative/);
@ -172,22 +173,22 @@ describe("Diceware", function() {
describe("convertBigNumberToString()", function() {
it("Please pass", function() {
diceware.convertBigNumberToString(6 * Math.pow(10, 6)).should.equal("6 million");
diceware.convertBigNumberToString(60 * Math.pow(10, 9)).should.equal("60 billion");
diceware.convertBigNumberToString(600 * Math.pow(10, 12)).should.equal("600 trillion");
diceware.convertBigNumberToString(1 * Math.pow(10, 15)).should.equal("1 quadrillion");
diceware.convertBigNumberToString(123 * Math.pow(10, 18)).should.equal("123 quintillion");
lib.convertBigNumberToString(6 * Math.pow(10, 6)).should.equal("6 million");
lib.convertBigNumberToString(60 * Math.pow(10, 9)).should.equal("60 billion");
lib.convertBigNumberToString(600 * Math.pow(10, 12)).should.equal("600 trillion");
lib.convertBigNumberToString(1 * Math.pow(10, 15)).should.equal("1 quadrillion");
lib.convertBigNumberToString(123 * Math.pow(10, 18)).should.equal("123 quintillion");
diceware.convertBigNumberToString(6e+6).should.equal("6 million");
diceware.convertBigNumberToString(50E+9).should.equal("50 billion");
lib.convertBigNumberToString(6e+6).should.equal("6 million");
lib.convertBigNumberToString(50E+9).should.equal("50 billion");
diceware.convertBigNumberToString("7e+6").should.equal("7 million");
diceware.convertBigNumberToString("51E+9").should.equal("51 billion");
diceware.convertBigNumberToString("512E+18").should.equal("512 quintillion");
diceware.convertBigNumberToString("513E+21").should.equal("513 sextillion");
diceware.convertBigNumberToString("514E+24").should.equal("514 septillion");
diceware.convertBigNumberToString("515E+27").should.equal("515 octillion");
diceware.convertBigNumberToString("516E+30").should.equal("516 nonillion");
lib.convertBigNumberToString("7e+6").should.equal("7 million");
lib.convertBigNumberToString("51E+9").should.equal("51 billion");
lib.convertBigNumberToString("512E+18").should.equal("512 quintillion");
lib.convertBigNumberToString("513E+21").should.equal("513 sextillion");
lib.convertBigNumberToString("514E+24").should.equal("514 septillion");
lib.convertBigNumberToString("515E+27").should.equal("515 octillion");
lib.convertBigNumberToString("516E+30").should.equal("516 nonillion");
});