tempmail.js
Version:
Programatically generate and receive mail from temporary email addresses. Works in browsers and in node.js.
1,645 lines (1,397 loc) • 400 kB
JavaScript
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, {
/******/ configurable: false,
/******/ enumerable: true,
/******/ get: getter
/******/ });
/******/ }
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 1);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
const IS_NODE = exports.IS_NODE = typeof window === 'undefined';
const API_BASE = exports.API_BASE = 'https://api.temp-mail.ru/request/';
const API_FORMAT = exports.API_FORMAT = '/format/' + (IS_NODE ? 'json' : 'jsonp?callback=messages');
const ENDPOINT_INBOX = exports.ENDPOINT_INBOX = 'mail/id';
const ENDPOINT_DOMAINS = exports.ENDPOINT_DOMAINS = 'domains';
const ENDPOINT_DELETE = exports.ENDPOINT_DELETE = 'delete/id';
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
__webpack_require__(2);
__webpack_require__(4);
var _constants = __webpack_require__(0);
var _utils = __webpack_require__(5);
function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; }
class TempMail {
/**
* @constructor
* @param {string} address - A temp-mail.ru email address or a prefix. Generated if not provided.
*/
constructor(address) {
this.active = false;
this._useOrCreateEmail(address).then(emailAddress => {
if (!emailAddress) {
throw new Error("Address unable to be created");
}
this.address = emailAddress;
this.address_id = (0, _utils.emailId)(this.address);
this.fetch = _constants.IS_NODE ? fetch : fetchJsonp;
this.active = true;
});
}
/**
* Get Mail
*/
getMail() {
var _this = this;
return _asyncToGenerator(function* () {
try {
let response = yield _this.fetch((0, _utils.inboxURL)(_this.address_id));
let messages = yield response.json();
if (messages.length) {
return messages.map(_utils.formatMessage);
}
return messages;
} catch (error) {
console.error(error);
}
return [];
})();
}
/**
* Address Domains
*/
domains() {
var _this2 = this;
return _asyncToGenerator(function* () {
try {
let domains = yield _this2.fetch((0, _utils.domainsURL)());
return yield domains.json();
} catch (error) {
console.error(error);
}
return [];
})();
}
/**
* Delete Message
*/
deleteMessage(message_id) {
var _this3 = this;
return _asyncToGenerator(function* () {
try {
let deletedMessage = yield _this3.fetch((0, _utils.deleteMessageURL)(message_id));
return yield deletedMessage.json();
} catch (error) {
console.error(error);
}
return [];
})();
}
// Private
/**
* Generate a random email from domains and prefix
*/
_useOrCreateEmail(addressPrefix) {
var _this4 = this;
return _asyncToGenerator(function* () {
if (addressPrefix.includes("@")) {
return addressPrefix;
}
try {
const domains = yield _this4.domains();
return `${addressPrefix}@${chance.pickone(domains)}`;
} catch (error) {
console.error(error);
}
return false;
})();
}
}
exports.default = TempMail;
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
// the whatwg-fetch polyfill installs the fetch() function
// on the global object (window or self)
//
// Return that as the export for use in Webpack, Browserify etc.
__webpack_require__(3);
module.exports = self.fetch.bind(self);
/***/ }),
/* 3 */
/***/ (function(module, exports) {
(function(self) {
'use strict';
if (self.fetch) {
return
}
var support = {
searchParams: 'URLSearchParams' in self,
iterable: 'Symbol' in self && 'iterator' in Symbol,
blob: 'FileReader' in self && 'Blob' in self && (function() {
try {
new Blob()
return true
} catch(e) {
return false
}
})(),
formData: 'FormData' in self,
arrayBuffer: 'ArrayBuffer' in self
}
if (support.arrayBuffer) {
var viewClasses = [
'[object Int8Array]',
'[object Uint8Array]',
'[object Uint8ClampedArray]',
'[object Int16Array]',
'[object Uint16Array]',
'[object Int32Array]',
'[object Uint32Array]',
'[object Float32Array]',
'[object Float64Array]'
]
var isDataView = function(obj) {
return obj && DataView.prototype.isPrototypeOf(obj)
}
var isArrayBufferView = ArrayBuffer.isView || function(obj) {
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
}
}
function normalizeName(name) {
if (typeof name !== 'string') {
name = String(name)
}
if (/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) {
throw new TypeError('Invalid character in header field name')
}
return name.toLowerCase()
}
function normalizeValue(value) {
if (typeof value !== 'string') {
value = String(value)
}
return value
}
// Build a destructive iterator for the value list
function iteratorFor(items) {
var iterator = {
next: function() {
var value = items.shift()
return {done: value === undefined, value: value}
}
}
if (support.iterable) {
iterator[Symbol.iterator] = function() {
return iterator
}
}
return iterator
}
function Headers(headers) {
this.map = {}
if (headers instanceof Headers) {
headers.forEach(function(value, name) {
this.append(name, value)
}, this)
} else if (Array.isArray(headers)) {
headers.forEach(function(header) {
this.append(header[0], header[1])
}, this)
} else if (headers) {
Object.getOwnPropertyNames(headers).forEach(function(name) {
this.append(name, headers[name])
}, this)
}
}
Headers.prototype.append = function(name, value) {
name = normalizeName(name)
value = normalizeValue(value)
var oldValue = this.map[name]
this.map[name] = oldValue ? oldValue+','+value : value
}
Headers.prototype['delete'] = function(name) {
delete this.map[normalizeName(name)]
}
Headers.prototype.get = function(name) {
name = normalizeName(name)
return this.has(name) ? this.map[name] : null
}
Headers.prototype.has = function(name) {
return this.map.hasOwnProperty(normalizeName(name))
}
Headers.prototype.set = function(name, value) {
this.map[normalizeName(name)] = normalizeValue(value)
}
Headers.prototype.forEach = function(callback, thisArg) {
for (var name in this.map) {
if (this.map.hasOwnProperty(name)) {
callback.call(thisArg, this.map[name], name, this)
}
}
}
Headers.prototype.keys = function() {
var items = []
this.forEach(function(value, name) { items.push(name) })
return iteratorFor(items)
}
Headers.prototype.values = function() {
var items = []
this.forEach(function(value) { items.push(value) })
return iteratorFor(items)
}
Headers.prototype.entries = function() {
var items = []
this.forEach(function(value, name) { items.push([name, value]) })
return iteratorFor(items)
}
if (support.iterable) {
Headers.prototype[Symbol.iterator] = Headers.prototype.entries
}
function consumed(body) {
if (body.bodyUsed) {
return Promise.reject(new TypeError('Already read'))
}
body.bodyUsed = true
}
function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result)
}
reader.onerror = function() {
reject(reader.error)
}
})
}
function readBlobAsArrayBuffer(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsArrayBuffer(blob)
return promise
}
function readBlobAsText(blob) {
var reader = new FileReader()
var promise = fileReaderReady(reader)
reader.readAsText(blob)
return promise
}
function readArrayBufferAsText(buf) {
var view = new Uint8Array(buf)
var chars = new Array(view.length)
for (var i = 0; i < view.length; i++) {
chars[i] = String.fromCharCode(view[i])
}
return chars.join('')
}
function bufferClone(buf) {
if (buf.slice) {
return buf.slice(0)
} else {
var view = new Uint8Array(buf.byteLength)
view.set(new Uint8Array(buf))
return view.buffer
}
}
function Body() {
this.bodyUsed = false
this._initBody = function(body) {
this._bodyInit = body
if (!body) {
this._bodyText = ''
} else if (typeof body === 'string') {
this._bodyText = body
} else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
this._bodyBlob = body
} else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
this._bodyFormData = body
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this._bodyText = body.toString()
} else if (support.arrayBuffer && support.blob && isDataView(body)) {
this._bodyArrayBuffer = bufferClone(body.buffer)
// IE 10-11 can't handle a DataView body.
this._bodyInit = new Blob([this._bodyArrayBuffer])
} else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
this._bodyArrayBuffer = bufferClone(body)
} else {
throw new Error('unsupported BodyInit type')
}
if (!this.headers.get('content-type')) {
if (typeof body === 'string') {
this.headers.set('content-type', 'text/plain;charset=UTF-8')
} else if (this._bodyBlob && this._bodyBlob.type) {
this.headers.set('content-type', this._bodyBlob.type)
} else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8')
}
}
}
if (support.blob) {
this.blob = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return Promise.resolve(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(new Blob([this._bodyArrayBuffer]))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as blob')
} else {
return Promise.resolve(new Blob([this._bodyText]))
}
}
this.arrayBuffer = function() {
if (this._bodyArrayBuffer) {
return consumed(this) || Promise.resolve(this._bodyArrayBuffer)
} else {
return this.blob().then(readBlobAsArrayBuffer)
}
}
}
this.text = function() {
var rejected = consumed(this)
if (rejected) {
return rejected
}
if (this._bodyBlob) {
return readBlobAsText(this._bodyBlob)
} else if (this._bodyArrayBuffer) {
return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))
} else if (this._bodyFormData) {
throw new Error('could not read FormData body as text')
} else {
return Promise.resolve(this._bodyText)
}
}
if (support.formData) {
this.formData = function() {
return this.text().then(decode)
}
}
this.json = function() {
return this.text().then(JSON.parse)
}
return this
}
// HTTP methods whose capitalization should be normalized
var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT']
function normalizeMethod(method) {
var upcased = method.toUpperCase()
return (methods.indexOf(upcased) > -1) ? upcased : method
}
function Request(input, options) {
options = options || {}
var body = options.body
if (input instanceof Request) {
if (input.bodyUsed) {
throw new TypeError('Already read')
}
this.url = input.url
this.credentials = input.credentials
if (!options.headers) {
this.headers = new Headers(input.headers)
}
this.method = input.method
this.mode = input.mode
if (!body && input._bodyInit != null) {
body = input._bodyInit
input.bodyUsed = true
}
} else {
this.url = String(input)
}
this.credentials = options.credentials || this.credentials || 'omit'
if (options.headers || !this.headers) {
this.headers = new Headers(options.headers)
}
this.method = normalizeMethod(options.method || this.method || 'GET')
this.mode = options.mode || this.mode || null
this.referrer = null
if ((this.method === 'GET' || this.method === 'HEAD') && body) {
throw new TypeError('Body not allowed for GET or HEAD requests')
}
this._initBody(body)
}
Request.prototype.clone = function() {
return new Request(this, { body: this._bodyInit })
}
function decode(body) {
var form = new FormData()
body.trim().split('&').forEach(function(bytes) {
if (bytes) {
var split = bytes.split('=')
var name = split.shift().replace(/\+/g, ' ')
var value = split.join('=').replace(/\+/g, ' ')
form.append(decodeURIComponent(name), decodeURIComponent(value))
}
})
return form
}
function parseHeaders(rawHeaders) {
var headers = new Headers()
rawHeaders.split(/\r?\n/).forEach(function(line) {
var parts = line.split(':')
var key = parts.shift().trim()
if (key) {
var value = parts.join(':').trim()
headers.append(key, value)
}
})
return headers
}
Body.call(Request.prototype)
function Response(bodyInit, options) {
if (!options) {
options = {}
}
this.type = 'default'
this.status = 'status' in options ? options.status : 200
this.ok = this.status >= 200 && this.status < 300
this.statusText = 'statusText' in options ? options.statusText : 'OK'
this.headers = new Headers(options.headers)
this.url = options.url || ''
this._initBody(bodyInit)
}
Body.call(Response.prototype)
Response.prototype.clone = function() {
return new Response(this._bodyInit, {
status: this.status,
statusText: this.statusText,
headers: new Headers(this.headers),
url: this.url
})
}
Response.error = function() {
var response = new Response(null, {status: 0, statusText: ''})
response.type = 'error'
return response
}
var redirectStatuses = [301, 302, 303, 307, 308]
Response.redirect = function(url, status) {
if (redirectStatuses.indexOf(status) === -1) {
throw new RangeError('Invalid status code')
}
return new Response(null, {status: status, headers: {location: url}})
}
self.Headers = Headers
self.Request = Request
self.Response = Response
self.fetch = function(input, init) {
return new Promise(function(resolve, reject) {
var request = new Request(input, init)
var xhr = new XMLHttpRequest()
xhr.onload = function() {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || '')
}
options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL')
var body = 'response' in xhr ? xhr.response : xhr.responseText
resolve(new Response(body, options))
}
xhr.onerror = function() {
reject(new TypeError('Network request failed'))
}
xhr.ontimeout = function() {
reject(new TypeError('Network request failed'))
}
xhr.open(request.method, request.url, true)
if (request.credentials === 'include') {
xhr.withCredentials = true
}
if ('responseType' in xhr && support.blob) {
xhr.responseType = 'blob'
}
request.headers.forEach(function(value, name) {
xhr.setRequestHeader(name, value)
})
xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit)
})
}
self.fetch.polyfill = true
})(typeof self !== 'undefined' ? self : this);
/***/ }),
/* 4 */
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (global, factory) {
if (true) {
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports, module], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
factory(exports, module);
} else {
var mod = {
exports: {}
};
factory(mod.exports, mod);
global.fetchJsonp = mod.exports;
}
})(this, function (exports, module) {
'use strict';
var defaultOptions = {
timeout: 5000,
jsonpCallback: 'callback',
jsonpCallbackFunction: null
};
function generateCallbackFunction() {
return 'jsonp_' + Date.now() + '_' + Math.ceil(Math.random() * 100000);
}
function clearFunction(functionName) {
// IE8 throws an exception when you try to delete a property on window
// http://stackoverflow.com/a/1824228/751089
try {
delete window[functionName];
} catch (e) {
window[functionName] = undefined;
}
}
function removeScript(scriptId) {
var script = document.getElementById(scriptId);
if (script) {
document.getElementsByTagName('head')[0].removeChild(script);
}
}
function fetchJsonp(_url) {
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1];
// to avoid param reassign
var url = _url;
var timeout = options.timeout || defaultOptions.timeout;
var jsonpCallback = options.jsonpCallback || defaultOptions.jsonpCallback;
var timeoutId = undefined;
return new Promise(function (resolve, reject) {
var callbackFunction = options.jsonpCallbackFunction || generateCallbackFunction();
var scriptId = jsonpCallback + '_' + callbackFunction;
window[callbackFunction] = function (response) {
resolve({
ok: true,
// keep consistent with fetch API
json: function json() {
return Promise.resolve(response);
}
});
if (timeoutId) clearTimeout(timeoutId);
removeScript(scriptId);
clearFunction(callbackFunction);
};
// Check if the user set their own params, and if not add a ? to start a list of params
url += url.indexOf('?') === -1 ? '?' : '&';
var jsonpScript = document.createElement('script');
jsonpScript.setAttribute('src', '' + url + jsonpCallback + '=' + callbackFunction);
if (options.charset) {
jsonpScript.setAttribute('charset', options.charset);
}
jsonpScript.id = scriptId;
document.getElementsByTagName('head')[0].appendChild(jsonpScript);
timeoutId = setTimeout(function () {
reject(new Error('JSONP request to ' + _url + ' timed out'));
clearFunction(callbackFunction);
removeScript(scriptId);
window[callbackFunction] = function () {
clearFunction(callbackFunction);
};
}, timeout);
// Caught if got 404/500
jsonpScript.onerror = function () {
reject(new Error('JSONP request to ' + _url + ' failed'));
clearFunction(callbackFunction);
removeScript(scriptId);
if (timeoutId) clearTimeout(timeoutId);
};
});
}
// export as global function
/*
let local;
if (typeof global !== 'undefined') {
local = global;
} else if (typeof self !== 'undefined') {
local = self;
} else {
try {
local = Function('return this')();
} catch (e) {
throw new Error('polyfill failed because global object is unavailable in this environment');
}
}
local.fetchJsonp = fetchJsonp;
*/
module.exports = fetchJsonp;
});
/***/ }),
/* 5 */
/***/ (function(module, exports, __webpack_require__) {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.formatMessage = formatMessage;
exports.randomEmailPrefix = randomEmailPrefix;
exports.emailId = emailId;
exports.endpoint = endpoint;
exports.inboxURL = inboxURL;
exports.domainsURL = domainsURL;
exports.deleteMessageURL = deleteMessageURL;
var _chance = __webpack_require__(6);
var _chance2 = _interopRequireDefault(_chance);
var _constants = __webpack_require__(0);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Format Mail
* @param {object} message
*/
function formatMessage(msg) {
return {
id: msg.mail_id,
uid: msg.mail_unique_id,
from: msg.mail_from,
subject: msg.mail_subject,
preview: msg.mail_preview,
text: msg.mail_preview.replace(/\s/g, ' ').trim(),
text_only: msg.mail_text_only,
html: msg.mail_html,
timestamp: new Date(parseInt(msg.mail_timestamp + '000'))
};
}
/**
* Generate random tempmail address prefix
* @returns {string} addressPrefix
*/
function randomEmailPrefix() {
const name = _chance2.default.name({ middle_initial: true }).toLowerCase().split(' ').join('.');
const year = _chance2.default.integer({ min: 1970, max: new Date().getFullYear() - 8 });
return `${name}-${year}`;
}
/**
* Gives same ID for email address
* @returns {string} ID
*/
function emailId(emailAddress) {
let code = emailAddress.split("").map(c => c.codePointAt(0)).join("");
return parseInt(code).toString(16).substr(2, 12);
}
/**
* Assemble API url
* @param {string} endpoint
* @returns {string}
*/
function endpoint(endpoint) {
return _constants.API_BASE + endpoint + _constants.API_FORMAT;
}
/**
* Inbox Endpoint URL
* @param {string} address - MD5 hashed email address to fetch
* @return {string} url
*/
function inboxURL(address) {
return endpoint(_constants.ENDPOINT_INBOX + '/' + address);
}
/**
* Domains Endpoint URL
* @return {string} url
*/
function domainsURL() {
return endpoint(_constants.ENDPOINT_DOMAINS);
}
/**
* Delete Message Endpoint URL
* @return {string} url
*/
function deleteMessageURL(message_id) {
return endpoint(_constants.ENDPOINT_DELETE + '/' + message_id);
}
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(Buffer) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Chance.js 1.0.13
// 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)) {
if (!seed) { seed = null; } // handle other non-truthy seeds, as described in issue #322
return seed === null ? new Chance() : new Chance(seed);
}
// if user has provided a function, use that as the generator
if (typeof seed === 'function') {
this.random = seed;
return this;
}
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 receive a blank
for (var i = 0; i < arguments.length; i++) {
var seedling = 0;
if (Object.prototype.toString.call(arguments[i]) === '[object String]') {
for (var j = 0; j < arguments[i].length; j++) {
// create a numeric hash for each argument, add to seedling
var hash = 0;
for (var k = 0; k < arguments[i].length; k++) {
hash = arguments[i].charCodeAt(k) + (hash << 6) + (hash << 16) - hash;
}
seedling += hash;
}
} 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.bimd5 = this.blueimp_md5();
this.random = function () {
return this.mt.random(this.seed);
};
return this;
}
Chance.prototype.VERSION = "1.0.13";
// 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 --
/**
* Return a random bool, either true or false
*
* @param {Object} [options={ likelihood: 50 }] alter the likelihood of
* receiving a true or false value back.
* @throws {RangeError} if the likelihood is out of bounds
* @returns {Bool} either true or false
*/
Chance.prototype.bool = function (options) {
// likelihood of success (true)
options = initOptions(options, {likelihood : 50});
// Note, we could get some minor perf optimizations by checking range
// prior to initializing defaults, but that makes code a bit messier
// and the check more complicated as we have to check existence of
// the object then existence of the key before checking constraints.
// Since the options initialization should be minor computationally,
// decision made for code cleanliness intentionally. This is mentioned
// here as it's the first occurrence, will not be mentioned again.
testRange(
options.likelihood < 0 || options.likelihood > 100,
"Chance: Likelihood accepts values from 0 to 100."
);
return this.random() * 100 < options.likelihood;
};
Chance.prototype.animal = function (options){
//returns a random animal
options = initOptions(options);
if(typeof options.type !== 'undefined'){
//if user does not put in a valid animal type, user will get an error
testRange(
!this.get("animals")[options.type.toLowerCase()],
"Please pick from desert, ocean, grassland, forest, zoo, pets, farm."
);
//if user does put in valid animal type, will return a random animal of that type
return this.pick(this.get("animals")[options.type.toLowerCase()]);
}
//if user does not put in any animal type, will return a random animal regardless
animalTypeArray = ["desert","forest","ocean","zoo","farm","pet","grassland"];
return this.pick(this.get("animals")[this.pick(animalTypeArray)]);
};
/**
* Return a random character.
*
* @param {Object} [options={}] can specify a character pool, only alpha,
* only symbols, and casing (lower or upper)
* @returns {String} a single random character
* @throws {RangeError} Can only specify alpha or symbols, not both
*/
Chance.prototype.character = function (options) {
options = initOptions(options);
testRange(
options.alpha && options.symbols,
"Chance: Cannot specify both alpha and symbols."
);
var symbols = "!@#$%^&*()[]",
letters, pool;
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
/**
* Return a random floating point number
*
* @param {Object} [options={}] can specify a fixed precision, min, max
* @returns {Number} a single floating point number
* @throws {RangeError} Can only specify fixed or precision, not both. Also
* min cannot be greater than max
*/
Chance.prototype.floating = function (options) {
options = initOptions(options, {fixed : 4});
testRange(
options.fixed && options.precision,
"Chance: Cannot specify both fixed and precision."
);
var num;
var fixed = Math.pow(10, options.fixed);
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);
};
/**
* Return a random integer
*
* NOTE the max and min are INCLUDED in the range. So:
* chance.integer({min: 1, max: 3});
* would return either 1, 2, or 3.
*
* @param {Object} [options={}] can specify a min and/or max
* @returns {Number} a single random integer number
* @throws {RangeError} min cannot be greater than max
*/
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);
};
/**
* Return a random natural
*
* NOTE the max and min are INCLUDED in the range. So:
* chance.natural({min: 1, max: 3});
* would return either 1, 2, or 3.
*
* @param {Object} [options={}] can specify a min and/or maxm or a numerals count.
* @returns {Number} a single random integer number
* @throws {RangeError} min cannot be greater than max
*/
Chance.prototype.natural = function (options) {
options = initOptions(options, {min: 0, max: MAX_INT});
if (typeof options.numerals === 'number'){
testRange(options.numerals < 1, "Chance: Numerals cannot be less than one.");
options.min = Math.pow(10, options.numerals - 1);
options.max = Math.pow(10, options.numerals) - 1;
}
testRange(options.min < 0, "Chance: Min cannot be less than zero.");
return this.integer(options);
};
/**
* Return a random hex number as string
*
* NOTE the max and min are INCLUDED in the range. So:
* chance.hex({min: '9', max: 'B'});
* would return either '9', 'A' or 'B'.
*
* @param {Object} [options={}] can specify a min and/or max and/or casing
* @returns {String} a single random string hex number
* @throws {RangeError} min cannot be greater than max
*/
Chance.prototype.hex = function (options) {
options = initOptions(options, {min: 0, max: MAX_INT, casing: 'lower'});
testRange(options.min < 0, "Chance: Min cannot be less than zero.");
var integer = this.natural({min: options.min, max: options.max});
if (options.casing === 'upper') {
return integer.toString(16).toUpperCase();
}
return integer.toString(16);
};
Chance.prototype.letter = function(options) {
options = initOptions(options, {casing: 'lower'});
var pool = "abcdefghijklmnopqrstuvwxyz";
var letter = this.character({pool: pool});
if (options.casing === 'upper') {
letter = letter.toUpperCase();
}
return letter;
}
/**
* Return a random string
*
* @param {Object} [options={}] can specify a length
* @returns {String} a string of random length
* @throws {RangeError} length cannot be less than zero
*/
Chance.prototype.string = function (options) {
options = initOptions(options, { length: this.natural({min: 5, max: 20}) });
testRange(options.length < 0, "Chance: Length cannot be less than zero.");
var length = options.length,
text = this.n(this.character, length, options);
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.
*
* @param {Function} fn the function that generates something random
* @param {Number} num number of terms to generate
* @param {Object} options any options to pass on to the generator function
* @returns {Array} an array of length `num` with every item generated by `fn` and unique
*
* There can be more parameters after these. All additional parameters are provided to the given function
*/
Chance.prototype.unique = function(fn, num, options) {
testRange(
typeof fn !== "function",
"Chance: The first argument must be a function."
);
var comparator = function(arr, val) { return arr.indexOf(val) !== -1; };
if (options) {
comparator = options.comparator || comparator;
}
var arr = [], count = 0, result, MAX_DUPLICATES = num * 50, params = slice.call(arguments, 2);
while (arr.length < num) {
var clonedParams = JSON.parse(JSON.stringify(params));
result = fn.apply(this, clonedParams);
if (!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 {Function} fn the function that generates something random
* @param {Number} n number of terms to generate
* @returns {Array} an array of length `n` with items generated by `fn`
*
* There can be more parameters after these. All additional parameters are provided to the given function
*/
Chance.prototype.n = function(fn, n) {
testRange(
typeof fn !== "function",
"Chance: The first argument must be a function."
);
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;
};
// DEPRECATED on 2015-10-01
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);
}
};
// Given an array, returns a single random element
Chance.prototype.pickone = function (arr) {
if (arr.length === 0) {
throw new RangeError("Chance: Cannot pickone() from an empty array");
}
return arr[this.natural({max: arr.length - 1})];
};
// Given an array, returns a random set with 'count' elements
Chance.prototype.pickset = function (arr, count) {
if (count === 0) {
return [];
}
if (arr.length === 0) {
throw new RangeError("Chance: Cannot pickset() from an empty array");
}
if (count < 0) {
throw new RangeError("Chance: Count must be a positive number");
}
if (!count || count === 1) {
return [ this.pickone(arr) ];
} 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, trim) {
if (arr.length !== weights.length) {
throw new RangeError("Chance: Length of array and weights must match");
}
// scan weights array and sum valid entries
var sum = 0;
var val;
for (var weightIndex = 0; weightIndex < weights.length; ++weightIndex) {
val = weights[weightIndex];
if (isNaN(val)) {
throw new RangeError("Chance: All weights must be numbers");
}
if (val > 0) {
sum += val;
}
}
if (sum === 0) {
throw new RangeError("Chance: No valid entries in array weights");
}
// select a value within range
var selected = this.random() * sum;
// find array entry corresponding to selected value
var total = 0;
var lastGoodIdx = -1;
var chosenIdx;
for (weightIndex = 0; weightIndex < weights.length; ++weightIndex) {
val = weights[weightIndex];
total += val;
if (val > 0) {
if (selected <= total) {
chosenIdx = weightIndex;
break;
}
lastGoodIdx = weightIndex;
}
// handle any possible rounding error comparison to ensure something is picked
if (weightIndex === (weights.length - 1)) {
chosenIdx = lastGoodIdx;
}
}
var chosen = arr[chosenIdx];
trim = (typeof trim === 'undefined') ? false : trim;
if (trim) {
arr.splice(chosenIdx, 1);
weights.splice(chosenIdx, 1);
}
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}),
punctuation = options.punctuation,
text, word_array = this.n(this.word, words);
text = word_array.join(' ');
// Capitalize first letter of sentence
text = this.capitalize(text);
// Make sure punctuation has a usable value
if (punctuation !== false && !/^[\.\?;!:]$/.test(punctuation)) {
punctuation = '.';
}
// Add punctuation mark
if (punctuation) {
text += punctuation;
}
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;
}
if (options.capitalize) {
text = this.capitalize(text);
}
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();
}
}
if (options.capitalize) {
text = this.capitalize(text);
}
return text;
};
// -- End Text --
// -- Person --
Chance.prototype.age = function (options) {
options = initOptions(options);
var ageRange;
switch (options.type) {
case 'child':
ageRange = {min: 0, 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: 0, max: 100};
break;
default:
ageRange = {min: 18, max: 65};
break;
}
return this.natural(ageRange);
};
Chance.prototype.birthday = function (options) {
var age = this.age(options);
var currentYear = new Date().getFullYear();
if (options && options.type) {
var min = new Date();
var max = new Date();
min.setFullYear(currentYear - age - 1);
max.setFullYear(currentYear - age);
options = initOptions(options, {
min: min,
max: max
});
} else {
options = initOptions(options, {
year: currentYear - age
});
}
return this.date(options);
};
// CPF; ID to identify taxpayers in Brazil
Chance.prototype.cpf = function (options) {
options = initOptions(options, {
formatted: true
});
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;
}
var cpf = ''+n[0]+n[1]+n[2]+'.'+n[3]+n[4]+n[5]+'.'+n[6]+n[7]+n[8]+'-'+d1+d2;
return options.formatted ? cpf : cpf.replace(/\D/g,'');
};
// CNPJ: ID to identify companies in Brazil
Chance.prototype.cnpj = function (options) {
options = initOptions(options, {
formatted: true
});
var n = this.n(this.natural, 12, { max: 12 });
var d1 = n[11]*2+n[10]*3+n[9]*4+n[8]*5+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<2) {
d1 = 0;
}
var d2 = d1*2+n[11]*3+n[10]*4+n[9]*5+n[8]*6+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<2) {