diff --git a/chance.js b/chance.js new file mode 100644 index 0000000..a672318 --- /dev/null +++ b/chance.js @@ -0,0 +1,1980 @@ +// Chance.js 0.7.3 +// http://chancejs.com +// (c) 2013 Victor Quinn +// Chance may be freely distributed or modified under the MIT license. + +(function () { + + // Constants + var MAX_INT = 9007199254740992; + var MIN_INT = -MAX_INT; + var NUMBERS = '0123456789'; + var CHARS_LOWER = 'abcdefghijklmnopqrstuvwxyz'; + var CHARS_UPPER = CHARS_LOWER.toUpperCase(); + var HEX_POOL = NUMBERS + "abcdef"; + + // Cached array helpers + var slice = Array.prototype.slice; + + // Constructor + function Chance (seed) { + if (!(this instanceof Chance)) { + return new Chance(seed); + } + + // if user has provided a function, use that as the generator + if (typeof seed === 'function') { + this.random = seed; + return this; + } + + var seedling; + + if (arguments.length) { + // set a starting value of zero so we can add to it + this.seed = 0; + } + // otherwise, leave this.seed blank so that MT will recieve a blank + + for (var i = 0; i < arguments.length; i++) { + seedling = 0; + if (typeof arguments[i] === 'string') { + for (var j = 0; j < arguments[i].length; j++) { + seedling += (arguments[i].length - j) * arguments[i].charCodeAt(j); + } + } else { + seedling = arguments[i]; + } + this.seed += (arguments.length - i) * seedling; + } + + // If no generator function was provided, use our MT + this.mt = this.mersenne_twister(this.seed); + this.random = function () { + return this.mt.random(this.seed); + }; + + return this; + } + + Chance.prototype.VERSION = "0.7.3"; + + // Random helper functions + function initOptions(options, defaults) { + options || (options = {}); + + if (defaults) { + for (var i in defaults) { + if (typeof options[i] === 'undefined') { + options[i] = defaults[i]; + } + } + } + + return options; + } + + function testRange(test, errorMessage) { + if (test) { + throw new RangeError(errorMessage); + } + } + + /** + * Encode the input string with Base64. + */ + var base64 = function() { + throw new Error('No Base64 encoder available.'); + }; + + // Select proper Base64 encoder. + (function determineBase64Encoder() { + if (typeof btoa === 'function') { + base64 = btoa; + } else if (typeof Buffer === 'function') { + base64 = function(input) { + return new Buffer(input).toString('base64'); + }; + } + })(); + + // -- Basics -- + + Chance.prototype.bool = function (options) { + + // likelihood of success (true) + options = initOptions(options, {likelihood : 50}); + + testRange( + options.likelihood < 0 || options.likelihood > 100, + "Chance: Likelihood accepts values from 0 to 100." + ); + + return this.random() * 100 < options.likelihood; + }; + + Chance.prototype.character = function (options) { + options = initOptions(options); + + var symbols = "!@#$%^&*()[]", + letters, pool; + + testRange( + options.alpha && options.symbols, + "Chance: Cannot specify both alpha and symbols." + ); + + + if (options.casing === 'lower') { + letters = CHARS_LOWER; + } else if (options.casing === 'upper') { + letters = CHARS_UPPER; + } else { + letters = CHARS_LOWER + CHARS_UPPER; + } + + if (options.pool) { + pool = options.pool; + } else if (options.alpha) { + pool = letters; + } else if (options.symbols) { + pool = symbols; + } else { + pool = letters + NUMBERS + symbols; + } + + return pool.charAt(this.natural({max: (pool.length - 1)})); + }; + + // Note, wanted to use "float" or "double" but those are both JS reserved words. + + // Note, fixed means N OR LESS digits after the decimal. This because + // It could be 14.9000 but in JavaScript, when this is cast as a number, + // the trailing zeroes are dropped. Left to the consumer if trailing zeroes are + // needed + Chance.prototype.floating = function (options) { + var num; + + options = initOptions(options, {fixed : 4}); + var fixed = Math.pow(10, options.fixed); + + testRange( + options.fixed && options.precision, + "Chance: Cannot specify both fixed and precision." + ); + + var max = MAX_INT / fixed; + var min = -max; + + testRange( + options.min && options.fixed && options.min < min, + "Chance: Min specified is out of range with fixed. Min should be, at least, " + min + ); + testRange( + options.max && options.fixed && options.max > max, + "Chance: Max specified is out of range with fixed. Max should be, at most, " + max + ); + + options = initOptions(options, {min : min, max : max}); + + // Todo - Make this work! + // options.precision = (typeof options.precision !== "undefined") ? options.precision : false; + + num = this.integer({min: options.min * fixed, max: options.max * fixed}); + var num_fixed = (num / fixed).toFixed(options.fixed); + + return parseFloat(num_fixed); + }; + + // NOTE the max and min are INCLUDED in the range. So: + // + // chance.natural({min: 1, max: 3}); + // + // would return either 1, 2, or 3. + + Chance.prototype.integer = function (options) { + + // 9007199254740992 (2^53) is the max integer number in JavaScript + // See: http://vq.io/132sa2j + options = initOptions(options, {min: MIN_INT, max: MAX_INT}); + + testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); + + return Math.floor(this.random() * (options.max - options.min + 1) + options.min); + }; + + Chance.prototype.natural = function (options) { + options = initOptions(options, {min: 0, max: MAX_INT}); + return this.integer(options); + }; + + Chance.prototype.string = function (options) { + options = initOptions(options); + + var length = options.length || this.natural({min: 5, max: 20}), + pool = options.pool, + text = this.n(this.character, length, {pool: pool}); + + return text.join(""); + }; + + // -- End Basics -- + + // -- Helpers -- + + Chance.prototype.capitalize = function (word) { + return word.charAt(0).toUpperCase() + word.substr(1); + }; + + Chance.prototype.mixin = function (obj) { + for (var func_name in obj) { + Chance.prototype[func_name] = obj[func_name]; + } + return this; + }; + + // Given a function that generates something random and a number of items to generate, + // return an array of items where none repeat. + Chance.prototype.unique = function(fn, num, options) { + options = initOptions(options, { + // Default comparator to check that val is not already in arr. + // Should return `false` if item not in array, `true` otherwise + comparator: function(arr, val) { + return arr.indexOf(val) !== -1; + } + }); + + var arr = [], count = 0, result, MAX_DUPLICATES = num * 50, params = slice.call(arguments, 2); + + while (arr.length < num) { + result = fn.apply(this, params); + if (!options.comparator(arr, result)) { + arr.push(result); + // reset count when unique found + count = 0; + } + + if (++count > MAX_DUPLICATES) { + throw new RangeError("Chance: num is likely too large for sample set"); + } + } + return arr; + }; + + /** + * Gives an array of n random terms + * @param fn the function that generates something random + * @param n number of terms to generate + * There can be more parameters after these. All additional parameters are provided to the given function + */ + Chance.prototype.n = function(fn, n) { + if (typeof n === 'undefined') { + n = 1; + } + var i = n, arr = [], params = slice.call(arguments, 2); + + // Providing a negative count should result in a noop. + i = Math.max( 0, i ); + + for (null; i--; null) { + arr.push(fn.apply(this, params)); + } + + return arr; + }; + + // H/T to SO for this one: http://vq.io/OtUrZ5 + Chance.prototype.pad = function (number, width, pad) { + // Default pad to 0 if none provided + pad = pad || '0'; + // Convert number to a string + number = number + ''; + return number.length >= width ? number : new Array(width - number.length + 1).join(pad) + number; + }; + + Chance.prototype.pick = function (arr, count) { + if (arr.length === 0) { + throw new RangeError("Chance: Cannot pick() from an empty array"); + } + if (!count || count === 1) { + return arr[this.natural({max: arr.length - 1})]; + } else { + return this.shuffle(arr).slice(0, count); + } + }; + + Chance.prototype.shuffle = function (arr) { + var old_array = arr.slice(0), + new_array = [], + j = 0, + length = Number(old_array.length); + + for (var i = 0; i < length; i++) { + // Pick a random index from the array + j = this.natural({max: old_array.length - 1}); + // Add it to the new array + new_array[i] = old_array[j]; + // Remove that element from the original array + old_array.splice(j, 1); + } + + return new_array; + }; + + // Returns a single item from an array with relative weighting of odds + Chance.prototype.weighted = function(arr, weights) { + if (arr.length !== weights.length) { + throw new RangeError("Chance: length of array and weights must match"); + } + + // Handle weights that are less or equal to zero. + for (var weightIndex = weights.length - 1; weightIndex >= 0; --weightIndex) { + // If the weight is less or equal to zero, remove it and the value. + if (weights[weightIndex] <= 0) { + arr.splice(weightIndex,1); + weights.splice(weightIndex,1); + } + } + + // If any of the weights are less than 1, we want to scale them up to whole + // numbers for the rest of this logic to work + if (weights.some(function(weight) { return weight < 1; })) { + var min = weights.reduce(function(min, weight) { + return (weight < min) ? weight : min; + }, weights[0]); + + var scaling_factor = 1 / min; + + weights = weights.map(function(weight) { + return weight * scaling_factor; + }); + } + + var sum = weights.reduce(function(total, weight) { + return total + weight; + }, 0); + + // get an index + var selected = this.natural({ min: 1, max: sum }); + + var total = 0; + var chosen; + // Using some() here so we can bail as soon as we get our match + weights.some(function(weight, index) { + if (selected <= total + weight) { + chosen = arr[index]; + return true; + } + total += weight; + return false; + }); + + return chosen; + }; + + // -- End Helpers -- + + // -- Text -- + + Chance.prototype.paragraph = function (options) { + options = initOptions(options); + + var sentences = options.sentences || this.natural({min: 3, max: 7}), + sentence_array = this.n(this.sentence, sentences); + + return sentence_array.join(' '); + }; + + // Could get smarter about this than generating random words and + // chaining them together. Such as: http://vq.io/1a5ceOh + Chance.prototype.sentence = function (options) { + options = initOptions(options); + + var words = options.words || this.natural({min: 12, max: 18}), + text, word_array = this.n(this.word, words); + + text = word_array.join(' '); + + // Capitalize first letter of sentence, add period at end + text = this.capitalize(text) + '.'; + + return text; + }; + + Chance.prototype.syllable = function (options) { + options = initOptions(options); + + var length = options.length || this.natural({min: 2, max: 3}), + consonants = 'bcdfghjklmnprstvwz', // consonants except hard to speak ones + vowels = 'aeiou', // vowels + all = consonants + vowels, // all + text = '', + chr; + + // I'm sure there's a more elegant way to do this, but this works + // decently well. + for (var i = 0; i < length; i++) { + if (i === 0) { + // First character can be anything + chr = this.character({pool: all}); + } else if (consonants.indexOf(chr) === -1) { + // Last character was a vowel, now we want a consonant + chr = this.character({pool: consonants}); + } else { + // Last character was a consonant, now we want a vowel + chr = this.character({pool: vowels}); + } + + text += chr; + } + + return text; + }; + + Chance.prototype.word = function (options) { + options = initOptions(options); + + testRange( + options.syllables && options.length, + "Chance: Cannot specify both syllables AND length." + ); + + var syllables = options.syllables || this.natural({min: 1, max: 3}), + text = ''; + + if (options.length) { + // Either bound word by length + do { + text += this.syllable(); + } while (text.length < options.length); + text = text.substring(0, options.length); + } else { + // Or by number of syllables + for (var i = 0; i < syllables; i++) { + text += this.syllable(); + } + } + return text; + }; + + // -- End Text -- + + // -- Person -- + + Chance.prototype.age = function (options) { + options = initOptions(options); + var ageRange; + + switch (options.type) { + case 'child': + ageRange = {min: 1, max: 12}; + break; + case 'teen': + ageRange = {min: 13, max: 19}; + break; + case 'adult': + ageRange = {min: 18, max: 65}; + break; + case 'senior': + ageRange = {min: 65, max: 100}; + break; + case 'all': + ageRange = {min: 1, max: 100}; + break; + default: + ageRange = {min: 18, max: 65}; + break; + } + + return this.natural(ageRange); + }; + + Chance.prototype.birthday = function (options) { + options = initOptions(options, { + year: (new Date().getFullYear() - this.age(options)) + }); + + return this.date(options); + }; + + // CPF; ID to identify taxpayers in Brazil + Chance.prototype.cpf = function () { + var n = this.n(this.natural, 9, { max: 9 }); + var d1 = n[8]*2+n[7]*3+n[6]*4+n[5]*5+n[4]*6+n[3]*7+n[2]*8+n[1]*9+n[0]*10; + d1 = 11 - (d1 % 11); + if (d1>=10) { + d1 = 0; + } + var d2 = d1*2+n[8]*3+n[7]*4+n[6]*5+n[5]*6+n[4]*7+n[3]*8+n[2]*9+n[1]*10+n[0]*11; + d2 = 11 - (d2 % 11); + if (d2>=10) { + d2 = 0; + } + return ''+n[0]+n[1]+n[2]+'.'+n[3]+n[4]+n[5]+'.'+n[6]+n[7]+n[8]+'-'+d1+d2; + }; + + Chance.prototype.first = function (options) { + options = initOptions(options, {gender: this.gender()}); + return this.pick(this.get("firstNames")[options.gender.toLowerCase()]); + }; + + Chance.prototype.gender = function () { + return this.pick(['Male', 'Female']); + }; + + Chance.prototype.last = function () { + return this.pick(this.get("lastNames")); + }; + + Chance.prototype.name = function (options) { + options = initOptions(options); + + var first = this.first(options), + last = this.last(), + name; + + if (options.middle) { + name = first + ' ' + this.first(options) + ' ' + last; + } else if (options.middle_initial) { + name = first + ' ' + this.character({alpha: true, casing: 'upper'}) + '. ' + last; + } else { + name = first + ' ' + last; + } + + if (options.prefix) { + name = this.prefix(options) + ' ' + name; + } + + if (options.suffix) { + name = name + ' ' + this.suffix(options); + } + + return name; + }; + + // Return the list of available name prefixes based on supplied gender. + Chance.prototype.name_prefixes = function (gender) { + gender = gender || "all"; + gender = gender.toLowerCase(); + + var prefixes = [ + { name: 'Doctor', abbreviation: 'Dr.' } + ]; + + if (gender === "male" || gender === "all") { + prefixes.push({ name: 'Mister', abbreviation: 'Mr.' }); + } + + if (gender === "female" || gender === "all") { + prefixes.push({ name: 'Miss', abbreviation: 'Miss' }); + prefixes.push({ name: 'Misses', abbreviation: 'Mrs.' }); + } + + return prefixes; + }; + + // Alias for name_prefix + Chance.prototype.prefix = function (options) { + return this.name_prefix(options); + }; + + Chance.prototype.name_prefix = function (options) { + options = initOptions(options, { gender: "all" }); + return options.full ? + this.pick(this.name_prefixes(options.gender)).name : + this.pick(this.name_prefixes(options.gender)).abbreviation; + }; + + Chance.prototype.ssn = function (options) { + options = initOptions(options, {ssnFour: false, dashes: true}); + var ssn_pool = "1234567890", + ssn, + dash = options.dashes ? '-' : ''; + + if(!options.ssnFour) { + ssn = this.string({pool: ssn_pool, length: 3}) + dash + + this.string({pool: ssn_pool, length: 2}) + dash + + this.string({pool: ssn_pool, length: 4}); + } else { + ssn = this.string({pool: ssn_pool, length: 4}); + } + return ssn; + }; + + // Return the list of available name suffixes + Chance.prototype.name_suffixes = function () { + var suffixes = [ + { name: 'Doctor of Osteopathic Medicine', abbreviation: 'D.O.' }, + { name: 'Doctor of Philosophy', abbreviation: 'Ph.D.' }, + { name: 'Esquire', abbreviation: 'Esq.' }, + { name: 'Junior', abbreviation: 'Jr.' }, + { name: 'Juris Doctor', abbreviation: 'J.D.' }, + { name: 'Master of Arts', abbreviation: 'M.A.' }, + { name: 'Master of Business Administration', abbreviation: 'M.B.A.' }, + { name: 'Master of Science', abbreviation: 'M.S.' }, + { name: 'Medical Doctor', abbreviation: 'M.D.' }, + { name: 'Senior', abbreviation: 'Sr.' }, + { name: 'The Third', abbreviation: 'III' }, + { name: 'The Fourth', abbreviation: 'IV' } + ]; + return suffixes; + }; + + // Alias for name_suffix + Chance.prototype.suffix = function (options) { + return this.name_suffix(options); + }; + + Chance.prototype.name_suffix = function (options) { + options = initOptions(options); + return options.full ? + this.pick(this.name_suffixes()).name : + this.pick(this.name_suffixes()).abbreviation; + }; + + // -- End Person -- + + // -- Mobile -- + // Android GCM Registration ID + Chance.prototype.android_id = function () { + return "APA91" + this.string({ pool: "0123456789abcefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_", length: 178 }); + }; + + // Apple Push Token + Chance.prototype.apple_token = function () { + return this.string({ pool: "abcdef1234567890", length: 64 }); + }; + + // Windows Phone 8 ANID2 + Chance.prototype.wp8_anid2 = function () { + return base64( this.hash( { length : 32 } ) ); + }; + + // Windows Phone 7 ANID + Chance.prototype.wp7_anid = function () { + return 'A=' + this.guid().replace(/-/g, '').toUpperCase() + '&E=' + this.hash({ length:3 }) + '&W=' + this.integer({ min:0, max:9 }); + }; + + // BlackBerry Device PIN + Chance.prototype.bb_pin = function () { + return this.hash({ length: 8 }); + }; + + // -- End Mobile -- + + // -- Web -- + Chance.prototype.color = function (options) { + function gray(value, delimiter) { + return [value, value, value].join(delimiter || ''); + } + + options = initOptions(options, {format: this.pick(['hex', 'shorthex', 'rgb', '0x']), grayscale: false, casing: 'lower'}); + var isGrayscale = options.grayscale; + var colorValue; + + if (options.format === 'hex') { + colorValue = '#' + (isGrayscale ? gray(this.hash({length: 2})) : this.hash({length: 6})); + + } else if (options.format === 'shorthex') { + colorValue = '#' + (isGrayscale ? gray(this.hash({length: 1})) : this.hash({length: 3})); + + } else if (options.format === 'rgb') { + if (isGrayscale) { + colorValue = 'rgb(' + gray(this.natural({max: 255}), ',') + ')'; + } else { + colorValue = 'rgb(' + this.natural({max: 255}) + ',' + this.natural({max: 255}) + ',' + this.natural({max: 255}) + ')'; + } + } else if (options.format === '0x') { + colorValue = '0x' + (isGrayscale ? gray(this.hash({length: 2})) : this.hash({length: 6})); + } else { + throw new Error('Invalid format provided. Please provide one of "hex", "shorthex", "rgb" or "0x".'); + } + + if (options.casing === 'upper' ) { + colorValue = colorValue.toUpperCase(); + } + + return colorValue; + }; + + Chance.prototype.domain = function (options) { + options = initOptions(options); + return this.word() + '.' + (options.tld || this.tld()); + }; + + Chance.prototype.email = function (options) { + options = initOptions(options); + return this.word({length: options.length}) + '@' + (options.domain || this.domain()); + }; + + Chance.prototype.fbid = function () { + return parseInt('10000' + this.natural({max: 100000000000}), 10); + }; + + Chance.prototype.google_analytics = function () { + var account = this.pad(this.natural({max: 999999}), 6); + var property = this.pad(this.natural({max: 99}), 2); + + return 'UA-' + account + '-' + property; + }; + + Chance.prototype.hashtag = function () { + return '#' + this.word(); + }; + + Chance.prototype.ip = function () { + // Todo: This could return some reserved IPs. See http://vq.io/137dgYy + // this should probably be updated to account for that rare as it may be + return this.natural({max: 255}) + '.' + + this.natural({max: 255}) + '.' + + this.natural({max: 255}) + '.' + + this.natural({max: 255}); + }; + + Chance.prototype.ipv6 = function () { + var ip_addr = this.n(this.hash, 8, {length: 4}); + + return ip_addr.join(":"); + }; + + Chance.prototype.klout = function () { + return this.natural({min: 1, max: 99}); + }; + + Chance.prototype.tlds = function () { + return ['com', 'org', 'edu', 'gov', 'co.uk', 'net', 'io']; + }; + + Chance.prototype.tld = function () { + return this.pick(this.tlds()); + }; + + Chance.prototype.twitter = function () { + return '@' + this.word(); + }; + + Chance.prototype.url = function (options) { + options = initOptions(options, { protocol: "http", domain: this.domain(options), domain_prefix: "", path: this.word(), extensions: []}); + + var extension = options.extensions.length > 0 ? "." + this.pick(options.extensions) : ""; + var domain = options.domain_prefix ? options.domain_prefix + "." + options.domain : options.domain; + + return options.protocol + "://" + domain + "/" + options.path + extension; + }; + + // -- End Web -- + + // -- Location -- + + Chance.prototype.address = function (options) { + options = initOptions(options); + return this.natural({min: 5, max: 2000}) + ' ' + this.street(options); + }; + + Chance.prototype.altitude = function (options) { + options = initOptions(options, {fixed : 5, max: 8848}); + return this.floating({min: 0, max: options.max, fixed: options.fixed}); + }; + + Chance.prototype.areacode = function (options) { + options = initOptions(options, {parens : true}); + // Don't want area codes to start with 1, or have a 9 as the second digit + var areacode = this.natural({min: 2, max: 9}).toString() + + this.natural({min: 0, max: 8}).toString() + + this.natural({min: 0, max: 9}).toString(); + + return options.parens ? '(' + areacode + ')' : areacode; + }; + + Chance.prototype.city = function () { + return this.capitalize(this.word({syllables: 3})); + }; + + Chance.prototype.coordinates = function (options) { + options = initOptions(options); + return this.latitude(options) + ', ' + this.longitude(options); + }; + + Chance.prototype.countries = function () { + return this.get("countries"); + }; + + Chance.prototype.country = function (options) { + options = initOptions(options); + var country = this.pick(this.countries()); + return options.full ? country.name : country.abbreviation; + }; + + Chance.prototype.depth = function (options) { + options = initOptions(options, {fixed: 5, min: -2550}); + return this.floating({min: options.min, max: 0, fixed: options.fixed}); + }; + + Chance.prototype.geohash = function (options) { + options = initOptions(options, { length: 7 }); + return this.string({ length: options.length, pool: '0123456789bcdefghjkmnpqrstuvwxyz' }); + }; + + Chance.prototype.geojson = function (options) { + options = initOptions(options); + return this.latitude(options) + ', ' + this.longitude(options) + ', ' + this.altitude(options); + }; + + Chance.prototype.latitude = function (options) { + options = initOptions(options, {fixed: 5, min: -90, max: 90}); + return this.floating({min: options.min, max: options.max, fixed: options.fixed}); + }; + + Chance.prototype.longitude = function (options) { + options = initOptions(options, {fixed: 5, min: -180, max: 180}); + return this.floating({min: options.min, max: options.max, fixed: options.fixed}); + }; + + Chance.prototype.phone = function (options) { + var self = this, + numPick, + ukNum = function (parts) { + var section = []; + //fills the section part of the phone number with random numbers. + parts.sections.forEach(function(n) { + section.push(self.string({ pool: '0123456789', length: n})); + }); + return parts.area + section.join(' '); + }; + options = initOptions(options, { + formatted: true, + country: 'us', + mobile: false + }); + if (!options.formatted) { + options.parens = false; + } + var phone; + switch (options.country) { + case 'fr': + if (!options.mobile) { + numPick = this.pick([ + // Valid zone and département codes. + '01' + this.pick(['30', '34', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '53', '55', '56', '58', '60', '64', '69', '70', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83']) + self.string({ pool: '0123456789', length: 6}), + '02' + this.pick(['14', '18', '22', '23', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '40', '41', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '56', '57', '61', '62', '69', '72', '76', '77', '78', '85', '90', '96', '97', '98', '99']) + self.string({ pool: '0123456789', length: 6}), + '03' + this.pick(['10', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '39', '44', '45', '51', '52', '54', '55', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90']) + self.string({ pool: '0123456789', length: 6}), + '04' + this.pick(['11', '13', '15', '20', '22', '26', '27', '30', '32', '34', '37', '42', '43', '44', '50', '56', '57', '63', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '88', '89', '90', '91', '92', '93', '94', '95', '97', '98']) + self.string({ pool: '0123456789', length: 6}), + '05' + this.pick(['08', '16', '17', '19', '24', '31', '32', '33', '34', '35', '40', '45', '46', '47', '49', '53', '55', '56', '57', '58', '59', '61', '62', '63', '64', '65', '67', '79', '81', '82', '86', '87', '90', '94']) + self.string({ pool: '0123456789', length: 6}), + '09' + self.string({ pool: '0123456789', length: 8}), + ]); + phone = options.formatted ? numPick.match(/../g).join(' ') : numPick; + } else { + numPick = this.pick(['06', '07']) + self.string({ pool: '0123456789', length: 8}); + phone = options.formatted ? numPick.match(/../g).join(' ') : numPick; + } + break; + case 'uk': + if (!options.mobile) { + numPick = this.pick([ + //valid area codes of major cities/counties followed by random numbers in required format. + { area: '01' + this.character({ pool: '234569' }) + '1 ', sections: [3,4] }, + { area: '020 ' + this.character({ pool: '378' }), sections: [3,4] }, + { area: '023 ' + this.character({ pool: '89' }), sections: [3,4] }, + { area: '024 7', sections: [3,4] }, + { area: '028 ' + this.pick(['25','28','37','71','82','90','92','95']), sections: [2,4] }, + { area: '012' + this.pick(['04','08','54','76','97','98']) + ' ', sections: [5] }, + { area: '013' + this.pick(['63','64','84','86']) + ' ', sections: [5] }, + { area: '014' + this.pick(['04','20','60','61','80','88']) + ' ', sections: [5] }, + { area: '015' + this.pick(['24','27','62','66']) + ' ', sections: [5] }, + { area: '016' + this.pick(['06','29','35','47','59','95']) + ' ', sections: [5] }, + { area: '017' + this.pick(['26','44','50','68']) + ' ', sections: [5] }, + { area: '018' + this.pick(['27','37','84','97']) + ' ', sections: [5] }, + { area: '019' + this.pick(['00','05','35','46','49','63','95']) + ' ', sections: [5] } + ]); + phone = options.formatted ? ukNum(numPick) : ukNum(numPick).replace(' ', '', 'g'); + } else { + numPick = this.pick([ + { area: '07' + this.pick(['4','5','7','8','9']), sections: [2,6] }, + { area: '07624 ', sections: [6] } + ]); + phone = options.formatted ? ukNum(numPick) : ukNum(numPick).replace(' ', ''); + } + break; + case 'us': + var areacode = this.areacode(options).toString(); + var exchange = this.natural({ min: 2, max: 9 }).toString() + + this.natural({ min: 0, max: 9 }).toString() + + this.natural({ min: 0, max: 9 }).toString(); + var subscriber = this.natural({ min: 1000, max: 9999 }).toString(); // this could be random [0-9]{4} + phone = options.formatted ? areacode + ' ' + exchange + '-' + subscriber : areacode + exchange + subscriber; + } + return phone; + }; + + Chance.prototype.postal = function () { + // Postal District + var pd = this.character({pool: "XVTSRPNKLMHJGECBA"}); + // Forward Sortation Area (FSA) + var fsa = pd + this.natural({max: 9}) + this.character({alpha: true, casing: "upper"}); + // Local Delivery Unut (LDU) + var ldu = this.natural({max: 9}) + this.character({alpha: true, casing: "upper"}) + this.natural({max: 9}); + + return fsa + " " + ldu; + }; + + Chance.prototype.provinces = function () { + return this.get("provinces"); + }; + + Chance.prototype.province = function (options) { + return (options && options.full) ? + this.pick(this.provinces()).name : + this.pick(this.provinces()).abbreviation; + }; + + Chance.prototype.state = function (options) { + return (options && options.full) ? + this.pick(this.states(options)).name : + this.pick(this.states(options)).abbreviation; + }; + + Chance.prototype.states = function (options) { + options = initOptions(options); + + var states, + us_states_and_dc = this.get("us_states_and_dc"), + territories = this.get("territories"), + armed_forces = this.get("armed_forces"); + + states = us_states_and_dc; + + if (options.territories) { + states = states.concat(territories); + } + if (options.armed_forces) { + states = states.concat(armed_forces); + } + + return states; + }; + + Chance.prototype.street = function (options) { + options = initOptions(options); + + var street = this.word({syllables: 2}); + street = this.capitalize(street); + street += ' '; + street += options.short_suffix ? + this.street_suffix().abbreviation : + this.street_suffix().name; + return street; + }; + + Chance.prototype.street_suffix = function () { + return this.pick(this.street_suffixes()); + }; + + Chance.prototype.street_suffixes = function () { + // These are the most common suffixes. + return this.get("street_suffixes"); + }; + + // Note: only returning US zip codes, internationalization will be a whole + // other beast to tackle at some point. + Chance.prototype.zip = function (options) { + var zip = this.n(this.natural, 5, {max: 9}); + + if (options && options.plusfour === true) { + zip.push('-'); + zip = zip.concat(this.n(this.natural, 4, {max: 9})); + } + + return zip.join(""); + }; + + // -- End Location -- + + // -- Time + + Chance.prototype.ampm = function () { + return this.bool() ? 'am' : 'pm'; + }; + + Chance.prototype.date = function (options) { + var date_string, date; + + // If interval is specified we ignore preset + if(options && (options.min || options.max)) { + options = initOptions(options, { + american: true, + string: false + }); + var min = typeof options.min !== "undefined" ? options.min.getTime() : 1; + // 100,000,000 days measured relative to midnight at the beginning of 01 January, 1970 UTC. http://es5.github.io/#x15.9.1.1 + var max = typeof options.max !== "undefined" ? options.max.getTime() : 8640000000000000; + + date = new Date(this.natural({min: min, max: max})); + } else { + var m = this.month({raw: true}); + + options = initOptions(options, { + year: parseInt(this.year(), 10), + // Necessary to subtract 1 because Date() 0-indexes month but not day or year + // for some reason. + month: m.numeric - 1, + day: this.natural({min: 1, max: m.days}), + hour: this.hour(), + minute: this.minute(), + second: this.second(), + millisecond: this.millisecond(), + american: true, + string: false + }); + + date = new Date(options.year, options.month, options.day, options.hour, options.minute, options.second, options.millisecond); + } + + if (options.american) { + // Adding 1 to the month is necessary because Date() 0-indexes + // months but not day for some odd reason. + date_string = (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear(); + } else { + date_string = date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear(); + } + + return options.string ? date_string : date; + }; + + Chance.prototype.hammertime = function (options) { + return this.date(options).getTime(); + }; + + Chance.prototype.hour = function (options) { + options = initOptions(options, {min: 1, max: options && options.twentyfour ? 24 : 12}); + + testRange(options.min < 1, "Chance: Min cannot be less than 1."); + testRange(options.twentyfour && options.max > 24, "Chance: Max cannot be greater than 24 for twentyfour option."); + testRange(!options.twentyfour && options.max > 12, "Chance: Max cannot be greater than 12."); + testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); + + return this.natural({min: options.min, max: options.max}); + }; + + Chance.prototype.millisecond = function () { + return this.natural({max: 999}); + }; + + Chance.prototype.minute = Chance.prototype.second = function (options) { + options = initOptions(options, {min: 0, max: 59}); + + testRange(options.min < 0, "Chance: Min cannot be less than 0."); + testRange(options.max > 59, "Chance: Max cannot be greater than 59."); + testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); + + return this.natural({min: options.min, max: options.max}); + }; + + Chance.prototype.month = function (options) { + options = initOptions(options, {min: 1, max: 12}); + + testRange(options.min < 1, "Chance: Min cannot be less than 1."); + testRange(options.max > 12, "Chance: Max cannot be greater than 12."); + testRange(options.min > options.max, "Chance: Min cannot be greater than Max."); + + var month = this.pick(this.months().slice(options.min - 1, options.max)); + return options.raw ? month : month.name; + }; + + Chance.prototype.months = function () { + return this.get("months"); + }; + + Chance.prototype.second = function () { + return this.natural({max: 59}); + }; + + Chance.prototype.timestamp = function () { + return this.natural({min: 1, max: parseInt(new Date().getTime() / 1000, 10)}); + }; + + Chance.prototype.year = function (options) { + // Default to current year as min if none specified + options = initOptions(options, {min: new Date().getFullYear()}); + + // Default to one century after current year as max if none specified + options.max = (typeof options.max !== "undefined") ? options.max : options.min + 100; + + return this.natural(options).toString(); + }; + + // -- End Time + + // -- Finance -- + + Chance.prototype.cc = function (options) { + options = initOptions(options); + + var type, number, to_generate; + + type = (options.type) ? + this.cc_type({ name: options.type, raw: true }) : + this.cc_type({ raw: true }); + + number = type.prefix.split(""); + to_generate = type.length - type.prefix.length - 1; + + // Generates n - 1 digits + number = number.concat(this.n(this.integer, to_generate, {min: 0, max: 9})); + + // Generates the last digit according to Luhn algorithm + number.push(this.luhn_calculate(number.join(""))); + + return number.join(""); + }; + + Chance.prototype.cc_types = function () { + // http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29 + return this.get("cc_types"); + }; + + Chance.prototype.cc_type = function (options) { + options = initOptions(options); + var types = this.cc_types(), + type = null; + + if (options.name) { + for (var i = 0; i < types.length; i++) { + // Accept either name or short_name to specify card type + if (types[i].name === options.name || types[i].short_name === options.name) { + type = types[i]; + break; + } + } + if (type === null) { + throw new Error("Credit card type '" + options.name + "'' is not supported"); + } + } else { + type = this.pick(types); + } + + return options.raw ? type : type.name; + }; + + //return all world currency by ISO 4217 + Chance.prototype.currency_types = function () { + return this.get("currency_types"); + }; + + //return random world currency by ISO 4217 + Chance.prototype.currency = function () { + return this.pick(this.currency_types()); + }; + + //Return random correct currency exchange pair (e.g. EUR/USD) or array of currency code + Chance.prototype.currency_pair = function (returnAsString) { + var currencies = this.unique(this.currency, 2, { + comparator: function(arr, val) { + + return arr.reduce(function(acc, item) { + // If a match has been found, short circuit check and just return + return acc || (item.code === val.code); + }, false); + } + }); + + if (returnAsString) { + return currencies[0] + '/' + currencies[1]; + } else { + return currencies; + } + }; + + Chance.prototype.dollar = function (options) { + // By default, a somewhat more sane max for dollar than all available numbers + options = initOptions(options, {max : 10000, min : 0}); + + var dollar = this.floating({min: options.min, max: options.max, fixed: 2}).toString(), + cents = dollar.split('.')[1]; + + if (cents === undefined) { + dollar += '.00'; + } else if (cents.length < 2) { + dollar = dollar + '0'; + } + + if (dollar < 0) { + return '-$' + dollar.replace('-', ''); + } else { + return '$' + dollar; + } + }; + + Chance.prototype.exp = function (options) { + options = initOptions(options); + var exp = {}; + + exp.year = this.exp_year(); + + // If the year is this year, need to ensure month is greater than the + // current month or this expiration will not be valid + if (exp.year === (new Date().getFullYear())) { + exp.month = this.exp_month({future: true}); + } else { + exp.month = this.exp_month(); + } + + return options.raw ? exp : exp.month + '/' + exp.year; + }; + + Chance.prototype.exp_month = function (options) { + options = initOptions(options); + var month, month_int, + curMonth = new Date().getMonth(); + + if (options.future) { + do { + month = this.month({raw: true}).numeric; + month_int = parseInt(month, 10); + } while (month_int < curMonth); + } else { + month = this.month({raw: true}).numeric; + } + + return month; + }; + + Chance.prototype.exp_year = function () { + return this.year({max: new Date().getFullYear() + 10}); + }; + + // -- End Finance + + // -- Miscellaneous -- + + // Dice - For all the board game geeks out there, myself included ;) + function diceFn (range) { + return function () { + return this.natural(range); + }; + } + Chance.prototype.d4 = diceFn({min: 1, max: 4}); + Chance.prototype.d6 = diceFn({min: 1, max: 6}); + Chance.prototype.d8 = diceFn({min: 1, max: 8}); + Chance.prototype.d10 = diceFn({min: 1, max: 10}); + Chance.prototype.d12 = diceFn({min: 1, max: 12}); + Chance.prototype.d20 = diceFn({min: 1, max: 20}); + Chance.prototype.d30 = diceFn({min: 1, max: 30}); + Chance.prototype.d100 = diceFn({min: 1, max: 100}); + + Chance.prototype.rpg = function (thrown, options) { + options = initOptions(options); + if (thrown === null) { + throw new Error("A type of die roll must be included"); + } else { + var bits = thrown.toLowerCase().split("d"), + rolls = []; + + if (bits.length !== 2 || !parseInt(bits[0], 10) || !parseInt(bits[1], 10)) { + throw new Error("Invalid format provided. Please provide #d# where the first # is the number of dice to roll, the second # is the max of each die"); + } + for (var i = bits[0]; i > 0; i--) { + rolls[i - 1] = this.natural({min: 1, max: bits[1]}); + } + return (typeof options.sum !== 'undefined' && options.sum) ? rolls.reduce(function (p, c) { return p + c; }) : rolls; + } + }; + + // Guid + Chance.prototype.guid = function (options) { + options = initOptions(options, { version: 5 }); + + var guid_pool = "abcdef1234567890", + variant_pool = "ab89", + guid = this.string({ pool: guid_pool, length: 8 }) + '-' + + this.string({ pool: guid_pool, length: 4 }) + '-' + + // The Version + options.version + + this.string({ pool: guid_pool, length: 3 }) + '-' + + // The Variant + this.string({ pool: variant_pool, length: 1 }) + + this.string({ pool: guid_pool, length: 3 }) + '-' + + this.string({ pool: guid_pool, length: 12 }); + return guid; + }; + + // Hash + Chance.prototype.hash = function (options) { + options = initOptions(options, {length : 40, casing: 'lower'}); + var pool = options.casing === 'upper' ? HEX_POOL.toUpperCase() : HEX_POOL; + return this.string({pool: pool, length: options.length}); + }; + + Chance.prototype.luhn_check = function (num) { + var str = num.toString(); + var checkDigit = +str.substring(str.length - 1); + return checkDigit === this.luhn_calculate(+str.substring(0, str.length - 1)); + }; + + Chance.prototype.luhn_calculate = function (num) { + var digits = num.toString().split("").reverse(); + var sum = 0; + var digit; + + for (var i = 0, l = digits.length; l > i; ++i) { + digit = +digits[i]; + if (i % 2 === 0) { + digit *= 2; + if (digit > 9) { + digit -= 9; + } + } + sum += digit; + } + return (sum * 9) % 10; + }; + + + var data = { + + firstNames: { + "male": ["James", "John", "Robert", "Michael", "William", "David", "Richard", "Joseph", "Charles", "Thomas", "Christopher", "Daniel", "Matthew", "George", "Donald", "Anthony", "Paul", "Mark", "Edward", "Steven", "Kenneth", "Andrew", "Brian", "Joshua", "Kevin", "Ronald", "Timothy", "Jason", "Jeffrey", "Frank", "Gary", "Ryan", "Nicholas", "Eric", "Stephen", "Jacob", "Larry", "Jonathan", "Scott", "Raymond", "Justin", "Brandon", "Gregory", "Samuel", "Benjamin", "Patrick", "Jack", "Henry", "Walter", "Dennis", "Jerry", "Alexander", "Peter", "Tyler", "Douglas", "Harold", "Aaron", "Jose", "Adam", "Arthur", "Zachary", "Carl", "Nathan", "Albert", "Kyle", "Lawrence", "Joe", "Willie", "Gerald", "Roger", "Keith", "Jeremy", "Terry", "Harry", "Ralph", "Sean", "Jesse", "Roy", "Louis", "Billy", "Austin", "Bruce", "Eugene", "Christian", "Bryan", "Wayne", "Russell", "Howard", "Fred", "Ethan", "Jordan", "Philip", "Alan", "Juan", "Randy", "Vincent", "Bobby", "Dylan", "Johnny", "Phillip", "Victor", "Clarence", "Ernest", "Martin", "Craig", "Stanley", "Shawn", "Travis", "Bradley", "Leonard", "Earl", "Gabriel", "Jimmy", "Francis", "Todd", "Noah", "Danny", "Dale", "Cody", "Carlos", "Allen", "Frederick", "Logan", "Curtis", "Alex", "Joel", "Luis", "Norman", "Marvin", "Glenn", "Tony", "Nathaniel", "Rodney", "Melvin", "Alfred", "Steve", "Cameron", "Chad", "Edwin", "Caleb", "Evan", "Antonio", "Lee", "Herbert", "Jeffery", "Isaac", "Derek", "Ricky", "Marcus", "Theodore", "Elijah", "Luke", "Jesus", "Eddie", "Troy", "Mike", "Dustin", "Ray", "Adrian", "Bernard", "Leroy", "Angel", "Randall", "Wesley", "Ian", "Jared", "Mason", "Hunter", "Calvin", "Oscar", "Clifford", "Jay", "Shane", "Ronnie", "Barry", "Lucas", "Corey", "Manuel", "Leo", "Tommy", "Warren", "Jackson", "Isaiah", "Connor", "Don", "Dean", "Jon", "Julian", "Miguel", "Bill", "Lloyd", "Charlie", "Mitchell", "Leon", "Jerome", "Darrell", "Jeremiah", "Alvin", "Brett", "Seth", "Floyd", "Jim", "Blake", "Micheal", "Gordon", "Trevor", "Lewis", "Erik", "Edgar", "Vernon", "Devin", "Gavin", "Jayden", "Chris", "Clyde", "Tom", "Derrick", "Mario", "Brent", "Marc", "Herman", "Chase", "Dominic", "Ricardo", "Franklin", "Maurice", "Max", "Aiden", "Owen", "Lester", "Gilbert", "Elmer", "Gene", "Francisco", "Glen", "Cory", "Garrett", "Clayton", "Sam", "Jorge", "Chester", "Alejandro", "Jeff", "Harvey", "Milton", "Cole", "Ivan", "Andre", "Duane", "Landon"], + "female": ["Mary", "Emma", "Elizabeth", "Minnie", "Margaret", "Ida", "Alice", "Bertha", "Sarah", "Annie", "Clara", "Ella", "Florence", "Cora", "Martha", "Laura", "Nellie", "Grace", "Carrie", "Maude", "Mabel", "Bessie", "Jennie", "Gertrude", "Julia", "Hattie", "Edith", "Mattie", "Rose", "Catherine", "Lillian", "Ada", "Lillie", "Helen", "Jessie", "Louise", "Ethel", "Lula", "Myrtle", "Eva", "Frances", "Lena", "Lucy", "Edna", "Maggie", "Pearl", "Daisy", "Fannie", "Josephine", "Dora", "Rosa", "Katherine", "Agnes", "Marie", "Nora", "May", "Mamie", "Blanche", "Stella", "Ellen", "Nancy", "Effie", "Sallie", "Nettie", "Della", "Lizzie", "Flora", "Susie", "Maud", "Mae", "Etta", "Harriet", "Sadie", "Caroline", "Katie", "Lydia", "Elsie", "Kate", "Susan", "Mollie", "Alma", "Addie", "Georgia", "Eliza", "Lulu", "Nannie", "Lottie", "Amanda", "Belle", "Charlotte", "Rebecca", "Ruth", "Viola", "Olive", "Amelia", "Hannah", "Jane", "Virginia", "Emily", "Matilda", "Irene", "Kathryn", "Esther", "Willie", "Henrietta", "Ollie", "Amy", "Rachel", "Sara", "Estella", "Theresa", "Augusta", "Ora", "Pauline", "Josie", "Lola", "Sophia", "Leona", "Anne", "Mildred", "Ann", "Beulah", "Callie", "Lou", "Delia", "Eleanor", "Barbara", "Iva", "Louisa", "Maria", "Mayme", "Evelyn", "Estelle", "Nina", "Betty", "Marion", "Bettie", "Dorothy", "Luella", "Inez", "Lela", "Rosie", "Allie", "Millie", "Janie", "Cornelia", "Victoria", "Ruby", "Winifred", "Alta", "Celia", "Christine", "Beatrice", "Birdie", "Harriett", "Mable", "Myra", "Sophie", "Tillie", "Isabel", "Sylvia", "Carolyn", "Isabelle", "Leila", "Sally", "Ina", "Essie", "Bertie", "Nell", "Alberta", "Katharine", "Lora", "Rena", "Mina", "Rhoda", "Mathilda", "Abbie", "Eula", "Dollie", "Hettie", "Eunice", "Fanny", "Ola", "Lenora", "Adelaide", "Christina", "Lelia", "Nelle", "Sue", "Johanna", "Lilly", "Lucinda", "Minerva", "Lettie", "Roxie", "Cynthia", "Helena", "Hilda", "Hulda", "Bernice", "Genevieve", "Jean", "Cordelia", "Marian", "Francis", "Jeanette", "Adeline", "Gussie", "Leah", "Lois", "Lura", "Mittie", "Hallie", "Isabella", "Olga", "Phoebe", "Teresa", "Hester", "Lida", "Lina", "Winnie", "Claudia", "Marguerite", "Vera", "Cecelia", "Bess", "Emilie", "John", "Rosetta", "Verna", "Myrtie", "Cecilia", "Elva", "Olivia", "Ophelia", "Georgie", "Elnora", "Violet", "Adele", "Lily", "Linnie", "Loretta", "Madge", "Polly", "Virgie", "Eugenia", "Lucile", "Lucille", "Mabelle", "Rosalie"] + }, + + lastNames: ['Smith', 'Johnson', 'Williams', 'Jones', 'Brown', 'Davis', 'Miller', 'Wilson', 'Moore', 'Taylor', 'Anderson', 'Thomas', 'Jackson', 'White', 'Harris', 'Martin', 'Thompson', 'Garcia', 'Martinez', 'Robinson', 'Clark', 'Rodriguez', 'Lewis', 'Lee', 'Walker', 'Hall', 'Allen', 'Young', 'Hernandez', 'King', 'Wright', 'Lopez', 'Hill', 'Scott', 'Green', 'Adams', 'Baker', 'Gonzalez', 'Nelson', 'Carter', 'Mitchell', 'Perez', 'Roberts', 'Turner', 'Phillips', 'Campbell', 'Parker', 'Evans', 'Edwards', 'Collins', 'Stewart', 'Sanchez', 'Morris', 'Rogers', 'Reed', 'Cook', 'Morgan', 'Bell', 'Murphy', 'Bailey', 'Rivera', 'Cooper', 'Richardson', 'Cox', 'Howard', 'Ward', 'Torres', 'Peterson', 'Gray', 'Ramirez', 'James', 'Watson', 'Brooks', 'Kelly', 'Sanders', 'Price', 'Bennett', 'Wood', 'Barnes', 'Ross', 'Henderson', 'Coleman', 'Jenkins', 'Perry', 'Powell', 'Long', 'Patterson', 'Hughes', 'Flores', 'Washington', 'Butler', 'Simmons', 'Foster', 'Gonzales', 'Bryant', 'Alexander', 'Russell', 'Griffin', 'Diaz', 'Hayes', 'Myers', 'Ford', 'Hamilton', 'Graham', 'Sullivan', 'Wallace', 'Woods', 'Cole', 'West', 'Jordan', 'Owens', 'Reynolds', 'Fisher', 'Ellis', 'Harrison', 'Gibson', 'McDonald', 'Cruz', 'Marshall', 'Ortiz', 'Gomez', 'Murray', 'Freeman', 'Wells', 'Webb', 'Simpson', 'Stevens', 'Tucker', 'Porter', 'Hunter', 'Hicks', 'Crawford', 'Henry', 'Boyd', 'Mason', 'Morales', 'Kennedy', 'Warren', 'Dixon', 'Ramos', 'Reyes', 'Burns', 'Gordon', 'Shaw', 'Holmes', 'Rice', 'Robertson', 'Hunt', 'Black', 'Daniels', 'Palmer', 'Mills', 'Nichols', 'Grant', 'Knight', 'Ferguson', 'Rose', 'Stone', 'Hawkins', 'Dunn', 'Perkins', 'Hudson', 'Spencer', 'Gardner', 'Stephens', 'Payne', 'Pierce', 'Berry', 'Matthews', 'Arnold', 'Wagner', 'Willis', 'Ray', 'Watkins', 'Olson', 'Carroll', 'Duncan', 'Snyder', 'Hart', 'Cunningham', 'Bradley', 'Lane', 'Andrews', 'Ruiz', 'Harper', 'Fox', 'Riley', 'Armstrong', 'Carpenter', 'Weaver', 'Greene', 'Lawrence', 'Elliott', 'Chavez', 'Sims', 'Austin', 'Peters', 'Kelley', 'Franklin', 'Lawson', 'Fields', 'Gutierrez', 'Ryan', 'Schmidt', 'Carr', 'Vasquez', 'Castillo', 'Wheeler', 'Chapman', 'Oliver', 'Montgomery', 'Richards', 'Williamson', 'Johnston', 'Banks', 'Meyer', 'Bishop', 'McCoy', 'Howell', 'Alvarez', 'Morrison', 'Hansen', 'Fernandez', 'Garza', 'Harvey', 'Little', 'Burton', 'Stanley', 'Nguyen', 'George', 'Jacobs', 'Reid', 'Kim', 'Fuller', 'Lynch', 'Dean', 'Gilbert', 'Garrett', 'Romero', 'Welch', 'Larson', 'Frazier', 'Burke', 'Hanson', 'Day', 'Mendoza', 'Moreno', 'Bowman', 'Medina', 'Fowler', 'Brewer', 'Hoffman', 'Carlson', 'Silva', 'Pearson', 'Holland', 'Douglas', 'Fleming', 'Jensen', 'Vargas', 'Byrd', 'Davidson', 'Hopkins', 'May', 'Terry', 'Herrera', 'Wade', 'Soto', 'Walters', 'Curtis', 'Neal', 'Caldwell', 'Lowe', 'Jennings', 'Barnett', 'Graves', 'Jimenez', 'Horton', 'Shelton', 'Barrett', 'Obrien', 'Castro', 'Sutton', 'Gregory', 'McKinney', 'Lucas', 'Miles', 'Craig', 'Rodriquez', 'Chambers', 'Holt', 'Lambert', 'Fletcher', 'Watts', 'Bates', 'Hale', 'Rhodes', 'Pena', 'Beck', 'Newman', 'Haynes', 'McDaniel', 'Mendez', 'Bush', 'Vaughn', 'Parks', 'Dawson', 'Santiago', 'Norris', 'Hardy', 'Love', 'Steele', 'Curry', 'Powers', 'Schultz', 'Barker', 'Guzman', 'Page', 'Munoz', 'Ball', 'Keller', 'Chandler', 'Weber', 'Leonard', 'Walsh', 'Lyons', 'Ramsey', 'Wolfe', 'Schneider', 'Mullins', 'Benson', 'Sharp', 'Bowen', 'Daniel', 'Barber', 'Cummings', 'Hines', 'Baldwin', 'Griffith', 'Valdez', 'Hubbard', 'Salazar', 'Reeves', 'Warner', 'Stevenson', 'Burgess', 'Santos', 'Tate', 'Cross', 'Garner', 'Mann', 'Mack', 'Moss', 'Thornton', 'Dennis', 'McGee', 'Farmer', 'Delgado', 'Aguilar', 'Vega', 'Glover', 'Manning', 'Cohen', 'Harmon', 'Rodgers', 'Robbins', 'Newton', 'Todd', 'Blair', 'Higgins', 'Ingram', 'Reese', 'Cannon', 'Strickland', 'Townsend', 'Potter', 'Goodwin', 'Walton', 'Rowe', 'Hampton', 'Ortega', 'Patton', 'Swanson', 'Joseph', 'Francis', 'Goodman', 'Maldonado', 'Yates', 'Becker', 'Erickson', 'Hodges', 'Rios', 'Conner', 'Adkins', 'Webster', 'Norman', 'Malone', 'Hammond', 'Flowers', 'Cobb', 'Moody', 'Quinn', 'Blake', 'Maxwell', 'Pope', 'Floyd', 'Osborne', 'Paul', 'McCarthy', 'Guerrero', 'Lindsey', 'Estrada', 'Sandoval', 'Gibbs', 'Tyler', 'Gross', 'Fitzgerald', 'Stokes', 'Doyle', 'Sherman', 'Saunders', 'Wise', 'Colon', 'Gill', 'Alvarado', 'Greer', 'Padilla', 'Simon', 'Waters', 'Nunez', 'Ballard', 'Schwartz', 'McBride', 'Houston', 'Christensen', 'Klein', 'Pratt', 'Briggs', 'Parsons', 'McLaughlin', 'Zimmerman', 'French', 'Buchanan', 'Moran', 'Copeland', 'Roy', 'Pittman', 'Brady', 'McCormick', 'Holloway', 'Brock', 'Poole', 'Frank', 'Logan', 'Owen', 'Bass', 'Marsh', 'Drake', 'Wong', 'Jefferson', 'Park', 'Morton', 'Abbott', 'Sparks', 'Patrick', 'Norton', 'Huff', 'Clayton', 'Massey', 'Lloyd', 'Figueroa', 'Carson', 'Bowers', 'Roberson', 'Barton', 'Tran', 'Lamb', 'Harrington', 'Casey', 'Boone', 'Cortez', 'Clarke', 'Mathis', 'Singleton', 'Wilkins', 'Cain', 'Bryan', 'Underwood', 'Hogan', 'McKenzie', 'Collier', 'Luna', 'Phelps', 'McGuire', 'Allison', 'Bridges', 'Wilkerson', 'Nash', 'Summers', 'Atkins'], + + // Data taken from https://github.com/umpirsky/country-list/blob/master/country/cldr/en_US/country.json + countries: [{"name":"Afghanistan","abbreviation":"AF"},{"name":"Albania","abbreviation":"AL"},{"name":"Algeria","abbreviation":"DZ"},{"name":"American Samoa","abbreviation":"AS"},{"name":"Andorra","abbreviation":"AD"},{"name":"Angola","abbreviation":"AO"},{"name":"Anguilla","abbreviation":"AI"},{"name":"Antarctica","abbreviation":"AQ"},{"name":"Antigua and Barbuda","abbreviation":"AG"},{"name":"Argentina","abbreviation":"AR"},{"name":"Armenia","abbreviation":"AM"},{"name":"Aruba","abbreviation":"AW"},{"name":"Australia","abbreviation":"AU"},{"name":"Austria","abbreviation":"AT"},{"name":"Azerbaijan","abbreviation":"AZ"},{"name":"Bahamas","abbreviation":"BS"},{"name":"Bahrain","abbreviation":"BH"},{"name":"Bangladesh","abbreviation":"BD"},{"name":"Barbados","abbreviation":"BB"},{"name":"Belarus","abbreviation":"BY"},{"name":"Belgium","abbreviation":"BE"},{"name":"Belize","abbreviation":"BZ"},{"name":"Benin","abbreviation":"BJ"},{"name":"Bermuda","abbreviation":"BM"},{"name":"Bhutan","abbreviation":"BT"},{"name":"Bolivia","abbreviation":"BO"},{"name":"Bosnia and Herzegovina","abbreviation":"BA"},{"name":"Botswana","abbreviation":"BW"},{"name":"Bouvet Island","abbreviation":"BV"},{"name":"Brazil","abbreviation":"BR"},{"name":"British Antarctic Territory","abbreviation":"BQ"},{"name":"British Indian Ocean Territory","abbreviation":"IO"},{"name":"British Virgin Islands","abbreviation":"VG"},{"name":"Brunei","abbreviation":"BN"},{"name":"Bulgaria","abbreviation":"BG"},{"name":"Burkina Faso","abbreviation":"BF"},{"name":"Burundi","abbreviation":"BI"},{"name":"Cambodia","abbreviation":"KH"},{"name":"Cameroon","abbreviation":"CM"},{"name":"Canada","abbreviation":"CA"},{"name":"Canton and Enderbury Islands","abbreviation":"CT"},{"name":"Cape Verde","abbreviation":"CV"},{"name":"Cayman Islands","abbreviation":"KY"},{"name":"Central African Republic","abbreviation":"CF"},{"name":"Chad","abbreviation":"TD"},{"name":"Chile","abbreviation":"CL"},{"name":"China","abbreviation":"CN"},{"name":"Christmas Island","abbreviation":"CX"},{"name":"Cocos [Keeling] Islands","abbreviation":"CC"},{"name":"Colombia","abbreviation":"CO"},{"name":"Comoros","abbreviation":"KM"},{"name":"Congo - Brazzaville","abbreviation":"CG"},{"name":"Congo - Kinshasa","abbreviation":"CD"},{"name":"Cook Islands","abbreviation":"CK"},{"name":"Costa Rica","abbreviation":"CR"},{"name":"Croatia","abbreviation":"HR"},{"name":"Cuba","abbreviation":"CU"},{"name":"Cyprus","abbreviation":"CY"},{"name":"Czech Republic","abbreviation":"CZ"},{"name":"Côte d’Ivoire","abbreviation":"CI"},{"name":"Denmark","abbreviation":"DK"},{"name":"Djibouti","abbreviation":"DJ"},{"name":"Dominica","abbreviation":"DM"},{"name":"Dominican Republic","abbreviation":"DO"},{"name":"Dronning Maud Land","abbreviation":"NQ"},{"name":"East Germany","abbreviation":"DD"},{"name":"Ecuador","abbreviation":"EC"},{"name":"Egypt","abbreviation":"EG"},{"name":"El Salvador","abbreviation":"SV"},{"name":"Equatorial Guinea","abbreviation":"GQ"},{"name":"Eritrea","abbreviation":"ER"},{"name":"Estonia","abbreviation":"EE"},{"name":"Ethiopia","abbreviation":"ET"},{"name":"Falkland Islands","abbreviation":"FK"},{"name":"Faroe Islands","abbreviation":"FO"},{"name":"Fiji","abbreviation":"FJ"},{"name":"Finland","abbreviation":"FI"},{"name":"France","abbreviation":"FR"},{"name":"French Guiana","abbreviation":"GF"},{"name":"French Polynesia","abbreviation":"PF"},{"name":"French Southern Territories","abbreviation":"TF"},{"name":"French Southern and Antarctic Territories","abbreviation":"FQ"},{"name":"Gabon","abbreviation":"GA"},{"name":"Gambia","abbreviation":"GM"},{"name":"Georgia","abbreviation":"GE"},{"name":"Germany","abbreviation":"DE"},{"name":"Ghana","abbreviation":"GH"},{"name":"Gibraltar","abbreviation":"GI"},{"name":"Greece","abbreviation":"GR"},{"name":"Greenland","abbreviation":"GL"},{"name":"Grenada","abbreviation":"GD"},{"name":"Guadeloupe","abbreviation":"GP"},{"name":"Guam","abbreviation":"GU"},{"name":"Guatemala","abbreviation":"GT"},{"name":"Guernsey","abbreviation":"GG"},{"name":"Guinea","abbreviation":"GN"},{"name":"Guinea-Bissau","abbreviation":"GW"},{"name":"Guyana","abbreviation":"GY"},{"name":"Haiti","abbreviation":"HT"},{"name":"Heard Island and McDonald Islands","abbreviation":"HM"},{"name":"Honduras","abbreviation":"HN"},{"name":"Hong Kong SAR China","abbreviation":"HK"},{"name":"Hungary","abbreviation":"HU"},{"name":"Iceland","abbreviation":"IS"},{"name":"India","abbreviation":"IN"},{"name":"Indonesia","abbreviation":"ID"},{"name":"Iran","abbreviation":"IR"},{"name":"Iraq","abbreviation":"IQ"},{"name":"Ireland","abbreviation":"IE"},{"name":"Isle of Man","abbreviation":"IM"},{"name":"Israel","abbreviation":"IL"},{"name":"Italy","abbreviation":"IT"},{"name":"Jamaica","abbreviation":"JM"},{"name":"Japan","abbreviation":"JP"},{"name":"Jersey","abbreviation":"JE"},{"name":"Johnston Island","abbreviation":"JT"},{"name":"Jordan","abbreviation":"JO"},{"name":"Kazakhstan","abbreviation":"KZ"},{"name":"Kenya","abbreviation":"KE"},{"name":"Kiribati","abbreviation":"KI"},{"name":"Kuwait","abbreviation":"KW"},{"name":"Kyrgyzstan","abbreviation":"KG"},{"name":"Laos","abbreviation":"LA"},{"name":"Latvia","abbreviation":"LV"},{"name":"Lebanon","abbreviation":"LB"},{"name":"Lesotho","abbreviation":"LS"},{"name":"Liberia","abbreviation":"LR"},{"name":"Libya","abbreviation":"LY"},{"name":"Liechtenstein","abbreviation":"LI"},{"name":"Lithuania","abbreviation":"LT"},{"name":"Luxembourg","abbreviation":"LU"},{"name":"Macau SAR China","abbreviation":"MO"},{"name":"Macedonia","abbreviation":"MK"},{"name":"Madagascar","abbreviation":"MG"},{"name":"Malawi","abbreviation":"MW"},{"name":"Malaysia","abbreviation":"MY"},{"name":"Maldives","abbreviation":"MV"},{"name":"Mali","abbreviation":"ML"},{"name":"Malta","abbreviation":"MT"},{"name":"Marshall Islands","abbreviation":"MH"},{"name":"Martinique","abbreviation":"MQ"},{"name":"Mauritania","abbreviation":"MR"},{"name":"Mauritius","abbreviation":"MU"},{"name":"Mayotte","abbreviation":"YT"},{"name":"Metropolitan France","abbreviation":"FX"},{"name":"Mexico","abbreviation":"MX"},{"name":"Micronesia","abbreviation":"FM"},{"name":"Midway Islands","abbreviation":"MI"},{"name":"Moldova","abbreviation":"MD"},{"name":"Monaco","abbreviation":"MC"},{"name":"Mongolia","abbreviation":"MN"},{"name":"Montenegro","abbreviation":"ME"},{"name":"Montserrat","abbreviation":"MS"},{"name":"Morocco","abbreviation":"MA"},{"name":"Mozambique","abbreviation":"MZ"},{"name":"Myanmar [Burma]","abbreviation":"MM"},{"name":"Namibia","abbreviation":"NA"},{"name":"Nauru","abbreviation":"NR"},{"name":"Nepal","abbreviation":"NP"},{"name":"Netherlands","abbreviation":"NL"},{"name":"Netherlands Antilles","abbreviation":"AN"},{"name":"Neutral Zone","abbreviation":"NT"},{"name":"New Caledonia","abbreviation":"NC"},{"name":"New Zealand","abbreviation":"NZ"},{"name":"Nicaragua","abbreviation":"NI"},{"name":"Niger","abbreviation":"NE"},{"name":"Nigeria","abbreviation":"NG"},{"name":"Niue","abbreviation":"NU"},{"name":"Norfolk Island","abbreviation":"NF"},{"name":"North Korea","abbreviation":"KP"},{"name":"North Vietnam","abbreviation":"VD"},{"name":"Northern Mariana Islands","abbreviation":"MP"},{"name":"Norway","abbreviation":"NO"},{"name":"Oman","abbreviation":"OM"},{"name":"Pacific Islands Trust Territory","abbreviation":"PC"},{"name":"Pakistan","abbreviation":"PK"},{"name":"Palau","abbreviation":"PW"},{"name":"Palestinian Territories","abbreviation":"PS"},{"name":"Panama","abbreviation":"PA"},{"name":"Panama Canal Zone","abbreviation":"PZ"},{"name":"Papua New Guinea","abbreviation":"PG"},{"name":"Paraguay","abbreviation":"PY"},{"name":"People's Democratic Republic of Yemen","abbreviation":"YD"},{"name":"Peru","abbreviation":"PE"},{"name":"Philippines","abbreviation":"PH"},{"name":"Pitcairn Islands","abbreviation":"PN"},{"name":"Poland","abbreviation":"PL"},{"name":"Portugal","abbreviation":"PT"},{"name":"Puerto Rico","abbreviation":"PR"},{"name":"Qatar","abbreviation":"QA"},{"name":"Romania","abbreviation":"RO"},{"name":"Russia","abbreviation":"RU"},{"name":"Rwanda","abbreviation":"RW"},{"name":"Réunion","abbreviation":"RE"},{"name":"Saint Barthélemy","abbreviation":"BL"},{"name":"Saint Helena","abbreviation":"SH"},{"name":"Saint Kitts and Nevis","abbreviation":"KN"},{"name":"Saint Lucia","abbreviation":"LC"},{"name":"Saint Martin","abbreviation":"MF"},{"name":"Saint Pierre and Miquelon","abbreviation":"PM"},{"name":"Saint Vincent and the Grenadines","abbreviation":"VC"},{"name":"Samoa","abbreviation":"WS"},{"name":"San Marino","abbreviation":"SM"},{"name":"Saudi Arabia","abbreviation":"SA"},{"name":"Senegal","abbreviation":"SN"},{"name":"Serbia","abbreviation":"RS"},{"name":"Serbia and Montenegro","abbreviation":"CS"},{"name":"Seychelles","abbreviation":"SC"},{"name":"Sierra Leone","abbreviation":"SL"},{"name":"Singapore","abbreviation":"SG"},{"name":"Slovakia","abbreviation":"SK"},{"name":"Slovenia","abbreviation":"SI"},{"name":"Solomon Islands","abbreviation":"SB"},{"name":"Somalia","abbreviation":"SO"},{"name":"South Africa","abbreviation":"ZA"},{"name":"South Georgia and the South Sandwich Islands","abbreviation":"GS"},{"name":"South Korea","abbreviation":"KR"},{"name":"Spain","abbreviation":"ES"},{"name":"Sri Lanka","abbreviation":"LK"},{"name":"Sudan","abbreviation":"SD"},{"name":"Suriname","abbreviation":"SR"},{"name":"Svalbard and Jan Mayen","abbreviation":"SJ"},{"name":"Swaziland","abbreviation":"SZ"},{"name":"Sweden","abbreviation":"SE"},{"name":"Switzerland","abbreviation":"CH"},{"name":"Syria","abbreviation":"SY"},{"name":"São Tomé and Príncipe","abbreviation":"ST"},{"name":"Taiwan","abbreviation":"TW"},{"name":"Tajikistan","abbreviation":"TJ"},{"name":"Tanzania","abbreviation":"TZ"},{"name":"Thailand","abbreviation":"TH"},{"name":"Timor-Leste","abbreviation":"TL"},{"name":"Togo","abbreviation":"TG"},{"name":"Tokelau","abbreviation":"TK"},{"name":"Tonga","abbreviation":"TO"},{"name":"Trinidad and Tobago","abbreviation":"TT"},{"name":"Tunisia","abbreviation":"TN"},{"name":"Turkey","abbreviation":"TR"},{"name":"Turkmenistan","abbreviation":"TM"},{"name":"Turks and Caicos Islands","abbreviation":"TC"},{"name":"Tuvalu","abbreviation":"TV"},{"name":"U.S. Minor Outlying Islands","abbreviation":"UM"},{"name":"U.S. Miscellaneous Pacific Islands","abbreviation":"PU"},{"name":"U.S. Virgin Islands","abbreviation":"VI"},{"name":"Uganda","abbreviation":"UG"},{"name":"Ukraine","abbreviation":"UA"},{"name":"Union of Soviet Socialist Republics","abbreviation":"SU"},{"name":"United Arab Emirates","abbreviation":"AE"},{"name":"United Kingdom","abbreviation":"GB"},{"name":"United States","abbreviation":"US"},{"name":"Unknown or Invalid Region","abbreviation":"ZZ"},{"name":"Uruguay","abbreviation":"UY"},{"name":"Uzbekistan","abbreviation":"UZ"},{"name":"Vanuatu","abbreviation":"VU"},{"name":"Vatican City","abbreviation":"VA"},{"name":"Venezuela","abbreviation":"VE"},{"name":"Vietnam","abbreviation":"VN"},{"name":"Wake Island","abbreviation":"WK"},{"name":"Wallis and Futuna","abbreviation":"WF"},{"name":"Western Sahara","abbreviation":"EH"},{"name":"Yemen","abbreviation":"YE"},{"name":"Zambia","abbreviation":"ZM"},{"name":"Zimbabwe","abbreviation":"ZW"},{"name":"Åland Islands","abbreviation":"AX"}], + + provinces: [ + {name: 'Alberta', abbreviation: 'AB'}, + {name: 'British Columbia', abbreviation: 'BC'}, + {name: 'Manitoba', abbreviation: 'MB'}, + {name: 'New Brunswick', abbreviation: 'NB'}, + {name: 'Newfoundland and Labrador', abbreviation: 'NL'}, + {name: 'Nova Scotia', abbreviation: 'NS'}, + {name: 'Ontario', abbreviation: 'ON'}, + {name: 'Prince Edward Island', abbreviation: 'PE'}, + {name: 'Quebec', abbreviation: 'QC'}, + {name: 'Saskatchewan', abbreviation: 'SK'}, + + // The case could be made that the following are not actually provinces + // since they are technically considered "territories" however they all + // look the same on an envelope! + {name: 'Northwest Territories', abbreviation: 'NT'}, + {name: 'Nunavut', abbreviation: 'NU'}, + {name: 'Yukon', abbreviation: 'YT'} + ], + + us_states_and_dc: [ + {name: 'Alabama', abbreviation: 'AL'}, + {name: 'Alaska', abbreviation: 'AK'}, + {name: 'Arizona', abbreviation: 'AZ'}, + {name: 'Arkansas', abbreviation: 'AR'}, + {name: 'California', abbreviation: 'CA'}, + {name: 'Colorado', abbreviation: 'CO'}, + {name: 'Connecticut', abbreviation: 'CT'}, + {name: 'Delaware', abbreviation: 'DE'}, + {name: 'District of Columbia', abbreviation: 'DC'}, + {name: 'Florida', abbreviation: 'FL'}, + {name: 'Georgia', abbreviation: 'GA'}, + {name: 'Hawaii', abbreviation: 'HI'}, + {name: 'Idaho', abbreviation: 'ID'}, + {name: 'Illinois', abbreviation: 'IL'}, + {name: 'Indiana', abbreviation: 'IN'}, + {name: 'Iowa', abbreviation: 'IA'}, + {name: 'Kansas', abbreviation: 'KS'}, + {name: 'Kentucky', abbreviation: 'KY'}, + {name: 'Louisiana', abbreviation: 'LA'}, + {name: 'Maine', abbreviation: 'ME'}, + {name: 'Maryland', abbreviation: 'MD'}, + {name: 'Massachusetts', abbreviation: 'MA'}, + {name: 'Michigan', abbreviation: 'MI'}, + {name: 'Minnesota', abbreviation: 'MN'}, + {name: 'Mississippi', abbreviation: 'MS'}, + {name: 'Missouri', abbreviation: 'MO'}, + {name: 'Montana', abbreviation: 'MT'}, + {name: 'Nebraska', abbreviation: 'NE'}, + {name: 'Nevada', abbreviation: 'NV'}, + {name: 'New Hampshire', abbreviation: 'NH'}, + {name: 'New Jersey', abbreviation: 'NJ'}, + {name: 'New Mexico', abbreviation: 'NM'}, + {name: 'New York', abbreviation: 'NY'}, + {name: 'North Carolina', abbreviation: 'NC'}, + {name: 'North Dakota', abbreviation: 'ND'}, + {name: 'Ohio', abbreviation: 'OH'}, + {name: 'Oklahoma', abbreviation: 'OK'}, + {name: 'Oregon', abbreviation: 'OR'}, + {name: 'Pennsylvania', abbreviation: 'PA'}, + {name: 'Rhode Island', abbreviation: 'RI'}, + {name: 'South Carolina', abbreviation: 'SC'}, + {name: 'South Dakota', abbreviation: 'SD'}, + {name: 'Tennessee', abbreviation: 'TN'}, + {name: 'Texas', abbreviation: 'TX'}, + {name: 'Utah', abbreviation: 'UT'}, + {name: 'Vermont', abbreviation: 'VT'}, + {name: 'Virginia', abbreviation: 'VA'}, + {name: 'Washington', abbreviation: 'WA'}, + {name: 'West Virginia', abbreviation: 'WV'}, + {name: 'Wisconsin', abbreviation: 'WI'}, + {name: 'Wyoming', abbreviation: 'WY'} + ], + + territories: [ + {name: 'American Samoa', abbreviation: 'AS'}, + {name: 'Federated States of Micronesia', abbreviation: 'FM'}, + {name: 'Guam', abbreviation: 'GU'}, + {name: 'Marshall Islands', abbreviation: 'MH'}, + {name: 'Northern Mariana Islands', abbreviation: 'MP'}, + {name: 'Puerto Rico', abbreviation: 'PR'}, + {name: 'Virgin Islands, U.S.', abbreviation: 'VI'} + ], + + armed_forces: [ + {name: 'Armed Forces Europe', abbreviation: 'AE'}, + {name: 'Armed Forces Pacific', abbreviation: 'AP'}, + {name: 'Armed Forces the Americas', abbreviation: 'AA'} + ], + + street_suffixes: [ + {name: 'Avenue', abbreviation: 'Ave'}, + {name: 'Boulevard', abbreviation: 'Blvd'}, + {name: 'Center', abbreviation: 'Ctr'}, + {name: 'Circle', abbreviation: 'Cir'}, + {name: 'Court', abbreviation: 'Ct'}, + {name: 'Drive', abbreviation: 'Dr'}, + {name: 'Extension', abbreviation: 'Ext'}, + {name: 'Glen', abbreviation: 'Gln'}, + {name: 'Grove', abbreviation: 'Grv'}, + {name: 'Heights', abbreviation: 'Hts'}, + {name: 'Highway', abbreviation: 'Hwy'}, + {name: 'Junction', abbreviation: 'Jct'}, + {name: 'Key', abbreviation: 'Key'}, + {name: 'Lane', abbreviation: 'Ln'}, + {name: 'Loop', abbreviation: 'Loop'}, + {name: 'Manor', abbreviation: 'Mnr'}, + {name: 'Mill', abbreviation: 'Mill'}, + {name: 'Park', abbreviation: 'Park'}, + {name: 'Parkway', abbreviation: 'Pkwy'}, + {name: 'Pass', abbreviation: 'Pass'}, + {name: 'Path', abbreviation: 'Path'}, + {name: 'Pike', abbreviation: 'Pike'}, + {name: 'Place', abbreviation: 'Pl'}, + {name: 'Plaza', abbreviation: 'Plz'}, + {name: 'Point', abbreviation: 'Pt'}, + {name: 'Ridge', abbreviation: 'Rdg'}, + {name: 'River', abbreviation: 'Riv'}, + {name: 'Road', abbreviation: 'Rd'}, + {name: 'Square', abbreviation: 'Sq'}, + {name: 'Street', abbreviation: 'St'}, + {name: 'Terrace', abbreviation: 'Ter'}, + {name: 'Trail', abbreviation: 'Trl'}, + {name: 'Turnpike', abbreviation: 'Tpke'}, + {name: 'View', abbreviation: 'Vw'}, + {name: 'Way', abbreviation: 'Way'} + ], + + months: [ + {name: 'January', short_name: 'Jan', numeric: '01', days: 31}, + // Not messing with leap years... + {name: 'February', short_name: 'Feb', numeric: '02', days: 28}, + {name: 'March', short_name: 'Mar', numeric: '03', days: 31}, + {name: 'April', short_name: 'Apr', numeric: '04', days: 30}, + {name: 'May', short_name: 'May', numeric: '05', days: 31}, + {name: 'June', short_name: 'Jun', numeric: '06', days: 30}, + {name: 'July', short_name: 'Jul', numeric: '07', days: 31}, + {name: 'August', short_name: 'Aug', numeric: '08', days: 31}, + {name: 'September', short_name: 'Sep', numeric: '09', days: 30}, + {name: 'October', short_name: 'Oct', numeric: '10', days: 31}, + {name: 'November', short_name: 'Nov', numeric: '11', days: 30}, + {name: 'December', short_name: 'Dec', numeric: '12', days: 31} + ], + + // http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29 + cc_types: [ + {name: "American Express", short_name: 'amex', prefix: '34', length: 15}, + {name: "Bankcard", short_name: 'bankcard', prefix: '5610', length: 16}, + {name: "China UnionPay", short_name: 'chinaunion', prefix: '62', length: 16}, + {name: "Diners Club Carte Blanche", short_name: 'dccarte', prefix: '300', length: 14}, + {name: "Diners Club enRoute", short_name: 'dcenroute', prefix: '2014', length: 15}, + {name: "Diners Club International", short_name: 'dcintl', prefix: '36', length: 14}, + {name: "Diners Club United States & Canada", short_name: 'dcusc', prefix: '54', length: 16}, + {name: "Discover Card", short_name: 'discover', prefix: '6011', length: 16}, + {name: "InstaPayment", short_name: 'instapay', prefix: '637', length: 16}, + {name: "JCB", short_name: 'jcb', prefix: '3528', length: 16}, + {name: "Laser", short_name: 'laser', prefix: '6304', length: 16}, + {name: "Maestro", short_name: 'maestro', prefix: '5018', length: 16}, + {name: "Mastercard", short_name: 'mc', prefix: '51', length: 16}, + {name: "Solo", short_name: 'solo', prefix: '6334', length: 16}, + {name: "Switch", short_name: 'switch', prefix: '4903', length: 16}, + {name: "Visa", short_name: 'visa', prefix: '4', length: 16}, + {name: "Visa Electron", short_name: 'electron', prefix: '4026', length: 16} + ], + + //return all world currency by ISO 4217 + currency_types: [ + {'code' : 'AED', 'name' : 'United Arab Emirates Dirham'}, + {'code' : 'AFN', 'name' : 'Afghanistan Afghani'}, + {'code' : 'ALL', 'name' : 'Albania Lek'}, + {'code' : 'AMD', 'name' : 'Armenia Dram'}, + {'code' : 'ANG', 'name' : 'Netherlands Antilles Guilder'}, + {'code' : 'AOA', 'name' : 'Angola Kwanza'}, + {'code' : 'ARS', 'name' : 'Argentina Peso'}, + {'code' : 'AUD', 'name' : 'Australia Dollar'}, + {'code' : 'AWG', 'name' : 'Aruba Guilder'}, + {'code' : 'AZN', 'name' : 'Azerbaijan New Manat'}, + {'code' : 'BAM', 'name' : 'Bosnia and Herzegovina Convertible Marka'}, + {'code' : 'BBD', 'name' : 'Barbados Dollar'}, + {'code' : 'BDT', 'name' : 'Bangladesh Taka'}, + {'code' : 'BGN', 'name' : 'Bulgaria Lev'}, + {'code' : 'BHD', 'name' : 'Bahrain Dinar'}, + {'code' : 'BIF', 'name' : 'Burundi Franc'}, + {'code' : 'BMD', 'name' : 'Bermuda Dollar'}, + {'code' : 'BND', 'name' : 'Brunei Darussalam Dollar'}, + {'code' : 'BOB', 'name' : 'Bolivia Boliviano'}, + {'code' : 'BRL', 'name' : 'Brazil Real'}, + {'code' : 'BSD', 'name' : 'Bahamas Dollar'}, + {'code' : 'BTN', 'name' : 'Bhutan Ngultrum'}, + {'code' : 'BWP', 'name' : 'Botswana Pula'}, + {'code' : 'BYR', 'name' : 'Belarus Ruble'}, + {'code' : 'BZD', 'name' : 'Belize Dollar'}, + {'code' : 'CAD', 'name' : 'Canada Dollar'}, + {'code' : 'CDF', 'name' : 'Congo/Kinshasa Franc'}, + {'code' : 'CHF', 'name' : 'Switzerland Franc'}, + {'code' : 'CLP', 'name' : 'Chile Peso'}, + {'code' : 'CNY', 'name' : 'China Yuan Renminbi'}, + {'code' : 'COP', 'name' : 'Colombia Peso'}, + {'code' : 'CRC', 'name' : 'Costa Rica Colon'}, + {'code' : 'CUC', 'name' : 'Cuba Convertible Peso'}, + {'code' : 'CUP', 'name' : 'Cuba Peso'}, + {'code' : 'CVE', 'name' : 'Cape Verde Escudo'}, + {'code' : 'CZK', 'name' : 'Czech Republic Koruna'}, + {'code' : 'DJF', 'name' : 'Djibouti Franc'}, + {'code' : 'DKK', 'name' : 'Denmark Krone'}, + {'code' : 'DOP', 'name' : 'Dominican Republic Peso'}, + {'code' : 'DZD', 'name' : 'Algeria Dinar'}, + {'code' : 'EGP', 'name' : 'Egypt Pound'}, + {'code' : 'ERN', 'name' : 'Eritrea Nakfa'}, + {'code' : 'ETB', 'name' : 'Ethiopia Birr'}, + {'code' : 'EUR', 'name' : 'Euro Member Countries'}, + {'code' : 'FJD', 'name' : 'Fiji Dollar'}, + {'code' : 'FKP', 'name' : 'Falkland Islands (Malvinas) Pound'}, + {'code' : 'GBP', 'name' : 'United Kingdom Pound'}, + {'code' : 'GEL', 'name' : 'Georgia Lari'}, + {'code' : 'GGP', 'name' : 'Guernsey Pound'}, + {'code' : 'GHS', 'name' : 'Ghana Cedi'}, + {'code' : 'GIP', 'name' : 'Gibraltar Pound'}, + {'code' : 'GMD', 'name' : 'Gambia Dalasi'}, + {'code' : 'GNF', 'name' : 'Guinea Franc'}, + {'code' : 'GTQ', 'name' : 'Guatemala Quetzal'}, + {'code' : 'GYD', 'name' : 'Guyana Dollar'}, + {'code' : 'HKD', 'name' : 'Hong Kong Dollar'}, + {'code' : 'HNL', 'name' : 'Honduras Lempira'}, + {'code' : 'HRK', 'name' : 'Croatia Kuna'}, + {'code' : 'HTG', 'name' : 'Haiti Gourde'}, + {'code' : 'HUF', 'name' : 'Hungary Forint'}, + {'code' : 'IDR', 'name' : 'Indonesia Rupiah'}, + {'code' : 'ILS', 'name' : 'Israel Shekel'}, + {'code' : 'IMP', 'name' : 'Isle of Man Pound'}, + {'code' : 'INR', 'name' : 'India Rupee'}, + {'code' : 'IQD', 'name' : 'Iraq Dinar'}, + {'code' : 'IRR', 'name' : 'Iran Rial'}, + {'code' : 'ISK', 'name' : 'Iceland Krona'}, + {'code' : 'JEP', 'name' : 'Jersey Pound'}, + {'code' : 'JMD', 'name' : 'Jamaica Dollar'}, + {'code' : 'JOD', 'name' : 'Jordan Dinar'}, + {'code' : 'JPY', 'name' : 'Japan Yen'}, + {'code' : 'KES', 'name' : 'Kenya Shilling'}, + {'code' : 'KGS', 'name' : 'Kyrgyzstan Som'}, + {'code' : 'KHR', 'name' : 'Cambodia Riel'}, + {'code' : 'KMF', 'name' : 'Comoros Franc'}, + {'code' : 'KPW', 'name' : 'Korea (North) Won'}, + {'code' : 'KRW', 'name' : 'Korea (South) Won'}, + {'code' : 'KWD', 'name' : 'Kuwait Dinar'}, + {'code' : 'KYD', 'name' : 'Cayman Islands Dollar'}, + {'code' : 'KZT', 'name' : 'Kazakhstan Tenge'}, + {'code' : 'LAK', 'name' : 'Laos Kip'}, + {'code' : 'LBP', 'name' : 'Lebanon Pound'}, + {'code' : 'LKR', 'name' : 'Sri Lanka Rupee'}, + {'code' : 'LRD', 'name' : 'Liberia Dollar'}, + {'code' : 'LSL', 'name' : 'Lesotho Loti'}, + {'code' : 'LTL', 'name' : 'Lithuania Litas'}, + {'code' : 'LYD', 'name' : 'Libya Dinar'}, + {'code' : 'MAD', 'name' : 'Morocco Dirham'}, + {'code' : 'MDL', 'name' : 'Moldova Leu'}, + {'code' : 'MGA', 'name' : 'Madagascar Ariary'}, + {'code' : 'MKD', 'name' : 'Macedonia Denar'}, + {'code' : 'MMK', 'name' : 'Myanmar (Burma) Kyat'}, + {'code' : 'MNT', 'name' : 'Mongolia Tughrik'}, + {'code' : 'MOP', 'name' : 'Macau Pataca'}, + {'code' : 'MRO', 'name' : 'Mauritania Ouguiya'}, + {'code' : 'MUR', 'name' : 'Mauritius Rupee'}, + {'code' : 'MVR', 'name' : 'Maldives (Maldive Islands) Rufiyaa'}, + {'code' : 'MWK', 'name' : 'Malawi Kwacha'}, + {'code' : 'MXN', 'name' : 'Mexico Peso'}, + {'code' : 'MYR', 'name' : 'Malaysia Ringgit'}, + {'code' : 'MZN', 'name' : 'Mozambique Metical'}, + {'code' : 'NAD', 'name' : 'Namibia Dollar'}, + {'code' : 'NGN', 'name' : 'Nigeria Naira'}, + {'code' : 'NIO', 'name' : 'Nicaragua Cordoba'}, + {'code' : 'NOK', 'name' : 'Norway Krone'}, + {'code' : 'NPR', 'name' : 'Nepal Rupee'}, + {'code' : 'NZD', 'name' : 'New Zealand Dollar'}, + {'code' : 'OMR', 'name' : 'Oman Rial'}, + {'code' : 'PAB', 'name' : 'Panama Balboa'}, + {'code' : 'PEN', 'name' : 'Peru Nuevo Sol'}, + {'code' : 'PGK', 'name' : 'Papua New Guinea Kina'}, + {'code' : 'PHP', 'name' : 'Philippines Peso'}, + {'code' : 'PKR', 'name' : 'Pakistan Rupee'}, + {'code' : 'PLN', 'name' : 'Poland Zloty'}, + {'code' : 'PYG', 'name' : 'Paraguay Guarani'}, + {'code' : 'QAR', 'name' : 'Qatar Riyal'}, + {'code' : 'RON', 'name' : 'Romania New Leu'}, + {'code' : 'RSD', 'name' : 'Serbia Dinar'}, + {'code' : 'RUB', 'name' : 'Russia Ruble'}, + {'code' : 'RWF', 'name' : 'Rwanda Franc'}, + {'code' : 'SAR', 'name' : 'Saudi Arabia Riyal'}, + {'code' : 'SBD', 'name' : 'Solomon Islands Dollar'}, + {'code' : 'SCR', 'name' : 'Seychelles Rupee'}, + {'code' : 'SDG', 'name' : 'Sudan Pound'}, + {'code' : 'SEK', 'name' : 'Sweden Krona'}, + {'code' : 'SGD', 'name' : 'Singapore Dollar'}, + {'code' : 'SHP', 'name' : 'Saint Helena Pound'}, + {'code' : 'SLL', 'name' : 'Sierra Leone Leone'}, + {'code' : 'SOS', 'name' : 'Somalia Shilling'}, + {'code' : 'SPL', 'name' : 'Seborga Luigino'}, + {'code' : 'SRD', 'name' : 'Suriname Dollar'}, + {'code' : 'STD', 'name' : 'São Tomé and Príncipe Dobra'}, + {'code' : 'SVC', 'name' : 'El Salvador Colon'}, + {'code' : 'SYP', 'name' : 'Syria Pound'}, + {'code' : 'SZL', 'name' : 'Swaziland Lilangeni'}, + {'code' : 'THB', 'name' : 'Thailand Baht'}, + {'code' : 'TJS', 'name' : 'Tajikistan Somoni'}, + {'code' : 'TMT', 'name' : 'Turkmenistan Manat'}, + {'code' : 'TND', 'name' : 'Tunisia Dinar'}, + {'code' : 'TOP', 'name' : 'Tonga Pa\'anga'}, + {'code' : 'TRY', 'name' : 'Turkey Lira'}, + {'code' : 'TTD', 'name' : 'Trinidad and Tobago Dollar'}, + {'code' : 'TVD', 'name' : 'Tuvalu Dollar'}, + {'code' : 'TWD', 'name' : 'Taiwan New Dollar'}, + {'code' : 'TZS', 'name' : 'Tanzania Shilling'}, + {'code' : 'UAH', 'name' : 'Ukraine Hryvnia'}, + {'code' : 'UGX', 'name' : 'Uganda Shilling'}, + {'code' : 'USD', 'name' : 'United States Dollar'}, + {'code' : 'UYU', 'name' : 'Uruguay Peso'}, + {'code' : 'UZS', 'name' : 'Uzbekistan Som'}, + {'code' : 'VEF', 'name' : 'Venezuela Bolivar'}, + {'code' : 'VND', 'name' : 'Viet Nam Dong'}, + {'code' : 'VUV', 'name' : 'Vanuatu Vatu'}, + {'code' : 'WST', 'name' : 'Samoa Tala'}, + {'code' : 'XAF', 'name' : 'Communauté Financière Africaine (BEAC) CFA Franc BEAC'}, + {'code' : 'XCD', 'name' : 'East Caribbean Dollar'}, + {'code' : 'XDR', 'name' : 'International Monetary Fund (IMF) Special Drawing Rights'}, + {'code' : 'XOF', 'name' : 'Communauté Financière Africaine (BCEAO) Franc'}, + {'code' : 'XPF', 'name' : 'Comptoirs Français du Pacifique (CFP) Franc'}, + {'code' : 'YER', 'name' : 'Yemen Rial'}, + {'code' : 'ZAR', 'name' : 'South Africa Rand'}, + {'code' : 'ZMW', 'name' : 'Zambia Kwacha'}, + {'code' : 'ZWD', 'name' : 'Zimbabwe Dollar'} + ] + }; + + var o_hasOwnProperty = Object.prototype.hasOwnProperty; + var o_keys = (Object.keys || function(obj) { + var result = []; + for (var key in obj) { + if (o_hasOwnProperty.call(obj, key)) { + result.push(key); + } + } + + return result; + }); + + function _copyObject(source, target) { + var keys = o_keys(source); + var key; + + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + target[key] = source[key] || target[key]; + } + } + + function _copyArray(source, target) { + for (var i = 0, l = source.length; i < l; i++) { + target[i] = source[i]; + } + } + + function copyObject(source, _target) { + var isArray = Array.isArray(source); + var target = _target || (isArray ? new Array(source.length) : {}); + + if (isArray) { + _copyArray(source, target); + } else { + _copyObject(source, target); + } + + return target; + } + + /** Get the data based on key**/ + Chance.prototype.get = function (name) { + return copyObject(data[name]); + }; + + // Mac Address + Chance.prototype.mac_address = function(options){ + // typically mac addresses are separated by ":" + // however they can also be separated by "-" + // the network variant uses a dot every fourth byte + + options = initOptions(options); + if(!options.separator) { + options.separator = options.networkVersion ? "." : ":"; + } + + var mac_pool="ABCDEF1234567890", + mac = ""; + if(!options.networkVersion) { + mac = this.n(this.string, 6, { pool: mac_pool, length:2 }).join(options.separator); + } else { + mac = this.n(this.string, 3, { pool: mac_pool, length:4 }).join(options.separator); + } + + return mac; + }; + + Chance.prototype.normal = function (options) { + options = initOptions(options, {mean : 0, dev : 1}); + + // The Marsaglia Polar method + var s, u, v, norm, + mean = options.mean, + dev = options.dev; + + do { + // U and V are from the uniform distribution on (-1, 1) + u = this.random() * 2 - 1; + v = this.random() * 2 - 1; + + s = u * u + v * v; + } while (s >= 1); + + // Compute the standard normal variate + norm = u * Math.sqrt(-2 * Math.log(s) / s); + + // Shape and scale + return dev * norm + mean; + }; + + Chance.prototype.radio = function (options) { + // Initial Letter (Typically Designated by Side of Mississippi River) + options = initOptions(options, {side : "?"}); + var fl = ""; + switch (options.side.toLowerCase()) { + case "east": + case "e": + fl = "W"; + break; + case "west": + case "w": + fl = "K"; + break; + default: + fl = this.character({pool: "KW"}); + break; + } + + return fl + this.character({alpha: true, casing: "upper"}) + + this.character({alpha: true, casing: "upper"}) + + this.character({alpha: true, casing: "upper"}); + }; + + // Set the data as key and data or the data map + Chance.prototype.set = function (name, values) { + if (typeof name === "string") { + data[name] = values; + } else { + data = copyObject(name, data); + } + }; + + Chance.prototype.tv = function (options) { + return this.radio(options); + }; + + // ID number for Brazil companies + Chance.prototype.cnpj = function () { + var n = this.n(this.natural, 8, { max: 9 }); + var d1 = 2+n[7]*6+n[6]*7+n[5]*8+n[4]*9+n[3]*2+n[2]*3+n[1]*4+n[0]*5; + d1 = 11 - (d1 % 11); + if (d1>=10){ + d1 = 0; + } + var d2 = d1*2+3+n[7]*7+n[6]*8+n[5]*9+n[4]*2+n[3]*3+n[2]*4+n[1]*5+n[0]*6; + d2 = 11 - (d2 % 11); + if (d2>=10){ + d2 = 0; + } + return ''+n[0]+n[1]+'.'+n[2]+n[3]+n[4]+'.'+n[5]+n[6]+n[7]+'/0001-'+d1+d2; + }; + + // -- End Miscellaneous -- + + Chance.prototype.mersenne_twister = function (seed) { + return new MersenneTwister(seed); + }; + + // Mersenne Twister from https://gist.github.com/banksean/300494 + var MersenneTwister = function (seed) { + if (seed === undefined) { + seed = new Date().getTime(); + } + /* Period parameters */ + this.N = 624; + this.M = 397; + this.MATRIX_A = 0x9908b0df; /* constant vector a */ + this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ + this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ + + this.mt = new Array(this.N); /* the array for the state vector */ + this.mti = this.N + 1; /* mti==N + 1 means mt[N] is not initialized */ + + this.init_genrand(seed); + }; + + /* initializes mt[N] with a seed */ + MersenneTwister.prototype.init_genrand = function (s) { + this.mt[0] = s >>> 0; + for (this.mti = 1; this.mti < this.N; this.mti++) { + s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30); + this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + this.mt[this.mti] >>>= 0; + /* for >32 bit machines */ + } + }; + + /* initialize by an array with array-length */ + /* init_key is the array for initializing keys */ + /* key_length is its length */ + /* slight change for C++, 2004/2/26 */ + MersenneTwister.prototype.init_by_array = function (init_key, key_length) { + var i = 1, j = 0, k, s; + this.init_genrand(19650218); + k = (this.N > key_length ? this.N : key_length); + for (; k; k--) { + s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + init_key[j] + j; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; + j++; + if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; } + if (j >= key_length) { j = 0; } + } + for (k = this.N - 1; k; k--) { + s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30); + this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; /* non linear */ + this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ + i++; + if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; } + } + + this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ + }; + + /* generates a random number on [0,0xffffffff]-interval */ + MersenneTwister.prototype.genrand_int32 = function () { + var y; + var mag01 = new Array(0x0, this.MATRIX_A); + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (this.mti >= this.N) { /* generate N words at one time */ + var kk; + + if (this.mti === this.N + 1) { /* if init_genrand() has not been called, */ + this.init_genrand(5489); /* a default initial seed is used */ + } + for (kk = 0; kk < this.N - this.M; kk++) { + y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK); + this.mt[kk] = this.mt[kk + this.M] ^ (y >>> 1) ^ mag01[y & 0x1]; + } + for (;kk < this.N - 1; kk++) { + y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK); + this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ (y >>> 1) ^ mag01[y & 0x1]; + } + y = (this.mt[this.N - 1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); + this.mt[this.N - 1] = this.mt[this.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + + /* Tempering */ + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); + + return y >>> 0; + }; + + /* generates a random number on [0,0x7fffffff]-interval */ + MersenneTwister.prototype.genrand_int31 = function () { + return (this.genrand_int32() >>> 1); + }; + + /* generates a random number on [0,1]-real-interval */ + MersenneTwister.prototype.genrand_real1 = function () { + return this.genrand_int32() * (1.0 / 4294967295.0); + /* divided by 2^32-1 */ + }; + + /* generates a random number on [0,1)-real-interval */ + MersenneTwister.prototype.random = function () { + return this.genrand_int32() * (1.0 / 4294967296.0); + /* divided by 2^32 */ + }; + + /* generates a random number on (0,1)-real-interval */ + MersenneTwister.prototype.genrand_real3 = function () { + return (this.genrand_int32() + 0.5) * (1.0 / 4294967296.0); + /* divided by 2^32 */ + }; + + /* generates a random number on [0,1) with 53-bit resolution*/ + MersenneTwister.prototype.genrand_res53 = function () { + var a = this.genrand_int32()>>>5, b = this.genrand_int32()>>>6; + return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0); + }; + + + // CommonJS module + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = Chance; + } + exports.Chance = Chance; + } + + // Register as an anonymous AMD module + if (typeof define === 'function' && define.amd) { + define([], function () { + return Chance; + }); + } + + // if there is a importsScrips object define chance for worker + if (typeof importScripts !== 'undefined') { + chance = new Chance(); + } + + // If there is a window object, that at least has a document property, + // instantiate and define chance on the window + if (typeof window === "object" && typeof window.document === "object") { + window.Chance = Chance; + window.chance = new Chance(); + } +})(); diff --git a/grimoire-core.js b/grimoire-core.js new file mode 100644 index 0000000..2ef2df8 --- /dev/null +++ b/grimoire-core.js @@ -0,0 +1,361 @@ +//GrimoireJS v0.2 +//by Russell Champoux +//Grimoire magic item generator for the Pathfinder roleplaying game +/*global $:false, _:false, Chance:false, console:true */ + +'use strict'; + +var Grimoire = {}; +var TYPES = { + ARMOR_AND_SHIELDS: 'Armor and Shields', + POTIONS: 'Potions', + RINGS: 'Rings', + RODS: 'Rods', + SCROLLS: 'Scrolls', + STAVES: 'Staves', + WANDS: 'Wands', + WEAPONS: 'Weapons', + WONDROUS_ITEMS: 'Wondrous Items', + SPELLS: 'Spells' +}; +var SPELL_TYPES = { + ARCANE: 'Arcane', + DIVINE: 'Divine' +}; +var QUALITIES = { + MINOR:'minor', + MEDIUM: 'medium', + MAJOR: 'major' +}; +var WEIGHTS = {}; +WEIGHTS[QUALITIES.MINOR] = +{ + ARMOR_AND_SHIELDS: 4, + WEAPONS: 5, + POTIONS: 35, + RINGS: 2, + RODS: 0, + SCROLLS: 35, + STAVES: 0, + WANDS: 10, + WONDROUS_ITEMS: 9, +}; +WEIGHTS[QUALITIES.MEDIUM] = +{ + ARMOR_AND_SHIELDS: 10, + WEAPONS: 10, + POTIONS: 10, + RINGS: 10, + RODS: 10, + SCROLLS: 15, + STAVES: 3, + WANDS: 15, + WONDROUS_ITEMS: 17, +}; +WEIGHTS[QUALITIES.MAJOR] = { + ARMOR_AND_SHIELDS: 10, + WEAPONS: 10, + POTIONS: 5, + RINGS: 10, + RODS: 10, + SCROLLS: 10, + STAVES: 20, + WANDS: 5, + WONDROUS_ITEMS: 20, +}; + +//Contains all the source info +var SOURCES = []; +$.getJSON('sources/sources.json', function(sources) { + sources.sources.forEach(function(url) { + $.getJSON('sources/' + url, function(source) { + console.log('loaded sources/'+ url); + source.data = {}; + //construct the data containers we will insert later data into + source.types.forEach(function(type) { + source.data[type] = {}; + source.data[type].enabled = true; + var typeUrl = 'sources/' + source.shortName + '/' + type + '.json'; + $.getJSON(typeUrl, function(typeData) { + source.data[type].data = typeData.data; + console.log('loaded sources/' + typeUrl); + }) + .fail(function(d, textStatus, error) { + console.error('getJSON failed to load '+typeUrl+ ', status: ' + textStatus + ', error: '+error); + }); + }); + SOURCES.push(source); + }) + .fail(function(d, textStatus, error) { + console.error('getJSON failed to load '+url+ ', status: ' + textStatus + ', error: '+error); + }); + }); + }) + .fail(function(err) { + console.log('Failed to load sources list:', err); + }); + +Grimoire.getItems = function(sources, quality, count, types, options) { + + var items = []; + + //Error checking block + if(!sources || sources.length === 0) { console.log('No sources given!'); return items; } + if(!types || types == 'any' || types.length === 0) { + types = _.values(TYPES); //set to all types + } + for(var i in types) { + if(_.values(TYPES).indexOf(types[i]) == -1) { console.log('Invalid type ' + types[i]); return items; } + } + if(_.values(QUALITIES).indexOf(quality) == -1) { console.log('Invalid quality ' + quality); return items; } + if(!count || count < 1) { count = 1; } + options = options ? options : {}; + + + + //initialize the chance engine, using a seed if provided (for testing purposes) + //we do this now instead of reinitializing the Chance engine each time we generate an item + options.chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + _.times(count, function() { + var item = Grimoire.getItem(sources, quality, types, options); + if(item) { + items.push(item); + console.log(item); + } + }); + return items; +}; + +var selectSource = function(sources, quality, type, options) { + options = options ? options : {}; + var chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + var source = {}; + var sourceWeights = {}; + + //***SPELLS AND SCROLLS*** + //************************ + if(type == TYPES.SCROLLS || type == TYPES.SPELLS) { + if(!options.spellType || !options.spellLevel) { + console.log('Error selecting source for Spells. Invalid options', options); + } + sourceWeights = _.map(sources, function(source) { + if(source.data.Spells && source.data.Spells.enabled) { + if(source.data.Spells.data[options.spellType]) { + return source.data[type].data[options.spellType][options.spellLevel].length; + } + else { //source did not have my desired spell type + return 0; + } + } + else { //source has no spells + return 0; + } + + }); + } + //***WONDROUS ITEMS*** + //******************** + else if(type == TYPES.WONDROUS_ITEMS) { + sourceWeights = _.map(sources, + function(source) { + if(source.data[type] && source.data[type].enabled && source.data[type].data[quality]) { + return source.data[type].data[quality].length; + } + else { + return 0; //either no data, disabled, or not items of that quality + } + }); + } + //***EVERYTHING ELSE*** + //********************* + else { + if(type == TYPES.RODS && quality == QUALITIES.MINOR) { + console.log('Error! There are no ' + QUALITIES.MINOR + ' ' + TYPES.RODS); + return; + } + //Determine which source to use + //get a count from each source of what we're looking for + sourceWeights = _.map(sources, + function(source) { + //only choose from sources that HAVE that type of item + //AND are enabled + //AND have at least one item of that quality with a probability weight + if(source.data[type] && source.data[type].enabled) { + return _.filter(source.data[type].data, + function(item) { + return item.weight[quality] > 0; + }).length; + } + else { + return 0; + } + }); + } + if(_.sum(sourceWeights) <= 0) { + console.log('Unable to find valid ' + quality + ' ' + type + ' within sources!', sources); + return; + } + //chance.weighted will DESTRUCTIVELY modify paramaters that have a weight of zero + //in this case, that means sources that do not contain the selected type + //or sources that have types we want but with no weight + //because of that, I create a temp array that I don't care if chance mangles + source = sources[chance.weighted(_.range(sources.length), sourceWeights)]; + return source; +}; + + +Grimoire.getItem = function(sources, quality, types, options) { + options = options ? options : {}; + + var chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + var itemType = ''; + if(types.length == 1) { + itemType = types[0]; + } + else { + //Create a weighted-average map based on the desired types + var localWeights = _.clone(WEIGHTS[quality]); //copy the default weighted-average for this quality + for(var key in localWeights) { + if(_.values(types).indexOf(TYPES[key]) == -1) { //remove any items that were not selected + delete localWeights[key]; + } + } + //Determine the type of item to generate + //console.log(localWeights); + itemType = TYPES[chance.weighted(_.keys(localWeights), _.values(localWeights))]; + } + + var item = {}; + var source = {}; + + //GENERATE ITEM + //SCROLLS + if(itemType == TYPES.SCROLLS) { + item = Grimoire.getScroll(sources, quality, undefined /*spell type*/, options); + } + //WONDROUS ITEMS + else if(itemType == TYPES.WONDROUS_ITEMS) { + //Select the item source based on the quality and item type + source = selectSource(sources, quality, itemType, options); + item = _.clone(chance.pick(source.data[itemType].data[quality])); + item.source = source.shortName; + if(options.allIntelligent || (!options.disableIntelligent && chance.d100() == 1)) { + item.intelligence = 'WOOHOO I AM INTELLIGENT!'; + } + } + //EVERYTHING ELSE + else { + //Select the item source based on the quality and item type + source = selectSource(sources, quality, itemType, options); + var totalWeights = _.sum(_.map(source.data[itemType].data, function(item) { return item.weight[quality]; })); + var roll = chance.integer({min: 1, max: totalWeights}); + + //EXCEPTIONS FOR ARMOR_AND_SHEILDS + + //EXCEPTIONS FOR WEAPONS + + //EXCEPTIONS FOR POTIONS + + //EXCEPTIONS FOR WANDS + + //CHANCE FOR INTELLIGENT WEAPONS, RINGS, RODS, STAVES, OR WONDROUS ITEMS + var itemData = source.data[itemType].data; + for(var i in itemData) { //check all 'itemType' items in the source + if(roll <= itemData[i].weight[quality]) { //if we roll under their weighted probability + item = _.clone(itemData[i]); + delete item.weight; + item.source = source.shortName; + break; + } + else { //decrease the roll value by the current item's weighted probability + roll -= itemData[i].weight[quality]; + } + } + } + item.quality = quality; + return item; +}; + +Grimoire.getSpells = function(sources, level, count, type, options) { + options = options ? options : {}; + + var spells = []; + options.chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + _.times(count, function() { + + spells.push(Grimoire.getSpell(sources, level, type, options)); + }); + + return spells; +}; + +Grimoire.getSpell = function(sources, level, type, options) { + options = options ? options : {}; + + //initialize the chance engine, using a seed if provided (for testing purposes) + var chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + var spellType = type ? type : chance.pick(_.values(SPELL_TYPES)); + options.spellType = spellType; + options.spellLevel = level; + var source = selectSource(sources, undefined, TYPES.SPELLS, options); + //console.log('Generating a level ' + level + ' ' + spellType + ' spell from ' + source.shortName); + + var spell = { + name: chance.pick(source.data.Spells.data[spellType][level]), + level: level, + type: spellType, + source: source.shortName + }; + + return spell; +}; + +Grimoire.getScroll = function(sources, quality, type, options) { + options = options ? options : {}; + var chance = (options.chance ? options.chance : options.seed ? new Chance(options.seed) : new Chance()); + + var cost = [12.5, 25, 150, 375, 700, 1125, 1650, 2275, 3000, 3825]; + var spellLevel = 0; + + var roll = chance.integer({min: 1, max: 100}); + if(quality == QUALITIES.MINOR) { + switch(true) { + case (roll <= 5): spellLevel = 0; break; + case (roll <= 50): spellLevel = 1; break; + case (roll <= 95): spellLevel = 2; break; + case (roll <= 100): spellLevel = 3; break; + } + } + else if(quality == QUALITIES.MEDIUM) { + switch(true) { + case (roll <= 5): spellLevel = 2; break; + case (roll <= 65): spellLevel = 3; break; + case (roll <= 95): spellLevel = 4; break; + case (roll <= 100): spellLevel = 5; break; + } + } + else if(quality == QUALITIES.MAJOR) { + switch(true) { + case (roll <= 5): spellLevel = 4; break; + case (roll <= 50): spellLevel = 5; break; + case (roll <= 70): spellLevel = 6; break; + case (roll <= 85): spellLevel = 7; break; + case (roll <= 95): spellLevel = 8; break; + case (roll <= 100): spellLevel = 9; break; + } + } + + var spell = Grimoire.getSpell(sources, spellLevel, type, options); + + return { + 'name': 'Scroll of ' + spell.name, + 'description': '(' + spell.type + ', spell level ' + spellLevel + ', caster level ' + _.max([1, (spellLevel*2-1)]) + ')', + 'cost': cost[spellLevel], + 'source': spell.source + }; +}; \ No newline at end of file diff --git a/index.html b/index.html index 28f1fe6..490240a 100644 --- a/index.html +++ b/index.html @@ -2,7 +2,12 @@