stellar-wallet-js-sdk
Version:
> # :warning: Alpha version. Don't use in production.
1,615 lines (1,412 loc) • 1.01 MB
JavaScript
var StellarWallet =
/******/ (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] = {
/******/ exports: {},
/******/ id: moduleId,
/******/ loaded: false
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.loaded = 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;
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
module.exports = __webpack_require__(1);
/***/ },
/* 1 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(10);
var errors = __webpack_require__(2);
var Promise = __webpack_require__(23);
var protocol = __webpack_require__(4);
var util = {
crypto: __webpack_require__(5),
totp: __webpack_require__(6),
keypair: __webpack_require__(7)
};
var Wallet = __webpack_require__(3);
function createWalletObject(initData) {
var wallet = new Wallet(initData);
return Promise.resolve(wallet);
}
module.exports = {
createWallet: function(p) {
var params = _.cloneDeep(p);
return protocol.createWallet(params)
.then(createWalletObject);
},
getWallet: function(p) {
var params = _.cloneDeep(p);
return protocol.login(params)
.then(createWalletObject);
},
createFromData: function(initData) {
return new Wallet(initData);
},
lostTotpDevice: function(p) {
var params = _.cloneDeep(p);
return protocol.lostTotpDevice(params);
},
recover: function(p) {
var params = _.cloneDeep(p);
return protocol.showRecovery(params);
},
errors: errors,
util: {
generateRandomTotpKey: util.totp.generateRandomTotpKey,
generateRandomRecoveryCode: util.crypto.generateRandomRecoveryCode,
generateTotpUri: util.totp.generateTotpUri,
generateKeyPair: util.keypair.generateKeyPair
}
};
/***/ },
/* 2 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var util = __webpack_require__(28);
Error.subclass = function(errorName) {
var newError = function(message) {
this.name = errorName;
this.message = (message || "");
};
newError.subclass = this.subclass;
util.inherits(newError, this);
return newError;
};
var errors = module.exports;
errors.Forbidden = Error.subclass('Forbidden');
errors.WalletNotFound = Error.subclass('WalletNotFound');
errors.UsernameAlreadyTaken = Error.subclass('UsernameAlreadyTaken');
errors.InvalidUsername = Error.subclass('InvalidUsername');
errors.DataCorrupt = Error.subclass('DataCorrupt');
errors.InvalidField = Error.subclass('InvalidField');
errors.MissingField = Error.subclass('MissingField');
errors.TotpCodeRequired = Error.subclass('TotpCodeRequired');
errors.InvalidTotpCode = Error.subclass('InvalidTotpCode');
errors.ConnectionError = Error.subclass('ConnectionError');
errors.UnknownError = Error.subclass('UnknownError');
errors.getProtocolError = function(code) {
switch(code) {
case 'not_found': return new errors.WalletNotFound();
case 'already_taken': return new errors.UsernameAlreadyTaken();
case 'invalid_username': return new errors.InvalidUsername();
case 'invalid_totp_code': return new errors.InvalidTotpCode();
case 'forbidden': return new errors.Forbidden();
default: return new errors.UnknownError();
}
};
/***/ },
/* 3 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(10);
var crypto = __webpack_require__(5);
var errors = __webpack_require__(2);
var sjcl = __webpack_require__(9);
var nacl = __webpack_require__(26);
var Promise = __webpack_require__(23);
var protocol = __webpack_require__(4);
function Wallet(p) {
var params = _.cloneDeep(p);
var self = this;
var properties = [
'server',
'username',
'rawMasterKey',
'rawWalletId',
'rawWalletKey',
'rawMainData',
'rawKeychainData',
'lockVersion',
'totpEnabled'
];
_.each(properties, function(param) {
self[param] = params[param];
});
this.updateEncodedValues = function() {
this.masterKey = sjcl.codec.base64.fromBits(this.rawMasterKey);
this.walletId = sjcl.codec.base64.fromBits(this.rawWalletId);
this.walletKey = sjcl.codec.base64.fromBits(this.rawWalletKey);
};
// rtrim /
this.server = this.server.replace(/\/+$/g,'');
this.updateEncodedValues();
}
Wallet.prototype.getServer = function() {
return this.server;
};
Wallet.prototype.getUsername = function() {
return this.username;
};
Wallet.prototype.getWalletId = function() {
return this.walletId;
};
Wallet.prototype.getWalletKey = function() {
return this.walletKey;
};
Wallet.prototype.getMainData = function() {
return this.rawMainData;
};
Wallet.prototype.getKeychainData = function() {
return this.rawKeychainData;
};
Wallet.prototype.updateMainData = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'walletId',
'username',
'lockVersion',
'rawWalletKey'
]));
var self = this;
return protocol.updateMainData(params)
.then(function(updateData) {
self.lockVersion = updateData.newLockVersion;
self.rawMainData = updateData.rawMainData;
return Promise.resolve();
});
};
Wallet.prototype.delete = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'walletId',
'username',
'lockVersion'
]));
return protocol.deleteWallet(params);
};
Wallet.prototype.changePassword = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'username',
'walletId',
'rawMainData',
'rawKeychainData',
'lockVersion'
]));
var self = this;
return protocol.changePassword(params)
.then(function(updateData) {
self.rawWalletId = updateData.rawWalletId;
self.rawWalletKey = updateData.rawWalletKey;
self.rawMasterKey = updateData.rawMasterKey;
self.lockVersion = updateData.newLockVersion;
self.updateEncodedValues();
return Promise.resolve();
});
};
Wallet.prototype.isTotpEnabled = function() {
return this.totpEnabled;
};
Wallet.prototype.enableTotp = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'username',
'walletId',
'lockVersion'
]));
var self = this;
return protocol.enableTotp(params)
.then(function(updateData) {
self.lockVersion = updateData.newLockVersion;
self.totpEnabled = true;
return Promise.resolve();
});
};
Wallet.prototype.enableRecovery = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'username',
'walletId',
'masterKey',
'lockVersion'
]));
var self = this;
return protocol.enableRecovery(params)
.then(function(data) {
self.lockVersion = data.newLockVersion;
return Promise.resolve();
});
};
Wallet.prototype.disableTotp = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'username',
'walletId',
'lockVersion'
]));
var self = this;
return protocol.disableTotp(params)
.then(function(updateData) {
self.lockVersion = updateData.newLockVersion;
self.totpEnabled = false;
return Promise.resolve();
});
};
Wallet.prototype.updateLockVersion = function(p) {
var params = _.cloneDeep(p);
params = _.extend(params, _.pick(this, [
'server',
'username',
'walletId'
]));
var self = this;
return protocol.getLockVersion(params)
.then(function(lockVersion) {
self.lockVersion = lockVersion;
return Promise.resolve();
});
};
module.exports = Wallet;
/***/ },
/* 4 */
/***/ function(module, exports, __webpack_require__) {
'use strict';
var _ = __webpack_require__(10);
var camelCase = __webpack_require__(27);
var Promise = __webpack_require__(23);
module.exports = {};
// Add protocol methods
var protocolMethods = [
'login',
'create_wallet',
'change_password',
'update_main_data',
'enable_recovery',
'show_recovery',
'enable_totp',
'disable_totp',
'lost_totp_device',
'delete_wallet',
'get_lock_version'
];
_.each(protocolMethods, function(method) {
module.exports[camelCase(method)] = function(params) {
return Promise.resolve(params)
.then(__webpack_require__(8)("./"+method));
}
});
/***/ },
/* 5 */
/***/ function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(Buffer) {'use strict';
var _ = __webpack_require__(10);
var base58 = __webpack_require__(30);
var crypto = __webpack_require__(24);
var errors = __webpack_require__(2);
var nacl = __webpack_require__(26);
var sjcl = __webpack_require__(9);
module.exports = {
calculateMasterKey: calculateMasterKey,
bytesToWords: bytesToWords,
decryptData: decryptData,
deriveWalletId: generateDeriveFromKeyFunction('WALLET_ID'),
deriveWalletKey: generateDeriveFromKeyFunction('WALLET_KEY'),
encryptData: encryptData,
generateRandomRecoveryCode: generateRandomRecoveryCode,
sha1: makeHasher('sha1'),
sha256: makeHasher('sha256'),
signRequest: signRequest
};
function base64Encode(str) {
return (new Buffer(str)).toString('base64');
}
function base64Decode(str) {
return (new Buffer(str, 'base64')).toString();
}
function signRequest(username, walletId, secretKey) {
return function(request) {
var rawSecretKey = nacl.util.decodeBase64(secretKey);
var serializedData = nacl.util.decodeUTF8(JSON.stringify(request._data));
var signature = nacl.sign.detached(serializedData, rawSecretKey);
signature = nacl.util.encodeBase64(signature);
request.set('Authorization', 'STELLAR-WALLET-V2 username="'+username+'", wallet-id="'+walletId+'", signature="'+signature+'"');
}
}
function makeHasher(algo) {
return function(value) {
var hasher = crypto.createHash(algo);
return hasher.update(value).digest("hex");
};
}
function generateDeriveFromKeyFunction(token) {
return function(masterKey) {
var hmac = new sjcl.misc.hmac(masterKey, sjcl.hash.sha256);
return hmac.encrypt(token);
};
}
function encryptData(data, key) {
if (!_.isString(data)) {
throw new TypeError('data must be a String.');
}
var cipherName = 'aes';
var modeName = 'gcm';
var cipher = new sjcl.cipher[cipherName](key);
var rawIV = sjcl.random.randomWords(3);
var encryptedData = sjcl.mode[modeName].encrypt(
cipher,
sjcl.codec.utf8String.toBits(data),
rawIV
);
data = JSON.stringify({
IV: sjcl.codec.base64.fromBits(rawIV),
cipherText: sjcl.codec.base64.fromBits(encryptedData),
cipherName: cipherName,
modeName: modeName
});
return base64Encode(data);
}
function decryptData(encryptedData, key) {
var rawCipherText, rawIV, cipherName, modeName;
try {
var resultObject = JSON.parse(base64Decode(encryptedData));
rawIV = sjcl.codec.base64.toBits(resultObject.IV);
rawCipherText = sjcl.codec.base64.toBits(resultObject.cipherText);
cipherName = resultObject.cipherName;
modeName = resultObject.modeName;
} catch(e) {
new errors.DataCorrupt();
}
var cipher = new sjcl.cipher[cipherName](key);
var rawData = sjcl.mode[modeName].decrypt(cipher, rawCipherText, rawIV);
return sjcl.codec.utf8String.fromBits(rawData);
}
function calculateMasterKey(s0, username, password, kdfParams) {
var versionBits = sjcl.codec.hex.toBits("0x01");
var s0Bits = sjcl.codec.base64.toBits(s0);
var usernameBits = sjcl.codec.utf8String.toBits(username);
var unhashedSaltBits = _.reduce([versionBits, s0Bits, usernameBits], sjcl.bitArray.concat);
var salt = sjcl.hash.sha256.hash(unhashedSaltBits);
return sjcl.misc.scrypt(
password,
salt,
kdfParams.n,
kdfParams.r,
kdfParams.p,
kdfParams.bits / 8
);
}
function generateRandomRecoveryCode() {
var rawRecoveryKey = nacl.randomBytes(32);
return base58.encode(new Buffer(rawRecoveryKey));
}
function bytesToWords(bytes) {
for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
words[b >>> 5] |= bytes[i] << (24 - b % 32);
return words;
}
/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(25).Buffer))
/***/ },
/* 6 */
/***/ function(module, exports, __webpack_require__) {
var _ = __webpack_require__(10);
var errors = __webpack_require__(2);
var nacl = __webpack_require__(26);
var base32 = __webpack_require__(32);
function generateRandomTotpKey() {
var key = nacl.randomBytes(10);
// Google Authenticator doesn't like ='s in the end
return base32.encode(key).toString().replace(/=/g,'');
}
function generateTotpUri(key, meta) {
var throwMissingField = function(field) {
var e = new errors.MissingField();
e.field = field;
throw e;
};
var fields = ['issuer', 'accountName'];
_.each(fields, function(field) {
if (!_.isString(meta[field])) {
throwMissingField(field);
}
});
meta = _.mapValues(meta, encodeURI);
return 'otpauth://totp/'+meta.issuer+':'+meta.accountName+'?secret='+key+'&issuer='+meta.issuer;
}
module.exports = {
generateRandomTotpKey: generateRandomTotpKey,
generateTotpUri: generateTotpUri
};
/***/ },
/* 7 */
/***/ function(module, exports, __webpack_require__) {
var _ = __webpack_require__(10);
var errors = __webpack_require__(2);
var nacl = __webpack_require__(26);
var Seed = __webpack_require__(38).Seed;
function generateKeyPair(seed) {
if(seed){
seed = new Seed().parse_json(seed);
} else {
seed = new Seed().random();
}
var keyPair = seed.get_key();
var address = keyPair.get_address();
var publicKey = nacl.util.encodeBase64(keyPair._pubkey);
var secretKey = nacl.util.encodeBase64(keyPair._secret);
return {
address: address.to_json(),
secret: seed.to_json(),
secretKey: secretKey,
publicKey: publicKey
};
}
module.exports = {
generateKeyPair: generateKeyPair
};
/***/ },
/* 8 */
/***/ function(module, exports, __webpack_require__) {
var map = {
"./change_password": 11,
"./change_password.js": 11,
"./common": 12,
"./common.js": 12,
"./create_wallet": 13,
"./create_wallet.js": 13,
"./delete_wallet": 14,
"./delete_wallet.js": 14,
"./disable_totp": 15,
"./disable_totp.js": 15,
"./enable_recovery": 16,
"./enable_recovery.js": 16,
"./enable_totp": 17,
"./enable_totp.js": 17,
"./get_lock_version": 18,
"./get_lock_version.js": 18,
"./index": 4,
"./index.js": 4,
"./login": 19,
"./login.js": 19,
"./lost_totp_device": 20,
"./lost_totp_device.js": 20,
"./show_recovery": 21,
"./show_recovery.js": 21,
"./update_main_data": 22,
"./update_main_data.js": 22
};
function webpackContext(req) {
return __webpack_require__(webpackContextResolve(req));
};
function webpackContextResolve(req) {
return map[req] || (function() { throw new Error("Cannot find module '" + req + "'.") }());
};
webpackContext.keys = function webpackContextKeys() {
return Object.keys(map);
};
webpackContext.resolve = webpackContextResolve;
module.exports = webpackContext;
webpackContext.id = 8;
/***/ },
/* 9 */
/***/ function(module, exports, __webpack_require__) {
// We're using stellar-lib's Seed to generate address and keypair in keypair.js.
// However, for some legacy browsers we need to add some entropy to sjcl using
// crypto.ensureEntropy method. Rather then doing this for both instances
// (stellar-wallet-js-sdk & stellar-lib) let's switch to stellar-lib's sjcl.
var sjcl = __webpack_require__(39).sjcl;
__webpack_require__(33).extendSjcl(sjcl);
var randomWords = sjcl.random.randomWords;
sjcl.random.randomWords = function(nwords) {
if (!sjcl.random.isReady()) {
for (var i = 0; i < 8; i++) {
sjcl.random.addEntropy(Math.random(), 32, "Math.random()");
}
if (!sjcl.random.isReady()) {
throw "Unable to seed sjcl entropy pool";
}
}
return randomWords.call(sjcl.random, nwords);
};
module.exports = sjcl;
/***/ },
/* 10 */
/***/ function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_RESULT__;/* WEBPACK VAR INJECTION */(function(module, global) {/**
* @license
* Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
* Build: `lodash -o ./dist/lodash.compat.js`
* Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
* Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Available under MIT license <http://lodash.com/license>
*/
;(function() {
/** Used as a safe reference for `undefined` in pre ES5 environments */
var undefined;
/** Used to pool arrays and objects used internally */
var arrayPool = [],
objectPool = [];
/** Used to generate unique IDs */
var idCounter = 0;
/** Used internally to indicate various things */
var indicatorObject = {};
/** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
var keyPrefix = +new Date + '';
/** Used as the size when optimizations are enabled for large arrays */
var largeArraySize = 75;
/** Used as the max size of the `arrayPool` and `objectPool` */
var maxPoolSize = 40;
/** Used to detect and test whitespace */
var whitespace = (
// whitespace
' \t\x0B\f\xA0\ufeff' +
// line terminators
'\n\r\u2028\u2029' +
// unicode category "Zs" space separators
'\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
);
/** Used to match empty string literals in compiled template source */
var reEmptyStringLeading = /\b__p \+= '';/g,
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
/**
* Used to match ES6 template delimiters
* http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
/** Used to match regexp flags from their coerced string values */
var reFlags = /\w*$/;
/** Used to detected named functions */
var reFuncName = /^\s*function[ \n\r\t]+\w/;
/** Used to match "interpolate" template delimiters */
var reInterpolate = /<%=([\s\S]+?)%>/g;
/** Used to match leading whitespace and zeros to be removed */
var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
/** Used to detect functions containing a `this` reference */
var reThis = /\bthis\b/;
/** Used to match unescaped characters in compiled string literals */
var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
/** Used to assign default `context` object properties */
var contextProps = [
'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object',
'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
'parseInt', 'setTimeout'
];
/** Used to fix the JScript [[DontEnum]] bug */
var shadowedProps = [
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'toLocaleString', 'toString', 'valueOf'
];
/** Used to make template sourceURLs easier to identify */
var templateCounter = 0;
/** `Object#toString` result shortcuts */
var argsClass = '[object Arguments]',
arrayClass = '[object Array]',
boolClass = '[object Boolean]',
dateClass = '[object Date]',
errorClass = '[object Error]',
funcClass = '[object Function]',
numberClass = '[object Number]',
objectClass = '[object Object]',
regexpClass = '[object RegExp]',
stringClass = '[object String]';
/** Used to identify object classifications that `_.clone` supports */
var cloneableClasses = {};
cloneableClasses[funcClass] = false;
cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
cloneableClasses[boolClass] = cloneableClasses[dateClass] =
cloneableClasses[numberClass] = cloneableClasses[objectClass] =
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
/** Used as an internal `_.debounce` options object */
var debounceOptions = {
'leading': false,
'maxWait': 0,
'trailing': false
};
/** Used as the property descriptor for `__bindData__` */
var descriptor = {
'configurable': false,
'enumerable': false,
'value': null,
'writable': false
};
/** Used as the data object for `iteratorTemplate` */
var iteratorData = {
'args': '',
'array': null,
'bottom': '',
'firstArg': '',
'init': '',
'keys': null,
'loop': '',
'shadowedProps': null,
'support': null,
'top': '',
'useHas': false
};
/** Used to determine if values are of the language type Object */
var objectTypes = {
'boolean': false,
'function': true,
'object': true,
'number': false,
'string': false,
'undefined': false
};
/** Used to escape characters for inclusion in compiled string literals */
var stringEscapes = {
'\\': '\\',
"'": "'",
'\n': 'n',
'\r': 'r',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
/** Used as a reference to the global object */
var root = (objectTypes[typeof window] && window) || this;
/** Detect free variable `exports` */
var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
/** Detect free variable `module` */
var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
/** Detect the popular CommonJS extension `module.exports` */
var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
/** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
var freeGlobal = objectTypes[typeof global] && global;
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
root = freeGlobal;
}
/*--------------------------------------------------------------------------*/
/**
* The base implementation of `_.indexOf` without support for binary searches
* or `fromIndex` constraints.
*
* @private
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {number} [fromIndex=0] The index to search from.
* @returns {number} Returns the index of the matched value or `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array ? array.length : 0;
while (++index < length) {
if (array[index] === value) {
return index;
}
}
return -1;
}
/**
* An implementation of `_.contains` for cache objects that mimics the return
* signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
*
* @private
* @param {Object} cache The cache object to inspect.
* @param {*} value The value to search for.
* @returns {number} Returns `0` if `value` is found, else `-1`.
*/
function cacheIndexOf(cache, value) {
var type = typeof value;
cache = cache.cache;
if (type == 'boolean' || value == null) {
return cache[value] ? 0 : -1;
}
if (type != 'number' && type != 'string') {
type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value;
cache = (cache = cache[type]) && cache[key];
return type == 'object'
? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
: (cache ? 0 : -1);
}
/**
* Adds a given value to the corresponding cache object.
*
* @private
* @param {*} value The value to add to the cache.
*/
function cachePush(value) {
var cache = this.cache,
type = typeof value;
if (type == 'boolean' || value == null) {
cache[value] = true;
} else {
if (type != 'number' && type != 'string') {
type = 'object';
}
var key = type == 'number' ? value : keyPrefix + value,
typeCache = cache[type] || (cache[type] = {});
if (type == 'object') {
(typeCache[key] || (typeCache[key] = [])).push(value);
} else {
typeCache[key] = true;
}
}
}
/**
* Used by `_.max` and `_.min` as the default callback when a given
* collection is a string value.
*
* @private
* @param {string} value The character to inspect.
* @returns {number} Returns the code unit of given character.
*/
function charAtCallback(value) {
return value.charCodeAt(0);
}
/**
* Used by `sortBy` to compare transformed `collection` elements, stable sorting
* them in ascending order.
*
* @private
* @param {Object} a The object to compare to `b`.
* @param {Object} b The object to compare to `a`.
* @returns {number} Returns the sort order indicator of `1` or `-1`.
*/
function compareAscending(a, b) {
var ac = a.criteria,
bc = b.criteria,
index = -1,
length = ac.length;
while (++index < length) {
var value = ac[index],
other = bc[index];
if (value !== other) {
if (value > other || typeof value == 'undefined') {
return 1;
}
if (value < other || typeof other == 'undefined') {
return -1;
}
}
}
// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
// that causes it, under certain circumstances, to return the same value for
// `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
//
// This also ensures a stable sort in V8 and other engines.
// See http://code.google.com/p/v8/issues/detail?id=90
return a.index - b.index;
}
/**
* Creates a cache object to optimize linear searches of large arrays.
*
* @private
* @param {Array} [array=[]] The array to search.
* @returns {null|Object} Returns the cache object or `null` if caching should not be used.
*/
function createCache(array) {
var index = -1,
length = array.length,
first = array[0],
mid = array[(length / 2) | 0],
last = array[length - 1];
if (first && typeof first == 'object' &&
mid && typeof mid == 'object' && last && typeof last == 'object') {
return false;
}
var cache = getObject();
cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
var result = getObject();
result.array = array;
result.cache = cache;
result.push = cachePush;
while (++index < length) {
result.push(array[index]);
}
return result;
}
/**
* Used by `template` to escape characters for inclusion in compiled
* string literals.
*
* @private
* @param {string} match The matched character to escape.
* @returns {string} Returns the escaped character.
*/
function escapeStringChar(match) {
return '\\' + stringEscapes[match];
}
/**
* Gets an array from the array pool or creates a new one if the pool is empty.
*
* @private
* @returns {Array} The array from the pool.
*/
function getArray() {
return arrayPool.pop() || [];
}
/**
* Gets an object from the object pool or creates a new one if the pool is empty.
*
* @private
* @returns {Object} The object from the pool.
*/
function getObject() {
return objectPool.pop() || {
'array': null,
'cache': null,
'criteria': null,
'false': false,
'index': 0,
'null': false,
'number': null,
'object': null,
'push': null,
'string': null,
'true': false,
'undefined': false,
'value': null
};
}
/**
* Checks if `value` is a DOM node in IE < 9.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`.
*/
function isNode(value) {
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
// methods that are `typeof` "string" and still can coerce nodes to strings
return typeof value.toString != 'function' && typeof (value + '') == 'string';
}
/**
* Releases the given array back to the array pool.
*
* @private
* @param {Array} [array] The array to release.
*/
function releaseArray(array) {
array.length = 0;
if (arrayPool.length < maxPoolSize) {
arrayPool.push(array);
}
}
/**
* Releases the given object back to the object pool.
*
* @private
* @param {Object} [object] The object to release.
*/
function releaseObject(object) {
var cache = object.cache;
if (cache) {
releaseObject(cache);
}
object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
if (objectPool.length < maxPoolSize) {
objectPool.push(object);
}
}
/**
* Slices the `collection` from the `start` index up to, but not including,
* the `end` index.
*
* Note: This function is used instead of `Array#slice` to support node lists
* in IE < 9 and to ensure dense arrays are returned.
*
* @private
* @param {Array|Object|string} collection The collection to slice.
* @param {number} start The start index.
* @param {number} end The end index.
* @returns {Array} Returns the new array.
*/
function slice(array, start, end) {
start || (start = 0);
if (typeof end == 'undefined') {
end = array ? array.length : 0;
}
var index = -1,
length = end - start || 0,
result = Array(length < 0 ? 0 : length);
while (++index < length) {
result[index] = array[start + index];
}
return result;
}
/*--------------------------------------------------------------------------*/
/**
* Create a new `lodash` function using the given context object.
*
* @static
* @memberOf _
* @category Utilities
* @param {Object} [context=root] The context object.
* @returns {Function} Returns the `lodash` function.
*/
function runInContext(context) {
// Avoid issues with some ES3 environments that attempt to use values, named
// after built-in constructors like `Object`, for the creation of literals.
// ES5 clears this up by stating that literals must use built-in constructors.
// See http://es5.github.io/#x11.1.5.
context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
/** Native constructor references */
var Array = context.Array,
Boolean = context.Boolean,
Date = context.Date,
Error = context.Error,
Function = context.Function,
Math = context.Math,
Number = context.Number,
Object = context.Object,
RegExp = context.RegExp,
String = context.String,
TypeError = context.TypeError;
/**
* Used for `Array` method references.
*
* Normally `Array.prototype` would suffice, however, using an array literal
* avoids issues in Narwhal.
*/
var arrayRef = [];
/** Used for native method references */
var errorProto = Error.prototype,
objectProto = Object.prototype,
stringProto = String.prototype;
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = context._;
/** Used to resolve the internal [[Class]] of values */
var toString = objectProto.toString;
/** Used to detect if a method is native */
var reNative = RegExp('^' +
String(toString)
.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
.replace(/toString| for [^\]]+/g, '.*?') + '$'
);
/** Native method shortcuts */
var ceil = Math.ceil,
clearTimeout = context.clearTimeout,
floor = Math.floor,
fnToString = Function.prototype.toString,
getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
hasOwnProperty = objectProto.hasOwnProperty,
push = arrayRef.push,
propertyIsEnumerable = objectProto.propertyIsEnumerable,
setTimeout = context.setTimeout,
splice = arrayRef.splice,
unshift = arrayRef.unshift;
/** Used to set meta data on functions */
var defineProperty = (function() {
// IE 8 only accepts DOM elements
try {
var o = {},
func = isNative(func = Object.defineProperty) && func,
result = func(o, o, o) && func;
} catch(e) { }
return result;
}());
/* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = context.isFinite,
nativeIsNaN = context.isNaN,
nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
nativeMax = Math.max,
nativeMin = Math.min,
nativeParseInt = context.parseInt,
nativeRandom = Math.random;
/** Used to lookup a built-in constructor by [[Class]] */
var ctorByClass = {};
ctorByClass[arrayClass] = Array;
ctorByClass[boolClass] = Boolean;
ctorByClass[dateClass] = Date;
ctorByClass[funcClass] = Function;
ctorByClass[objectClass] = Object;
ctorByClass[numberClass] = Number;
ctorByClass[regexpClass] = RegExp;
ctorByClass[stringClass] = String;
/** Used to avoid iterating non-enumerable properties in IE < 9 */
var nonEnumProps = {};
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
nonEnumProps[errorClass] = nonEnumProps[funcClass] = nonEnumProps[regexpClass] = { 'constructor': true, 'toString': true };
nonEnumProps[objectClass] = { 'constructor': true };
(function() {
var length = shadowedProps.length;
while (length--) {
var key = shadowedProps[length];
for (var className in nonEnumProps) {
if (hasOwnProperty.call(nonEnumProps, className) && !hasOwnProperty.call(nonEnumProps[className], key)) {
nonEnumProps[className][key] = false;
}
}
}
}());
/*--------------------------------------------------------------------------*/
/**
* Creates a `lodash` object which wraps the given value to enable intuitive
* method chaining.
*
* In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
* `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
* and `unshift`
*
* Chaining is supported in custom builds as long as the `value` method is
* implicitly or explicitly included in the build.
*
* The chainable wrapper functions are:
* `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
* `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
* `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
* `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
* `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
* `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
* `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
* `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
* `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
* `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
* and `zip`
*
* The non-chainable wrapper functions are:
* `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
* `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
* `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
* `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
* `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
* `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
* `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
* `template`, `unescape`, `uniqueId`, and `value`
*
* The wrapper functions `first` and `last` return wrapped values when `n` is
* provided, otherwise they return unwrapped values.
*
* Explicit chaining can be enabled by using the `_.chain` method.
*
* @name _
* @constructor
* @category Chaining
* @param {*} value The value to wrap in a `lodash` instance.
* @returns {Object} Returns a `lodash` instance.
* @example
*
* var wrapped = _([1, 2, 3]);
*
* // returns an unwrapped value
* wrapped.reduce(function(sum, num) {
* return sum + num;
* });
* // => 6
*
* // returns a wrapped value
* var squares = wrapped.map(function(num) {
* return num * num;
* });
*
* _.isArray(squares);
* // => false
*
* _.isArray(squares.value());
* // => true
*/
function lodash(value) {
// don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
? value
: new lodashWrapper(value);
}
/**
* A fast path for creating `lodash` wrapper objects.
*
* @private
* @param {*} value The value to wrap in a `lodash` instance.
* @param {boolean} chainAll A flag to enable chaining for all methods
* @returns {Object} Returns a `lodash` instance.
*/
function lodashWrapper(value, chainAll) {
this.__chain__ = !!chainAll;
this.__wrapped__ = value;
}
// ensure `new lodashWrapper` is an instance of `lodash`
lodashWrapper.prototype = lodash.prototype;
/**
* An object used to flag environments features.
*
* @static
* @memberOf _
* @type Object
*/
var support = lodash.support = {};
(function() {
var ctor = function() { this.x = 1; },
object = { '0': 1, 'length': 1 },
props = [];
ctor.prototype = { 'valueOf': 1, 'y': 1 };
for (var key in new ctor) { props.push(key); }
for (key in arguments) { }
/**
* Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
*
* @memberOf _.support
* @type boolean
*/
support.argsClass = toString.call(arguments) == argsClass;
/**
* Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5).
*
* @memberOf _.support
* @type boolean
*/
support.argsObject = arguments.constructor == Object && !(arguments instanceof Array);
/**
* Detect if `name` or `message` properties of `Error.prototype` are
* enumerable by default. (IE < 9, Safari < 5.1)
*
* @memberOf _.support
* @type boolean
*/
support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name');
/**
* Detect if `prototype` properties are enumerable by default.
*
* Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
* (if the prototype or a property on the prototype has been set)
* incorrectly sets a function's `prototype` property [[Enumerable]]
* value to `true`.
*
* @memberOf _.support
* @type boolean
*/
support.enumPrototypes = propertyIsEnumerable.call(ctor, 'prototype');
/**
* Detect if functions can be decompiled by `Function#toString`
* (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
*
* @memberOf _.support
* @type boolean
*/
support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
/**
* Detect if `Function#name` is supported (all but IE).
*
* @memberOf _.support
* @type boolean
*/
support.funcNames = typeof Function.name == 'string';
/**
* Detect if `arguments` object indexes are non-enumerable
* (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1).
*
* @memberOf _.support
* @type boolean
*/
support.nonEnumArgs = key != 0;
/**
* Detect if properties shadowing those on `Object.prototype` are non-enumerable.
*
* In IE < 9 an objects own properties, shadowing non-enumerable ones, are
* made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug).
*
* @memberOf _.support
* @type boolean
*/
support.nonEnumShadows = !/valueOf/.test(props);
/**
* Detect if own properties are iterated after inherited properties (all but IE < 9).
*
* @memberOf _.support
* @type boolean
*/
support.ownLast = props[0] != 'x';
/**
* Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
*
* Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
* and `splice()` functions that fail to remove the last element, `value[0]`,
* of array-like objects even though the `length` property is set to `0`.
* The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
* is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
*
* @memberOf _.support
* @type boolean
*/
support.spliceObjects = (arrayRef.splice.call(object, 0, 1), !object[0]);
/**
* Detect lack of support for accessing string characters by index.
*
* IE < 8 can't access characters by index and IE 8 can only access
* characters by index on string literals.
*
* @memberOf _.support
* @type boolean
*/
support.unindexedChars = ('x'[0] + Object('x')[0]) != 'xx';
/**
* Detect if a DOM node's [[Class]] is resolvable (all but IE < 9)
* and that the JS engine errors when attempting to coerce an object to
* a string without a `toString` function.
*
* @memberOf _.support
* @type boolean
*/
try {
support.nodeClass = !(toString.call(document) == objectClass && !({ 'toString': 0 } + ''));
} catch(e) {
support.nodeClass = true;
}
}(1));
/**
* By default, the template delimiters used by Lo-Dash are similar to those in
* embedded Ruby (ERB). Change the following template settings to use alternative
* delimiters.
*
* @static
* @memberOf _
* @type Object
*/
lodash.templateSettings = {
/**
* Used to detect `data` property values to be HTML-escaped.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'escape': /<%-([\s\S]+?)%>/g,
/**
* Used to detect code to be evaluated.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'evaluate': /<%([\s\S]+?)%>/g,
/**
* Used to detect `data` property values to inject.
*
* @memberOf _.templateSettings
* @type RegExp
*/
'interpolate': reInterpolate,
/**
* Used to reference the data object in the template text.
*
* @memberOf _.templateSettings
* @type string
*/
'variable': '',
/**
* Used to import variables into the compiled template.
*
* @memberOf _.templateSettings
* @type Object
*/
'imports': {
/**
* A reference to the `lodash` function.
*
* @memberOf _.templateSettings.imports
* @type Function
*/
'_': lodash
}
};
/*--------------------------------------------------------------------------*/
/**
* The template used to create iterator functions.
*
* @private
* @param {Object} data The data object used to populate the text.
* @returns {string} Returns the interpolated text.
*/
var iteratorTemplate = function(obj) {
var __p = 'var index, iterable = ' +
(obj.firstArg) +
', result = ' +
(obj.init) +
';\nif (!iterable) return result;\n' +
(obj.top) +
';';
if (obj.array) {
__p += '\nvar length = iterable.length; index = -1;\nif (' +
(obj.array) +
') { ';
if (support.unindexedChars) {
__p += '\n if (isString(iterable)) {\n iterable = iterable.split(\'\')\n } ';
}
__p += '\n while (++index < length) {\n ' +
(obj.loop) +
';\n }\n}\nelse { ';
} else if (support.nonEnumArgs) {
__p += '\n var length = iterable.length; index = -1;\n if (length && isArguments(iterable)) {\n while (++index < length) {\n index += \'\';\n ' +
(obj.loop) +
';\n }\n } else { ';
}
if (support.enumPrototypes) {
__p += '\n var skipProto = typeof iterable == \'function\';\n ';
}
if (support.enumErrorProps) {
__p += '\n var skipErrorProps = iterable === errorProto || iterable instanceof Error;\n ';
}
var conditions = []; if (support.enumPrototypes) { conditions.push('!(skipProto && index == "prototype")'); } if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (index == "message" || index == "name"))'); }
if (obj.useHas && obj.keys) {
__p += '\n var ownIndex = -1,\n ownProps = objectTypes[typeof iterable] && keys(iterable),\n length = ownProps ? ownProps.length : 0;\n\n while (++ownIndex < length) {\n index = ownProps[ownIndex];\n';
if (conditions.length) {
__p += ' if (' +
(conditions.join(' && ')) +
') {\n ';
}
__p +=
(obj.loop) +
'; ';
if (conditions.length) {
__p += '\n }';
}
__p += '\n } ';
} else {
__p += '\n for (index in iterable) {\n';
if (obj.useHas) { conditions.push("hasOwnProperty.call(iterable, index)"); } if (conditions.length) {
__p += ' if (' +
(conditions.join(' && ')) +
') {\n ';
}
__p +=
(obj.loop) +
'; ';
if (conditions.length) {
__p += '\n }';
}
__p += '\n } ';
if (support.nonEnumShadows) {
__p += '\n\n if (iterable !== objectProto) {\n var ctor = iterable.constructor,\n isProto = iterable === (ctor && ctor.prototype),\n className = iterable === stringProto ? stringClass : iterable === errorProto ? errorClass : toString.call(iterable),\n nonEnum = nonEnumProps[className];\n ';
for (k = 0; k < 7; k++) {
__p += '\n index = \'' +
(obj.shadowedProps[k]) +
'\';\n if ((!(isProto && nonEnum[index]) && hasOwnProperty.call(iterable, index))';
if (!obj.useHas) {
__p += ' || (!nonEnum[index] && iterable[index] !== objectProto[index])';
}