Update vendor/underscore and underscore tests.

This commit is contained in:
John-David Dalton
2014-04-15 01:01:18 -07:00
parent 900c0eafac
commit d653b951e3
8 changed files with 446 additions and 285 deletions

View File

@@ -31,15 +31,6 @@
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
@@ -71,13 +62,11 @@
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
// Handles raw objects in addition to array-likes. Treats all
// sparse array-likes as if they were dense.
_.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return obj;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
@@ -91,12 +80,10 @@
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
@@ -105,15 +92,11 @@
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
// or `foldl`.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
@@ -126,20 +109,15 @@
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
@@ -155,7 +133,7 @@
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var result;
any(obj, function(value, index, list) {
_.some(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) {
result = value;
return true;
@@ -165,13 +143,11 @@
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, predicate, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(predicate, context);
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
if (predicate.call(context, value, index, list)) results.push(value);
});
return results;
@@ -179,34 +155,28 @@
// Return all the elements for which a truth test fails.
_.reject = function(obj, predicate, context) {
return _.filter(obj, function(value, index, list) {
return !predicate.call(context, value, index, list);
}, context);
return _.filter(obj, _.negate(predicate), context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(predicate, context);
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
if (!(result = result && predicate.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, predicate, context) {
_.some = _.any = function(obj, predicate, context) {
predicate || (predicate = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(predicate, context);
each(obj, function(value, index, list) {
_.each(obj, function(value, index, list) {
if (result || (result = predicate.call(context, value, index, list))) return breaker;
});
return !!result;
@@ -216,8 +186,8 @@
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
if (obj.length === +obj.length) return _.indexOf(obj, target) >= 0;
return _.some(obj, function(value) {
return value === target;
});
};
@@ -249,36 +219,48 @@
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
var result = -Infinity, lastComputed = -Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed > lastComputed) {
result = value;
lastComputed = computed;
var result = -Infinity, lastComputed = -Infinity,
value, computed;
if (!iterator && _.isArray(obj)) {
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value > result) {
result = value;
}
}
});
} else {
_.each(obj, function(value, index, list) {
computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed > lastComputed) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
var result = Infinity, lastComputed = Infinity;
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed < lastComputed) {
result = value;
lastComputed = computed;
var result = Infinity, lastComputed = Infinity,
value, computed;
if (!iterator && _.isArray(obj)) {
for (var i = 0, length = obj.length; i < length; i++) {
value = obj[i];
if (value < result) {
result = value;
}
}
});
} else {
_.each(obj, function(value, index, list) {
computed = iterator ? iterator.call(context, value, index, list) : value;
if (computed < lastComputed) {
result = value;
lastComputed = computed;
}
});
}
return result;
};
@@ -288,7 +270,7 @@
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
_.each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
@@ -308,20 +290,23 @@
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
var lookupIterator = function(value, context) {
if (value == null) return _.identity;
if (_.isFunction(value)) return value;
return _.property(value);
if (!_.isFunction(value)) return _.property(value);
if (!context) return value;
return function() {
return value.apply(context, arguments);
};
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, iterator, context) {
iterator = lookupIterator(iterator);
iterator = lookupIterator(iterator, context);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
criteria: iterator(value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
@@ -338,9 +323,9 @@
var group = function(behavior) {
return function(obj, iterator, context) {
var result = {};
iterator = lookupIterator(iterator);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
iterator = lookupIterator(iterator, context);
_.each(obj, function(value, index) {
var key = iterator(value, index, obj);
behavior(result, key, value);
});
return result;
@@ -369,12 +354,12 @@
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = lookupIterator(iterator);
var value = iterator.call(context, obj);
iterator = lookupIterator(iterator, context);
var value = iterator(obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
iterator(array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
@@ -411,7 +396,7 @@
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
};
// Get the last element of an array. Passing **n** will return the last N
@@ -436,23 +421,26 @@
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
var flatten = function(input, shallow, strict, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
for (var i = 0, length = input.length; i < length; i++) {
var value = input[i];
if (!_.isArray(value) && !_.isArguments(value)) {
if (!strict) output.push(value);
} else if (shallow) {
push.apply(output, value);
} else {
output.push(value);
flatten(value, shallow, strict, output);
}
});
}
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
return flatten(array, shallow, false, []);
};
// Return a version of the array that does not contain the specified value(s).
@@ -462,9 +450,10 @@
// Split an array into two arrays: one whose elements all satisfy the given
// predicate, and one whose elements all do not satisfy the predicate.
_.partition = function(array, predicate) {
_.partition = function(obj, predicate, context) {
predicate = lookupIterator(predicate, context);
var pass = [], fail = [];
each(array, function(elem) {
_.each(obj, function(elem) {
(predicate(elem) ? pass : fail).push(elem);
});
return [pass, fail];
@@ -474,44 +463,53 @@
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (array == null) return [];
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var result = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
for (var i = 0, length = array.length; i < length; i++) {
var value = array[i];
if (iterator) value = iterator.call(context, value, i, array);
if (isSorted ? (!i || seen !== value) : !_.contains(seen, value)) {
if (isSorted) seen = value;
else seen.push(value);
result.push(array[i]);
}
});
return results;
}
return result;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
return _.uniq(flatten(arguments, true, true, []));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.contains(other, item);
});
});
if (array == null) return [];
var result = [];
var argsLength = arguments.length;
for (var i = 0, length = array.length; i < length; i++) {
var item = array[i];
if (_.contains(result, item)) continue;
for (var j = 1; j < argsLength; j++) {
if (!_.contains(arguments[j], item)) break;
}
if (j === argsLength) result.push(item);
}
return result;
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
var rest = flatten(slice.call(arguments, 1), true, true, []);
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
@@ -542,10 +540,8 @@
return result;
};
// 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 occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// Return the position of the first occurrence of an item in an array,
// or -1 if the item is not included in the array.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
@@ -559,19 +555,13 @@
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
var i = from == null ? array.length : from;
while (i--) if (array[i] === item) return i;
return -1;
};
@@ -645,7 +635,7 @@
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error('bindAll must be passed function names');
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
_.each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
@@ -694,7 +684,7 @@
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
if (remaining <= 0 || remaining > wait) {
clearTimeout(timeout);
timeout = null;
previous = now;
@@ -716,7 +706,8 @@
var later = function() {
var last = _.now() - timestamp;
if (last < wait) {
if (last < wait && last > 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
@@ -764,6 +755,13 @@
return _.partial(wrapper, func);
};
// Returns a negated version of the passed-in predicate.
_.negate = function(predicate) {
return function() {
return !predicate.apply(this, arguments);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
@@ -843,7 +841,7 @@
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
_.each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
@@ -854,28 +852,38 @@
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
_.pick = function(obj, iterator, context) {
var result = {};
if (_.isFunction(iterator)) {
for (var key in obj) {
var value = obj[key];
if (iterator.call(context, value, key, obj)) result[key] = value;
}
} else {
var keys = concat.apply([], slice.call(arguments, 1));
for (var i = 0, length = keys.length; i < length; i++) {
var key = keys[i];
if (key in obj) result[key] = obj[key];
}
}
return result;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
_.omit = function(obj, iterator, context) {
var keys;
if (_.isFunction(iterator)) {
iterator = _.negate(iterator);
} else {
keys = _.map(concat.apply([], slice.call(arguments, 1)), String);
iterator = function(value, key) { return !_.contains(keys, key); };
}
return copy;
return _.pick(obj, iterator, context);
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
_.each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
@@ -1000,7 +1008,7 @@
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
if (_.isArray(obj) || _.isString(obj) || _.isArguments(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
@@ -1022,7 +1030,7 @@
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
@@ -1090,11 +1098,13 @@
};
_.constant = function(value) {
return function () {
return function() {
return value;
};
};
_.noop = function(){};
_.property = function(key) {
return function(obj) {
return obj[key];
@@ -1104,11 +1114,9 @@
// Returns a predicate for checking whether an object has a given set of `key:value` pairs.
_.matches = function(attrs) {
return function(obj) {
if (obj === attrs) return true; //avoid comparing an object to itself.
for (var key in attrs) {
if (attrs[key] !== obj[key])
return false;
}
if (obj == null) return _.isEmpty(attrs);
if (obj === attrs) return true;
for (var key in attrs) if (attrs[key] !== obj[key]) return false;
return true;
}
};
@@ -1165,12 +1173,12 @@
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
return _.isFunction(value) ? object[property]() : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
_.each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
@@ -1208,18 +1216,20 @@
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
var escapeChar = function(match) {
return '\\' + escapes[match];
};
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
@@ -1233,19 +1243,18 @@
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
source += text.slice(index, offset).replace(escaper, escapeChar);
index = offset + match.length;
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
} else if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
} else if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
// Adobe VMs need the match returned to produce the correct offest.
return match;
});
source += "';\n";
@@ -1258,7 +1267,7 @@
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
var render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
@@ -1269,8 +1278,9 @@
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
// Provide the compiled source as a convenience for precompilation.
var argument = settings.variable || 'obj';
template.source = 'function(' + argument + '){\n' + source + '}';
return template;
};
@@ -1295,7 +1305,7 @@
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
@@ -1306,7 +1316,7 @@
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
_.each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));