Merge branch 'master' of ssh://pwan.org/var/www/git/projects/tech-radar
[tech-radar.git] / examples / chance.js
1 // Chance.js 0.5.5
2 // http://chancejs.com
3 // (c) 2013 Victor Quinn
4 // Chance may be freely distributed or modified under the MIT license.
5
6 (function () {
7
8 // Constants
9 var MAX_INT = 9007199254740992;
10 var MIN_INT = -MAX_INT;
11 var NUMBERS = '0123456789';
12 var CHARS_LOWER = 'abcdefghijklmnopqrstuvwxyz';
13 var CHARS_UPPER = CHARS_LOWER.toUpperCase();
14 var HEX_POOL = NUMBERS + "abcdef";
15
16 // Constructor
17 var Chance = function (seed) {
18 if (!(this instanceof Chance)) {
19 return new Chance(seed);
20 }
21
22 if (seed !== undefined) {
23 // If we were passed a generator rather than a seed, use it.
24 if (typeof seed === 'function') {
25 this.random = seed;
26 } else {
27 this.seed = seed;
28 }
29 }
30
31 // If no generator function was provided, use our MT
32 if (typeof this.random === 'undefined') {
33 this.mt = this.mersenne_twister(seed);
34 this.random = function () {
35 return this.mt.random(this.seed);
36 };
37 }
38 };
39
40 // Random helper functions
41 function initOptions(options, defaults) {
42 options || (options = {});
43 if (!defaults) {
44 return options;
45 }
46 for (var i in defaults) {
47 if (typeof options[i] === 'undefined') {
48 options[i] = defaults[i];
49 }
50 }
51 return options;
52 }
53
54 function testRange(test, errorMessage) {
55 if (test) {
56 throw new RangeError(errorMessage);
57 }
58 }
59
60 // -- Basics --
61
62 Chance.prototype.bool = function (options) {
63
64 // likelihood of success (true)
65 options = initOptions(options, {likelihood : 50});
66
67 testRange(
68 options.likelihood < 0 || options.likelihood > 100,
69 "Chance: Likelihood accepts values from 0 to 100."
70 );
71
72 return this.random() * 100 < options.likelihood;
73 };
74
75 Chance.prototype.character = function (options) {
76 options = initOptions(options);
77
78 var symbols = "!@#$%^&*()[]",
79 letters, pool;
80
81 testRange(
82 options.alpha && options.symbols,
83 "Chance: Cannot specify both alpha and symbols."
84 );
85
86
87 if (options.casing === 'lower') {
88 letters = CHARS_LOWER;
89 } else if (options.casing === 'upper') {
90 letters = CHARS_UPPER;
91 } else {
92 letters = CHARS_LOWER + CHARS_UPPER;
93 }
94
95 if (options.pool) {
96 pool = options.pool;
97 } else if (options.alpha) {
98 pool = letters;
99 } else if (options.symbols) {
100 pool = symbols;
101 } else {
102 pool = letters + NUMBERS + symbols;
103 }
104
105 return pool.charAt(this.natural({max: (pool.length - 1)}));
106 };
107
108 // Note, wanted to use "float" or "double" but those are both JS reserved words.
109
110 // Note, fixed means N OR LESS digits after the decimal. This because
111 // It could be 14.9000 but in JavaScript, when this is cast as a number,
112 // the trailing zeroes are dropped. Left to the consumer if trailing zeroes are
113 // needed
114 Chance.prototype.floating = function (options) {
115 var num, range;
116
117 options = initOptions(options, {fixed : 4});
118 var fixed = Math.pow(10, options.fixed);
119
120 testRange(
121 options.fixed && options.precision,
122 "Chance: Cannot specify both fixed and precision."
123 );
124
125 var max = MAX_INT / fixed;
126 var min = -max;
127
128 testRange(
129 options.min && options.fixed && options.min < min,
130 "Chance: Min specified is out of range with fixed. Min should be, at least, " + min
131 );
132 testRange(
133 options.max && options.fixed && options.max > max,
134 "Chance: Max specified is out of range with fixed. Max should be, at most, " + max
135 );
136
137 options = initOptions(options, {min : min, max : max});
138
139 // Todo - Make this work!
140 // options.precision = (typeof options.precision !== "undefined") ? options.precision : false;
141
142 num = this.integer({min: options.min * fixed, max: options.max * fixed});
143 var num_fixed = (num / fixed).toFixed(options.fixed);
144
145 return parseFloat(num_fixed);
146 };
147
148 // NOTE the max and min are INCLUDED in the range. So:
149 //
150 // chance.natural({min: 1, max: 3});
151 //
152 // would return either 1, 2, or 3.
153
154 Chance.prototype.integer = function (options) {
155
156 // 9007199254740992 (2^53) is the max integer number in JavaScript
157 // See: http://vq.io/132sa2j
158 options = initOptions(options, {min: MIN_INT, max: MAX_INT});
159
160 testRange(options.min > options.max, "Chance: Min cannot be greater than Max.");
161
162 return Math.floor(this.random() * (options.max - options.min + 1) + options.min);
163 };
164
165 Chance.prototype.natural = function (options) {
166 options = initOptions(options, {min: 0, max: MAX_INT});
167 return this.integer(options);
168 };
169
170 Chance.prototype.normal = function (options) {
171 options = initOptions(options, {mean : 0, dev : 1});
172
173 // The Marsaglia Polar method
174 var s, u, v, norm,
175 mean = options.mean,
176 dev = options.dev;
177
178 do {
179 // U and V are from the uniform distribution on (-1, 1)
180 u = this.random() * 2 - 1;
181 v = this.random() * 2 - 1;
182
183 s = u * u + v * v;
184 } while (s >= 1);
185
186 // Compute the standard normal variate
187 norm = u * Math.sqrt(-2 * Math.log(s) / s);
188
189 // Shape and scale
190 return dev * norm + mean;
191 };
192
193 Chance.prototype.string = function (options) {
194 options = initOptions(options);
195
196 var length = options.length || this.natural({min: 5, max: 20}),
197 text = '',
198 pool = options.pool;
199
200 for (var i = 0; i < length; i++) {
201 text += this.character({pool: pool});
202 }
203 return text;
204 };
205
206 // -- End Basics --
207
208 // -- Helpers --
209
210 Chance.prototype.capitalize = function (word) {
211 return word.charAt(0).toUpperCase() + word.substr(1);
212 };
213
214 Chance.prototype.mixin = function (obj) {
215 var chance = this;
216 for (var func_name in obj) {
217 Chance.prototype[func_name] = obj[func_name];
218 }
219 return this;
220 };
221
222 // H/T to SO for this one: http://vq.io/OtUrZ5
223 Chance.prototype.pad = function (number, width, pad) {
224 // Default pad to 0 if none provided
225 pad = pad || '0';
226 // Convert number to a string
227 number = number + '';
228 return number.length >= width ? number : new Array(width - number.length + 1).join(pad) + number;
229 };
230
231 Chance.prototype.pick = function (arr, count) {
232 if (!count || count === 1) {
233 return arr[this.natural({max: arr.length - 1})];
234 } else {
235 return this.shuffle(arr).slice(0, count);
236 }
237 };
238
239 Chance.prototype.shuffle = function (arr) {
240 var old_array = arr.slice(0),
241 new_array = [],
242 j = 0,
243 length = Number(old_array.length);
244
245 for (var i = 0; i < length; i++) {
246 // Pick a random index from the array
247 j = this.natural({max: old_array.length - 1});
248 // Add it to the new array
249 new_array[i] = old_array[j];
250 // Remove that element from the original array
251 old_array.splice(j, 1);
252 }
253
254 return new_array;
255 };
256
257 // -- End Helpers --
258
259 // -- Text --
260
261 Chance.prototype.paragraph = function (options) {
262 options = initOptions(options);
263
264 var sentences = options.sentences || this.natural({min: 3, max: 7}),
265 sentence_array = [];
266
267 for (var i = 0; i < sentences; i++) {
268 sentence_array.push(this.sentence());
269 }
270
271 return sentence_array.join(' ');
272 };
273
274 // Could get smarter about this than generating random words and
275 // chaining them together. Such as: http://vq.io/1a5ceOh
276 Chance.prototype.sentence = function (options) {
277 options = initOptions(options);
278
279 var words = options.words || this.natural({min: 12, max: 18}),
280 text, word_array = [];
281
282 for (var i = 0; i < words; i++) {
283 word_array.push(this.word());
284 }
285
286 text = word_array.join(' ');
287
288 // Capitalize first letter of sentence, add period at end
289 text = this.capitalize(text) + '.';
290
291 return text;
292 };
293
294 Chance.prototype.syllable = function (options) {
295 options = initOptions(options);
296
297 var length = options.length || this.natural({min: 2, max: 3}),
298 consonants = 'bcdfghjklmnprstvwz', // consonants except hard to speak ones
299 vowels = 'aeiou', // vowels
300 all = consonants + vowels, // all
301 text = '',
302 chr;
303
304 // I'm sure there's a more elegant way to do this, but this works
305 // decently well.
306 for (var i = 0; i < length; i++) {
307 if (i === 0) {
308 // First character can be anything
309 chr = this.character({pool: all});
310 } else if (consonants.indexOf(chr) === -1) {
311 // Last character was a vowel, now we want a consonant
312 chr = this.character({pool: consonants});
313 } else {
314 // Last character was a consonant, now we want a vowel
315 chr = this.character({pool: vowels});
316 }
317
318 text += chr;
319 }
320
321 return text;
322 };
323
324 Chance.prototype.word = function (options) {
325 options = initOptions(options);
326
327 testRange(
328 options.syllables && options.length,
329 "Chance: Cannot specify both syllables AND length."
330 );
331
332 var syllables = options.syllables || this.natural({min: 1, max: 3}),
333 text = '';
334
335 if (options.length) {
336 // Either bound word by length
337 do {
338 text += this.syllable();
339 } while (text.length < options.length);
340 text = text.substring(0, options.length);
341 } else {
342 // Or by number of syllables
343 for (var i = 0; i < syllables; i++) {
344 text += this.syllable();
345 }
346 }
347 return text;
348 };
349
350 // -- End Text --
351
352 // -- Person --
353
354 Chance.prototype.age = function (options) {
355 options = initOptions(options);
356 var age;
357
358 switch (options.type) {
359 case 'child':
360 age = this.natural({min: 1, max: 12});
361 break;
362 case 'teen':
363 age = this.natural({min: 13, max: 19});
364 break;
365 case 'adult':
366 age = this.natural({min: 18, max: 120});
367 break;
368 case 'senior':
369 age = this.natural({min: 65, max: 120});
370 break;
371 default:
372 age = this.natural({min: 1, max: 120});
373 break;
374 }
375
376 return age;
377 };
378
379 Chance.prototype.birthday = function (options) {
380 options = initOptions(options, {
381 year: (new Date().getFullYear() - this.age(options))
382 });
383
384 return this.date(options);
385 };
386
387 var firstNames = {
388 "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"],
389 "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"]
390 };
391
392 Chance.prototype.first = function (options) {
393 options = initOptions(options, {gender: this.gender()});
394 return this.pick(firstNames[options.gender.toLowerCase()]);
395 };
396
397 Chance.prototype.gender = function () {
398 return this.pick(['Male', 'Female']);
399 };
400
401 var 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'];
402
403 Chance.prototype.last = function () {
404 return this.pick(lastNames);
405 };
406
407 Chance.prototype.name = function (options) {
408 options = initOptions(options);
409
410 var first = this.first(options),
411 last = this.last(),
412 name;
413
414 if (options.middle) {
415 name = first + ' ' + this.first(options) + ' ' + last;
416 } else if (options.middle_initial) {
417 name = first + ' ' + this.character({alpha: true, casing: 'upper'}) + '. ' + last;
418 } else {
419 name = first + ' ' + last;
420 }
421
422 if (options.prefix) {
423 name = this.prefix() + ' ' + name;
424 }
425
426 return name;
427 };
428
429 Chance.prototype.name_prefixes = function () {
430 return [
431 {name: 'Doctor', abbreviation: 'Dr.'},
432 {name: 'Miss', abbreviation: 'Miss'},
433 {name: 'Misses', abbreviation: 'Mrs.'},
434 {name: 'Mister', abbreviation: 'Mr.'}
435 ];
436 };
437
438 // Alias for name_prefix
439 Chance.prototype.prefix = function (options) {
440 return this.name_prefix(options);
441 };
442
443 Chance.prototype.name_prefix = function (options) {
444 options = initOptions(options);
445 return options.full ?
446 this.pick(this.name_prefixes()).name :
447 this.pick(this.name_prefixes()).abbreviation;
448 };
449
450 // -- End Person --
451
452 // -- Web --
453
454 Chance.prototype.color = function (options) {
455 function gray(value, delimiter) {
456 return [value, value, value].join(delimiter || '');
457 }
458
459 options = initOptions(options, {format: this.pick(['hex', 'shorthex', 'rgb']), grayscale: false});
460 var isGrayscale = options.grayscale;
461
462 if (options.format === 'hex') {
463 return '#' + (isGrayscale ? gray(this.hash({length: 2})) : this.hash({length: 6}));
464 }
465
466 if (options.format === 'shorthex') {
467 return '#' + (isGrayscale ? gray(this.hash({length: 1})) : this.hash({length: 3}));
468 }
469
470 if (options.format === 'rgb') {
471 if (isGrayscale) {
472 return 'rgb(' + gray(this.natural({max: 255}), ',') + ')';
473 } else {
474 return 'rgb(' + this.natural({max: 255}) + ',' + this.natural({max: 255}) + ',' + this.natural({max: 255}) + ')';
475 }
476 }
477
478 throw new Error('Invalid format provided. Please provide one of "hex", "shorthex", or "rgb"');
479 };
480
481 Chance.prototype.domain = function (options) {
482 options = initOptions(options);
483 return this.word() + '.' + (options.tld || this.tld());
484 };
485
486 Chance.prototype.email = function (options) {
487 options = initOptions(options);
488 return this.word() + '@' + (options.domain || this.domain());
489 };
490
491 Chance.prototype.fbid = function () {
492 return parseInt('10000' + this.natural({max: 100000000000}), 10);
493 };
494
495 Chance.prototype.google_analytics = function () {
496 var account = this.pad(this.natural({max: 999999}), 6);
497 var property = this.pad(this.natural({max: 99}), 2);
498 return 'UA-' + account + '-' + property;
499 };
500
501 Chance.prototype.hashtag = function () {
502 return '#' + this.word();
503 };
504
505 Chance.prototype.ip = function () {
506 // Todo: This could return some reserved IPs. See http://vq.io/137dgYy
507 // this should probably be updated to account for that rare as it may be
508 return this.natural({max: 255}) + '.' +
509 this.natural({max: 255}) + '.' +
510 this.natural({max: 255}) + '.' +
511 this.natural({max: 255});
512 };
513
514 Chance.prototype.ipv6 = function () {
515 var ip_addr = "";
516
517 for (var i = 0; i < 8; i++) {
518 ip_addr += this.hash({length: 4}) + ':';
519 }
520 return ip_addr.substr(0, ip_addr.length - 1);
521 };
522
523 Chance.prototype.klout = function () {
524 return this.natural({min: 1, max: 99});
525 };
526
527 Chance.prototype.tlds = function () {
528 return ['com', 'org', 'edu', 'gov', 'co.uk', 'net', 'io'];
529 };
530
531 Chance.prototype.tld = function () {
532 return this.pick(this.tlds());
533 };
534
535 Chance.prototype.twitter = function () {
536 return '@' + this.word();
537 };
538
539 // -- End Web --
540
541 // -- Address --
542
543 Chance.prototype.address = function (options) {
544 options = initOptions(options);
545 return this.natural({min: 5, max: 2000}) + ' ' + this.street(options);
546 };
547
548 Chance.prototype.areacode = function (options) {
549 options = initOptions(options, {parens : true});
550 // Don't want area codes to start with 1, or have a 9 as the second digit
551 var areacode = this.natural({min: 2, max: 9}).toString() + this.natural({min: 0, max: 8}).toString() + this.natural({min: 0, max: 9}).toString();
552 return options.parens ? '(' + areacode + ')' : areacode;
553 };
554
555 Chance.prototype.city = function () {
556 return this.capitalize(this.word({syllables: 3}));
557 };
558
559 Chance.prototype.coordinates = function (options) {
560 options = initOptions(options);
561 return this.latitude(options) + ', ' + this.longitude(options);
562 };
563
564 Chance.prototype.geoJson = function (options) {
565 options = initOptions(options);
566 return this.latitude(options) + ', ' + this.longitude(options) + ', ' + this.altitude(options);
567 };
568
569 Chance.prototype.altitude = function (options) {
570 options = initOptions(options, {fixed : 5});
571 return this.floating({min: 0, max: 32736000, fixed: options.fixed});
572 };
573
574 Chance.prototype.depth = function (options) {
575 options = initOptions(options, {fixed: 5});
576 return this.floating({min: -35994, max: 0, fixed: options.fixed});
577 };
578
579 Chance.prototype.latitude = function (options) {
580 options = initOptions(options, {fixed: 5, min: -90, max: 90});
581 return this.floating({min: options.min, max: options.max, fixed: options.fixed});
582 };
583
584 Chance.prototype.longitude = function (options) {
585 options = initOptions(options, {fixed: 5, min: -180, max: 180});
586 return this.floating({min: options.min, max: options.max, fixed: options.fixed});
587 };
588
589 Chance.prototype.phone = function (options) {
590 options = initOptions(options, {formatted : true});
591 if (!options.formatted) {
592 options.parens = false;
593 }
594 var areacode = this.areacode(options).toString();
595 var exchange = this.natural({min: 2, max: 9}).toString() + this.natural({min: 0, max: 9}).toString() + this.natural({min: 0, max: 9}).toString();
596 var subscriber = this.natural({min: 1000, max: 9999}).toString(); // this could be random [0-9]{4}
597 return options.formatted ? areacode + ' ' + exchange + '-' + subscriber : areacode + exchange + subscriber;
598 };
599
600 Chance.prototype.postal = function () {
601 // Postal District
602 var pd = this.character({pool: "XVTSRPNKLMHJGECBA"});
603 // Forward Sortation Area (FSA)
604 var fsa = pd + this.natural({max: 9}) + this.character({alpha: true, casing: "upper"});
605 // Local Delivery Unut (LDU)
606 var ldu = this.natural({max: 9}) + this.character({alpha: true, casing: "upper"}) + this.natural({max: 9});
607
608 return fsa + " " + ldu;
609 };
610
611 Chance.prototype.provinces = function () {
612 return [
613 {name: 'Alberta', abbreviation: 'AB'},
614 {name: 'British Columbia', abbreviation: 'BC'},
615 {name: 'Manitoba', abbreviation: 'MB'},
616 {name: 'New Brunswick', abbreviation: 'NB'},
617 {name: 'Newfoundland and Labrador', abbreviation: 'NL'},
618 {name: 'Nova Scotia', abbreviation: 'NS'},
619 {name: 'Ontario', abbreviation: 'ON'},
620 {name: 'Prince Edward Island', abbreviation: 'PE'},
621 {name: 'Quebec', abbreviation: 'QC'},
622 {name: 'Saskatchewan', abbreviation: 'SK'},
623
624 // The case could be made that the following are not actually provinces
625 // since they are technically considered "territories" however they all
626 // look the same on an envelope!
627 {name: 'Northwest Territories', abbreviation: 'NT'},
628 {name: 'Nunavut', abbreviation: 'NU'},
629 {name: 'Yukon', abbreviation: 'YT'}
630 ];
631 };
632
633 Chance.prototype.province = function (options) {
634 return (options && options.full) ?
635 this.pick(this.provinces()).name :
636 this.pick(this.provinces()).abbreviation;
637 };
638
639 Chance.prototype.radio = function (options) {
640 // Initial Letter (Typically Designated by Side of Mississippi River)
641 options = initOptions(options, {side : "?"});
642 var fl = "";
643 switch (options.side.toLowerCase()) {
644 case "east":
645 case "e":
646 fl = "W";
647 break;
648 case "west":
649 case "w":
650 fl = "K";
651 break;
652 default:
653 fl = this.character({pool: "KW"});
654 break;
655 }
656
657 return fl + this.character({alpha: true, casing: "upper"}) + this.character({alpha: true, casing: "upper"}) + this.character({alpha: true, casing: "upper"});
658 };
659
660 Chance.prototype.state = function (options) {
661 return (options && options.full) ?
662 this.pick(this.states()).name :
663 this.pick(this.states()).abbreviation;
664 };
665
666 Chance.prototype.states = function () {
667 return [
668 {name: 'Alabama', abbreviation: 'AL'},
669 {name: 'Alaska', abbreviation: 'AK'},
670 {name: 'American Samoa', abbreviation: 'AS'},
671 {name: 'Arizona', abbreviation: 'AZ'},
672 {name: 'Arkansas', abbreviation: 'AR'},
673 {name: 'Armed Forces Europe', abbreviation: 'AE'},
674 {name: 'Armed Forces Pacific', abbreviation: 'AP'},
675 {name: 'Armed Forces the Americas', abbreviation: 'AA'},
676 {name: 'California', abbreviation: 'CA'},
677 {name: 'Colorado', abbreviation: 'CO'},
678 {name: 'Connecticut', abbreviation: 'CT'},
679 {name: 'Delaware', abbreviation: 'DE'},
680 {name: 'District of Columbia', abbreviation: 'DC'},
681 {name: 'Federated States of Micronesia', abbreviation: 'FM'},
682 {name: 'Florida', abbreviation: 'FL'},
683 {name: 'Georgia', abbreviation: 'GA'},
684 {name: 'Guam', abbreviation: 'GU'},
685 {name: 'Hawaii', abbreviation: 'HI'},
686 {name: 'Idaho', abbreviation: 'ID'},
687 {name: 'Illinois', abbreviation: 'IL'},
688 {name: 'Indiana', abbreviation: 'IN'},
689 {name: 'Iowa', abbreviation: 'IA'},
690 {name: 'Kansas', abbreviation: 'KS'},
691 {name: 'Kentucky', abbreviation: 'KY'},
692 {name: 'Louisiana', abbreviation: 'LA'},
693 {name: 'Maine', abbreviation: 'ME'},
694 {name: 'Marshall Islands', abbreviation: 'MH'},
695 {name: 'Maryland', abbreviation: 'MD'},
696 {name: 'Massachusetts', abbreviation: 'MA'},
697 {name: 'Michigan', abbreviation: 'MI'},
698 {name: 'Minnesota', abbreviation: 'MN'},
699 {name: 'Mississippi', abbreviation: 'MS'},
700 {name: 'Missouri', abbreviation: 'MO'},
701 {name: 'Montana', abbreviation: 'MT'},
702 {name: 'Nebraska', abbreviation: 'NE'},
703 {name: 'Nevada', abbreviation: 'NV'},
704 {name: 'New Hampshire', abbreviation: 'NH'},
705 {name: 'New Jersey', abbreviation: 'NJ'},
706 {name: 'New Mexico', abbreviation: 'NM'},
707 {name: 'New York', abbreviation: 'NY'},
708 {name: 'North Carolina', abbreviation: 'NC'},
709 {name: 'North Dakota', abbreviation: 'ND'},
710 {name: 'Northern Mariana Islands', abbreviation: 'MP'},
711 {name: 'Ohio', abbreviation: 'OH'},
712 {name: 'Oklahoma', abbreviation: 'OK'},
713 {name: 'Oregon', abbreviation: 'OR'},
714 {name: 'Pennsylvania', abbreviation: 'PA'},
715 {name: 'Puerto Rico', abbreviation: 'PR'},
716 {name: 'Rhode Island', abbreviation: 'RI'},
717 {name: 'South Carolina', abbreviation: 'SC'},
718 {name: 'South Dakota', abbreviation: 'SD'},
719 {name: 'Tennessee', abbreviation: 'TN'},
720 {name: 'Texas', abbreviation: 'TX'},
721 {name: 'Utah', abbreviation: 'UT'},
722 {name: 'Vermont', abbreviation: 'VT'},
723 {name: 'Virgin Islands, U.S.', abbreviation: 'VI'},
724 {name: 'Virginia', abbreviation: 'VA'},
725 {name: 'Washington', abbreviation: 'WA'},
726 {name: 'West Virginia', abbreviation: 'WV'},
727 {name: 'Wisconsin', abbreviation: 'WI'},
728 {name: 'Wyoming', abbreviation: 'WY'}
729 ];
730 };
731
732 Chance.prototype.street = function (options) {
733 options = initOptions(options);
734
735 var street = this.word({syllables: 2});
736 street = this.capitalize(street);
737 street += ' ';
738 street += options.short_suffix ?
739 this.street_suffix().abbreviation :
740 this.street_suffix().name;
741 return street;
742 };
743
744 Chance.prototype.street_suffix = function () {
745 return this.pick(this.street_suffixes());
746 };
747
748 Chance.prototype.street_suffixes = function () {
749 // These are the most common suffixes.
750 return [
751 {name: 'Avenue', abbreviation: 'Ave'},
752 {name: 'Boulevard', abbreviation: 'Blvd'},
753 {name: 'Center', abbreviation: 'Ctr'},
754 {name: 'Circle', abbreviation: 'Cir'},
755 {name: 'Court', abbreviation: 'Ct'},
756 {name: 'Drive', abbreviation: 'Dr'},
757 {name: 'Extension', abbreviation: 'Ext'},
758 {name: 'Glen', abbreviation: 'Gln'},
759 {name: 'Grove', abbreviation: 'Grv'},
760 {name: 'Heights', abbreviation: 'Hts'},
761 {name: 'Highway', abbreviation: 'Hwy'},
762 {name: 'Junction', abbreviation: 'Jct'},
763 {name: 'Key', abbreviation: 'Key'},
764 {name: 'Lane', abbreviation: 'Ln'},
765 {name: 'Loop', abbreviation: 'Loop'},
766 {name: 'Manor', abbreviation: 'Mnr'},
767 {name: 'Mill', abbreviation: 'Mill'},
768 {name: 'Park', abbreviation: 'Park'},
769 {name: 'Parkway', abbreviation: 'Pkwy'},
770 {name: 'Pass', abbreviation: 'Pass'},
771 {name: 'Path', abbreviation: 'Path'},
772 {name: 'Pike', abbreviation: 'Pike'},
773 {name: 'Place', abbreviation: 'Pl'},
774 {name: 'Plaza', abbreviation: 'Plz'},
775 {name: 'Point', abbreviation: 'Pt'},
776 {name: 'Ridge', abbreviation: 'Rdg'},
777 {name: 'River', abbreviation: 'Riv'},
778 {name: 'Road', abbreviation: 'Rd'},
779 {name: 'Square', abbreviation: 'Sq'},
780 {name: 'Street', abbreviation: 'St'},
781 {name: 'Terrace', abbreviation: 'Ter'},
782 {name: 'Trail', abbreviation: 'Trl'},
783 {name: 'Turnpike', abbreviation: 'Tpke'},
784 {name: 'View', abbreviation: 'Vw'},
785 {name: 'Way', abbreviation: 'Way'}
786 ];
787 };
788
789 Chance.prototype.tv = function (options) {
790 return this.radio(options);
791 };
792
793 // Note: only returning US zip codes, internationalization will be a whole
794 // other beast to tackle at some point.
795 Chance.prototype.zip = function (options) {
796 var zip = "";
797
798 for (var i = 0; i < 5; i++) {
799 zip += this.natural({max: 9}).toString();
800 }
801
802 if (options && options.plusfour === true) {
803 zip += '-';
804 for (i = 0; i < 4; i++) {
805 zip += this.natural({max: 9}).toString();
806 }
807 }
808
809 return zip;
810 };
811
812 // -- End Address --
813
814 // -- Time
815
816 Chance.prototype.ampm = function () {
817 return this.bool() ? 'am' : 'pm';
818 };
819
820 Chance.prototype.date = function (options) {
821 var m = this.month({raw: true}),
822 date_string;
823
824 options = initOptions(options, {
825 year: parseInt(this.year(), 10),
826 // Necessary to subtract 1 because Date() 0-indexes month but not day or year
827 // for some reason.
828 month: m.numeric - 1,
829 day: this.natural({min: 1, max: m.days}),
830 hour: this.hour(),
831 minute: this.minute(),
832 second: this.second(),
833 millisecond: this.millisecond(),
834 american: true,
835 string: false
836 });
837
838 var date = new Date(options.year, options.month, options.day, options.hour, options.minute, options.second, options.millisecond);
839
840 if (options.american) {
841 // Adding 1 to the month is necessary because Date() 0-indexes
842 // months but not day for some odd reason.
843 date_string = (date.getMonth() + 1) + '/' + date.getDate() + '/' + date.getFullYear();
844 } else {
845 date_string = date.getDate() + '/' + (date.getMonth() + 1) + '/' + date.getFullYear();
846 }
847
848 return options.string ? date_string : date;
849 };
850
851 Chance.prototype.hammertime = function (options) {
852 return this.date(options).getTime();
853 };
854
855 Chance.prototype.hour = function (options) {
856 options = initOptions(options);
857 var max = options.twentyfour ? 24 : 12;
858 return this.natural({min: 1, max: max});
859 };
860
861 Chance.prototype.millisecond = function () {
862 return this.natural({max: 999});
863 };
864
865 Chance.prototype.minute = Chance.prototype.second = function () {
866 return this.natural({max: 59});
867 };
868
869 Chance.prototype.month = function (options) {
870 options = initOptions(options);
871 var month = this.pick(this.months());
872 return options.raw ? month : month.name;
873 };
874
875 Chance.prototype.months = function () {
876 return [
877 {name: 'January', short_name: 'Jan', numeric: '01', days: 31},
878 // Not messing with leap years...
879 {name: 'February', short_name: 'Feb', numeric: '02', days: 28},
880 {name: 'March', short_name: 'Mar', numeric: '03', days: 31},
881 {name: 'April', short_name: 'Apr', numeric: '04', days: 30},
882 {name: 'May', short_name: 'May', numeric: '05', days: 31},
883 {name: 'June', short_name: 'Jun', numeric: '06', days: 30},
884 {name: 'July', short_name: 'Jul', numeric: '07', days: 31},
885 {name: 'August', short_name: 'Aug', numeric: '08', days: 31},
886 {name: 'September', short_name: 'Sep', numeric: '09', days: 30},
887 {name: 'October', short_name: 'Oct', numeric: '10', days: 31},
888 {name: 'November', short_name: 'Nov', numeric: '11', days: 30},
889 {name: 'December', short_name: 'Dec', numeric: '12', days: 31}
890 ];
891 };
892
893 Chance.prototype.second = function () {
894 return this.natural({max: 59});
895 };
896
897 Chance.prototype.timestamp = function () {
898 return this.natural({min: 1, max: parseInt(new Date().getTime() / 1000, 10)});
899 };
900
901 Chance.prototype.year = function (options) {
902 // Default to current year as min if none specified
903 options = initOptions(options, {min: new Date().getFullYear()});
904
905 // Default to one century after current year as max if none specified
906 options.max = (typeof options.max !== "undefined") ? options.max : options.min + 100;
907
908 return this.natural(options).toString();
909 };
910
911 // -- End Time
912
913 // -- Finance --
914
915 Chance.prototype.cc = function (options) {
916 options = initOptions(options);
917
918 var type, number, to_generate, type_name;
919
920 type = (options.type) ?
921 this.cc_type({ name: options.type, raw: true }) :
922 this.cc_type({ raw: true });
923 number = type.prefix.split("");
924 to_generate = type.length - type.prefix.length - 1;
925
926 // Generates n - 1 digits
927 for (var i = 0; i < to_generate; i++) {
928 number.push(this.integer({min: 0, max: 9}));
929 }
930
931 // Generates the last digit according to Luhn algorithm
932 number.push(this.luhn_calculate(number.join("")));
933
934 return number.join("");
935 };
936
937 Chance.prototype.cc_types = function () {
938 // http://en.wikipedia.org/wiki/Bank_card_number#Issuer_identification_number_.28IIN.29
939 return [
940 {name: "American Express", short_name: 'amex', prefix: '34', length: 15},
941 {name: "Bankcard", short_name: 'bankcard', prefix: '5610', length: 16},
942 {name: "China UnionPay", short_name: 'chinaunion', prefix: '62', length: 16},
943 {name: "Diners Club Carte Blanche", short_name: 'dccarte', prefix: '300', length: 14},
944 {name: "Diners Club enRoute", short_name: 'dcenroute', prefix: '2014', length: 15},
945 {name: "Diners Club International", short_name: 'dcintl', prefix: '36', length: 14},
946 {name: "Diners Club United States & Canada", short_name: 'dcusc', prefix: '54', length: 16},
947 {name: "Discover Card", short_name: 'discover', prefix: '6011', length: 16},
948 {name: "InstaPayment", short_name: 'instapay', prefix: '637', length: 16},
949 {name: "JCB", short_name: 'jcb', prefix: '3528', length: 16},
950 {name: "Laser", short_name: 'laser', prefix: '6304', length: 16},
951 {name: "Maestro", short_name: 'maestro', prefix: '5018', length: 16},
952 {name: "Mastercard", short_name: 'mc', prefix: '51', length: 16},
953 {name: "Solo", short_name: 'solo', prefix: '6334', length: 16},
954 {name: "Switch", short_name: 'switch', prefix: '4903', length: 16},
955 {name: "Visa", short_name: 'visa', prefix: '4', length: 16},
956 {name: "Visa Electron", short_name: 'electron', prefix: '4026', length: 16}
957 ];
958 };
959
960 Chance.prototype.cc_type = function (options) {
961 options = initOptions(options);
962 var types = this.cc_types(),
963 type = null;
964
965 if (options.name) {
966 for (var i = 0; i < types.length; i++) {
967 // Accept either name or short_name to specify card type
968 if (types[i].name === options.name || types[i].short_name === options.name) {
969 type = types[i];
970 break;
971 }
972 }
973 if (type === null) {
974 throw new Error("Credit card type '" + options.name + "'' is not supported");
975 }
976 } else {
977 type = this.pick(types);
978 }
979
980 return options.raw ? type : type.name;
981 };
982
983 Chance.prototype.dollar = function (options) {
984 // By default, a somewhat more sane max for dollar than all available numbers
985 options = initOptions(options, {max : 10000, min : 0});
986
987 var dollar = this.floating({min: options.min, max: options.max, fixed: 2}).toString(),
988 cents = dollar.split('.')[1];
989
990 if (cents === undefined) {
991 dollar += '.00';
992 } else if (cents.length < 2) {
993 dollar = dollar + '0';
994 }
995
996 if (dollar < 0) {
997 return '-$' + dollar.replace('-', '');
998 } else {
999 return '$' + dollar;
1000 }
1001 };
1002
1003 Chance.prototype.exp = function (options) {
1004 options = initOptions(options);
1005 var exp = {};
1006
1007 exp.year = this.exp_year();
1008
1009 // If the year is this year, need to ensure month is greater than the
1010 // current month or this expiration will not be valid
1011 if (exp.year === (new Date().getFullYear())) {
1012 exp.month = this.exp_month({future: true});
1013 } else {
1014 exp.month = this.exp_month();
1015 }
1016
1017 return options.raw ? exp : exp.month + '/' + exp.year;
1018 };
1019
1020 Chance.prototype.exp_month = function (options) {
1021 options = initOptions(options);
1022 var month, month_int;
1023
1024 if (options.future) {
1025 do {
1026 month = this.month({raw: true}).numeric;
1027 month_int = parseInt(month, 10);
1028 } while (month_int < new Date().getMonth());
1029 } else {
1030 month = this.month({raw: true}).numeric;
1031 }
1032
1033 return month;
1034 };
1035
1036 Chance.prototype.exp_year = function () {
1037 return this.year({max: new Date().getFullYear() + 10});
1038 };
1039
1040 // -- End Finance
1041
1042 // -- Miscellaneous --
1043
1044 // Dice - For all the board game geeks out there, myself included ;)
1045 Chance.prototype.d4 = function () { return this.natural({min: 1, max: 4}); };
1046 Chance.prototype.d6 = function () { return this.natural({min: 1, max: 6}); };
1047 Chance.prototype.d8 = function () { return this.natural({min: 1, max: 8}); };
1048 Chance.prototype.d10 = function () { return this.natural({min: 1, max: 10}); };
1049 Chance.prototype.d12 = function () { return this.natural({min: 1, max: 12}); };
1050 Chance.prototype.d20 = function () { return this.natural({min: 1, max: 20}); };
1051 Chance.prototype.d30 = function () { return this.natural({min: 1, max: 30}); };
1052 Chance.prototype.d100 = function () { return this.natural({min: 1, max: 100}); };
1053 Chance.prototype.rpg = function (thrown, options) {
1054 options = initOptions(options);
1055 if (thrown === null) {
1056 throw new Error("A type of die roll must be included");
1057 } else {
1058 var bits = thrown.toLowerCase().split("d"),
1059 rolls = [];
1060
1061 if (bits.length !== 2 || !parseInt(bits[0], 10) || !parseInt(bits[1], 10)) {
1062 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");
1063 }
1064 for (var i = bits[0]; i > 0; i--) {
1065 rolls[i - 1] = this.natural({min: 1, max: bits[1]});
1066 }
1067 return (typeof options.sum !== 'undefined' && options.sum) ? rolls.reduce(function (p, c) { return p + c; }) : rolls;
1068 }
1069 };
1070
1071 // Guid
1072 Chance.prototype.guid = function (options) {
1073 options = options || {version: 5};
1074
1075 var guid_pool = "ABCDEF1234567890",
1076 variant_pool = "AB89",
1077 guid = this.string({pool: guid_pool, length: 8}) + '-' +
1078 this.string({pool: guid_pool, length: 4}) + '-' +
1079 // The Version
1080 options.version +
1081 this.string({pool: guid_pool, length: 3}) + '-' +
1082 // The Variant
1083 this.string({pool: variant_pool, length: 1}) +
1084 this.string({pool: guid_pool, length: 3}) + '-' +
1085 this.string({pool: guid_pool, length: 12});
1086 return guid;
1087 };
1088
1089 // Hash
1090 Chance.prototype.hash = function (options) {
1091 options = initOptions(options, {length : 40, casing: 'lower'});
1092 var pool = options.casing === 'upper' ? HEX_POOL.toUpperCase() : HEX_POOL;
1093 return this.string({pool: pool, length: options.length});
1094 };
1095
1096 Chance.prototype.luhn_check = function (num) {
1097 var str = num.toString();
1098 var checkDigit = +str.substring(str.length - 1);
1099 return checkDigit === this.luhn_calculate(+str.substring(0, str.length - 1));
1100 };
1101
1102 Chance.prototype.luhn_calculate = function (num) {
1103 var digits = num.toString().split("").reverse();
1104 var sum = 0;
1105 for (var i = 0, l = digits.length; l > i; ++i) {
1106 var digit = +digits[i];
1107 if (i % 2 === 0) {
1108 digit *= 2;
1109 if (digit > 9) {
1110 digit -= 9;
1111 }
1112 }
1113 sum += digit;
1114 }
1115 return (sum * 9) % 10;
1116 };
1117
1118 Chance.prototype.mersenne_twister = function (seed) {
1119 return new MersenneTwister(seed);
1120 };
1121
1122 // -- End Miscellaneous --
1123
1124 Chance.prototype.VERSION = "0.5.5";
1125
1126 // Mersenne Twister from https://gist.github.com/banksean/300494
1127 var MersenneTwister = function (seed) {
1128 if (seed === undefined) {
1129 seed = new Date().getTime();
1130 }
1131 /* Period parameters */
1132 this.N = 624;
1133 this.M = 397;
1134 this.MATRIX_A = 0x9908b0df; /* constant vector a */
1135 this.UPPER_MASK = 0x80000000; /* most significant w-r bits */
1136 this.LOWER_MASK = 0x7fffffff; /* least significant r bits */
1137
1138 this.mt = new Array(this.N); /* the array for the state vector */
1139 this.mti = this.N + 1; /* mti==N + 1 means mt[N] is not initialized */
1140
1141 this.init_genrand(seed);
1142 };
1143
1144 /* initializes mt[N] with a seed */
1145 MersenneTwister.prototype.init_genrand = function (s) {
1146 this.mt[0] = s >>> 0;
1147 for (this.mti = 1; this.mti < this.N; this.mti++) {
1148 s = this.mt[this.mti - 1] ^ (this.mt[this.mti - 1] >>> 30);
1149 this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + this.mti;
1150 /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
1151 /* In the previous versions, MSBs of the seed affect */
1152 /* only MSBs of the array mt[]. */
1153 /* 2002/01/09 modified by Makoto Matsumoto */
1154 this.mt[this.mti] >>>= 0;
1155 /* for >32 bit machines */
1156 }
1157 };
1158
1159 /* initialize by an array with array-length */
1160 /* init_key is the array for initializing keys */
1161 /* key_length is its length */
1162 /* slight change for C++, 2004/2/26 */
1163 MersenneTwister.prototype.init_by_array = function (init_key, key_length) {
1164 var i = 1, j = 0, k, s;
1165 this.init_genrand(19650218);
1166 k = (this.N > key_length ? this.N : key_length);
1167 for (; k; k--) {
1168 s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30);
1169 this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) + init_key[j] + j; /* non linear */
1170 this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
1171 i++;
1172 j++;
1173 if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; }
1174 if (j >= key_length) { j = 0; }
1175 }
1176 for (k = this.N - 1; k; k--) {
1177 s = this.mt[i - 1] ^ (this.mt[i - 1] >>> 30);
1178 this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) - i; /* non linear */
1179 this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */
1180 i++;
1181 if (i >= this.N) { this.mt[0] = this.mt[this.N - 1]; i = 1; }
1182 }
1183
1184 this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */
1185 };
1186
1187 /* generates a random number on [0,0xffffffff]-interval */
1188 MersenneTwister.prototype.genrand_int32 = function () {
1189 var y;
1190 var mag01 = new Array(0x0, this.MATRIX_A);
1191 /* mag01[x] = x * MATRIX_A for x=0,1 */
1192
1193 if (this.mti >= this.N) { /* generate N words at one time */
1194 var kk;
1195
1196 if (this.mti === this.N + 1) { /* if init_genrand() has not been called, */
1197 this.init_genrand(5489); /* a default initial seed is used */
1198 }
1199 for (kk = 0; kk < this.N - this.M; kk++) {
1200 y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK);
1201 this.mt[kk] = this.mt[kk + this.M] ^ (y >>> 1) ^ mag01[y & 0x1];
1202 }
1203 for (;kk < this.N - 1; kk++) {
1204 y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk + 1]&this.LOWER_MASK);
1205 this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ (y >>> 1) ^ mag01[y & 0x1];
1206 }
1207 y = (this.mt[this.N - 1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK);
1208 this.mt[this.N - 1] = this.mt[this.M - 1] ^ (y >>> 1) ^ mag01[y & 0x1];
1209
1210 this.mti = 0;
1211 }
1212
1213 y = this.mt[this.mti++];
1214
1215 /* Tempering */
1216 y ^= (y >>> 11);
1217 y ^= (y << 7) & 0x9d2c5680;
1218 y ^= (y << 15) & 0xefc60000;
1219 y ^= (y >>> 18);
1220
1221 return y >>> 0;
1222 };
1223
1224 /* generates a random number on [0,0x7fffffff]-interval */
1225 MersenneTwister.prototype.genrand_int31 = function () {
1226 return (this.genrand_int32() >>> 1);
1227 };
1228
1229 /* generates a random number on [0,1]-real-interval */
1230 MersenneTwister.prototype.genrand_real1 = function () {
1231 return this.genrand_int32() * (1.0 / 4294967295.0);
1232 /* divided by 2^32-1 */
1233 };
1234
1235 /* generates a random number on [0,1)-real-interval */
1236 MersenneTwister.prototype.random = function () {
1237 return this.genrand_int32() * (1.0 / 4294967296.0);
1238 /* divided by 2^32 */
1239 };
1240
1241 /* generates a random number on (0,1)-real-interval */
1242 MersenneTwister.prototype.genrand_real3 = function () {
1243 return (this.genrand_int32() + 0.5) * (1.0 / 4294967296.0);
1244 /* divided by 2^32 */
1245 };
1246
1247 /* generates a random number on [0,1) with 53-bit resolution*/
1248 MersenneTwister.prototype.genrand_res53 = function () {
1249 var a = this.genrand_int32()>>>5, b = this.genrand_int32()>>>6;
1250 return (a * 67108864.0 + b) * (1.0 / 9007199254740992.0);
1251 };
1252
1253
1254 // CommonJS module
1255 if (typeof exports !== 'undefined') {
1256 if (typeof module !== 'undefined' && module.exports) {
1257 exports = module.exports = Chance;
1258 }
1259 exports.Chance = Chance;
1260 }
1261
1262 // Register as an anonymous AMD module
1263 if (typeof define === 'function' && define.amd) {
1264 define([], function () {
1265 return Chance;
1266 });
1267 }
1268
1269 // If there is a window object, that at least has a document property,
1270 // instantiate and define chance on the window
1271 if (typeof window === "object" && typeof window.document === "object") {
1272 window.Chance = Chance;
1273 window.chance = new Chance();
1274 }
1275 })();