Upgrading to Underscore.js 1.1.3.

This commit is contained in:
Samuel Clay 2010-12-15 16:11:10 -05:00
parent cc36baeb18
commit 549baed775

View file

@ -1,37 +1,36 @@
// Underscore.js // Underscore.js 1.1.3
// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc. // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the terms of the MIT license. // Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired by or borrowed from Prototype.js, // Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating. // Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation: // For all details and documentation:
// http://documentcloud.github.com/underscore // http://documentcloud.github.com/underscore
(function() { (function() {
// ------------------------- Baseline setup ---------------------------------
// Establish the root object, "window" in the browser, or "global" on the server. // Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `global` on the server.
var root = this; var root = this;
// Save the previous value of the "_" variable. // Save the previous value of the `_` variable.
var previousUnderscore = root._; var previousUnderscore = root._;
// Establish the object that gets thrown to break out of a loop iteration. // Establish the object that gets returned to break out of a loop iteration.
var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__'; var breaker = {};
// Quick regexp-escaping function, because JS doesn't have RegExp.escape().
var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };
// Save bytes in the minified (but not gzipped) version: // Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype; var ArrayProto = Array.prototype, ObjProto = Object.prototype;
// Create quick reference variables for speed access to core prototypes. // Create quick reference variables for speed access to core prototypes.
var slice = ArrayProto.slice, var slice = ArrayProto.slice,
unshift = ArrayProto.unshift, unshift = ArrayProto.unshift,
toString = ObjProto.toString, toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty, hasOwnProperty = ObjProto.hasOwnProperty;
propertyIsEnumerable = ObjProto.propertyIsEnumerable;
// All ECMA5 native implementations we hope to use are declared here. // All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var var
nativeForEach = ArrayProto.forEach, nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map, nativeMap = ArrayProto.map,
@ -48,39 +47,44 @@
// Create a safe reference to the Underscore object for use below. // Create a safe reference to the Underscore object for use below.
var _ = function(obj) { return new wrapper(obj); }; var _ = function(obj) { return new wrapper(obj); };
// Export the Underscore object for CommonJS. // Export the Underscore object for **CommonJS**, with backwards-compatibility
if (typeof exports !== 'undefined') exports._ = _; // for the old `require()` API. If we're not in CommonJS, add `_` to the
// global object.
// Export underscore to global scope. if (typeof module !== 'undefined' && module.exports) {
root._ = _; module.exports = _;
_._ = _;
} else {
root._ = _;
}
// Current version. // Current version.
_.VERSION = '1.1.0'; _.VERSION = '1.1.3';
// ------------------------ Collection Functions: --------------------------- // Collection Functions
// --------------------
// The cornerstone, an each implementation. // The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects implementing forEach, arrays, and raw objects. // Handles objects implementing `forEach`, arrays, and raw objects.
// Delegates to JavaScript 1.6's native forEach if available. // Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.forEach = function(obj, iterator, context) { var each = _.each = _.forEach = function(obj, iterator, context) {
try { var value;
if (nativeForEach && obj.forEach === nativeForEach) { if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context); obj.forEach(iterator, context);
} else if (_.isNumber(obj.length)) { } else if (_.isNumber(obj.length)) {
for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj); for (var i = 0, l = obj.length; i < l; i++) {
} else { if (iterator.call(context, obj[i], i, obj) === breaker) return;
for (var key in obj) { }
if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj); } else {
for (var key in obj) {
if (hasOwnProperty.call(obj, key)) {
if (iterator.call(context, obj[key], key, obj) === breaker) return;
} }
} }
} catch(e) {
if (e != breaker) throw e;
} }
return obj;
}; };
// Return the results of applying the iterator to each element. // Return the results of applying the iterator to each element.
// Delegates to JavaScript 1.6's native map if available. // Delegates to **ECMAScript 5**'s native `map` if available.
_.map = function(obj, iterator, context) { _.map = function(obj, iterator, context) {
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
var results = []; var results = [];
@ -90,45 +94,51 @@
return results; return results;
}; };
// Reduce builds up a single result from a list of values, aka inject, or foldl. // **Reduce** builds up a single result from a list of values, aka `inject`,
// Delegates to JavaScript 1.8's native reduce if available. // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = function(obj, iterator, memo, context) { _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = memo !== void 0;
if (nativeReduce && obj.reduce === nativeReduce) { if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context); if (context) iterator = _.bind(iterator, context);
return obj.reduce(iterator, memo); return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
} }
each(obj, function(value, index, list) { each(obj, function(value, index, list) {
memo = iterator.call(context, memo, value, index, list); if (!initial && index === 0) {
memo = value;
} else {
memo = iterator.call(context, memo, value, index, list);
}
}); });
return memo; return memo;
}; };
// The right-associative version of reduce, also known as foldr. Uses // The right-associative version of reduce, also known as `foldr`.
// Delegates to JavaScript 1.8's native reduceRight if available. // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = function(obj, iterator, memo, context) { _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context); if (context) iterator = _.bind(iterator, context);
return obj.reduceRight(iterator, memo); return memo !== void 0 ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
} }
var reversed = _.clone(_.toArray(obj)).reverse(); var reversed = (_.isArray(obj) ? obj.slice() : _.toArray(obj)).reverse();
return _.reduce(reversed, iterator, memo, context); return _.reduce(reversed, iterator, memo, context);
}; };
// Return the first value which passes a truth test. // Return the first value which passes a truth test. Aliased as `detect`.
_.detect = function(obj, iterator, context) { _.find = _.detect = function(obj, iterator, context) {
var result; var result;
each(obj, function(value, index, list) { any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) { if (iterator.call(context, value, index, list)) {
result = value; result = value;
_.breakLoop(); return true;
} }
}); });
return result; return result;
}; };
// Return all the elements that pass a truth test. // Return all the elements that pass a truth test.
// Delegates to JavaScript 1.6's native filter if available. // Delegates to **ECMAScript 5**'s native `filter` if available.
_.filter = function(obj, iterator, context) { // Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
var results = []; var results = [];
each(obj, function(value, index, list) { each(obj, function(value, index, list) {
@ -147,53 +157,56 @@
}; };
// Determine whether all of the elements match a truth test. // Determine whether all of the elements match a truth test.
// Delegates to JavaScript 1.6's native every if available. // Delegates to **ECMAScript 5**'s native `every` if available.
_.every = function(obj, iterator, context) { // Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator = iterator || _.identity; iterator = iterator || _.identity;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
var result = true; var result = true;
each(obj, function(value, index, list) { each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop(); if (!(result = result && iterator.call(context, value, index, list))) return breaker;
}); });
return result; return result;
}; };
// Determine if at least one element in the object matches a truth test. // Determine if at least one element in the object matches a truth test.
// Delegates to JavaScript 1.6's native some if available. // Delegates to **ECMAScript 5**'s native `some` if available.
_.some = function(obj, iterator, context) { // Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator = iterator || _.identity; iterator = iterator || _.identity;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
var result = false; var result = false;
each(obj, function(value, index, list) { each(obj, function(value, index, list) {
if (result = iterator.call(context, value, index, list)) _.breakLoop(); if (result = iterator.call(context, value, index, list)) return breaker;
}); });
return result; return result;
}; };
// Determine if a given value is included in the array or object using '==='. // Determine if a given value is included in the array or object using `===`.
_.include = function(obj, target) { // Aliased as `contains`.
_.include = _.contains = function(obj, target) {
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
var found = false; var found = false;
each(obj, function(value) { any(obj, function(value) {
if (found = value === target) _.breakLoop(); if (found = value === target) return true;
}); });
return found; return found;
}; };
// Invoke a method with arguments on every item in a collection. // Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) { _.invoke = function(obj, method) {
var args = _.rest(arguments, 2); var args = slice.call(arguments, 2);
return _.map(obj, function(value) { return _.map(obj, function(value) {
return (method ? value[method] : value).apply(value, args); return (method ? value[method] : value).apply(value, args);
}); });
}; };
// Convenience version of a common use case of map: fetching a property. // Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) { _.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; }); return _.map(obj, function(value){ return value[key]; });
}; };
// Return the maximum item or (item-based computation). // Return the maximum element or (element-based computation).
_.max = function(obj, iterator, context) { _.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
var result = {computed : -Infinity}; var result = {computed : -Infinity};
@ -240,7 +253,7 @@
return low; return low;
}; };
// Convert anything iterable into a real, live array. // Safely convert anything iterable into a real, live array.
_.toArray = function(iterable) { _.toArray = function(iterable) {
if (!iterable) return []; if (!iterable) return [];
if (iterable.toArray) return iterable.toArray(); if (iterable.toArray) return iterable.toArray();
@ -254,20 +267,21 @@
return _.toArray(obj).length; return _.toArray(obj).length;
}; };
// -------------------------- Array Functions: ------------------------------ // Array Functions
// ---------------
// Get the first element of an array. Passing "n" will return the first N // Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as "head". The "guard" check allows it to work // values in the array. Aliased as `head`. The **guard** check allows it to work
// with _.map. // with `_.map`.
_.first = function(array, n, guard) { _.first = _.head = function(array, n, guard) {
return n && !guard ? slice.call(array, 0, n) : array[0]; return n && !guard ? slice.call(array, 0, n) : array[0];
}; };
// Returns everything but the first entry of the array. Aliased as "tail". // Returns everything but the first entry of the array. Aliased as `tail`.
// Especially useful on the arguments object. Passing an "index" will return // Especially useful on the arguments object. Passing an **index** will return
// the rest of the values in the array from that index onward. The "guard" // the rest of the values in the array from that index onward. The **guard**
//check allows it to work with _.map. // check allows it to work with `_.map`.
_.rest = function(array, index, guard) { _.rest = _.tail = function(array, index, guard) {
return slice.call(array, _.isUndefined(index) || guard ? 1 : index); return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
}; };
@ -292,13 +306,14 @@
// Return a version of the array that does not contain the specified value(s). // Return a version of the array that does not contain the specified value(s).
_.without = function(array) { _.without = function(array) {
var values = _.rest(arguments); var values = slice.call(arguments, 1);
return _.filter(array, function(value){ return !_.include(values, value); }); return _.filter(array, function(value){ return !_.include(values, value); });
}; };
// Produce a duplicate-free version of the array. If the array has already // Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm. // been sorted, you have the option of using a faster algorithm.
_.uniq = function(array, isSorted) { // Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted) {
return _.reduce(array, function(memo, el, i) { return _.reduce(array, function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el; if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo[memo.length] = el;
return memo; return memo;
@ -308,7 +323,7 @@
// Produce an array that contains every item shared between all the // Produce an array that contains every item shared between all the
// passed-in arrays. // passed-in arrays.
_.intersect = function(array) { _.intersect = function(array) {
var rest = _.rest(arguments); var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) { return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) { return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0; return _.indexOf(other, item) >= 0;
@ -319,17 +334,17 @@
// Zip together multiple lists into a single array -- elements that share // Zip together multiple lists into a single array -- elements that share
// an index go together. // an index go together.
_.zip = function() { _.zip = function() {
var args = _.toArray(arguments); var args = slice.call(arguments);
var length = _.max(_.pluck(args, 'length')); var length = _.max(_.pluck(args, 'length'));
var results = new Array(length); var results = new Array(length);
for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i); for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
return results; return results;
}; };
// If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurence of an // we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array. // item in an array, or -1 if the item is not included in the array.
// Delegates to JavaScript 1.8's native indexOf if available. // Delegates to **ECMAScript 5**'s native `indexOf` if available.
_.indexOf = function(array, item) { _.indexOf = function(array, item) {
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item); if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i; for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
@ -337,7 +352,7 @@
}; };
// Delegates to JavaScript 1.6's native lastIndexOf if available. // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item) { _.lastIndexOf = function(array, item) {
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item); if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
var i = array.length; var i = array.length;
@ -346,36 +361,40 @@
}; };
// Generate an integer Array containing an arithmetic progression. A port of // Generate an integer Array containing an arithmetic progression. A port of
// the native Python range() function. See: // the native Python `range()` function. See
// http://docs.python.org/library/functions.html#range // [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) { _.range = function(start, stop, step) {
var a = _.toArray(arguments); var args = slice.call(arguments),
var solo = a.length <= 1; solo = args.length <= 1,
var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1; start = solo ? 0 : args[0],
var len = Math.ceil((stop - start) / step); stop = solo ? args[0] : args[1],
if (len <= 0) return []; step = args[2] || 1,
var range = new Array(len); len = Math.max(Math.ceil((stop - start) / step), 0),
for (var i = start, idx = 0; true; i += step) { idx = 0,
if ((step > 0 ? i - stop : stop - i) >= 0) return range; range = new Array(len);
range[idx++] = i; while (idx < len) {
range[idx++] = start;
start += step;
} }
return range;
}; };
// ----------------------- Function Functions: ------------------------------ // Function (ahem) Functions
// ------------------
// Create a function bound to a given object (assigning 'this', and arguments, // Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Binding with arguments is also known as 'curry'. // optionally). Binding with arguments is also known as `curry`.
_.bind = function(func, obj) { _.bind = function(func, obj) {
var args = _.rest(arguments, 2); var args = slice.call(arguments, 2);
return function() { return function() {
return func.apply(obj || {}, args.concat(_.toArray(arguments))); return func.apply(obj || {}, args.concat(slice.call(arguments)));
}; };
}; };
// Bind all of an object's methods to that object. Useful for ensuring that // Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it. // all callbacks defined on an object belong to it.
_.bindAll = function(obj) { _.bindAll = function(obj) {
var funcs = _.rest(arguments); var funcs = slice.call(arguments, 1);
if (funcs.length == 0) funcs = _.functions(obj); if (funcs.length == 0) funcs = _.functions(obj);
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj; return obj;
@ -394,14 +413,41 @@
// Delays a function for the given number of milliseconds, and then calls // Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied. // it with the arguments supplied.
_.delay = function(func, wait) { _.delay = function(func, wait) {
var args = _.rest(arguments, 2); var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait); return setTimeout(function(){ return func.apply(func, args); }, wait);
}; };
// Defers a function, scheduling it to run after the current call stack has // Defers a function, scheduling it to run after the current call stack has
// cleared. // cleared.
_.defer = function(func) { _.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(_.rest(arguments))); return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Internal function used to implement `_.throttle` and `_.debounce`.
var limit = function(func, wait, debounce) {
var timeout;
return function() {
var context = this, args = arguments;
var throttler = function() {
timeout = null;
func.apply(context, args);
};
if (debounce) clearTimeout(timeout);
if (debounce || !timeout) timeout = setTimeout(throttler, wait);
};
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time.
_.throttle = function(func, wait) {
return limit(func, wait, false);
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
_.debounce = function(func, wait) {
return limit(func, wait, true);
}; };
// Returns the first function passed as an argument to the second, // Returns the first function passed as an argument to the second,
@ -409,7 +455,7 @@
// conditionally execute the original function. // conditionally execute the original function.
_.wrap = function(func, wrapper) { _.wrap = function(func, wrapper) {
return function() { return function() {
var args = [func].concat(_.toArray(arguments)); var args = [func].concat(slice.call(arguments));
return wrapper.apply(wrapper, args); return wrapper.apply(wrapper, args);
}; };
}; };
@ -417,9 +463,9 @@
// Returns a function that is the composition of a list of functions, each // Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows. // consuming the return value of the function that follows.
_.compose = function() { _.compose = function() {
var funcs = _.toArray(arguments); var funcs = slice.call(arguments);
return function() { return function() {
var args = _.toArray(arguments); var args = slice.call(arguments);
for (var i=funcs.length-1; i >= 0; i--) { for (var i=funcs.length-1; i >= 0; i--) {
args = [funcs[i].apply(this, args)]; args = [funcs[i].apply(this, args)];
} }
@ -427,10 +473,11 @@
}; };
}; };
// ------------------------- Object Functions: ------------------------------ // Object Functions
// ----------------
// Retrieve the names of an object's properties. // Retrieve the names of an object's properties.
// Delegates to ECMA5's native Object.keys // Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) { _.keys = nativeKeys || function(obj) {
if (_.isArray(obj)) return _.range(0, obj.length); if (_.isArray(obj)) return _.range(0, obj.length);
var keys = []; var keys = [];
@ -444,13 +491,14 @@
}; };
// Return a sorted list of the function names available on the object. // Return a sorted list of the function names available on the object.
_.functions = function(obj) { // Aliased as `methods`
_.functions = _.methods = function(obj) {
return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
}; };
// Extend a given object with all the properties in passed-in object(s). // Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) { _.extend = function(obj) {
each(_.rest(arguments), function(source) { each(slice.call(arguments, 1), function(source) {
for (var prop in source) obj[prop] = source[prop]; for (var prop in source) obj[prop] = source[prop];
}); });
return obj; return obj;
@ -458,12 +506,12 @@
// Create a (shallow-cloned) duplicate of an object. // Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) { _.clone = function(obj) {
if (_.isArray(obj)) return obj.slice(0); return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
return _.extend({}, obj);
}; };
// Invokes interceptor with the obj, and then returns obj. // Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. // The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) { _.tap = function(obj, interceptor) {
interceptor(obj); interceptor(obj);
return obj; return obj;
@ -540,7 +588,13 @@
// Is a given value a number? // Is a given value a number?
_.isNumber = function(obj) { _.isNumber = function(obj) {
return (obj === +obj) || (toString.call(obj) === '[object Number]'); return !!(obj === 0 || (obj && obj.toExponential && obj.toFixed));
};
// Is the given value NaN -- this one is interesting. NaN != NaN, and
// isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN = function(obj) {
return toString.call(obj) === '[object Number]' && isNaN(obj);
}; };
// Is a given value a boolean? // Is a given value a boolean?
@ -558,12 +612,6 @@
return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false)); return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
}; };
// Is the given value NaN -- this one is interesting. NaN != NaN, and
// isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN = function(obj) {
return _.isNumber(obj) && isNaN(obj);
};
// Is a given value equal to null? // Is a given value equal to null?
_.isNull = function(obj) { _.isNull = function(obj) {
return obj === null; return obj === null;
@ -571,12 +619,13 @@
// Is a given variable undefined? // Is a given variable undefined?
_.isUndefined = function(obj) { _.isUndefined = function(obj) {
return typeof obj == 'undefined'; return obj === void 0;
}; };
// -------------------------- Utility Functions: ---------------------------- // Utility Functions
// -----------------
// Run Underscore.js in noConflict mode, returning the '_' variable to its // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object. // previous owner. Returns a reference to the Underscore object.
_.noConflict = function() { _.noConflict = function() {
root._ = previousUnderscore; root._ = previousUnderscore;
@ -588,16 +637,11 @@
return value; return value;
}; };
// Run a function n times. // Run a function **n** times.
_.times = function (n, iterator, context) { _.times = function (n, iterator, context) {
for (var i = 0; i < n; i++) iterator.call(context, i); for (var i = 0; i < n; i++) iterator.call(context, i);
}; };
// Break out of the middle of an iteration.
_.breakLoop = function() {
throw breaker;
};
// Add your own custom functions to the Underscore object, ensuring that // Add your own custom functions to the Underscore object, ensuring that
// they're correctly added to the OOP wrapper as well. // they're correctly added to the OOP wrapper as well.
_.mixin = function(obj) { _.mixin = function(obj) {
@ -617,54 +661,45 @@
// By default, Underscore uses ERB-style template delimiters, change the // By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters. // following template settings to use alternative delimiters.
_.templateSettings = { _.templateSettings = {
start : '<%', evaluate : /<%([\s\S]+?)%>/g,
end : '%>', interpolate : /<%=([\s\S]+?)%>/g
interpolate : /<%=(.+?)%>/g
}; };
// JavaScript templating a-la ERB, pilfered from John Resig's // JavaScript micro-templating, similar to John Resig's implementation.
// "Secrets of the JavaScript Ninja", page 83. // Underscore templating handles arbitrary delimiters, preserves whitespace,
// Single-quote fix from Rick Strahl's version. // and correctly escapes quotes within interpolated code.
// With alterations for arbitrary delimiters, and to preserve whitespace.
_.template = function(str, data) { _.template = function(str, data) {
var c = _.templateSettings; var c = _.templateSettings;
var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g"); var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
var fn = new Function('obj',
'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
'with(obj||{}){__p.push(\'' + 'with(obj||{}){__p.push(\'' +
str.replace(/\r/g, '\\r') str.replace(/\\/g, '\\\\')
.replace(/'/g, "\\'")
.replace(c.interpolate, function(match, code) {
return "'," + code.replace(/\\'/g, "'") + ",'";
})
.replace(c.evaluate || null, function(match, code) {
return "');" + code.replace(/\\'/g, "'")
.replace(/[\r\n\t]/g, ' ') + "__p.push('";
})
.replace(/\r/g, '\\r')
.replace(/\n/g, '\\n') .replace(/\n/g, '\\n')
.replace(/\t/g, '\\t') .replace(/\t/g, '\\t')
.replace(endMatch,"✄") + "');}return __p.join('');";
.split("'").join("\\'") var func = new Function('obj', tmpl);
.split("✄").join("'") return data ? func(data) : func;
.replace(c.interpolate, "',$1,'")
.split(c.start).join("');")
.split(c.end).join("__p.push('")
+ "');}return __p.join('');");
return data ? fn(data) : fn;
}; };
// ------------------------------- Aliases ---------------------------------- // The OOP Wrapper
// ---------------
_.each = _.forEach;
_.foldl = _.inject = _.reduce;
_.foldr = _.reduceRight;
_.select = _.filter;
_.all = _.every;
_.any = _.some;
_.contains = _.include;
_.head = _.first;
_.tail = _.rest;
_.methods = _.functions;
// ------------------------ Setup the OOP Wrapper: --------------------------
// If Underscore is called as a function, it returns a wrapped object that // If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the // can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained. // underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; }; var wrapper = function(obj) { this._wrapped = obj; };
// Expose `wrapper.prototype` as `_.prototype`
_.prototype = wrapper.prototype;
// Helper function to continue chaining intermediate results. // Helper function to continue chaining intermediate results.
var result = function(obj, chain) { var result = function(obj, chain) {
return chain ? _(obj).chain() : obj; return chain ? _(obj).chain() : obj;
@ -673,7 +708,7 @@
// A method to easily add functions to the OOP wrapper. // A method to easily add functions to the OOP wrapper.
var addToWrapper = function(name, func) { var addToWrapper = function(name, func) {
wrapper.prototype[name] = function() { wrapper.prototype[name] = function() {
var args = _.toArray(arguments); var args = slice.call(arguments);
unshift.call(args, this._wrapped); unshift.call(args, this._wrapped);
return result(func.apply(_, args), this._chain); return result(func.apply(_, args), this._chain);
}; };